summaryrefslogtreecommitdiffstats
path: root/dom/base
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base')
-rw-r--r--dom/base/Element.cpp24
-rw-r--r--dom/base/FragmentOrElement.cpp1
-rw-r--r--dom/base/IdleRequest.cpp119
-rw-r--r--dom/base/IdleRequest.h40
-rw-r--r--dom/base/Location.cpp21
-rw-r--r--dom/base/Timeout.h8
-rw-r--r--dom/base/TimeoutHandler.cpp43
-rw-r--r--dom/base/TimeoutHandler.h50
-rwxr-xr-xdom/base/moz.build2
-rw-r--r--dom/base/nsContentPolicy.cpp6
-rw-r--r--dom/base/nsDocument.cpp37
-rw-r--r--dom/base/nsGlobalWindow.cpp360
-rw-r--r--dom/base/nsGlobalWindow.h31
-rw-r--r--dom/base/nsIDocument.h3
-rw-r--r--dom/base/nsINode.cpp4
15 files changed, 532 insertions, 217 deletions
diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp
index 9ced64c0d..092755590 100644
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -183,6 +183,12 @@ Element::DoGetClasses() const
NS_IMETHODIMP
Element::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
+ if (aIID.Equals(NS_GET_IID(Element))) {
+ NS_ADDREF_THIS();
+ *aInstancePtr = this;
+ return NS_OK;
+ }
+
NS_ASSERTION(aInstancePtr,
"QueryInterface requires a non-NULL destination!");
nsresult rv = FragmentOrElement::QueryInterface(aIID, aInstancePtr);
@@ -1838,6 +1844,24 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
SetParentIsContent(false);
}
+#ifdef DEBUG
+ // If we can get access to the PresContext, then we sanity-check that
+ // we're not leaving behind a pointer to ourselves as the PresContext's
+ // cached provider of the viewport's scrollbar styles.
+ if (document) {
+ nsIPresShell* presShell = document->GetShell();
+ if (presShell) {
+ nsPresContext* presContext = presShell->GetPresContext();
+ if (presContext) {
+ MOZ_ASSERT(this !=
+ presContext->GetViewportScrollbarStylesOverrideNode(),
+ "Leaving behind a raw pointer to this node (as having "
+ "propagated scrollbar styles) - that's dangerous...");
+ }
+ }
+ }
+#endif
+
// Ensure that CSS transitions don't continue on an element at a
// different place in the tree (even if reinserted before next
// animation refresh).
diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp
index 79f6cff51..b22a0d4ff 100644
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -1937,7 +1937,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
- NS_INTERFACE_MAP_ENTRY(Element)
NS_INTERFACE_MAP_ENTRY(nsIContent)
NS_INTERFACE_MAP_ENTRY(nsINode)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
diff --git a/dom/base/IdleRequest.cpp b/dom/base/IdleRequest.cpp
index 26190f98b..fb3983d37 100644
--- a/dom/base/IdleRequest.cpp
+++ b/dom/base/IdleRequest.cpp
@@ -9,7 +9,6 @@
#include "mozilla/Function.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/IdleDeadline.h"
-#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PerformanceTiming.h"
#include "mozilla/dom/WindowBinding.h"
#include "nsComponentManagerUtils.h"
@@ -20,138 +19,56 @@
namespace mozilla {
namespace dom {
-IdleRequest::IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
- IdleRequestCallback& aCallback, uint32_t aHandle)
- : mWindow(aWindow)
- , mCallback(&aCallback)
+IdleRequest::IdleRequest(IdleRequestCallback* aCallback, uint32_t aHandle)
+ : mCallback(aCallback)
, mHandle(aHandle)
, mTimeoutHandle(Nothing())
{
- MOZ_ASSERT(aWindow);
-
- // Get the calling location.
- nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
+ MOZ_DIAGNOSTIC_ASSERT(mCallback);
}
IdleRequest::~IdleRequest()
{
}
-NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequest)
+NS_IMPL_CYCLE_COLLECTION(IdleRequest, mCallback)
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequest)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequest)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequest)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequest)
- NS_INTERFACE_MAP_ENTRY(nsIRunnable)
- NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
- NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
- NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimeoutHandler)
NS_INTERFACE_MAP_END
-nsresult
-IdleRequest::SetTimeout(uint32_t aTimeout)
-{
- int32_t handle;
- nsresult rv = nsGlobalWindow::Cast(mWindow)->SetTimeoutOrInterval(
- this, aTimeout, false, Timeout::Reason::eIdleCallbackTimeout, &handle);
- mTimeoutHandle = Some(handle);
-
- return rv;
-}
-
-nsresult
-IdleRequest::Run()
-{
- if (mCallback) {
- RunIdleRequestCallback(false);
- }
-
- return NS_OK;
-}
-
-nsresult
-IdleRequest::Cancel()
+void
+IdleRequest::SetTimeoutHandle(int32_t aHandle)
{
- mCallback = nullptr;
- CancelTimeout();
- if (isInList()) {
- remove();
- }
- Release();
-
- return NS_OK;
+ mTimeoutHandle = Some(aHandle);
}
-void
-IdleRequest::SetDeadline(TimeStamp aDeadline)
+uint32_t
+IdleRequest::GetTimeoutHandle() const
{
- mozilla::dom::Performance* perf = mWindow->GetPerformance();
- mDeadline =
- perf ? perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline) : 0.0;
+ MOZ_DIAGNOSTIC_ASSERT(mTimeoutHandle.isSome());
+ return mTimeoutHandle.value();
}
nsresult
-IdleRequest::RunIdleRequestCallback(bool aDidTimeout)
+IdleRequest::IdleRun(nsPIDOMWindowInner* aWindow,
+ DOMHighResTimeStamp aDeadline,
+ bool aDidTimeout)
{
MOZ_ASSERT(NS_IsMainThread());
+ MOZ_DIAGNOSTIC_ASSERT(mCallback);
- if (!aDidTimeout) {
- CancelTimeout();
- }
-
- remove();
ErrorResult error;
RefPtr<IdleDeadline> deadline =
- new IdleDeadline(mWindow, aDidTimeout, mDeadline);
+ new IdleDeadline(aWindow, aDidTimeout, aDeadline);
mCallback->Call(*deadline, error, "requestIdleCallback handler");
- mCallback = nullptr;
- Release();
+ mCallback = nullptr;
+ error.SuppressException();
return error.StealNSResult();
}
-void
-IdleRequest::CancelTimeout()
-{
- if (mTimeoutHandle.isSome()) {
- nsGlobalWindow::Cast(mWindow)->ClearTimeoutOrInterval(
- mTimeoutHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
- }
-}
-
-nsresult
-IdleRequest::Call()
-{
- SetDeadline(TimeStamp::Now());
- return RunIdleRequestCallback(true);
-}
-
-void
-IdleRequest::GetLocation(const char** aFileName, uint32_t* aLineNo,
- uint32_t* aColumn)
-{
- *aFileName = mFileName.get();
- *aLineNo = mLineNo;
- *aColumn = mColumn;
-}
-
-void
-IdleRequest::MarkForCC()
-{
- mCallback->MarkForCC();
-}
-
} // namespace dom
} // namespace mozilla
diff --git a/dom/base/IdleRequest.h b/dom/base/IdleRequest.h
index cb234430a..acf56f852 100644
--- a/dom/base/IdleRequest.h
+++ b/dom/base/IdleRequest.h
@@ -12,7 +12,6 @@
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDOMNavigationTiming.h"
-#include "nsITimeoutHandler.h"
class nsPIDOMWindowInner;
@@ -21,28 +20,19 @@ namespace dom {
class IdleRequestCallback;
-class IdleRequest final : public nsITimeoutHandler
- , public nsIRunnable
- , public nsICancelableRunnable
- , public nsIIncrementalRunnable
- , public LinkedListElement<IdleRequest>
+class IdleRequest final : public nsISupports,
+ public LinkedListElement<IdleRequest>
{
public:
- IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
- IdleRequestCallback& aCallback, uint32_t aHandle);
+ IdleRequest(IdleRequestCallback* aCallback, uint32_t aHandle);
- virtual nsresult Call() override;
- virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
- uint32_t* aColumn) override;
- virtual void MarkForCC() override;
+ nsresult IdleRun(nsPIDOMWindowInner* aWindow,
+ DOMHighResTimeStamp aDeadline,
+ bool aDidTimeout);
- nsresult SetTimeout(uint32_t aTimout);
- nsresult RunIdleRequestCallback(bool aDidTimeout);
- void CancelTimeout();
-
- NS_DECL_NSIRUNNABLE;
- virtual nsresult Cancel() override;
- virtual void SetDeadline(mozilla::TimeStamp aDeadline) override;
+ void SetTimeoutHandle(int32_t aHandle);
+ bool HasTimeout() const { return mTimeoutHandle.isSome(); }
+ uint32_t GetTimeoutHandle() const;
uint32_t Handle() const
{
@@ -50,22 +40,14 @@ public:
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequest, nsITimeoutHandler)
+ NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequest)
private:
~IdleRequest();
- // filename, line number and JS language version string of the
- // caller of setTimeout()
- nsCString mFileName;
- uint32_t mLineNo;
- uint32_t mColumn;
-
- nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<IdleRequestCallback> mCallback;
- uint32_t mHandle;
+ const uint32_t mHandle;
mozilla::Maybe<int32_t> mTimeoutHandle;
- DOMHighResTimeStamp mDeadline;
};
} // namespace dom
diff --git a/dom/base/Location.cpp b/dom/base/Location.cpp
index e3b614931..b6b95aaa6 100644
--- a/dom/base/Location.cpp
+++ b/dom/base/Location.cpp
@@ -577,19 +577,17 @@ Location::GetPathname(nsAString& aPathname)
aPathname.Truncate();
nsCOMPtr<nsIURI> uri;
- nsresult result = NS_OK;
+ nsresult result = GetURI(getter_AddRefs(uri));
+ if (NS_FAILED(result) || !uri) {
+ return result;
+ }
- result = GetURI(getter_AddRefs(uri));
+ nsAutoCString file;
- nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(uri));
- if (url) {
- nsAutoCString file;
+ result = uri->GetFilePath(file);
- result = url->GetFilePath(file);
-
- if (NS_SUCCEEDED(result)) {
- AppendUTF8toUTF16(file, aPathname);
- }
+ if (NS_SUCCEEDED(result)) {
+ AppendUTF8toUTF16(file, aPathname);
}
return result;
@@ -604,8 +602,7 @@ Location::SetPathname(const nsAString& aPathname)
return rv;
}
- nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(uri));
- if (url && NS_SUCCEEDED(url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname)))) {
+ if (NS_SUCCEEDED(uri->SetFilePath(NS_ConvertUTF16toUTF8(aPathname)))) {
return SetURI(uri);
}
diff --git a/dom/base/Timeout.h b/dom/base/Timeout.h
index e929f3dd1..42e2f57f5 100644
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -41,7 +41,11 @@ public:
// default main thread being used.
nsresult InitTimer(nsIEventTarget* aTarget, uint32_t aDelay);
- enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
+ enum class Reason
+ {
+ eTimeoutOrInterval,
+ eIdleCallbackTimeout,
+ };
#ifdef DEBUG
bool HasRefCntOne() const;
@@ -62,6 +66,8 @@ public:
// True if this is a repeating/interval timer
bool mIsInterval;
+ // Used to allow several reasons for setting a timeout, where each
+ // 'Reason' value is using a possibly overlapping set of id:s.
Reason mReason;
// Returned as value of setTimeout()
diff --git a/dom/base/TimeoutHandler.cpp b/dom/base/TimeoutHandler.cpp
new file mode 100644
index 000000000..78c3f16dd
--- /dev/null
+++ b/dom/base/TimeoutHandler.cpp
@@ -0,0 +1,43 @@
+/* -*- 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 "TimeoutHandler.h"
+
+namespace mozilla {
+namespace dom {
+
+TimeoutHandler::TimeoutHandler(JSContext* aCx)
+ : TimeoutHandler()
+{
+ nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
+}
+
+nsresult
+TimeoutHandler::Call()
+{
+ return NS_OK;
+}
+
+void
+TimeoutHandler::GetLocation(const char** aFileName, uint32_t* aLineNo,
+ uint32_t* aColumn)
+{
+ *aFileName = mFileName.get();
+ *aLineNo = mLineNo;
+ *aColumn = mColumn;
+}
+
+NS_IMPL_CYCLE_COLLECTION_0(TimeoutHandler)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TimeoutHandler)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TimeoutHandler)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TimeoutHandler)
+ NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
+NS_INTERFACE_MAP_END
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/base/TimeoutHandler.h b/dom/base/TimeoutHandler.h
new file mode 100644
index 000000000..cb0a0ce94
--- /dev/null
+++ b/dom/base/TimeoutHandler.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_timeout_handler_h
+#define mozilla_dom_timeout_handler_h
+
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsITimeoutHandler.h"
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * Utility class for implementing nsITimeoutHandlers, designed to be subclassed.
+ */
+class TimeoutHandler : public nsITimeoutHandler
+{
+public:
+ // TimeoutHandler doesn't actually contain cycles, but subclasses
+ // probably will.
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(TimeoutHandler)
+
+ virtual nsresult Call() override;
+ virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
+ uint32_t* aColumn) override;
+ virtual void MarkForCC() override {}
+protected:
+ TimeoutHandler() : mFileName(""), mLineNo(0), mColumn(0) {}
+ explicit TimeoutHandler(JSContext *aCx);
+
+ virtual ~TimeoutHandler() {}
+private:
+ TimeoutHandler(const TimeoutHandler&) = delete;
+ TimeoutHandler& operator=(const TimeoutHandler&) = delete;
+ TimeoutHandler& operator=(const TimeoutHandler&&) = delete;
+
+ nsCString mFileName;
+ uint32_t mLineNo;
+ uint32_t mColumn;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_timeout_handler_h
diff --git a/dom/base/moz.build b/dom/base/moz.build
index 0fb345d22..76c765b1c 100755
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -217,6 +217,7 @@ EXPORTS.mozilla.dom += [
'TabGroup.h',
'Text.h',
'Timeout.h',
+ 'TimeoutHandler.h',
'TreeWalker.h',
'WebKitCSSMatrix.h',
'WebSocket.h',
@@ -364,6 +365,7 @@ UNIFIED_SOURCES += [
'TextInputProcessor.cpp',
'ThirdPartyUtil.cpp',
'Timeout.cpp',
+ 'TimeoutHandler.cpp',
'TimerClamping.cpp',
'TreeWalker.cpp',
'WebKitCSSMatrix.cpp',
diff --git a/dom/base/nsContentPolicy.cpp b/dom/base/nsContentPolicy.cpp
index 337debcea..5511b9086 100644
--- a/dom/base/nsContentPolicy.cpp
+++ b/dom/base/nsContentPolicy.cpp
@@ -20,6 +20,7 @@
#include "nsIDOMElement.h"
#include "nsIDOMNode.h"
#include "nsIDOMWindow.h"
+#include "nsITabChild.h"
#include "nsIContent.h"
#include "nsILoadContext.h"
#include "nsCOMArray.h"
@@ -89,8 +90,9 @@ nsContentPolicy::CheckPolicy(CPMethod policyMethod,
{
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(requestingContext));
nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(requestingContext));
- NS_ASSERTION(!requestingContext || node || window,
- "Context should be a DOM node or a DOM window!");
+ nsCOMPtr<nsITabChild> tabChild(do_QueryInterface(requestingContext));
+ NS_ASSERTION(!requestingContext || node || window || tabChild,
+ "Context should be a DOM node, DOM window or a tabChild!");
}
#endif
diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
index eaea49b02..fd3b52948 100644
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2054,10 +2054,17 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
mFirstChild = content->GetNextSibling();
}
mChildren.RemoveChildAt(i);
+ if (content == mCachedRootElement) {
+ // Immediately clear mCachedRootElement, now that it's been removed
+ // from mChildren, so that GetRootElement() will stop returning this
+ // now-stale value.
+ mCachedRootElement = nullptr;
+ }
nsNodeUtils::ContentRemoved(this, content, i, previousSibling);
content->UnbindFromTree();
}
- mCachedRootElement = nullptr;
+ MOZ_ASSERT(!mCachedRootElement,
+ "After removing all children, there should be no root elem");
}
mInUnlinkOrDeletion = oldVal;
@@ -3913,8 +3920,18 @@ nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify)
DestroyElementMaps();
}
- doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
+ // Preemptively clear mCachedRootElement, since we may be about to remove it
+ // from our child list, and we don't want to return this maybe-obsolete value
+ // from any GetRootElement() calls that happen inside of doRemoveChildAt().
+ // (NOTE: for this to be useful, doRemoveChildAt() must NOT trigger any
+ // GetRootElement() calls until after it's removed the child from mChildren.
+ // Any call before that point would restore this soon-to-be-obsolete cached
+ // answer, and our clearing here would be fruitless.)
mCachedRootElement = nullptr;
+ doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
+ MOZ_ASSERT(mCachedRootElement != oldKid,
+ "Stale pointer in mCachedRootElement, after we tried to clear it "
+ "(maybe somebody called GetRootElement() too early?)");
}
void
@@ -12846,3 +12863,19 @@ nsDocument::CheckCustomElementName(const ElementCreationOptions& aOptions,
return is;
}
+
+Selection*
+nsIDocument::GetSelection(ErrorResult& aRv)
+{
+ nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
+ if (!window) {
+ return nullptr;
+ }
+
+ NS_ASSERTION(window->IsInnerWindow(), "Should have inner window here!");
+ if (!window->IsCurrentInnerWindow()) {
+ return nullptr;
+ }
+
+ return nsGlobalWindow::Cast(window)->GetSelection(aRv);
+}
diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
index f784031f6..3d5c44a78 100644
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -23,6 +23,7 @@
#include "mozilla/dom/StorageEvent.h"
#include "mozilla/dom/StorageEventBinding.h"
#include "mozilla/dom/Timeout.h"
+#include "mozilla/dom/TimeoutHandler.h"
#include "mozilla/IntegerPrintfMacros.h"
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
#include "mozilla/dom/WindowOrientationObserver.h"
@@ -552,29 +553,284 @@ DialogValueHolder::Get(JSContext* aCx, JS::Handle<JSObject*> aScope,
}
}
+class IdleRequestExecutor final : public nsIRunnable
+ , public nsICancelableRunnable
+ , public nsIIncrementalRunnable
+{
+public:
+ explicit IdleRequestExecutor(nsGlobalWindow* aWindow)
+ : mDispatched(false)
+ , mDeadline(TimeStamp::Now())
+ , mWindow(aWindow)
+ {
+ MOZ_DIAGNOSTIC_ASSERT(mWindow);
+ MOZ_DIAGNOSTIC_ASSERT(mWindow->IsInnerWindow());
+ }
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
+
+ NS_DECL_NSIRUNNABLE
+ nsresult Cancel() override;
+ void SetDeadline(TimeStamp aDeadline) override;
+
+ void MaybeDispatch();
+private:
+ ~IdleRequestExecutor() {}
+
+ bool mDispatched;
+ TimeStamp mDeadline;
+ RefPtr<nsGlobalWindow> mWindow;
+};
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequestExecutor)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequestExecutor)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequestExecutor)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
+ NS_INTERFACE_MAP_ENTRY(nsIRunnable)
+ NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
+ NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+IdleRequestExecutor::Run()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mDispatched = false;
+ if (mWindow) {
+ return mWindow->ExecuteIdleRequest(mDeadline);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+IdleRequestExecutor::Cancel()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mWindow = nullptr;
+ return NS_OK;
+}
+
void
-nsGlobalWindow::PostThrottledIdleCallback()
+IdleRequestExecutor::SetDeadline(TimeStamp aDeadline)
{
- AssertIsOnMainThread();
+ MOZ_ASSERT(NS_IsMainThread());
- if (mThrottledIdleRequestCallbacks.isEmpty())
+ if (!mWindow) {
return;
+ }
+
+ mDeadline = aDeadline;
+}
- RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
- // ownership transferred from mThrottledIdleRequestCallbacks to
- // mIdleRequestCallbacks
- mIdleRequestCallbacks.insertBack(request);
+void
+IdleRequestExecutor::MaybeDispatch()
+{
+ // If we've already dispatched the executor we don't want to do it
+ // again. Also, if we've called IdleRequestExecutor::Cancel mWindow
+ // will be null, which indicates that we shouldn't dispatch this
+ // executor either.
+ if (mDispatched || !mWindow) {
+ return;
+ }
+
+ mDispatched = true;
+ RefPtr<IdleRequestExecutor> request = this;
NS_IdleDispatchToCurrentThread(request.forget());
}
-/* static */ void
-nsGlobalWindow::InsertIdleCallbackIntoList(IdleRequest* aRequest,
- IdleRequests& aList)
+class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler
+{
+public:
+ explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
+ : mExecutor(aExecutor)
+ {
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestExecutorTimeoutHandler,
+ TimeoutHandler)
+
+ nsresult Call() override
+ {
+ mExecutor->MaybeDispatch();
+ return NS_OK;
+ }
+private:
+ ~IdleRequestExecutorTimeoutHandler() {}
+ RefPtr<IdleRequestExecutor> mExecutor;
+};
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler, mExecutor)
+
+NS_IMPL_ADDREF_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
+NS_IMPL_RELEASE_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
+ NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
+NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
+
+void
+nsGlobalWindow::ScheduleIdleRequestDispatch()
+{
+ AssertIsOnMainThread();
+
+ if (mIdleRequestCallbacks.isEmpty()) {
+ if (mIdleRequestExecutor) {
+ mIdleRequestExecutor->Cancel();
+ mIdleRequestExecutor = nullptr;
+ }
+
+ return;
+ }
+
+ if (!mIdleRequestExecutor) {
+ mIdleRequestExecutor = new IdleRequestExecutor(this);
+ }
+
+ nsPIDOMWindowOuter* outer = GetOuterWindow();
+ if (outer && outer->AsOuter()->IsBackground()) {
+ nsCOMPtr<nsITimeoutHandler> handler = new IdleRequestExecutorTimeoutHandler(mIdleRequestExecutor);
+ int32_t dummy;
+ // Set a timeout handler with a timeout of 0 ms to throttle idle
+ // callback requests coming from a backround window using
+ // background timeout throttling.
+ SetTimeoutOrInterval(handler, 0, false,
+ Timeout::Reason::eIdleCallbackTimeout, &dummy);
+ return;
+ }
+
+ mIdleRequestExecutor->MaybeDispatch();
+}
+
+void
+nsGlobalWindow::SuspendIdleRequests()
+{
+ if (mIdleRequestExecutor) {
+ mIdleRequestExecutor->Cancel();
+ mIdleRequestExecutor = nullptr;
+ }
+}
+
+void
+nsGlobalWindow::ResumeIdleRequests()
{
- aList.insertBack(aRequest);
+ MOZ_ASSERT(!mIdleRequestExecutor);
+
+ ScheduleIdleRequestDispatch();
+}
+
+void
+nsGlobalWindow::InsertIdleCallback(IdleRequest* aRequest)
+{
+ AssertIsOnMainThread();
+ mIdleRequestCallbacks.insertBack(aRequest);
aRequest->AddRef();
}
+void
+nsGlobalWindow::RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest)
+{
+ AssertIsOnMainThread();
+
+ if (aRequest->HasTimeout()) {
+ ClearTimeoutOrInterval(aRequest->GetTimeoutHandle(),
+ Timeout::Reason::eIdleCallbackTimeout);
+ }
+
+ aRequest->removeFrom(mIdleRequestCallbacks);
+ aRequest->Release();
+}
+
+nsresult
+nsGlobalWindow::RunIdleRequest(IdleRequest* aRequest,
+ DOMHighResTimeStamp aDeadline,
+ bool aDidTimeout)
+{
+ AssertIsOnMainThread();
+ RefPtr<IdleRequest> request(aRequest);
+ RemoveIdleCallback(request);
+ return request->IdleRun(AsInner(), aDeadline, aDidTimeout);
+}
+
+nsresult
+nsGlobalWindow::ExecuteIdleRequest(TimeStamp aDeadline)
+{
+ AssertIsOnMainThread();
+ RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
+
+ if (!request) {
+ // There are no more idle requests, so stop scheduling idle
+ // request callbacks.
+ return NS_OK;
+ }
+
+ DOMHighResTimeStamp deadline = 0.0;
+
+ if (Performance* perf = GetPerformance()) {
+ deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
+ }
+
+ nsresult result = RunIdleRequest(request, deadline, false);
+
+ ScheduleIdleRequestDispatch();
+ return result;
+}
+
+class IdleRequestTimeoutHandler final : public TimeoutHandler
+{
+public:
+ IdleRequestTimeoutHandler(JSContext* aCx,
+ IdleRequest* aIdleRequest,
+ nsPIDOMWindowInner* aWindow)
+ : TimeoutHandler(aCx)
+ , mIdleRequest(aIdleRequest)
+ , mWindow(aWindow)
+ {
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestTimeoutHandler,
+ TimeoutHandler)
+
+ nsresult Call() override
+ {
+ return nsGlobalWindow::Cast(mWindow)->RunIdleRequest(mIdleRequest, 0.0, true);
+ }
+
+private:
+ ~IdleRequestTimeoutHandler() {}
+
+ RefPtr<IdleRequest> mIdleRequest;
+ nsCOMPtr<nsPIDOMWindowInner> mWindow;
+};
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestTimeoutHandler,
+ TimeoutHandler,
+ mIdleRequest,
+ mWindow)
+
+NS_IMPL_ADDREF_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
+NS_IMPL_RELEASE_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
+ NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
+NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
+
uint32_t
nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
IdleRequestCallback& aCallback,
@@ -584,33 +840,36 @@ nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
MOZ_RELEASE_ASSERT(IsInnerWindow());
AssertIsOnMainThread();
- uint32_t handle = ++mIdleRequestCallbackCounter;
+ uint32_t handle = mIdleRequestCallbackCounter++;
RefPtr<IdleRequest> request =
- new IdleRequest(aCx, AsInner(), aCallback, handle);
+ new IdleRequest(&aCallback, handle);
if (aOptions.mTimeout.WasPassed()) {
- aError = request->SetTimeout(aOptions.mTimeout.Value());
- if (NS_WARN_IF(aError.Failed())) {
+ int32_t timeoutHandle;
+ nsCOMPtr<nsITimeoutHandler> handler(new IdleRequestTimeoutHandler(aCx, request, AsInner()));
+
+ nsresult rv = SetTimeoutOrInterval(
+ handler, aOptions.mTimeout.Value(), false,
+ Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
return 0;
}
- }
- nsGlobalWindow* outer = GetOuterWindowInternal();
- if (outer && outer->AsOuter()->IsBackground()) {
- // mThrottledIdleRequestCallbacks now owns request
- InsertIdleCallbackIntoList(request, mThrottledIdleRequestCallbacks);
-
- NS_DelayedDispatchToCurrentThread(
- NewRunnableMethod(this, &nsGlobalWindow::PostThrottledIdleCallback),
- 10000);
- } else {
- MOZ_ASSERT(mThrottledIdleRequestCallbacks.isEmpty());
+ request->SetTimeoutHandle(timeoutHandle);
+ }
- // mIdleRequestCallbacks now owns request
- InsertIdleCallbackIntoList(request, mIdleRequestCallbacks);
+ // If the list of idle callback requests is not empty it means that
+ // we've already dispatched the first idle request. If we're
+ // suspended we should only queue the idle callback and not schedule
+ // it to run, that will be done in ResumeIdleRequest.
+ bool needsScheduling = !IsSuspended() && mIdleRequestCallbacks.isEmpty();
+ // mIdleRequestCallbacks now owns request
+ InsertIdleCallback(request);
- NS_IdleDispatchToCurrentThread(request.forget());
+ if (needsScheduling) {
+ ScheduleIdleRequestDispatch();
}
return handle;
@@ -623,7 +882,7 @@ nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
for (IdleRequest* r : mIdleRequestCallbacks) {
if (r->Handle() == aHandle) {
- r->Cancel();
+ RemoveIdleCallback(r);
break;
}
}
@@ -632,29 +891,17 @@ nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
void
nsGlobalWindow::DisableIdleCallbackRequests()
{
- while (!mIdleRequestCallbacks.isEmpty()) {
- RefPtr<IdleRequest> request = mIdleRequestCallbacks.popFirst();
- request->Cancel();
+ if (mIdleRequestExecutor) {
+ mIdleRequestExecutor->Cancel();
+ mIdleRequestExecutor = nullptr;
}
- while (!mThrottledIdleRequestCallbacks.isEmpty()) {
- RefPtr<IdleRequest> request = mThrottledIdleRequestCallbacks.popFirst();
- request->Cancel();
- }
-}
-
-void nsGlobalWindow::UnthrottleIdleCallbackRequests()
-{
- AssertIsOnMainThread();
-
- while (!mThrottledIdleRequestCallbacks.isEmpty()) {
- RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
- mIdleRequestCallbacks.insertBack(request);
- NS_IdleDispatchToCurrentThread(request.forget());
+ while (!mIdleRequestCallbacks.isEmpty()) {
+ RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
+ RemoveIdleCallback(request);
}
}
-
namespace mozilla {
namespace dom {
extern uint64_t
@@ -1306,6 +1553,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
mSerial(0),
mIdleCallbackTimeoutCounter(1),
mIdleRequestCallbackCounter(1),
+ mIdleRequestExecutor(nullptr),
#ifdef DEBUG
mSetOpenerWindowCalled(false),
#endif
@@ -2033,14 +2281,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
}
- for (IdleRequest* request : tmp->mThrottledIdleRequestCallbacks) {
- cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
- }
-
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
#ifdef MOZ_GAMEPAD
@@ -2147,6 +2392,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
tmp->UnlinkHostObjectURIs();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
tmp->DisableIdleCallbackRequests();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -10237,12 +10483,6 @@ void nsGlobalWindow::SetIsBackground(bool aIsBackground)
ResetTimersForThrottleReduction(gMinBackgroundTimeoutValue);
}
- if (!aIsBackground) {
- nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
- if (inner) {
- inner->UnthrottleIdleCallbackRequests();
- }
- }
#ifdef MOZ_GAMEPAD
if (!aIsBackground) {
nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
@@ -12036,6 +12276,8 @@ nsGlobalWindow::Suspend()
mozilla::dom::workers::SuspendWorkersForWindow(AsInner());
+ SuspendIdleRequests();
+
for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
// Leave the timers with the current time remaining. This will
// cause the timers to potentially fire when the window is
@@ -12146,6 +12388,8 @@ nsGlobalWindow::Resume()
t->AddRef();
}
+ ResumeIdleRequests();
+
// Resume all of the workers for this window. We must do this
// after timeouts since workers may have queued events that can trigger
// a setTimeout().
diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
index dbceeab74..78bee63a1 100644
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -102,6 +102,8 @@ struct nsRect;
class nsWindowSizes;
+class IdleRequestExecutor;
+
namespace mozilla {
class DOMEventTargetHelper;
class ThrottledEventQueue;
@@ -118,6 +120,7 @@ class Gamepad;
enum class ImageBitmapFormat : uint32_t;
class IdleRequest;
class IdleRequestCallback;
+class IncrementalRunnable;
class Location;
class MediaQueryList;
class MozSelfSupport;
@@ -1097,7 +1100,6 @@ public:
mozilla::ErrorResult& aError);
void CancelIdleCallback(uint32_t aHandle);
-
#ifdef MOZ_WEBSPEECH
mozilla::dom::SpeechSynthesis*
GetSpeechSynthesis(mozilla::ErrorResult& aError);
@@ -1762,6 +1764,21 @@ private:
mozilla::dom::TabGroup* TabGroupInner();
mozilla::dom::TabGroup* TabGroupOuter();
+public:
+ void DisableIdleCallbackRequests();
+ uint32_t IdleRequestHandle() const { return mIdleRequestCallbackCounter; }
+ nsresult RunIdleRequest(mozilla::dom::IdleRequest* aRequest,
+ DOMHighResTimeStamp aDeadline, bool aDidTimeout);
+ nsresult ExecuteIdleRequest(TimeStamp aDeadline);
+ void ScheduleIdleRequestDispatch();
+ void SuspendIdleRequests();
+ void ResumeIdleRequests();
+
+ typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
+ void InsertIdleCallback(mozilla::dom::IdleRequest* aRequest);
+
+ void RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest);
+
protected:
// These members are only used on outer window objects. Make sure
// you never set any of these on an inner object!
@@ -1912,21 +1929,12 @@ protected:
uint32_t mSerial;
- void DisableIdleCallbackRequests();
- void UnthrottleIdleCallbackRequests();
-
- void PostThrottledIdleCallback();
-
- typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
- static void InsertIdleCallbackIntoList(mozilla::dom::IdleRequest* aRequest,
- IdleRequests& aList);
-
// The current idle request callback timeout handle
uint32_t mIdleCallbackTimeoutCounter;
// The current idle request callback handle
uint32_t mIdleRequestCallbackCounter;
IdleRequests mIdleRequestCallbacks;
- IdleRequests mThrottledIdleRequestCallbacks;
+ RefPtr<IdleRequestExecutor> mIdleRequestExecutor;
#ifdef DEBUG
bool mSetOpenerWindowCalled;
@@ -2002,6 +2010,7 @@ protected:
friend class nsDOMWindowUtils;
friend class mozilla::dom::PostMessageEvent;
friend class DesktopNotification;
+ friend class IdleRequestExecutor;
static WindowByIdTable* sWindowsById;
static bool sWarnedAboutWindowInternal;
diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
index 8f35e9ba5..1e0c9562e 100644
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -151,6 +151,7 @@ class NodeIterator;
enum class OrientationType : uint32_t;
class ProcessingInstruction;
class Promise;
+class Selection;
class StyleSheetList;
class SVGDocument;
class SVGSVGElement;
@@ -898,6 +899,8 @@ public:
*/
Element* GetRootElement() const;
+ mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aRv);
+
/**
* Retrieve information about the viewport as a data structure.
* This will return information in the viewport META data section
diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp
index 3a649a61d..715ca93ea 100644
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1907,6 +1907,10 @@ void
nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify,
nsIContent* aKid, nsAttrAndChildArray& aChildArray)
{
+ // NOTE: This function must not trigger any calls to
+ // nsIDocument::GetRootElement() calls until *after* it has removed aKid from
+ // aChildArray. Any calls before then could potentially restore a stale
+ // value for our cached root element, per note in nsDocument::RemoveChildAt().
NS_PRECONDITION(aKid && aKid->GetParentNode() == this &&
aKid == GetChildAt(aIndex) &&
IndexOf(aKid) == (int32_t)aIndex, "Bogus aKid");