зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central a=merge
This commit is contained in:
Коммит
97c902e8f9
|
@ -251,11 +251,11 @@ add_task(async function navigate_around() {
|
|||
// The following 2 graphics prefs are covered by
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1639497
|
||||
knownProblematicPrefs["gfx.canvas.azure.backends"] = {
|
||||
min: 100,
|
||||
min: 90,
|
||||
max: 110,
|
||||
};
|
||||
knownProblematicPrefs["gfx.content.azure.backends"] = {
|
||||
min: 100,
|
||||
min: 90,
|
||||
max: 110,
|
||||
};
|
||||
// The following 2 sandbox prefs are covered by
|
||||
|
|
|
@ -4196,14 +4196,12 @@ BrowserGlue.prototype = {
|
|||
},
|
||||
|
||||
async _showUpgradeDialog() {
|
||||
const msg = await OnboardingMessageProvider.getUpgradeMessage();
|
||||
const data = await OnboardingMessageProvider.getUpgradeMessage();
|
||||
const win = BrowserWindowTracker.getTopWindow();
|
||||
const browser = win.gBrowser.selectedBrowser;
|
||||
const config = {
|
||||
type: "SHOW_SPOTLIGHT",
|
||||
data: {
|
||||
content: msg.content,
|
||||
},
|
||||
data,
|
||||
};
|
||||
SpecialMessageActions.handleAction(config, browser);
|
||||
},
|
||||
|
|
|
@ -928,6 +928,11 @@ var Policies = {
|
|||
},
|
||||
},
|
||||
|
||||
ExemptDomainFileTypePairsFromFileTypeDownloadWarnings: {
|
||||
// This policy is handled directly in EnterprisePoliciesParent.jsm
|
||||
// and requires no validation (It's done by the schema).
|
||||
},
|
||||
|
||||
Extensions: {
|
||||
onBeforeUIStartup(manager, param) {
|
||||
let uninstallingPromise = Promise.resolve();
|
||||
|
|
|
@ -461,6 +461,24 @@
|
|||
}
|
||||
},
|
||||
|
||||
"ExemptDomainFileTypePairsFromFileTypeDownloadWarnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_extension": {
|
||||
"type": "string"
|
||||
},
|
||||
"domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"Extensions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_exempt_xxx() {
|
||||
await setupPolicyEngineWithJson({
|
||||
policies: {
|
||||
ExemptDomainFileTypePairsFromFileTypeDownloadWarnings: [
|
||||
{
|
||||
file_extension: "jnlp",
|
||||
domains: ["example.com", "www.example.edu"],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
equal(
|
||||
Services.policies.isExemptExecutableExtension(
|
||||
"https://www.example.edu",
|
||||
"jnlp"
|
||||
),
|
||||
true
|
||||
);
|
||||
equal(
|
||||
Services.policies.isExemptExecutableExtension(
|
||||
"https://example.edu",
|
||||
"jnlp"
|
||||
),
|
||||
false
|
||||
);
|
||||
equal(
|
||||
Services.policies.isExemptExecutableExtension(
|
||||
"https://example.com",
|
||||
"jnlp"
|
||||
),
|
||||
true
|
||||
);
|
||||
equal(
|
||||
Services.policies.isExemptExecutableExtension(
|
||||
"https://www.example.com",
|
||||
"jnlp"
|
||||
),
|
||||
true
|
||||
);
|
||||
equal(
|
||||
Services.policies.isExemptExecutableExtension(
|
||||
"https://wwwexample.com",
|
||||
"jnlp"
|
||||
),
|
||||
false
|
||||
);
|
||||
equal(
|
||||
Services.policies.isExemptExecutableExtension(
|
||||
"https://www.example.org",
|
||||
"jnlp"
|
||||
),
|
||||
false
|
||||
);
|
||||
equal(
|
||||
Services.policies.isExemptExecutableExtension(
|
||||
"https://www.example.edu",
|
||||
"exe"
|
||||
),
|
||||
false
|
||||
);
|
||||
});
|
|
@ -13,6 +13,7 @@ support-files =
|
|||
[test_clear_blocked_cookies.js]
|
||||
[test_defaultbrowsercheck.js]
|
||||
[test_empty_policy.js]
|
||||
[test_exempt_domain_file_type_pairs_from_file_type_download_warnings.js]
|
||||
[test_extensions.js]
|
||||
[test_extensionsettings.js]
|
||||
[test_macosparser_unflatten.js]
|
||||
|
|
|
@ -15,10 +15,18 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
"resource://messaging-system/lib/SpecialMessageActions.jsm",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(
|
||||
this,
|
||||
"AWTelemetry",
|
||||
() => new AboutWelcomeTelemetry()
|
||||
);
|
||||
|
||||
const Spotlight = {
|
||||
sendUserEventTelemetry(event, message, dispatch) {
|
||||
const message_id =
|
||||
message.template === "multistage" ? message.content.id : message.id;
|
||||
const ping = {
|
||||
message_id: message.id,
|
||||
message_id,
|
||||
event,
|
||||
};
|
||||
dispatch({
|
||||
|
@ -27,6 +35,13 @@ const Spotlight = {
|
|||
});
|
||||
},
|
||||
|
||||
defaultDispatch(message) {
|
||||
if (message.type === "SPOTLIGHT_TELEMETRY") {
|
||||
const { message_id, event } = message.data;
|
||||
AWTelemetry.sendTelemetry({ message_id, event });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows spotlight tab or window modal specific to the given browser
|
||||
* @param browser The browser for spotlight display
|
||||
|
@ -34,7 +49,7 @@ const Spotlight = {
|
|||
* @param dispatchCFRAction A function to dispatch resulting actions
|
||||
* @return boolean value capturing if spotlight was displayed
|
||||
*/
|
||||
async showSpotlightDialog(browser, message, dispatch) {
|
||||
async showSpotlightDialog(browser, message, dispatch = this.defaultDispatch) {
|
||||
const win = browser.ownerGlobal;
|
||||
if (win.gDialogBox.isOpen) {
|
||||
return false;
|
||||
|
@ -43,9 +58,7 @@ const Spotlight = {
|
|||
|
||||
const dispatchCFRAction =
|
||||
// This also blocks CFR impressions, which is fine for current use cases.
|
||||
message.content?.metrics === "block"
|
||||
? () => {}
|
||||
: dispatch ?? (msg => new AboutWelcomeTelemetry().sendTelemetry(msg));
|
||||
message.content?.metrics === "block" ? () => {} : dispatch;
|
||||
let params = { primaryBtn: false, secondaryBtn: false };
|
||||
|
||||
// There are two events named `IMPRESSION` the first one refers to telemetry
|
||||
|
|
|
@ -10,19 +10,31 @@ const { BrowserWindowTracker } = ChromeUtils.import(
|
|||
"resource:///modules/BrowserWindowTracker.jsm"
|
||||
);
|
||||
|
||||
const { AboutWelcomeTelemetry } = ChromeUtils.import(
|
||||
"resource://activity-stream/aboutwelcome/lib/AboutWelcomeTelemetry.jsm"
|
||||
);
|
||||
|
||||
async function waitForClick(selector, win) {
|
||||
await TestUtils.waitForCondition(() => win.document.querySelector(selector));
|
||||
win.document.querySelector(selector).click();
|
||||
}
|
||||
|
||||
async function showDialog(dialogOptions) {
|
||||
function waitForDialog(callback = win => win.close()) {
|
||||
return BrowserTestUtils.promiseAlertDialog(
|
||||
null,
|
||||
"chrome://browser/content/spotlight.html",
|
||||
{ callback, isSubDialog: true }
|
||||
);
|
||||
}
|
||||
|
||||
function showAndWaitForDialog(dialogOptions, callback) {
|
||||
const promise = waitForDialog(callback);
|
||||
Spotlight.showSpotlightDialog(
|
||||
dialogOptions.browser,
|
||||
dialogOptions.message,
|
||||
dialogOptions.dispatchStub
|
||||
);
|
||||
const [win] = await TestUtils.topicObserved("subdialog-loaded");
|
||||
return win;
|
||||
return promise;
|
||||
}
|
||||
|
||||
add_task(async function send_spotlight_as_page_in_telemetry() {
|
||||
|
@ -31,20 +43,69 @@ add_task(async function send_spotlight_as_page_in_telemetry() {
|
|||
);
|
||||
let dispatchStub = sinon.stub();
|
||||
let browser = BrowserWindowTracker.getTopWindow().gBrowser.selectedBrowser;
|
||||
let win = await showDialog({ message, browser, dispatchStub });
|
||||
let sandbox = sinon.createSandbox();
|
||||
|
||||
sandbox.stub(win, "AWSendEventTelemetry");
|
||||
await showAndWaitForDialog({ message, browser, dispatchStub }, async win => {
|
||||
let stub = sandbox.stub(win, "AWSendEventTelemetry");
|
||||
await waitForClick("button.secondary", win);
|
||||
|
||||
Assert.equal(
|
||||
win.AWSendEventTelemetry.lastCall.args[0].event_context.page,
|
||||
stub.lastCall.args[0].event_context.page,
|
||||
"spotlight",
|
||||
"The value of event context page should be set to 'spotlight' in event telemetry"
|
||||
);
|
||||
|
||||
win.close();
|
||||
registerCleanupFunction(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
add_task(async function send_dismiss_event_telemetry() {
|
||||
const messageId = "PB_FOCUS_PROMO"; // a multistage spotlight promo with dismiss button
|
||||
let message = (await PanelTestProvider.getMessages()).find(
|
||||
m => m.id === messageId
|
||||
);
|
||||
let browser = BrowserWindowTracker.getTopWindow().gBrowser.selectedBrowser;
|
||||
let sandbox = sinon.createSandbox();
|
||||
let stub = sandbox.stub(AboutWelcomeTelemetry.prototype, "sendTelemetry");
|
||||
// send without a dispatch function so that default is used
|
||||
await showAndWaitForDialog({ message, browser }, async win => {
|
||||
await waitForClick("button.dismiss-button", win);
|
||||
win.close();
|
||||
});
|
||||
|
||||
Assert.equal(
|
||||
stub.lastCall.args[0].message_id,
|
||||
messageId,
|
||||
"A dismiss event is called with the correct message id"
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
stub.lastCall.args[0].event,
|
||||
"DISMISS",
|
||||
"A dismiss event is called with a top level event field with value 'DISMISS'"
|
||||
);
|
||||
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
add_task(
|
||||
async function do_not_send_impression_telemetry_from_default_dispatch() {
|
||||
// Don't send impression telemetry from the Spotlight default dispatch function
|
||||
let message = (await PanelTestProvider.getMessages()).find(
|
||||
m => m.id === "MULTISTAGE_SPOTLIGHT_MESSAGE"
|
||||
);
|
||||
let browser = BrowserWindowTracker.getTopWindow().gBrowser.selectedBrowser;
|
||||
let sandbox = sinon.createSandbox();
|
||||
let stub = sandbox.stub(AboutWelcomeTelemetry.prototype, "sendTelemetry");
|
||||
// send without a dispatch function so that default is used
|
||||
await showAndWaitForDialog({ message, browser });
|
||||
|
||||
Assert.equal(
|
||||
stub.calledOn(),
|
||||
false,
|
||||
"No extra impression event was sent for multistage Spotlight"
|
||||
);
|
||||
|
||||
sandbox.restore();
|
||||
}
|
||||
);
|
||||
|
|
|
@ -102,6 +102,8 @@ policy-EnableTrackingProtection = Enable or disable Content Blocking and optiona
|
|||
# “lock” means that the user won’t be able to change this setting
|
||||
policy-EncryptedMediaExtensions = Enable or disable Encrypted Media Extensions and optionally lock it.
|
||||
|
||||
policy-ExemptDomainFileTypePairsFromFileTypeDownloadWarnings = Disable warnings based on file extension for specific file types on domains.
|
||||
|
||||
# A “locked” extension can’t be disabled or removed by the user. This policy
|
||||
# takes 3 keys (“Install”, ”Uninstall”, ”Locked”), you can either keep them in
|
||||
# English or translate them as verbs.
|
||||
|
|
|
@ -622,7 +622,7 @@
|
|||
}
|
||||
|
||||
#urlbar-zoom-button:focus-visible {
|
||||
outline: var(--toolbarbutton-focus-outline);
|
||||
outline: var(--focus-outline);
|
||||
outline-offset: var(--focus-outline-inset);
|
||||
}
|
||||
|
||||
|
|
|
@ -11482,6 +11482,7 @@ void Document::Destroy() {
|
|||
}
|
||||
|
||||
if (IsStaticDocument()) {
|
||||
RemoveProperty(nsGkAtoms::printisfocuseddoc);
|
||||
RemoveProperty(nsGkAtoms::printselectionranges);
|
||||
}
|
||||
|
||||
|
@ -13118,11 +13119,39 @@ static nsINode* GetCorrespondingNodeInDocument(const nsINode* aOrigNode,
|
|||
static void CachePrintSelectionRanges(const Document& aSourceDoc,
|
||||
Document& aStaticClone) {
|
||||
MOZ_ASSERT(aStaticClone.IsStaticDocument());
|
||||
MOZ_ASSERT(!aStaticClone.GetProperty(nsGkAtoms::printisfocuseddoc));
|
||||
MOZ_ASSERT(!aStaticClone.GetProperty(nsGkAtoms::printselectionranges));
|
||||
|
||||
bool sourceDocIsStatic = aSourceDoc.IsStaticDocument();
|
||||
|
||||
// When the user opts to "Print Selection Only", the print code prefers any
|
||||
// selection in the static clone corresponding to the focused frame. If this
|
||||
// is that static clone, flag it for the printing code:
|
||||
const bool isFocusedDoc = [&] {
|
||||
if (sourceDocIsStatic) {
|
||||
return bool(aSourceDoc.GetProperty(nsGkAtoms::printisfocuseddoc));
|
||||
}
|
||||
nsPIDOMWindowOuter* window = aSourceDoc.GetWindow();
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
|
||||
if (!rootWindow) {
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
|
||||
nsFocusManager::GetFocusedDescendant(rootWindow,
|
||||
nsFocusManager::eIncludeAllDescendants,
|
||||
getter_AddRefs(focusedWindow));
|
||||
return focusedWindow && focusedWindow->GetExtantDoc() == &aSourceDoc;
|
||||
}();
|
||||
if (isFocusedDoc) {
|
||||
aStaticClone.SetProperty(nsGkAtoms::printisfocuseddoc,
|
||||
reinterpret_cast<void*>(true));
|
||||
}
|
||||
|
||||
const Selection* origSelection = nullptr;
|
||||
const nsTArray<RefPtr<nsRange>>* origRanges = nullptr;
|
||||
bool sourceDocIsStatic = aSourceDoc.IsStaticDocument();
|
||||
|
||||
if (sourceDocIsStatic) {
|
||||
origRanges = static_cast<nsTArray<RefPtr<nsRange>>*>(
|
||||
|
|
|
@ -2912,17 +2912,17 @@ void EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
|
|||
actualDevPixelScrollAmount.y = 0;
|
||||
}
|
||||
|
||||
nsIScrollbarMediator::ScrollSnapMode snapMode =
|
||||
nsIScrollbarMediator::DISABLE_SNAP;
|
||||
ScrollSnapFlags snapFlags = ScrollSnapFlags::Disabled;
|
||||
mozilla::ScrollOrigin origin = mozilla::ScrollOrigin::NotSpecified;
|
||||
switch (aEvent->mDeltaMode) {
|
||||
case WheelEvent_Binding::DOM_DELTA_LINE:
|
||||
origin = mozilla::ScrollOrigin::MouseWheel;
|
||||
snapMode = nsIScrollableFrame::ENABLE_SNAP;
|
||||
snapFlags = ScrollSnapFlags::IntendedDirection;
|
||||
break;
|
||||
case WheelEvent_Binding::DOM_DELTA_PAGE:
|
||||
origin = mozilla::ScrollOrigin::Pages;
|
||||
snapMode = nsIScrollableFrame::ENABLE_SNAP;
|
||||
snapFlags = ScrollSnapFlags::IntendedDirection |
|
||||
ScrollSnapFlags::IntendedEndPosition;
|
||||
break;
|
||||
case WheelEvent_Binding::DOM_DELTA_PIXEL:
|
||||
origin = mozilla::ScrollOrigin::Pixels;
|
||||
|
@ -2984,7 +2984,7 @@ void EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
|
|||
nsIntPoint overflow;
|
||||
aScrollableFrame->ScrollBy(actualDevPixelScrollAmount,
|
||||
ScrollUnit::DEVICE_PIXELS, mode, &overflow, origin,
|
||||
momentum, snapMode);
|
||||
momentum, snapFlags);
|
||||
|
||||
if (!scrollFrameWeak.IsAlive()) {
|
||||
// If the scroll causes changing the layout, we can think that the event
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
#if LIBAVCODEC_VERSION_MAJOR >= 57
|
||||
# include "mozilla/layers/TextureClient.h"
|
||||
#endif
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 58
|
||||
# include "mozilla/ProfilerMarkers.h"
|
||||
#endif
|
||||
#ifdef MOZ_WAYLAND_USE_VAAPI
|
||||
# include "H264.h"
|
||||
# include "mozilla/layers/DMABUFSurfaceImage.h"
|
||||
|
@ -58,9 +61,10 @@ typedef int VAStatus;
|
|||
# define VA_EXPORT_SURFACE_SEPARATE_LAYERS 0x0004
|
||||
# define VA_STATUS_SUCCESS 0x00000000
|
||||
#endif
|
||||
|
||||
// Use some extra HW frames for potential rendering lags.
|
||||
#define EXTRA_HW_FRAMES 6
|
||||
// Defines number of delayed frames until we switch back to SW decode.
|
||||
#define HW_DECODE_LATE_FRAMES 15
|
||||
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 57 && LIBAVUTIL_VERSION_MAJOR >= 56
|
||||
# define CUSTOMIZED_BUFFER_ALLOCATION 1
|
||||
|
@ -388,6 +392,7 @@ FFmpegVideoDecoder<LIBAV_VER>::FFmpegVideoDecoder(
|
|||
mDecodedFrames(0),
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 58
|
||||
mDecodedFramesLate(0),
|
||||
mMissedDecodeInAverangeTime(0),
|
||||
#endif
|
||||
mAverangeDecodeTime(0),
|
||||
mLowLatency(aLowLatency) {
|
||||
|
@ -783,18 +788,28 @@ void FFmpegVideoDecoder<LIBAV_VER>::UpdateDecodeTimes(TimeStamp aDecodeStart) {
|
|||
(mAverangeDecodeTime * (mDecodedFrames - 1) + decodeTime) /
|
||||
mDecodedFrames;
|
||||
FFMPEG_LOG(
|
||||
" decode time %.2f ms averange decode time %.2f ms decoded frames %d\n",
|
||||
"Frame decode finished, time %.2f ms averange decode time %.2f ms "
|
||||
"decoded %d frames\n",
|
||||
decodeTime, mAverangeDecodeTime, mDecodedFrames);
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 58
|
||||
int frameDuration = mFrame->pkt_duration;
|
||||
if (frameDuration > 0 && frameDuration / 1000.0 < decodeTime) {
|
||||
if (mFrame->pkt_duration > 0) {
|
||||
// Switch frame duration to ms
|
||||
float frameDuration = mFrame->pkt_duration / 1000.0f;
|
||||
if (frameDuration < decodeTime) {
|
||||
PROFILER_MARKER_TEXT("FFmpegVideoDecoder::DoDecode", MEDIA_PLAYBACK, {},
|
||||
"frame decode takes too long");
|
||||
mDecodedFramesLate++;
|
||||
if (frameDuration < mAverangeDecodeTime) {
|
||||
mMissedDecodeInAverangeTime++;
|
||||
}
|
||||
FFMPEG_LOG(
|
||||
" slow decode: failed to decode in time, frame duration %.2f ms, "
|
||||
"decode time %.2f\n",
|
||||
frameDuration / 1000.0, decodeTime);
|
||||
FFMPEG_LOG(" all decoded frames / late decoded frames %d/%d\n",
|
||||
mDecodedFrames, mDecodedFramesLate);
|
||||
frameDuration, decodeTime);
|
||||
FFMPEG_LOG(" frames: all decoded %d late decoded %d over averange %d\n",
|
||||
mDecodedFrames, mDecodedFramesLate,
|
||||
mMissedDecodeInAverangeTime);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -868,6 +883,14 @@ MediaResult FFmpegVideoDecoder<LIBAV_VER>::DoDecode(
|
|||
MediaResult rv;
|
||||
# ifdef MOZ_WAYLAND_USE_VAAPI
|
||||
if (IsHardwareAccelerated()) {
|
||||
if (mMissedDecodeInAverangeTime > HW_DECODE_LATE_FRAMES) {
|
||||
PROFILER_MARKER_TEXT("FFmpegVideoDecoder::DoDecode", MEDIA_PLAYBACK, {},
|
||||
"Fallback to SW decode");
|
||||
FFMPEG_LOG(" HW decoding is slow, switch back to SW decode");
|
||||
return MediaResult(
|
||||
NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("HW decoding is slow, switch back to SW decode"));
|
||||
}
|
||||
rv = CreateImageVAAPI(mFrame->pkt_pos, GetFramePts(mFrame),
|
||||
mFrame->pkt_duration, aResults);
|
||||
// If VA-API playback failed, just quit. Decoder is going to be restarted
|
||||
|
@ -1131,7 +1154,7 @@ bool FFmpegVideoDecoder<LIBAV_VER>::GetVAAPISurfaceDescriptor(
|
|||
MediaResult FFmpegVideoDecoder<LIBAV_VER>::CreateImageVAAPI(
|
||||
int64_t aOffset, int64_t aPts, int64_t aDuration,
|
||||
MediaDataDecoder::DecodedData& aResults) {
|
||||
FFMPEG_LOG("VA-API Got one frame output with pts=%" PRId64 "dts=%" PRId64
|
||||
FFMPEG_LOG("VA-API Got one frame output with pts=%" PRId64 " dts=%" PRId64
|
||||
" duration=%" PRId64 " opaque=%" PRId64,
|
||||
aPts, mFrame->pkt_dts, aDuration, mCodecContext->reordered_opaque);
|
||||
|
||||
|
|
|
@ -148,6 +148,11 @@ class FFmpegVideoDecoder<LIBAV_VER>
|
|||
int mDecodedFrames;
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 58
|
||||
int mDecodedFramesLate;
|
||||
// Tracks when decode time of recent frame and averange decode time of
|
||||
// previous frames is bigger than frame interval,
|
||||
// i.e. we fail to decode in time.
|
||||
// We switch to SW decode when we hit HW_DECODE_LATE_FRAMES treshold.
|
||||
int mMissedDecodeInAverangeTime;
|
||||
#endif
|
||||
float mAverangeDecodeTime;
|
||||
|
||||
|
|
|
@ -449,6 +449,8 @@ already_AddRefed<Promise> WebAuthnManager::MakeCredential(
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
const size_t MAX_ALLOWED_CREDENTIALS = 20;
|
||||
|
||||
already_AddRefed<Promise> WebAuthnManager::GetAssertion(
|
||||
const PublicKeyCredentialRequestOptions& aOptions,
|
||||
const Optional<OwningNonNull<AbortSignal>>& aSignal, ErrorResult& aError) {
|
||||
|
@ -521,6 +523,12 @@ already_AddRefed<Promise> WebAuthnManager::GetAssertion(
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
// Abort the request if the allowCredentials set is too large
|
||||
if (aOptions.mAllowCredentials.Length() > MAX_ALLOWED_CREDENTIALS) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Use assertionChallenge, callerOrigin and rpId, along with the token binding
|
||||
// key associated with callerOrigin (if any), to create a ClientData structure
|
||||
// representing this request. Choose a hash algorithm for hashAlg and compute
|
||||
|
|
|
@ -54,6 +54,10 @@
|
|||
ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError, got " + aResult);
|
||||
}
|
||||
|
||||
function expectSecurityError(aResult) {
|
||||
ok(aResult.toString().startsWith("SecurityError"), "Expecting a SecurityError, got " + aResult);
|
||||
}
|
||||
|
||||
function expectAbortError(aResult) {
|
||||
is(aResult.code, DOMException.ABORT_ERR, "Expecting an AbortError");
|
||||
}
|
||||
|
@ -172,6 +176,19 @@
|
|||
.catch(expectInvalidStateError);
|
||||
});
|
||||
|
||||
// Test with too many credentials
|
||||
add_task(async () => {
|
||||
let tooManyCredentials = Array(21).fill(validCred);
|
||||
let publicKey = {
|
||||
challenge: gAssertionChallenge,
|
||||
allowCredentials: tooManyCredentials,
|
||||
};
|
||||
|
||||
await requestGetAssertion({publicKey})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
});
|
||||
|
||||
// Test with an unexpected option and an invalid credential
|
||||
add_task(async () => {
|
||||
let publicKey = {
|
||||
|
|
|
@ -61,6 +61,12 @@ uint32_t SupportedLimits::MaxUniformBufferBindingSize() const {
|
|||
uint32_t SupportedLimits::MaxStorageBufferBindingSize() const {
|
||||
return mLimits->max_storage_buffer_binding_size;
|
||||
}
|
||||
uint32_t SupportedLimits::MinUniformBufferOffsetAlignment() const {
|
||||
return mLimits->min_uniform_buffer_offset_alignment;
|
||||
}
|
||||
uint32_t SupportedLimits::MinStorageBufferOffsetAlignment() const {
|
||||
return mLimits->min_storage_buffer_offset_alignment;
|
||||
}
|
||||
uint32_t SupportedLimits::MaxVertexBuffers() const {
|
||||
return mLimits->max_vertex_buffers;
|
||||
}
|
||||
|
@ -70,5 +76,26 @@ uint32_t SupportedLimits::MaxVertexAttributes() const {
|
|||
uint32_t SupportedLimits::MaxVertexBufferArrayStride() const {
|
||||
return mLimits->max_vertex_buffer_array_stride;
|
||||
}
|
||||
uint32_t SupportedLimits::MaxInterStageShaderComponents() const {
|
||||
return mLimits->max_inter_stage_shader_components;
|
||||
}
|
||||
uint32_t SupportedLimits::MaxComputeWorkgroupStorageSize() const {
|
||||
return mLimits->max_compute_workgroup_storage_size;
|
||||
}
|
||||
uint32_t SupportedLimits::MaxComputeInvocationsPerWorkgroup() const {
|
||||
return mLimits->max_compute_invocations_per_workgroup;
|
||||
}
|
||||
uint32_t SupportedLimits::MaxComputeWorkgroupSizeX() const {
|
||||
return mLimits->max_compute_workgroup_size_x;
|
||||
}
|
||||
uint32_t SupportedLimits::MaxComputeWorkgroupSizeY() const {
|
||||
return mLimits->max_compute_workgroup_size_y;
|
||||
}
|
||||
uint32_t SupportedLimits::MaxComputeWorkgroupSizeZ() const {
|
||||
return mLimits->max_compute_workgroup_size_z;
|
||||
}
|
||||
uint32_t SupportedLimits::MaxComputeWorkgroupsPerDimension() const {
|
||||
return mLimits->max_compute_workgroups_per_dimension;
|
||||
}
|
||||
|
||||
} // namespace mozilla::webgpu
|
||||
|
|
|
@ -36,9 +36,18 @@ class SupportedLimits final : public nsWrapperCache, public ChildOf<Adapter> {
|
|||
uint32_t MaxUniformBuffersPerShaderStage() const;
|
||||
uint32_t MaxUniformBufferBindingSize() const;
|
||||
uint32_t MaxStorageBufferBindingSize() const;
|
||||
uint32_t MinUniformBufferOffsetAlignment() const;
|
||||
uint32_t MinStorageBufferOffsetAlignment() const;
|
||||
uint32_t MaxVertexBuffers() const;
|
||||
uint32_t MaxVertexAttributes() const;
|
||||
uint32_t MaxVertexBufferArrayStride() const;
|
||||
uint32_t MaxInterStageShaderComponents() const;
|
||||
uint32_t MaxComputeWorkgroupStorageSize() const;
|
||||
uint32_t MaxComputeInvocationsPerWorkgroup() const;
|
||||
uint32_t MaxComputeWorkgroupSizeX() const;
|
||||
uint32_t MaxComputeWorkgroupSizeY() const;
|
||||
uint32_t MaxComputeWorkgroupSizeZ() const;
|
||||
uint32_t MaxComputeWorkgroupsPerDimension() const;
|
||||
|
||||
SupportedLimits(Adapter* const aParent, UniquePtr<ffi::WGPULimits>&& aLimits);
|
||||
|
||||
|
|
|
@ -125,9 +125,18 @@ interface GPUSupportedLimits {
|
|||
readonly attribute unsigned long maxUniformBuffersPerShaderStage;
|
||||
readonly attribute unsigned long maxUniformBufferBindingSize;
|
||||
readonly attribute unsigned long maxStorageBufferBindingSize;
|
||||
readonly attribute unsigned long minUniformBufferOffsetAlignment;
|
||||
readonly attribute unsigned long minStorageBufferOffsetAlignment;
|
||||
readonly attribute unsigned long maxVertexBuffers;
|
||||
readonly attribute unsigned long maxVertexAttributes;
|
||||
readonly attribute unsigned long maxVertexBufferArrayStride;
|
||||
readonly attribute unsigned long maxInterStageShaderComponents;
|
||||
readonly attribute unsigned long maxComputeWorkgroupStorageSize;
|
||||
readonly attribute unsigned long maxComputeInvocationsPerWorkgroup;
|
||||
readonly attribute unsigned long maxComputeWorkgroupSizeX;
|
||||
readonly attribute unsigned long maxComputeWorkgroupSizeY;
|
||||
readonly attribute unsigned long maxComputeWorkgroupSizeZ;
|
||||
readonly attribute unsigned long maxComputeWorkgroupsPerDimension;
|
||||
};
|
||||
|
||||
[Pref="dom.webgpu.enabled",
|
||||
|
|
|
@ -602,10 +602,22 @@ EditorSpellCheck::SetCurrentDictionaries(
|
|||
uint32_t flags = 0;
|
||||
mEditor->GetFlags(&flags);
|
||||
if (!(flags & nsIEditor::eEditorMailMask)) {
|
||||
if (!aDictionaries.IsEmpty() &&
|
||||
(mPreferredLang.IsEmpty() || aDictionaries.Length() > 1 ||
|
||||
!mPreferredLang.Equals(aDictionaries[0],
|
||||
nsCaseInsensitiveCStringComparator))) {
|
||||
bool contentPrefMatchesUserPref = true;
|
||||
// Check if aDictionaries has the same languages as mPreferredLangs.
|
||||
if (!aDictionaries.IsEmpty()) {
|
||||
if (aDictionaries.Length() != mPreferredLangs.Length()) {
|
||||
contentPrefMatchesUserPref = false;
|
||||
} else {
|
||||
for (const auto& dictName : aDictionaries) {
|
||||
if (mPreferredLangs.IndexOf(dictName) ==
|
||||
nsTArray<nsCString>::NoIndex) {
|
||||
contentPrefMatchesUserPref = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!contentPrefMatchesUserPref) {
|
||||
// When user sets dictionary manually, we store this value associated
|
||||
// with editor url, if it doesn't match the document language exactly.
|
||||
// For example on "en" sites, we need to store "en-GB", otherwise
|
||||
|
@ -642,6 +654,24 @@ EditorSpellCheck::SetCurrentDictionaries(
|
|||
asString.Data());
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(flags & nsIEditor::eEditorMailMask);
|
||||
// Since the mail editor can only influence the language selection by the
|
||||
// html lang attribute, set the content-language document to persist
|
||||
// multi language selections.
|
||||
nsCOMPtr<nsIContent> rootContent;
|
||||
if (HTMLEditor* htmlEditor = mEditor->GetAsHTMLEditor()) {
|
||||
rootContent = htmlEditor->GetActiveEditingHost();
|
||||
} else {
|
||||
rootContent = mEditor->GetRoot();
|
||||
}
|
||||
RefPtr<Document> ownerDoc = rootContent->OwnerDoc();
|
||||
Document* parentDoc = ownerDoc->GetInProcessParentDocument();
|
||||
if (parentDoc) {
|
||||
parentDoc->SetHeaderData(
|
||||
nsGkAtoms::headerContentLanguage,
|
||||
NS_ConvertUTF8toUTF16(DictionariesToString(aDictionaries)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -751,7 +781,7 @@ EditorSpellCheck::UpdateCurrentDictionary(
|
|||
|
||||
// Helper function that iterates over the list of dictionaries and sets the one
|
||||
// that matches based on a given comparison type.
|
||||
void EditorSpellCheck::BuildDictionaryList(const nsACString& aDictName,
|
||||
bool EditorSpellCheck::BuildDictionaryList(const nsACString& aDictName,
|
||||
const nsTArray<nsCString>& aDictList,
|
||||
enum dictCompare aCompareType,
|
||||
nsTArray<nsCString>& aOutList) {
|
||||
|
@ -771,16 +801,20 @@ void EditorSpellCheck::BuildDictionaryList(const nsACString& aDictName,
|
|||
break;
|
||||
}
|
||||
if (equals) {
|
||||
// Avoid adding duplicates to aOutList.
|
||||
if (aOutList.IndexOf(dictStr) == nsTArray<nsCString>::NoIndex) {
|
||||
aOutList.AppendElement(dictStr);
|
||||
}
|
||||
#ifdef DEBUG_DICT
|
||||
printf("***** Trying |%s|.\n", dictStr.get());
|
||||
#endif
|
||||
// We always break here. We tried to set the dictionary to an existing
|
||||
// dictionary from the list. This must work, if it doesn't, there is
|
||||
// no point trying another one.
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult EditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher) {
|
||||
|
@ -812,6 +846,9 @@ nsresult EditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher) {
|
|||
* However, we prefer what is stored in "spellchecker.dictionary",
|
||||
* so if the user chose "en-AU" before, they will get "en-AU" on a plain
|
||||
* "en" site. (Introduced in bug 682564.)
|
||||
* If the site has multiple languages declared in its Content-Language
|
||||
* header and there is no more specific lang tag in HTML, we try to
|
||||
* enable a dictionary for every content language.
|
||||
* 3) The value of "spellchecker.dictionary" which reflects a previous
|
||||
* language choice of the user (on another site).
|
||||
* (This was the original behaviour before the aforementioned bugs
|
||||
|
@ -824,19 +861,24 @@ nsresult EditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher) {
|
|||
|
||||
// Get the language from the element or its closest parent according to:
|
||||
// https://html.spec.whatwg.org/#attr-lang
|
||||
// This is used in SetCurrentDictionary.
|
||||
CopyUTF16toUTF8(aFetcher->mRootContentLang, mPreferredLang);
|
||||
// This is used in SetCurrentDictionaries.
|
||||
nsCString contentLangs;
|
||||
// Reset mPreferredLangs so we only get the current state.
|
||||
mPreferredLangs.Clear();
|
||||
CopyUTF16toUTF8(aFetcher->mRootContentLang, contentLangs);
|
||||
#ifdef DEBUG_DICT
|
||||
printf("***** mPreferredLang (element) |%s|\n", mPreferredLang.get());
|
||||
printf("***** mPreferredLangs (element) |%s|\n", contentLangs.get());
|
||||
#endif
|
||||
|
||||
if (!contentLangs.IsEmpty()) {
|
||||
mPreferredLangs.AppendElement(contentLangs);
|
||||
} else {
|
||||
// If no luck, try the "Content-Language" header.
|
||||
if (mPreferredLang.IsEmpty()) {
|
||||
CopyUTF16toUTF8(aFetcher->mRootDocContentLang, mPreferredLang);
|
||||
CopyUTF16toUTF8(aFetcher->mRootDocContentLang, contentLangs);
|
||||
#ifdef DEBUG_DICT
|
||||
printf("***** mPreferredLang (content-language) |%s|\n",
|
||||
mPreferredLang.get());
|
||||
printf("***** mPreferredLangs (content-language) |%s|\n",
|
||||
contentLangs.get());
|
||||
#endif
|
||||
StringToDictionaries(contentLangs, mPreferredLangs);
|
||||
}
|
||||
|
||||
// We obtain a list of available dictionaries.
|
||||
|
@ -921,12 +963,8 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
}
|
||||
|
||||
// Priority 2:
|
||||
// After checking the content preferences, we use the language of the element
|
||||
// After checking the content preferences, we use the languages of the element
|
||||
// or document.
|
||||
nsAutoCString dictName(mPreferredLang);
|
||||
#ifdef DEBUG_DICT
|
||||
printf("***** Assigned from element/doc |%s|\n", dictName.get());
|
||||
#endif
|
||||
|
||||
// Get the preference value.
|
||||
nsAutoCString prefDictionariesAsString;
|
||||
|
@ -936,14 +974,17 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
StringToDictionaries(prefDictionariesAsString, prefDictionaries);
|
||||
|
||||
nsAutoCString appLocaleStr;
|
||||
if (!dictName.IsEmpty()) {
|
||||
// We pick one dictionary for every language that the element or document
|
||||
// indicates it contains.
|
||||
for (const auto& dictName : mPreferredLangs) {
|
||||
// RFC 5646 explicitly states that matches should be case-insensitive.
|
||||
BuildDictionaryList(dictName, dictList, DICT_COMPARE_CASE_INSENSITIVE,
|
||||
tryDictList);
|
||||
|
||||
if (BuildDictionaryList(dictName, dictList, DICT_COMPARE_CASE_INSENSITIVE,
|
||||
tryDictList)) {
|
||||
#ifdef DEBUG_DICT
|
||||
printf("***** Trying from element/doc |%s| \n", dictName.get());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
// Required dictionary was not available. Try to get a dictionary
|
||||
// matching at least language part of dictName.
|
||||
|
@ -955,6 +996,7 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
|
||||
// Try dictionary.spellchecker preference, if it starts with langCode,
|
||||
// so we don't just get any random dictionary matching the language.
|
||||
bool didAppend = false;
|
||||
for (const auto& dictionary : prefDictionaries) {
|
||||
if (nsStyleUtil::DashMatchCompare(NS_ConvertUTF8toUTF16(dictionary),
|
||||
NS_ConvertUTF8toUTF16(langCode),
|
||||
|
@ -965,13 +1007,17 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
"code\n",
|
||||
dictionary.Data());
|
||||
#endif
|
||||
BuildDictionaryList(dictionary, dictList,
|
||||
DICT_COMPARE_CASE_INSENSITIVE, tryDictList);
|
||||
if (BuildDictionaryList(dictionary, dictList,
|
||||
DICT_COMPARE_CASE_INSENSITIVE, tryDictList)) {
|
||||
didAppend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (didAppend) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tryDictList.IsEmpty()) {
|
||||
// Use the application locale dictionary when the required language
|
||||
// equals applocation locale language.
|
||||
LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocaleStr);
|
||||
|
@ -981,8 +1027,10 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
mozilla::intl::LocaleParser::TryParse(appLocaleStr, appLoc);
|
||||
if (result.isOk() && loc.Canonicalize().isOk() &&
|
||||
loc.Language().Span() == appLoc.Language().Span()) {
|
||||
BuildDictionaryList(appLocaleStr, dictList,
|
||||
DICT_COMPARE_CASE_INSENSITIVE, tryDictList);
|
||||
if (BuildDictionaryList(appLocaleStr, dictList,
|
||||
DICT_COMPARE_CASE_INSENSITIVE, tryDictList)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -996,8 +1044,9 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
mozilla::intl::LocaleParser::TryParse(sysLocaleStr, sysLoc);
|
||||
if (result.isOk() && loc.Canonicalize().isOk() &&
|
||||
loc.Language().Span() == sysLoc.Language().Span()) {
|
||||
BuildDictionaryList(sysLocaleStr, dictList,
|
||||
DICT_COMPARE_CASE_INSENSITIVE, tryDictList);
|
||||
if (BuildDictionaryList(sysLocaleStr, dictList,
|
||||
DICT_COMPARE_CASE_INSENSITIVE, tryDictList)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1014,17 +1063,24 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
|
||||
RefPtr<EditorSpellCheck> self = this;
|
||||
RefPtr<DictionaryFetcher> fetcher = aFetcher;
|
||||
RefPtr<GenericPromise> promise;
|
||||
|
||||
if (tryDictList.IsEmpty()) {
|
||||
// Proceed to priority 3 if the list of dictionaries is empty.
|
||||
promise = GenericPromise::CreateAndReject(NS_ERROR_INVALID_ARG, __func__);
|
||||
} else {
|
||||
promise = mSpellChecker->SetCurrentDictionaries(tryDictList);
|
||||
}
|
||||
|
||||
// If an error was thrown while setting the dictionary, just
|
||||
// fail silently so that the spellchecker dialog is allowed to come
|
||||
// up. The user can manually reset the language to their choice on
|
||||
// the dialog if it is wrong.
|
||||
mSpellChecker->SetCurrentDictionaryFromList(tryDictList)
|
||||
->Then(
|
||||
promise->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[self, fetcher]() { self->SetDictionarySucceeded(fetcher); },
|
||||
[prefDictionaries = prefDictionaries.Clone(),
|
||||
dictList = dictList.Clone(), self, fetcher]() {
|
||||
[prefDictionaries = prefDictionaries.Clone(), dictList = dictList.Clone(),
|
||||
self, fetcher]() {
|
||||
// Build tryDictList with dictionaries for priorities 4 through 7.
|
||||
// We'll use this list if there is no user preference or trying
|
||||
// the user preference fails.
|
||||
|
@ -1038,8 +1094,7 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
printf("***** Trying locale |%s|\n", appLocaleStr.get());
|
||||
#endif
|
||||
self->BuildDictionaryList(appLocaleStr, dictList,
|
||||
DICT_COMPARE_CASE_INSENSITIVE,
|
||||
tryDictList);
|
||||
DICT_COMPARE_CASE_INSENSITIVE, tryDictList);
|
||||
|
||||
// Priority 5:
|
||||
// If we have a current dictionary and we don't have no item in try
|
||||
|
@ -1084,8 +1139,8 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
// Priority 7:
|
||||
// If it does not work, pick the first one.
|
||||
if (!dictList.IsEmpty()) {
|
||||
self->BuildDictionaryList(dictList[0], dictList,
|
||||
DICT_NORMAL_COMPARE, tryDictList);
|
||||
self->BuildDictionaryList(dictList[0], dictList, DICT_NORMAL_COMPARE,
|
||||
tryDictList);
|
||||
#ifdef DEBUG_DICT
|
||||
printf("***** Trying first of list |%s|\n", dictList[0].get());
|
||||
#endif
|
||||
|
@ -1098,9 +1153,7 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
self->mSpellChecker->SetCurrentDictionaries(prefDictionaries)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[self, fetcher]() {
|
||||
self->SetDictionarySucceeded(fetcher);
|
||||
},
|
||||
[self, fetcher]() { self->SetDictionarySucceeded(fetcher); },
|
||||
// Priority 3 failed, we'll use the list we built of
|
||||
// priorities 4 to 7.
|
||||
[tryDictList = tryDictList.Clone(), self, fetcher]() {
|
||||
|
@ -1115,10 +1168,9 @@ void EditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher) {
|
|||
// We don't have a user preference, so we'll try the list we
|
||||
// built of priorities 4 to 7.
|
||||
self->mSpellChecker->SetCurrentDictionaryFromList(tryDictList)
|
||||
->Then(GetMainThreadSerialEventTarget(), __func__,
|
||||
[self, fetcher]() {
|
||||
self->SetDictionarySucceeded(fetcher);
|
||||
});
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[self, fetcher]() { self->SetDictionarySucceeded(fetcher); });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ class EditorSpellCheck final : public nsIEditorSpellCheck {
|
|||
// GetPersonalDictionary must be called to load them.
|
||||
nsTArray<nsString> mDictionaryList;
|
||||
|
||||
nsCString mPreferredLang;
|
||||
nsTArray<nsCString> mPreferredLangs;
|
||||
|
||||
uint32_t mTxtSrvFilterType;
|
||||
int32_t mSuggestedWordIndex;
|
||||
|
@ -79,7 +79,7 @@ class EditorSpellCheck final : public nsIEditorSpellCheck {
|
|||
|
||||
nsresult DeleteSuggestedWordList();
|
||||
|
||||
void BuildDictionaryList(const nsACString& aDictName,
|
||||
bool BuildDictionaryList(const nsACString& aDictName,
|
||||
const nsTArray<nsCString>& aDictList,
|
||||
enum dictCompare aCompareType,
|
||||
nsTArray<nsCString>& aOutList);
|
||||
|
|
|
@ -10,6 +10,7 @@ support-files =
|
|||
bug1200533_subframe.html
|
||||
bug1204147_subframe.html
|
||||
bug1204147_subframe2.html
|
||||
multiple_content_languages_subframe.html
|
||||
en-GB/en_GB.dic
|
||||
en-GB/en_GB.aff
|
||||
en-AU/en_AU.dic
|
||||
|
@ -42,6 +43,7 @@ skip-if = e10s
|
|||
[test_bug1497480.html]
|
||||
[test_bug1602526.html]
|
||||
[test_bug1761273.html]
|
||||
[test_multiple_content_languages.html]
|
||||
[test_spellcheck_after_edit.html]
|
||||
[test_spellcheck_after_pressing_navigation_key.html]
|
||||
[test_suggest.html]
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Language" content="en-US, en-GB, ko, en-CA">
|
||||
</head>
|
||||
<body>
|
||||
<textarea id="none">root en-US and en-GB</textarea>
|
||||
<textarea id="en-GB" lang="en-GB">root multiple, but element only en-GB</textarea>
|
||||
<textarea id="en-ZA-not-avail" lang="en-ZA">root multiple en, but element en-ZA (which is not installed)</textarea>
|
||||
<textarea id="en" lang="en">root multiple en, but element en</textarea>
|
||||
<textarea id="ko-not-avail" lang="ko">root multiple en, but element ko (which is not installed)</textarea>
|
||||
</body>
|
||||
</html>
|
|
@ -124,8 +124,9 @@ async function handlePopup() {
|
|||
|
||||
// Check that the English dictionary is loaded and that the spell check has worked.
|
||||
is(currentDictionaries.length, 2, "expected two dictionaries");
|
||||
is(currentDictionaries[0], "de-DE", "expected de-DE");
|
||||
is(currentDictionaries[1], "en-US", "expected en-US");
|
||||
let dictionaryArray = Array.from(currentDictionaries);
|
||||
ok(dictionaryArray.includes("de-DE"), "expected de-DE");
|
||||
ok(dictionaryArray.includes("en-US"), "expected en-US");
|
||||
is(getMisspelledWords(editor_de), "", "No misspelled words expected");
|
||||
|
||||
// Remove the fake de_DE dictionary again.
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for multiple Content-Language values</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<iframe id="content"></iframe>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody">
|
||||
|
||||
/** Test for multiple Content-Language values **/
|
||||
/** Visit the elements defined above and check the dictionaries we got **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var content = document.getElementById("content");
|
||||
|
||||
var tests = [
|
||||
// text area, value of spellchecker.dictionary, result.
|
||||
// Result: Document language.
|
||||
[ "none", "", ["en-US", "en-GB"] ],
|
||||
|
||||
// Result: Element language.
|
||||
[ "en-GB", "", ["en-GB"] ],
|
||||
// Result: Random en-* or en-US (if application locale is en-US).
|
||||
[ "en-ZA-not-avail", "", ["*"] ],
|
||||
[ "en", "", ["*"] ],
|
||||
// Result: Locale.
|
||||
[ "ko-not-avail", "", ["en-US"] ],
|
||||
|
||||
// Result: Document language, plus preference value in all cases.
|
||||
[ "none", "en-AU", ["en-US", "en-GB", "en-AU"] ],
|
||||
[ "en-ZA-not-avail", "en-AU", ["en-AU"] ],
|
||||
[ "ko-not-avail", "en-AU", ["en-AU"] ],
|
||||
|
||||
// Result: Document language, plus first matching preference language.
|
||||
[ "none", "en-AU,en-US", ["en-US", "en-GB", "en-AU"] ],
|
||||
// Result: First matching preference language.
|
||||
[ "en-ZA-not-avail", "en-AU,en-US", ["en-AU"] ],
|
||||
// Result: Fall back to preference languages.
|
||||
[ "ko-not-avail", "en-AU,en-US", ["en-AU", "en-US"] ],
|
||||
|
||||
// Result: Random en-*.
|
||||
[ "en-ZA-not-avail", "de-DE", ["*"] ],
|
||||
// Result: Preference value.
|
||||
[ "ko-not-avail", "de-DE", ["de-DE"] ],
|
||||
];
|
||||
|
||||
var loadCount = 0;
|
||||
var retrying = false;
|
||||
var script;
|
||||
|
||||
var loadListener = async function(evt) {
|
||||
if (loadCount == 0) {
|
||||
/* eslint-env mozilla/frame-script */
|
||||
script = SpecialPowers.loadChromeScript(function() {
|
||||
// eslint-disable-next-line mozilla/use-services
|
||||
var dir = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties)
|
||||
.get("CurWorkD", Ci.nsIFile);
|
||||
dir.append("tests");
|
||||
dir.append("editor");
|
||||
dir.append("spellchecker");
|
||||
dir.append("tests");
|
||||
|
||||
var hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
|
||||
.getService(Ci.mozISpellCheckingEngine);
|
||||
|
||||
// Install en-GB, en-AU and de-DE dictionaries.
|
||||
var en_GB = dir.clone();
|
||||
var en_AU = dir.clone();
|
||||
var de_DE = dir.clone();
|
||||
en_GB.append("en-GB");
|
||||
en_AU.append("en-AU");
|
||||
de_DE.append("de-DE");
|
||||
hunspell.addDirectory(en_GB);
|
||||
hunspell.addDirectory(en_AU);
|
||||
hunspell.addDirectory(de_DE);
|
||||
|
||||
addMessageListener("check-existence",
|
||||
() => [en_GB.exists(), en_AU.exists(),
|
||||
de_DE.exists()]);
|
||||
addMessageListener("destroy", () => {
|
||||
hunspell.removeDirectory(en_GB);
|
||||
hunspell.removeDirectory(en_AU);
|
||||
hunspell.removeDirectory(de_DE);
|
||||
});
|
||||
});
|
||||
var existenceChecks = await script.sendQuery("check-existence");
|
||||
is(existenceChecks[0], true, "true expected (en-GB directory should exist)");
|
||||
is(existenceChecks[1], true, "true expected (en-AU directory should exist)");
|
||||
is(existenceChecks[2], true, "true expected (de-DE directory should exist)");
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({set: [["spellchecker.dictionary", tests[loadCount][1]]]},
|
||||
function() { continueTest(evt); });
|
||||
};
|
||||
|
||||
function continueTest(evt) {
|
||||
var doc = evt.target.contentDocument;
|
||||
var elem = doc.getElementById(tests[loadCount][0]);
|
||||
var editor = SpecialPowers.wrap(elem).editor;
|
||||
editor.setSpellcheckUserOverride(true);
|
||||
var inlineSpellChecker = editor.getInlineSpellChecker(true);
|
||||
const is_en_US = SpecialPowers.Services.locale.appLocaleAsBCP47 == "en-US";
|
||||
|
||||
const { onSpellCheck } = SpecialPowers.ChromeUtils.import(
|
||||
"resource://testing-common/AsyncSpellCheckTestHelper.jsm"
|
||||
);
|
||||
onSpellCheck(elem, async function() {
|
||||
var spellchecker = inlineSpellChecker.spellChecker;
|
||||
let currentDictionaries;
|
||||
try {
|
||||
currentDictionaries = spellchecker.getCurrentDictionaries();
|
||||
} catch (e) {}
|
||||
|
||||
if (!currentDictionaries && !retrying) {
|
||||
// It's possible for an asynchronous font-list update to cause a reflow
|
||||
// that disrupts the async spell-check and results in not getting a
|
||||
// current dictionary here; if that happens, we retry the same testcase
|
||||
// by reloading the iframe without bumping loadCount.
|
||||
info(`No current dictionary: retrying testcase ${loadCount}`);
|
||||
retrying = true;
|
||||
} else {
|
||||
let expectedDictionaries = tests[loadCount][2];
|
||||
let dictionaryArray = Array.from(currentDictionaries);
|
||||
is(
|
||||
dictionaryArray.length,
|
||||
expectedDictionaries.length,
|
||||
"Expected matching dictionary count"
|
||||
);
|
||||
if (expectedDictionaries[0] != "*") {
|
||||
ok(
|
||||
dictionaryArray.every(dict => expectedDictionaries.includes(dict)),
|
||||
"active dictionaries should match expectation"
|
||||
);
|
||||
} else if (is_en_US && tests[loadCount][0].startsWith("en")) {
|
||||
// Current application locale is en-US and content lang is en or
|
||||
// en-unknown, so we should use en-US dictionary as default.
|
||||
is(
|
||||
dictionaryArray[0],
|
||||
"en-US",
|
||||
"expected en-US that is application locale"
|
||||
);
|
||||
} else {
|
||||
let dict = dictionaryArray[0];
|
||||
var gotEn = (dict == "en-GB" || dict == "en-AU" || dict == "en-US");
|
||||
is(gotEn, true, "expected en-AU or en-GB or en-US");
|
||||
}
|
||||
|
||||
loadCount++;
|
||||
retrying = false;
|
||||
}
|
||||
|
||||
if (loadCount < tests.length) {
|
||||
// Load the iframe again.
|
||||
content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/multiple_content_languages_subframe.html?firstload=false";
|
||||
} else {
|
||||
// Remove the fake dictionaries again, since it's otherwise picked up by later tests.
|
||||
await script.sendQuery("destroy");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
content.addEventListener("load", loadListener);
|
||||
|
||||
content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/multiple_content_languages_subframe.html?firstload=true";
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1773,7 +1773,7 @@ nsEventStatus AsyncPanZoomController::OnScaleEnd(
|
|||
}
|
||||
// Along with clearing the overscroll, we also want to snap to the nearest
|
||||
// snap point as appropriate.
|
||||
ScrollSnap();
|
||||
ScrollSnap(ScrollSnapFlags::IntendedEndPosition);
|
||||
} else {
|
||||
// when zoom is not allowed
|
||||
EndTouch(aEvent.mTimeStamp);
|
||||
|
@ -1975,8 +1975,8 @@ nsEventStatus AsyncPanZoomController::OnKeyboard(const KeyboardInput& aEvent) {
|
|||
CSSPoint destination = GetKeyboardDestination(aEvent.mAction);
|
||||
ScrollOrigin scrollOrigin =
|
||||
SmoothScrollAnimation::GetScrollOriginForAction(aEvent.mAction.mType);
|
||||
bool scrollSnapped =
|
||||
MaybeAdjustDestinationForScrollSnapping(aEvent, destination);
|
||||
bool scrollSnapped = MaybeAdjustDestinationForScrollSnapping(
|
||||
aEvent, destination, GetScrollSnapFlagsForKeyboardAction(aEvent.mAction));
|
||||
ScrollMode scrollMode = apz::GetScrollModeForOrigin(scrollOrigin);
|
||||
|
||||
RecordScrollPayload(aEvent.mTimeStamp);
|
||||
|
@ -2127,6 +2127,21 @@ CSSPoint AsyncPanZoomController::GetKeyboardDestination(
|
|||
return scrollDestination;
|
||||
}
|
||||
|
||||
ScrollSnapFlags AsyncPanZoomController::GetScrollSnapFlagsForKeyboardAction(
|
||||
const KeyboardScrollAction& aAction) const {
|
||||
switch (aAction.mType) {
|
||||
case KeyboardScrollAction::eScrollCharacter:
|
||||
case KeyboardScrollAction::eScrollLine:
|
||||
return ScrollSnapFlags::IntendedDirection;
|
||||
case KeyboardScrollAction::eScrollPage:
|
||||
return ScrollSnapFlags::IntendedDirection |
|
||||
ScrollSnapFlags::IntendedEndPosition;
|
||||
case KeyboardScrollAction::eScrollComplete:
|
||||
return ScrollSnapFlags::IntendedEndPosition;
|
||||
}
|
||||
return ScrollSnapFlags::Disabled;
|
||||
}
|
||||
|
||||
ParentLayerPoint AsyncPanZoomController::GetDeltaForEvent(
|
||||
const InputData& aEvent) const {
|
||||
ParentLayerPoint delta;
|
||||
|
@ -3945,7 +3960,7 @@ void AsyncPanZoomController::CancelAnimation(CancelAnimationFlags aFlags) {
|
|||
// Similar to relieving overscroll, we also need to snap to any snap points
|
||||
// if appropriate.
|
||||
if (aFlags & CancelAnimationFlags::ScrollSnap) {
|
||||
ScrollSnap();
|
||||
ScrollSnap(ScrollSnapFlags::IntendedEndPosition);
|
||||
}
|
||||
if (repaint) {
|
||||
RequestContentRepaint();
|
||||
|
@ -4230,7 +4245,8 @@ bool AsyncPanZoomController::SnapBackIfOverscrolled() {
|
|||
// main thread to snap to any nearby snap points, assuming we haven't already
|
||||
// done so when we started this fling
|
||||
if (mState != FLING) {
|
||||
ScrollSnap();
|
||||
ScrollSnap(ScrollSnapFlags::IntendedEndPosition |
|
||||
ScrollSnapFlags::IntendedDirection);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -5993,13 +6009,14 @@ void AsyncPanZoomController::SetTestAsyncZoom(
|
|||
}
|
||||
|
||||
Maybe<CSSPoint> AsyncPanZoomController::FindSnapPointNear(
|
||||
const CSSPoint& aDestination, ScrollUnit aUnit) {
|
||||
const CSSPoint& aDestination, ScrollUnit aUnit,
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
mRecursiveMutex.AssertCurrentThreadIn();
|
||||
APZC_LOG("%p scroll snapping near %s\n", this,
|
||||
ToString(aDestination).c_str());
|
||||
CSSRect scrollRange = Metrics().CalculateScrollRange();
|
||||
if (Maybe<nsPoint> snapPoint = ScrollSnapUtils::GetSnapPointForDestination(
|
||||
mScrollMetadata.GetSnapInfo(), aUnit,
|
||||
mScrollMetadata.GetSnapInfo(), aUnit, aSnapFlags,
|
||||
CSSRect::ToAppUnits(scrollRange),
|
||||
CSSPoint::ToAppUnits(Metrics().GetVisualScrollOffset()),
|
||||
CSSPoint::ToAppUnits(aDestination))) {
|
||||
|
@ -6013,9 +6030,10 @@ Maybe<CSSPoint> AsyncPanZoomController::FindSnapPointNear(
|
|||
return Nothing();
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) {
|
||||
if (Maybe<CSSPoint> snapPoint =
|
||||
FindSnapPointNear(aDestination, ScrollUnit::DEVICE_PIXELS)) {
|
||||
void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination,
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
if (Maybe<CSSPoint> snapPoint = FindSnapPointNear(
|
||||
aDestination, ScrollUnit::DEVICE_PIXELS, aSnapFlags)) {
|
||||
if (*snapPoint != Metrics().GetVisualScrollOffset()) {
|
||||
APZC_LOG("%p smooth scrolling to snap point %s\n", this,
|
||||
ToString(*snapPoint).c_str());
|
||||
|
@ -6024,9 +6042,9 @@ void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) {
|
|||
}
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::ScrollSnap() {
|
||||
void AsyncPanZoomController::ScrollSnap(ScrollSnapFlags aSnapFlags) {
|
||||
RecursiveMutexAutoLock lock(mRecursiveMutex);
|
||||
ScrollSnapNear(Metrics().GetVisualScrollOffset());
|
||||
ScrollSnapNear(Metrics().GetVisualScrollOffset(), aSnapFlags);
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::ScrollSnapToDestination() {
|
||||
|
@ -6054,7 +6072,11 @@ void AsyncPanZoomController::ScrollSnapToDestination() {
|
|||
}
|
||||
|
||||
CSSPoint startPosition = Metrics().GetVisualScrollOffset();
|
||||
if (MaybeAdjustDeltaForScrollSnapping(ScrollUnit::DEVICE_PIXELS,
|
||||
ScrollSnapFlags snapFlags = ScrollSnapFlags::IntendedEndPosition;
|
||||
if (predictedDelta != ParentLayerPoint()) {
|
||||
snapFlags |= ScrollSnapFlags::IntendedDirection;
|
||||
}
|
||||
if (MaybeAdjustDeltaForScrollSnapping(ScrollUnit::DEVICE_PIXELS, snapFlags,
|
||||
predictedDelta, startPosition)) {
|
||||
APZC_LOG(
|
||||
"%p fling snapping. friction: %f velocity: %f, %f "
|
||||
|
@ -6070,7 +6092,8 @@ void AsyncPanZoomController::ScrollSnapToDestination() {
|
|||
}
|
||||
|
||||
bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnapping(
|
||||
ScrollUnit aUnit, ParentLayerPoint& aDelta, CSSPoint& aStartPosition) {
|
||||
ScrollUnit aUnit, ScrollSnapFlags aSnapFlags, ParentLayerPoint& aDelta,
|
||||
CSSPoint& aStartPosition) {
|
||||
RecursiveMutexAutoLock lock(mRecursiveMutex);
|
||||
CSSToParentLayerScale zoom = Metrics().GetZoom();
|
||||
if (zoom == CSSToParentLayerScale(0)) {
|
||||
|
@ -6079,7 +6102,8 @@ bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnapping(
|
|||
CSSPoint destination = Metrics().CalculateScrollRange().ClampPoint(
|
||||
aStartPosition + (aDelta / zoom));
|
||||
|
||||
if (Maybe<CSSPoint> snapPoint = FindSnapPointNear(destination, aUnit)) {
|
||||
if (Maybe<CSSPoint> snapPoint =
|
||||
FindSnapPointNear(destination, aUnit, aSnapFlags)) {
|
||||
aDelta = (*snapPoint - aStartPosition) * zoom;
|
||||
aStartPosition = *snapPoint;
|
||||
return true;
|
||||
|
@ -6096,17 +6120,32 @@ bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnappingOnWheelInput(
|
|||
return false;
|
||||
}
|
||||
|
||||
// Note that this MaybeAdjustDeltaForScrollSnappingOnWheelInput also gets
|
||||
// called for pan gestures at least on older Mac and Windows. In such cases
|
||||
// `aEvent.mDeltaType` is `SCROLLDELTA_PIXEL` which should be filtered out by
|
||||
// the above `if` block, so we assume all incoming `aEvent` are purely wheel
|
||||
// events, thus we basically use `IntendedDirection` here.
|
||||
// If we want to change the behavior, i.e. we want to do scroll snap for
|
||||
// such cases as well, we need to use `IntendedEndPoint`.
|
||||
ScrollSnapFlags snapFlags = ScrollSnapFlags::IntendedDirection;
|
||||
if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_PAGE) {
|
||||
// On Windows there are a couple of cases where scroll events happen with
|
||||
// SCROLLDELTA_PAGE, in such case we consider it's a page scroll.
|
||||
snapFlags |= ScrollSnapFlags::IntendedEndPosition;
|
||||
}
|
||||
return MaybeAdjustDeltaForScrollSnapping(
|
||||
ScrollWheelInput::ScrollUnitForDeltaType(aEvent.mDeltaType), aDelta,
|
||||
aStartPosition);
|
||||
ScrollWheelInput::ScrollUnitForDeltaType(aEvent.mDeltaType),
|
||||
ScrollSnapFlags::IntendedDirection, aDelta, aStartPosition);
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::MaybeAdjustDestinationForScrollSnapping(
|
||||
const KeyboardInput& aEvent, CSSPoint& aDestination) {
|
||||
const KeyboardInput& aEvent, CSSPoint& aDestination,
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
RecursiveMutexAutoLock lock(mRecursiveMutex);
|
||||
ScrollUnit unit = KeyboardScrollAction::GetScrollUnit(aEvent.mAction.mType);
|
||||
|
||||
if (Maybe<CSSPoint> snapPoint = FindSnapPointNear(aDestination, unit)) {
|
||||
if (Maybe<CSSPoint> snapPoint =
|
||||
FindSnapPointNear(aDestination, unit, aSnapFlags)) {
|
||||
aDestination = *snapPoint;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -689,6 +689,11 @@ class AsyncPanZoomController {
|
|||
|
||||
CSSPoint GetKeyboardDestination(const KeyboardScrollAction& aAction) const;
|
||||
|
||||
// Returns the corresponding ScrollSnapFlags for the given |aAction|.
|
||||
// See https://drafts.csswg.org/css-scroll-snap/#scroll-types
|
||||
ScrollSnapFlags GetScrollSnapFlagsForKeyboardAction(
|
||||
const KeyboardScrollAction& aAction) const;
|
||||
|
||||
/**
|
||||
* Helper methods for long press gestures.
|
||||
*/
|
||||
|
@ -1785,6 +1790,7 @@ class AsyncPanZoomController {
|
|||
// GetSnapPointForDestination).
|
||||
// Returns true iff. a target snap point was found.
|
||||
bool MaybeAdjustDeltaForScrollSnapping(ScrollUnit aUnit,
|
||||
ScrollSnapFlags aFlags,
|
||||
ParentLayerPoint& aDelta,
|
||||
CSSPoint& aStartPosition);
|
||||
|
||||
|
@ -1795,17 +1801,18 @@ class AsyncPanZoomController {
|
|||
CSSPoint& aStartPosition);
|
||||
|
||||
bool MaybeAdjustDestinationForScrollSnapping(const KeyboardInput& aEvent,
|
||||
CSSPoint& aDestination);
|
||||
CSSPoint& aDestination,
|
||||
ScrollSnapFlags aSnapFlags);
|
||||
|
||||
// Snap to a snap position nearby the current scroll position, if appropriate.
|
||||
void ScrollSnap();
|
||||
void ScrollSnap(ScrollSnapFlags aSnapFlags);
|
||||
|
||||
// Snap to a snap position nearby the destination predicted based on the
|
||||
// current velocity, if appropriate.
|
||||
void ScrollSnapToDestination();
|
||||
|
||||
// Snap to a snap position nearby the provided destination, if appropriate.
|
||||
void ScrollSnapNear(const CSSPoint& aDestination);
|
||||
void ScrollSnapNear(const CSSPoint& aDestination, ScrollSnapFlags aSnapFlags);
|
||||
|
||||
// Find a snap point near |aDestination| that we should snap to.
|
||||
// Returns the snap point if one was found, or an empty Maybe otherwise.
|
||||
|
@ -1813,7 +1820,8 @@ class AsyncPanZoomController {
|
|||
// GetSnapPointForDestination). It should generally be determined by the
|
||||
// type of event that's triggering the scroll.
|
||||
Maybe<CSSPoint> FindSnapPointNear(const CSSPoint& aDestination,
|
||||
ScrollUnit aUnit);
|
||||
ScrollUnit aUnit,
|
||||
ScrollSnapFlags aSnapFlags);
|
||||
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream& aOut, const AsyncPanZoomController::PanZoomState& aState);
|
||||
|
|
|
@ -59,9 +59,11 @@ class OverscrollAnimation : public AsyncPanZoomAnimation {
|
|||
// done in a deferred task, otherwise the state change to NOTHING caused
|
||||
// by the overscroll animation ending would clobber a possible state
|
||||
// change to SMOOTH_SCROLL in ScrollSnap().
|
||||
mDeferredTasks.AppendElement(
|
||||
NewRunnableMethod("layers::AsyncPanZoomController::ScrollSnap",
|
||||
&mApzc, &AsyncPanZoomController::ScrollSnap));
|
||||
mDeferredTasks.AppendElement(NewRunnableMethod<ScrollSnapFlags>(
|
||||
"layers::AsyncPanZoomController::ScrollSnap", &mApzc,
|
||||
&AsyncPanZoomController::ScrollSnap,
|
||||
ScrollSnapFlags::IntendedDirection |
|
||||
ScrollSnapFlags::IntendedEndPosition));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -326,6 +326,11 @@ bool gfxFontEntry::TryGetSVGData(const gfxFont* aFont) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// We don't support SVG-in-OT glyphs in offscreen-canvas worker threads.
|
||||
if (!NS_IsMainThread()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mSVGInitialized) {
|
||||
// If UnitsPerEm is not known/valid, we can't use SVG glyphs
|
||||
if (UnitsPerEm() == kInvalidUPEM) {
|
||||
|
|
|
@ -1708,7 +1708,7 @@ class MVariadicControlInstruction : public MVariadicT<MControlInstruction> {
|
|||
// Jump to the start of another basic block.
|
||||
class MGoto : public MAryControlInstruction<0, 1>, public NoTypePolicy::Data {
|
||||
explicit MGoto(MBasicBlock* target) : MAryControlInstruction(classOpcode) {
|
||||
setSuccessor(0, target);
|
||||
setSuccessor(TargetIndex, target);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -1719,9 +1719,9 @@ class MGoto : public MAryControlInstruction<0, 1>, public NoTypePolicy::Data {
|
|||
// Variant that may patch the target later.
|
||||
static MGoto* New(TempAllocator& alloc);
|
||||
|
||||
static const size_t TargetIndex = 0;
|
||||
static constexpr size_t TargetIndex = 0;
|
||||
|
||||
MBasicBlock* target() { return getSuccessor(0); }
|
||||
MBasicBlock* target() { return getSuccessor(TargetIndex); }
|
||||
AliasSet getAliasSet() const override { return AliasSet::None(); }
|
||||
};
|
||||
|
||||
|
@ -1731,8 +1731,8 @@ class MTest : public MAryControlInstruction<1, 2>, public TestPolicy::Data {
|
|||
MTest(MDefinition* ins, MBasicBlock* trueBranch, MBasicBlock* falseBranch)
|
||||
: MAryControlInstruction(classOpcode) {
|
||||
initOperand(0, ins);
|
||||
setSuccessor(0, trueBranch);
|
||||
setSuccessor(1, falseBranch);
|
||||
setSuccessor(TrueBranchIndex, trueBranch);
|
||||
setSuccessor(FalseBranchIndex, falseBranch);
|
||||
}
|
||||
|
||||
// Variant which may patch the ifTrue branch later.
|
||||
|
@ -1751,10 +1751,11 @@ class MTest : public MAryControlInstruction<1, 2>, public TestPolicy::Data {
|
|||
observedTypes_ = observed;
|
||||
}
|
||||
|
||||
static const size_t TrueBranchIndex = 0;
|
||||
static constexpr size_t TrueBranchIndex = 0;
|
||||
static constexpr size_t FalseBranchIndex = 1;
|
||||
|
||||
MBasicBlock* ifTrue() const { return getSuccessor(0); }
|
||||
MBasicBlock* ifFalse() const { return getSuccessor(1); }
|
||||
MBasicBlock* ifTrue() const { return getSuccessor(TrueBranchIndex); }
|
||||
MBasicBlock* ifFalse() const { return getSuccessor(FalseBranchIndex); }
|
||||
MBasicBlock* branchSuccessor(BranchDirection dir) const {
|
||||
return (dir == TRUE_BRANCH) ? ifTrue() : ifFalse();
|
||||
}
|
||||
|
|
|
@ -266,18 +266,21 @@ bool WarpBuilder::startNewOsrPreHeaderBlock(BytecodeLocation loopHead) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WarpBuilder::addPendingEdge(const PendingEdge& edge,
|
||||
BytecodeLocation target) {
|
||||
bool WarpBuilder::addPendingEdge(BytecodeLocation target, MBasicBlock* block,
|
||||
uint32_t successor, uint32_t numToPop) {
|
||||
MOZ_ASSERT(successor < block->lastIns()->numSuccessors());
|
||||
MOZ_ASSERT(numToPop <= block->stackDepth());
|
||||
|
||||
jsbytecode* targetPC = target.toRawBytecode();
|
||||
PendingEdgesMap::AddPtr p = pendingEdges_.lookupForAdd(targetPC);
|
||||
if (p) {
|
||||
return p->value().append(edge);
|
||||
return p->value().emplaceBack(block, successor, numToPop);
|
||||
}
|
||||
|
||||
PendingEdges edges;
|
||||
static_assert(PendingEdges::InlineLength >= 1,
|
||||
"Appending one element should be infallible");
|
||||
MOZ_ALWAYS_TRUE(edges.append(edge));
|
||||
MOZ_ALWAYS_TRUE(edges.emplaceBack(block, successor, numToPop));
|
||||
|
||||
return pendingEdges_.add(p, targetPC, std::move(edges));
|
||||
}
|
||||
|
@ -1080,8 +1083,6 @@ bool WarpBuilder::build_JumpTarget(BytecodeLocation loc) {
|
|||
|
||||
MOZ_ASSERT(!edges.empty());
|
||||
|
||||
MBasicBlock* joinBlock = nullptr;
|
||||
|
||||
// Create join block if there's fall-through from the previous bytecode op.
|
||||
if (!hasTerminatedBlock()) {
|
||||
MBasicBlock* pred = current;
|
||||
|
@ -1089,75 +1090,29 @@ bool WarpBuilder::build_JumpTarget(BytecodeLocation loc) {
|
|||
return false;
|
||||
}
|
||||
pred->end(MGoto::New(alloc(), current));
|
||||
joinBlock = current;
|
||||
setTerminatedBlock();
|
||||
}
|
||||
|
||||
auto addEdge = [&](MBasicBlock* pred, size_t numToPop) -> bool {
|
||||
if (joinBlock) {
|
||||
MOZ_ASSERT(pred->stackDepth() - numToPop == joinBlock->stackDepth());
|
||||
return joinBlock->addPredecessorPopN(alloc(), pred, numToPop);
|
||||
}
|
||||
if (!startNewBlock(pred, loc, numToPop)) {
|
||||
return false;
|
||||
}
|
||||
joinBlock = current;
|
||||
setTerminatedBlock();
|
||||
return true;
|
||||
};
|
||||
|
||||
for (const PendingEdge& edge : edges) {
|
||||
MBasicBlock* source = edge.block();
|
||||
MControlInstruction* lastIns = source->lastIns();
|
||||
switch (edge.kind()) {
|
||||
case PendingEdge::Kind::TestTrue: {
|
||||
// JSOp::Case must pop the value when branching to the true-target.
|
||||
const size_t numToPop = (edge.testOp() == JSOp::Case) ? 1 : 0;
|
||||
uint32_t numToPop = edge.numToPop();
|
||||
|
||||
const size_t successor = 0; // true-branch
|
||||
if (!addEdge(source, numToPop)) {
|
||||
if (hasTerminatedBlock()) {
|
||||
if (!startNewBlock(source, loc, numToPop)) {
|
||||
return false;
|
||||
}
|
||||
lastIns->toTest()->initSuccessor(successor, joinBlock);
|
||||
continue;
|
||||
}
|
||||
|
||||
case PendingEdge::Kind::TestFalse: {
|
||||
const size_t numToPop = 0;
|
||||
const size_t successor = 1; // false-branch
|
||||
if (!addEdge(source, numToPop)) {
|
||||
} else {
|
||||
MOZ_ASSERT(source->stackDepth() - numToPop == current->stackDepth());
|
||||
if (!current->addPredecessorPopN(alloc(), source, numToPop)) {
|
||||
return false;
|
||||
}
|
||||
lastIns->toTest()->initSuccessor(successor, joinBlock);
|
||||
continue;
|
||||
}
|
||||
|
||||
case PendingEdge::Kind::Goto:
|
||||
if (!addEdge(source, /* numToPop = */ 0)) {
|
||||
return false;
|
||||
}
|
||||
lastIns->toGoto()->initSuccessor(0, joinBlock);
|
||||
continue;
|
||||
|
||||
case PendingEdge::Kind::TableSwitch: {
|
||||
if (!addEdge(source, /* numToPop = */ 0)) {
|
||||
return false;
|
||||
}
|
||||
uint32_t successor = edge.tableSwitchSuccessor();
|
||||
lastIns->toTableSwitch()->initSuccessor(successor, joinBlock);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
MOZ_CRASH("Invalid kind");
|
||||
MOZ_ASSERT(source->lastIns()->isTest() || source->lastIns()->isGoto() ||
|
||||
source->lastIns()->isTableSwitch());
|
||||
source->lastIns()->initSuccessor(edge.successor(), current);
|
||||
}
|
||||
|
||||
// Start traversing the join block. Make sure it comes after predecessor
|
||||
// blocks created by createEmptyBlockForTest.
|
||||
MOZ_ASSERT(hasTerminatedBlock());
|
||||
MOZ_ASSERT(joinBlock);
|
||||
graph().moveBlockToEnd(joinBlock);
|
||||
current = joinBlock;
|
||||
|
||||
MOZ_ASSERT(!hasTerminatedBlock());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1295,10 +1250,7 @@ bool WarpBuilder::buildTestOp(BytecodeLocation loc) {
|
|||
|
||||
// JSOp::And and JSOp::Or leave the top stack value unchanged. The
|
||||
// top stack value may have been converted to bool by a transpiled
|
||||
// ToBool IC, so we push the original value. Also note that
|
||||
// JSOp::Case must pop a second value on the true-branch (the input
|
||||
// to the switch-statement). This conditional pop happens in
|
||||
// build_JumpTarget.
|
||||
// ToBool IC, so we push the original value.
|
||||
bool mustKeepCondition = (op == JSOp::And || op == JSOp::Or);
|
||||
if (mustKeepCondition) {
|
||||
current->push(originalValue);
|
||||
|
@ -1315,10 +1267,14 @@ bool WarpBuilder::buildTestOp(BytecodeLocation loc) {
|
|||
/* ifFalse = */ nullptr);
|
||||
current->end(test);
|
||||
|
||||
if (!addPendingEdge(PendingEdge::NewTestTrue(current, op), target1)) {
|
||||
// JSOp::Case must pop a second value on the true-branch (the input to the
|
||||
// switch-statement).
|
||||
uint32_t numToPop = (loc.getOp() == JSOp::Case) ? 1 : 0;
|
||||
|
||||
if (!addPendingEdge(target1, current, MTest::TrueBranchIndex, numToPop)) {
|
||||
return false;
|
||||
}
|
||||
if (!addPendingEdge(PendingEdge::NewTestFalse(current, op), target2)) {
|
||||
if (!addPendingEdge(target2, current, MTest::FalseBranchIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1331,8 +1287,7 @@ bool WarpBuilder::buildTestOp(BytecodeLocation loc) {
|
|||
}
|
||||
|
||||
bool WarpBuilder::buildTestBackedge(BytecodeLocation loc) {
|
||||
JSOp op = loc.getOp();
|
||||
MOZ_ASSERT(op == JSOp::JumpIfTrue);
|
||||
MOZ_ASSERT(loc.is(JSOp::JumpIfTrue));
|
||||
MOZ_ASSERT(loopDepth() > 0);
|
||||
|
||||
MDefinition* value = current->pop();
|
||||
|
@ -1358,7 +1313,7 @@ bool WarpBuilder::buildTestBackedge(BytecodeLocation loc) {
|
|||
test->setObservedTypes(typesSnapshot->list());
|
||||
}
|
||||
|
||||
if (!addPendingEdge(PendingEdge::NewTestFalse(pred, op), successor)) {
|
||||
if (!addPendingEdge(successor, pred, MTest::FalseBranchIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1397,12 +1352,10 @@ bool WarpBuilder::build_Coalesce(BytecodeLocation loc) {
|
|||
current->end(MTest::New(alloc(), isNullOrUndefined, /* ifTrue = */ nullptr,
|
||||
/* ifFalse = */ nullptr));
|
||||
|
||||
if (!addPendingEdge(PendingEdge::NewTestTrue(current, JSOp::Coalesce),
|
||||
target1)) {
|
||||
if (!addPendingEdge(target1, current, MTest::TrueBranchIndex)) {
|
||||
return false;
|
||||
}
|
||||
if (!addPendingEdge(PendingEdge::NewTestFalse(current, JSOp::Coalesce),
|
||||
target2)) {
|
||||
if (!addPendingEdge(target2, current, MTest::FalseBranchIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1427,7 +1380,7 @@ bool WarpBuilder::buildBackedge() {
|
|||
bool WarpBuilder::buildForwardGoto(BytecodeLocation target) {
|
||||
current->end(MGoto::New(alloc(), nullptr));
|
||||
|
||||
if (!addPendingEdge(PendingEdge::NewGoto(current), target)) {
|
||||
if (!addPendingEdge(target, current, MGoto::TargetIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2862,8 +2815,7 @@ bool WarpBuilder::build_TableSwitch(BytecodeLocation loc) {
|
|||
if (!tableswitch->addDefault(nullptr, &index)) {
|
||||
return false;
|
||||
}
|
||||
if (!addPendingEdge(PendingEdge::NewTableSwitch(current, index),
|
||||
defaultLoc)) {
|
||||
if (!addPendingEdge(defaultLoc, current, index)) {
|
||||
return false;
|
||||
}
|
||||
if (!targetToSuccessor.put(defaultOffset, index)) {
|
||||
|
@ -2883,8 +2835,7 @@ bool WarpBuilder::build_TableSwitch(BytecodeLocation loc) {
|
|||
if (!tableswitch->addSuccessor(nullptr, &index)) {
|
||||
return false;
|
||||
}
|
||||
if (!addPendingEdge(PendingEdge::NewTableSwitch(current, index),
|
||||
caseLoc)) {
|
||||
if (!addPendingEdge(caseLoc, current, index)) {
|
||||
return false;
|
||||
}
|
||||
if (!targetToSuccessor.add(p, caseOffset, index)) {
|
||||
|
|
|
@ -112,68 +112,27 @@ enum class CacheKind : uint8_t;
|
|||
// Baseline does not attempt OSR into Warp at loops that are only reachable via
|
||||
// catch/finally blocks.
|
||||
//
|
||||
// Finally-blocks are currently not supported by WarpBuilder.
|
||||
// Finally-blocks are compiled by WarpBuilder, but when we have to enter a
|
||||
// finally-block from the exception handler, we bail out to the Baseline
|
||||
// Interpreter.
|
||||
|
||||
// PendingEdge is used whenever a block is terminated with a forward branch in
|
||||
// the bytecode. When we reach the jump target we use this information to link
|
||||
// the block to the jump target's block.
|
||||
class PendingEdge {
|
||||
public:
|
||||
enum class Kind : uint8_t {
|
||||
// MTest true-successor.
|
||||
TestTrue,
|
||||
|
||||
// MTest false-successor.
|
||||
TestFalse,
|
||||
|
||||
// MGoto successor.
|
||||
Goto,
|
||||
|
||||
// MTableSwitch successor.
|
||||
TableSwitch,
|
||||
};
|
||||
|
||||
private:
|
||||
MBasicBlock* block_;
|
||||
Kind kind_;
|
||||
union {
|
||||
JSOp testOp_;
|
||||
uint32_t tableSwitchSuccessor_;
|
||||
};
|
||||
|
||||
PendingEdge(MBasicBlock* block, Kind kind) : block_(block), kind_(kind) {}
|
||||
uint32_t successor_;
|
||||
uint8_t numToPop_;
|
||||
|
||||
public:
|
||||
static PendingEdge NewTestTrue(MBasicBlock* block, JSOp op) {
|
||||
PendingEdge edge(block, Kind::TestTrue);
|
||||
edge.testOp_ = op;
|
||||
return edge;
|
||||
}
|
||||
static PendingEdge NewTestFalse(MBasicBlock* block, JSOp op) {
|
||||
PendingEdge edge(block, Kind::TestFalse);
|
||||
edge.testOp_ = op;
|
||||
return edge;
|
||||
}
|
||||
static PendingEdge NewGoto(MBasicBlock* block) {
|
||||
return PendingEdge(block, Kind::Goto);
|
||||
}
|
||||
static PendingEdge NewTableSwitch(MBasicBlock* block, uint32_t successor) {
|
||||
PendingEdge edge(block, Kind::TableSwitch);
|
||||
edge.tableSwitchSuccessor_ = successor;
|
||||
return edge;
|
||||
PendingEdge(MBasicBlock* block, uint32_t successor, uint32_t numToPop)
|
||||
: block_(block), successor_(successor), numToPop_(numToPop) {
|
||||
MOZ_ASSERT(numToPop_ == numToPop, "value must fit in field");
|
||||
}
|
||||
|
||||
MBasicBlock* block() const { return block_; }
|
||||
Kind kind() const { return kind_; }
|
||||
|
||||
JSOp testOp() const {
|
||||
MOZ_ASSERT(kind_ == Kind::TestTrue || kind_ == Kind::TestFalse);
|
||||
return testOp_;
|
||||
}
|
||||
uint32_t tableSwitchSuccessor() const {
|
||||
MOZ_ASSERT(kind_ == Kind::TableSwitch);
|
||||
return tableSwitchSuccessor_;
|
||||
}
|
||||
uint32_t successor() const { return successor_; }
|
||||
uint8_t numToPop() const { return numToPop_; }
|
||||
};
|
||||
|
||||
// PendingEdgesMap maps a bytecode instruction to a Vector of PendingEdges
|
||||
|
@ -278,8 +237,8 @@ class MOZ_STACK_CLASS WarpBuilder : public WarpBuilderShared {
|
|||
bool hasTerminatedBlock() const { return current == nullptr; }
|
||||
void setTerminatedBlock() { current = nullptr; }
|
||||
|
||||
[[nodiscard]] bool addPendingEdge(const PendingEdge& edge,
|
||||
BytecodeLocation target);
|
||||
[[nodiscard]] bool addPendingEdge(BytecodeLocation target, MBasicBlock* block,
|
||||
uint32_t successor, uint32_t numToPop = 0);
|
||||
[[nodiscard]] bool buildForwardGoto(BytecodeLocation target);
|
||||
[[nodiscard]] bool buildBackedge();
|
||||
[[nodiscard]] bool buildTestBackedge(BytecodeLocation loc);
|
||||
|
|
|
@ -2387,10 +2387,12 @@ PresShell::ScrollPage(bool aForward) {
|
|||
GetScrollableFrameToScroll(VerticalScrollDirection);
|
||||
ScrollMode scrollMode = apz::GetScrollModeForOrigin(ScrollOrigin::Pages);
|
||||
if (scrollFrame) {
|
||||
scrollFrame->ScrollBy(
|
||||
nsIntPoint(0, aForward ? 1 : -1), ScrollUnit::PAGES, scrollMode,
|
||||
nullptr, mozilla::ScrollOrigin::NotSpecified,
|
||||
nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
|
||||
scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1), ScrollUnit::PAGES,
|
||||
scrollMode, nullptr,
|
||||
mozilla::ScrollOrigin::NotSpecified,
|
||||
nsIScrollableFrame::NOT_MOMENTUM,
|
||||
ScrollSnapFlags::IntendedDirection |
|
||||
ScrollSnapFlags::IntendedEndPosition);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2407,7 +2409,7 @@ PresShell::ScrollLine(bool aForward) {
|
|||
scrollFrame->ScrollBy(
|
||||
nsIntPoint(0, aForward ? lineCount : -lineCount), ScrollUnit::LINES,
|
||||
scrollMode, nullptr, mozilla::ScrollOrigin::NotSpecified,
|
||||
nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
|
||||
nsIScrollableFrame::NOT_MOMENTUM, ScrollSnapFlags::IntendedDirection);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2424,7 +2426,7 @@ PresShell::ScrollCharacter(bool aRight) {
|
|||
scrollFrame->ScrollBy(
|
||||
nsIntPoint(aRight ? h : -h, 0), ScrollUnit::LINES, scrollMode, nullptr,
|
||||
mozilla::ScrollOrigin::NotSpecified, nsIScrollableFrame::NOT_MOMENTUM,
|
||||
nsIScrollableFrame::ENABLE_SNAP);
|
||||
ScrollSnapFlags::IntendedDirection);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2438,7 +2440,7 @@ PresShell::CompleteScroll(bool aForward) {
|
|||
scrollFrame->ScrollBy(
|
||||
nsIntPoint(0, aForward ? 1 : -1), ScrollUnit::WHOLE, scrollMode,
|
||||
nullptr, mozilla::ScrollOrigin::NotSpecified,
|
||||
nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
|
||||
nsIScrollableFrame::NOT_MOMENTUM, ScrollSnapFlags::IntendedEndPosition);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -3570,8 +3572,8 @@ static void ScrollToShowRect(nsIScrollableFrame* aFrameAsScrollable,
|
|||
AutoWeakFrame weakFrame(frame);
|
||||
aFrameAsScrollable->ScrollTo(scrollPt, scrollMode, &allowedRange,
|
||||
aScrollFlags & ScrollFlags::ScrollSnap
|
||||
? nsIScrollbarMediator::ENABLE_SNAP
|
||||
: nsIScrollbarMediator::DISABLE_SNAP,
|
||||
? ScrollSnapFlags::IntendedEndPosition
|
||||
: ScrollSnapFlags::Disabled,
|
||||
aScrollFlags & ScrollFlags::TriggeredByScript
|
||||
? ScrollTriggeredByScript::Yes
|
||||
: ScrollTriggeredByScript::No);
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#ifndef mozilla_ScrollTypes_h
|
||||
#define mozilla_ScrollTypes_h
|
||||
|
||||
#include "mozilla/TypedEnumBits.h"
|
||||
|
||||
// Types used in main-thread scrolling interfaces such as nsIScrollableFrame.
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -56,6 +58,16 @@ enum class APZScrollAnimationType {
|
|||
TriggeredByUserInput // Animation triggered by user input.
|
||||
};
|
||||
|
||||
enum class ScrollSnapFlags : uint8_t {
|
||||
Disabled = 0,
|
||||
// https://drafts.csswg.org/css-scroll-snap/#intended-end-position
|
||||
IntendedEndPosition = 1 << 0,
|
||||
// https://drafts.csswg.org/css-scroll-snap/#intended-direction
|
||||
IntendedDirection = 1 << 1
|
||||
};
|
||||
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ScrollSnapFlags);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_ScrollTypes_h
|
||||
|
|
|
@ -23,8 +23,8 @@ using layers::ScrollSnapInfo;
|
|||
*/
|
||||
class CalcSnapPoints final {
|
||||
public:
|
||||
CalcSnapPoints(ScrollUnit aUnit, const nsPoint& aDestination,
|
||||
const nsPoint& aStartPos);
|
||||
CalcSnapPoints(ScrollUnit aUnit, ScrollSnapFlags aSnapFlags,
|
||||
const nsPoint& aDestination, const nsPoint& aStartPos);
|
||||
void AddHorizontalEdge(nscoord aEdge);
|
||||
void AddVerticalEdge(nscoord aEdge);
|
||||
void AddEdge(nscoord aEdge, nscoord aDestination, nscoord aStartPos,
|
||||
|
@ -43,6 +43,7 @@ class CalcSnapPoints final {
|
|||
|
||||
protected:
|
||||
ScrollUnit mUnit;
|
||||
ScrollSnapFlags mSnapFlags;
|
||||
nsPoint mDestination; // gives the position after scrolling but before
|
||||
// snapping
|
||||
nsPoint mStartPos; // gives the position before scrolling
|
||||
|
@ -57,9 +58,13 @@ class CalcSnapPoints final {
|
|||
// edge
|
||||
};
|
||||
|
||||
CalcSnapPoints::CalcSnapPoints(ScrollUnit aUnit, const nsPoint& aDestination,
|
||||
CalcSnapPoints::CalcSnapPoints(ScrollUnit aUnit, ScrollSnapFlags aSnapFlags,
|
||||
const nsPoint& aDestination,
|
||||
const nsPoint& aStartPos) {
|
||||
MOZ_ASSERT(aSnapFlags != ScrollSnapFlags::Disabled);
|
||||
|
||||
mUnit = aUnit;
|
||||
mSnapFlags = aSnapFlags;
|
||||
mDestination = aDestination;
|
||||
mStartPos = aStartPos;
|
||||
|
||||
|
@ -106,33 +111,17 @@ void CalcSnapPoints::AddEdge(nscoord aEdge, nscoord aDestination,
|
|||
nscoord aStartPos, nscoord aScrollingDirection,
|
||||
nscoord* aBestEdge, nscoord* aSecondBestEdge,
|
||||
bool* aEdgeFound) {
|
||||
// ScrollUnit::DEVICE_PIXELS indicates that we are releasing a drag
|
||||
// gesture or any other user input event that sets an absolute scroll
|
||||
// position. In this case, scroll snapping is expected to travel in any
|
||||
// direction. Otherwise, we will restrict the direction of the scroll
|
||||
// snapping movement based on aScrollingDirection.
|
||||
if (mUnit != ScrollUnit::DEVICE_PIXELS) {
|
||||
// Unless DEVICE_PIXELS, we only want to snap to points ahead of the
|
||||
// direction we are scrolling
|
||||
if (aScrollingDirection == 0) {
|
||||
// The scroll direction is neutral - will not hit a snap point.
|
||||
return;
|
||||
}
|
||||
// ScrollUnit::WHOLE indicates that we are navigating to "home" or
|
||||
// "end". In this case, we will always select the first or last snap point
|
||||
// regardless of the direction of the scroll. Otherwise, we will select
|
||||
// scroll snapping points only in the direction specified by
|
||||
// aScrollingDirection.
|
||||
if (mUnit != ScrollUnit::WHOLE) {
|
||||
// Direction of the edge from the current position (before scrolling) in
|
||||
// the direction of scrolling
|
||||
nscoord direction = (aEdge - aStartPos) * aScrollingDirection;
|
||||
if (direction <= 0) {
|
||||
// The edge is not in the direction we are scrolling, skip it.
|
||||
if (mSnapFlags & ScrollSnapFlags::IntendedDirection) {
|
||||
// In the case of intended direction, we only want to snap to points ahead
|
||||
// of the direction we are scrolling.
|
||||
if (aScrollingDirection == 0 ||
|
||||
(aEdge - aStartPos) * aScrollingDirection <= 0) {
|
||||
// The scroll direction is neutral - will not hit a snap point, or the
|
||||
// edge is not in the direction we are scrolling, skip it.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!*aEdgeFound) {
|
||||
*aBestEdge = aEdge;
|
||||
*aEdgeFound = true;
|
||||
|
@ -162,7 +151,8 @@ void CalcSnapPoints::AddEdge(nscoord aEdge, nscoord aDestination,
|
|||
}
|
||||
};
|
||||
|
||||
if (mUnit == ScrollUnit::DEVICE_PIXELS || mUnit == ScrollUnit::LINES) {
|
||||
if (mUnit == ScrollUnit::DEVICE_PIXELS || mUnit == ScrollUnit::LINES ||
|
||||
mUnit == ScrollUnit::WHOLE) {
|
||||
nscoord distance = std::abs(aEdge - aDestination);
|
||||
updateBestEdges(
|
||||
distance < std::abs(*aBestEdge - aDestination),
|
||||
|
@ -191,14 +181,6 @@ void CalcSnapPoints::AddEdge(nscoord aEdge, nscoord aDestination,
|
|||
if (overshoot > 0) {
|
||||
updateBestEdges(overshoot < curOvershoot, overshoot < secondOvershoot);
|
||||
}
|
||||
} else if (mUnit == ScrollUnit::WHOLE) {
|
||||
// the edge closest to the top/bottom/left/right is used, depending on
|
||||
// scrolling direction
|
||||
if (aScrollingDirection > 0) {
|
||||
updateBestEdges(aEdge > *aBestEdge, aEdge > *aSecondBestEdge);
|
||||
} else if (aScrollingDirection < 0) {
|
||||
updateBestEdges(aEdge < *aBestEdge, aEdge < *aSecondBestEdge);
|
||||
}
|
||||
} else {
|
||||
NS_ERROR("Invalid scroll mode");
|
||||
return;
|
||||
|
@ -231,8 +213,8 @@ static void ProcessSnapPositions(CalcSnapPoints& aCalcSnapPoints,
|
|||
|
||||
Maybe<nsPoint> ScrollSnapUtils::GetSnapPointForDestination(
|
||||
const ScrollSnapInfo& aSnapInfo, ScrollUnit aUnit,
|
||||
const nsRect& aScrollRange, const nsPoint& aStartPos,
|
||||
const nsPoint& aDestination) {
|
||||
ScrollSnapFlags aSnapFlags, const nsRect& aScrollRange,
|
||||
const nsPoint& aStartPos, const nsPoint& aDestination) {
|
||||
if (aSnapInfo.mScrollSnapStrictnessY == StyleScrollSnapStrictness::None &&
|
||||
aSnapInfo.mScrollSnapStrictnessX == StyleScrollSnapStrictness::None) {
|
||||
return Nothing();
|
||||
|
@ -242,7 +224,7 @@ Maybe<nsPoint> ScrollSnapUtils::GetSnapPointForDestination(
|
|||
return Nothing();
|
||||
}
|
||||
|
||||
CalcSnapPoints calcSnapPoints(aUnit, aDestination, aStartPos);
|
||||
CalcSnapPoints calcSnapPoints(aUnit, aSnapFlags, aDestination, aStartPos);
|
||||
|
||||
ProcessSnapPositions(calcSnapPoints, aSnapInfo);
|
||||
|
||||
|
|
|
@ -36,8 +36,8 @@ struct ScrollSnapUtils {
|
|||
*/
|
||||
static mozilla::Maybe<nsPoint> GetSnapPointForDestination(
|
||||
const layers::ScrollSnapInfo& aSnapInfo, ScrollUnit aUnit,
|
||||
const nsRect& aScrollRange, const nsPoint& aStartPos,
|
||||
const nsPoint& aDestination);
|
||||
ScrollSnapFlags aSnapFlags, const nsRect& aScrollRange,
|
||||
const nsPoint& aStartPos, const nsPoint& aDestination);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -1724,23 +1724,23 @@ static nsRect GetOnePixelRangeAroundPoint(const nsPoint& aPoint,
|
|||
return allowedRange;
|
||||
}
|
||||
|
||||
void ScrollFrameHelper::ScrollByPage(
|
||||
nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap) {
|
||||
void ScrollFrameHelper::ScrollByPage(nsScrollbarFrame* aScrollbar,
|
||||
int32_t aDirection,
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
ScrollByUnit(aScrollbar, ScrollMode::Smooth, aDirection, ScrollUnit::PAGES,
|
||||
aSnap);
|
||||
aSnapFlags);
|
||||
}
|
||||
|
||||
void ScrollFrameHelper::ScrollByWhole(
|
||||
nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap) {
|
||||
void ScrollFrameHelper::ScrollByWhole(nsScrollbarFrame* aScrollbar,
|
||||
int32_t aDirection,
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
ScrollByUnit(aScrollbar, ScrollMode::Instant, aDirection, ScrollUnit::WHOLE,
|
||||
aSnap);
|
||||
aSnapFlags);
|
||||
}
|
||||
|
||||
void ScrollFrameHelper::ScrollByLine(
|
||||
nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap) {
|
||||
void ScrollFrameHelper::ScrollByLine(nsScrollbarFrame* aScrollbar,
|
||||
int32_t aDirection,
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
bool isHorizontal = aScrollbar->IsXULHorizontal();
|
||||
nsIntPoint delta;
|
||||
if (isHorizontal) {
|
||||
|
@ -1771,7 +1771,7 @@ void ScrollFrameHelper::ScrollByLine(
|
|||
|
||||
nsIntPoint overflow;
|
||||
ScrollBy(delta, ScrollUnit::LINES, ScrollMode::Smooth, &overflow,
|
||||
ScrollOrigin::Other, nsIScrollableFrame::NOT_MOMENTUM, aSnap);
|
||||
ScrollOrigin::Other, nsIScrollableFrame::NOT_MOMENTUM, aSnapFlags);
|
||||
}
|
||||
|
||||
void ScrollFrameHelper::RepeatButtonScroll(nsScrollbarFrame* aScrollbar) {
|
||||
|
@ -1811,9 +1811,10 @@ void ScrollFrameHelper::ScrollbarReleased(nsScrollbarFrame* aScrollbar) {
|
|||
ScrollSnap(mDestination, ScrollMode::Smooth);
|
||||
}
|
||||
|
||||
void ScrollFrameHelper::ScrollByUnit(
|
||||
nsScrollbarFrame* aScrollbar, ScrollMode aMode, int32_t aDirection,
|
||||
ScrollUnit aUnit, nsIScrollbarMediator::ScrollSnapMode aSnap) {
|
||||
void ScrollFrameHelper::ScrollByUnit(nsScrollbarFrame* aScrollbar,
|
||||
ScrollMode aMode, int32_t aDirection,
|
||||
ScrollUnit aUnit,
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
MOZ_ASSERT(aScrollbar != nullptr);
|
||||
bool isHorizontal = aScrollbar->IsXULHorizontal();
|
||||
nsIntPoint delta;
|
||||
|
@ -1824,7 +1825,7 @@ void ScrollFrameHelper::ScrollByUnit(
|
|||
}
|
||||
nsIntPoint overflow;
|
||||
ScrollBy(delta, aUnit, aMode, &overflow, ScrollOrigin::Other,
|
||||
nsIScrollableFrame::NOT_MOMENTUM, aSnap);
|
||||
nsIScrollableFrame::NOT_MOMENTUM, aSnapFlags);
|
||||
}
|
||||
|
||||
nsresult nsXULScrollFrame::CreateAnonymousContent(
|
||||
|
@ -2431,12 +2432,12 @@ bool ScrollFrameHelper::HasBgAttachmentLocal() const {
|
|||
|
||||
void ScrollFrameHelper::ScrollTo(nsPoint aScrollPosition, ScrollMode aMode,
|
||||
ScrollOrigin aOrigin, const nsRect* aRange,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap,
|
||||
ScrollSnapFlags aSnapFlags,
|
||||
ScrollTriggeredByScript aTriggeredByScript) {
|
||||
if (aOrigin == ScrollOrigin::NotSpecified) {
|
||||
aOrigin = ScrollOrigin::Other;
|
||||
}
|
||||
ScrollToWithOrigin(aScrollPosition, aMode, aOrigin, aRange, aSnap,
|
||||
ScrollToWithOrigin(aScrollPosition, aMode, aOrigin, aRange, aSnapFlags,
|
||||
aTriggeredByScript);
|
||||
}
|
||||
|
||||
|
@ -2456,7 +2457,9 @@ void ScrollFrameHelper::ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
|
|||
APZScrollAnimationType::TriggeredByUserInput &&
|
||||
!isScrollAnimating) {
|
||||
CSSIntPoint delta = aScrollPosition - currentCSSPixels;
|
||||
ScrollByCSSPixels(delta, aMode);
|
||||
// This transmogrification need to be an intended end position scroll
|
||||
// operation.
|
||||
ScrollByCSSPixels(delta, aMode, ScrollSnapFlags::IntendedEndPosition);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2479,7 +2482,9 @@ void ScrollFrameHelper::ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
|
|||
range.height = 0;
|
||||
}
|
||||
ScrollTo(pt, aMode, ScrollOrigin::Other, &range,
|
||||
nsIScrollableFrame::ENABLE_SNAP, ScrollTriggeredByScript::Yes);
|
||||
// This ScrollToCSSPixels is used for Element.scrollTo,
|
||||
// Element.scrollTop, Element.scrollLeft and for Window.scrollTo.
|
||||
ScrollSnapFlags::IntendedEndPosition, ScrollTriggeredByScript::Yes);
|
||||
// 'this' might be destroyed here
|
||||
}
|
||||
|
||||
|
@ -2503,7 +2508,7 @@ CSSIntPoint ScrollFrameHelper::GetScrollPositionCSSPixels() {
|
|||
*/
|
||||
void ScrollFrameHelper::ScrollToWithOrigin(
|
||||
nsPoint aScrollPosition, ScrollMode aMode, ScrollOrigin aOrigin,
|
||||
const nsRect* aRange, nsIScrollbarMediator::ScrollSnapMode aSnap,
|
||||
const nsRect* aRange, ScrollSnapFlags aSnapFlags,
|
||||
ScrollTriggeredByScript aTriggeredByScript) {
|
||||
// None is never a valid scroll origin to be passed in.
|
||||
MOZ_ASSERT(aOrigin != ScrollOrigin::None);
|
||||
|
@ -2518,8 +2523,8 @@ void ScrollFrameHelper::ScrollToWithOrigin(
|
|||
}
|
||||
|
||||
bool willSnap = false;
|
||||
if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
|
||||
willSnap = GetSnapPointForDestination(ScrollUnit::DEVICE_PIXELS,
|
||||
if (aSnapFlags != ScrollSnapFlags::Disabled) {
|
||||
willSnap = GetSnapPointForDestination(ScrollUnit::DEVICE_PIXELS, aSnapFlags,
|
||||
mDestination, aScrollPosition);
|
||||
}
|
||||
|
||||
|
@ -4758,11 +4763,18 @@ nsRect ScrollFrameHelper::GetVisualOptimalViewingRect() const {
|
|||
return rect;
|
||||
}
|
||||
|
||||
static void AdjustForWholeDelta(int32_t aDelta, nscoord* aCoord) {
|
||||
if (aDelta < 0) {
|
||||
*aCoord = nscoord_MIN;
|
||||
} else if (aDelta > 0) {
|
||||
*aCoord = nscoord_MAX;
|
||||
static void AdjustDestinationForWholeDelta(const nsIntPoint& aDelta,
|
||||
const nsRect& aScrollRange,
|
||||
nsPoint& aPoint) {
|
||||
if (aDelta.x < 0) {
|
||||
aPoint.x = aScrollRange.X();
|
||||
} else if (aDelta.x > 0) {
|
||||
aPoint.x = aScrollRange.XMost();
|
||||
}
|
||||
if (aDelta.y < 0) {
|
||||
aPoint.y = aScrollRange.Y();
|
||||
} else if (aDelta.y > 0) {
|
||||
aPoint.y = aScrollRange.YMost();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4792,7 +4804,7 @@ void ScrollFrameHelper::ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit,
|
|||
ScrollMode aMode, nsIntPoint* aOverflow,
|
||||
ScrollOrigin aOrigin,
|
||||
nsIScrollableFrame::ScrollMomentum aMomentum,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap) {
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
// When a smooth scroll is being processed on a frame, mouse wheel and
|
||||
// trackpad momentum scroll event updates must notcancel the SMOOTH or
|
||||
// SMOOTH_MSD scroll animations, enabling Javascript that depends on them to
|
||||
|
@ -4825,7 +4837,7 @@ void ScrollFrameHelper::ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit,
|
|||
bool isGenericOrigin = (aOrigin == ScrollOrigin::Other);
|
||||
|
||||
bool askApzToDoTheScroll = false;
|
||||
if ((aSnap != nsIScrollableFrame::ENABLE_SNAP || !NeedsScrollSnap()) &&
|
||||
if ((aSnapFlags == ScrollSnapFlags::Disabled || !NeedsScrollSnap()) &&
|
||||
gfxPlatform::UseDesktopZoomingScrollbars() &&
|
||||
nsLayoutUtils::AsyncPanZoomEnabled(mOuter) &&
|
||||
!nsLayoutUtils::ShouldDisableApzForElement(mOuter->GetContent()) &&
|
||||
|
@ -4869,10 +4881,9 @@ void ScrollFrameHelper::ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit,
|
|||
break;
|
||||
} else {
|
||||
nsPoint pos = GetScrollPosition();
|
||||
AdjustForWholeDelta(aDelta.x, &pos.x);
|
||||
AdjustForWholeDelta(aDelta.y, &pos.y);
|
||||
if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
|
||||
GetSnapPointForDestination(aUnit, mDestination, pos);
|
||||
AdjustDestinationForWholeDelta(aDelta, GetLayoutScrollRange(), pos);
|
||||
if (aSnapFlags != ScrollSnapFlags::Disabled) {
|
||||
GetSnapPointForDestination(aUnit, aSnapFlags, mDestination, pos);
|
||||
}
|
||||
ScrollTo(pos, aMode, ScrollOrigin::Other);
|
||||
// 'this' might be destroyed here
|
||||
|
@ -4925,7 +4936,7 @@ void ScrollFrameHelper::ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit,
|
|||
NSCoordSaturatingNonnegativeMultiply(
|
||||
aDelta.y, deltaMultiplier.height)));
|
||||
|
||||
if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
|
||||
if (aSnapFlags != ScrollSnapFlags::Disabled) {
|
||||
if (NeedsScrollSnap()) {
|
||||
nscoord appUnitsPerDevPixel =
|
||||
mOuter->PresContext()->AppUnitsPerDevPixel();
|
||||
|
@ -4939,7 +4950,7 @@ void ScrollFrameHelper::ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit,
|
|||
// of scroll delta.
|
||||
snapUnit = ScrollUnit::LINES;
|
||||
}
|
||||
GetSnapPointForDestination(snapUnit, mDestination, newPos);
|
||||
GetSnapPointForDestination(snapUnit, aSnapFlags, mDestination, newPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4974,7 +4985,8 @@ void ScrollFrameHelper::ScrollBy(nsIntPoint aDelta, ScrollUnit aUnit,
|
|||
}
|
||||
|
||||
void ScrollFrameHelper::ScrollByCSSPixels(const CSSIntPoint& aDelta,
|
||||
ScrollMode aMode) {
|
||||
ScrollMode aMode,
|
||||
mozilla::ScrollSnapFlags aSnapFlags) {
|
||||
nsPoint current = GetScrollPosition();
|
||||
// `current` value above might be a value which was aligned to in
|
||||
// layer-pixels, so starting from such points will make the difference between
|
||||
|
@ -5004,8 +5016,7 @@ void ScrollFrameHelper::ScrollByCSSPixels(const CSSIntPoint& aDelta,
|
|||
range.y = pt.y;
|
||||
range.height = 0;
|
||||
}
|
||||
ScrollToWithOrigin(pt, aMode, ScrollOrigin::Relative, &range,
|
||||
nsIScrollableFrame::ENABLE_SNAP,
|
||||
ScrollToWithOrigin(pt, aMode, ScrollOrigin::Relative, &range, aSnapFlags,
|
||||
ScrollTriggeredByScript::Yes);
|
||||
// 'this' might be destroyed here
|
||||
}
|
||||
|
@ -5032,7 +5043,11 @@ void ScrollFrameHelper::ScrollSnap(const nsPoint& aDestination,
|
|||
nsRect scrollRange = GetLayoutScrollRange();
|
||||
nsPoint pos = GetScrollPosition();
|
||||
nsPoint snapDestination = scrollRange.ClampPoint(aDestination);
|
||||
if (GetSnapPointForDestination(ScrollUnit::DEVICE_PIXELS, pos,
|
||||
ScrollSnapFlags snapFlags = ScrollSnapFlags::IntendedEndPosition;
|
||||
if (mVelocityQueue.GetVelocity() != nsPoint()) {
|
||||
snapFlags |= ScrollSnapFlags::IntendedDirection;
|
||||
}
|
||||
if (GetSnapPointForDestination(ScrollUnit::DEVICE_PIXELS, snapFlags, pos,
|
||||
snapDestination)) {
|
||||
ScrollTo(snapDestination, aMode, ScrollOrigin::Other);
|
||||
}
|
||||
|
@ -7875,11 +7890,12 @@ layers::ScrollSnapInfo ScrollFrameHelper::GetScrollSnapInfo(
|
|||
}
|
||||
|
||||
bool ScrollFrameHelper::GetSnapPointForDestination(ScrollUnit aUnit,
|
||||
ScrollSnapFlags aSnapFlags,
|
||||
const nsPoint& aStartPos,
|
||||
nsPoint& aDestination) {
|
||||
Maybe<nsPoint> snapPoint = ScrollSnapUtils::GetSnapPointForDestination(
|
||||
GetScrollSnapInfo(Some(aDestination)), aUnit, GetLayoutScrollRange(),
|
||||
aStartPos, aDestination);
|
||||
GetScrollSnapInfo(Some(aDestination)), aUnit, aSnapFlags,
|
||||
GetLayoutScrollRange(), aStartPos, aDestination);
|
||||
if (snapPoint) {
|
||||
aDestination = snapPoint.ref();
|
||||
return true;
|
||||
|
|
|
@ -259,8 +259,7 @@ class ScrollFrameHelper : public nsIReflowCallback {
|
|||
nsPoint aScrollPosition, ScrollMode aMode,
|
||||
ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
|
||||
const nsRect* aRange = nullptr,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP,
|
||||
mozilla::ScrollSnapFlags aSnapFlags = mozilla::ScrollSnapFlags::Disabled,
|
||||
ScrollTriggeredByScript aTriggeredByScript = ScrollTriggeredByScript::No);
|
||||
/**
|
||||
* @note This method might destroy the frame, pres shell and other objects.
|
||||
|
@ -284,15 +283,20 @@ class ScrollFrameHelper : public nsIReflowCallback {
|
|||
/**
|
||||
* @note This method might destroy the frame, pres shell and other objects.
|
||||
*/
|
||||
void ScrollBy(nsIntPoint aDelta, mozilla::ScrollUnit aUnit, ScrollMode aMode,
|
||||
nsIntPoint* aOverflow,
|
||||
ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
|
||||
void ScrollBy(
|
||||
nsIntPoint aDelta, mozilla::ScrollUnit aUnit, ScrollMode aMode,
|
||||
nsIntPoint* aOverflow, ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
|
||||
nsIScrollableFrame::ScrollMomentum aMomentum =
|
||||
nsIScrollableFrame::NOT_MOMENTUM,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP);
|
||||
mozilla::ScrollSnapFlags aSnapFlags = mozilla::ScrollSnapFlags::Disabled);
|
||||
void ScrollByCSSPixels(const CSSIntPoint& aDelta,
|
||||
ScrollMode aMode = ScrollMode::Instant);
|
||||
ScrollMode aMode = ScrollMode::Instant,
|
||||
// This ScrollByCSSPixels is mainly used for
|
||||
// Element.scrollBy and Window.scrollBy. An exception
|
||||
// is the transmogirification of ScrollToCSSPixels.
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::IntendedDirection |
|
||||
mozilla::ScrollSnapFlags::IntendedEndPosition);
|
||||
/**
|
||||
* @note This method might destroy the frame, pres shell and other objects.
|
||||
*/
|
||||
|
@ -311,6 +315,7 @@ class ScrollFrameHelper : public nsIReflowCallback {
|
|||
* been updated to a valid snapping position.
|
||||
*/
|
||||
bool GetSnapPointForDestination(mozilla::ScrollUnit aUnit,
|
||||
mozilla::ScrollSnapFlags aFlags,
|
||||
const nsPoint& aStartPos,
|
||||
nsPoint& aDestination);
|
||||
|
||||
|
@ -518,19 +523,19 @@ class ScrollFrameHelper : public nsIReflowCallback {
|
|||
WebRenderLayerManager* aLayerManager, const nsIFrame* aItemFrame,
|
||||
const nsPoint& aOffsetToReferenceFrame) const;
|
||||
// nsIScrollbarMediator
|
||||
void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP);
|
||||
void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP);
|
||||
void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP);
|
||||
void ScrollByUnit(nsScrollbarFrame* aScrollbar, ScrollMode aMode,
|
||||
int32_t aDirection, ScrollUnit aUnit,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP);
|
||||
void ScrollByPage(
|
||||
nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
mozilla::ScrollSnapFlags aSnapFlags = mozilla::ScrollSnapFlags::Disabled);
|
||||
void ScrollByWhole(
|
||||
nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
mozilla::ScrollSnapFlags aSnapFlags = mozilla::ScrollSnapFlags::Disabled);
|
||||
void ScrollByLine(
|
||||
nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
mozilla::ScrollSnapFlags aSnapFlags = mozilla::ScrollSnapFlags::Disabled);
|
||||
void ScrollByUnit(
|
||||
nsScrollbarFrame* aScrollbar, ScrollMode aMode, int32_t aDirection,
|
||||
ScrollUnit aUnit,
|
||||
mozilla::ScrollSnapFlags aSnapFlags = mozilla::ScrollSnapFlags::Disabled);
|
||||
void RepeatButtonScroll(nsScrollbarFrame* aScrollbar);
|
||||
void ThumbMoved(nsScrollbarFrame* aScrollbar, nscoord aOldPos,
|
||||
nscoord aNewPos);
|
||||
|
@ -806,8 +811,7 @@ class ScrollFrameHelper : public nsIReflowCallback {
|
|||
void ScrollToWithOrigin(
|
||||
nsPoint aScrollPosition, ScrollMode aMode, ScrollOrigin aOrigin,
|
||||
const nsRect* aRange,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP,
|
||||
mozilla::ScrollSnapFlags aSnapFlags = mozilla::ScrollSnapFlags::Disabled,
|
||||
ScrollTriggeredByScript aTriggeredByScript = ScrollTriggeredByScript::No);
|
||||
|
||||
void CompleteAsyncScroll(const nsRect& aRange,
|
||||
|
@ -1027,14 +1031,13 @@ class nsHTMLScrollFrame : public nsContainerFrame,
|
|||
/**
|
||||
* @note This method might destroy the frame, pres shell and other objects.
|
||||
*/
|
||||
void ScrollTo(nsPoint aScrollPosition, ScrollMode aMode,
|
||||
const nsRect* aRange = nullptr,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP,
|
||||
void ScrollTo(
|
||||
nsPoint aScrollPosition, ScrollMode aMode, const nsRect* aRange = nullptr,
|
||||
mozilla::ScrollSnapFlags aSnapFlags = mozilla::ScrollSnapFlags::Disabled,
|
||||
mozilla::ScrollTriggeredByScript aTriggeredByScript =
|
||||
mozilla::ScrollTriggeredByScript::No) final {
|
||||
mHelper.ScrollTo(aScrollPosition, aMode, ScrollOrigin::Other, aRange,
|
||||
aSnap);
|
||||
aSnapFlags);
|
||||
}
|
||||
/**
|
||||
* @note This method might destroy the frame, pres shell and other objects.
|
||||
|
@ -1060,10 +1063,10 @@ class nsHTMLScrollFrame : public nsContainerFrame,
|
|||
ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
|
||||
nsIScrollableFrame::ScrollMomentum aMomentum =
|
||||
nsIScrollableFrame::NOT_MOMENTUM,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) final {
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) final {
|
||||
mHelper.ScrollBy(aDelta, aUnit, aMode, aOverflow, aOrigin, aMomentum,
|
||||
aSnap);
|
||||
aSnapFlags);
|
||||
}
|
||||
void ScrollByCSSPixels(const CSSIntPoint& aDelta,
|
||||
ScrollMode aMode = ScrollMode::Instant) final {
|
||||
|
@ -1174,24 +1177,25 @@ class nsHTMLScrollFrame : public nsContainerFrame,
|
|||
|
||||
// nsIScrollbarMediator
|
||||
void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) final {
|
||||
mHelper.ScrollByPage(aScrollbar, aDirection, aSnap);
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) final {
|
||||
mHelper.ScrollByPage(aScrollbar, aDirection, aSnapFlags);
|
||||
}
|
||||
void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) final {
|
||||
mHelper.ScrollByWhole(aScrollbar, aDirection, aSnap);
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) final {
|
||||
mHelper.ScrollByWhole(aScrollbar, aDirection, aSnapFlags);
|
||||
}
|
||||
void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) final {
|
||||
mHelper.ScrollByLine(aScrollbar, aDirection, aSnap);
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) final {
|
||||
mHelper.ScrollByLine(aScrollbar, aDirection, aSnapFlags);
|
||||
}
|
||||
void ScrollByUnit(nsScrollbarFrame* aScrollbar, ScrollMode aMode,
|
||||
int32_t aDirection, mozilla::ScrollUnit aUnit,
|
||||
ScrollSnapMode aSnap = DISABLE_SNAP) final {
|
||||
mHelper.ScrollByUnit(aScrollbar, aMode, aDirection, aUnit, aSnap);
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) final {
|
||||
mHelper.ScrollByUnit(aScrollbar, aMode, aDirection, aUnit, aSnapFlags);
|
||||
}
|
||||
void RepeatButtonScroll(nsScrollbarFrame* aScrollbar) final {
|
||||
mHelper.RepeatButtonScroll(aScrollbar);
|
||||
|
@ -1496,13 +1500,13 @@ class nsXULScrollFrame final : public nsBoxFrame,
|
|||
/**
|
||||
* @note This method might destroy the frame, pres shell and other objects.
|
||||
*/
|
||||
void ScrollTo(nsPoint aScrollPosition, ScrollMode aMode,
|
||||
const nsRect* aRange = nullptr,
|
||||
ScrollSnapMode aSnap = nsIScrollbarMediator::DISABLE_SNAP,
|
||||
void ScrollTo(
|
||||
nsPoint aScrollPosition, ScrollMode aMode, const nsRect* aRange = nullptr,
|
||||
mozilla::ScrollSnapFlags aSnapFlags = mozilla::ScrollSnapFlags::Disabled,
|
||||
mozilla::ScrollTriggeredByScript aTriggeredByScript =
|
||||
mozilla::ScrollTriggeredByScript::No) final {
|
||||
mHelper.ScrollTo(aScrollPosition, aMode, ScrollOrigin::Other, aRange,
|
||||
aSnap);
|
||||
aSnapFlags);
|
||||
}
|
||||
/**
|
||||
* @note This method might destroy the frame, pres shell and other objects.
|
||||
|
@ -1525,10 +1529,10 @@ class nsXULScrollFrame final : public nsBoxFrame,
|
|||
ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
|
||||
nsIScrollableFrame::ScrollMomentum aMomentum =
|
||||
nsIScrollableFrame::NOT_MOMENTUM,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) final {
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) final {
|
||||
mHelper.ScrollBy(aDelta, aUnit, aMode, aOverflow, aOrigin, aMomentum,
|
||||
aSnap);
|
||||
aSnapFlags);
|
||||
}
|
||||
void ScrollByCSSPixels(const CSSIntPoint& aDelta,
|
||||
ScrollMode aMode = ScrollMode::Instant) final {
|
||||
|
@ -1626,24 +1630,25 @@ class nsXULScrollFrame final : public nsBoxFrame,
|
|||
}
|
||||
|
||||
void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) final {
|
||||
mHelper.ScrollByPage(aScrollbar, aDirection, aSnap);
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) final {
|
||||
mHelper.ScrollByPage(aScrollbar, aDirection, aSnapFlags);
|
||||
}
|
||||
void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) final {
|
||||
mHelper.ScrollByWhole(aScrollbar, aDirection, aSnap);
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) final {
|
||||
mHelper.ScrollByWhole(aScrollbar, aDirection, aSnapFlags);
|
||||
}
|
||||
void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) final {
|
||||
mHelper.ScrollByLine(aScrollbar, aDirection, aSnap);
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) final {
|
||||
mHelper.ScrollByLine(aScrollbar, aDirection, aSnapFlags);
|
||||
}
|
||||
void ScrollByUnit(nsScrollbarFrame* aScrollbar, ScrollMode aMode,
|
||||
int32_t aDirection, mozilla::ScrollUnit aUnit,
|
||||
ScrollSnapMode aSnap = DISABLE_SNAP) final {
|
||||
mHelper.ScrollByUnit(aScrollbar, aMode, aDirection, aUnit, aSnap);
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) final {
|
||||
mHelper.ScrollByUnit(aScrollbar, aMode, aDirection, aUnit, aSnapFlags);
|
||||
}
|
||||
void RepeatButtonScroll(nsScrollbarFrame* aScrollbar) final {
|
||||
mHelper.RepeatButtonScroll(aScrollbar);
|
||||
|
|
|
@ -3085,30 +3085,6 @@ struct AutoCheckBuilder {
|
|||
nsDisplayListBuilder* mBuilder;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper class to track container creation. Stores the first tracked container.
|
||||
* Used to find the innermost container for hit test information, and to notify
|
||||
* callers whether a container item was created or not.
|
||||
*/
|
||||
struct ContainerTracker {
|
||||
void TrackContainer(nsDisplayItem* aContainer) {
|
||||
if (!aContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mContainer) {
|
||||
mContainer = aContainer;
|
||||
}
|
||||
|
||||
mCreatedContainer = true;
|
||||
}
|
||||
|
||||
void ResetCreatedContainer() { mCreatedContainer = false; }
|
||||
|
||||
nsDisplayItem* mContainer = nullptr;
|
||||
bool mCreatedContainer = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tries to reuse a top-level stacking context item from the previous paint.
|
||||
* Returns true if an item was reused, otherwise false.
|
||||
|
@ -3579,7 +3555,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
// Get the ASR to use for the container items that we create here.
|
||||
const ActiveScrolledRoot* containerItemASR = contASRTracker.GetContainerASR();
|
||||
|
||||
ContainerTracker ct;
|
||||
bool createdContainer = false;
|
||||
|
||||
/* If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
|
||||
* same list, the nsDisplayBlendContainer should be added first. This only
|
||||
|
@ -3592,7 +3568,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
DisplayListClipState::AutoSaveRestore blendContainerClipState(aBuilder);
|
||||
resultList.AppendToTop(nsDisplayBlendContainer::CreateForMixBlendMode(
|
||||
aBuilder, this, &resultList, containerItemASR));
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
if (insertBackdropRoot) {
|
||||
|
@ -3600,7 +3576,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
aBuilder);
|
||||
resultList.AppendNewToTop<nsDisplayBackdropRootContainer>(
|
||||
aBuilder, this, &resultList, containerItemASR);
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
if (usingBackdropFilter) {
|
||||
|
@ -3609,7 +3585,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
|
||||
resultList.AppendNewToTop<nsDisplayBackdropFilters>(
|
||||
aBuilder, this, &resultList, backdropRect);
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
/* If there are any SVG effects, wrap the list up in an SVG effects item
|
||||
|
@ -3631,7 +3607,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) {
|
||||
/* List now emptied, so add the new list to the top. */
|
||||
resultList.AppendNewToTop<nsDisplayFilters>(aBuilder, this, &resultList);
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
if (usingMask) {
|
||||
|
@ -3651,12 +3627,12 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
/* List now emptied, so add the new list to the top. */
|
||||
resultList.AppendNewToTop<nsDisplayMasksAndClipPaths>(
|
||||
aBuilder, this, &resultList, maskASR);
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
// TODO(miko): We could probably create a wraplist here and avoid creating
|
||||
// it later in |BuildDisplayListForChild()|.
|
||||
ct.ResetCreatedContainer();
|
||||
createdContainer = false;
|
||||
|
||||
// Also add the hoisted scroll info items. We need those for APZ scrolling
|
||||
// because nsDisplayMasksAndClipPaths items can't build active layers.
|
||||
|
@ -3678,7 +3654,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
resultList.AppendNewToTop<nsDisplayOpacity>(
|
||||
aBuilder, this, &resultList, containerItemASR, opacityItemForEventsOnly,
|
||||
needsActiveOpacityLayer);
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
/* If we're going to apply a transformation and don't have preserve-3d set,
|
||||
|
@ -3726,7 +3702,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
index++, &separator);
|
||||
|
||||
if (separator) {
|
||||
ct.TrackContainer(separator);
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
resultList.AppendToTop(&participants);
|
||||
|
@ -3775,7 +3751,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
aBuilder, this, &resultList, visibleRect, prerenderInfo.mDecision);
|
||||
if (transformItem) {
|
||||
resultList.AppendToTop(transformItem);
|
||||
ct.TrackContainer(transformItem);
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
if (hasPerspective) {
|
||||
|
@ -3786,7 +3762,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
}
|
||||
resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this,
|
||||
&resultList);
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3799,7 +3775,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
&resultList, aBuilder->CurrentActiveScrolledRoot(),
|
||||
nsDisplayOwnLayerFlags::None, ScrollbarData{},
|
||||
/* aForceActive = */ false, false);
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
/* If we have sticky positioning, wrap it in a sticky position item.
|
||||
|
@ -3820,7 +3796,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
containerItemASR, aBuilder->CurrentActiveScrolledRoot());
|
||||
resultList.AppendNewToTop<nsDisplayFixedPosition>(
|
||||
aBuilder, this, &resultList, fixedASR, containerItemASR);
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
} else if (useStickyPosition) {
|
||||
// For position:sticky, the clip needs to be applied both to the sticky
|
||||
// container item and to the contents. The container item needs the clip
|
||||
|
@ -3855,7 +3831,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
stickyItem->SetShouldFlatten(shouldFlatten);
|
||||
|
||||
resultList.AppendToTop(stickyItem);
|
||||
ct.TrackContainer(stickyItem);
|
||||
createdContainer = true;
|
||||
|
||||
// If the sticky element is inside a filter, annotate the scroll frame that
|
||||
// scrolls the filter as having out-of-flow content inside a filter (this
|
||||
|
@ -3876,7 +3852,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
resultList.AppendNewToTop<nsDisplayBlendMode>(aBuilder, this, &resultList,
|
||||
effects->mMixBlendMode,
|
||||
containerItemASR, false);
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
bool createdOwnLayer = false;
|
||||
|
@ -3885,7 +3861,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
&createdOwnLayer);
|
||||
|
||||
if (createdOwnLayer) {
|
||||
ct.TrackContainer(resultList.GetTop());
|
||||
createdContainer = true;
|
||||
}
|
||||
|
||||
if (aBuilder->IsReusingStackingContextItems()) {
|
||||
|
@ -3909,13 +3885,13 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||
container->SetReusable();
|
||||
}
|
||||
aList->AppendToTop(container);
|
||||
ct.TrackContainer(container);
|
||||
createdContainer = true;
|
||||
} else {
|
||||
aList->AppendToTop(&resultList);
|
||||
}
|
||||
|
||||
if (aCreatedContainerItem) {
|
||||
*aCreatedContainerItem = ct.mCreatedContainer;
|
||||
*aCreatedContainerItem = createdContainer;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -261,10 +261,9 @@ class nsIScrollableFrame : public nsIScrollbarMediator {
|
|||
* Any point within this area can be chosen.
|
||||
* The choosen point will be as close as possible to aScrollPosition.
|
||||
*/
|
||||
virtual void ScrollTo(nsPoint aScrollPosition, ScrollMode aMode,
|
||||
const nsRect* aRange = nullptr,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP,
|
||||
virtual void ScrollTo(
|
||||
nsPoint aScrollPosition, ScrollMode aMode, const nsRect* aRange = nullptr,
|
||||
mozilla::ScrollSnapFlags aSnapFlags = mozilla::ScrollSnapFlags::Disabled,
|
||||
mozilla::ScrollTriggeredByScript aTriggeredByScript =
|
||||
mozilla::ScrollTriggeredByScript::No) = 0;
|
||||
/**
|
||||
|
@ -316,8 +315,8 @@ class nsIScrollableFrame : public nsIScrollbarMediator {
|
|||
ScrollMode aMode, nsIntPoint* aOverflow = nullptr,
|
||||
ScrollOrigin aOrigin = ScrollOrigin::NotSpecified,
|
||||
ScrollMomentum aMomentum = NOT_MOMENTUM,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) = 0;
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) = 0;
|
||||
|
||||
virtual void ScrollByCSSPixels(const CSSIntPoint& aDelta,
|
||||
ScrollMode aMode = ScrollMode::Instant) = 0;
|
||||
|
|
|
@ -206,15 +206,14 @@ static bool IsParentAFrameSet(nsIDocShell* aParent) {
|
|||
* @param aPrintData nsPrintData for the current print, must not be null.
|
||||
*/
|
||||
static void BuildNestedPrintObjects(const UniquePtr<nsPrintObject>& aParentPO,
|
||||
const RefPtr<Document>& aFocusedDoc,
|
||||
RefPtr<nsPrintData>& aPrintData) {
|
||||
MOZ_ASSERT(aParentPO);
|
||||
MOZ_ASSERT(aPrintData);
|
||||
|
||||
// If aParentPO is for an iframe and its original document is focusedDoc then
|
||||
// always set as the selection root.
|
||||
// If aParentPO is for an iframe and its original document was the document
|
||||
// that had focus then always set as the selection root.
|
||||
if (aParentPO->mFrameType == eIFrame &&
|
||||
aParentPO->mDocument->GetOriginalDocument() == aFocusedDoc) {
|
||||
aParentPO->mDocument->GetProperty(nsGkAtoms::printisfocuseddoc)) {
|
||||
aPrintData->mSelectionRoot = aParentPO.get();
|
||||
} else if (!aPrintData->mSelectionRoot && aParentPO->HasSelection()) {
|
||||
// If there is no focused iframe but there is a selection in one or more
|
||||
|
@ -255,7 +254,7 @@ static void BuildNestedPrintObjects(const UniquePtr<nsPrintObject>& aParentPO,
|
|||
}
|
||||
|
||||
aPrintData->mPrintDocList.AppendElement(childPO.get());
|
||||
BuildNestedPrintObjects(childPO, aFocusedDoc, aPrintData);
|
||||
BuildNestedPrintObjects(childPO, aPrintData);
|
||||
aParentPO->mKids.AppendElement(std::move(childPO));
|
||||
}
|
||||
}
|
||||
|
@ -496,9 +495,6 @@ nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview,
|
|||
printData->mPrintProgressListeners.AppendObject(aWebProgressListener);
|
||||
}
|
||||
|
||||
// Get the document from the currently focused window.
|
||||
RefPtr<Document> focusedDoc = FindFocusedDocument(aDoc);
|
||||
|
||||
// Get the docshell for this documentviewer
|
||||
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -522,7 +518,7 @@ nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview,
|
|||
printData->mPrintObject->mFrameType =
|
||||
printData->mIsParentAFrameSet ? eFrameSet : eDoc;
|
||||
|
||||
BuildNestedPrintObjects(printData->mPrintObject, focusedDoc, printData);
|
||||
BuildNestedPrintObjects(printData->mPrintObject, printData);
|
||||
}
|
||||
|
||||
// The nsAutoScriptBlocker above will now have been destroyed, which may
|
||||
|
@ -2074,47 +2070,6 @@ void nsPrintJob::SetIsPrintPreview(bool aIsPrintPreview) {
|
|||
}
|
||||
}
|
||||
|
||||
Document* nsPrintJob::FindFocusedDocument(Document* aDoc) const {
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
NS_ENSURE_TRUE(fm, nullptr);
|
||||
|
||||
nsPIDOMWindowOuter* window = aDoc->GetOriginalDocument()->GetWindow();
|
||||
NS_ENSURE_TRUE(window, nullptr);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
|
||||
NS_ENSURE_TRUE(rootWindow, nullptr);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
|
||||
nsFocusManager::GetFocusedDescendant(rootWindow,
|
||||
nsFocusManager::eIncludeAllDescendants,
|
||||
getter_AddRefs(focusedWindow));
|
||||
NS_ENSURE_TRUE(focusedWindow, nullptr);
|
||||
|
||||
if (IsWindowsInOurSubTree(focusedWindow)) {
|
||||
return focusedWindow->GetDoc();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
bool nsPrintJob::IsWindowsInOurSubTree(nsPIDOMWindowOuter* window) const {
|
||||
if (window) {
|
||||
nsCOMPtr<nsIDocShell> ourDocShell(do_QueryReferent(mDocShell));
|
||||
if (ourDocShell) {
|
||||
BrowsingContext* ourBC = ourDocShell->GetBrowsingContext();
|
||||
BrowsingContext* bc = window->GetBrowsingContext();
|
||||
while (bc) {
|
||||
if (bc == ourBC) {
|
||||
return true;
|
||||
}
|
||||
bc = bc->GetParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
bool nsPrintJob::DonePrintingSheets(nsPrintObject* aPO, nsresult aResult) {
|
||||
// NS_ASSERTION(aPO, "Pointer is null!");
|
||||
|
|
|
@ -176,16 +176,6 @@ class nsPrintJob final : public nsIWebProgressListener,
|
|||
|
||||
nsresult StartPagePrintTimer(const mozilla::UniquePtr<nsPrintObject>& aPO);
|
||||
|
||||
bool IsWindowsInOurSubTree(nsPIDOMWindowOuter* aDOMWindow) const;
|
||||
|
||||
/**
|
||||
* @return The document from the focused windows for a document viewer.
|
||||
*
|
||||
* FIXME: This is somewhat unsound, this looks at the original document, which
|
||||
* could've mutated after print was initiated.
|
||||
*/
|
||||
Document* FindFocusedDocument(Document* aDoc) const;
|
||||
|
||||
/// Customizes the behaviour of GetDisplayTitleAndURL.
|
||||
enum class DocTitleDefault : uint32_t { eDocURLElseFallback, eFallback };
|
||||
|
||||
|
@ -243,13 +233,6 @@ class nsPrintJob final : public nsIWebProgressListener,
|
|||
|
||||
void PageDone(nsresult aResult);
|
||||
|
||||
// The document that we were originally created for in order to print it or
|
||||
// create a print preview of it. This may belong to mDocViewerPrint or may
|
||||
// belong to a different docViewer in a different docShell. In reality, this
|
||||
// also may not be the original document that the user selected to print (see
|
||||
// the comment documenting Initialize() above).
|
||||
RefPtr<Document> mOriginalDoc;
|
||||
|
||||
// The docViewer that owns us, and its docShell.
|
||||
nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
|
||||
nsWeakPtr mDocShell;
|
||||
|
|
|
@ -30,11 +30,13 @@ body {
|
|||
<!--
|
||||
Put an SVG element so that the transform display item has a blob on
|
||||
WebRender which means this test properly fails without the proper fix.
|
||||
Note: As SVG items get "webrenderized" this test won't work. For now
|
||||
rounded rectangles go through the blob path.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900">
|
||||
<!-- green on the pre-render area -->
|
||||
<rect fill="green" x="0" width="1125" height="900"></rect>
|
||||
<rect fill="red" x="1125" width="875" height="900"></rect>
|
||||
<rect fill="green" x="0" width="1125" height="900" rx="0.1"></rect>
|
||||
<rect fill="red" x="1125" width="875" height="900" rx="0.1"></rect>
|
||||
</svg>
|
||||
</div>
|
||||
<script>
|
||||
|
|
|
@ -31,7 +31,6 @@ class nsIScrollbarMediator : public nsQueryFrame {
|
|||
* In case of DEFAULT, it means ENABLE_SNAP for CSS scroll snap v1,
|
||||
* DISABLE_SNAP for the old scroll snap.
|
||||
*/
|
||||
enum ScrollSnapMode { DEFAULT, DISABLE_SNAP, ENABLE_SNAP };
|
||||
|
||||
/**
|
||||
* One of the following three methods is called when the scrollbar's button is
|
||||
|
@ -39,17 +38,21 @@ class nsIScrollbarMediator : public nsQueryFrame {
|
|||
* @note These methods might destroy the frame, pres shell, and other objects.
|
||||
*/
|
||||
virtual void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
ScrollSnapMode aSnap = DISABLE_SNAP) = 0;
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) = 0;
|
||||
virtual void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
ScrollSnapMode aSnap = DISABLE_SNAP) = 0;
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) = 0;
|
||||
virtual void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
ScrollSnapMode aSnap = DISABLE_SNAP) = 0;
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) = 0;
|
||||
|
||||
// Only implemented for nsGfxScrollFrame, not nsTreeBodyFrame.
|
||||
virtual void ScrollByUnit(nsScrollbarFrame* aScrollbar,
|
||||
mozilla::ScrollMode aMode, int32_t aDirection,
|
||||
mozilla::ScrollUnit aUnit,
|
||||
ScrollSnapMode aSnap = DISABLE_SNAP) = 0;
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) = 0;
|
||||
|
||||
/**
|
||||
* RepeatButtonScroll is called when the scrollbar's button is held down. When
|
||||
|
|
|
@ -141,19 +141,21 @@ bool nsScrollbarButtonFrame::HandleButtonPress(nsPresContext* aPresContext,
|
|||
case 0:
|
||||
sb->SetIncrementToLine(direction);
|
||||
if (m) {
|
||||
m->ScrollByLine(sb, direction, nsIScrollbarMediator::ENABLE_SNAP);
|
||||
m->ScrollByLine(sb, direction, ScrollSnapFlags::IntendedDirection);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
sb->SetIncrementToPage(direction);
|
||||
if (m) {
|
||||
m->ScrollByPage(sb, direction, nsIScrollbarMediator::ENABLE_SNAP);
|
||||
m->ScrollByPage(sb, direction,
|
||||
ScrollSnapFlags::IntendedDirection |
|
||||
ScrollSnapFlags::IntendedEndPosition);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
sb->SetIncrementToWhole(direction);
|
||||
if (m) {
|
||||
m->ScrollByWhole(sb, direction, nsIScrollbarMediator::ENABLE_SNAP);
|
||||
m->ScrollByWhole(sb, direction, ScrollSnapFlags::IntendedEndPosition);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
|
|
|
@ -251,9 +251,12 @@ int32_t nsScrollbarFrame::MoveToNewPosition(
|
|||
MOZ_ASSERT(m);
|
||||
// aImplementsScrollByUnit being Yes indicates the caller doesn't care
|
||||
// about the return value.
|
||||
m->ScrollByUnit(this,
|
||||
mSmoothScroll ? ScrollMode::Smooth : ScrollMode::Instant,
|
||||
mDirection, mScrollUnit, nsIScrollbarMediator::ENABLE_SNAP);
|
||||
// Note that this `MoveToNewPosition` is used for scrolling triggered by
|
||||
// repeating scrollbar button press, so we'd use an intended-direction
|
||||
// scroll snap flag.
|
||||
m->ScrollByUnit(
|
||||
this, mSmoothScroll ? ScrollMode::Smooth : ScrollMode::Instant,
|
||||
mDirection, mScrollUnit, ScrollSnapFlags::IntendedDirection);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ nsresult nsSliderFrame::AttributeChanged(int32_t aNameSpaceID,
|
|||
scrollbarFrame->SetIncrementToWhole(direction);
|
||||
if (mediator) {
|
||||
mediator->ScrollByWhole(scrollbarFrame, direction,
|
||||
nsIScrollbarMediator::ENABLE_SNAP);
|
||||
ScrollSnapFlags::IntendedEndPosition);
|
||||
}
|
||||
}
|
||||
// 'this' might be destroyed here
|
||||
|
@ -1569,7 +1569,9 @@ void nsSliderFrame::PageScroll(nscoord aChange) {
|
|||
nsIScrollbarMediator* m = sb->GetScrollbarMediator();
|
||||
sb->SetIncrementToPage(aChange);
|
||||
if (m) {
|
||||
m->ScrollByPage(sb, aChange, nsIScrollbarMediator::ENABLE_SNAP);
|
||||
m->ScrollByPage(sb, aChange,
|
||||
ScrollSnapFlags::IntendedDirection |
|
||||
ScrollSnapFlags::IntendedEndPosition);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3873,15 +3873,15 @@ nsresult nsTreeBodyFrame::ScrollHorzInternal(const ScrollParts& aParts,
|
|||
|
||||
void nsTreeBodyFrame::ScrollByPage(nsScrollbarFrame* aScrollbar,
|
||||
int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap) {
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
// CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
|
||||
MOZ_ASSERT(aScrollbar != nullptr);
|
||||
ScrollByPages(aDirection);
|
||||
}
|
||||
|
||||
void nsTreeBodyFrame::ScrollByWhole(
|
||||
nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap) {
|
||||
void nsTreeBodyFrame::ScrollByWhole(nsScrollbarFrame* aScrollbar,
|
||||
int32_t aDirection,
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
// CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
|
||||
MOZ_ASSERT(aScrollbar != nullptr);
|
||||
int32_t newIndex = aDirection < 0 ? 0 : mTopRowIndex;
|
||||
|
@ -3890,16 +3890,15 @@ void nsTreeBodyFrame::ScrollByWhole(
|
|||
|
||||
void nsTreeBodyFrame::ScrollByLine(nsScrollbarFrame* aScrollbar,
|
||||
int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap) {
|
||||
ScrollSnapFlags aSnapFlags) {
|
||||
// CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
|
||||
MOZ_ASSERT(aScrollbar != nullptr);
|
||||
ScrollByLines(aDirection);
|
||||
}
|
||||
|
||||
void nsTreeBodyFrame::ScrollByUnit(nsScrollbarFrame* aScrollbar,
|
||||
ScrollMode aMode, int32_t aDirection,
|
||||
ScrollUnit aUnit,
|
||||
ScrollSnapMode aSnap /* = DISABLE_SNAP */) {
|
||||
void nsTreeBodyFrame::ScrollByUnit(
|
||||
nsScrollbarFrame* aScrollbar, ScrollMode aMode, int32_t aDirection,
|
||||
ScrollUnit aUnit, ScrollSnapFlags aSnapFlags /* = Disabled */) {
|
||||
MOZ_ASSERT_UNREACHABLE("Can't get here, we pass false to MoveToNewPosition");
|
||||
}
|
||||
|
||||
|
|
|
@ -126,18 +126,19 @@ class nsTreeBodyFrame final : public nsLeafBoxFrame,
|
|||
|
||||
// nsIScrollbarMediator
|
||||
virtual void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) override;
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) override;
|
||||
virtual void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) override;
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) override;
|
||||
virtual void ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
|
||||
nsIScrollbarMediator::ScrollSnapMode aSnap =
|
||||
nsIScrollbarMediator::DISABLE_SNAP) override;
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) override;
|
||||
virtual void ScrollByUnit(nsScrollbarFrame* aScrollbar,
|
||||
mozilla::ScrollMode aMode, int32_t aDirection,
|
||||
mozilla::ScrollUnit aUnit,
|
||||
ScrollSnapMode aSnap = DISABLE_SNAP) override;
|
||||
mozilla::ScrollSnapFlags aSnapFlags =
|
||||
mozilla::ScrollSnapFlags::Disabled) override;
|
||||
virtual void RepeatButtonScroll(nsScrollbarFrame* aScrollbar) override;
|
||||
virtual void ThumbMoved(nsScrollbarFrame* aScrollbar, nscoord aOldPos,
|
||||
nscoord aNewPos) override;
|
||||
|
|
|
@ -51,10 +51,6 @@ treeherder:
|
|||
'M-a11y-checks': 'Mochitests with accessibility checks enabled'
|
||||
'M-a11y-checks-nofis': 'Mochitests with accessibility checks enabled and without fission enabled'
|
||||
'M-a11y-checks-fis': 'Mochitests with accessibility checks enabled and fission enabled'
|
||||
'M-dfpi': 'Mochitests with dFPI enabled'
|
||||
'M-dfpi-1proc': 'Mochitests with dFPI enabled and no fission or e10s'
|
||||
'M-dfpi-fis': 'Mochitests with dFPI and Fission enabled'
|
||||
'M-dfpi-fis-xorig': 'Mochitests with dFPI, cross-origin and fission enabled'
|
||||
'M-fis': 'Mochitests with fission enabled'
|
||||
'M-fis-xorig': 'Mochitests with cross-origin and fission enabled'
|
||||
'M-fis-gli': 'Mochitests with fission and WebGL IPC enabled'
|
||||
|
@ -128,7 +124,6 @@ treeherder:
|
|||
'SY': 'Are we slim yet tests by TaskCluster'
|
||||
'SY-fis': 'Are we slim yet tests by TaskCluster, fission enabled'
|
||||
'W': 'Web platform tests'
|
||||
'W-dfpi-fis': 'Web platform tests with dFPI and Fission enabled'
|
||||
'W-fis': 'Web platform tests with fission enabled'
|
||||
'W-nofis': 'Web platform tests without fission enabled'
|
||||
'W-headless-nofis': 'Headless web platform tests without fission enabled'
|
||||
|
|
|
@ -50,8 +50,6 @@ mochitest-plain:
|
|||
virtualization: virtual
|
||||
variants:
|
||||
- aab+no-fission
|
||||
- dynamic-first-party-isolation+fission
|
||||
- dynamic-first-party-isolation+fission-xorigin
|
||||
- fission
|
||||
- fission-xorigin
|
||||
- no-fission
|
||||
|
@ -66,14 +64,6 @@ mochitest-plain:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
dynamic-first-party-isolation+fission-xorigin:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
@ -206,7 +196,6 @@ mochitest-browser-chrome:
|
|||
schedules-component: mochitest-browser-chrome
|
||||
loopback-video: true
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- webrender-sw+no-fission
|
||||
- webrender-sw+fission
|
||||
|
@ -218,10 +207,6 @@ mochitest-browser-chrome:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
@ -393,17 +378,12 @@ mochitest-chrome:
|
|||
loopback-video: true
|
||||
run-without-variant: false
|
||||
variants:
|
||||
- dynamic-first-party-isolation+1proc
|
||||
- 1proc
|
||||
- socketprocess_networking+1proc
|
||||
- wayland+1proc
|
||||
- webrender-sw+1proc
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+1proc:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
socketprocess_networking+1proc:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['autoland', 'mozilla-central']
|
||||
|
@ -448,7 +428,6 @@ mochitest-devtools-chrome:
|
|||
variants:
|
||||
- a11y-checks+no-fission
|
||||
- a11y-checks+fission
|
||||
- dynamic-first-party-isolation+fission
|
||||
- no-fission
|
||||
- fission
|
||||
- socketprocess_networking+no-fission
|
||||
|
@ -472,10 +451,6 @@ mochitest-devtools-chrome:
|
|||
by-test-platform:
|
||||
linux.*64(-shippable)?-qr/opt: ['trunk']
|
||||
default: []
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
@ -543,7 +518,6 @@ mochitest-plain-gpu:
|
|||
schedules-component: mochitest-plain
|
||||
loopback-video: true
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- wayland
|
||||
|
@ -552,10 +526,6 @@ mochitest-plain-gpu:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*/.*: []
|
||||
|
@ -612,16 +582,11 @@ mochitest-chrome-gpu:
|
|||
run-without-variant: false
|
||||
variants:
|
||||
- 1proc
|
||||
- dynamic-first-party-isolation+1proc
|
||||
- socketprocess_networking+1proc
|
||||
- wayland+1proc
|
||||
- webrender-sw+1proc
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+1proc:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
socketprocess_networking+1proc: []
|
||||
wayland+1proc:
|
||||
by-test-platform:
|
||||
|
@ -663,7 +628,6 @@ mochitest-media:
|
|||
linux.*64-tsan-qr/opt: 7200
|
||||
default: 5400
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- socketprocess+no-fission
|
||||
|
@ -675,10 +639,6 @@ mochitest-media:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
@ -849,7 +809,6 @@ mochitest-webgl1-core:
|
|||
macosx.*64-ccov.*/.*: 7200
|
||||
default: 1800
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- wayland
|
||||
|
@ -860,10 +819,6 @@ mochitest-webgl1-core:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
.*-tsan-qr/opt: ['trunk']
|
||||
|
@ -923,7 +878,6 @@ mochitest-webgl1-ext:
|
|||
loopback-video: true
|
||||
max-run-time: 2700
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- webgl-ipc+fission
|
||||
|
@ -934,10 +888,6 @@ mochitest-webgl1-ext:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
.*-tsan-qr/opt: ['trunk']
|
||||
|
@ -998,7 +948,6 @@ mochitest-webgl2-core:
|
|||
loopback-video: true
|
||||
max-run-time: 1800
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- webgl-ipc+fission
|
||||
|
@ -1009,10 +958,6 @@ mochitest-webgl2-core:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
.*-tsan-qr/opt: ['trunk']
|
||||
|
@ -1064,7 +1009,6 @@ mochitest-webgl2-ext:
|
|||
schedules-component: mochitest-plain
|
||||
virtualization: virtual-with-gpu
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- webgl-ipc+fission
|
||||
|
@ -1075,10 +1019,6 @@ mochitest-webgl2-ext:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
@ -1156,7 +1096,6 @@ mochitest-webgpu:
|
|||
virtualization: virtual-with-gpu
|
||||
loopback-video: true
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- socketprocess_networking+no-fission
|
||||
|
@ -1165,10 +1104,6 @@ mochitest-webgpu:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
@ -1206,7 +1141,6 @@ mochitest-remote:
|
|||
treeherder-symbol: M(remote)
|
||||
loopback-video: true
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- socketprocess_networking+no-fission
|
||||
|
@ -1217,10 +1151,6 @@ mochitest-remote:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
|
|
@ -168,14 +168,3 @@ devtools-no-eft:
|
|||
mozharness:
|
||||
extra-options:
|
||||
- "--setpref=devtools.every-frame-target.enabled=false"
|
||||
|
||||
dynamic-first-party-isolation:
|
||||
description: "{description} with dFPI enabled"
|
||||
component: "Core::Privacy: Anti-Tracking"
|
||||
suffix: "dfpi"
|
||||
replace:
|
||||
tier: 3
|
||||
merge:
|
||||
mozharness:
|
||||
extra-options:
|
||||
- "--setpref=network.cookie.cookieBehavior=5"
|
||||
|
|
|
@ -64,7 +64,6 @@ web-platform-tests:
|
|||
android-em.*/.*: 10800
|
||||
default: 7200
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- webrender-sw+no-fission
|
||||
|
@ -72,10 +71,6 @@ web-platform-tests:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
@ -159,7 +154,6 @@ web-platform-tests-reftest:
|
|||
android.*: 6
|
||||
default: 4
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- webrender-sw+no-fission
|
||||
|
@ -167,10 +161,6 @@ web-platform-tests-reftest:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
@ -246,7 +236,6 @@ web-platform-tests-wdspec:
|
|||
extra-options:
|
||||
- --test-type=wdspec
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- webrender-sw+no-fission
|
||||
|
@ -256,10 +245,6 @@ web-platform-tests-wdspec:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
@ -342,7 +327,6 @@ web-platform-tests-crashtest:
|
|||
extra-options:
|
||||
- --test-type=crashtest
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- webrender-sw+fission
|
||||
|
@ -350,10 +334,6 @@ web-platform-tests-crashtest:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
@ -388,7 +368,6 @@ web-platform-tests-print-reftest:
|
|||
test-manifest-loader: null # don't load tests in the taskgraph
|
||||
tier: default
|
||||
variants:
|
||||
- dynamic-first-party-isolation+fission
|
||||
- fission
|
||||
- no-fission
|
||||
- webrender-sw+fission
|
||||
|
@ -396,10 +375,6 @@ web-platform-tests-print-reftest:
|
|||
run-without-variant: false
|
||||
run-on-projects:
|
||||
by-variant:
|
||||
dynamic-first-party-isolation+fission:
|
||||
by-test-platform:
|
||||
(linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['mozilla-central']
|
||||
default: []
|
||||
fission:
|
||||
by-test-platform:
|
||||
android.*: []
|
||||
|
|
|
@ -67,7 +67,9 @@ def path_total(data, path):
|
|||
|
||||
# Make sure the value is sane. A misbehaving reporter could lead to
|
||||
# negative values.
|
||||
assert unclassified >= 0, "heap-unclassified was negative: %d" % unclassified
|
||||
# This assertion fails on Beta while running TP6, in the Google Docs process.
|
||||
# Disable this for now, but only on Beta. See bug 1735556.
|
||||
# assert unclassified >= 0, "heap-unclassified was negative: %d" % unclassified
|
||||
|
||||
return unclassified
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<!DOCTYPE html>
|
||||
<title>`intended direction` scroll snaps only at points ahead of the scroll direction</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" />
|
||||
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1766805">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<style>
|
||||
div {
|
||||
position: absolute;
|
||||
margin: 0px;
|
||||
}
|
||||
#scroller {
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
overflow: scroll;
|
||||
scroll-snap-type: x mandatory;
|
||||
}
|
||||
.snap {
|
||||
scroll-snap-align: start;
|
||||
background: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="scroller">
|
||||
<div style="width: 2000px; height: 100px;"></div>
|
||||
<div class="snap" style="left: 0px; width: 20px; height: 100px;">1</div>
|
||||
<div class="snap" style="left: 100px; width: 20px; height: 100px;">2</div>
|
||||
<div class="snap" style="left: 120px; width: 20px; height: 100px;">3</div>
|
||||
<div class="snap" style="left: 300px; width: 20px; height: 100px;">4</div>
|
||||
<div class="snap" style="left: 400px; width: 20px; height: 100px;">5</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
test(() => {
|
||||
scroller.scrollBy(10, 0);
|
||||
assert_equals(scroller.scrollLeft, 100);
|
||||
|
||||
scroller.scrollBy(10, 0);
|
||||
assert_equals(scroller.scrollLeft, 120);
|
||||
|
||||
scroller.scrollBy(10, 0);
|
||||
// Snaps to the next snap point even if the previous snap point is closer to
|
||||
// the current position.
|
||||
assert_equals(scroller.scrollLeft, 300);
|
||||
}, "`intended direction` scroll snaps only at points ahead of the scroll " +
|
||||
"direction");
|
||||
|
||||
</script>
|
|
@ -734,6 +734,17 @@ var DownloadIntegration = {
|
|||
fileExtension &&
|
||||
fileExtension.toLowerCase() == "exe";
|
||||
|
||||
let isExemptExecutableExtension = false;
|
||||
try {
|
||||
let url = new URL(aDownload.source.url);
|
||||
isExemptExecutableExtension = Services.policies.isExemptExecutableExtension(
|
||||
url.origin,
|
||||
fileExtension?.toLowerCase()
|
||||
);
|
||||
} catch (e) {
|
||||
// Invalid URL, go down the original path.
|
||||
}
|
||||
|
||||
// Ask for confirmation if the file is executable, except for .exe on
|
||||
// Windows where the operating system will show the prompt based on the
|
||||
// security zone. We do this here, instead of letting the caller handle
|
||||
|
@ -741,9 +752,11 @@ var DownloadIntegration = {
|
|||
// first is because of its security nature, so that add-ons cannot forget
|
||||
// to do this check. The second is that the system-level security prompt
|
||||
// would be displayed at launch time in any case.
|
||||
// We allow policy to override this behavior for file extensions on specific domains.
|
||||
if (
|
||||
file.isExecutable() &&
|
||||
!isWindowsExe &&
|
||||
!isExemptExecutableExtension &&
|
||||
!(await this.confirmLaunchExecutable(file.path))
|
||||
) {
|
||||
return;
|
||||
|
|
|
@ -427,6 +427,25 @@ EnterprisePoliciesManager.prototype = {
|
|||
allowedInstallSource(uri) {
|
||||
return InstallSources ? InstallSources.matches(uri) : true;
|
||||
},
|
||||
|
||||
isExemptExecutableExtension(origin, extension) {
|
||||
let hostname = new URL(origin)?.hostname;
|
||||
let exemptArray = this.getActivePolicies()
|
||||
?.ExemptDomainFileTypePairsFromFileTypeDownloadWarnings;
|
||||
if (!hostname || !extension || !exemptArray) {
|
||||
return false;
|
||||
}
|
||||
let domains = exemptArray
|
||||
.filter(item => item.file_extension == extension)
|
||||
.map(item => item.domains)
|
||||
.flat();
|
||||
for (let domain of domains) {
|
||||
if (Services.eTLD.hasRootDomain(hostname, domain)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
let DisallowedFeatures = {};
|
||||
|
|
|
@ -65,4 +65,11 @@ interface nsIEnterprisePolicies : nsISupports
|
|||
* @returns A boolean - true of the extension may be installed.
|
||||
*/
|
||||
bool allowedInstallSource(in nsIURI uri);
|
||||
/**
|
||||
* Uses ExemptDomainFileTypePairsFromFileTypeDownloadWarnings to determine
|
||||
* if a given file extension is exempted from executable warnings.
|
||||
*
|
||||
* @returns A boolean - true if warnings should not be shown.
|
||||
*/
|
||||
bool isExemptExecutableExtension(in ACString origin, in ACString extension);
|
||||
};
|
||||
|
|
|
@ -95,22 +95,85 @@ Services.cpmm.addMessageListener("clearRecipeCache", () => {
|
|||
|
||||
let gLastRightClickTimeStamp = Number.NEGATIVE_INFINITY;
|
||||
|
||||
// Input element on which enter keydown event was fired.
|
||||
let gKeyDownEnterForInput = null;
|
||||
|
||||
// Events on pages with Shadow DOM could return the shadow host element
|
||||
// (aEvent.target) rather than the actual username or password field
|
||||
// (aEvent.composedTarget).
|
||||
// Only allow input elements (can be extended later) to avoid false negatives.
|
||||
class WeakFieldSet extends WeakSet {
|
||||
add(value) {
|
||||
if (ChromeUtils.getClassName(value) != "HTMLInputElement") {
|
||||
if (!HTMLInputElement.isInstance(value)) {
|
||||
throw new Error("Non-field type added to a WeakFieldSet");
|
||||
}
|
||||
super.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
class LoginFormState {
|
||||
/**
|
||||
* Keeps track of filled fields and values.
|
||||
*/
|
||||
fillsByRootElement = new WeakMap();
|
||||
/**
|
||||
* Keeps track of fields we've filled with generated passwords
|
||||
*/
|
||||
generatedPasswordFields = new WeakFieldSet();
|
||||
/**
|
||||
* Keeps track of logins that were last submitted.
|
||||
*/
|
||||
lastSubmittedValuesByRootElement = new WeakMap();
|
||||
fieldModificationsByRootElement = new WeakMap();
|
||||
loginFormRootElements = new WeakSet();
|
||||
/**
|
||||
* Anything entered into an <input> that we think might be a username
|
||||
*/
|
||||
possibleUsernames = new Set();
|
||||
/**
|
||||
* Anything entered into an <input> that we think might be a password
|
||||
*/
|
||||
possiblePasswords = new Set();
|
||||
|
||||
/**
|
||||
* Keeps track of the formLike of nodes (form or formless password field)
|
||||
* that we are watching when they are removed from DOM.
|
||||
*/
|
||||
formLikeByObservedNode = new WeakMap();
|
||||
|
||||
/**
|
||||
* Keeps track of all formless password fields that have been
|
||||
* updated by the user.
|
||||
*/
|
||||
formlessModifiedPasswordFields = new WeakFieldSet();
|
||||
|
||||
/**
|
||||
* Caches the results of the username heuristics
|
||||
*/
|
||||
cachedIsInferredUsernameField = new WeakMap();
|
||||
cachedIsInferredEmailField = new WeakMap();
|
||||
cachedIsInferredLoginForm = new WeakMap();
|
||||
|
||||
/**
|
||||
* Records the mock username field when its associated form is submitted.
|
||||
*/
|
||||
mockUsernameOnlyField = null;
|
||||
|
||||
/**
|
||||
* Records the number of possible username event received for this document.
|
||||
*/
|
||||
numFormHasPossibleUsernameEvent = 0;
|
||||
|
||||
captureLoginTimeStamp = 0;
|
||||
|
||||
storeUserInput(field) {
|
||||
if (field.value && LoginHelper.captureInputChanges) {
|
||||
if (LoginHelper.isPasswordFieldType(field)) {
|
||||
this.possiblePasswords.add(field.value);
|
||||
} else if (LoginHelper.isUsernameFieldType(field)) {
|
||||
this.possibleUsernames.add(field.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const observer = {
|
||||
QueryInterface: ChromeUtils.generateQI([
|
||||
"nsIObserver",
|
||||
|
@ -128,27 +191,22 @@ const observer = {
|
|||
return;
|
||||
}
|
||||
|
||||
log(
|
||||
"onLocationChange handled:",
|
||||
aLocation.displaySpec,
|
||||
aWebProgress.DOMWindow.document
|
||||
);
|
||||
|
||||
LoginManagerChild.forWindow(aWebProgress.DOMWindow)._onNavigation(
|
||||
aWebProgress.DOMWindow.document
|
||||
);
|
||||
const window = aWebProgress.DOMWindow;
|
||||
log("onLocationChange handled:", aLocation.displaySpec, window.document);
|
||||
LoginManagerChild.forWindow(window)._onNavigation(window.document);
|
||||
},
|
||||
|
||||
onStateChange(aWebProgress, aRequest, aState, aStatus) {
|
||||
const window = aWebProgress.DOMWindow;
|
||||
const loginManagerChild = () => LoginManagerChild.forWindow(window);
|
||||
|
||||
if (
|
||||
aState & Ci.nsIWebProgressListener.STATE_RESTORING &&
|
||||
aState & Ci.nsIWebProgressListener.STATE_STOP
|
||||
) {
|
||||
// Re-fill a document restored from bfcache since password field values
|
||||
// aren't persisted there.
|
||||
LoginManagerChild.forWindow(aWebProgress.DOMWindow)._onDocumentRestored(
|
||||
aWebProgress.DOMWindow.document
|
||||
);
|
||||
loginManagerChild()._onDocumentRestored(window.document);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -181,9 +239,7 @@ const observer = {
|
|||
}
|
||||
|
||||
log("onStateChange handled:", channel);
|
||||
LoginManagerChild.forWindow(aWebProgress.DOMWindow)._onNavigation(
|
||||
aWebProgress.DOMWindow.document
|
||||
);
|
||||
loginManagerChild()._onNavigation(window.document);
|
||||
},
|
||||
|
||||
// nsIObserver
|
||||
|
@ -230,79 +286,68 @@ const observer = {
|
|||
return;
|
||||
}
|
||||
|
||||
let ownerDocument = aEvent.target.ownerDocument;
|
||||
let window = ownerDocument.defaultView;
|
||||
let docState = LoginManagerChild.forWindow(window).stateForDocument(
|
||||
ownerDocument
|
||||
);
|
||||
const ownerDocument = aEvent.target.ownerDocument;
|
||||
const window = ownerDocument.defaultView;
|
||||
const loginManagerChild = LoginManagerChild.forWindow(window);
|
||||
const docState = loginManagerChild.stateForDocument(ownerDocument);
|
||||
const field = aEvent.composedTarget;
|
||||
|
||||
switch (aEvent.type) {
|
||||
// Used to mask fields with filled generated passwords when blurred.
|
||||
case "blur": {
|
||||
if (docState.generatedPasswordFields.has(aEvent.composedTarget)) {
|
||||
let unmask = false;
|
||||
LoginManagerChild.forWindow(window)._togglePasswordFieldMasking(
|
||||
aEvent.composedTarget,
|
||||
unmask
|
||||
);
|
||||
if (docState.generatedPasswordFields.has(field)) {
|
||||
const unmask = false;
|
||||
loginManagerChild._togglePasswordFieldMasking(field, unmask);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Used to watch for changes to username and password fields.
|
||||
case "change": {
|
||||
let formLikeRoot = FormLikeFactory.findRootForField(
|
||||
aEvent.composedTarget
|
||||
);
|
||||
let formLikeRoot = FormLikeFactory.findRootForField(field);
|
||||
if (!docState.fieldModificationsByRootElement.get(formLikeRoot)) {
|
||||
log("Ignoring change event on form that hasn't been user-modified");
|
||||
if (aEvent.composedTarget.hasBeenTypePassword) {
|
||||
if (field.hasBeenTypePassword) {
|
||||
// Send notification that the password field has not been changed.
|
||||
// This is used only for testing.
|
||||
LoginManagerChild.forWindow(window)._ignorePasswordEdit();
|
||||
loginManagerChild._ignorePasswordEdit();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// _storeUserInput mutates docstate
|
||||
this._storeUserInput(docState, aEvent.composedTarget);
|
||||
docState.storeUserInput(field);
|
||||
let detail = {
|
||||
possibleValues: {
|
||||
usernames: docState.possibleUsernames,
|
||||
passwords: docState.possiblePasswords,
|
||||
},
|
||||
};
|
||||
LoginManagerChild.forWindow(window).sendAsyncMessage(
|
||||
loginManagerChild.sendAsyncMessage(
|
||||
"PasswordManager:updateDoorhangerSuggestions",
|
||||
detail
|
||||
);
|
||||
|
||||
if (aEvent.composedTarget.hasBeenTypePassword) {
|
||||
if (field.hasBeenTypePassword) {
|
||||
let triggeredByFillingGenerated = docState.generatedPasswordFields.has(
|
||||
aEvent.composedTarget
|
||||
field
|
||||
);
|
||||
// Autosave generated password initial fills and subsequent edits
|
||||
if (triggeredByFillingGenerated) {
|
||||
LoginManagerChild.forWindow(window)._passwordEditedOrGenerated(
|
||||
aEvent.composedTarget,
|
||||
{
|
||||
loginManagerChild._passwordEditedOrGenerated(field, {
|
||||
triggeredByFillingGenerated,
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Send a notification that we are not saving the edit to the password field.
|
||||
// This is used only for testing.
|
||||
LoginManagerChild.forWindow(window)._ignorePasswordEdit();
|
||||
loginManagerChild._ignorePasswordEdit();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "input": {
|
||||
let field = aEvent.composedTarget;
|
||||
let isPasswordType = LoginHelper.isPasswordFieldType(field);
|
||||
// React to input into fields filled with generated passwords.
|
||||
let loginManagerChild = LoginManagerChild.forWindow(window);
|
||||
if (
|
||||
docState.generatedPasswordFields.has(field) &&
|
||||
// Depending on the edit, we may no longer want to consider
|
||||
|
@ -353,7 +398,7 @@ const observer = {
|
|||
// Keep track of the modified formless password field to trigger form submission
|
||||
// when it is removed from DOM.
|
||||
let alreadyModifiedFormLessField = true;
|
||||
if (ChromeUtils.getClassName(formLikeRoot) !== "HTMLFormElement") {
|
||||
if (!HTMLFormElement.isInstance(formLikeRoot)) {
|
||||
alreadyModifiedFormLessField = docState.formlessModifiedPasswordFields.has(
|
||||
field
|
||||
);
|
||||
|
@ -396,32 +441,25 @@ const observer = {
|
|||
let form = LoginFormFactory.createFromField(field);
|
||||
if (
|
||||
docState.generatedPasswordFields.has(field) &&
|
||||
LoginManagerChild.forWindow(window)._getFormFields(form)
|
||||
.confirmPasswordField === field
|
||||
loginManagerChild._getFormFields(form).confirmPasswordField ===
|
||||
field
|
||||
) {
|
||||
break;
|
||||
}
|
||||
// Don't check for triggeredByFillingGenerated, as we do not want to autosave
|
||||
// a field marked as a generated password field on every "input" event
|
||||
LoginManagerChild.forWindow(window)._passwordEditedOrGenerated(field);
|
||||
loginManagerChild._passwordEditedOrGenerated(field);
|
||||
} else {
|
||||
let [usernameField, passwordField] = LoginManagerChild.forWindow(
|
||||
window
|
||||
).getUserNameAndPasswordFields(field);
|
||||
if (
|
||||
usernameField &&
|
||||
field == usernameField &&
|
||||
passwordField &&
|
||||
passwordField.value
|
||||
) {
|
||||
LoginManagerChild.forWindow(window)._passwordEditedOrGenerated(
|
||||
let [
|
||||
usernameField,
|
||||
passwordField,
|
||||
{
|
||||
] = loginManagerChild.getUserNameAndPasswordFields(field);
|
||||
if (field == usernameField && passwordField?.value) {
|
||||
loginManagerChild._passwordEditedOrGenerated(passwordField, {
|
||||
triggeredByFillingGenerated: docState.generatedPasswordFields.has(
|
||||
passwordField
|
||||
),
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -429,7 +467,7 @@ const observer = {
|
|||
|
||||
case "keydown": {
|
||||
if (
|
||||
aEvent.composedTarget.value &&
|
||||
field.value &&
|
||||
(aEvent.keyCode == aEvent.DOM_VK_TAB ||
|
||||
aEvent.keyCode == aEvent.DOM_VK_RETURN)
|
||||
) {
|
||||
|
@ -440,9 +478,7 @@ const observer = {
|
|||
);
|
||||
|
||||
if (autofillForm) {
|
||||
LoginManagerChild.forWindow(window).onUsernameAutocompleted(
|
||||
aEvent.composedTarget
|
||||
);
|
||||
loginManagerChild.onUsernameAutocompleted(field);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -450,20 +486,17 @@ const observer = {
|
|||
|
||||
case "focus": {
|
||||
if (
|
||||
aEvent.composedTarget.hasBeenTypePassword &&
|
||||
docState.generatedPasswordFields.has(aEvent.composedTarget)
|
||||
field.hasBeenTypePassword &&
|
||||
docState.generatedPasswordFields.has(field)
|
||||
) {
|
||||
// Used to unmask fields with filled generated passwords when focused.
|
||||
let unmask = true;
|
||||
LoginManagerChild.forWindow(window)._togglePasswordFieldMasking(
|
||||
aEvent.composedTarget,
|
||||
unmask
|
||||
);
|
||||
const unmask = true;
|
||||
loginManagerChild._togglePasswordFieldMasking(field, unmask);
|
||||
break;
|
||||
}
|
||||
|
||||
// Only used for username fields.
|
||||
LoginManagerChild.forWindow(window)._onUsernameFocus(aEvent);
|
||||
loginManagerChild._onUsernameFocus(aEvent);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -482,33 +515,12 @@ const observer = {
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
_storeUserInput(docState, field) {
|
||||
if (
|
||||
!Services.prefs.getBoolPref(
|
||||
"signon.capture.inputChanges.enabled",
|
||||
false
|
||||
) ||
|
||||
!field.value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (LoginHelper.isPasswordFieldType(field)) {
|
||||
docState.possiblePasswords.add(field.value);
|
||||
} else if (LoginHelper.isUsernameFieldType(field)) {
|
||||
docState.possibleUsernames.add(field.value);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Add this observer once for the process.
|
||||
Services.obs.addObserver(observer, "autocomplete-did-enter-text");
|
||||
|
||||
this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
/**
|
||||
* WeakMap of the root element of a LoginForm to the DeferredTask to fill its fields.
|
||||
*
|
||||
|
@ -518,7 +530,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
*
|
||||
* @type {WeakMap}
|
||||
*/
|
||||
this._deferredPasswordAddedTasksByRootElement = new WeakMap();
|
||||
#deferredPasswordAddedTasksByRootElement = new WeakMap();
|
||||
|
||||
/**
|
||||
* WeakMap of a document to the array of callbacks to execute when it becomes visible
|
||||
|
@ -529,28 +541,22 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
*
|
||||
* @type {WeakMap}
|
||||
*/
|
||||
this._visibleTasksByDocument = new WeakMap();
|
||||
#visibleTasksByDocument = new WeakMap();
|
||||
|
||||
/**
|
||||
* Maps all DOM content documents in this content process, including those in
|
||||
* frames, to the current state used by the Login Manager.
|
||||
*/
|
||||
this._loginFormStateByDocument = new WeakMap();
|
||||
#loginFormStateByDocument = new WeakMap();
|
||||
|
||||
/**
|
||||
* Set of fields where the user specifically requested password generation
|
||||
* (from the context menu) even if we wouldn't offer it on this field by default.
|
||||
*/
|
||||
this._fieldsWithPasswordGenerationForcedOn = new WeakSet();
|
||||
}
|
||||
#fieldsWithPasswordGenerationForcedOn = new WeakSet();
|
||||
|
||||
static forWindow(window) {
|
||||
let windowGlobal = window.windowGlobalChild;
|
||||
if (windowGlobal) {
|
||||
return windowGlobal.getActor("LoginManager");
|
||||
}
|
||||
|
||||
return null;
|
||||
return window.windowGlobalChild?.getActor("LoginManager");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -639,7 +645,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
break;
|
||||
}
|
||||
|
||||
this._fieldsWithPasswordGenerationForcedOn.add(inputElement);
|
||||
this.#fieldsWithPasswordGenerationForcedOn.add(inputElement);
|
||||
this.repopulateAutocompletePopup();
|
||||
break;
|
||||
}
|
||||
|
@ -650,7 +656,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
}
|
||||
|
||||
case "PasswordManager:formIsPending": {
|
||||
return this._visibleTasksByDocument.has(this.document);
|
||||
return this.#visibleTasksByDocument.has(this.document);
|
||||
}
|
||||
|
||||
case "PasswordManager:formProcessed": {
|
||||
|
@ -689,55 +695,36 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
return;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case "DOMDocFetchSuccess": {
|
||||
if (this.shouldIgnoreLoginManagerEvent(event)) {
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
this.onDOMDocFetchSuccess(event);
|
||||
switch (event.type) {
|
||||
case "DOMDocFetchSuccess": {
|
||||
this.#onDOMDocFetchSuccess(event);
|
||||
break;
|
||||
}
|
||||
case "DOMFormBeforeSubmit": {
|
||||
if (this.shouldIgnoreLoginManagerEvent(event)) {
|
||||
break;
|
||||
}
|
||||
|
||||
this.onDOMFormBeforeSubmit(event);
|
||||
this.#onDOMFormBeforeSubmit(event);
|
||||
break;
|
||||
}
|
||||
case "DOMFormHasPassword": {
|
||||
if (this.shouldIgnoreLoginManagerEvent(event)) {
|
||||
break;
|
||||
}
|
||||
|
||||
this.onDOMFormHasPassword(event);
|
||||
this.#onDOMFormHasPassword(event);
|
||||
let formLike = LoginFormFactory.createFromForm(event.originalTarget);
|
||||
InsecurePasswordUtils.reportInsecurePasswords(formLike);
|
||||
break;
|
||||
}
|
||||
case "DOMFormHasPossibleUsername": {
|
||||
if (this.shouldIgnoreLoginManagerEvent(event)) {
|
||||
break;
|
||||
}
|
||||
|
||||
this.onDOMFormHasPossibleUsername(event);
|
||||
this.#onDOMFormHasPossibleUsername(event);
|
||||
break;
|
||||
}
|
||||
case "DOMFormRemoved":
|
||||
case "DOMInputPasswordRemoved": {
|
||||
if (this.shouldIgnoreLoginManagerEvent(event)) {
|
||||
break;
|
||||
}
|
||||
this.onDOMFormRemoved(event);
|
||||
this.#onDOMFormRemoved(event);
|
||||
break;
|
||||
}
|
||||
case "DOMInputPasswordAdded": {
|
||||
if (this.shouldIgnoreLoginManagerEvent(event)) {
|
||||
break;
|
||||
}
|
||||
|
||||
this.onDOMInputPasswordAdded(event, this.document.defaultView);
|
||||
this.#onDOMInputPasswordAdded(event, this.document.defaultView);
|
||||
let formLike = LoginFormFactory.createFromField(event.originalTarget);
|
||||
InsecurePasswordUtils.reportInsecurePasswords(formLike);
|
||||
break;
|
||||
|
@ -759,9 +746,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
*/
|
||||
_getLoginDataFromParent(form, options) {
|
||||
let actionOrigin = LoginHelper.getFormActionOrigin(form);
|
||||
|
||||
let messageData = { actionOrigin, options };
|
||||
|
||||
let resultPromise = this.sendQuery(
|
||||
"PasswordManager:findLogins",
|
||||
messageData
|
||||
|
@ -785,7 +770,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
let docShell;
|
||||
for (
|
||||
let browsingContext = BrowsingContext.getFromWindow(window);
|
||||
browsingContext && browsingContext.docShell;
|
||||
browsingContext?.docShell;
|
||||
browsingContext = browsingContext.parent
|
||||
) {
|
||||
docShell = browsingContext.docShell;
|
||||
|
@ -809,7 +794,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
* This method sets up form removal listener for form and password fields that
|
||||
* users have interacted with.
|
||||
*/
|
||||
onDOMDocFetchSuccess(event) {
|
||||
#onDOMDocFetchSuccess(event) {
|
||||
let document = event.target;
|
||||
let docState = this.stateForDocument(document);
|
||||
let weakModificationsRootElements = ChromeUtils.nondeterministicGetWeakMapKeys(
|
||||
|
@ -837,7 +822,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
);
|
||||
|
||||
for (let rootElement of weakModificationsRootElements) {
|
||||
if (ChromeUtils.getClassName(rootElement) === "HTMLFormElement") {
|
||||
if (HTMLFormElement.isInstance(rootElement)) {
|
||||
// If we create formLike when it is removed, we might not have the
|
||||
// right elements at that point, so create formLike object now.
|
||||
let formLike = LoginFormFactory.createFromForm(rootElement);
|
||||
|
@ -880,10 +865,9 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
* 3. When a form is removed, onDOMFormRemoved triggers the login capture
|
||||
* code.
|
||||
*/
|
||||
onDOMFormRemoved(event) {
|
||||
#onDOMFormRemoved(event) {
|
||||
let document = event.composedTarget.ownerDocument;
|
||||
let docState = this.stateForDocument(document);
|
||||
|
||||
let formLike = docState.formLikeByObservedNode.get(event.target);
|
||||
if (!formLike) {
|
||||
return;
|
||||
|
@ -910,7 +894,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
}
|
||||
}
|
||||
|
||||
onDOMFormBeforeSubmit(event) {
|
||||
#onDOMFormBeforeSubmit(event) {
|
||||
if (!event.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
@ -927,7 +911,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
return;
|
||||
}
|
||||
let document = event.target;
|
||||
let onVisibleTasks = this._visibleTasksByDocument.get(document);
|
||||
let onVisibleTasks = this.#visibleTasksByDocument.get(document);
|
||||
if (!onVisibleTasks) {
|
||||
return;
|
||||
}
|
||||
|
@ -935,20 +919,20 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
log("onDocumentVisibilityChange, executing queued task");
|
||||
task();
|
||||
}
|
||||
this._visibleTasksByDocument.delete(document);
|
||||
this.#visibleTasksByDocument.delete(document);
|
||||
}
|
||||
|
||||
_deferHandlingEventUntilDocumentVisible(event, document, fn) {
|
||||
log(
|
||||
`document.visibilityState: ${document.visibilityState}, defer handling ${event.type}`
|
||||
);
|
||||
let onVisibleTasks = this._visibleTasksByDocument.get(document);
|
||||
let onVisibleTasks = this.#visibleTasksByDocument.get(document);
|
||||
if (!onVisibleTasks) {
|
||||
log(
|
||||
`deferHandling, first queued event, register the visibilitychange handler`
|
||||
);
|
||||
onVisibleTasks = [];
|
||||
this._visibleTasksByDocument.set(document, onVisibleTasks);
|
||||
this.#visibleTasksByDocument.set(document, onVisibleTasks);
|
||||
document.addEventListener(
|
||||
"visibilitychange",
|
||||
event => {
|
||||
|
@ -960,13 +944,15 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
onVisibleTasks.push(fn);
|
||||
}
|
||||
|
||||
onDOMFormHasPassword(event) {
|
||||
#getIsPrimaryPasswordSet() {
|
||||
return Services.cpmm.sharedData.get("isPrimaryPasswordSet");
|
||||
}
|
||||
|
||||
#onDOMFormHasPassword(event) {
|
||||
if (!event.isTrusted) {
|
||||
return;
|
||||
}
|
||||
let isPrimaryPasswordSet = Services.cpmm.sharedData.get(
|
||||
"isPrimaryPasswordSet"
|
||||
);
|
||||
const isPrimaryPasswordSet = this.#getIsPrimaryPasswordSet();
|
||||
let document = event.target.ownerDocument;
|
||||
|
||||
// don't attempt to defer handling when a primary password is set
|
||||
|
@ -996,13 +982,11 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
this._fetchLoginsFromParentAndFillForm(formLike);
|
||||
}
|
||||
|
||||
onDOMFormHasPossibleUsername(event) {
|
||||
#onDOMFormHasPossibleUsername(event) {
|
||||
if (!event.isTrusted) {
|
||||
return;
|
||||
}
|
||||
let isPrimaryPasswordSet = Services.cpmm.sharedData.get(
|
||||
"isPrimaryPasswordSet"
|
||||
);
|
||||
const isPrimaryPasswordSet = this.#getIsPrimaryPasswordSet();
|
||||
let document = event.target.ownerDocument;
|
||||
|
||||
log(
|
||||
|
@ -1066,7 +1050,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
.add(!!usernameField);
|
||||
}
|
||||
|
||||
onDOMInputPasswordAdded(event, window) {
|
||||
#onDOMInputPasswordAdded(event, window) {
|
||||
if (!event.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
@ -1080,9 +1064,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
}
|
||||
|
||||
let document = pwField.ownerDocument;
|
||||
let isPrimaryPasswordSet = Services.cpmm.sharedData.get(
|
||||
"isPrimaryPasswordSet"
|
||||
);
|
||||
const isPrimaryPasswordSet = this.#getIsPrimaryPasswordSet();
|
||||
log(
|
||||
"onDOMInputPasswordAdded, visibilityState:",
|
||||
document.visibilityState,
|
||||
|
@ -1105,11 +1087,10 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
|
||||
_processDOMInputPasswordAddedEvent(event) {
|
||||
let pwField = event.originalTarget;
|
||||
|
||||
let formLike = LoginFormFactory.createFromField(pwField);
|
||||
log(" _processDOMInputPasswordAddedEvent:", pwField, formLike);
|
||||
|
||||
let deferredTask = this._deferredPasswordAddedTasksByRootElement.get(
|
||||
let deferredTask = this.#deferredPasswordAddedTasksByRootElement.get(
|
||||
formLike.rootElement
|
||||
);
|
||||
if (!deferredTask) {
|
||||
|
@ -1129,7 +1110,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
"Running deferred processing of onDOMInputPasswordAdded",
|
||||
formLike2
|
||||
);
|
||||
this._deferredPasswordAddedTasksByRootElement.delete(
|
||||
this.#deferredPasswordAddedTasksByRootElement.delete(
|
||||
formLike2.rootElement
|
||||
);
|
||||
this._fetchLoginsFromParentAndFillForm(formLike2);
|
||||
|
@ -1138,7 +1119,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
0
|
||||
);
|
||||
|
||||
this._deferredPasswordAddedTasksByRootElement.set(
|
||||
this.#deferredPasswordAddedTasksByRootElement.set(
|
||||
formLike.rootElement,
|
||||
deferredTask
|
||||
);
|
||||
|
@ -1199,7 +1180,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
}
|
||||
|
||||
isPasswordGenerationForcedOn(passwordField) {
|
||||
return this._fieldsWithPasswordGenerationForcedOn.has(passwordField);
|
||||
return this.#fieldsWithPasswordGenerationForcedOn.has(passwordField);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1207,64 +1188,10 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
* document. This is initialized to an object with default values.
|
||||
*/
|
||||
stateForDocument(document) {
|
||||
let loginFormState = this._loginFormStateByDocument.get(document);
|
||||
let loginFormState = this.#loginFormStateByDocument.get(document);
|
||||
if (!loginFormState) {
|
||||
loginFormState = {
|
||||
/**
|
||||
* Keeps track of filled fields and values.
|
||||
*/
|
||||
fillsByRootElement: new WeakMap(),
|
||||
/**
|
||||
* Keeps track of fields we've filled with generated passwords
|
||||
*/
|
||||
generatedPasswordFields: new WeakFieldSet(),
|
||||
/**
|
||||
* Keeps track of logins that were last submitted.
|
||||
*/
|
||||
lastSubmittedValuesByRootElement: new WeakMap(),
|
||||
fieldModificationsByRootElement: new WeakMap(),
|
||||
loginFormRootElements: new WeakSet(),
|
||||
/**
|
||||
* Anything entered into an <input> that we think might be a username
|
||||
*/
|
||||
possibleUsernames: new Set(),
|
||||
/**
|
||||
* Anything entered into an <input> that we think might be a password
|
||||
*/
|
||||
possiblePasswords: new Set(),
|
||||
|
||||
/**
|
||||
* Keeps track of the formLike of nodes (form or formless password field)
|
||||
* that we are watching when they are removed from DOM.
|
||||
*/
|
||||
formLikeByObservedNode: new WeakMap(),
|
||||
|
||||
/**
|
||||
* Keeps track of all formless password fields that have been
|
||||
* updated by the user.
|
||||
*/
|
||||
formlessModifiedPasswordFields: new WeakFieldSet(),
|
||||
|
||||
/**
|
||||
* Caches the results of the username heuristics
|
||||
*/
|
||||
cachedIsInferredUsernameField: new WeakMap(),
|
||||
cachedIsInferredEmailField: new WeakMap(),
|
||||
cachedIsInferredLoginForm: new WeakMap(),
|
||||
|
||||
/**
|
||||
* Records the mock username field when its associated form is submitted.
|
||||
*/
|
||||
mockUsernameOnlyField: null,
|
||||
|
||||
/**
|
||||
* Records the number of possible username event received for this document.
|
||||
*/
|
||||
numFormHasPossibleUsernameEvent: 0,
|
||||
|
||||
captureLoginTimeStamp: 0,
|
||||
};
|
||||
this._loginFormStateByDocument.set(document, loginFormState);
|
||||
loginFormState = new LoginFormState();
|
||||
this.#loginFormStateByDocument.set(document, loginFormState);
|
||||
}
|
||||
return loginFormState;
|
||||
}
|
||||
|
@ -1314,7 +1241,6 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
|
||||
if (!originMatches) {
|
||||
if (
|
||||
!inputElement ||
|
||||
LoginHelper.getLoginOrigin(inputElement.ownerDocument.documentURI) !=
|
||||
loginFormOrigin
|
||||
) {
|
||||
|
@ -1408,9 +1334,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
}
|
||||
|
||||
// This is probably a bit over-conservatative.
|
||||
if (
|
||||
ChromeUtils.getClassName(acInputField.ownerDocument) != "HTMLDocument"
|
||||
) {
|
||||
if (!Document.isInstance(acInputField.ownerDocument)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1509,7 +1433,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
for (let i = 0; i < form.elements.length; i++) {
|
||||
let element = form.elements[i];
|
||||
if (
|
||||
ChromeUtils.getClassName(element) !== "HTMLInputElement" ||
|
||||
!HTMLInputElement.isInstance(element) ||
|
||||
!element.hasBeenTypePassword ||
|
||||
(!element.isConnected && !ignoreConnect)
|
||||
) {
|
||||
|
@ -1518,8 +1442,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
|
||||
// Exclude ones matching a `notPasswordSelector`, if specified.
|
||||
if (
|
||||
fieldOverrideRecipe &&
|
||||
fieldOverrideRecipe.notPasswordSelector &&
|
||||
fieldOverrideRecipe?.notPasswordSelector &&
|
||||
element.matches(fieldOverrideRecipe.notPasswordSelector)
|
||||
) {
|
||||
log(
|
||||
|
@ -1702,8 +1625,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
}
|
||||
|
||||
if (
|
||||
fieldOverrideRecipe &&
|
||||
fieldOverrideRecipe.notUsernameSelector &&
|
||||
fieldOverrideRecipe?.notUsernameSelector &&
|
||||
element.matches(fieldOverrideRecipe.notUsernameSelector)
|
||||
) {
|
||||
continue;
|
||||
|
@ -1853,7 +1775,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
* specified element.
|
||||
*/
|
||||
_isAutocompleteDisabled(element) {
|
||||
return element && element.autocomplete == "off";
|
||||
return element?.autocomplete == "off";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1979,7 +1901,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
let win = doc.defaultView;
|
||||
let logMessagePrefix = isSubmission ? "form submission" : "field edit";
|
||||
let passwordField = null;
|
||||
if (targetField && targetField.hasBeenTypePassword) {
|
||||
if (targetField?.hasBeenTypePassword) {
|
||||
passwordField = targetField;
|
||||
}
|
||||
|
||||
|
@ -2061,7 +1983,6 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
let logMessagePrefix = isSubmission ? "form submission" : "field edit";
|
||||
let doc = form.ownerDocument;
|
||||
let win = doc.defaultView;
|
||||
|
||||
let detail = { messageSent: false };
|
||||
try {
|
||||
// when filling a generated password, we do still want to message the parent
|
||||
|
@ -2101,7 +2022,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
usernameField = docState.mockUsernameOnlyField;
|
||||
}
|
||||
}
|
||||
if (usernameField && usernameField.value.match(/\.{3,}|\*{3,}|•{3,}/)) {
|
||||
if (usernameField?.value.match(/\.{3,}|\*{3,}|•{3,}/)) {
|
||||
log(
|
||||
`usernameField.value "${usernameField.value}" looks munged, setting to null`
|
||||
);
|
||||
|
@ -2135,7 +2056,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
? { name: oldPasswordField.name, value: oldPasswordField.value }
|
||||
: null;
|
||||
|
||||
let usernameValue = usernameField ? usernameField.value : null;
|
||||
let usernameValue = usernameField?.value;
|
||||
// Dismiss prompt if the username field is a credit card number AND
|
||||
// if the password field is a three digit number. Also dismiss prompt if
|
||||
// the password is a credit card number and the password field has attribute
|
||||
|
@ -2328,7 +2249,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
// Once the generated password was filled we no longer want to autocomplete
|
||||
// saved logins into a non-empty password field (see LoginAutoComplete.startSearch)
|
||||
// because it is confusing.
|
||||
this._fieldsWithPasswordGenerationForcedOn.delete(passwordField);
|
||||
this.#fieldsWithPasswordGenerationForcedOn.delete(passwordField);
|
||||
}
|
||||
|
||||
this._maybeSendFormInteractionMessage(
|
||||
|
@ -2378,7 +2299,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
let acFieldName = passwordField.getAutocompleteInfo()?.fieldName;
|
||||
|
||||
// Match same autocomplete values first
|
||||
if (acFieldName && acFieldName == "new-password") {
|
||||
if (acFieldName == "new-password") {
|
||||
let matchIndex = afterFields.findIndex(
|
||||
elem =>
|
||||
LoginHelper.isPasswordFieldType(elem) &&
|
||||
|
@ -2547,7 +2468,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
style = null,
|
||||
} = {}
|
||||
) {
|
||||
if (ChromeUtils.getClassName(form) === "HTMLFormElement") {
|
||||
if (HTMLFormElement.isInstance(form)) {
|
||||
throw new Error("_fillForm should only be called with LoginForm objects");
|
||||
}
|
||||
|
||||
|
@ -2874,7 +2795,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
this._highlightFilledField(passwordField);
|
||||
}
|
||||
|
||||
if (style && style === "generatedPassword") {
|
||||
if (style === "generatedPassword") {
|
||||
this._filledWithGeneratedPassword(passwordField);
|
||||
}
|
||||
|
||||
|
@ -3028,7 +2949,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
*/
|
||||
getUserNameAndPasswordFields(aField) {
|
||||
let noResult = [null, null, null];
|
||||
if (ChromeUtils.getClassName(aField) !== "HTMLInputElement") {
|
||||
if (!HTMLInputElement.isInstance(aField)) {
|
||||
throw new Error("getUserNameAndPasswordFields: input element required");
|
||||
}
|
||||
|
||||
|
@ -3071,7 +2992,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
getFieldContext(aField) {
|
||||
// If the element is not a proper form field, return null.
|
||||
if (
|
||||
ChromeUtils.getClassName(aField) !== "HTMLInputElement" ||
|
||||
!HTMLInputElement.isInstance(aField) ||
|
||||
(!aField.hasBeenTypePassword &&
|
||||
!LoginHelper.isUsernameFieldType(aField)) ||
|
||||
aField.nodePrincipal.isNullPrincipal ||
|
||||
|
@ -3134,7 +3055,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
|||
* username-only form).
|
||||
*/
|
||||
getUsernameFieldFromUsernameOnlyForm(formElement, recipe = null) {
|
||||
if (ChromeUtils.getClassName(formElement) !== "HTMLFormElement") {
|
||||
if (!HTMLFormElement.isInstance(formElement)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ class _RFPHelper {
|
|||
|
||||
_removeRFPObservers() {
|
||||
try {
|
||||
Services.pref.removeObserver(kPrefSpoofEnglish, this);
|
||||
Services.prefs.removeObserver(kPrefSpoofEnglish, this);
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
|
|
|
@ -1020,6 +1020,7 @@ STATIC_ATOMS = [
|
|||
Atom("previewDiv", "preview-div"),
|
||||
Atom("primary", "primary"),
|
||||
Atom("print", "print"),
|
||||
Atom("printisfocuseddoc", "printisfocuseddoc"),
|
||||
Atom("printselectionranges", "printselectionranges"),
|
||||
Atom("priority", "priority"),
|
||||
Atom("processingInstruction", "processing-instruction"),
|
||||
|
|
Загрузка…
Ссылка в новой задаче