Merge autoland to mozilla-central a=merge

This commit is contained in:
Andreea Pavel 2022-05-13 00:30:12 +03:00
Родитель ba8673637d f69adc8eac
Коммит 97c902e8f9
61 изменённых файлов: 1401 добавлений и 1059 удалений

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

@ -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 wont 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 cant 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"),