summaryrefslogtreecommitdiffstats
path: root/dom/base
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:29 +0000
commit59203c63bb777a3bacec32fb8830fba33540e809 (patch)
tree58298e711c0ff0575818c30485b44a2f21bf28a0 /dom/base
parentAdding upstream version 126.0.1. (diff)
downloadfirefox-59203c63bb777a3bacec32fb8830fba33540e809.tar.xz
firefox-59203c63bb777a3bacec32fb8830fba33540e809.zip
Adding upstream version 127.0.upstream/127.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/base')
-rw-r--r--dom/base/AbstractRange.cpp8
-rw-r--r--dom/base/BodyConsumer.cpp25
-rw-r--r--dom/base/ContentIterator.cpp2
-rw-r--r--dom/base/CrossShadowBoundaryRange.cpp115
-rw-r--r--dom/base/CrossShadowBoundaryRange.h89
-rw-r--r--dom/base/DOMIntersectionObserver.cpp3
-rw-r--r--dom/base/Document.cpp70
-rw-r--r--dom/base/Document.h5
-rw-r--r--dom/base/Element.cpp4
-rw-r--r--dom/base/EventSource.cpp6
-rw-r--r--dom/base/FocusModel.h41
-rw-r--r--dom/base/FragmentOrElement.cpp14
-rw-r--r--dom/base/Highlight.cpp40
-rw-r--r--dom/base/HighlightRegistry.cpp22
-rw-r--r--dom/base/MimeType.cpp14
-rw-r--r--dom/base/RadioGroupContainer.cpp8
-rw-r--r--dom/base/ResizeObserver.h1
-rw-r--r--dom/base/Selection.h24
-rw-r--r--dom/base/StaticRange.h12
-rw-r--r--dom/base/crashtests/1419902.html24
-rw-r--r--dom/base/crashtests/1741957.html22
-rw-r--r--dom/base/crashtests/crashtests.list3
-rw-r--r--dom/base/fragmentdirectives/lib.rs4
-rw-r--r--dom/base/fragmentdirectives/test.rs20
-rw-r--r--dom/base/moz.build3
-rw-r--r--dom/base/nsAttrValue.cpp32
-rw-r--r--dom/base/nsAttrValue.h4
-rw-r--r--dom/base/nsContentAreaDragDrop.cpp2
-rw-r--r--dom/base/nsContentUtils.cpp81
-rw-r--r--dom/base/nsContentUtils.h22
-rw-r--r--dom/base/nsCopySupport.cpp26
-rw-r--r--dom/base/nsCopySupport.h4
-rw-r--r--dom/base/nsDOMWindowUtils.cpp8
-rw-r--r--dom/base/nsFocusManager.cpp70
-rw-r--r--dom/base/nsGlobalWindowInner.cpp33
-rw-r--r--dom/base/nsGlobalWindowInner.h8
-rw-r--r--dom/base/nsIContent.h23
-rw-r--r--dom/base/nsJSEnvironment.cpp5
-rw-r--r--dom/base/nsRange.cpp46
-rw-r--r--dom/base/nsRange.h17
-rw-r--r--dom/base/nsTextFragment.cpp40
-rw-r--r--dom/base/nsTextFragment.h17
-rw-r--r--dom/base/test/browser_page_load_event_telemetry.js10
-rw-r--r--dom/base/test/chrome/bug418986-1.js2
-rw-r--r--dom/base/test/fullscreen/browser.toml3
-rw-r--r--dom/base/test/jsmodules/importmaps/bug_1893164_module_1.mjs3
-rw-r--r--dom/base/test/jsmodules/importmaps/bug_1893164_module_2.mjs5
-rw-r--r--dom/base/test/jsmodules/importmaps/bug_1893164_module_3.mjs1
-rw-r--r--dom/base/test/jsmodules/importmaps/bug_1894631_module_1.mjs5
-rw-r--r--dom/base/test/jsmodules/importmaps/bug_1894631_module_2.mjs3
-rw-r--r--dom/base/test/jsmodules/importmaps/bug_1894631_module_3.mjs1
-rw-r--r--dom/base/test/jsmodules/importmaps/bug_1894631_module_4.mjs1
-rw-r--r--dom/base/test/jsmodules/importmaps/mochitest.toml10
-rw-r--r--dom/base/test/jsmodules/importmaps/test_bug_1893164.html20
-rw-r--r--dom/base/test/jsmodules/importmaps/test_shared_submodules_with_modulepreload.html30
-rw-r--r--dom/base/test/test_anchor_target_blank_referrer.html3
-rw-r--r--dom/base/test/test_focus_radio.html69
57 files changed, 833 insertions, 350 deletions
diff --git a/dom/base/AbstractRange.cpp b/dom/base/AbstractRange.cpp
index c9138a19d2..f5d584599b 100644
--- a/dom/base/AbstractRange.cpp
+++ b/dom/base/AbstractRange.cpp
@@ -49,6 +49,8 @@ template nsresult AbstractRange::SetStartAndEndInternal(
const RawRangeBoundary& aEndBoundary, StaticRange* aRange);
template bool AbstractRange::MaybeCacheToReuse(nsRange& aInstance);
template bool AbstractRange::MaybeCacheToReuse(StaticRange& aInstance);
+template bool AbstractRange::MaybeCacheToReuse(
+ CrossShadowBoundaryRange& aInstance);
bool AbstractRange::sHasShutDown = false;
@@ -209,6 +211,12 @@ void AbstractRange::Shutdown() {
cachedRanges->Clear();
delete cachedRanges;
}
+ if (nsTArray<RefPtr<CrossShadowBoundaryRange>>* cachedRanges =
+ CrossShadowBoundaryRange::sCachedRanges) {
+ CrossShadowBoundaryRange::sCachedRanges = nullptr;
+ cachedRanges->Clear();
+ delete cachedRanges;
+ }
}
// static
diff --git a/dom/base/BodyConsumer.cpp b/dom/base/BodyConsumer.cpp
index 754586a08b..81c9bb113d 100644
--- a/dom/base/BodyConsumer.cpp
+++ b/dom/base/BodyConsumer.cpp
@@ -74,7 +74,7 @@ class ContinueConsumeBodyRunnable final : public MainThreadWorkerRunnable {
ContinueConsumeBodyRunnable(BodyConsumer* aBodyConsumer,
WorkerPrivate* aWorkerPrivate, nsresult aStatus,
uint32_t aLength, uint8_t* aResult)
- : MainThreadWorkerRunnable(aWorkerPrivate, "ContinueConsumeBodyRunnable"),
+ : MainThreadWorkerRunnable("ContinueConsumeBodyRunnable"),
mBodyConsumer(aBodyConsumer),
mStatus(aStatus),
mLength(aLength),
@@ -97,7 +97,7 @@ class AbortConsumeBodyControlRunnable final
public:
AbortConsumeBodyControlRunnable(BodyConsumer* aBodyConsumer,
WorkerPrivate* aWorkerPrivate)
- : MainThreadWorkerControlRunnable(aWorkerPrivate),
+ : MainThreadWorkerControlRunnable("AbortConsumeBodyControlRunnable"),
mBodyConsumer(aBodyConsumer) {
MOZ_ASSERT(NS_IsMainThread());
}
@@ -131,7 +131,7 @@ class MOZ_STACK_CLASS AutoFailConsumeBody final {
RefPtr<AbortConsumeBodyControlRunnable> r =
new AbortConsumeBodyControlRunnable(mBodyConsumer,
mWorkerRef->Private());
- if (!r->Dispatch()) {
+ if (!r->Dispatch(mWorkerRef->Private())) {
MOZ_CRASH("We are going to leak");
}
return;
@@ -159,8 +159,7 @@ class ContinueConsumeBlobBodyRunnable final : public MainThreadWorkerRunnable {
ContinueConsumeBlobBodyRunnable(BodyConsumer* aBodyConsumer,
WorkerPrivate* aWorkerPrivate,
BlobImpl* aBlobImpl)
- : MainThreadWorkerRunnable(aWorkerPrivate,
- "ContinueConsumeBlobBodyRunnable"),
+ : MainThreadWorkerRunnable("ContinueConsumeBlobBodyRunnable"),
mBodyConsumer(aBodyConsumer),
mBlobImpl(aBlobImpl) {
MOZ_ASSERT(NS_IsMainThread());
@@ -182,7 +181,7 @@ class AbortConsumeBlobBodyControlRunnable final
public:
AbortConsumeBlobBodyControlRunnable(BodyConsumer* aBodyConsumer,
WorkerPrivate* aWorkerPrivate)
- : MainThreadWorkerControlRunnable(aWorkerPrivate),
+ : MainThreadWorkerControlRunnable("AbortConsumeBlobBodyControlRunnable"),
mBodyConsumer(aBodyConsumer) {
MOZ_ASSERT(NS_IsMainThread());
}
@@ -227,7 +226,7 @@ class ConsumeBodyDoneObserver final : public nsIStreamLoaderObserver,
RefPtr<ContinueConsumeBodyRunnable> r = new ContinueConsumeBodyRunnable(
mBodyConsumer, mWorkerRef->Private(), aStatus, aResultLength,
nonconstResult);
- if (r->Dispatch()) {
+ if (r->Dispatch(mWorkerRef->Private())) {
// The caller is responsible for data.
return NS_SUCCESS_ADOPTED_DATA;
}
@@ -239,7 +238,7 @@ class ConsumeBodyDoneObserver final : public nsIStreamLoaderObserver,
RefPtr<AbortConsumeBodyControlRunnable> r =
new AbortConsumeBodyControlRunnable(mBodyConsumer,
mWorkerRef->Private());
- if (NS_WARN_IF(!r->Dispatch())) {
+ if (NS_WARN_IF(!r->Dispatch(mWorkerRef->Private()))) {
return NS_ERROR_FAILURE;
}
@@ -620,14 +619,14 @@ void BodyConsumer::DispatchContinueConsumeBlobBody(
new ContinueConsumeBlobBodyRunnable(this, aWorkerRef->Private(),
aBlobImpl);
- if (r->Dispatch()) {
+ if (r->Dispatch(aWorkerRef->Private())) {
return;
}
} else {
RefPtr<ContinueConsumeBodyRunnable> r = new ContinueConsumeBodyRunnable(
this, aWorkerRef->Private(), NS_ERROR_DOM_ABORT_ERR, 0, nullptr);
- if (r->Dispatch()) {
+ if (r->Dispatch(aWorkerRef->Private())) {
return;
}
}
@@ -638,7 +637,7 @@ void BodyConsumer::DispatchContinueConsumeBlobBody(
RefPtr<AbortConsumeBlobBodyControlRunnable> r =
new AbortConsumeBlobBodyControlRunnable(this, aWorkerRef->Private());
- Unused << NS_WARN_IF(!r->Dispatch());
+ Unused << NS_WARN_IF(!r->Dispatch(aWorkerRef->Private()));
}
/*
@@ -673,12 +672,14 @@ void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
if (NS_WARN_IF(NS_FAILED(aStatus))) {
// Per
- // https://fetch.spec.whatwg.org/#concept-read-all-bytes-from-readablestream
+ // https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-all-bytes
// Decoding errors should reject with a TypeError
if (aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
localPromise->MaybeRejectWithTypeError<MSG_DOM_DECODING_FAILED>();
} else if (aStatus == NS_ERROR_DOM_WRONG_TYPE_ERR) {
localPromise->MaybeRejectWithTypeError<MSG_FETCH_BODY_WRONG_TYPE>();
+ } else if (aStatus == NS_ERROR_NET_PARTIAL_TRANSFER) {
+ localPromise->MaybeRejectWithTypeError<MSG_FETCH_PARTIAL>();
} else {
localPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
}
diff --git a/dom/base/ContentIterator.cpp b/dom/base/ContentIterator.cpp
index 0b405f0348..80c795d137 100644
--- a/dom/base/ContentIterator.cpp
+++ b/dom/base/ContentIterator.cpp
@@ -715,7 +715,7 @@ nsIContent* ContentIteratorBase<NodeType>::GetNextSibling(
// For shadow root, instead of getting to the sibling of the parent
// directly, we need to get into the light tree of the parent to handle
// slotted contents.
- if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(aNode)) {
+ if (aNode->IsShadowRoot()) {
if (nsIContent* child = parent->GetFirstChild()) {
return child;
}
diff --git a/dom/base/CrossShadowBoundaryRange.cpp b/dom/base/CrossShadowBoundaryRange.cpp
new file mode 100644
index 0000000000..33fa9760e6
--- /dev/null
+++ b/dom/base/CrossShadowBoundaryRange.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/CrossShadowBoundaryRange.h"
+#include "nsContentUtils.h"
+#include "nsINode.h"
+
+namespace mozilla::dom {
+template already_AddRefed<CrossShadowBoundaryRange>
+CrossShadowBoundaryRange::Create(const RangeBoundary& aStartBoundary,
+ const RangeBoundary& aEndBoundary);
+template already_AddRefed<CrossShadowBoundaryRange>
+CrossShadowBoundaryRange::Create(const RangeBoundary& aStartBoundary,
+ const RawRangeBoundary& aEndBoundary);
+template already_AddRefed<CrossShadowBoundaryRange>
+CrossShadowBoundaryRange::Create(const RawRangeBoundary& aStartBoundary,
+ const RangeBoundary& aEndBoundary);
+template already_AddRefed<CrossShadowBoundaryRange>
+CrossShadowBoundaryRange::Create(const RawRangeBoundary& aStartBoundary,
+ const RawRangeBoundary& aEndBoundary);
+
+template void CrossShadowBoundaryRange::DoSetRange(
+ const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
+ nsINode* aRootNode);
+template void CrossShadowBoundaryRange::DoSetRange(
+ const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
+ nsINode* aRootNode);
+template void CrossShadowBoundaryRange::DoSetRange(
+ const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
+ nsINode* aRootNode);
+template void CrossShadowBoundaryRange::DoSetRange(
+ const RawRangeBoundary& aStartBoundary,
+ const RawRangeBoundary& aEndBoundary, nsINode* aRootNode);
+
+template nsresult CrossShadowBoundaryRange::SetStartAndEnd(
+ const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary);
+template nsresult CrossShadowBoundaryRange::SetStartAndEnd(
+ const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary);
+template nsresult CrossShadowBoundaryRange::SetStartAndEnd(
+ const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary);
+template nsresult CrossShadowBoundaryRange::SetStartAndEnd(
+ const RawRangeBoundary& aStartBoundary,
+ const RawRangeBoundary& aEndBoundary);
+
+nsTArray<RefPtr<CrossShadowBoundaryRange>>*
+ CrossShadowBoundaryRange::sCachedRanges = nullptr;
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(CrossShadowBoundaryRange)
+
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_INTERRUPTABLE_LAST_RELEASE(
+ CrossShadowBoundaryRange,
+ DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr),
+ AbstractRange::MaybeCacheToReuse(*this))
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CrossShadowBoundaryRange)
+NS_INTERFACE_MAP_END_INHERITING(CrossShadowBoundaryRange)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(CrossShadowBoundaryRange)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CrossShadowBoundaryRange,
+ StaticRange)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommonAncestor)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CrossShadowBoundaryRange,
+ StaticRange)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommonAncestor)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CrossShadowBoundaryRange,
+ StaticRange)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+/* static */
+template <typename SPT, typename SRT, typename EPT, typename ERT>
+already_AddRefed<CrossShadowBoundaryRange> CrossShadowBoundaryRange::Create(
+ const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
+ const RangeBoundaryBase<EPT, ERT>& aEndBoundary) {
+ RefPtr<CrossShadowBoundaryRange> range;
+ if (!sCachedRanges || sCachedRanges->IsEmpty()) {
+ range = new CrossShadowBoundaryRange(aStartBoundary.Container());
+ } else {
+ range = sCachedRanges->PopLastElement().forget();
+ }
+
+ range->Init(aStartBoundary.Container());
+ range->DoSetRange(aStartBoundary, aEndBoundary, nullptr);
+ return range.forget();
+}
+
+template <typename SPT, typename SRT, typename EPT, typename ERT>
+void CrossShadowBoundaryRange::DoSetRange(
+ const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
+ const RangeBoundaryBase<EPT, ERT>& aEndBoundary, nsINode* aRootNode) {
+ // aRootNode is useless to CrossShadowBoundaryRange because aStartBoundary
+ // and aEndBoundary could have different roots.
+ StaticRange::DoSetRange(aStartBoundary, aEndBoundary, nullptr);
+
+ nsINode* startRoot = RangeUtils::ComputeRootNode(mStart.Container());
+ nsINode* endRoot = RangeUtils::ComputeRootNode(mEnd.Container());
+
+ if (startRoot == endRoot) {
+ // This should be the case when Release() is called.
+ MOZ_ASSERT(!startRoot && !endRoot);
+ mCommonAncestor = startRoot;
+ } else {
+ mCommonAncestor =
+ nsContentUtils::GetClosestCommonShadowIncludingInclusiveAncestor(
+ mStart.Container(), mEnd.Container());
+ }
+}
+} // namespace mozilla::dom
diff --git a/dom/base/CrossShadowBoundaryRange.h b/dom/base/CrossShadowBoundaryRange.h
new file mode 100644
index 0000000000..3ad4fa1793
--- /dev/null
+++ b/dom/base/CrossShadowBoundaryRange.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_CrossShadowBoundaryRange_h
+#define mozilla_dom_CrossShadowBoundaryRange_h
+
+#include "mozilla/RangeBoundary.h"
+#include "mozilla/RangeUtils.h"
+#include "mozilla/dom/AbstractRange.h"
+#include "mozilla/dom/StaticRange.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+class CrossShadowBoundaryRange final : public StaticRange {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_IMETHODIMP_(void) DeleteCycleCollectable(void) override;
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
+ CrossShadowBoundaryRange, StaticRange)
+
+ CrossShadowBoundaryRange() = delete;
+ explicit CrossShadowBoundaryRange(const StaticRange& aOther) = delete;
+
+ template <typename SPT, typename SRT, typename EPT, typename ERT>
+ static already_AddRefed<CrossShadowBoundaryRange> Create(
+ const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
+ const RangeBoundaryBase<EPT, ERT>& aEndBoundary);
+
+ void NotifyNodeBecomesShadowHost(nsINode* aNode) {
+ if (aNode == mStart.Container()) {
+ mStart.NotifyParentBecomesShadowHost();
+ }
+
+ if (aNode == mEnd.Container()) {
+ mEnd.NotifyParentBecomesShadowHost();
+ }
+ }
+
+ nsINode* GetCommonAncestor() const { return mCommonAncestor; }
+
+ // CrossShadowBoundaryRange should have a very limited usage.
+ nsresult SetStartAndEnd(nsINode* aStartContainer, uint32_t aStartOffset,
+ nsINode* aEndContainer, uint32_t aEndOffset) = delete;
+
+ template <typename SPT, typename SRT, typename EPT, typename ERT>
+ nsresult SetStartAndEnd(const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
+ const RangeBoundaryBase<EPT, ERT>& aEndBoundary) {
+ return StaticRange::SetStartAndEnd(aStartBoundary, aEndBoundary);
+ }
+
+ private:
+ explicit CrossShadowBoundaryRange(nsINode* aNode) : StaticRange(aNode) {}
+ virtual ~CrossShadowBoundaryRange() = default;
+
+ /**
+ * DoSetRange() is called when `AbstractRange::SetStartAndEndInternal()` sets
+ * mStart and mEnd.
+ *
+ * @param aStartBoundary Computed start point. This must equals or be before
+ * aEndBoundary in the DOM tree order.
+ * @param aEndBoundary Computed end point.
+ * @param aRootNode The root node of aStartBoundary or aEndBoundary.
+ * It's useless to CrossShadowBoundaryRange.
+ */
+ template <typename SPT, typename SRT, typename EPT, typename ERT>
+ void DoSetRange(const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
+ const RangeBoundaryBase<EPT, ERT>& aEndBoundary,
+ nsINode* aRootNode);
+
+ // This is either NULL if this CrossShadowBoundaryRange has been
+ // reset by Release() or the closest common shadow-including ancestor
+ // of mStart and mEnd.
+ nsCOMPtr<nsINode> mCommonAncestor;
+
+ static nsTArray<RefPtr<CrossShadowBoundaryRange>>* sCachedRanges;
+
+ friend class AbstractRange;
+};
+} // namespace dom
+} // namespace mozilla
+
+#endif // #ifndef mozilla_dom_CrossShadowBoundaryRange_h
diff --git a/dom/base/DOMIntersectionObserver.cpp b/dom/base/DOMIntersectionObserver.cpp
index 12f7ee3029..37d17ddcd3 100644
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -323,7 +323,8 @@ static Maybe<nsRect> ComputeTheIntersection(
//
// `intersectionRect` is kept relative to `target` during the loop.
auto inflowRect = nsLayoutUtils::GetAllInFlowRectsUnion(
- target, target, nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
+ target, target,
+ nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms);
// For content-visibility, we need to observe the overflow clip edge,
// https://drafts.csswg.org/css-contain-2/#close-to-the-viewport
if (aIsForProximityToViewport ==
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
index 8cbf8b8075..68e4100540 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -16423,47 +16423,33 @@ WindowContext* Document::GetWindowContextForPageUseCounters() const {
return wc;
}
-void Document::UpdateIntersectionObservations(TimeStamp aNowTime) {
- if (mIntersectionObservers.IsEmpty()) {
- return;
- }
-
- DOMHighResTimeStamp time = 0;
- if (nsPIDOMWindowInner* win = GetInnerWindow()) {
- if (Performance* perf = win->GetPerformance()) {
- time = perf->TimeStampToDOMHighResForRendering(aNowTime);
+void Document::UpdateIntersections(TimeStamp aNowTime) {
+ if (!mIntersectionObservers.IsEmpty()) {
+ DOMHighResTimeStamp time = 0;
+ if (nsPIDOMWindowInner* win = GetInnerWindow()) {
+ if (Performance* perf = win->GetPerformance()) {
+ time = perf->TimeStampToDOMHighResForRendering(aNowTime);
+ }
}
- }
-
- const auto observers = ToTArray<nsTArray<RefPtr<DOMIntersectionObserver>>>(
- mIntersectionObservers);
- for (const auto& observer : observers) {
- if (observer) {
+ for (DOMIntersectionObserver* observer : mIntersectionObservers) {
observer->Update(*this, time);
}
+ Dispatch(NewRunnableMethod("Document::NotifyIntersectionObservers", this,
+ &Document::NotifyIntersectionObservers));
}
-}
-
-void Document::ScheduleIntersectionObserverNotification() {
- if (mIntersectionObservers.IsEmpty()) {
- return;
- }
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- nsCOMPtr<nsIRunnable> notification =
- NewRunnableMethod("Document::NotifyIntersectionObservers", this,
- &Document::NotifyIntersectionObservers);
- Dispatch(notification.forget());
+ EnumerateSubDocuments([aNowTime](Document& aDoc) {
+ aDoc.UpdateIntersections(aNowTime);
+ return CallState::Continue;
+ });
}
void Document::NotifyIntersectionObservers() {
const auto observers = ToTArray<nsTArray<RefPtr<DOMIntersectionObserver>>>(
mIntersectionObservers);
for (const auto& observer : observers) {
- if (observer) {
- // MOZ_KnownLive because the 'observers' array guarantees to keep it
- // alive.
- MOZ_KnownLive(observer)->Notify();
- }
+ // MOZ_KnownLive because the 'observers' array guarantees to keep it
+ // alive.
+ MOZ_KnownLive(observer)->Notify();
}
}
@@ -18647,6 +18633,13 @@ nsICookieJarSettings* Document::CookieJarSettings() {
if (!mCookieJarSettings) {
Document* inProcessParent = GetInProcessParentDocument();
+ auto shouldInheritFrom = [this](Document* aDoc) {
+ return aDoc && (this->NodePrincipal()->Equals(aDoc->NodePrincipal()) ||
+ this->NodePrincipal()->GetIsNullPrincipal());
+ };
+ RefPtr<BrowsingContext> opener =
+ GetBrowsingContext() ? GetBrowsingContext()->GetOpener() : nullptr;
+
if (inProcessParent) {
mCookieJarSettings = net::CookieJarSettings::Create(
inProcessParent->CookieJarSettings()->GetCookieBehavior(),
@@ -18674,6 +18667,18 @@ nsICookieJarSettings* Document::CookieJarSettings() {
->SetTopLevelWindowContextId(
net::CookieJarSettings::Cast(inProcessParent->CookieJarSettings())
->GetTopLevelWindowContextId());
+ } else if (opener && shouldInheritFrom(opener->GetDocument())) {
+ mCookieJarSettings = net::CookieJarSettings::Create(NodePrincipal());
+
+ nsTArray<uint8_t> randomKey;
+ nsresult rv = opener->GetDocument()
+ ->CookieJarSettings()
+ ->GetFingerprintingRandomizationKey(randomKey);
+
+ if (NS_SUCCEEDED(rv)) {
+ net::CookieJarSettings::Cast(mCookieJarSettings)
+ ->SetFingerprintingRandomizationKey(randomKey);
+ }
} else {
mCookieJarSettings = net::CookieJarSettings::Create(NodePrincipal());
@@ -18933,8 +18938,7 @@ void Document::AddPendingFrameStaticClone(nsFrameLoaderOwner* aElement,
}
bool Document::ShouldAvoidNativeTheme() const {
- return StaticPrefs::widget_non_native_theme_enabled() &&
- (!IsInChromeDocShell() || XRE_IsContentProcess());
+ return !IsInChromeDocShell() || XRE_IsContentProcess();
}
bool Document::UseRegularPrincipal() const {
diff --git a/dom/base/Document.h b/dom/base/Document.h
index 0b0d0ca3d0..e919f19be0 100644
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3709,8 +3709,9 @@ class Document : public nsINode,
return !mIntersectionObservers.IsEmpty();
}
- void UpdateIntersectionObservations(TimeStamp aNowTime);
- void ScheduleIntersectionObserverNotification();
+ // Update intersection observers in this document and all
+ // same-process subdocuments.
+ void UpdateIntersections(TimeStamp aNowTime);
MOZ_CAN_RUN_SCRIPT void NotifyIntersectionObservers();
DOMIntersectionObserver* GetLazyLoadObserver() { return mLazyLoadObserver; }
diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp
index b6f5d5c3be..758134990b 100644
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1088,7 +1088,7 @@ already_AddRefed<DOMRectList> Element::GetClientRects() {
nsLayoutUtils::RectListBuilder builder(rectList);
nsLayoutUtils::GetAllInFlowRects(
frame, nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder,
- nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
+ nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms);
return rectList.forget();
}
@@ -1353,7 +1353,7 @@ already_AddRefed<ShadowRoot> Element::AttachShadowWithoutNameChecks(
for (const AbstractRange* range : *ranges) {
if (range->MayCrossShadowBoundary()) {
MOZ_ASSERT(range->IsDynamicRange());
- StaticRange* crossBoundaryRange =
+ CrossShadowBoundaryRange* crossBoundaryRange =
range->AsDynamicRange()->GetCrossShadowBoundaryRange();
MOZ_ASSERT(crossBoundaryRange);
// We may have previously selected this node before it
diff --git a/dom/base/EventSource.cpp b/dom/base/EventSource.cpp
index def3c90ec0..9e73f31fc6 100644
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -1827,14 +1827,14 @@ nsresult EventSourceImpl::ParseCharacter(char16_t aChr) {
namespace {
-class WorkerRunnableDispatcher final : public WorkerRunnable {
+class WorkerRunnableDispatcher final : public WorkerThreadRunnable {
RefPtr<EventSourceImpl> mEventSourceImpl;
public:
WorkerRunnableDispatcher(RefPtr<EventSourceImpl>&& aImpl,
WorkerPrivate* aWorkerPrivate,
already_AddRefed<nsIRunnable> aEvent)
- : WorkerRunnable(aWorkerPrivate, "WorkerRunnableDispatcher"),
+ : WorkerThreadRunnable("WorkerRunnableDispatcher"),
mEventSourceImpl(std::move(aImpl)),
mEvent(std::move(aEvent)) {}
@@ -1928,7 +1928,7 @@ EventSourceImpl::Dispatch(already_AddRefed<nsIRunnable> aEvent,
RefPtr<WorkerRunnableDispatcher> event = new WorkerRunnableDispatcher(
this, mWorkerRef->Private(), event_ref.forget());
- if (!event->Dispatch()) {
+ if (!event->Dispatch(mWorkerRef->Private())) {
return NS_ERROR_FAILURE;
}
return NS_OK;
diff --git a/dom/base/FocusModel.h b/dom/base/FocusModel.h
new file mode 100644
index 0000000000..588c0503a2
--- /dev/null
+++ b/dom/base/FocusModel.h
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_FocusModel_h
+#define mozilla_FocusModel_h
+
+#include "mozilla/StaticPrefs_accessibility.h"
+#include "mozilla/TypedEnumBits.h"
+
+namespace mozilla {
+
+enum class IsFocusableFlags : uint8_t {
+ WithMouse = 1 << 0,
+ // Whether to treat an invisible frame as not focusable
+ IgnoreVisibility = 1 << 1,
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(IsFocusableFlags);
+
+enum class TabFocusableType : uint8_t {
+ TextControls = 1, // Textboxes and lists always tabbable
+ FormElements = 1 << 1, // Non-text form elements
+ Links = 1 << 2, // Links
+ Any = TextControls | FormElements | Links,
+};
+
+class FocusModel final {
+ public:
+ static bool AppliesToXUL() {
+ return StaticPrefs::accessibility_tabfocus_applies_to_xul();
+ }
+
+ static bool IsTabFocusable(TabFocusableType aType) {
+ return StaticPrefs::accessibility_tabfocus() & int32_t(aType);
+ }
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp
index 87fd81bfa3..8338fc6b9e 100644
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -105,8 +105,6 @@
using namespace mozilla;
using namespace mozilla::dom;
-int32_t nsIContent::sTabFocusModel = eTabFocus_any;
-bool nsIContent::sTabFocusModelAppliesToXUL = false;
uint64_t nsMutationGuard::sGeneration = 0;
NS_IMPL_CYCLE_COLLECTION_CLASS(nsIContent)
@@ -1022,7 +1020,7 @@ void nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
}
}
-Element* nsIContent::GetAutofocusDelegate(bool aWithMouse) const {
+Element* nsIContent::GetAutofocusDelegate(IsFocusableFlags aFlags) const {
for (nsINode* node = GetFirstChild(); node; node = node->GetNextNode(this)) {
auto* descendant = Element::FromNode(*node);
if (!descendant || !descendant->GetBoolAttr(nsGkAtoms::autofocus)) {
@@ -1030,14 +1028,14 @@ Element* nsIContent::GetAutofocusDelegate(bool aWithMouse) const {
}
nsIFrame* frame = descendant->GetPrimaryFrame();
- if (frame && frame->IsFocusable(aWithMouse)) {
+ if (frame && frame->IsFocusable(aFlags)) {
return descendant;
}
}
return nullptr;
}
-Element* nsIContent::GetFocusDelegate(bool aWithMouse) const {
+Element* nsIContent::GetFocusDelegate(IsFocusableFlags aFlags) const {
const nsIContent* whereToLook = this;
if (ShadowRoot* root = GetShadowRoot()) {
if (!root->DelegatesFocus()) {
@@ -1055,7 +1053,7 @@ Element* nsIContent::GetFocusDelegate(bool aWithMouse) const {
return {};
}
- return frame->IsFocusable(aWithMouse);
+ return frame->IsFocusable(aFlags);
};
Element* potentialFocus = nullptr;
@@ -1097,7 +1095,7 @@ Element* nsIContent::GetFocusDelegate(bool aWithMouse) const {
if (auto* shadow = el->GetShadowRoot()) {
if (shadow->DelegatesFocus()) {
- if (Element* delegatedFocus = shadow->GetFocusDelegate(aWithMouse)) {
+ if (Element* delegatedFocus = shadow->GetFocusDelegate(aFlags)) {
if (autofocus) {
// This element has autofocus and we found an focus delegates
// in its descendants, so use the focus delegates
@@ -1114,7 +1112,7 @@ Element* nsIContent::GetFocusDelegate(bool aWithMouse) const {
return potentialFocus;
}
-Focusable nsIContent::IsFocusableWithoutStyle(bool aWithMouse) {
+Focusable nsIContent::IsFocusableWithoutStyle(IsFocusableFlags) {
// Default, not tabbable
return {};
}
diff --git a/dom/base/Highlight.cpp b/dom/base/Highlight.cpp
index cd0efccce5..0bac8193d7 100644
--- a/dom/base/Highlight.cpp
+++ b/dom/base/Highlight.cpp
@@ -101,24 +101,36 @@ already_AddRefed<Selection> Highlight::CreateHighlightSelection(
}
void Highlight::Add(AbstractRange& aRange, ErrorResult& aRv) {
+ // Manually check if the range `aKey` is already present in this highlight,
+ // because `SetlikeHelpers::Add()` doesn't indicate this.
+ // To keep the setlike and the mirrored array in sync, the range must not
+ // be added to `mRanges` if it was already present.
+ // `SetlikeHelpers::Has()` is much faster in checking this than
+ // `nsTArray<>::Contains()`.
+ if (Highlight_Binding::SetlikeHelpers::Has(this, aRange, aRv) ||
+ aRv.Failed()) {
+ return;
+ }
Highlight_Binding::SetlikeHelpers::Add(this, aRange, aRv);
if (aRv.Failed()) {
return;
}
- if (!mRanges.Contains(&aRange)) {
- mRanges.AppendElement(&aRange);
- AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__,
- mHighlightRegistries.Count());
- for (const RefPtr<HighlightRegistry>& registry :
- mHighlightRegistries.Keys()) {
- auto frameSelection = registry->GetFrameSelection();
- selectionBatcher.AddFrameSelection(frameSelection);
- // since this is run in a context guarded by a selection batcher,
- // no strong reference is needed to keep `registry` alive.
- MOZ_KnownLive(registry)->MaybeAddRangeToHighlightSelection(aRange, *this);
- if (aRv.Failed()) {
- return;
- }
+
+ MOZ_ASSERT(!mRanges.Contains(&aRange),
+ "setlike and DOM mirror are not in sync");
+
+ mRanges.AppendElement(&aRange);
+ AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__,
+ mHighlightRegistries.Count());
+ for (const RefPtr<HighlightRegistry>& registry :
+ mHighlightRegistries.Keys()) {
+ auto frameSelection = registry->GetFrameSelection();
+ selectionBatcher.AddFrameSelection(frameSelection);
+ // since this is run in a context guarded by a selection batcher,
+ // no strong reference is needed to keep `registry` alive.
+ MOZ_KnownLive(registry)->MaybeAddRangeToHighlightSelection(aRange, *this);
+ if (aRv.Failed()) {
+ return;
}
}
}
diff --git a/dom/base/HighlightRegistry.cpp b/dom/base/HighlightRegistry.cpp
index 035aadb78a..755977286f 100644
--- a/dom/base/HighlightRegistry.cpp
+++ b/dom/base/HighlightRegistry.cpp
@@ -133,18 +133,28 @@ void HighlightRegistry::AddHighlightSelectionsToFrameSelection() {
void HighlightRegistry::Set(const nsAString& aKey, Highlight& aValue,
ErrorResult& aRv) {
+ // manually check if the highlight `aKey` is already registered to be able to
+ // provide a fast path later that avoids calling `std::find_if()`.
+ const bool highlightAlreadyPresent =
+ HighlightRegistry_Binding::MaplikeHelpers::Has(this, aKey, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
HighlightRegistry_Binding::MaplikeHelpers::Set(this, aKey, aValue, aRv);
if (aRv.Failed()) {
return;
}
RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
RefPtr<nsAtom> highlightNameAtom = NS_AtomizeMainThread(aKey);
- auto foundIter =
- std::find_if(mHighlightsOrdered.begin(), mHighlightsOrdered.end(),
- [&highlightNameAtom](auto const& aElm) {
- return aElm.first() == highlightNameAtom;
- });
- if (foundIter != mHighlightsOrdered.end()) {
+ if (highlightAlreadyPresent) {
+ // If the highlight named `aKey` was present before, replace its value.
+ auto foundIter =
+ std::find_if(mHighlightsOrdered.begin(), mHighlightsOrdered.end(),
+ [&highlightNameAtom](auto const& aElm) {
+ return aElm.first() == highlightNameAtom;
+ });
+ MOZ_ASSERT(foundIter != mHighlightsOrdered.end(),
+ "webIDL maplike and DOM mirror are not in sync");
foundIter->second()->RemoveFromHighlightRegistry(*this, *highlightNameAtom);
if (frameSelection) {
frameSelection->RemoveHighlightSelection(highlightNameAtom);
diff --git a/dom/base/MimeType.cpp b/dom/base/MimeType.cpp
index da61489c1f..1e6d6a7cd4 100644
--- a/dom/base/MimeType.cpp
+++ b/dom/base/MimeType.cpp
@@ -328,13 +328,13 @@ template <typename char_type>
template <typename char_type>
void TMimeType<char_type>::Serialize(nsTSubstring<char_type>& aOutput) const {
aOutput.Assign(mType);
- aOutput.AppendLiteral("/");
+ aOutput.Append('/');
aOutput.Append(mSubtype);
for (uint32_t i = 0; i < mParameterNames.Length(); i++) {
auto name = mParameterNames[i];
- aOutput.AppendLiteral(";");
+ aOutput.Append(';');
aOutput.Append(name);
- aOutput.AppendLiteral("=");
+ aOutput.Append('=');
GetParameterValue(name, aOutput, true);
}
}
@@ -342,7 +342,7 @@ void TMimeType<char_type>::Serialize(nsTSubstring<char_type>& aOutput) const {
template <typename char_type>
void TMimeType<char_type>::GetEssence(nsTSubstring<char_type>& aOutput) const {
aOutput.Assign(mType);
- aOutput.AppendLiteral("/");
+ aOutput.Append('/');
aOutput.Append(mSubtype);
}
@@ -366,17 +366,17 @@ bool TMimeType<char_type>::GetParameterValue(
}
if (aWithQuotes && (value.mRequiresQuoting || value.IsEmpty())) {
- aOutput.AppendLiteral("\"");
+ aOutput.Append('"');
const char_type* vcur = value.BeginReading();
const char_type* vend = value.EndReading();
while (vcur < vend) {
if (*vcur == '"' || *vcur == '\\') {
- aOutput.AppendLiteral("\\");
+ aOutput.Append('\\');
}
aOutput.Append(*vcur);
vcur++;
}
- aOutput.AppendLiteral("\"");
+ aOutput.Append('"');
} else {
aOutput.Append(value);
}
diff --git a/dom/base/RadioGroupContainer.cpp b/dom/base/RadioGroupContainer.cpp
index 3bb4d7da20..988c8186f9 100644
--- a/dom/base/RadioGroupContainer.cpp
+++ b/dom/base/RadioGroupContainer.cpp
@@ -8,6 +8,7 @@
#include "mozilla/dom/RadioGroupContainer.h"
#include "mozilla/dom/TreeOrderedArrayInlines.h"
#include "mozilla/Assertions.h"
+#include "nsIFrame.h"
#include "nsIRadioVisitor.h"
#include "nsRadioVisitor.h"
@@ -125,7 +126,9 @@ nsresult RadioGroupContainer::GetNextRadioButton(
index = 0;
}
radio = radioGroup->mRadioButtons->ElementAt(index);
- } while (radio->Disabled() && radio != currentRadio);
+ } while ((radio->Disabled() || !radio->GetPrimaryFrame() ||
+ !radio->GetPrimaryFrame()->IsVisibleConsideringAncestors()) &&
+ radio != currentRadio);
radio.forget(aRadioOut);
return NS_OK;
@@ -135,7 +138,8 @@ HTMLInputElement* RadioGroupContainer::GetFirstRadioButton(
const nsAString& aName) {
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
for (HTMLInputElement* radio : radioGroup->mRadioButtons.AsList()) {
- if (!radio->Disabled()) {
+ if (!radio->Disabled() && radio->GetPrimaryFrame() &&
+ radio->GetPrimaryFrame()->IsVisibleConsideringAncestors()) {
return radio;
}
}
diff --git a/dom/base/ResizeObserver.h b/dom/base/ResizeObserver.h
index eaa0e1726d..2fd630c66c 100644
--- a/dom/base/ResizeObserver.h
+++ b/dom/base/ResizeObserver.h
@@ -121,7 +121,6 @@ class ResizeObservation final : public LinkedListElement<ResizeObservation> {
* https://drafts.csswg.org/resize-observer/#api
*/
class ResizeObserver final : public nsISupports, public nsWrapperCache {
-
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ResizeObserver)
diff --git a/dom/base/Selection.h b/dom/base/Selection.h
index 08563993ac..101bada49f 100644
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -386,6 +386,30 @@ class Selection final : public nsSupportsWeakReference,
return mStyledRanges.mRanges[0].mRange->Collapsed();
}
+ // Returns whether both normal range and cross-shadow-boundary
+ // range are collapsed.
+ //
+ // If StaticPrefs::dom_shadowdom_selection_across_boundary_enabled is
+ // disabled, this method always returns result as nsRange::IsCollapsed.
+ bool AreNormalAndCrossShadowBoundaryRangesCollapsed() const {
+ if (!IsCollapsed()) {
+ return false;
+ }
+
+ size_t cnt = mStyledRanges.Length();
+ if (cnt == 0) {
+ return true;
+ }
+
+ AbstractRange* range = mStyledRanges.mRanges[0].mRange;
+ MOZ_ASSERT_IF(
+ range->MayCrossShadowBoundary(),
+ !range->AsDynamicRange()->CrossShadowBoundaryRangeCollapsed());
+ // Returns false if nsRange::mCrossBoundaryRange exists,
+ // true otherwise.
+ return !range->MayCrossShadowBoundary();
+ }
+
// *JS() methods are mapped to Selection.*().
// They may move focus only when the range represents normal selection.
// These methods shouldn't be used by non-JS callers.
diff --git a/dom/base/StaticRange.h b/dom/base/StaticRange.h
index af7054f843..249c938b2f 100644
--- a/dom/base/StaticRange.h
+++ b/dom/base/StaticRange.h
@@ -19,7 +19,7 @@ class ErrorResult;
namespace dom {
-class StaticRange final : public AbstractRange {
+class StaticRange : public AbstractRange {
public:
StaticRange() = delete;
explicit StaticRange(const StaticRange& aOther) = delete;
@@ -71,16 +71,6 @@ class StaticRange final : public AbstractRange {
*/
bool IsValid() const;
- void NotifyNodeBecomesShadowHost(nsINode* aNode) {
- if (aNode == mStart.Container()) {
- mStart.NotifyParentBecomesShadowHost();
- }
-
- if (aNode == mEnd.Container()) {
- mEnd.NotifyParentBecomesShadowHost();
- }
- }
-
private:
// Whether the start and end points are in the same tree.
// They could be in different trees, i.e, cross shadow boundaries.
diff --git a/dom/base/crashtests/1419902.html b/dom/base/crashtests/1419902.html
index b0742b5be0..a0aa1b0698 100644
--- a/dom/base/crashtests/1419902.html
+++ b/dom/base/crashtests/1419902.html
@@ -1,23 +1 @@
-<html>
- <head>
- <script>
- var winsToClose = []
- onbeforeunload = function() {
- for (let win of winsToClose) {
- if (win) {
- win.close();
- }
- }
- };
- for (let i = 0; i < 38; i++) {
- customElements.define("custom-element_0", class extends HTMLElement {
- constructor() {
- try { o1 = document.createElement("custom-element_0") } catch (e) {}
- try { winsToClose.push(window.open("javascript:'<html><body>dummy</body></html>';")); } catch (e) {}
- }
- })
- try { o3 = document.createElement("custom-element_0") } catch (e) {}
- }
- </script>
- </head>
-</html>
+<!-- file is now located at testing/crashtest/final/dom/base/crashtests/1419902.html -->
diff --git a/dom/base/crashtests/1741957.html b/dom/base/crashtests/1741957.html
new file mode 100644
index 0000000000..56b695052e
--- /dev/null
+++ b/dom/base/crashtests/1741957.html
@@ -0,0 +1,22 @@
+<html><head><style>
+:root {
+ column-width: 0px;
+}
+* {
+ height: 0;
+}
+</style>
+<script>
+function go() {
+ document.execCommand("selectAll", false)
+ a.appendChild(b)
+}
+</script>
+<link href="moz-extension://1abf2dce-edd2-4c86-b0c5-2eeeaa9386d5/css/content.css" rel="stylesheet" type="text/css"></head><body onload="go()">
+
+<button>
+<font dir="rtl">x</font>
+<animatetransform id="a">
+<textarea id="b">x</textarea></animatetransform></button>
+<!-- x -->
+</body><div></div></html>
diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list
index 22aaf50e6b..7aa5e6a92a 100644
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -220,7 +220,7 @@ load 1406109-1.html
load 1411473.html
load 1413815.html
load 1419799.html
-skip-if(geckoview) skip-if(geckoview&&isDebugBuild) skip-if(AddressSanitizer) skip-if(ThreadSanitizer) pref(dom.disable_open_during_load,false) load 1419902.html # skip Bug 1419902. Bug 1563013 for GV+WR. Bug 1524493 GV+debug. Bug 1573281 asan
+# load 1419902.html # this test is run at the very end in testing/crashtest/final/crashtests.list
load 1422883.html
load 1428053.html
load 1441029.html
@@ -265,6 +265,7 @@ load 1757923.html
load 1766472.html
pref(dom.enable_web_task_scheduling,true) load 1780790.html
load 1700237.html
+load 1741957.html
load 1811939.html
load 1822717.html
load 1835886.html
diff --git a/dom/base/fragmentdirectives/lib.rs b/dom/base/fragmentdirectives/lib.rs
index 0003849eb7..5f9d5ebdd8 100644
--- a/dom/base/fragmentdirectives/lib.rs
+++ b/dom/base/fragmentdirectives/lib.rs
@@ -96,9 +96,7 @@ pub extern "C" fn parse_fragment_directive(
&url_as_rust_string,
)
{
- result
- .url_without_fragment_directive
- .assign(&stripped_url);
+ result.url_without_fragment_directive.assign(&stripped_url);
result.fragment_directive.assign(&fragment_directive);
result.text_directives.extend(
text_directives
diff --git a/dom/base/fragmentdirectives/test.rs b/dom/base/fragmentdirectives/test.rs
index d4509cb033..e5479d8022 100644
--- a/dom/base/fragmentdirectives/test.rs
+++ b/dom/base/fragmentdirectives/test.rs
@@ -145,13 +145,16 @@ mod test {
),
(
"http://example.com/page.html?query=irrelevant:~:#bar:~:text=foo",
- "http://example.com/page.html?query=irrelevant:~:#bar"
- )
+ "http://example.com/page.html?query=irrelevant:~:#bar",
+ ),
] {
let (stripped_url, fragment_directive, _) =
parse_fragment_directive_and_remove_it_from_hash(&url)
.expect("The parser must find a result");
- assert_eq!(stripped_url, stripped_url_ref, "The stripped url is not correct.");
+ assert_eq!(
+ stripped_url, stripped_url_ref,
+ "The stripped url is not correct."
+ );
assert_eq!(fragment_directive, "text=foo");
}
}
@@ -195,9 +198,8 @@ mod test {
#[test]
fn test_parse_multiple_text_fragments() {
let url = "#:~:text=prefix-,start,-suffix&text=foo&text=bar,-suffix";
- let (_, _, text_directives) =
- parse_fragment_directive_and_remove_it_from_hash(&url)
- .expect("The parser must find a result.");
+ let (_, _, text_directives) = parse_fragment_directive_and_remove_it_from_hash(&url)
+ .expect("The parser must find a result.");
assert_eq!(
text_directives.len(),
3,
@@ -339,8 +341,7 @@ mod test {
"text=prefix-,start",
"#:~:text=foo-,bar,-baz:~:text=foo",
] {
- let text_directives =
- parse_fragment_directive_and_remove_it_from_hash(&url);
+ let text_directives = parse_fragment_directive_and_remove_it_from_hash(&url);
assert!(
text_directives.is_none(),
"The fragment `{}` does not contain a valid or known fragment directive.",
@@ -369,8 +370,7 @@ mod test {
"#:~:text=,prefix,start",
"#:~:text=",
] {
- let text_directives =
- parse_fragment_directive_and_remove_it_from_hash(&url);
+ let text_directives = parse_fragment_directive_and_remove_it_from_hash(&url);
assert!(
text_directives.is_none(),
"The fragment directive `{}` does not contain a valid text directive.",
diff --git a/dom/base/moz.build b/dom/base/moz.build
index ffcfb0aaf6..e2cf2dfb78 100644
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -126,6 +126,7 @@ EXPORTS.mozilla += [
"ContentIterator.h",
"CORSMode.h",
"FlushType.h",
+ "FocusModel.h",
"FullscreenChange.h",
"GlobalTeardownObserver.h",
"IdentifierMapEntry.h",
@@ -166,6 +167,7 @@ EXPORTS.mozilla.dom += [
"CompressionStream.h",
"ContentFrameMessageManager.h",
"ContentProcessMessageManager.h",
+ "CrossShadowBoundaryRange.h",
"CustomElementRegistry.h",
"DecompressionStream.h",
"DirectionalityUtils.h",
@@ -336,6 +338,7 @@ UNIFIED_SOURCES += [
"ContentFrameMessageManager.cpp",
"ContentIterator.cpp",
"ContentProcessMessageManager.cpp",
+ "CrossShadowBoundaryRange.cpp",
"Crypto.cpp",
"CustomElementRegistry.cpp",
"DirectionalityUtils.cpp",
diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp
index 01efef2eb9..e4bf977f56 100644
--- a/dom/base/nsAttrValue.cpp
+++ b/dom/base/nsAttrValue.cpp
@@ -141,7 +141,7 @@ bool MiscContainer::GetString(nsAString& aString) const {
}
if (isString) {
auto* buffer = static_cast<nsStringBuffer*>(ptr);
- buffer->ToString(buffer->StorageSize() / sizeof(char16_t) - 1, aString);
+ aString.Assign(buffer, buffer->StorageSize() / sizeof(char16_t) - 1);
} else {
static_cast<nsAtom*>(ptr)->ToString(aString);
}
@@ -280,11 +280,9 @@ void nsAttrValue::Shutdown() {
void nsAttrValue::Reset() {
switch (BaseType()) {
case eStringBase: {
- nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
- if (str) {
+ if (auto* str = static_cast<nsStringBuffer*>(GetPtr())) {
str->Release();
}
-
break;
}
case eOtherBase: {
@@ -320,8 +318,7 @@ void nsAttrValue::SetTo(const nsAttrValue& aOther) {
switch (aOther.BaseType()) {
case eStringBase: {
ResetIfSet();
- nsStringBuffer* str = static_cast<nsStringBuffer*>(aOther.GetPtr());
- if (str) {
+ if (auto* str = static_cast<nsStringBuffer*>(aOther.GetPtr())) {
str->AddRef();
SetPtrValueAndType(str, eStringBase);
}
@@ -623,18 +620,16 @@ void nsAttrValue::ToString(nsAString& aResult) const {
switch (Type()) {
case eString: {
- nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
- if (str) {
- str->ToString(str->StorageSize() / sizeof(char16_t) - 1, aResult);
+ if (auto* str = static_cast<nsStringBuffer*>(GetPtr())) {
+ aResult.Assign(str, str->StorageSize() / sizeof(char16_t) - 1);
} else {
aResult.Truncate();
}
break;
}
case eAtom: {
- nsAtom* atom = static_cast<nsAtom*>(GetPtr());
+ auto* atom = static_cast<nsAtom*>(GetPtr());
atom->ToString(aResult);
-
break;
}
case eInteger: {
@@ -895,8 +890,7 @@ nsAtom* nsAttrValue::AtomAt(int32_t aIndex) const {
uint32_t nsAttrValue::HashValue() const {
switch (BaseType()) {
case eStringBase: {
- nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
- if (str) {
+ if (auto* str = static_cast<nsStringBuffer*>(GetPtr())) {
uint32_t len = str->StorageSize() / sizeof(char16_t) - 1;
return HashString(static_cast<char16_t*>(str->Data()), len);
}
@@ -1208,8 +1202,7 @@ bool nsAttrValue::SubstringCheck(const nsAString& aValue,
nsCaseTreatment aCaseSensitive) const {
switch (BaseType()) {
case eStringBase: {
- auto str = static_cast<nsStringBuffer*>(GetPtr());
- if (str) {
+ if (auto* str = static_cast<nsStringBuffer*>(GetPtr())) {
return F::Check(static_cast<char16_t*>(str->Data()),
str->StorageSize() / sizeof(char16_t) - 1, aValue,
aCaseSensitive);
@@ -1217,7 +1210,7 @@ bool nsAttrValue::SubstringCheck(const nsAString& aValue,
return aValue.IsEmpty();
}
case eAtomBase: {
- auto atom = static_cast<nsAtom*>(GetPtr());
+ auto* atom = static_cast<nsAtom*>(GetPtr());
return F::Check(atom->GetUTF16String(), atom->GetLength(), aValue,
aCaseSensitive);
}
@@ -2107,12 +2100,11 @@ already_AddRefed<nsStringBuffer> nsAttrValue::GetStringBuffer(
if (!len) {
return nullptr;
}
-
- RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aValue);
- if (buf && (buf->StorageSize() / sizeof(char16_t) - 1) == len) {
+ if (nsStringBuffer* buf = aValue.GetStringBuffer();
+ buf && (buf->StorageSize() / sizeof(char16_t) - 1) == len) {
// We can only reuse the buffer if it's exactly sized, since we rely on
// StorageSize() to get the string length in ToString().
- return buf.forget();
+ return do_AddRef(buf);
}
return nsStringBuffer::Create(aValue.Data(), aValue.Length());
}
diff --git a/dom/base/nsAttrValue.h b/dom/base/nsAttrValue.h
index 4ef63c287f..b32dfc98fb 100644
--- a/dom/base/nsAttrValue.h
+++ b/dom/base/nsAttrValue.h
@@ -108,7 +108,9 @@ const uintptr_t NS_ATTRVALUE_BASETYPE_MASK = 3;
class nsCheapString : public nsString {
public:
explicit nsCheapString(nsStringBuffer* aBuf) {
- if (aBuf) aBuf->ToString(aBuf->StorageSize() / sizeof(char16_t) - 1, *this);
+ if (aBuf) {
+ Assign(aBuf, aBuf->StorageSize() / sizeof(char16_t) - 1);
+ }
}
};
diff --git a/dom/base/nsContentAreaDragDrop.cpp b/dom/base/nsContentAreaDragDrop.cpp
index 3ca21e725a..b2ccc8cb1c 100644
--- a/dom/base/nsContentAreaDragDrop.cpp
+++ b/dom/base/nsContentAreaDragDrop.cpp
@@ -295,7 +295,7 @@ nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable* aTransferable,
bool isPrivate = aTransferable->GetIsPrivateData();
- nsCOMPtr<nsIPrincipal> principal = aTransferable->GetRequestingPrincipal();
+ nsCOMPtr<nsIPrincipal> principal = aTransferable->GetDataPrincipal();
nsContentPolicyType contentPolicyType =
aTransferable->GetContentPolicyType();
nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index d2c863bd65..e4f4fadf9c 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3011,6 +3011,18 @@ nsINode* nsContentUtils::GetCommonAncestorHelper(nsINode* aNode1,
}
/* static */
+nsINode* nsContentUtils::GetClosestCommonShadowIncludingInclusiveAncestor(
+ nsINode* aNode1, nsINode* aNode2) {
+ if (aNode1 == aNode2) {
+ return aNode1;
+ }
+
+ return GetCommonAncestorInternal(aNode1, aNode2, [](nsINode* aNode) {
+ return aNode->GetParentOrShadowHostNode();
+ });
+}
+
+/* static */
nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
nsIContent* aContent1, nsIContent* aContent2) {
return GetCommonAncestorInternal(
@@ -6998,25 +7010,6 @@ bool nsContentUtils::PlatformToDOMLineBreaks(nsString& aString,
return true;
}
-void nsContentUtils::PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
- nsAString& aResultString) {
- MOZ_ASSERT(aBuf, "Expecting a non-null string buffer");
-
- uint32_t stringLen = NS_strlen(static_cast<char16_t*>(aBuf->Data()));
-
- // SANITY CHECK: In case the nsStringBuffer isn't correctly
- // null-terminated, let's clamp its length using the allocated size, to be
- // sure the resulting string doesn't sample past the end of the the buffer.
- // (Note that StorageSize() is in units of bytes, so we have to convert that
- // to units of PRUnichars, and subtract 1 for the null-terminator.)
- uint32_t allocStringLen = (aBuf->StorageSize() / sizeof(char16_t)) - 1;
- MOZ_ASSERT(stringLen <= allocStringLen,
- "string buffer lacks null terminator!");
- stringLen = std::min(stringLen, allocStringLen);
-
- aBuf->ToString(stringLen, aResultString);
-}
-
already_AddRefed<nsContentList> nsContentUtils::GetElementsByClassName(
nsINode* aRootNode, const nsAString& aClasses) {
MOZ_ASSERT(aRootNode, "Must have root node");
@@ -7442,8 +7435,12 @@ int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
// static
bool nsContentUtils::IsPointInSelection(
const mozilla::dom::Selection& aSelection, const nsINode& aNode,
- const uint32_t aOffset) {
- if (aSelection.IsCollapsed()) {
+ const uint32_t aOffset, const bool aAllowCrossShadowBoundary) {
+ const bool selectionIsCollapsed =
+ !aAllowCrossShadowBoundary
+ ? aSelection.IsCollapsed()
+ : aSelection.AreNormalAndCrossShadowBoundaryRangesCollapsed();
+ if (selectionIsCollapsed) {
return false;
}
@@ -7457,7 +7454,8 @@ bool nsContentUtils::IsPointInSelection(
}
// Done when we find a range that we are in
- if (range->IsPointInRange(aNode, aOffset, IgnoreErrors())) {
+ if (range->IsPointInRange(aNode, aOffset, IgnoreErrors(),
+ aAllowCrossShadowBoundary)) {
return true;
}
}
@@ -8211,7 +8209,7 @@ nsresult nsContentUtils::IPCTransferableToTransferable(
aTransferable->SetCookieJarSettings(cookieJarSettings);
}
aTransferable->SetReferrerInfo(aIPCTransferable.referrerInfo());
- aTransferable->SetRequestingPrincipal(aIPCTransferable.requestingPrincipal());
+ aTransferable->SetDataPrincipal(aIPCTransferable.dataPrincipal());
aTransferable->SetContentPolicyType(aIPCTransferable.contentPolicyType());
return NS_OK;
@@ -8302,7 +8300,7 @@ nsresult nsContentUtils::CalculateBufferSizeForImage(
}
static already_AddRefed<DataSourceSurface> BigBufferToDataSurface(
- BigBuffer& aData, uint32_t aStride, const IntSize& aImageSize,
+ const BigBuffer& aData, uint32_t aStride, const IntSize& aImageSize,
SurfaceFormat aFormat) {
if (!aData.Size() || !aImageSize.width || !aImageSize.height) {
return nullptr;
@@ -8325,22 +8323,13 @@ static already_AddRefed<DataSourceSurface> BigBufferToDataSurface(
nsresult nsContentUtils::DeserializeTransferableDataImageContainer(
const IPCTransferableDataImageContainer& aData,
imgIContainer** aContainer) {
- const IntSize size(aData.width(), aData.height());
- size_t maxBufferSize = 0;
- size_t usedBufferSize = 0;
- nsresult rv = CalculateBufferSizeForImage(
- aData.stride(), size, aData.format(), &maxBufferSize, &usedBufferSize);
- NS_ENSURE_SUCCESS(rv, rv);
- if (usedBufferSize > aData.data().Size()) {
- return NS_ERROR_FAILURE;
- }
- RefPtr<DataSourceSurface> surface =
- CreateDataSourceSurfaceFromData(size, aData.format(), aData.data().Data(),
- static_cast<int32_t>(aData.stride()));
+ RefPtr<DataSourceSurface> surface = IPCImageToSurface(aData.image());
if (!surface) {
return NS_ERROR_FAILURE;
}
- RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size);
+
+ RefPtr<gfxDrawable> drawable =
+ new gfxSurfaceDrawable(surface, surface->GetSize());
nsCOMPtr<imgIContainer> imageContainer =
image::ImageOps::CreateFromDrawable(drawable);
imageContainer.forget(aContainer);
@@ -8472,23 +8461,16 @@ void nsContentUtils::TransferableToIPCTransferableData(
if (!dataSurface) {
continue;
}
- size_t length;
- int32_t stride;
- Maybe<BigBuffer> surfaceData =
- GetSurfaceData(*dataSurface, &length, &stride);
- if (surfaceData.isNothing()) {
+ auto imageData = nsContentUtils::SurfaceToIPCImage(*dataSurface);
+ if (!imageData) {
continue;
}
IPCTransferableDataItem* item =
aTransferableData->items().AppendElement();
item->flavor() = flavorStr;
-
- mozilla::gfx::IntSize size = dataSurface->GetSize();
- item->data() = IPCTransferableDataImageContainer(
- std::move(*surfaceData), size.width, size.height, stride,
- dataSurface->GetFormat());
+ item->data() = IPCTransferableDataImageContainer(std::move(*imageData));
continue;
}
@@ -8569,8 +8551,7 @@ void nsContentUtils::TransferableToIPCTransferable(
aIPCTransferable->data() = std::move(ipcTransferableData);
aIPCTransferable->isPrivateData() = aTransferable->GetIsPrivateData();
- aIPCTransferable->requestingPrincipal() =
- aTransferable->GetRequestingPrincipal();
+ aIPCTransferable->dataPrincipal() = aTransferable->GetDataPrincipal();
aIPCTransferable->cookieJarSettings() = std::move(cookieJarSettingsArgs);
aIPCTransferable->contentPolicyType() = aTransferable->GetContentPolicyType();
aIPCTransferable->referrerInfo() = aTransferable->GetReferrerInfo();
@@ -8617,7 +8598,7 @@ Maybe<IPCImage> nsContentUtils::SurfaceToIPCImage(DataSourceSurface& aSurface) {
}
already_AddRefed<DataSourceSurface> nsContentUtils::IPCImageToSurface(
- IPCImage&& aImage) {
+ const IPCImage& aImage) {
return BigBufferToDataSurface(aImage.data(), aImage.stride(),
aImage.size().ToUnknownSize(), aImage.format());
}
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 4291d2c5d1..a1f466fa8d 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -115,7 +115,6 @@ class nsNodeInfoManager;
class nsParser;
class nsPIWindowRoot;
class nsPresContext;
-class nsStringBuffer;
class nsTextFragment;
class nsView;
class nsWrapperCache;
@@ -501,6 +500,9 @@ class nsContentUtils {
return GetCommonAncestorHelper(aNode1, aNode2);
}
+ static nsINode* GetClosestCommonShadowIncludingInclusiveAncestor(
+ nsINode* aNode1, nsINode* aNode2);
+
/**
* Returns the common flattened tree ancestor, if any, for two given content
* nodes.
@@ -2443,14 +2445,6 @@ class nsContentUtils {
[[nodiscard]] static bool PlatformToDOMLineBreaks(nsString& aString,
const mozilla::fallible_t&);
- /**
- * Populates aResultString with the contents of the string-buffer aBuf, up
- * to aBuf's null-terminator. aBuf must not be null. Ownership of the string
- * is not transferred.
- */
- static void PopulateStringFromStringBuffer(nsStringBuffer* aBuf,
- nsAString& aResultString);
-
static bool IsHandlingKeyBoardEvent() { return sIsHandlingKeyBoardEvent; }
static void SetIsHandlingKeyBoardEvent(bool aHandling) {
@@ -2778,9 +2772,13 @@ class nsContentUtils {
* check. aNode and aOffset can be computed with
* UIEvent::GetRangeParentContentAndOffset() if you want to
* check the click point.
+ * @param aAllowCrossShadowBoundary If true, this method allows the selection
+ * to have boundaries that cross shadow
+ * boundaries.
*/
static bool IsPointInSelection(const mozilla::dom::Selection& aSelection,
- const nsINode& aNode, const uint32_t aOffset);
+ const nsINode& aNode, const uint32_t aOffset,
+ const bool aAllowCrossShadowBoundary = false);
/**
* Takes a selection, and a text control element (<input> or <textarea>), and
@@ -3041,7 +3039,7 @@ class nsContentUtils {
static mozilla::Maybe<mozilla::dom::IPCImage> SurfaceToIPCImage(
mozilla::gfx::DataSourceSurface&);
static already_AddRefed<mozilla::gfx::DataSourceSurface> IPCImageToSurface(
- mozilla::dom::IPCImage&&);
+ const mozilla::dom::IPCImage&);
// Helpers shared by the implementations of nsContentUtils methods and
// nsIDOMWindowUtils methods.
@@ -3597,6 +3595,8 @@ class nsContentUtils {
aCallback);
static nsINode* GetCommonAncestorHelper(nsINode* aNode1, nsINode* aNode2);
+ static nsINode* GetCommonShadowIncludingAncestorHelper(nsINode* aNode1,
+ nsINode* aNode2);
static nsIContent* GetCommonFlattenedTreeAncestorHelper(
nsIContent* aContent1, nsIContent* aContent2);
diff --git a/dom/base/nsCopySupport.cpp b/dom/base/nsCopySupport.cpp
index 15c0cf4cf0..2e24d963c8 100644
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -245,7 +245,7 @@ static nsresult CreateTransferable(
NS_ENSURE_TRUE(aTransferable, NS_ERROR_NULL_POINTER);
aTransferable->Init(aDocument.GetLoadContext());
- aTransferable->SetRequestingPrincipal(aDocument.NodePrincipal());
+ aTransferable->SetDataPrincipal(aDocument.NodePrincipal());
if (aEncodedDocumentWithContext.mUnicodeEncodingIsTextHTML) {
// Set up a format converter so that clipboard flavor queries work.
// This converter isn't really used for conversions.
@@ -332,7 +332,8 @@ static nsresult PutToClipboard(
rv = CreateTransferable(aEncodedDocumentWithContext, aDocument, transferable);
NS_ENSURE_SUCCESS(rv, rv);
- rv = clipboard->SetData(transferable, nullptr, aClipboardID);
+ rv = clipboard->SetData(transferable, nullptr, aClipboardID,
+ aDocument.GetWindowContext());
NS_ENSURE_SUCCESS(rv, rv);
return rv;
@@ -455,9 +456,9 @@ nsresult nsCopySupport::GetContents(const nsACString& aMimeType,
return docEncoder->EncodeToString(outdata);
}
-nsresult nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement,
- nsILoadContext* aLoadContext,
- int32_t aCopyFlags) {
+nsresult nsCopySupport::ImageCopy(
+ nsIImageLoadingContent* aImageElement, nsILoadContext* aLoadContext,
+ int32_t aCopyFlags, mozilla::dom::WindowContext* aSettingWindowContext) {
nsresult rv;
nsCOMPtr<nsINode> imageNode = do_QueryInterface(aImageElement, &rv);
@@ -467,7 +468,7 @@ nsresult nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement,
nsCOMPtr<nsITransferable> trans(do_CreateInstance(kCTransferableCID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
trans->Init(aLoadContext);
- trans->SetRequestingPrincipal(imageNode->NodePrincipal());
+ trans->SetDataPrincipal(imageNode->NodePrincipal());
if (aCopyFlags & nsIDocumentViewerEdit::COPY_IMAGE_TEXT) {
// get the location from the element
@@ -527,11 +528,13 @@ nsresult nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement,
// check whether the system supports the selection clipboard or not.
if (clipboard->IsClipboardTypeSupported(nsIClipboard::kSelectionClipboard)) {
// put the transferable on the clipboard
- rv = clipboard->SetData(trans, nullptr, nsIClipboard::kSelectionClipboard);
+ rv = clipboard->SetData(trans, nullptr, nsIClipboard::kSelectionClipboard,
+ aSettingWindowContext);
NS_ENSURE_SUCCESS(rv, rv);
}
- return clipboard->SetData(trans, nullptr, nsIClipboard::kGlobalClipboard);
+ return clipboard->SetData(trans, nullptr, nsIClipboard::kGlobalClipboard,
+ aSettingWindowContext);
}
static nsresult AppendString(nsITransferable* aTransferable,
@@ -928,7 +931,12 @@ bool nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
NS_ENSURE_TRUE(transferable, false);
// put the transferable on the clipboard
- nsresult rv = clipboard->SetData(transferable, nullptr, aClipboardType);
+ WindowContext* settingWindowContext = nullptr;
+ if (aPresShell && aPresShell->GetDocument()) {
+ settingWindowContext = aPresShell->GetDocument()->GetWindowContext();
+ }
+ nsresult rv = clipboard->SetData(transferable, nullptr, aClipboardType,
+ settingWindowContext);
if (NS_FAILED(rv)) {
return false;
}
diff --git a/dom/base/nsCopySupport.h b/dom/base/nsCopySupport.h
index a6b4b5e783..5552e1a607 100644
--- a/dom/base/nsCopySupport.h
+++ b/dom/base/nsCopySupport.h
@@ -23,6 +23,7 @@ class PresShell;
namespace dom {
class Document;
class Selection;
+class WindowContext;
} // namespace dom
} // namespace mozilla
@@ -46,7 +47,8 @@ class nsCopySupport {
mozilla::dom::Document* aDoc, nsAString& outdata);
static nsresult ImageCopy(nsIImageLoadingContent* aImageElement,
- nsILoadContext* aLoadContext, int32_t aCopyFlags);
+ nsILoadContext* aLoadContext, int32_t aCopyFlags,
+ mozilla::dom::WindowContext* aSettingWindowContext);
// Get the selection as a transferable.
// @param aSelection Can be nullptr.
diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp
index 9bc8340b90..16bf19534a 100644
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2039,7 +2039,7 @@ nsDOMWindowUtils::GetBoundsWithoutFlushing(Element* aElement,
if (frame) {
nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(
frame, nsLayoutUtils::GetContainingBlockForClientRect(frame),
- nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
+ nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms);
rect->SetLayoutRect(r);
}
@@ -3333,12 +3333,6 @@ nsDOMWindowUtils::IsPartOfOpaqueLayer(Element* aElement, bool* aResult) {
}
NS_IMETHODIMP
-nsDOMWindowUtils::NumberOfAssignedPaintedLayers(
- const nsTArray<RefPtr<Element>>& aElements, uint32_t* aResult) {
- return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
nsDOMWindowUtils::EnableDialogs() {
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
index 4e2d604693..56827a457f 100644
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -45,6 +45,7 @@
#include "mozilla/AccessibleCaretEventHub.h"
#include "mozilla/ContentEvents.h"
+#include "mozilla/FocusModel.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
@@ -173,7 +174,6 @@ bool nsFocusManager::sTestMode = false;
uint64_t nsFocusManager::sFocusActionCounter = 0;
static const char* kObservedPrefs[] = {"accessibility.browsewithcaret",
- "accessibility.tabfocus_applies_to_xul",
"focusmanager.testmode", nullptr};
nsFocusManager::nsFocusManager()
@@ -198,10 +198,6 @@ nsFocusManager::~nsFocusManager() {
nsresult nsFocusManager::Init() {
sInstance = new nsFocusManager();
- nsIContent::sTabFocusModelAppliesToXUL =
- Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
- nsIContent::sTabFocusModelAppliesToXUL);
-
sTestMode = Preferences::GetBool("focusmanager.testmode", false);
Preferences::RegisterCallbacks(nsFocusManager::PrefChanged, kObservedPrefs,
@@ -229,10 +225,6 @@ void nsFocusManager::PrefChanged(const char* aPref) {
nsDependentCString pref(aPref);
if (pref.EqualsLiteral("accessibility.browsewithcaret")) {
UpdateCaretForCaretBrowsingMode();
- } else if (pref.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
- nsIContent::sTabFocusModelAppliesToXUL =
- Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
- nsIContent::sTabFocusModelAppliesToXUL);
} else if (pref.EqualsLiteral("focusmanager.testmode")) {
sTestMode = Preferences::GetBool("focusmanager.testmode", false);
}
@@ -3362,9 +3354,6 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
doc = aWindow->GetExtantDoc();
if (!doc) return NS_OK;
- LookAndFeel::GetInt(LookAndFeel::IntID::TabFocusModel,
- &nsIContent::sTabFocusModel);
-
// True if we are navigating by document (F6/Shift+F6) or false if we are
// navigating by element (Tab/Shift+Tab).
const bool forDocumentNavigation =
@@ -3674,12 +3663,10 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
}
} else {
if (aNavigateByKey) {
- // There is no parent, so call the tree owner. This will tell the
- // embedder or parent process that it should take the focus.
- bool tookFocus;
- docShell->TabToTreeOwner(forward, forDocumentNavigation, &tookFocus);
- // If the tree owner took the focus, blur the current element.
- if (tookFocus) {
+ // There is no parent, so move the focus to the parent process.
+ if (auto* child = BrowserChild::GetFrom(docShell)) {
+ child->SendMoveFocus(forward, forDocumentNavigation);
+ // Blur the current element.
RefPtr<BrowsingContext> focusedBC = GetFocusedBrowsingContext();
if (focusedBC && focusedBC->IsInProcess()) {
Blur(focusedBC, nullptr, true, true, false,
@@ -4303,20 +4290,22 @@ nsresult nsFocusManager::GetNextTabbableContent(
// Stepping out popover scope.
// For forward, search for the next tabbable content after invoker.
- // For backward, we should get back to the invoker.
+ // For backward, we should get back to the invoker if the invoker is
+ // focusable. Otherwise search for the next tabbable content after
+ // invoker.
if (oldTopLevelScopeOwner &&
IsOpenPopoverWithInvoker(oldTopLevelScopeOwner) &&
currentTopLevelScopeOwner != oldTopLevelScopeOwner) {
if (auto* popover = Element::FromNode(oldTopLevelScopeOwner)) {
RefPtr<nsIContent> invokerContent =
popover->GetPopoverData()->GetInvoker()->AsContent();
+ RefPtr<nsIContent> rootElement = invokerContent;
+ if (auto* doc = invokerContent->GetComposedDoc()) {
+ rootElement = doc->GetRootElement();
+ }
if (aForward) {
nsIFrame* frame = invokerContent->GetPrimaryFrame();
int32_t tabIndex = frame->IsFocusable().mTabIndex;
- RefPtr<nsIContent> rootElement = invokerContent;
- if (auto* doc = invokerContent->GetComposedDoc()) {
- rootElement = doc->GetRootElement();
- }
if (tabIndex >= 0 &&
(aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
nsresult rv = GetNextTabbableContent(
@@ -4327,12 +4316,19 @@ nsresult nsFocusManager::GetNextTabbableContent(
return rv;
}
}
- } else if (invokerContent &&
- invokerContent->IsFocusableWithoutStyle()) {
- // FIXME(emilio): The check above should probably use
- // nsIFrame::IsFocusable, not IsFocusableWithoutStyle.
- invokerContent.forget(aResultContent);
- return NS_OK;
+ } else if (invokerContent) {
+ nsIFrame* frame = invokerContent->GetPrimaryFrame();
+ if (frame && frame->IsFocusable()) {
+ invokerContent.forget(aResultContent);
+ return NS_OK;
+ }
+ nsresult rv = GetNextTabbableContent(
+ aPresShell, rootElement, aOriginalStartContent, invokerContent,
+ false, 0, true, false, aNavigateByKey, true,
+ aReachedToEndForDocumentNavigation, aResultContent);
+ if (NS_SUCCEEDED(rv) && *aResultContent) {
+ return rv;
+ }
}
}
}
@@ -5518,6 +5514,14 @@ bool nsFocusManager::CanSkipFocus(nsIContent* aContent) {
return false;
}
+static IsFocusableFlags FocusManagerFlagsToIsFocusableFlags(uint32_t aFlags) {
+ auto flags = IsFocusableFlags(0);
+ if (aFlags & nsIFocusManager::FLAG_BYMOUSE) {
+ flags |= IsFocusableFlags::WithMouse;
+ }
+ return flags;
+}
+
/* static */
Element* nsFocusManager::GetTheFocusableArea(Element* aTarget,
uint32_t aFlags) {
@@ -5547,7 +5551,8 @@ Element* nsFocusManager::GetTheFocusableArea(Element* aTarget,
// 3. If focus target is a navigable container with a non-null content
// navigable
// nsIFrame::IsFocusable will effectively perform the checks for them.
- if (frame->IsFocusable(aFlags & FLAG_BYMOUSE)) {
+ IsFocusableFlags flags = FocusManagerFlagsToIsFocusableFlags(aFlags);
+ if (frame->IsFocusable(flags)) {
return aTarget;
}
@@ -5567,8 +5572,7 @@ Element* nsFocusManager::GetTheFocusableArea(Element* aTarget,
}
}
- if (Element* firstFocusable =
- root->GetFocusDelegate(aFlags & FLAG_BYMOUSE)) {
+ if (Element* firstFocusable = root->GetFocusDelegate(flags)) {
return firstFocusable;
}
}
@@ -5586,7 +5590,7 @@ bool nsFocusManager::IsAreaElementFocusable(HTMLAreaElement& aArea) {
// GetPrimaryFrame() is not relevant as to whether it is focusable or
// not, so we have to do all the relevant checks manually for them.
return frame->IsVisibleConsideringAncestors() &&
- aArea.IsFocusableWithoutStyle(false /* aWithMouse */);
+ aArea.IsFocusableWithoutStyle();
}
nsresult NS_NewFocusManager(nsIFocusManager** aResult) {
diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp
index 7dcd265ca4..f263c8efcc 100644
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -48,6 +48,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/BaseProfilerMarkersPrerequisites.h"
#include "mozilla/BasicEvents.h"
+#include "mozilla/BounceTrackingStorageObserver.h"
#include "mozilla/CallState.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/DOMEventTargetHelper.h"
@@ -3502,18 +3503,7 @@ double nsGlobalWindowInner::GetDevicePixelRatio(CallerType aCallerType,
if (nsIGlobalObject::ShouldResistFingerprinting(
aCallerType, RFPTarget::WindowDevicePixelRatio)) {
- // Spoofing the DevicePixelRatio causes blurriness in some situations
- // on HiDPI displays. pdf.js is a non-system caller; but it can't
- // expose the fingerprintable information, so we can safely disable
- // spoofing in this situation. It doesn't address the issue for
- // web-rendered content (including pdf.js instances on the web.)
- // In the future we hope to have a better solution to fix all HiDPI
- // blurriness...
- nsAutoCString origin;
- nsresult rv = this->GetPrincipal()->GetOrigin(origin);
- if (NS_FAILED(rv) || origin != "resource://pdf.js"_ns) {
- return 1.0;
- }
+ return 2.0;
}
if (aCallerType == CallerType::NonSystem) {
@@ -4648,6 +4638,19 @@ already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyleHelper(
aError, nullptr);
}
+void nsGlobalWindowInner::MaybeNotifyStorageKeyUsed() {
+ // Only notify once per window lifetime.
+ if (hasNotifiedStorageKeyUsed) {
+ return;
+ }
+ nsresult rv =
+ BounceTrackingStorageObserver::OnInitialStorageAccess(GetWindowContext());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ hasNotifiedStorageKeyUsed = true;
+}
+
Storage* nsGlobalWindowInner::GetSessionStorage(ErrorResult& aError) {
nsIPrincipal* principal = GetPrincipal();
nsIPrincipal* storagePrincipal;
@@ -4770,6 +4773,8 @@ Storage* nsGlobalWindowInner::GetSessionStorage(ErrorResult& aError) {
}
}
+ MaybeNotifyStorageKeyUsed();
+
MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
("nsGlobalWindowInner %p returns %p sessionStorage", this,
mSessionStorage.get()));
@@ -4938,6 +4943,8 @@ Storage* nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) {
new PartitionedLocalStorage(this, principal, storagePrincipal, cache);
}
+ MaybeNotifyStorageKeyUsed();
+
MOZ_ASSERT(mLocalStorage);
MOZ_ASSERT(
mLocalStorage->Type() ==
@@ -4957,6 +4964,8 @@ IDBFactory* nsGlobalWindowInner::GetIndexedDB(JSContext* aCx,
}
}
+ MaybeNotifyStorageKeyUsed();
+
return mIndexedDB;
}
diff --git a/dom/base/nsGlobalWindowInner.h b/dom/base/nsGlobalWindowInner.h
index 215e362dad..33e1264571 100644
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -728,6 +728,9 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
mozilla::ErrorResult& aError);
void Btoa(const nsAString& aBinaryData, nsAString& aAsciiBase64String,
mozilla::ErrorResult& aError);
+
+ void MaybeNotifyStorageKeyUsed();
+
mozilla::dom::Storage* GetSessionStorage(mozilla::ErrorResult& aError);
mozilla::dom::Storage* GetLocalStorage(mozilla::ErrorResult& aError);
mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aError);
@@ -1389,6 +1392,11 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
mozilla::Maybe<mozilla::StorageAccess> mStorageAllowedCache;
uint32_t mStorageAllowedReasonCache;
+ // When window associated storage is accessed we need to notify the parent
+ // process. This flag is used to ensure we only do it once per window
+ // lifetime.
+ bool hasNotifiedStorageKeyUsed{false};
+
RefPtr<mozilla::dom::DebuggerNotificationManager>
mDebuggerNotificationManager;
diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h
index 700855370f..e6fc8fc430 100644
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -16,6 +16,7 @@ class nsTextFragment;
class nsIFrame;
namespace mozilla {
+enum class IsFocusableFlags : uint8_t;
class EventChainPreVisitor;
class HTMLEditor;
struct URLExtraData;
@@ -274,13 +275,14 @@ class nsIContent : public nsINode {
* some widgets may be focusable but removed from the tab order.
* @return whether the content is focusable via mouse, kbd or script.
*/
- virtual Focusable IsFocusableWithoutStyle(bool aWithMouse = false);
+ virtual Focusable IsFocusableWithoutStyle(
+ mozilla::IsFocusableFlags = mozilla::IsFocusableFlags(0));
// https://html.spec.whatwg.org/multipage/interaction.html#focus-delegate
- mozilla::dom::Element* GetFocusDelegate(bool aWithMouse) const;
+ mozilla::dom::Element* GetFocusDelegate(mozilla::IsFocusableFlags) const;
// https://html.spec.whatwg.org/multipage/interaction.html#autofocus-delegate
- mozilla::dom::Element* GetAutofocusDelegate(bool aWithMouse) const;
+ mozilla::dom::Element* GetAutofocusDelegate(mozilla::IsFocusableFlags) const;
/*
* Get desired IME state for the content.
@@ -753,21 +755,6 @@ class nsIContent : public nsINode {
virtual void DumpContent(FILE* out = stdout, int32_t aIndent = 0,
bool aDumpAll = true) const = 0;
#endif
-
- enum ETabFocusType {
- eTabFocus_textControlsMask =
- (1 << 0), // textboxes and lists always tabbable
- eTabFocus_formElementsMask = (1 << 1), // non-text form elements
- eTabFocus_linksMask = (1 << 2), // links
- eTabFocus_any = 1 + (1 << 1) + (1 << 2) // everything that can be focused
- };
-
- // Tab focus model bit field:
- static int32_t sTabFocusModel;
-
- // accessibility.tabfocus_applies_to_xul pref - if it is set to true,
- // the tabfocus bit field applies to xul elements.
- static bool sTabFocusModelAppliesToXUL;
};
NON_VIRTUAL_ADDREF_RELEASE(nsIContent)
diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp
index a14a22bcf0..035faa1ee1 100644
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2139,6 +2139,11 @@ void nsJSContext::EnsureStatics() {
(void*)JSGC_PARALLEL_MARKING_THRESHOLD_MB);
Preferences::RegisterCallbackAndCall(
+ SetMemoryPrefChangedCallbackInt,
+ "javascript.options.mem.gc_max_parallel_marking_threads",
+ (void*)JSGC_MAX_MARKING_THREADS);
+
+ Preferences::RegisterCallbackAndCall(
SetMemoryGCSliceTimePrefChangedCallback,
"javascript.options.mem.gc_incremental_slice_ms");
diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp
index cf15f239c5..e879bb5806 100644
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -792,8 +792,17 @@ void nsRange::ParentChainChanged(nsIContent* aContent) {
DoSetRange(mStart, mEnd, newRoot);
}
+bool nsRange::IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor(
+ const nsINode& aContainer) const {
+ MOZ_ASSERT(mCrossShadowBoundaryRange &&
+ mCrossShadowBoundaryRange->GetCommonAncestor());
+ return aContainer.IsShadowIncludingInclusiveDescendantOf(
+ mCrossShadowBoundaryRange->GetCommonAncestor());
+}
+
bool nsRange::IsPointComparableToRange(const nsINode& aContainer,
uint32_t aOffset,
+ bool aAllowCrossShadowBoundary,
ErrorResult& aRv) const {
// our range is in a good state?
if (!mIsPositioned) {
@@ -801,7 +810,13 @@ bool nsRange::IsPointComparableToRange(const nsINode& aContainer,
return false;
}
- if (!aContainer.IsInclusiveDescendantOf(mRoot)) {
+ const bool isContainerInRange =
+ aContainer.IsInclusiveDescendantOf(mRoot) ||
+ (aAllowCrossShadowBoundary && mCrossShadowBoundaryRange &&
+ IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor(
+ aContainer));
+
+ if (!isContainerInRange) {
// TODO(emilio): Switch to ThrowWrongDocumentError, but IsPointInRange
// relies on the error code right now in order to suppress the exception.
aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
@@ -832,8 +847,10 @@ bool nsRange::IsPointComparableToRange(const nsINode& aContainer,
}
bool nsRange::IsPointInRange(const nsINode& aContainer, uint32_t aOffset,
- ErrorResult& aRv) const {
- uint16_t compareResult = ComparePoint(aContainer, aOffset, aRv);
+ ErrorResult& aRv,
+ bool aAllowCrossShadowBoundary) const {
+ int16_t compareResult =
+ ComparePoint(aContainer, aOffset, aRv, aAllowCrossShadowBoundary);
// If the node isn't in the range's document, it clearly isn't in the range.
if (aRv.ErrorCodeIs(NS_ERROR_DOM_WRONG_DOCUMENT_ERR)) {
aRv.SuppressException();
@@ -844,8 +861,10 @@ bool nsRange::IsPointInRange(const nsINode& aContainer, uint32_t aOffset,
}
int16_t nsRange::ComparePoint(const nsINode& aContainer, uint32_t aOffset,
- ErrorResult& aRv) const {
- if (!IsPointComparableToRange(aContainer, aOffset, aRv)) {
+ ErrorResult& aRv,
+ bool aAllowCrossShadowBoundary) const {
+ if (!IsPointComparableToRange(aContainer, aOffset, aAllowCrossShadowBoundary,
+ aRv)) {
return 0;
}
@@ -853,11 +872,15 @@ int16_t nsRange::ComparePoint(const nsINode& aContainer, uint32_t aOffset,
MOZ_ASSERT(point.IsSetAndValid());
- if (Maybe<int32_t> order = nsContentUtils::ComparePoints(point, mStart);
+ if (Maybe<int32_t> order = nsContentUtils::ComparePoints(
+ point, aAllowCrossShadowBoundary ? MayCrossShadowBoundaryStartRef()
+ : StartRef());
order && *order <= 0) {
return int16_t(*order);
}
- if (Maybe<int32_t> order = nsContentUtils::ComparePoints(mEnd, point);
+ if (Maybe<int32_t> order = nsContentUtils::ComparePoints(
+ aAllowCrossShadowBoundary ? MayCrossShadowBoundaryEndRef() : EndRef(),
+ point);
order && *order == -1) {
return 1;
}
@@ -881,7 +904,9 @@ bool nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv) {
return false;
}
- if (!IsPointComparableToRange(*parent, *nodeIndex, IgnoreErrors())) {
+ if (!IsPointComparableToRange(*parent, *nodeIndex,
+ false /* aAllowCrossShadowBoundary */,
+ IgnoreErrors())) {
return false;
}
@@ -2866,7 +2891,8 @@ static void CollectClientRectsForSubtree(
if (!aTextOnly || isText) {
nsLayoutUtils::GetAllInFlowRectsAndTexts(
frame, nsLayoutUtils::GetContainingBlockForClientRect(frame),
- aCollector, aTextList, nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
+ aCollector, aTextList,
+ nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms);
if (isText) {
return;
}
@@ -3504,7 +3530,7 @@ void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
if (!mCrossShadowBoundaryRange) {
mCrossShadowBoundaryRange =
- StaticRange::Create(aStartBoundary, aEndBoundary, IgnoreErrors());
+ CrossShadowBoundaryRange::Create(aStartBoundary, aEndBoundary);
return;
}
diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h
index 94459087cb..3c8f43a7dc 100644
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -14,6 +14,7 @@
#include "nsCOMPtr.h"
#include "mozilla/dom/AbstractRange.h"
#include "mozilla/dom/StaticRange.h"
+#include "mozilla/dom/CrossShadowBoundaryRange.h"
#include "prmon.h"
#include "nsStubMutationObserver.h"
#include "nsWrapperCache.h"
@@ -209,7 +210,8 @@ class nsRange final : public mozilla::dom::AbstractRange,
int16_t CompareBoundaryPoints(uint16_t aHow, const nsRange& aOtherRange,
ErrorResult& aRv);
int16_t ComparePoint(const nsINode& aContainer, uint32_t aOffset,
- ErrorResult& aRv) const;
+ ErrorResult& aRv,
+ bool aAllowCrossShadowBoundary = false) const;
void DeleteContents(ErrorResult& aRv);
already_AddRefed<mozilla::dom::DocumentFragment> ExtractContents(
ErrorResult& aErr);
@@ -223,7 +225,8 @@ class nsRange final : public mozilla::dom::AbstractRange,
void InsertNode(nsINode& aNode, ErrorResult& aErr);
bool IntersectsNode(nsINode& aNode, ErrorResult& aRv);
bool IsPointInRange(const nsINode& aContainer, uint32_t aOffset,
- ErrorResult& aRv) const;
+ ErrorResult& aRv,
+ bool aAllowCrossShadowBoundary = false) const;
void ToString(nsAString& aReturn, ErrorResult& aErr);
void Detach();
@@ -336,8 +339,14 @@ class nsRange final : public mozilla::dom::AbstractRange,
// document as the range, aContainer is a DOCUMENT_TYPE_NODE and
// aOffset doesn't exceed aContainer's length.
bool IsPointComparableToRange(const nsINode& aContainer, uint32_t aOffset,
+ bool aAllowCrossShadowBoundary,
ErrorResult& aErrorResult) const;
+ // @return true iff aContainer is a shadow including inclusive descendant of
+ // the common ancestor of the mCrossBoundaryRange.
+ bool IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor(
+ const nsINode& aContainer) const;
+
/**
* @brief Returns true if the range is part of exactly one |Selection|.
*/
@@ -424,7 +433,7 @@ class nsRange final : public mozilla::dom::AbstractRange,
: mEnd.GetChildAtOffset();
}
- mozilla::dom::StaticRange* GetCrossShadowBoundaryRange() const {
+ mozilla::dom::CrossShadowBoundaryRange* GetCrossShadowBoundaryRange() const {
return mCrossShadowBoundaryRange;
}
@@ -549,7 +558,7 @@ class nsRange final : public mozilla::dom::AbstractRange,
// just return one point when a collapse is needed.
// Bug https://bugzilla.mozilla.org/show_bug.cgi?id=1886028 is going
// to be used to improve mCrossShadowBoundaryRange.
- RefPtr<mozilla::dom::StaticRange> mCrossShadowBoundaryRange;
+ RefPtr<mozilla::dom::CrossShadowBoundaryRange> mCrossShadowBoundaryRange;
friend class mozilla::dom::AbstractRange;
};
diff --git a/dom/base/nsTextFragment.cpp b/dom/base/nsTextFragment.cpp
index 5cba2577b8..c3f3d388be 100644
--- a/dom/base/nsTextFragment.cpp
+++ b/dom/base/nsTextFragment.cpp
@@ -198,23 +198,22 @@ bool nsTextFragment::SetTo(const char16_t* aBuffer, uint32_t aLength,
}
if (aForce2b && mState.mIs2b && !m2b->IsReadonly()) {
+ // Try to re-use our existing StringBuffer.
uint32_t storageSize = m2b->StorageSize();
uint32_t neededSize = aLength * sizeof(char16_t);
if (!neededSize) {
if (storageSize < AutoStringDefaultStorageSize) {
// If we're storing small enough nsStringBuffer, let's preserve it.
-
static_cast<char16_t*>(m2b->Data())[0] = char16_t(0);
mState.mLength = 0;
mState.mIsBidi = false;
return true;
}
- } else if ((neededSize < storageSize) &&
- ((storageSize / 2) <
- (neededSize + AutoStringDefaultStorageSize))) {
- // Don't try to reuse the existing nsStringBuffer, if it would have
- // lots of unused space.
-
+ } else if (neededSize < storageSize &&
+ (storageSize / 2) <
+ (neededSize + AutoStringDefaultStorageSize)) {
+ // Don't try to reuse the existing nsStringBuffer, if it would have lots
+ // of unused space.
memcpy(m2b->Data(), aBuffer, neededSize);
static_cast<char16_t*>(m2b->Data())[aLength] = char16_t(0);
mState.mLength = aLength;
@@ -226,19 +225,18 @@ bool nsTextFragment::SetTo(const char16_t* aBuffer, uint32_t aLength,
}
}
- ReleaseText();
-
if (aLength == 0) {
+ ReleaseText();
return true;
}
char16_t firstChar = *aBuffer;
if (!aForce2b && aLength == 1 && firstChar < 256) {
+ ReleaseText();
m1b = sSingleCharSharedString + firstChar;
mState.mInHeap = false;
mState.mIs2b = false;
mState.mLength = 1;
-
return true;
}
@@ -266,6 +264,7 @@ bool nsTextFragment::SetTo(const char16_t* aBuffer, uint32_t aLength,
if (ucp == uend && endNewLine - start <= TEXTFRAG_MAX_NEWLINES &&
ucp - endNewLine <= TEXTFRAG_WHITE_AFTER_NEWLINE) {
+ ReleaseText();
char** strings = space == ' ' ? sSpaceSharedString : sTabSharedString;
m1b = strings[endNewLine - start];
@@ -287,27 +286,29 @@ bool nsTextFragment::SetTo(const char16_t* aBuffer, uint32_t aLength,
if (first16bit != -1) { // aBuffer contains no non-8bit character
// Use ucs2 storage because we have to
- CheckedUint32 m2bSize = CheckedUint32(aLength) + 1;
- if (!m2bSize.isValid()) {
+ CheckedUint32 size = CheckedUint32(aLength) + 1;
+ if (!size.isValid()) {
return false;
}
- m2bSize *= sizeof(char16_t);
- if (!m2bSize.isValid()) {
+ size *= sizeof(char16_t);
+ if (!size.isValid()) {
return false;
}
- m2b = nsStringBuffer::Alloc(m2bSize.value()).take();
- if (!m2b) {
+ RefPtr<nsStringBuffer> newBuffer = nsStringBuffer::Alloc(size.value());
+ if (!newBuffer) {
return false;
}
- memcpy(m2b->Data(), aBuffer, aLength * sizeof(char16_t));
- static_cast<char16_t*>(m2b->Data())[aLength] = char16_t(0);
+ ReleaseText();
+ memcpy(newBuffer->Data(), aBuffer, aLength * sizeof(char16_t));
+ static_cast<char16_t*>(newBuffer->Data())[aLength] = char16_t(0);
+
+ m2b = newBuffer.forget().take();
mState.mIs2b = true;
if (aUpdateBidi) {
UpdateBidiFlag(aBuffer + first16bit, aLength - first16bit);
}
-
} else {
// Use 1 byte storage because we can
char* buff = static_cast<char*>(malloc(aLength));
@@ -315,6 +316,7 @@ bool nsTextFragment::SetTo(const char16_t* aBuffer, uint32_t aLength,
return false;
}
+ ReleaseText();
// Copy data
LossyConvertUtf16toLatin1(Span(aBuffer, aLength), Span(buff, aLength));
m1b = buff;
diff --git a/dom/base/nsTextFragment.h b/dom/base/nsTextFragment.h
index 5330815683..91efa49254 100644
--- a/dom/base/nsTextFragment.h
+++ b/dom/base/nsTextFragment.h
@@ -113,8 +113,7 @@ class nsTextFragment final {
}
ReleaseText();
if (aForce2b && !aUpdateBidi) {
- nsStringBuffer* buffer = nsStringBuffer::FromString(aString);
- if (buffer) {
+ if (nsStringBuffer* buffer = aString.GetStringBuffer()) {
NS_ADDREF(m2b = buffer);
mState.mInHeap = true;
mState.mIs2b = true;
@@ -154,19 +153,13 @@ class nsTextFragment final {
const mozilla::fallible_t& aFallible) const {
if (mState.mIs2b) {
if (aString.IsEmpty()) {
- m2b->ToString(mState.mLength, aString);
+ aString.Assign(m2b, mState.mLength);
return true;
}
- bool ok = aString.Append(Get2b(), mState.mLength, aFallible);
- if (!ok) {
- return false;
- }
-
- return true;
- } else {
- return AppendASCIItoUTF16(Substring(m1b, mState.mLength), aString,
- aFallible);
+ return aString.Append(Get2b(), mState.mLength, aFallible);
}
+ return AppendASCIItoUTF16(Substring(m1b, mState.mLength), aString,
+ aFallible);
}
/**
diff --git a/dom/base/test/browser_page_load_event_telemetry.js b/dom/base/test/browser_page_load_event_telemetry.js
index 871e06026a..530d45db9a 100644
--- a/dom/base/test/browser_page_load_event_telemetry.js
+++ b/dom/base/test/browser_page_load_event_telemetry.js
@@ -34,6 +34,16 @@ add_task(async function () {
30,
"Should have at least 30 page load events"
);
+
+ // Ensure the events in the pageload ping are reasonable.
+ record.forEach(entry => {
+ Assert.equal(entry.name, "page_load");
+ Assert.greater(parseInt(entry.extra.load_time), 0);
+ Assert.ok(
+ entry.extra.using_webdriver,
+ "Webdriver field should be set to true."
+ );
+ });
});
// Perform page load 30 times to trigger the ping being sent
diff --git a/dom/base/test/chrome/bug418986-1.js b/dom/base/test/chrome/bug418986-1.js
index e7e3c63b5c..565ae418dd 100644
--- a/dom/base/test/chrome/bug418986-1.js
+++ b/dom/base/test/chrome/bug418986-1.js
@@ -35,7 +35,7 @@ var test = function (isContent) {
["screen.orientation.type", "'landscape-primary'"],
["screen.orientation.angle", 0],
["screen.mozOrientation", "'landscape-primary'"],
- ["devicePixelRatio", 1],
+ ["devicePixelRatio", 2],
];
// checkPair: tests if members of pair [a, b] are equal when evaluated.
diff --git a/dom/base/test/fullscreen/browser.toml b/dom/base/test/fullscreen/browser.toml
index dc883a4ac2..7c989bafcb 100644
--- a/dom/base/test/fullscreen/browser.toml
+++ b/dom/base/test/fullscreen/browser.toml
@@ -45,6 +45,9 @@ skip-if = [
["browser_fullscreen-sizemode.js"]
["browser_fullscreen-tab-close-race.js"]
+skip-if = [
+ "apple_silicon && !debug", # Bug 1877642
+]
["browser_fullscreen-tab-close.js"]
diff --git a/dom/base/test/jsmodules/importmaps/bug_1893164_module_1.mjs b/dom/base/test/jsmodules/importmaps/bug_1893164_module_1.mjs
new file mode 100644
index 0000000000..45894ce609
--- /dev/null
+++ b/dom/base/test/jsmodules/importmaps/bug_1893164_module_1.mjs
@@ -0,0 +1,3 @@
+/* eslint-disable import/no-named-default, import/no-unresolved */
+import { default as default_non } from "./non_existing.mjs";
+import { default as default_3 } from "./bug_1893164_module_3.mjs";
diff --git a/dom/base/test/jsmodules/importmaps/bug_1893164_module_2.mjs b/dom/base/test/jsmodules/importmaps/bug_1893164_module_2.mjs
new file mode 100644
index 0000000000..e6f5e9f9f0
--- /dev/null
+++ b/dom/base/test/jsmodules/importmaps/bug_1893164_module_2.mjs
@@ -0,0 +1,5 @@
+/* eslint-disable import/no-named-default */
+import { default as default_3 } from "./bug_1893164_module_3.mjs";
+
+module2_loaded = true;
+result = default_3;
diff --git a/dom/base/test/jsmodules/importmaps/bug_1893164_module_3.mjs b/dom/base/test/jsmodules/importmaps/bug_1893164_module_3.mjs
new file mode 100644
index 0000000000..dbb41f0e1f
--- /dev/null
+++ b/dom/base/test/jsmodules/importmaps/bug_1893164_module_3.mjs
@@ -0,0 +1 @@
+export default 3;
diff --git a/dom/base/test/jsmodules/importmaps/bug_1894631_module_1.mjs b/dom/base/test/jsmodules/importmaps/bug_1894631_module_1.mjs
new file mode 100644
index 0000000000..b0c91fe0cf
--- /dev/null
+++ b/dom/base/test/jsmodules/importmaps/bug_1894631_module_1.mjs
@@ -0,0 +1,5 @@
+/* eslint-disable import/no-named-default, import/no-unresolved, import/named */
+import { default as default_2 } from "./bug_1894631_module_2.mjs";
+import { default as default_non } from "./non_existing.mjs";
+
+module1_loaded = true;
diff --git a/dom/base/test/jsmodules/importmaps/bug_1894631_module_2.mjs b/dom/base/test/jsmodules/importmaps/bug_1894631_module_2.mjs
new file mode 100644
index 0000000000..dd17da2abc
--- /dev/null
+++ b/dom/base/test/jsmodules/importmaps/bug_1894631_module_2.mjs
@@ -0,0 +1,3 @@
+/* eslint-disable import/no-named-default */
+import { default as default_3 } from "./bug_1894631_module_3.mjs";
+import { default as default_4 } from "./bug_1894631_module_4.mjs";
diff --git a/dom/base/test/jsmodules/importmaps/bug_1894631_module_3.mjs b/dom/base/test/jsmodules/importmaps/bug_1894631_module_3.mjs
new file mode 100644
index 0000000000..dbb41f0e1f
--- /dev/null
+++ b/dom/base/test/jsmodules/importmaps/bug_1894631_module_3.mjs
@@ -0,0 +1 @@
+export default 3;
diff --git a/dom/base/test/jsmodules/importmaps/bug_1894631_module_4.mjs b/dom/base/test/jsmodules/importmaps/bug_1894631_module_4.mjs
new file mode 100644
index 0000000000..456ffaafac
--- /dev/null
+++ b/dom/base/test/jsmodules/importmaps/bug_1894631_module_4.mjs
@@ -0,0 +1 @@
+export default 4;
diff --git a/dom/base/test/jsmodules/importmaps/mochitest.toml b/dom/base/test/jsmodules/importmaps/mochitest.toml
index 1f95b155ac..b71bf21def 100644
--- a/dom/base/test/jsmodules/importmaps/mochitest.toml
+++ b/dom/base/test/jsmodules/importmaps/mochitest.toml
@@ -3,6 +3,13 @@ support-files = [
"bug_1865410_module_a.mjs",
"bug_1865410_module_b.mjs",
"bug_1873417.mjs",
+ "bug_1893164_module_1.mjs",
+ "bug_1893164_module_2.mjs",
+ "bug_1893164_module_3.mjs",
+ "bug_1894631_module_1.mjs",
+ "bug_1894631_module_2.mjs",
+ "bug_1894631_module_3.mjs",
+ "bug_1894631_module_4.mjs",
"classic_script.js",
"module_chain_1.mjs",
"module_chain_2.mjs",
@@ -34,7 +41,10 @@ support-files = [
["test_bug_1873417.html"]
+["test_bug_1893164.html"]
+
["test_importMap_with_external_script.html"]
["test_importMap_with_nonexisting_module.html"]
["test_dynamic_importMap_with_external_script.html"]
["test_dynamic_importMap_load_completes.html"]
+["test_shared_submodules_with_modulepreload.html"]
diff --git a/dom/base/test/jsmodules/importmaps/test_bug_1893164.html b/dom/base/test/jsmodules/importmaps/test_bug_1893164.html
new file mode 100644
index 0000000000..6c5306b815
--- /dev/null
+++ b/dom/base/test/jsmodules/importmaps/test_bug_1893164.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test module cancel</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script>
+ var module2_loaded = false, result;
+
+ SimpleTest.waitForExplicitFinish();
+
+ function testLoaded() {
+ ok(module2_loaded, 'module_2.mjs should be loaded');
+ ok(result == 3, "result should be 3 from module_3.mjs");
+ SimpleTest.finish();
+ }
+</script>
+
+<script src="./bug_1893164_module_1.mjs" type="module"></script>
+<script src="./bug_1893164_module_2.mjs" type="module"></script>
+
+<body onload='testLoaded()'></body>
diff --git a/dom/base/test/jsmodules/importmaps/test_shared_submodules_with_modulepreload.html b/dom/base/test/jsmodules/importmaps/test_shared_submodules_with_modulepreload.html
new file mode 100644
index 0000000000..a99582f8d3
--- /dev/null
+++ b/dom/base/test/jsmodules/importmaps/test_shared_submodules_with_modulepreload.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test module cancel won't trigger an assert</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="modulepreload" href="./bug_1894631_module_2.mjs" />
+<link rel="modulepreload" href="./bug_1894631_module_3.mjs" />
+<link rel="modulepreload" href="./non_existing.mjs" />
+
+<script src="./bug_1894631_module_1.mjs" type="module" id="module_1"></script>
+<script>
+ var module1_loaded = false;
+ var module1_error = false;
+
+ SimpleTest.waitForExplicitFinish();
+
+ const module1 = document.getElementById("module_1");
+ module1.addEventListener("error", (event) => {
+ info("error event");
+ module1_error = true;
+ });
+
+ function testLoaded() {
+ ok(module1_error, "module_1.mjs should fire an error event");
+ ok(!module1_loaded, "module_1.mjs should not be loaded");
+ SimpleTest.finish();
+ }
+</script>
+
+<body onload='testLoaded()'>
+</body>
diff --git a/dom/base/test/test_anchor_target_blank_referrer.html b/dom/base/test/test_anchor_target_blank_referrer.html
index b494c28017..643cfd6c03 100644
--- a/dom/base/test/test_anchor_target_blank_referrer.html
+++ b/dom/base/test/test_anchor_target_blank_referrer.html
@@ -35,6 +35,9 @@
const testCases = [
{ACTION: ["generate-anchor-target-blank-policy-test"],
+ PREFS: [
+ ["dom.security.https_first", false], // need to test http and https
+ ],
TESTS: [
// Referrer policy is set in meta
{NAME: 'origin-in-meta-rel-noopener',
diff --git a/dom/base/test/test_focus_radio.html b/dom/base/test/test_focus_radio.html
index 8e97012745..9e14143143 100644
--- a/dom/base/test/test_focus_radio.html
+++ b/dom/base/test/test_focus_radio.html
@@ -2,7 +2,14 @@
<title>Test for input radio focus</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
-
+<style>
+.visHidden {
+ visibility: hidden;
+}
+.dispNone {
+ display: none;
+}
+</style>
<button id="before">before</button>
<fieldset>
<legend>a</legend>
@@ -26,6 +33,27 @@
<label><input id="d3" type="radio" name="d" disabled>d3</label>
<label><input id="d4" type="radio" name="d">d4</label>
</fieldset>
+<fieldset>
+ <legend>e</legend>
+ <label><input id="e1" type="radio" name="e" hidden>e1</label>
+ <label><input id="e2" type="radio" name="e">e2</label>
+ <label><input id="e3" type="radio" name="e" hidden>e3</label>
+ <label><input id="e4" type="radio" name="e">e4</label>
+</fieldset>
+<fieldset>
+ <legend>f</legend>
+ <label><input id="f1" type="radio" name="f" class="visHidden">f1</label>
+ <label><input id="f2" type="radio" name="f">f2</label>
+ <label><input id="f3" type="radio" name="f" class="visHidden">f3</label>
+ <label><input id="f4" type="radio" name="f">f4</label>
+</fieldset>
+<fieldset>
+ <legend>g</legend>
+ <label><input id="g1" type="radio" name="g" class="dispNone">g1</label>
+ <label><input id="g2" type="radio" name="g">g2</label>
+ <label><input id="g3" type="radio" name="g" class="dispNone">g3</label>
+ <label><input id="g4" type="radio" name="g">g4</label>
+</fieldset>
<button id="after">after</button>
<script>
@@ -51,8 +79,17 @@
expectFocusAfterKey("Tab", "c1");
// d1 is disabled, so d2 should get focus.
expectFocusAfterKey("Tab", "d2");
+ // e1 is hidden, so e2 should get focus.
+ expectFocusAfterKey("Tab", "e2");
+ // f1 is hidden, so f2 should get focus.
+ expectFocusAfterKey("Tab", "f2");
+ // g1 is hidden, so g2 should get focus.
+ expectFocusAfterKey("Tab", "g2");
expectFocusAfterKey("Tab", "after");
+ expectFocusAfterKey("Shift+Tab", "g2");
+ expectFocusAfterKey("Shift+Tab", "f2");
+ expectFocusAfterKey("Shift+Tab", "e2");
expectFocusAfterKey("Shift+Tab", "d2");
expectFocusAfterKey("Shift+Tab", "c1");
expectFocusAfterKey("Shift+Tab", "b2");
@@ -85,6 +122,36 @@
// Up arrow should wrap at the top.
expectFocusAfterKey("ArrowUp", "d4");
+ expectFocusAfterKey("Tab", "e2");
+ // e3 is hidden, so down arrow should focus e4.
+ expectFocusAfterKey("ArrowDown", "e4");
+ expectFocusAfterKey("ArrowUp", "e2");
+ expectFocusAfterKey("ArrowDown", "e4");
+ // Down arrow should wrap at the bottom, skipping hidden.
+ expectFocusAfterKey("ArrowDown", "e2");
+ // Up arrow should wrap at the top.
+ expectFocusAfterKey("ArrowUp", "e4");
+
+ expectFocusAfterKey("Tab", "f2");
+ // f3 is hidden, so down arrow should focus f4.
+ expectFocusAfterKey("ArrowDown", "f4");
+ expectFocusAfterKey("ArrowUp", "f2");
+ expectFocusAfterKey("ArrowDown", "f4");
+ // Down arrow should wrap at the bottom, skipping hidden.
+ expectFocusAfterKey("ArrowDown", "f2");
+ // Up arrow should wrap at the top.
+ expectFocusAfterKey("ArrowUp", "f4");
+
+ expectFocusAfterKey("Tab", "g2");
+ // g3 is hidden, so down arrow should focus g4.
+ expectFocusAfterKey("ArrowDown", "g4");
+ expectFocusAfterKey("ArrowUp", "g2");
+ expectFocusAfterKey("ArrowDown", "g4");
+ // Down arrow should wrap at the bottom, skipping hidden.
+ expectFocusAfterKey("ArrowDown", "g2");
+ // Up arrow should wrap at the top.
+ expectFocusAfterKey("ArrowUp", "g4");
+
SimpleTest.finish();
});
</script>