diff options
65 files changed, 1941 insertions, 466 deletions
diff --git a/dom/abort/AbortController.cpp b/dom/abort/AbortController.cpp new file mode 100644 index 000000000..bd8159e7b --- /dev/null +++ b/dom/abort/AbortController.cpp @@ -0,0 +1,98 @@ +/* -*- 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 "AbortController.h" +#include "AbortSignal.h" +#include "mozilla/dom/AbortControllerBinding.h" +#include "WorkerPrivate.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AbortController, mGlobal, mSignal) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(AbortController) +NS_IMPL_CYCLE_COLLECTING_RELEASE(AbortController) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbortController) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +/* static */ bool +AbortController::IsEnabled(JSContext* aCx, JSObject* aGlobal) +{ + if (NS_IsMainThread()) { + return Preferences::GetBool("dom.abortController.enabled", false); + } + + using namespace workers; + + // Otherwise, check the pref via the WorkerPrivate + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); + if (!workerPrivate) { + return false; + } + + return workerPrivate->AbortControllerEnabled(); +} + +/* static */ already_AddRefed<AbortController> +AbortController::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr<AbortController> abortController = new AbortController(global); + return abortController.forget(); +} + +AbortController::AbortController(nsIGlobalObject* aGlobal) + : mGlobal(aGlobal) + , mAborted(false) +{} + +JSObject* +AbortController::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return AbortControllerBinding::Wrap(aCx, this, aGivenProto); +} + +nsIGlobalObject* +AbortController::GetParentObject() const +{ + return mGlobal; +} + +AbortSignal* +AbortController::Signal() +{ + if (!mSignal) { + mSignal = new AbortSignal(this, mAborted); + } + + return mSignal; +} + +void +AbortController::Abort() +{ + if (mAborted) { + return; + } + + mAborted = true; + + if (mSignal) { + mSignal->Abort(); + } +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/abort/AbortController.h b/dom/abort/AbortController.h new file mode 100644 index 000000000..0b99dc49c --- /dev/null +++ b/dom/abort/AbortController.h @@ -0,0 +1,59 @@ +/* -*- 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_AbortController_h +#define mozilla_dom_AbortController_h + +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/AbortSignal.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "mozilla/ErrorResult.h" +#include "nsIGlobalObject.h" + +namespace mozilla { +namespace dom { + +class AbortController final : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbortController) + + static bool + IsEnabled(JSContext* aCx, JSObject* aGlobal); + + static already_AddRefed<AbortController> + Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); + + explicit AbortController(nsIGlobalObject* aGlobal); + + JSObject* + WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + nsIGlobalObject* + GetParentObject() const; + + AbortSignal* + Signal(); + + void + Abort(); + +private: + ~AbortController() = default; + + nsCOMPtr<nsIGlobalObject> mGlobal; + RefPtr<AbortSignal> mSignal; + + bool mAborted; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_AbortController_h diff --git a/dom/abort/AbortSignal.cpp b/dom/abort/AbortSignal.cpp new file mode 100644 index 000000000..20f36d2ab --- /dev/null +++ b/dom/abort/AbortSignal.cpp @@ -0,0 +1,124 @@ +/* -*- 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 "AbortSignal.h" +#include "AbortController.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/AbortSignalBinding.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_CLASS(AbortSignal) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AbortSignal, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mController) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AbortSignal, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mController) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AbortSignal) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(AbortSignal, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(AbortSignal, DOMEventTargetHelper) + +AbortSignal::AbortSignal(AbortController* aController, + bool aAborted) + : DOMEventTargetHelper(aController->GetParentObject()) + , mController(aController) + , mAborted(aAborted) +{} + +AbortSignal::AbortSignal(bool aAborted) + : mAborted(aAborted) +{} + +JSObject* +AbortSignal::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return AbortSignalBinding::Wrap(aCx, this, aGivenProto); +} + +bool +AbortSignal::Aborted() const +{ + return mAborted; +} + +void +AbortSignal::Abort() +{ + MOZ_ASSERT(!mAborted); + mAborted = true; + + // Let's inform the followers. + for (uint32_t i = 0; i < mFollowers.Length(); ++i) { + mFollowers[i]->Aborted(); + } + + EventInit init; + init.mBubbles = false; + init.mCancelable = false; + + RefPtr<Event> event = + Event::Constructor(this, NS_LITERAL_STRING("abort"), init); + event->SetTrusted(true); + + bool dummy; + DispatchEvent(event, &dummy); +} + +void +AbortSignal::AddFollower(AbortSignal::Follower* aFollower) +{ + MOZ_DIAGNOSTIC_ASSERT(aFollower); + if (!mFollowers.Contains(aFollower)) { + mFollowers.AppendElement(aFollower); + } +} + +void +AbortSignal::RemoveFollower(AbortSignal::Follower* aFollower) +{ + MOZ_DIAGNOSTIC_ASSERT(aFollower); + mFollowers.RemoveElement(aFollower); +} + +// AbortSignal::Follower +// ---------------------------------------------------------------------------- + +AbortSignal::Follower::~Follower() +{ + Unfollow(); +} + +void +AbortSignal::Follower::Follow(AbortSignal* aSignal) +{ + MOZ_DIAGNOSTIC_ASSERT(aSignal); + + Unfollow(); + + mFollowingSignal = aSignal; + aSignal->AddFollower(this); +} + +void +AbortSignal::Follower::Unfollow() +{ + if (mFollowingSignal) { + mFollowingSignal->RemoveFollower(this); + mFollowingSignal = nullptr; + } +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/abort/AbortSignal.h b/dom/abort/AbortSignal.h new file mode 100644 index 000000000..35e582942 --- /dev/null +++ b/dom/abort/AbortSignal.h @@ -0,0 +1,76 @@ +/* -*- 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_AbortSignal_h +#define mozilla_dom_AbortSignal_h + +#include "mozilla/DOMEventTargetHelper.h" + +namespace mozilla { +namespace dom { + +class AbortController; +class AbortSignal; + +class AbortSignal final : public DOMEventTargetHelper +{ +public: + // This class must be implemented by objects who want to follow a AbortSignal. + class Follower + { + public: + virtual void Aborted() = 0; + + protected: + virtual ~Follower(); + + void + Follow(AbortSignal* aSignal); + + void + Unfollow(); + + RefPtr<AbortSignal> mFollowingSignal; + }; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AbortSignal, DOMEventTargetHelper) + + AbortSignal(AbortController* aController, bool aAborted); + explicit AbortSignal(bool aAborted); + + JSObject* + WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + bool + Aborted() const; + + void + Abort(); + + IMPL_EVENT_HANDLER(abort); + + void + AddFollower(Follower* aFollower); + + void + RemoveFollower(Follower* aFollower); + +private: + ~AbortSignal() = default; + + RefPtr<AbortController> mController; + + // Raw pointers. Follower unregisters itself in the DTOR. + nsTArray<Follower*> mFollowers; + + bool mAborted; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_AbortSignal_h diff --git a/dom/abort/moz.build b/dom/abort/moz.build new file mode 100644 index 000000000..cb48ee15f --- /dev/null +++ b/dom/abort/moz.build @@ -0,0 +1,26 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +with Files("**"): + BUG_COMPONENT = ("Core", "DOM") + +TEST_DIRS += ['tests'] + +EXPORTS.mozilla.dom += [ + 'AbortController.h', + 'AbortSignal.h', +] + +UNIFIED_SOURCES += [ + 'AbortController.cpp', + 'AbortSignal.cpp', +] + +LOCAL_INCLUDES += [ + '../workers', +] + +FINAL_LIBRARY = 'xul' diff --git a/dom/abort/tests/file_abort_controller.html b/dom/abort/tests/file_abort_controller.html new file mode 100644 index 000000000..3a15fa346 --- /dev/null +++ b/dom/abort/tests/file_abort_controller.html @@ -0,0 +1,113 @@ +<script> +function ok(a, msg) { + parent.postMessage({ type: "check", status: !!a, message: msg }, "*"); +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +function testWebIDL() { + ok("FetchController" in self, "We have a FetchController prototype"); + ok("FetchSignal" in self, "We have a FetchSignal prototype"); + + var fc = new FetchController(); + ok(!!fc, "FetchController can be created"); + ok(fc instanceof FetchController, "FetchController is a FetchController"); + + ok(!!fc.signal, "FetchController has a signal"); + ok(fc.signal instanceof FetchSignal, "fetchSignal is a FetchSignal"); + is(fc.signal.aborted, false, "By default FetchSignal.aborted is false"); + next(); +} + +function testUpdateData() { + var fc = new FetchController(); + + is(fc.signal.aborted, false, "By default FetchSignal.aborted is false"); + + fc.abort(); + is(fc.signal.aborted, true, "Signal is aborted"); + + next(); +} + +function testAbortEvent() { + var fc = new FetchController(); + fc.signal.onabort = function(e) { + is(e.type, "abort", "Abort received"); + next(); + } + fc.abort(); +} + +function testAbortedFetch() { + var fc = new FetchController(); + fc.abort(); + + fetch('slow.sjs', { signal: fc.signal }).then(() => { + ok(false, "Fetch should not return a resolved promise"); + }, e => { + is(e.name, "AbortError", "We have an abort error"); + }).then(next); +} + +function testFetchAndAbort() { + var fc = new FetchController(); + + var p = fetch('slow.sjs', { signal: fc.signal }); + fc.abort(); + + p.then(() => { + ok(false, "Fetch should not return a resolved promise"); + }, e => { + is(e.name, "AbortError", "We have an abort error"); + }).then(next); +} + +function testWorkerAbortedFetch() { + var w = new Worker('worker_fetch_controller.js'); + w.onmessage = function(e) { + ok(e.data, "Abort + Fetch works in workers"); + next(); + } + w.postMessage('testWorkerAbortedFetch'); +} + +function testWorkerFetchAndAbort() { + var w = new Worker('worker_fetch_controller.js'); + w.onmessage = function(e) { + ok(e.data, "Abort + Fetch works in workers"); + next(); + } + w.postMessage('testWorkerFetchAndAbort'); +} + +var steps = [ + // Simple stuff + testWebIDL, + testUpdateData, + + // Event propagation + testAbortEvent, + + // fetch + signaling + testAbortedFetch, + testFetchAndAbort, + testWorkerAbortedFetch, + testWorkerFetchAndAbort, +]; + +function next() { + if (!steps.length) { + parent.postMessage({ type: "finish" }, "*"); + return; + } + + var step = steps.shift(); + step(); +} + +next(); + +</script> diff --git a/dom/abort/tests/mochitest.ini b/dom/abort/tests/mochitest.ini new file mode 100644 index 000000000..c8cc95fda --- /dev/null +++ b/dom/abort/tests/mochitest.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + file_abort_controller.html + worker_fetch_controller.js + +[test_abort_controller.html] diff --git a/dom/abort/tests/moz.build b/dom/abort/tests/moz.build new file mode 100644 index 000000000..8e5cb5d71 --- /dev/null +++ b/dom/abort/tests/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOCHITEST_MANIFESTS += ['mochitest.ini'] + diff --git a/dom/abort/tests/test_abort_controller.html b/dom/abort/tests/test_abort_controller.html new file mode 100644 index 000000000..812fb9161 --- /dev/null +++ b/dom/abort/tests/test_abort_controller.html @@ -0,0 +1,40 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test FetchController</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> + +SpecialPowers.pushPrefEnv({"set": [["dom.fetchController.enabled", true ]]}, () => { + let ifr = document.createElement('iframe'); + ifr.src = "file_fetch_controller.html"; + document.body.appendChild(ifr); + + onmessage = function(e) { + if (e.data.type == "finish") { + SimpleTest.finish(); + return; + } + + if (e.data.type == "check") { + ok(e.data.status, e.data.message); + return; + } + + ok(false, "Something when wrong."); + } +}); + +SimpleTest.waitForExplicitFinish(); + +</script> +</body> +</html> + diff --git a/dom/abort/tests/worker_abort_controller.js b/dom/abort/tests/worker_abort_controller.js new file mode 100644 index 000000000..6fd1c7888 --- /dev/null +++ b/dom/abort/tests/worker_abort_controller.js @@ -0,0 +1,27 @@ +function testWorkerAbortedFetch() { + var fc = new AbortController(); + fc.abort(); + + fetch('slow.sjs', { signal: fc.signal }).then(() => { + postMessage(false); + }, e => { + postMessage(e.name == "AbortError"); + }); +} + +function testWorkerFetchAndAbort() { + var fc = new AbortController(); + + var p = fetch('slow.sjs', { signal: fc.signal }); + fc.abort(); + + p.then(() => { + postMessage(false); + }, e => { + postMessage(e.name == "AbortError"); + }); +} + +onmessage = function(e) { + self[e.data](); +} diff --git a/dom/base/ChromeNodeList.cpp b/dom/base/ChromeNodeList.cpp index ffa101971..2e9f05823 100644 --- a/dom/base/ChromeNodeList.cpp +++ b/dom/base/ChromeNodeList.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/ChromeNodeList.h" #include "mozilla/dom/ChromeNodeListBinding.h" +#include "nsPIDOMWindow.h" using namespace mozilla; using namespace mozilla::dom; diff --git a/dom/base/ChromeNodeList.h b/dom/base/ChromeNodeList.h index 9908808ac..8dbd42be3 100644 --- a/dom/base/ChromeNodeList.h +++ b/dom/base/ChromeNodeList.h @@ -6,6 +6,7 @@ #include "nsCOMArray.h" #include "nsContentList.h" +#include "nsIDocument.h" namespace mozilla { class ErrorResult; diff --git a/dom/base/DocumentOrShadowRoot.cpp b/dom/base/DocumentOrShadowRoot.cpp new file mode 100644 index 000000000..13ee3cb15 --- /dev/null +++ b/dom/base/DocumentOrShadowRoot.cpp @@ -0,0 +1,149 @@ +/* -*- 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 "DocumentOrShadowRoot.h" +#include "mozilla/dom/StyleSheetList.h" +#include "nsDocument.h" +#include "nsFocusManager.h" +#include "ShadowRoot.h" +#include "XULDocument.h" + +class nsINode; +class nsIDocument; +class ShadowRoot; + +namespace mozilla { +namespace dom { + +DocumentOrShadowRoot::DocumentOrShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) + : mAsNode(aShadowRoot) + , mKind(Kind::ShadowRoot) +{ + MOZ_ASSERT(mAsNode); +} + +DocumentOrShadowRoot::DocumentOrShadowRoot(nsIDocument* aDoc) + : mAsNode(aDoc) + , mKind(Kind::Document) +{ + MOZ_ASSERT(mAsNode); +} + +StyleSheetList& +DocumentOrShadowRoot::EnsureDOMStyleSheets() +{ + if (!mDOMStyleSheets) { + mDOMStyleSheets = new StyleSheetList(*this); + } + return *mDOMStyleSheets; +} + +Element* +DocumentOrShadowRoot::GetElementById(const nsAString& aElementId) +{ + if (MOZ_UNLIKELY(aElementId.IsEmpty())) { + nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc()); + return nullptr; + } + + if (nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aElementId)) { + if (Element* el = entry->GetIdElement()) { + return el; + } + } + + if (MOZ_UNLIKELY(mKind == Kind::Document && + static_cast<nsIDocument&>(AsNode()).IsXULDocument())) { + return static_cast<XULDocument&>(AsNode()).GetRefById(aElementId); + } + + return nullptr; +} + +already_AddRefed<nsContentList> +DocumentOrShadowRoot::GetElementsByTagNameNS(const nsAString& aNamespaceURI, + const nsAString& aLocalName) +{ + ErrorResult rv; + RefPtr<nsContentList> list = + GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv); + if (rv.Failed()) { + return nullptr; + } + return list.forget(); +} + +already_AddRefed<nsContentList> +DocumentOrShadowRoot::GetElementsByTagNameNS(const nsAString& aNamespaceURI, + const nsAString& aLocalName, + mozilla::ErrorResult& aResult) +{ + int32_t nameSpaceId = kNameSpaceID_Wildcard; + + if (!aNamespaceURI.EqualsLiteral("*")) { + aResult = + nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, + nameSpaceId); + if (aResult.Failed()) { + return nullptr; + } + } + + NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!"); + return NS_GetContentList(&AsNode(), nameSpaceId, aLocalName); +} + +already_AddRefed<nsContentList> +DocumentOrShadowRoot::GetElementsByClassName(const nsAString& aClasses) +{ + return nsContentUtils::GetElementsByClassName(&AsNode(), aClasses); +} + +nsIContent* +DocumentOrShadowRoot::Retarget(nsIContent* aContent) const +{ + for (nsIContent* cur = aContent; + cur; + cur = cur->GetContainingShadowHost()) { + if (cur->SubtreeRoot() == &AsNode()) { + return cur; + } + } + return nullptr; +} + +Element* +DocumentOrShadowRoot::GetRetargetedFocusedElement() +{ + if (nsCOMPtr<nsPIDOMWindowOuter> window = AsNode().OwnerDoc()->GetWindow()) { + nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; + nsIContent* focusedContent = + nsFocusManager::GetFocusedDescendant(window, + false, + getter_AddRefs(focusedWindow)); + // be safe and make sure the element is from this document + if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) { + if (focusedContent->ChromeOnlyAccess()) { + focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent(); + } + + if (focusedContent) { + if (!nsDocument::IsWebComponentsEnabled(focusedContent)) { + return focusedContent->AsElement(); + } + + if (nsIContent* retarget = Retarget(focusedContent)) { + return retarget->AsElement(); + } + } + } + } + + return nullptr; +} + +} +} diff --git a/dom/base/DocumentOrShadowRoot.h b/dom/base/DocumentOrShadowRoot.h new file mode 100644 index 000000000..2092cd54f --- /dev/null +++ b/dom/base/DocumentOrShadowRoot.h @@ -0,0 +1,160 @@ +/* -*- 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_DocumentOrShadowRoot_h__ +#define mozilla_dom_DocumentOrShadowRoot_h__ + +#include "nsTArray.h" +#include "nsIdentifierMapEntry.h" +#include "nsContentListDeclarations.h" +#include "nsNameSpaceManager.h" +#include "mozilla/dom/NameSpaceConstants.h" + +class nsContentList; +class nsINode; + +namespace mozilla { +class StyleSheet; + +namespace dom { + +class StyleSheetList; +class ShadowRoot; + +/** + * A class meant to be shared by ShadowRoot and Document, that holds a list of + * stylesheets. + * + * TODO(emilio, bug 1418159): In the future this should hold most of the + * relevant style state, this should allow us to fix bug 548397. + */ +class DocumentOrShadowRoot +{ + enum class Kind { + Document, + ShadowRoot, + }; + +public: + explicit DocumentOrShadowRoot(nsIDocument*); + explicit DocumentOrShadowRoot(mozilla::dom::ShadowRoot*); + + nsINode& AsNode() + { + return *mAsNode; + } + + const nsINode& AsNode() const + { + return *mAsNode; + } + + StyleSheet* SheetAt(size_t aIndex) const + { + return mStyleSheets.SafeElementAt(aIndex); + } + + size_t SheetCount() const + { + return mStyleSheets.Length(); + } + + int32_t IndexOfSheet(const StyleSheet& aSheet) const + { + return mStyleSheets.IndexOf(&aSheet); + } + + void InsertSheetAt(size_t aIndex, StyleSheet& aSheet) + { + mStyleSheets.InsertElementAt(aIndex, &aSheet); + } + + void RemoveSheet(StyleSheet& aSheet) + { + mStyleSheets.RemoveElement(&aSheet); + } + + void AppendStyleSheet(StyleSheet& aSheet) + { + mStyleSheets.AppendElement(&aSheet); + } + + StyleSheetList& EnsureDOMStyleSheets(); + + Element* GetElementById(const nsAString& aElementId); + + /** + * This method returns _all_ the elements in this scope which have id + * aElementId, if there are any. Otherwise it returns null. + * + * This is useful for stuff like QuerySelector optimization and such. + */ + inline const nsTArray<Element*>* + GetAllElementsForId(const nsAString& aElementId) const; + + already_AddRefed<nsContentList> + GetElementsByTagName(const nsAString& aTagName) + { + return NS_GetContentList(&AsNode(), kNameSpaceID_Unknown, aTagName); + } + + already_AddRefed<nsContentList> + GetElementsByTagNameNS(const nsAString& aNamespaceURI, + const nsAString& aLocalName); + + already_AddRefed<nsContentList> + GetElementsByTagNameNS(const nsAString& aNamespaceURI, + const nsAString& aLocalName, + mozilla::ErrorResult&); + + already_AddRefed<nsContentList> + GetElementsByClassName(const nsAString& aClasses); + + ~DocumentOrShadowRoot() = default; + +protected: + nsIContent* Retarget(nsIContent* aContent) const; + + /** + * If focused element's subtree root is this document or shadow root, return + * focused element, otherwise, get the shadow host recursively until the + * shadow host's subtree root is this document or shadow root. + */ + Element* GetRetargetedFocusedElement(); + + nsTArray<RefPtr<mozilla::StyleSheet>> mStyleSheets; + RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets; + + /* + * mIdentifierMap works as follows for IDs: + * 1) Attribute changes affect the table immediately (removing and adding + * entries as needed). + * 2) Removals from the DOM affect the table immediately + * 3) Additions to the DOM always update existing entries for names, and add + * new ones for IDs. + */ + nsTHashtable<nsIdentifierMapEntry> mIdentifierMap; + + nsINode* mAsNode; + const Kind mKind; +}; + +inline const nsTArray<Element*>* +DocumentOrShadowRoot::GetAllElementsForId(const nsAString& aElementId) const +{ + if (aElementId.IsEmpty()) { + return nullptr; + } + + nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aElementId); + return entry ? &entry->GetIdElements() : nullptr; +} + +} + +} + +#endif diff --git a/dom/base/Element.h b/dom/base/Element.h index 3d9b80a98..fd4fa7108 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -139,6 +139,7 @@ class EventStateManager; namespace dom { +struct CustomElementDefinition; class Animation; class CustomElementRegistry; class Link; diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 6fbe04d25..e33d3c63a 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1138,6 +1138,15 @@ FragmentOrElement::GetAssignedSlot() const return slots ? slots->mAssignedSlot.get() : nullptr; } +nsIContent* +nsIContent::GetContainingShadowHost() const +{ + if (mozilla::dom::ShadowRoot* shadow = GetContainingShadow()) { + return shadow->GetHost(); + } + return nullptr; +} + void FragmentOrElement::SetAssignedSlot(HTMLSlotElement* aSlot) { diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp index 2383c951a..c354e04c1 100644 --- a/dom/base/ShadowRoot.cpp +++ b/dom/base/ShadowRoot.cpp @@ -58,6 +58,7 @@ ShadowRoot::ShadowRoot(Element* aElement, bool aClosed, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, nsXBLPrototypeBinding* aProtoBinding) : DocumentFragment(aNodeInfo) + , DocumentOrShadowRoot(this) , mProtoBinding(aProtoBinding) , mInsertionPointChanged(false) , mIsComposedDocParticipant(false) @@ -240,7 +241,7 @@ ShadowRoot::InsertSheet(StyleSheet* aSheet, linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet - MOZ_DIAGNOSTIC_ASSERT(mProtoBinding->SheetCount() == StyleScope::SheetCount()); + MOZ_DIAGNOSTIC_ASSERT(mProtoBinding->SheetCount() == DocumentOrShadowRoot::SheetCount()); #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED // FIXME(emilio, bug 1425759): For now we keep them duplicated, the proto // binding will disappear soon (tm). @@ -278,49 +279,17 @@ void ShadowRoot::RemoveSheet(StyleSheet* aSheet) { mProtoBinding->RemoveStyleSheet(aSheet); - StyleScope::RemoveSheet(*aSheet); + DocumentOrShadowRoot::RemoveSheet(*aSheet); if (aSheet->IsApplicable()) { StyleSheetChanged(); } } -Element* -ShadowRoot::GetElementById(const nsAString& aElementId) -{ - nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId); - return entry ? entry->GetIdElement() : nullptr; -} - -already_AddRefed<nsContentList> -ShadowRoot::GetElementsByTagName(const nsAString& aTagName) -{ - return NS_GetContentList(this, kNameSpaceID_Unknown, aTagName); -} - -already_AddRefed<nsContentList> -ShadowRoot::GetElementsByTagNameNS(const nsAString& aNamespaceURI, - const nsAString& aLocalName) -{ - int32_t nameSpaceId = kNameSpaceID_Wildcard; - - if (!aNamespaceURI.EqualsLiteral("*")) { - nsresult rv = - nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, - nameSpaceId); - NS_ENSURE_SUCCESS(rv, nullptr); - } - - NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!"); - - return NS_GetContentList(this, nameSpaceId, aLocalName); -} - void ShadowRoot::AddToIdTable(Element* aElement, nsIAtom* aId) { - nsIdentifierMapEntry *entry = - mIdentifierMap.PutEntry(nsDependentAtomString(aId)); + nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId); if (entry) { entry->AddIdElement(aElement); } @@ -329,8 +298,7 @@ ShadowRoot::AddToIdTable(Element* aElement, nsIAtom* aId) void ShadowRoot::RemoveFromIdTable(Element* aElement, nsIAtom* aId) { - nsIdentifierMapEntry *entry = - mIdentifierMap.GetEntry(nsDependentAtomString(aId)); + nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId); if (entry) { entry->RemoveIdElement(aElement); if (entry->IsEmpty()) { @@ -339,12 +307,6 @@ ShadowRoot::RemoveFromIdTable(Element* aElement, nsIAtom* aId) } } -already_AddRefed<nsContentList> -ShadowRoot::GetElementsByClassName(const nsAString& aClasses) -{ - return nsContentUtils::GetElementsByClassName(this, aClasses); -} - nsresult ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) { @@ -499,6 +461,12 @@ ShadowRoot::DistributeAllNodes() DistributionChanged(); } +Element* +ShadowRoot::GetActiveElement() +{ + return GetRetargetedFocusedElement(); +} + void ShadowRoot::GetInnerHTML(nsAString& aInnerHTML) { diff --git a/dom/base/ShadowRoot.h b/dom/base/ShadowRoot.h index 21c6e1733..f83a6f774 100644 --- a/dom/base/ShadowRoot.h +++ b/dom/base/ShadowRoot.h @@ -8,7 +8,7 @@ #define mozilla_dom_shadowroot_h__ #include "mozilla/dom/DocumentFragment.h" -#include "mozilla/dom/StyleScope.h" +#include "mozilla/dom/DocumentOrShadowRoot.h" #include "nsCOMPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsIContentInlines.h" @@ -29,7 +29,7 @@ namespace dom { class Element; class ShadowRoot final : public DocumentFragment, - public StyleScope, + public DocumentOrShadowRoot, public nsStubMutationObserver { public: @@ -57,22 +57,14 @@ public: return mMode == ShadowRootMode::Closed; } - // StyleScope. - nsINode& AsNode() final - { - return *this; - } - // [deprecated] Shadow DOM v0 - void AddToIdTable(Element* aElement, nsIAtom* aId); - void RemoveFromIdTable(Element* aElement, nsIAtom* aId); void InsertSheet(StyleSheet* aSheet, nsIContent* aLinkingContent); void RemoveSheet(StyleSheet* aSheet); bool ApplyAuthorStyles(); void SetApplyAuthorStyles(bool aApplyAuthorStyles); StyleSheetList* StyleSheets() { - return &StyleScope::EnsureDOMStyleSheets(); + return &DocumentOrShadowRoot::EnsureDOMStyleSheets(); } /** @@ -123,15 +115,13 @@ public: static ShadowRoot* FromNode(nsINode* aNode); + void AddToIdTable(Element* aElement, nsIAtom* aId); + void RemoveFromIdTable(Element* aElement, nsIAtom* aId); + // WebIDL methods. - Element* GetElementById(const nsAString& aElementId); - already_AddRefed<nsContentList> - GetElementsByTagName(const nsAString& aNamespaceURI); - already_AddRefed<nsContentList> - GetElementsByTagNameNS(const nsAString& aNamespaceURI, - const nsAString& aLocalName); - already_AddRefed<nsContentList> - GetElementsByClassName(const nsAString& aClasses); + using mozilla::dom::DocumentOrShadowRoot::GetElementById; + + Element* GetActiveElement(); void GetInnerHTML(nsAString& aInnerHTML); void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError); void StyleSheetChanged(); @@ -154,7 +144,6 @@ protected: // are in the shadow tree and should be kept alive by its parent. nsClassHashtable<nsStringHashKey, nsTArray<mozilla::dom::HTMLSlotElement*>> mSlotMap; - nsTHashtable<nsIdentifierMapEntry> mIdentifierMap; nsXBLPrototypeBinding* mProtoBinding; // It is necessary to hold a reference to the associated nsXBLBinding diff --git a/dom/base/StyleScope.cpp b/dom/base/StyleScope.cpp deleted file mode 100644 index 6c708a8ba..000000000 --- a/dom/base/StyleScope.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* -*- 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 "StyleScope.h" -#include "mozilla/dom/StyleSheetList.h" - -namespace mozilla { -namespace dom { - -StyleScope::~StyleScope() -{ -} - -StyleSheetList& -StyleScope::EnsureDOMStyleSheets() -{ - if (!mDOMStyleSheets) { - mDOMStyleSheets = new StyleSheetList(*this); - } - return *mDOMStyleSheets; -} - -} -} diff --git a/dom/base/StyleScope.h b/dom/base/StyleScope.h deleted file mode 100644 index c8957b069..000000000 --- a/dom/base/StyleScope.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- 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_StyleScope_h__ -#define mozilla_dom_StyleScope_h__ - -#include "nsTArray.h" - -class nsINode; - -namespace mozilla { -class StyleSheet; - -namespace dom { - -class StyleSheetList; - -/** - * A class meant to be shared by ShadowRoot and Document, that holds a list of - * stylesheets. - * - * TODO(emilio, bug 1418159): In the future this should hold most of the - * relevant style state, this should allow us to fix bug 548397. - */ -class StyleScope -{ -public: - virtual nsINode& AsNode() = 0; - - const nsINode& AsNode() const - { - return const_cast<StyleScope&>(*this).AsNode(); - } - - StyleSheet* SheetAt(size_t aIndex) const - { - return mStyleSheets.SafeElementAt(aIndex); - } - - size_t SheetCount() const - { - return mStyleSheets.Length(); - } - - int32_t IndexOfSheet(const StyleSheet& aSheet) const - { - return mStyleSheets.IndexOf(&aSheet); - } - - void InsertSheetAt(size_t aIndex, StyleSheet& aSheet) - { - mStyleSheets.InsertElementAt(aIndex, &aSheet); - } - - void RemoveSheet(StyleSheet& aSheet) - { - mStyleSheets.RemoveElement(&aSheet); - } - - void AppendStyleSheet(StyleSheet& aSheet) - { - mStyleSheets.AppendElement(&aSheet); - } - - StyleSheetList& EnsureDOMStyleSheets(); - - ~StyleScope(); - -protected: - nsTArray<RefPtr<mozilla::StyleSheet>> mStyleSheets; - RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets; -}; - -} - -} - -#endif diff --git a/dom/base/StyleSheetList.cpp b/dom/base/StyleSheetList.cpp index 42db3179d..2b41473ff 100644 --- a/dom/base/StyleSheetList.cpp +++ b/dom/base/StyleSheetList.cpp @@ -48,19 +48,19 @@ StyleSheetList::SlowItem(uint32_t aIndex, nsIDOMStyleSheet** aItem) void StyleSheetList::NodeWillBeDestroyed(const nsINode* aNode) { - mStyleScope = nullptr; + mDocumentOrShadowRoot = nullptr; } -StyleSheetList::StyleSheetList(StyleScope& aScope) - : mStyleScope(&aScope) +StyleSheetList::StyleSheetList(DocumentOrShadowRoot& aScope) + : mDocumentOrShadowRoot(&aScope) { - mStyleScope->AsNode().AddMutationObserver(this); + mDocumentOrShadowRoot->AsNode().AddMutationObserver(this); } StyleSheetList::~StyleSheetList() { - if (mStyleScope) { - mStyleScope->AsNode().RemoveMutationObserver(this); + if (mDocumentOrShadowRoot) { + mDocumentOrShadowRoot->AsNode().RemoveMutationObserver(this); } } diff --git a/dom/base/StyleSheetList.h b/dom/base/StyleSheetList.h index ea5c33a98..f8f371a81 100644 --- a/dom/base/StyleSheetList.h +++ b/dom/base/StyleSheetList.h @@ -7,7 +7,7 @@ #ifndef mozilla_dom_StyleSheetList_h #define mozilla_dom_StyleSheetList_h -#include "mozilla/dom/StyleScope.h" +#include "mozilla/dom/DocumentOrShadowRoot.h" #include "nsIDOMStyleSheetList.h" #include "nsWrapperCache.h" #include "nsStubDocumentObserver.h" @@ -31,28 +31,28 @@ public: NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED - explicit StyleSheetList(StyleScope& aScope); + explicit StyleSheetList(DocumentOrShadowRoot& aScope); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override final; nsINode* GetParentObject() const { - return mStyleScope ? &mStyleScope->AsNode() : nullptr; + return mDocumentOrShadowRoot ? &mDocumentOrShadowRoot->AsNode() : nullptr; } uint32_t Length() const { - return mStyleScope ? mStyleScope->SheetCount() : 0; + return mDocumentOrShadowRoot ? mDocumentOrShadowRoot->SheetCount() : 0; } StyleSheet* IndexedGetter(uint32_t aIndex, bool& aFound) const { - if (!mStyleScope) { + if (!mDocumentOrShadowRoot) { aFound = false; return nullptr; } - StyleSheet* sheet = mStyleScope->SheetAt(aIndex); + StyleSheet* sheet = mDocumentOrShadowRoot->SheetAt(aIndex); aFound = !!sheet; return sheet; } @@ -66,7 +66,7 @@ public: protected: virtual ~StyleSheetList(); - StyleScope* mStyleScope; // Weak, cleared on "NodeWillBeDestroyed". + DocumentOrShadowRoot* mDocumentOrShadowRoot; // Weak, cleared on "NodeWillBeDestroyed". }; } // namespace dom diff --git a/dom/base/moz.build b/dom/base/moz.build index ab0f0e0ab..cedaa0e49 100755 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -164,6 +164,7 @@ EXPORTS.mozilla.dom += [ 'DirectionalityUtils.h', 'DocGroup.h', 'DocumentFragment.h', + 'DocumentOrShadowRoot.h', 'DocumentType.h', 'DOMCursor.h', 'DOMError.h', @@ -214,7 +215,6 @@ EXPORTS.mozilla.dom += [ 'SimpleTreeIterator.h', 'StructuredCloneHolder.h', 'StructuredCloneTags.h', - 'StyleScope.h', 'StyleSheetList.h', 'SubtleCrypto.h', 'TabGroup.h', @@ -243,6 +243,7 @@ SOURCES += [ 'DirectionalityUtils.cpp', 'DocGroup.cpp', 'DocumentFragment.cpp', + 'DocumentOrShadowRoot.cpp', 'DocumentType.cpp', 'DOMCursor.cpp', 'DOMError.cpp', @@ -361,7 +362,6 @@ SOURCES += [ 'ScriptSettings.cpp', 'ShadowRoot.cpp', 'StructuredCloneHolder.cpp', - 'StyleScope.cpp', 'StyleSheetList.cpp', 'SubtleCrypto.cpp', 'TabGroup.cpp', diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index c0d81a41e..61d10e022 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3658,6 +3658,14 @@ nsContentUtils::ReportToConsole(uint32_t aErrorFlags, aLineNumber, aColumnNumber); } +/* static */ void +nsContentUtils::ReportEmptyGetElementByIdArg(const nsIDocument* aDoc) +{ + ReportToConsole(nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("DOM"), aDoc, + nsContentUtils::eDOM_PROPERTIES, + "EmptyGetElementByIdParam"); +} /* static */ nsresult nsContentUtils::ReportToConsoleNonLocalized(const nsAString& aErrorText, diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index b58b0e0e3..8cf105bb3 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -941,6 +941,8 @@ public: uint32_t aLineNumber = 0, uint32_t aColumnNumber = 0); + static void ReportEmptyGetElementByIdArg(const nsIDocument* aDoc); + static void LogMessageToConsole(const char* aMsg); /** diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 7b280a188..40bfa97e2 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -300,9 +300,24 @@ GetHttpChannelHelper(nsIChannel* aChannel, nsIHttpChannel** aHttpChannel) #define NAME_NOT_VALID ((nsSimpleContentList*)1) +nsIdentifierMapEntry::nsIdentifierMapEntry(const nsIdentifierMapEntry::AtomOrString& aKey) + : mKey(aKey) +{} + +nsIdentifierMapEntry::nsIdentifierMapEntry(const nsIdentifierMapEntry::AtomOrString* aKey) + : mKey(aKey ? *aKey : nullptr) +{} + nsIdentifierMapEntry::~nsIdentifierMapEntry() -{ -} +{} + +nsIdentifierMapEntry::nsIdentifierMapEntry(nsIdentifierMapEntry&& aOther) + : mKey(mozilla::Move(aOther.mKey)) + , mIdContentList(mozilla::Move(aOther.mIdContentList)) + , mNameContentList(mozilla::Move(aOther.mNameContentList)) + , mChangeCallbacks(mozilla::Move(aOther.mChangeCallbacks)) + , mImageElement(mozilla::Move(aOther.mImageElement)) +{} void nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback) @@ -326,6 +341,12 @@ nsIdentifierMapEntry::IsEmpty() !mChangeCallbacks && !mImageElement; } +bool +nsIdentifierMapEntry::HasNameElement() const +{ + return mNameContentList && mNameContentList->Length() != 0; +} + Element* nsIdentifierMapEntry::GetIdElement() { @@ -537,7 +558,7 @@ nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() size_t nsIdentifierMapEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { - return nsStringHashKey::SizeOfExcludingThis(aMallocSizeOf); + return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf); } // Helper structs for the content->subdoc map @@ -1226,6 +1247,7 @@ static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo; // ================================================================== nsIDocument::nsIDocument() : nsINode(nullNodeInfo), + DocumentOrShadowRoot(this), mReferrerPolicySet(false), mReferrerPolicy(mozilla::net::RP_Default), mBlockAllMixedContent(false), @@ -2704,8 +2726,7 @@ nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName) "Only put elements that need to be exposed as document['name'] in " "the named table."); - nsIdentifierMapEntry *entry = - mIdentifierMap.PutEntry(nsDependentAtomString(aName)); + nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName); // Null for out-of-memory if (entry) { @@ -2724,8 +2745,7 @@ nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName) if (mIdentifierMap.Count() == 0) return; - nsIdentifierMapEntry *entry = - mIdentifierMap.GetEntry(nsDependentAtomString(aName)); + nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName); if (!entry) // Could be false if the element was anonymous, hence never added return; @@ -2739,8 +2759,7 @@ nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName) void nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId) { - nsIdentifierMapEntry *entry = - mIdentifierMap.PutEntry(nsDependentAtomString(aId)); + nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId); if (entry) { /* True except on OOM */ if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) && @@ -2762,8 +2781,7 @@ nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId) return; } - nsIdentifierMapEntry *entry = - mIdentifierMap.GetEntry(nsDependentAtomString(aId)); + nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId); if (!entry) // Can be null for XML elements with changing ids. return; @@ -3013,20 +3031,9 @@ Element* nsIDocument::GetActiveElement() { // Get the focused element. - if (nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow()) { - nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; - nsIContent* focusedContent = - nsFocusManager::GetFocusedDescendant(window, false, - getter_AddRefs(focusedWindow)); - // be safe and make sure the element is from this document - if (focusedContent && focusedContent->OwnerDoc() == this) { - if (focusedContent->ChromeOnlyAccess()) { - focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent(); - } - if (focusedContent) { - return focusedContent->AsElement(); - } - } + Element* focusedElement = GetRetargetedFocusedElement(); + if (focusedElement) { + return focusedElement; } // No focused element anywhere in this document. Try to get the BODY. @@ -3238,12 +3245,6 @@ nsDocument::GetElementsByClassName(const nsAString& aClasses, return NS_OK; } -already_AddRefed<nsContentList> -nsIDocument::GetElementsByClassName(const nsAString& aClasses) -{ - return nsContentUtils::GetElementsByClassName(this, aClasses); -} - NS_IMETHODIMP nsDocument::ReleaseCapture() { @@ -4733,32 +4734,7 @@ nsDocument::BeginLoad() void nsDocument::ReportEmptyGetElementByIdArg() { - nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, - NS_LITERAL_CSTRING("DOM"), this, - nsContentUtils::eDOM_PROPERTIES, - "EmptyGetElementByIdParam"); -} - -Element* -nsDocument::GetElementById(const nsAString& aElementId) -{ - if (!CheckGetElementByIdArg(aElementId)) { - return nullptr; - } - - nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId); - return entry ? entry->GetIdElement() : nullptr; -} - -const nsTArray<Element*>* -nsDocument::GetAllElementsForId(const nsAString& aElementId) const -{ - if (aElementId.IsEmpty()) { - return nullptr; - } - - nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId); - return entry ? &entry->GetIdElements() : nullptr; + nsContentUtils::ReportEmptyGetElementByIdArg(this); } NS_IMETHODIMP @@ -4783,7 +4759,7 @@ nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver, if (!CheckGetElementByIdArg(id)) return nullptr; - nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id); + nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aID); NS_ENSURE_TRUE(entry, nullptr); entry->AddContentChangeCallback(aObserver, aData, aForImage); @@ -4799,7 +4775,7 @@ nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver, if (!CheckGetElementByIdArg(id)) return; - nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id); + nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aID); if (!entry) { return; } @@ -5650,27 +5626,6 @@ nsDocument::BlockedTrackingNodes() const return list.forget(); } -already_AddRefed<nsContentList> -nsIDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI, - const nsAString& aLocalName, - ErrorResult& aResult) -{ - int32_t nameSpaceId = kNameSpaceID_Wildcard; - - if (!aNamespaceURI.EqualsLiteral("*")) { - aResult = - nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, - nameSpaceId); - if (aResult.Failed()) { - return nullptr; - } - } - - NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!"); - - return NS_GetContentList(this, nameSpaceId, aLocalName); -} - NS_IMETHODIMP nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI, const nsAString& aLocalName, @@ -5678,7 +5633,7 @@ nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI, { ErrorResult rv; RefPtr<nsContentList> list = - nsIDocument::GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv); + GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv); if (rv.Failed()) { return rv.StealNSResult(); } diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 6baf40270..9d7b0aafd 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -333,7 +333,6 @@ class nsDocument : public nsIDocument, public: typedef mozilla::dom::Element Element; - using nsIDocument::GetElementsByTagName; typedef mozilla::net::ReferrerPolicy ReferrerPolicy; NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -631,6 +630,11 @@ public: // nsIDOMDocumentXBL NS_DECL_NSIDOMDOCUMENTXBL + using mozilla::dom::DocumentOrShadowRoot::GetElementById; + using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagName; + using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagNameNS; + using mozilla::dom::DocumentOrShadowRoot::GetElementsByClassName; + // nsIDOMEventTarget virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; @@ -819,10 +823,7 @@ public: virtual void ResetScrolledToRefAlready() override; virtual void SetChangeScrollPosWhenScrollingToRef(bool aValue) override; - virtual Element *GetElementById(const nsAString& aElementId) override; - virtual const nsTArray<Element*>* GetAllElementsForId(const nsAString& aElementId) const override; - - virtual Element *LookupImageElement(const nsAString& aElementId) override; + virtual Element* LookupImageElement(const nsAString& aElementId) override; virtual void MozSetImageElement(const nsAString& aImageElementId, Element* aElement) override; @@ -1206,14 +1207,6 @@ public: RefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList; RefPtr<nsScriptLoader> mScriptLoader; nsDocHeaderData* mHeaderData; - /* mIdentifierMap works as follows for IDs: - * 1) Attribute changes affect the table immediately (removing and adding - * entries as needed). - * 2) Removals from the DOM affect the table immediately - * 3) Additions to the DOM always update existing entries for names, and add - * new ones for IDs. - */ - nsTHashtable<nsIdentifierMapEntry> mIdentifierMap; nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups; diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index c14087c8a..3fc9546e8 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -813,10 +813,11 @@ nsFocusManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent) if (!window) return NS_OK; - // if the content is currently focused in the window, or is an ancestor - // of the currently focused element, reset the focus within that window. + // if the content is currently focused in the window, or is an + // shadow-including inclusive ancestor of the currently focused element, + // reset the focus within that window. nsIContent* content = window->GetFocusedNode(); - if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) { + if (content && nsContentUtils::ContentIsHostIncludingDescendantOf(content, aContent)) { bool shouldShowFocusRing = window->ShouldShowFocusRing(); window->SetFocusedNode(nullptr); diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index af3077604..f4d2974cd 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -909,7 +909,9 @@ GK_ATOM(onreadystatechange, "onreadystatechange") GK_ATOM(onreceived, "onreceived") GK_ATOM(onremoteheld, "onremoteheld") GK_ATOM(onremoteresumed, "onremoteresumed") +GK_ATOM(onrequestprogress, "onrequestprogress") GK_ATOM(onresourcetimingbufferfull, "onresourcetimingbufferfull") +GK_ATOM(onresponseprogress, "onresponseprogress") GK_ATOM(onretrieving, "onretrieving") GK_ATOM(onRequest, "onRequest") GK_ATOM(onrequestmediaplaystatus, "onrequestmediaplaystatus") diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index ce0a4e596..101d5e931 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -697,6 +697,14 @@ public: virtual mozilla::dom::ShadowRoot *GetContainingShadow() const = 0; /** + * Gets the shadow host if this content is in a shadow tree. That is, the host + * of |GetContainingShadow|, if its not null. + * + * @return The shadow host, if this is in shadow tree, or null. + */ + nsIContent* GetContainingShadowHost() const; + + /** * Gets the assigned slot associated with this content. * * @return The assigned slot element or null. diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index b4fda21c1..d88db8423 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -34,7 +34,7 @@ #include "prclist.h" #include "mozilla/UniquePtr.h" #include "mozilla/CORSMode.h" -#include "mozilla/dom/StyleScope.h" +#include "mozilla/dom/DocumentOrShadowRoot.h" #include "mozilla/LinkedList.h" #include "mozilla/StyleBackendType.h" #include "mozilla/StyleSheet.h" @@ -198,7 +198,7 @@ class nsContentList; // Document interface. This is implemented by all document objects in // Gecko. class nsIDocument : public nsINode, - public mozilla::dom::StyleScope + public mozilla::dom::DocumentOrShadowRoot { typedef mozilla::dom::GlobalObject GlobalObject; @@ -499,7 +499,7 @@ public: * to remove it. */ typedef bool (* IDTargetObserver)(Element* aOldElement, - Element* aNewelement, void* aData); + Element* aNewelement, void* aData); /** * Add an IDTargetObserver for a specific ID. The IDTargetObserver @@ -1071,14 +1071,9 @@ public: */ virtual void EnsureOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet) = 0; - nsINode& AsNode() final - { - return *this; - } - mozilla::dom::StyleSheetList* StyleSheets() { - return &StyleScope::EnsureDOMStyleSheets(); + return &DocumentOrShadowRoot::EnsureDOMStyleSheets(); } /** @@ -2362,19 +2357,10 @@ public: virtual void ResetScrolledToRefAlready() = 0; virtual void SetChangeScrollPosWhenScrollingToRef(bool aValue) = 0; - /** - * This method is similar to GetElementById() from nsIDOMDocument but it - * returns a mozilla::dom::Element instead of a nsIDOMElement. - * It prevents converting nsIDOMElement to mozilla::dom::Element which is - * already converted from mozilla::dom::Element. - */ - virtual Element* GetElementById(const nsAString& aElementId) = 0; - - /** - * This method returns _all_ the elements in this document which - * have id aElementId, if there are any. Otherwise it returns null. - */ - virtual const nsTArray<Element*>* GetAllElementsForId(const nsAString& aElementId) const = 0; + using mozilla::dom::DocumentOrShadowRoot::GetElementById; + using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagName; + using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagNameNS; + using mozilla::dom::DocumentOrShadowRoot::GetElementsByClassName; /** * Lookup an image element using its associated ID, which is usually provided @@ -2574,18 +2560,6 @@ public: nsIDocument* GetTopLevelContentDocument(); - already_AddRefed<nsContentList> - GetElementsByTagName(const nsAString& aTagName) - { - return NS_GetContentList(this, kNameSpaceID_Unknown, aTagName); - } - already_AddRefed<nsContentList> - GetElementsByTagNameNS(const nsAString& aNamespaceURI, - const nsAString& aLocalName, - mozilla::ErrorResult& aResult); - already_AddRefed<nsContentList> - GetElementsByClassName(const nsAString& aClasses); - // GetElementById defined above virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName, const mozilla::dom::ElementCreationOptionsOrString& aOptions, diff --git a/dom/base/nsIdentifierMapEntry.h b/dom/base/nsIdentifierMapEntry.h index fce506cef..119a7e453 100644 --- a/dom/base/nsIdentifierMapEntry.h +++ b/dom/base/nsIdentifierMapEntry.h @@ -15,18 +15,24 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/Move.h" -#include "mozilla/dom/Element.h" #include "mozilla/net/ReferrerPolicy.h" #include "nsCOMArray.h" #include "nsCOMPtr.h" -#include "nsContentList.h" #include "nsIAtom.h" -#include "nsIDocument.h" #include "nsTArray.h" #include "nsTHashtable.h" +#include "nsHashKeys.h" class nsIContent; +class nsContentList; +class nsBaseContentList; + +namespace mozilla { +namespace dom { + class Element; +} // namespace dom +} // namespace mozilla /** * Right now our identifier map entries contain information for 'name' @@ -40,26 +46,84 @@ class nsIContent; * Perhaps the document.all results should have their own hashtable * in nsHTMLDocument. */ -class nsIdentifierMapEntry : public nsStringHashKey +class nsIdentifierMapEntry : public PLDHashEntryHdr { -public: typedef mozilla::dom::Element Element; typedef mozilla::net::ReferrerPolicy ReferrerPolicy; - explicit nsIdentifierMapEntry(const nsAString& aKey) : - nsStringHashKey(&aKey), mNameContentList(nullptr) + /** + * @see nsIDocument::IDTargetObserver, this is just here to avoid include + * hell. + */ + typedef bool (* IDTargetObserver)(Element* aOldElement, + Element* aNewelement, void* aData); + +public: + struct AtomOrString { + MOZ_IMPLICIT AtomOrString(nsIAtom* aAtom) : mAtom(aAtom) {} + MOZ_IMPLICIT AtomOrString(const nsAString& aString) : mString(aString) {} + AtomOrString(const AtomOrString& aOther) + : mAtom(aOther.mAtom) + , mString(aOther.mString) + { + } + + AtomOrString(AtomOrString&& aOther) + : mAtom(aOther.mAtom.forget()) + , mString(aOther.mString) + { + } + + nsCOMPtr<nsIAtom> mAtom; + const nsString mString; + }; + + typedef const AtomOrString& KeyType; + typedef const AtomOrString* KeyTypePointer; + + explicit nsIdentifierMapEntry(const AtomOrString& aKey); + explicit nsIdentifierMapEntry(const AtomOrString* aKey); + nsIdentifierMapEntry(nsIdentifierMapEntry&& aOther); + ~nsIdentifierMapEntry(); + + KeyType GetKey() const { return mKey; } + + nsString GetKeyAsString() const + { + if (mKey.mAtom) { + return nsAtomString(mKey.mAtom); + } + + return mKey.mString; } - explicit nsIdentifierMapEntry(const nsAString* aKey) : - nsStringHashKey(aKey), mNameContentList(nullptr) + + bool KeyEquals(const KeyTypePointer aOtherKey) const { + if (mKey.mAtom) { + if (aOtherKey->mAtom) { + return mKey.mAtom == aOtherKey->mAtom; + } + + return mKey.mAtom->Equals(aOtherKey->mString); + } + + if (aOtherKey->mAtom) { + return aOtherKey->mAtom->Equals(mKey.mString); + } + + return mKey.mString.Equals(aOtherKey->mString); } - nsIdentifierMapEntry(const nsIdentifierMapEntry& aOther) : - nsStringHashKey(&aOther.GetKey()) + + static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } + + static PLDHashNumber HashKey(const KeyTypePointer aKey) { - NS_ERROR("Should never be called"); + return aKey->mAtom ? + aKey->mAtom->hash() : mozilla::HashString(aKey->mString); } - ~nsIdentifierMapEntry(); + + enum { ALLOW_MEMMOVE = false }; void AddNameElement(nsINode* aDocument, Element* aElement); void RemoveNameElement(Element* aElement); @@ -67,9 +131,7 @@ public: nsBaseContentList* GetNameContentList() { return mNameContentList; } - bool HasNameElement() const { - return mNameContentList && mNameContentList->Length() != 0; - } + bool HasNameElement() const; /** * Returns the element if we know the element associated with this @@ -109,9 +171,9 @@ public: bool HasIdElementExposedAsHTMLDocumentProperty(); bool HasContentChangeCallback() { return mChangeCallbacks != nullptr; } - void AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback, + void AddContentChangeCallback(IDTargetObserver aCallback, void* aData, bool aForImage); - void RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback, + void RemoveContentChangeCallback(IDTargetObserver aCallback, void* aData, bool aForImage); /** @@ -122,7 +184,7 @@ public: void Traverse(nsCycleCollectionTraversalCallback* aCallback); struct ChangeCallback { - nsIDocument::IDTargetObserver mCallback; + IDTargetObserver mCallback; void* mData; bool mForImage; }; @@ -156,12 +218,16 @@ public: size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; private: + nsIdentifierMapEntry(const nsIdentifierMapEntry& aOther) = delete; + nsIdentifierMapEntry& operator=(const nsIdentifierMapEntry& aOther) = delete; + void FireChangeCallbacks(Element* aOldElement, Element* aNewElement, bool aImageOnly = false); + AtomOrString mKey; // empty if there are no elements with this ID. // The elements are stored as weak pointers. - nsTArray<Element*> mIdContentList; + AutoTArray<Element*, 1> mIdContentList; RefPtr<nsBaseContentList> mNameContentList; nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks; RefPtr<Element> mImageElement; diff --git a/dom/base/nsNameSpaceManager.h b/dom/base/nsNameSpaceManager.h index d5c3a25fe..e6dc67a3b 100644 --- a/dom/base/nsNameSpaceManager.h +++ b/dom/base/nsNameSpaceManager.h @@ -10,13 +10,13 @@ #include "nsDataHashtable.h" #include "nsHashKeys.h" #include "nsIAtom.h" -#include "nsIDocument.h" #include "nsIObserver.h" #include "nsTArray.h" #include "mozilla/StaticPtr.h" class nsAString; +class nsIDocument; /** * The Name Space Manager tracks the association between a NameSpace diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index f944352e3..191f4cfc3 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -39,6 +39,7 @@ #include "mozilla/dom/URLSearchParams.h" #include "mozilla/dom/workers/ServiceWorkerManager.h" +#include "FetchObserver.h" #include "InternalRequest.h" #include "InternalResponse.h" @@ -52,38 +53,141 @@ namespace dom { using namespace workers; +// This class helps the proxying of AbortSignal changes cross threads. +class AbortSignalProxy final : public AbortSignal::Follower +{ + // This is created and released on the main-thread. + RefPtr<AbortSignal> mSignalMainThread; + + // This value is used only for the creation of AbortSignal on the + // main-thread. They are not updated. + const bool mAborted; + + // This runnable propagates changes from the AbortSignal on workers to the + // AbortSignal on main-thread. + class AbortSignalProxyRunnable final : public Runnable + { + RefPtr<AbortSignalProxy> mProxy; + + public: + explicit AbortSignalProxyRunnable(AbortSignalProxy* aProxy) + : mProxy(aProxy) + {} + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + AbortSignal* signal = mProxy->GetOrCreateSignalForMainThread(); + signal->Abort(); + return NS_OK; + } + }; + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbortSignalProxy) + + explicit AbortSignalProxy(AbortSignal* aSignal) + : mAborted(aSignal->Aborted()) + { + Follow(aSignal); + } + + void + Aborted() override + { + RefPtr<AbortSignalProxyRunnable> runnable = + new AbortSignalProxyRunnable(this); + NS_DispatchToMainThread(runnable); + } + + AbortSignal* + GetOrCreateSignalForMainThread() + { + MOZ_ASSERT(NS_IsMainThread()); + if (!mSignalMainThread) { + mSignalMainThread = new AbortSignal(mAborted); + } + return mSignalMainThread; + } + + void + Shutdown() + { + Unfollow(); + } + +private: + ~AbortSignalProxy() + { + NS_ReleaseOnMainThread(mSignalMainThread.forget()); + } +}; + class WorkerFetchResolver final : public FetchDriverObserver { friend class MainThreadFetchRunnable; + friend class WorkerDataAvailableRunnable; + friend class WorkerFetchResponseEndBase; friend class WorkerFetchResponseEndRunnable; friend class WorkerFetchResponseRunnable; RefPtr<PromiseWorkerProxy> mPromiseProxy; + RefPtr<AbortSignalProxy> mSignalProxy; + RefPtr<FetchObserver> mFetchObserver; + public: // Returns null if worker is shutting down. static already_AddRefed<WorkerFetchResolver> - Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise) + Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise, + AbortSignal* aSignal, FetchObserver* aObserver) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(aWorkerPrivate, aPromise); + RefPtr<PromiseWorkerProxy> proxy = + PromiseWorkerProxy::Create(aWorkerPrivate, aPromise); if (!proxy) { return nullptr; } - RefPtr<WorkerFetchResolver> r = new WorkerFetchResolver(proxy); + RefPtr<AbortSignalProxy> signalProxy; + if (aSignal) { + signalProxy = new AbortSignalProxy(aSignal); + } + + RefPtr<WorkerFetchResolver> r = + new WorkerFetchResolver(proxy, signalProxy, aObserver); return r.forget(); } + AbortSignal* + GetAbortSignal() + { + MOZ_ASSERT(NS_IsMainThread()); + + if (!mSignalProxy) { + return nullptr; + } + + return mSignalProxy->GetOrCreateSignalForMainThread(); + } + void OnResponseAvailableInternal(InternalResponse* aResponse) override; void - OnResponseEnd() override; + OnResponseEnd(FetchDriverObserver::EndReason eReason) override; + + void + OnDataAvailable() override; private: - explicit WorkerFetchResolver(PromiseWorkerProxy* aProxy) + WorkerFetchResolver(PromiseWorkerProxy* aProxy, + AbortSignalProxy* aSignalProxy, + FetchObserver* aObserver) : mPromiseProxy(aProxy) + , mSignalProxy(aSignalProxy) + , mFetchObserver(aObserver) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(mPromiseProxy); @@ -100,12 +204,16 @@ class MainThreadFetchResolver final : public FetchDriverObserver { RefPtr<Promise> mPromise; RefPtr<Response> mResponse; + RefPtr<FetchObserver> mFetchObserver; nsCOMPtr<nsIDocument> mDocument; NS_DECL_OWNINGTHREAD public: - explicit MainThreadFetchResolver(Promise* aPromise); + MainThreadFetchResolver(Promise* aPromise, FetchObserver* aObserver) + : mPromise(aPromise) + , mFetchObserver(aObserver) + {} void OnResponseAvailableInternal(InternalResponse* aResponse) override; @@ -115,11 +223,20 @@ public: mDocument = aDocument; } - virtual void OnResponseEnd() override + void OnResponseEnd(FetchDriverObserver::EndReason aReason) override { + if (aReason == eAborted) { + mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + } + + mFetchObserver = nullptr; + FlushConsoleReport(); } + void + OnDataAvailable() override; + private: ~MainThreadFetchResolver(); @@ -170,9 +287,11 @@ public: fetch->SetWorkerScript(spec); } + RefPtr<AbortSignal> signal = mResolver->GetAbortSignal(); + // ...but release it before calling Fetch, because mResolver's callback can // be called synchronously and they want the mutex, too. - return fetch->Fetch(mResolver); + return fetch->Fetch(signal, mResolver); } }; @@ -210,6 +329,23 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, RefPtr<InternalRequest> r = request->GetInternalRequest(); + RefPtr<AbortSignal> signal; + if (aInit.mSignal.WasPassed()) { + signal = &aInit.mSignal.Value(); + } + + if (signal && signal->Aborted()) { + // An already aborted signal should reject immediately. + aRv.Throw(NS_ERROR_DOM_ABORT_ERR); + return nullptr; + } + + RefPtr<FetchObserver> observer; + if (aInit.mObserve.WasPassed()) { + observer = new FetchObserver(aGlobal, signal); + aInit.mObserve.Value().HandleEvent(*observer); + } + if (NS_IsMainThread()) { nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal); nsCOMPtr<nsIDocument> doc; @@ -236,11 +372,12 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, } } - RefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(p); + RefPtr<MainThreadFetchResolver> resolver = + new MainThreadFetchResolver(p, observer); RefPtr<FetchDriver> fetch = new FetchDriver(r, principal, loadGroup); fetch->SetDocument(doc); resolver->SetDocument(doc); - aRv = fetch->Fetch(resolver); + aRv = fetch->Fetch(signal, resolver); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -252,7 +389,8 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, r->SetSkipServiceWorker(); } - RefPtr<WorkerFetchResolver> resolver = WorkerFetchResolver::Create(worker, p); + RefPtr<WorkerFetchResolver> resolver = + WorkerFetchResolver::Create(worker, p, signal, observer); if (!resolver) { NS_WARNING("Could not add WorkerFetchResolver workerHolder to worker"); aRv.Throw(NS_ERROR_DOM_ABORT_ERR); @@ -266,11 +404,6 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, return p.forget(); } -MainThreadFetchResolver::MainThreadFetchResolver(Promise* aPromise) - : mPromise(aPromise) -{ -} - void MainThreadFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse) { @@ -278,16 +411,39 @@ MainThreadFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse AssertIsOnMainThread(); if (aResponse->Type() != ResponseType::Error) { + if (mFetchObserver) { + mFetchObserver->SetState(FetchState::Complete); + } + nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject(); mResponse = new Response(go, aResponse); mPromise->MaybeResolve(mResponse); } else { + if (mFetchObserver) { + mFetchObserver->SetState(FetchState::Errored); + } + ErrorResult result; result.ThrowTypeError<MSG_FETCH_FAILED>(); mPromise->MaybeReject(result); } } +void +MainThreadFetchResolver::OnDataAvailable() +{ + NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); + AssertIsOnMainThread(); + + if (!mFetchObserver) { + return; + } + + if (mFetchObserver->State() == FetchState::Requesting) { + mFetchObserver->SetState(FetchState::Responding); + } +} + MainThreadFetchResolver::~MainThreadFetchResolver() { NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); @@ -306,6 +462,7 @@ public: , mResolver(aResolver) , mInternalResponse(aResponse) { + MOZ_ASSERT(mResolver); } bool @@ -317,10 +474,18 @@ public: RefPtr<Promise> promise = mResolver->mPromiseProxy->WorkerPromise(); if (mInternalResponse->Type() != ResponseType::Error) { + if (mResolver->mFetchObserver) { + mResolver->mFetchObserver->SetState(FetchState::Complete); + } + RefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope(); RefPtr<Response> response = new Response(global, mInternalResponse); promise->MaybeResolve(response); } else { + if (mResolver->mFetchObserver) { + mResolver->mFetchObserver->SetState(FetchState::Errored); + } + ErrorResult result; result.ThrowTypeError<MSG_FETCH_FAILED>(); promise->MaybeReject(result); @@ -329,14 +494,42 @@ public: } }; +class WorkerDataAvailableRunnable final : public MainThreadWorkerRunnable +{ + RefPtr<WorkerFetchResolver> mResolver; +public: + WorkerDataAvailableRunnable(WorkerPrivate* aWorkerPrivate, + WorkerFetchResolver* aResolver) + : MainThreadWorkerRunnable(aWorkerPrivate) + , mResolver(aResolver) + { + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + if (mResolver->mFetchObserver && + mResolver->mFetchObserver->State() == FetchState::Requesting) { + mResolver->mFetchObserver->SetState(FetchState::Responding); + } + + return true; + } +}; + class WorkerFetchResponseEndBase { - RefPtr<PromiseWorkerProxy> mPromiseProxy; +protected: + RefPtr<WorkerFetchResolver> mResolver; + public: - explicit WorkerFetchResponseEndBase(PromiseWorkerProxy* aPromiseProxy) - : mPromiseProxy(aPromiseProxy) + explicit WorkerFetchResponseEndBase(WorkerFetchResolver* aResolver) + : mResolver(aResolver) { - MOZ_ASSERT(mPromiseProxy); + MOZ_ASSERT(aResolver); } void @@ -344,23 +537,41 @@ public: { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - mPromiseProxy->CleanUp(); + + mResolver->mPromiseProxy->CleanUp(); + + mResolver->mFetchObserver = nullptr; + + if (mResolver->mSignalProxy) { + mResolver->mSignalProxy->Shutdown(); + mResolver->mSignalProxy = nullptr; + } } }; class WorkerFetchResponseEndRunnable final : public MainThreadWorkerRunnable , public WorkerFetchResponseEndBase { + FetchDriverObserver::EndReason mReason; + public: - explicit WorkerFetchResponseEndRunnable(PromiseWorkerProxy* aPromiseProxy) - : MainThreadWorkerRunnable(aPromiseProxy->GetWorkerPrivate()) - , WorkerFetchResponseEndBase(aPromiseProxy) + WorkerFetchResponseEndRunnable(WorkerPrivate* aWorkerPrivate, + WorkerFetchResolver* aResolver, + FetchDriverObserver::EndReason aReason) + : MainThreadWorkerRunnable(aWorkerPrivate) + , WorkerFetchResponseEndBase(aResolver) + , mReason(aReason) { } bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { + if (mReason == FetchDriverObserver::eAborted) { + RefPtr<Promise> promise = mResolver->mPromiseProxy->WorkerPromise(); + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + } + WorkerRunInternal(aWorkerPrivate); return true; } @@ -379,9 +590,10 @@ class WorkerFetchResponseEndControlRunnable final : public MainThreadWorkerContr , public WorkerFetchResponseEndBase { public: - explicit WorkerFetchResponseEndControlRunnable(PromiseWorkerProxy* aPromiseProxy) - : MainThreadWorkerControlRunnable(aPromiseProxy->GetWorkerPrivate()) - , WorkerFetchResponseEndBase(aPromiseProxy) + WorkerFetchResponseEndControlRunnable(WorkerPrivate* aWorkerPrivate, + WorkerFetchResolver* aResolver) + : MainThreadWorkerControlRunnable(aWorkerPrivate) + , WorkerFetchResponseEndBase(aResolver) { } @@ -415,7 +627,22 @@ WorkerFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse) } void -WorkerFetchResolver::OnResponseEnd() +WorkerFetchResolver::OnDataAvailable() +{ + AssertIsOnMainThread(); + + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { + return; + } + + RefPtr<WorkerDataAvailableRunnable> r = + new WorkerDataAvailableRunnable(mPromiseProxy->GetWorkerPrivate(), this); + Unused << r->Dispatch(); +} + +void +WorkerFetchResolver::OnResponseEnd(FetchDriverObserver::EndReason aReason) { AssertIsOnMainThread(); MutexAutoLock lock(mPromiseProxy->Lock()); @@ -426,11 +653,13 @@ WorkerFetchResolver::OnResponseEnd() FlushConsoleReport(); RefPtr<WorkerFetchResponseEndRunnable> r = - new WorkerFetchResponseEndRunnable(mPromiseProxy); + new WorkerFetchResponseEndRunnable(mPromiseProxy->GetWorkerPrivate(), + this, aReason); if (!r->Dispatch()) { RefPtr<WorkerFetchResponseEndControlRunnable> cr = - new WorkerFetchResponseEndControlRunnable(mPromiseProxy); + new WorkerFetchResponseEndControlRunnable(mPromiseProxy->GetWorkerPrivate(), + this); // This can fail if the worker thread is canceled or killed causing // the PromiseWorkerProxy to give up its WorkerHolder immediately, // allowing the worker thread to become Dead. diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index 6294b0dc5..067e32db4 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -67,7 +67,7 @@ FetchDriver::~FetchDriver() } nsresult -FetchDriver::Fetch(FetchDriverObserver* aObserver) +FetchDriver::Fetch(AbortSignal* aSignal, FetchDriverObserver* aObserver) { workers::AssertIsOnMainThread(); #ifdef DEBUG @@ -90,6 +90,18 @@ FetchDriver::Fetch(FetchDriverObserver* aObserver) } mRequest->SetPrincipalInfo(Move(principalInfo)); + + // If the signal is aborted, it's time to inform the observer and terminate + // the operation. + if (aSignal) { + if (aSignal->Aborted()) { + Aborted(); + return NS_OK; + } + + Follow(aSignal); + } + if (NS_FAILED(HttpFetch())) { FailWithNetworkError(); } @@ -114,11 +126,7 @@ FetchDriver::HttpFetch() nsAutoCString url; mRequest->GetURL(url); nsCOMPtr<nsIURI> uri; - rv = NS_NewURI(getter_AddRefs(uri), - url, - nullptr, - nullptr, - ios); + rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr, ios); NS_ENSURE_SUCCESS(rv, rv); // Unsafe requests aren't allowed with when using no-core mode. @@ -380,6 +388,8 @@ FetchDriver::HttpFetch() NS_ENSURE_SUCCESS(rv, rv); // Step 4 onwards of "HTTP Fetch" is handled internally by Necko. + + mChannel = chan; return NS_OK; } already_AddRefed<InternalResponse> @@ -433,9 +443,11 @@ FetchDriver::FailWithNetworkError() #ifdef DEBUG mResponseAvailableCalled = true; #endif - mObserver->OnResponseEnd(); + mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking); mObserver = nullptr; } + + mChannel = nullptr; } namespace { @@ -655,6 +667,31 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest, return NS_OK; } +namespace { + +// Runnable to call the observer OnDataAvailable on the main-thread. +class DataAvailableRunnable final : public Runnable +{ + RefPtr<FetchDriverObserver> mObserver; + +public: + explicit DataAvailableRunnable(FetchDriverObserver* aObserver) + : mObserver(aObserver) + { + MOZ_ASSERT(aObserver); + } + + NS_IMETHOD + Run() override + { + mObserver->OnDataAvailable(); + mObserver = nullptr; + return NS_OK; + } +}; + +} // anonymous namespace + NS_IMETHODIMP FetchDriver::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, @@ -666,6 +703,18 @@ FetchDriver::OnDataAvailable(nsIRequest* aRequest, // called between OnStartRequest and OnStopRequest, so we don't need to worry // about races. + if (mObserver) { + if (NS_IsMainThread()) { + mObserver->OnDataAvailable(); + } else { + RefPtr<Runnable> runnable = new DataAvailableRunnable(mObserver); + nsresult rv = NS_DispatchToMainThread(runnable); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + } + uint32_t aRead; MOZ_ASSERT(mResponse); MOZ_ASSERT(mPipeOutputStream); @@ -777,10 +826,11 @@ FetchDriver::OnStopRequest(nsIRequest* aRequest, #endif } - mObserver->OnResponseEnd(); + mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking); mObserver = nullptr; } + mChannel = nullptr; return NS_OK; } @@ -921,5 +971,21 @@ FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel) const } } +void FetchDriver::Aborted() +{ + if (mObserver) { +#ifdef DEBUG + mResponseAvailableCalled = true; +#endif + mObserver->OnResponseEnd(FetchDriverObserver::eAborted); + mObserver = nullptr; + } + + if (mChannel) { + mChannel->Cancel(NS_BINDING_ABORTED); + mChannel = nullptr; + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/fetch/FetchDriver.h b/dom/fetch/FetchDriver.h index f74298a48..57dffa5a7 100644 --- a/dom/fetch/FetchDriver.h +++ b/dom/fetch/FetchDriver.h @@ -12,6 +12,7 @@ #include "nsIStreamListener.h" #include "nsIThreadRetargetableStreamListener.h" #include "mozilla/ConsoleReportCollector.h" +#include "mozilla/dom/AbortSignal.h" #include "mozilla/dom/SRIMetadata.h" #include "mozilla/RefPtr.h" @@ -49,7 +50,14 @@ public: mGotResponseAvailable = true; OnResponseAvailableInternal(aResponse); } - virtual void OnResponseEnd() + + enum EndReason + { + eAborted, + eByNetworking, + }; + + virtual void OnResponseEnd(EndReason aReason) { }; nsIConsoleReportCollector* GetReporter() const @@ -58,6 +66,9 @@ public: } virtual void FlushConsoleReport() = 0; + + virtual void OnDataAvailable() = 0; + protected: virtual ~FetchDriverObserver() { }; @@ -72,7 +83,8 @@ private: class FetchDriver final : public nsIStreamListener, public nsIChannelEventSink, public nsIInterfaceRequestor, - public nsIThreadRetargetableStreamListener + public nsIThreadRetargetableStreamListener, + public AbortSignal::Follower { public: NS_DECL_ISUPPORTS @@ -82,9 +94,12 @@ public: NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER - explicit FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal, - nsILoadGroup* aLoadGroup); - NS_IMETHOD Fetch(FetchDriverObserver* aObserver); + FetchDriver(InternalRequest* aRequest, + nsIPrincipal* aPrincipal, + nsILoadGroup* aLoadGroup); + + nsresult Fetch(AbortSignal* aSignal, + FetchDriverObserver* aObserver); void SetDocument(nsIDocument* aDocument); @@ -96,6 +111,11 @@ public: mWorkerScript = aWorkerScirpt; } + // AbortSignal::Follower + + void + Aborted() override; + private: nsCOMPtr<nsIPrincipal> mPrincipal; nsCOMPtr<nsILoadGroup> mLoadGroup; @@ -104,6 +124,7 @@ private: nsCOMPtr<nsIOutputStream> mPipeOutputStream; RefPtr<FetchDriverObserver> mObserver; nsCOMPtr<nsIDocument> mDocument; + nsCOMPtr<nsIChannel> mChannel; nsAutoPtr<SRICheckDataVerifier> mSRIDataVerifier; SRIMetadata mSRIMetadata; nsCString mWorkerScript; diff --git a/dom/fetch/FetchObserver.cpp b/dom/fetch/FetchObserver.cpp new file mode 100644 index 000000000..93d93773f --- /dev/null +++ b/dom/fetch/FetchObserver.cpp @@ -0,0 +1,117 @@ +/* -*- 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 "FetchObserver.h" +#include "WorkerPrivate.h" +#include "mozilla/dom/Event.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_CLASS(FetchObserver) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FetchObserver, + DOMEventTargetHelper) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FetchObserver, + DOMEventTargetHelper) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FetchObserver) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(FetchObserver, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(FetchObserver, DOMEventTargetHelper) + +/* static */ bool +FetchObserver::IsEnabled(JSContext* aCx, JSObject* aGlobal) +{ + if (NS_IsMainThread()) { + return Preferences::GetBool("dom.fetchObserver.enabled", false); + } + + using namespace workers; + + // Otherwise, check the pref via the WorkerPrivate + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); + if (!workerPrivate) { + return false; + } + + return workerPrivate->FetchObserverEnabled(); +} + +FetchObserver::FetchObserver(nsIGlobalObject* aGlobal, + AbortSignal* aSignal) + : DOMEventTargetHelper(aGlobal) + , mState(FetchState::Requesting) +{ + if (aSignal) { + Follow(aSignal); + } +} + +JSObject* +FetchObserver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return FetchObserverBinding::Wrap(aCx, this, aGivenProto); +} + +FetchState +FetchObserver::State() const +{ + return mState; +} + +void +FetchObserver::Aborted() +{ + SetState(FetchState::Aborted); +} + +void +FetchObserver::SetState(FetchState aState) +{ + MOZ_ASSERT(mState < aState); + + if (mState == FetchState::Aborted || + mState == FetchState::Errored || + mState == FetchState::Complete) { + // We are already in a final state. + return; + } + + // We cannot pass from Requesting to Complete directly. + if (mState == FetchState::Requesting && + aState == FetchState::Complete) { + SetState(FetchState::Responding); + } + + mState = aState; + + if (mState == FetchState::Aborted || + mState == FetchState::Errored || + mState == FetchState::Complete) { + Unfollow(); + } + + EventInit init; + init.mBubbles = false; + init.mCancelable = false; + + // TODO which kind of event should we dispatch here? + + RefPtr<Event> event = + Event::Constructor(this, NS_LITERAL_STRING("statechange"), init); + event->SetTrusted(true); + + bool dummy; + DispatchEvent(event, &dummy); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/fetch/FetchObserver.h b/dom/fetch/FetchObserver.h new file mode 100644 index 000000000..5cd835b3d --- /dev/null +++ b/dom/fetch/FetchObserver.h @@ -0,0 +1,54 @@ +/* -*- 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_FetchObserver_h +#define mozilla_dom_FetchObserver_h + +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/dom/FetchObserverBinding.h" +#include "mozilla/dom/AbortSignal.h" + +namespace mozilla { +namespace dom { + +class FetchObserver final : public DOMEventTargetHelper + , public AbortSignal::Follower +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FetchObserver, DOMEventTargetHelper) + + static bool + IsEnabled(JSContext* aCx, JSObject* aGlobal); + + FetchObserver(nsIGlobalObject* aGlobal, AbortSignal* aSignal); + + JSObject* + WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + FetchState + State() const; + + IMPL_EVENT_HANDLER(statechange); + IMPL_EVENT_HANDLER(requestprogress); + IMPL_EVENT_HANDLER(responseprogress); + + void + Aborted() override; + + void + SetState(FetchState aState); + +private: + ~FetchObserver() = default; + + FetchState mState; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_FetchObserver_h diff --git a/dom/fetch/Request.h b/dom/fetch/Request.h index d33c74812..f6fe9be7b 100644 --- a/dom/fetch/Request.h +++ b/dom/fetch/Request.h @@ -12,6 +12,7 @@ #include "nsWrapperCache.h" #include "mozilla/dom/Fetch.h" +#include "mozilla/dom/AbortSignal.h" #include "mozilla/dom/InternalRequest.h" // Required here due to certain WebIDL enums/classes being declared in both // files. diff --git a/dom/fetch/moz.build b/dom/fetch/moz.build index be820ab57..e2b466428 100644 --- a/dom/fetch/moz.build +++ b/dom/fetch/moz.build @@ -9,6 +9,7 @@ EXPORTS.mozilla.dom += [ 'Fetch.h', 'FetchDriver.h', 'FetchIPCTypes.h', + 'FetchObserver.h', 'FetchUtil.h', 'Headers.h', 'InternalHeaders.h', @@ -28,6 +29,7 @@ UNIFIED_SOURCES += [ SOURCES += [ 'ChannelInfo.cpp', 'FetchDriver.cpp', + 'FetchObserver.cpp', 'FetchUtil.cpp', 'Headers.cpp', 'InternalHeaders.cpp', diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp index bd9de39f0..f3cb096b9 100644 --- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -2094,7 +2094,7 @@ nsHTMLDocument::GetSupportedNames(nsTArray<nsString>& aNames) nsIdentifierMapEntry* entry = iter.Get(); if (entry->HasNameElement() || entry->HasIdElementExposedAsHTMLDocumentProperty()) { - aNames.AppendElement(entry->GetKey()); + aNames.AppendElement(entry->GetKeyAsString()); } } } diff --git a/dom/html/nsHTMLDocument.h b/dom/html/nsHTMLDocument.h index c9e46b3fa..4f44befbb 100644 --- a/dom/html/nsHTMLDocument.h +++ b/dom/html/nsHTMLDocument.h @@ -155,10 +155,7 @@ public: virtual void RemovedFromDocShell() override; - virtual mozilla::dom::Element *GetElementById(const nsAString& aElementId) override - { - return nsDocument::GetElementById(aElementId); - } + using mozilla::dom::DocumentOrShadowRoot::GetElementById; virtual void DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const override; // DocAddSizeOfIncludingThis is inherited from nsIDocument. diff --git a/dom/moz.build b/dom/moz.build index 89c539b4b..8a958982f 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -37,6 +37,7 @@ interfaces = [ DIRS += ['interfaces/' + i for i in interfaces] DIRS += [ + 'abort', 'animation', 'apps', 'base', diff --git a/dom/tests/mochitest/fetch/file_fetch_observer.html b/dom/tests/mochitest/fetch/file_fetch_observer.html new file mode 100644 index 000000000..668e609ec --- /dev/null +++ b/dom/tests/mochitest/fetch/file_fetch_observer.html @@ -0,0 +1,146 @@ +<script> +function ok(a, msg) { + parent.postMessage({ type: "check", status: !!a, message: msg }, "*"); +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +function testObserver() { + ok("FetchObserver" in self, "We have a FetchObserver prototype"); + + fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { observe: o => { + ok(!!o, "We have an observer"); + ok(o instanceof FetchObserver, "The correct object has been passed"); + is(o.state, "requesting", "By default the state is requesting"); + next(); + }}); +} + +function testObserveAbort() { + var fc = new AbortController(); + + fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { + signal: fc.signal, + observe: o => { + o.onstatechange = () => { + ok(true, "StateChange event dispatched"); + if (o.state == "aborted") { + ok(true, "Aborted!"); + next(); + } + } + fc.abort(); + } + }); +} + +function testObserveComplete() { + var fc = new AbortController(); + + fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { + signal: fc.signal, + observe: o => { + o.onstatechange = () => { + ok(true, "StateChange event dispatched"); + if (o.state == "complete") { + ok(true, "Operation completed"); + next(); + } + } + } + }); +} + +function testObserveErrored() { + var fc = new AbortController(); + + fetch('foo: bar', { + signal: fc.signal, + observe: o => { + o.onstatechange = () => { + ok(true, "StateChange event dispatched"); + if (o.state == "errored") { + ok(true, "Operation completed"); + next(); + } + } + } + }); +} + +function testObserveResponding() { + var fc = new AbortController(); + + fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { + signal: fc.signal, + observe: o => { + o.onstatechange = () => { + if (o.state == "responding") { + ok(true, "We have responding events"); + next(); + } + } + } + }); +} + +function workify(worker) { + function methods() { + function ok(a, msg) { + postMessage( { type: 'check', state: !!a, message: msg }); + }; + function is(a, b, msg) { + postMessage( { type: 'check', state: a === b, message: msg }); + }; + function next() { + postMessage( { type: 'finish' }); + }; + } + + var str = methods.toString(); + var methodsContent = str.substring(0, str.length - 1).split('\n').splice(1).join('\n'); + + str = worker.toString(); + var workerContent = str.substring(0, str.length - 1).split('\n').splice(1).join('\n'); + + var content = methodsContent + workerContent; + var url = URL.createObjectURL(new Blob([content], { type: "application/javascript" })); + var w = new Worker(url); + w.onmessage = e => { + if (e.data.type == 'check') { + ok(e.data.state, "WORKER: " + e.data.message); + } else if (e.data.type == 'finish') { + next(); + } else { + ok(false, "Something went wrong"); + } + } +} + +var steps = [ + testObserver, + testObserveAbort, + function() { workify(testObserveAbort); }, + testObserveComplete, + function() { workify(testObserveComplete); }, + testObserveErrored, + function() { workify(testObserveErrored); }, + testObserveResponding, + function() { workify(testObserveResponding); }, +]; + +function next() { + if (!steps.length) { + parent.postMessage({ type: "finish" }, "*"); + return; + } + + var step = steps.shift(); + step(); +} + +next(); + +</script> diff --git a/dom/tests/mochitest/fetch/mochitest.ini b/dom/tests/mochitest/fetch/mochitest.ini index cf4477463..a9447d0d9 100644 --- a/dom/tests/mochitest/fetch/mochitest.ini +++ b/dom/tests/mochitest/fetch/mochitest.ini @@ -4,6 +4,7 @@ support-files = test_fetch_basic.js test_fetch_basic_http.js test_fetch_cors.js + file_fetch_observer.html test_formdataparsing.js test_headers_common.js test_request.js @@ -15,6 +16,7 @@ support-files = reroute.html reroute.js reroute.js^headers^ + slow.sjs sw_reroute.js empty.js empty.js^headers^ @@ -47,6 +49,7 @@ skip-if = toolkit == 'android' # Bug 1210282 skip-if = toolkit == 'android' # Bug 1210282 [test_fetch_cors_sw_empty_reroute.html] skip-if = toolkit == 'android' # Bug 1210282 +[test_fetch_observer.html] [test_formdataparsing.html] [test_formdataparsing_sw_reroute.html] [test_request.html] diff --git a/dom/tests/mochitest/fetch/slow.sjs b/dom/tests/mochitest/fetch/slow.sjs new file mode 100644 index 000000000..feab0f1fc --- /dev/null +++ b/dom/tests/mochitest/fetch/slow.sjs @@ -0,0 +1,11 @@ +function handleRequest(request, response) +{ + response.processAsync(); + + timer = Components.classes["@mozilla.org/timer;1"]. + createInstance(Components.interfaces.nsITimer); + timer.init(function() { + response.write("Here the content. But slowly."); + response.finish(); + }, 1000, Components.interfaces.nsITimer.TYPE_ONE_SHOT); +} diff --git a/dom/tests/mochitest/fetch/test_fetch_observer.html b/dom/tests/mochitest/fetch/test_fetch_observer.html new file mode 100644 index 000000000..2af86977c --- /dev/null +++ b/dom/tests/mochitest/fetch/test_fetch_observer.html @@ -0,0 +1,41 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test FetchObserver</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> + +SpecialPowers.pushPrefEnv({"set": [["dom.fetchObserver.enabled", true ], + ["dom.fetchController.enabled", true ]]}, () => { + let ifr = document.createElement('iframe'); + ifr.src = "file_fetch_observer.html"; + document.body.appendChild(ifr); + + onmessage = function(e) { + if (e.data.type == "finish") { + SimpleTest.finish(); + return; + } + + if (e.data.type == "check") { + ok(e.data.status, e.data.message); + return; + } + + ok(false, "Something when wrong."); + } +}); + +SimpleTest.waitForExplicitFinish(); + +</script> +</body> +</html> + diff --git a/dom/webidl/AbortController.webidl b/dom/webidl/AbortController.webidl new file mode 100644 index 000000000..4e9124075 --- /dev/null +++ b/dom/webidl/AbortController.webidl @@ -0,0 +1,13 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +[Constructor(), Exposed=(Window,Worker), + Func="AbortController::IsEnabled"] +interface AbortController { + readonly attribute AbortSignal signal; + + void abort(); +}; diff --git a/dom/webidl/AbortSignal.webidl b/dom/webidl/AbortSignal.webidl new file mode 100644 index 000000000..b4b03bb7e --- /dev/null +++ b/dom/webidl/AbortSignal.webidl @@ -0,0 +1,13 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +[Exposed=(Window,Worker), + Func="AbortController::IsEnabled"] +interface AbortSignal : EventTarget { + readonly attribute boolean aborted; + + attribute EventHandler onabort; +}; diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index ba2ec17ea..26985202b 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -136,7 +136,6 @@ partial interface Document { // user interaction [Pure] readonly attribute WindowProxy? defaultView; - readonly attribute Element? activeElement; [Throws] boolean hasFocus(); //(HTML only) attribute DOMString designMode; @@ -283,8 +282,6 @@ partial interface Document { // http://dev.w3.org/csswg/cssom/#extensions-to-the-document-interface partial interface Document { - [Constant] - readonly attribute StyleSheetList styleSheets; attribute DOMString? selectedStyleSheetSet; readonly attribute DOMString? lastStyleSheetSet; readonly attribute DOMString? preferredStyleSheetSet; @@ -456,3 +453,4 @@ Document implements ParentNode; Document implements OnErrorEventHandlerForNodes; Document implements GeometryUtils; Document implements FontFaceSource; +Document implements DocumentOrShadowRoot; diff --git a/dom/webidl/DocumentOrShadowRoot.webidl b/dom/webidl/DocumentOrShadowRoot.webidl new file mode 100644 index 000000000..16388d126 --- /dev/null +++ b/dom/webidl/DocumentOrShadowRoot.webidl @@ -0,0 +1,29 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is + * https://dom.spec.whatwg.org/#documentorshadowroot + * http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-the-documentorshadowroot-mixin + */ + +[NoInterfaceObject] +interface DocumentOrShadowRoot { + // Not implemented yet: bug 1430308. + // Selection? getSelection(); + // Not implemented yet: bug 1430301. + // Element? elementFromPoint (float x, float y); + // Not implemented yet: bug 1430301. + // sequence<Element> elementsFromPoint (float x, float y); + // Not implemented yet: bug 1430307. + // CaretPosition? caretPositionFromPoint (float x, float y); + + readonly attribute Element? activeElement; + readonly attribute StyleSheetList styleSheets; + + // Not implemented yet: bug 1430303. + // readonly attribute Element? pointerLockElement; + // Not implemented yet: bug 1430305. + // readonly attribute Element? fullscreenElement; +}; diff --git a/dom/webidl/FetchObserver.webidl b/dom/webidl/FetchObserver.webidl new file mode 100644 index 000000000..eecd67e66 --- /dev/null +++ b/dom/webidl/FetchObserver.webidl @@ -0,0 +1,27 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +callback interface ObserverCallback { + void handleEvent(FetchObserver observer); +}; + +enum FetchState { + // Pending states + "requesting", "responding", + // Final states + "aborted", "errored", "complete" +}; + +[Exposed=(Window,Worker), + Func="FetchObserver::IsEnabled"] +interface FetchObserver : EventTarget { + readonly attribute FetchState state; + + // Events + attribute EventHandler onstatechange; + attribute EventHandler onrequestprogress; + attribute EventHandler onresponseprogress; +}; diff --git a/dom/webidl/Request.webidl b/dom/webidl/Request.webidl index e29c084d0..fe6a63ec0 100644 --- a/dom/webidl/Request.webidl +++ b/dom/webidl/Request.webidl @@ -47,6 +47,12 @@ dictionary RequestInit { RequestCache cache; RequestRedirect redirect; DOMString integrity; + + [Func="AbortController::IsEnabled"] + AbortSignal signal; + + [Func="FetchObserver::IsEnabled"] + ObserverCallback observe; }; // Gecko currently does not ship RequestContext, so please don't use it in IDL diff --git a/dom/webidl/ShadowRoot.webidl b/dom/webidl/ShadowRoot.webidl index 47e6cf5ec..83acd4161 100644 --- a/dom/webidl/ShadowRoot.webidl +++ b/dom/webidl/ShadowRoot.webidl @@ -32,6 +32,5 @@ interface ShadowRoot : DocumentFragment [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString innerHTML; attribute boolean applyAuthorStyles; - readonly attribute StyleSheetList styleSheets; }; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 45bf92a97..acdc392be 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -17,6 +17,8 @@ PREPROCESSED_WEBIDL_FILES = [ ] WEBIDL_FILES = [ + 'AbortController.webidl', + 'AbortSignal.webidl', 'AbstractWorker.webidl', 'AnalyserNode.webidl', 'Animatable.webidl', @@ -111,6 +113,7 @@ WEBIDL_FILES = [ 'Directory.webidl', 'Document.webidl', 'DocumentFragment.webidl', + 'DocumentOrShadowRoot.webidl', 'DocumentTimeline.webidl', 'DocumentType.webidl', 'DOMCursor.webidl', @@ -142,6 +145,7 @@ WEBIDL_FILES = [ 'FakePluginTagInit.webidl', 'Fetch.webidl', 'FetchEvent.webidl', + 'FetchObserver.webidl', 'File.webidl', 'FileList.webidl', 'FileMode.webidl', @@ -647,10 +651,10 @@ WEBIDL_FILES += [ # We only expose our prefable test interfaces in debug builds, just to be on # the safe side. if CONFIG['MOZ_DEBUG']: - WEBIDL_FILES += ['TestFunctions.webidl', - 'TestInterfaceJS.webidl', - 'TestInterfaceJSDictionaries.webidl', - 'TestInterfaceJSMaplikeSetlikeIterable.webidl'] + WEBIDL_FILES += [ + 'TestInterfaceJS.webidl', + 'TestInterfaceJSDictionaries.webidl', + ] if CONFIG['MOZ_SECUREELEMENT']: WEBIDL_FILES += [ diff --git a/dom/workers/WorkerPrefs.h b/dom/workers/WorkerPrefs.h index 9a1be4801..b552d8956 100644 --- a/dom/workers/WorkerPrefs.h +++ b/dom/workers/WorkerPrefs.h @@ -39,6 +39,8 @@ WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED) WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED) WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED) WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK) +WORKER_SIMPLE_PREF("dom.abortController.enabled", AbortControllerEnabled, ABORTCONTROLLER_ENABLED) +WORKER_SIMPLE_PREF("dom.fetchObserver.enabled", FetchObserverEnabled, FETCHOBSERVER_ENABLED) WORKER_PREF("dom.workers.latestJSVersion", JSVersionChanged) WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) WORKER_PREF("general.appname.override", AppNameOverrideChanged) diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp index 929efc1af..d3b94920a 100644 --- a/dom/xul/XULDocument.cpp +++ b/dom/xul/XULDocument.cpp @@ -1578,24 +1578,13 @@ XULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker) } Element* -XULDocument::GetElementById(const nsAString& aId) +XULDocument::GetRefById(const nsAString& aID) { - if (!CheckGetElementByIdArg(aId)) - return nullptr; - - nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId); - if (entry) { - Element* element = entry->GetIdElement(); - if (element) - return element; - } - - nsRefMapEntry* refEntry = mRefMap.GetEntry(aId); - if (refEntry) { - NS_ASSERTION(refEntry->GetFirstElement(), - "nsRefMapEntries should have nonempty content lists"); + if (nsRefMapEntry* refEntry = mRefMap.GetEntry(aID)) { + MOZ_ASSERT(refEntry->GetFirstElement()); return refEntry->GetFirstElement(); } + return nullptr; } diff --git a/dom/xul/XULDocument.h b/dom/xul/XULDocument.h index 06abb797f..a5ed49704 100644 --- a/dom/xul/XULDocument.h +++ b/dom/xul/XULDocument.h @@ -148,6 +148,7 @@ public: using nsDocument::CreateElementNS; NS_FORWARD_NSIDOMDOCUMENT(XMLDocument::) // And explicitly import the things from nsDocument that we just shadowed + using mozilla::dom::DocumentOrShadowRoot::GetElementById; using nsDocument::GetImplementation; using nsDocument::GetTitle; using nsDocument::SetTitle; @@ -156,8 +157,8 @@ public: using nsDocument::GetMozFullScreenElement; using nsIDocument::GetLocation; - // nsDocument interface overrides - virtual Element* GetElementById(const nsAString & elementId) override; + // Helper for StyleScope::GetElementById. + Element* GetRefById(const nsAString & elementId); // nsIDOMXULDocument interface NS_DECL_NSIDOMXULDOCUMENT diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp index c3cfa4a72..07b06a96a 100644 --- a/editor/libeditor/TextEditor.cpp +++ b/editor/libeditor/TextEditor.cpp @@ -24,6 +24,7 @@ #include "nsCharTraits.h" #include "nsComponentManagerUtils.h" #include "nsContentCID.h" +#include "nsContentList.h" #include "nsCopySupport.h" #include "nsDebug.h" #include "nsDependentSubstring.h" diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index b5b112020..766c00d39 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4738,6 +4738,9 @@ pref("dom.vibrator.max_vibrate_list_len", 128); // Disabled by default to reduce private data exposure. pref("dom.battery.enabled", false); +// Abort API +pref("dom.abortController.enabled", true); + // Push pref("dom.push.enabled", false); diff --git a/testing/web-platform/meta/shadow-dom/ShadowRoot-interface.html.ini b/testing/web-platform/meta/shadow-dom/ShadowRoot-interface.html.ini deleted file mode 100644 index 546e41144..000000000 --- a/testing/web-platform/meta/shadow-dom/ShadowRoot-interface.html.ini +++ /dev/null @@ -1,29 +0,0 @@ -[ShadowRoot-interface.html] - type: testharness - [ShadowRoot.activeElement must return the focused element of the context object when shadow root is open.] - expected: FAIL - - [ShadowRoot.activeElement must return the focused element of the context object when shadow root is closed.] - expected: FAIL - - [ShadowRoot.host must return the shadow host of the context object.] - expected: FAIL - - [ShadowRoot.innerHTML must return the result of the HTML fragment serialization algorithm when shadow root is open.] - expected: FAIL - - [ShadowRoot.innerHTML must return the result of the HTML fragment serialization algorithm when shadow root is closed.] - expected: FAIL - - [ShadowRoot.innerHTML must replace all with the result of invoking the fragment parsing algorithm when shadow root is open.] - expected: FAIL - - [ShadowRoot.innerHTML must replace all with the result of invoking the fragment parsing algorithm when shadow root is closed.] - expected: FAIL - - [ShadowRoot.styleSheets must return a StyleSheetList sequence containing the shadow root style sheets when shadow root is open.] - expected: FAIL - - [ShadowRoot.styleSheets must return a StyleSheetList sequence containing the shadow root style sheets when shadow root is closed.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/activeElement-confirm-return-null.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/activeElement-confirm-return-null.html.ini deleted file mode 100644 index d82555f73..000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/activeElement-confirm-return-null.html.ini +++ /dev/null @@ -1,11 +0,0 @@ -[activeElement-confirm-return-null.html] - type: testharness - [confirm activeElement return null] - expected: FAIL - - [confirm activeElement return null when there is other element in body] - expected: FAIL - - [confirm activeElement return null when focus on the element in the outer shadow tree] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-007.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-007.html.ini deleted file mode 100644 index e621a9779..000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-007.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-007.html] - type: testharness - [A_10_01_01_03_01_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-001.html.ini deleted file mode 100644 index fdcb44343..000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-001.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-001.html] - type: testharness - [A_07_03_01_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-002.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-002.html.ini deleted file mode 100644 index e2a9f3f2f..000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-002.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-002.html] - type: testharness - [A_07_03_02_T01] - expected: FAIL - |