Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2024-01-18 10:17:09 +08:00
commit b0345ff809
35 changed files with 789 additions and 133 deletions

View file

@ -136,7 +136,7 @@ nsDSURIContentListener::DoContent(const nsACString& aContentType,
mExistingJPEGRequest = baseChannel; mExistingJPEGRequest = baseChannel;
} }
if (rv == NS_ERROR_REMOTE_XUL) { if (rv == NS_ERROR_REMOTE_XUL || rv == NS_ERROR_DOCSHELL_DYING) {
aRequest->Cancel(rv); aRequest->Cancel(rv);
*aAbortProcess = true; *aAbortProcess = true;
return NS_OK; return NS_OK;

View file

@ -893,6 +893,8 @@ nsDocShell::~nsDocShell()
nsresult nsresult
nsDocShell::Init() nsDocShell::Init()
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
nsresult rv = nsDocLoader::Init(); nsresult rv = nsDocLoader::Init();
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
@ -1921,6 +1923,8 @@ bool
nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest, nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
bool aFireOnLocationChange, uint32_t aLocationFlags) bool aFireOnLocationChange, uint32_t aLocationFlags)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
if (gDocShellLeakLog && MOZ_LOG_TEST(gDocShellLeakLog, LogLevel::Debug)) { if (gDocShellLeakLog && MOZ_LOG_TEST(gDocShellLeakLog, LogLevel::Debug)) {
PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n",
this, aURI ? aURI->GetSpecOrDefault().get() : ""); this, aURI ? aURI->GetSpecOrDefault().get() : "");
@ -2181,6 +2185,8 @@ nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
NS_IMETHODIMP NS_IMETHODIMP
nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0); bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
if (changed) { if (changed) {
mPrivateBrowsingId = aUsePrivateBrowsing ? 1 : 0; mPrivateBrowsingId = aUsePrivateBrowsing ? 1 : 0;
@ -2251,6 +2257,8 @@ nsDocShell::SetRemoteTabs(bool aUseRemoteTabs)
NS_IMETHODIMP NS_IMETHODIMP
nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime) nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
bool change = aAffectLifetime != mAffectPrivateSessionLifetime; bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
if (change && UsePrivateBrowsing()) { if (change && UsePrivateBrowsing()) {
AssertOriginAttributesMatchPrivateBrowsing(); AssertOriginAttributesMatchPrivateBrowsing();
@ -2753,6 +2761,8 @@ nsDocShell::GetSecurityUI(nsISecureBrowserUI** aSecurityUI)
NS_IMETHODIMP NS_IMETHODIMP
nsDocShell::SetSecurityUI(nsISecureBrowserUI* aSecurityUI) nsDocShell::SetSecurityUI(nsISecureBrowserUI* aSecurityUI)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
mSecurityUI = aSecurityUI; mSecurityUI = aSecurityUI;
mSecurityUI->SetDocShell(this); mSecurityUI->SetDocShell(this);
return NS_OK; return NS_OK;
@ -3935,6 +3945,10 @@ PrintDocTree(nsIDocShellTreeItem* aParentNode)
NS_IMETHODIMP NS_IMETHODIMP
nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner)
{ {
if (mIsBeingDestroyed && aTreeOwner) {
return NS_ERROR_FAILURE;
}
#ifdef DEBUG_DOCSHELL_FOCUS #ifdef DEBUG_DOCSHELL_FOCUS
nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner)); nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner));
if (item) { if (item) {
@ -5216,6 +5230,8 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
const char* aCSSClass, const char* aCSSClass,
nsIChannel* aFailedChannel) nsIChannel* aFailedChannel)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
#if defined(DEBUG) #if defined(DEBUG)
if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) { if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
nsAutoCString chanName; nsAutoCString chanName;
@ -5571,6 +5587,8 @@ nsDocShell::SetSessionHistory(nsISHistory* aSessionHistory)
// make sure that we are the root docshell and // make sure that we are the root docshell and
// set a handle to root docshell in SH. // set a handle to root docshell in SH.
MOZ_ASSERT(!mIsBeingDestroyed);
nsCOMPtr<nsIDocShellTreeItem> root; nsCOMPtr<nsIDocShellTreeItem> root;
/* Get the root docshell. If *this* is the root docshell /* Get the root docshell. If *this* is the root docshell
* then save a handle to *this* in SH. SH needs it to do * then save a handle to *this* in SH. SH needs it to do
@ -5751,16 +5769,22 @@ nsDocShell::Create()
NS_IMETHODIMP NS_IMETHODIMP
nsDocShell::Destroy() nsDocShell::Destroy()
{ {
// XXX: We allow this function to be called just once. If you are going to
// reset new variables in this function, please make sure the variables will
// never be re-initialized. Adding assertions to check |mIsBeingDestroyed|
// in the setter functions for the variables would be enough.
if (mIsBeingDestroyed) {
return NS_ERROR_DOCSHELL_DYING;
}
NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
"Unexpected item type in docshell"); "Unexpected item type in docshell");
if (!mIsBeingDestroyed) { nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
nsCOMPtr<nsIObserverService> serv = services::GetObserverService(); if (serv) {
if (serv) { const char* msg = mItemType == typeContent ?
const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY; serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
}
} }
mIsBeingDestroyed = true; mIsBeingDestroyed = true;
@ -6043,6 +6067,7 @@ nsDocShell::GetParentWidget(nsIWidget** aParentWidget)
NS_IMETHODIMP NS_IMETHODIMP
nsDocShell::SetParentWidget(nsIWidget* aParentWidget) nsDocShell::SetParentWidget(nsIWidget* aParentWidget)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
mParentWidget = aParentWidget; mParentWidget = aParentWidget;
return NS_OK; return NS_OK;
@ -6313,6 +6338,8 @@ nsDocShell::SetOnePermittedSandboxedNavigator(nsIDocShell* aSandboxedNavigator)
return NS_OK; return NS_OK;
} }
MOZ_ASSERT(!mIsBeingDestroyed);
mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator); mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator);
NS_ASSERTION(mOnePermittedSandboxedNavigator, NS_ASSERTION(mOnePermittedSandboxedNavigator,
"One Permitted Sandboxed Navigator must support weak references."); "One Permitted Sandboxed Navigator must support weak references.");
@ -6673,6 +6700,8 @@ nsDocShell::RefreshURI(nsIURI* aURI,
bool aMetaRefresh, bool aMetaRefresh,
nsIPrincipal* aPrincipal) nsIPrincipal* aPrincipal)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
NS_ENSURE_ARG(aURI); NS_ENSURE_ARG(aURI);
/* Check if Meta refresh/redirects are permitted. Some /* Check if Meta refresh/redirects are permitted. Some
@ -8056,6 +8085,10 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
// wrong information :-( // wrong information :-(
// //
(void)FirePageHideNotification(!mSavingOldViewer); (void)FirePageHideNotification(!mSavingOldViewer);
// pagehide notification might destroy this docshell.
if (mIsBeingDestroyed) {
return NS_ERROR_DOCSHELL_DYING;
}
} }
// Now make sure we don't think we're in the middle of firing unload after // Now make sure we don't think we're in the middle of firing unload after
@ -8199,6 +8232,8 @@ nsDocShell::CanSavePresentation(uint32_t aLoadType,
void void
nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
NS_ASSERTION(!mEditorData, NS_ASSERTION(!mEditorData,
"Why reattach an editor when we already have one?"); "Why reattach an editor when we already have one?");
NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(), NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
@ -8236,6 +8271,9 @@ nsDocShell::DetachEditorFromWindow()
if (NS_SUCCEEDED(res)) { if (NS_SUCCEEDED(res)) {
// Make mOSHE hold the owning ref to the editor data. // Make mOSHE hold the owning ref to the editor data.
if (mOSHE) { if (mOSHE) {
MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
"We should not set the editor data again once after we "
"detached the editor data during destroying this docshell");
mOSHE->SetEditorData(mEditorData.forget()); mOSHE->SetEditorData(mEditorData.forget());
} else { } else {
mEditorData = nullptr; mEditorData = nullptr;
@ -8422,6 +8460,8 @@ nsDocShell::GetRestoringDocument(bool* aRestoring)
nsresult nsresult
nsDocShell::RestorePresentation(nsISHEntry* aSHEntry, bool* aRestoring) nsDocShell::RestorePresentation(nsISHEntry* aSHEntry, bool* aRestoring)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY, NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
"RestorePresentation should only be called for history loads"); "RestorePresentation should only be called for history loads");
@ -8583,6 +8623,10 @@ nsDocShell::RestoreFromHistory()
// Notify the old content viewer that it's being hidden. // Notify the old content viewer that it's being hidden.
FirePageHideNotification(!mSavingOldViewer); FirePageHideNotification(!mSavingOldViewer);
// pagehide notification might destroy this docshell.
if (mIsBeingDestroyed) {
return NS_ERROR_DOCSHELL_DYING;
}
// If mLSHE was changed as a result of the pagehide event, then // If mLSHE was changed as a result of the pagehide event, then
// something else was loaded. Don't finish restoring. // something else was loaded. Don't finish restoring.
@ -9040,6 +9084,12 @@ nsDocShell::CreateContentViewer(const nsACString& aContentType,
{ {
*aContentHandler = nullptr; *aContentHandler = nullptr;
if (!mTreeOwner || mIsBeingDestroyed) {
// If we don't have a tree owner, then we're in the process of being
// destroyed. Rather than continue trying to load something, just give up.
return NS_ERROR_DOCSHELL_DYING;
}
// Can we check the content type of the current content viewer // Can we check the content type of the current content viewer
// and reuse it without destroying it and re-creating it? // and reuse it without destroying it and re-creating it?
@ -9079,6 +9129,11 @@ nsDocShell::CreateContentViewer(const nsACString& aContentType,
aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI)); aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
} }
FirePageHideNotification(!mSavingOldViewer); FirePageHideNotification(!mSavingOldViewer);
if (mIsBeingDestroyed) {
// Force to stop the newly created orphaned viewer.
viewer->Stop();
return NS_ERROR_DOCSHELL_DYING;
}
mLoadingURI = nullptr; mLoadingURI = nullptr;
// Set mFiredUnloadEvent = false so that the unload handler for the // Set mFiredUnloadEvent = false so that the unload handler for the
@ -9268,6 +9323,8 @@ nsDocShell::NewContentViewerObj(const nsACString& aContentType,
nsresult nsresult
nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer) nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
// //
// Copy content viewer state from previous or parent content viewer. // Copy content viewer state from previous or parent content viewer.
// //
@ -13460,6 +13517,8 @@ nsDocShell::EnsureScriptEnvironment()
NS_IMETHODIMP NS_IMETHODIMP
nsDocShell::EnsureEditorData() nsDocShell::EnsureEditorData()
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor(); bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) { if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
// We shouldn't recreate the editor data if it already exists, or // We shouldn't recreate the editor data if it already exists, or
@ -13475,6 +13534,8 @@ nsDocShell::EnsureEditorData()
nsresult nsresult
nsDocShell::EnsureTransferableHookData() nsDocShell::EnsureTransferableHookData()
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
if (!mTransferableHookData) { if (!mTransferableHookData) {
mTransferableHookData = new nsTransferableHookData(); mTransferableHookData = new nsTransferableHookData();
} }
@ -14562,6 +14623,8 @@ nsDocShell::CanSetOriginAttributes()
nsresult nsresult
nsDocShell::SetOriginAttributes(const DocShellOriginAttributes& aAttrs) nsDocShell::SetOriginAttributes(const DocShellOriginAttributes& aAttrs)
{ {
MOZ_ASSERT(!mIsBeingDestroyed);
if (!CanSetOriginAttributes()) { if (!CanSetOriginAttributes()) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }

View file

@ -80,7 +80,14 @@ DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
RefPtr<DOMIntersectionObserver> observer = RefPtr<DOMIntersectionObserver> observer =
new DOMIntersectionObserver(window.forget(), aCb); new DOMIntersectionObserver(window.forget(), aCb);
observer->mRoot = aOptions.mRoot; if (!aOptions.mRoot.IsNull()) {
if (aOptions.mRoot.Value().IsElement()) {
observer->mRoot = aOptions.mRoot.Value().GetAsElement();
} else {
MOZ_ASSERT(aOptions.mRoot.Value().IsDocument());
observer->mRoot = aOptions.mRoot.Value().GetAsDocument();
}
}
if (!observer->SetRootMargin(aOptions.mRootMargin)) { if (!observer->SetRootMargin(aOptions.mRootMargin)) {
aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR, aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR,
@ -258,6 +265,30 @@ EdgeInclusiveIntersection(const nsRect& aRect, const nsRect& aOtherRect)
return Some(nsRect(left, top, right - left, bottom - top)); return Some(nsRect(left, top, right - left, bottom - top));
} }
// NOTE: This returns nullptr if |aDocument| is in a cross process.
static nsIDocument* GetTopLevelDocument(const nsIDocument& aDocument) {
nsCOMPtr<nsIPresShell> presShell = aDocument.GetShell();
if (presShell) {
nsIFrame* rootFrame = presShell->GetRootScrollFrame();
if (rootFrame) {
nsPresContext* presContext = rootFrame->PresContext();
while (!presContext->IsRootContentDocument()) {
// Walk up the tree
presContext = presContext->GetParentPresContext();
if (!presContext) {
break;
}
}
if(presContext && presContext->IsRootContentDocument()) {
return presContext->Document();
}
}
}
return nullptr;
}
enum class BrowsingContextInfo { enum class BrowsingContextInfo {
SimilarOriginBrowsingContext, SimilarOriginBrowsingContext,
DifferentOriginBrowsingContext, DifferentOriginBrowsingContext,
@ -267,14 +298,12 @@ enum class BrowsingContextInfo {
void void
DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time) DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time)
{ {
Element* root = nullptr; nsINode* root = mRoot;
nsIFrame* rootFrame = nullptr; nsIFrame* rootFrame = nullptr;
nsRect rootRect; nsRect rootRect;
if (mRoot) { if (mRoot && mRoot->IsElement()) {
root = mRoot; if ((rootFrame = mRoot->AsElement()->GetPrimaryFrame())) {
rootFrame = root->GetPrimaryFrame();
if (rootFrame) {
nsRect rootRectRelativeToRootFrame; nsRect rootRectRelativeToRootFrame;
if (rootFrame->GetType() == nsGkAtoms::scrollFrame) { if (rootFrame->GetType() == nsGkAtoms::scrollFrame) {
// rootRectRelativeToRootFrame should be the content rect of rootFrame, not including the scrollbars. // rootRectRelativeToRootFrame should be the content rect of rootFrame, not including the scrollbars.
@ -292,30 +321,18 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time
containingBlock); containingBlock);
} }
} else { } else {
nsCOMPtr<nsIPresShell> presShell = aDocument->GetShell(); MOZ_ASSERT(!mRoot || mRoot->IsInUncomposedDoc());
if (presShell) { nsIDocument* rootDocument =
rootFrame = presShell->GetRootScrollFrame(); mRoot ? mRoot->GetUncomposedDoc() : GetTopLevelDocument(*aDocument);
if (rootFrame) { if (rootDocument) {
nsPresContext* presContext = rootFrame->PresContext(); if (nsIPresShell* presShell = rootDocument->GetShell()) {
while (!presContext->IsRootContentDocument()) { rootFrame = presShell->GetRootScrollFrame();
// Walk up the tree if (rootFrame) {
presContext = presContext->GetParentPresContext(); root = rootFrame->GetContent()->AsElement();
if (!presContext) { nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame);
break; if (scrollFrame) {
rootRect = scrollFrame->GetScrollPortRect();
} }
nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame();
if (rootScrollFrame) {
rootFrame = rootScrollFrame;
} else {
break;
}
}
root = rootFrame->GetContent()->AsElement();
nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame);
// If we end up with a null root frame for some reason, we'll proceed
// with an empty root intersection rect.
if (scrollFrame) {
rootRect = scrollFrame->GetScrollPortRect();
} }
} }
} }

View file

@ -145,7 +145,7 @@ public:
return mOwner; return mOwner;
} }
Element* GetRoot() const { nsINode* GetRoot() const {
return mRoot; return mRoot;
} }
@ -178,7 +178,7 @@ protected:
nsCOMPtr<nsPIDOMWindowInner> mOwner; nsCOMPtr<nsPIDOMWindowInner> mOwner;
RefPtr<nsIDocument> mDocument; RefPtr<nsIDocument> mDocument;
RefPtr<mozilla::dom::IntersectionCallback> mCallback; RefPtr<mozilla::dom::IntersectionCallback> mCallback;
RefPtr<Element> mRoot; RefPtr<nsINode> mRoot;
nsCSSRect mRootMargin; nsCSSRect mRootMargin;
nsTArray<double> mThresholds; nsTArray<double> mThresholds;

View file

@ -100,6 +100,7 @@ nsContentSink::nsContentSink()
, mDeferredFlushTags(0) , mDeferredFlushTags(0)
, mIsDocumentObserver(0) , mIsDocumentObserver(0)
, mRunsToCompletion(0) , mRunsToCompletion(0)
, mIsBlockingOnload(false)
, mDeflectedCount(0) , mDeflectedCount(0)
, mHasPendingEvent(false) , mHasPendingEvent(false)
, mCurrentParseEndTime(0) , mCurrentParseEndTime(0)
@ -1539,8 +1540,14 @@ nsContentSink::DropParserAndPerfHint(void)
FavorPerformanceHint(true, 0); FavorPerformanceHint(true, 0);
} }
if (!mRunsToCompletion) { // Call UnblockOnload only if mRunsToComletion is false and if
// we have already started loading because it's possible that this function
// is called (i.e. the parser is terminated) before we start loading due to
// destroying the window inside unload event callbacks for the previous
// document.
if (!mRunsToCompletion && mIsBlockingOnload) {
mDocument->UnblockOnload(true); mDocument->UnblockOnload(true);
mIsBlockingOnload = false;
} }
} }
@ -1595,6 +1602,7 @@ nsContentSink::WillBuildModelImpl()
{ {
if (!mRunsToCompletion) { if (!mRunsToCompletion) {
mDocument->BlockOnload(); mDocument->BlockOnload();
mIsBlockingOnload = true;
mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow()); mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow());
} }

View file

@ -304,6 +304,8 @@ protected:
// True if this is parser is a fragment parser or an HTML DOMParser. // True if this is parser is a fragment parser or an HTML DOMParser.
// XML DOMParser leaves this to false for now! // XML DOMParser leaves this to false for now!
uint8_t mRunsToCompletion : 1; uint8_t mRunsToCompletion : 1;
// True if we are blocking load event.
bool mIsBlockingOnload : 1;
// //
// -- Can interrupt parsing members -- // -- Can interrupt parsing members --

View file

@ -5444,6 +5444,14 @@ nsContentUtils::AddPendingIDBTransaction(already_AddRefed<nsIRunnable> aTransact
CycleCollectedJSContext::Get()->AddPendingIDBTransaction(Move(aTransaction)); CycleCollectedJSContext::Get()->AddPendingIDBTransaction(Move(aTransaction));
} }
/* static */
bool
nsContentUtils::IsInStableOrMetaStableState()
{
MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
}
/* /*
* Helper function for nsContentUtils::ProcessViewportInfo. * Helper function for nsContentUtils::ProcessViewportInfo.
* *

View file

@ -1779,6 +1779,11 @@ public:
*/ */
static void AddPendingIDBTransaction(already_AddRefed<nsIRunnable> aTransaction); static void AddPendingIDBTransaction(already_AddRefed<nsIRunnable> aTransaction);
/**
* Returns true if we are doing StableState/MetastableState.
*/
static bool IsInStableOrMetaStableState();
/* Process viewport META data. This gives us information for the scale /* Process viewport META data. This gives us information for the scale
* and zoom of a page on mobile devices. We stick the information in * and zoom of a page on mobile devices. We stick the information in
* the document header and use it later on after rendering. * the document header and use it later on after rendering.

View file

@ -269,6 +269,7 @@ nsRange::nsRange(nsINode* aNode)
, mStartOffsetWasIncremented(false) , mStartOffsetWasIncremented(false)
, mEndOffsetWasIncremented(false) , mEndOffsetWasIncremented(false)
, mEnableGravitationOnElementRemoval(true) , mEnableGravitationOnElementRemoval(true)
, mCalledByJS(false)
#ifdef DEBUG #ifdef DEBUG
, mAssertNextInsertOrAppendIndex(-1) , mAssertNextInsertOrAppendIndex(-1)
, mAssertNextInsertOrAppendNode(nullptr) , mAssertNextInsertOrAppendNode(nullptr)
@ -997,7 +998,16 @@ nsRange::DoSetRange(nsINode* aStartN, uint32_t aStartOffset,
// Notify any selection listeners. This has to occur last because otherwise the world // Notify any selection listeners. This has to occur last because otherwise the world
// could be observed by a selection listener while the range was in an invalid state. // could be observed by a selection listener while the range was in an invalid state.
if (mSelection) { if (mSelection) {
mSelection->NotifySelectionListeners(); // Our internal code should not move focus with using this instance while
// it's calling Selection::NotifySelectionListeners() which may move focus
// or calls selection listeners. So, let's set mCalledByJS to false here
// since non-*JS() methods don't set it to false.
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = false;
// Be aware, this range may be modified or stop being a range for selection
// after this call. Additionally, the selection instance may have gone.
RefPtr<Selection> selection = mSelection;
selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
} }
} }
@ -1224,6 +1234,14 @@ nsRange::IsValidBoundary(nsINode* aNode)
return root; return root;
} }
void
nsRange::SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetStart(aNode, aOffset, aErr);
}
void void
nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv) nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
{ {
@ -1279,6 +1297,14 @@ nsRange::SetStart(nsINode* aParent, uint32_t aOffset)
return NS_OK; return NS_OK;
} }
void
nsRange::SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetStartBefore(aNode, aErr);
}
void void
nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv) nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv)
{ {
@ -1310,6 +1336,14 @@ nsRange::SetStartBefore(nsIDOMNode* aSibling)
return rv.StealNSResult(); return rv.StealNSResult();
} }
void
nsRange::SetStartAfterJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetStartAfter(aNode, aErr);
}
void void
nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv) nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv)
{ {
@ -1341,6 +1375,14 @@ nsRange::SetStartAfter(nsIDOMNode* aSibling)
return rv.StealNSResult(); return rv.StealNSResult();
} }
void
nsRange::SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetEnd(aNode, aOffset, aErr);
}
void void
nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv) nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
{ {
@ -1455,6 +1497,14 @@ nsRange::SetStartAndEnd(nsINode* aStartParent, uint32_t aStartOffset,
return NS_OK; return NS_OK;
} }
void
nsRange::SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetEndBefore(aNode, aErr);
}
void void
nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv) nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv)
{ {
@ -1486,6 +1536,14 @@ nsRange::SetEndBefore(nsIDOMNode* aSibling)
return rv.StealNSResult(); return rv.StealNSResult();
} }
void
nsRange::SetEndAfterJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetEndAfter(aNode, aErr);
}
void void
nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv) nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv)
{ {
@ -1532,6 +1590,14 @@ nsRange::Collapse(bool aToStart)
return NS_OK; return NS_OK;
} }
void
nsRange::CollapseJS(bool aToStart)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
Unused << Collapse(aToStart);
}
NS_IMETHODIMP NS_IMETHODIMP
nsRange::SelectNode(nsIDOMNode* aN) nsRange::SelectNode(nsIDOMNode* aN)
{ {
@ -1543,6 +1609,14 @@ nsRange::SelectNode(nsIDOMNode* aN)
return rv.StealNSResult(); return rv.StealNSResult();
} }
void
nsRange::SelectNodeJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SelectNode(aNode, aErr);
}
void void
nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv) nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv)
{ {
@ -1582,6 +1656,14 @@ nsRange::SelectNodeContents(nsIDOMNode* aN)
return rv.StealNSResult(); return rv.StealNSResult();
} }
void
nsRange::SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SelectNodeContents(aNode, aErr);
}
void void
nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv) nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv)
{ {

View file

@ -20,6 +20,7 @@
#include "nsStubMutationObserver.h" #include "nsStubMutationObserver.h"
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
namespace mozilla { namespace mozilla {
class ErrorResult; class ErrorResult;
@ -256,14 +257,20 @@ public:
void InsertNode(nsINode& aNode, ErrorResult& aErr); void InsertNode(nsINode& aNode, ErrorResult& aErr);
bool IntersectsNode(nsINode& aNode, ErrorResult& aRv); bool IntersectsNode(nsINode& aNode, ErrorResult& aRv);
bool IsPointInRange(nsINode& aParent, uint32_t aOffset, ErrorResult& aErr); bool IsPointInRange(nsINode& aParent, uint32_t aOffset, ErrorResult& aErr);
void SelectNode(nsINode& aNode, ErrorResult& aErr);
void SelectNodeContents(nsINode& aNode, ErrorResult& aErr); // *JS() methods are mapped to Range.*() of DOM.
void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr); // They may move focus only when the range represents normal selection.
void SetEndAfter(nsINode& aNode, ErrorResult& aErr); // These methods shouldn't be used from internal.
void SetEndBefore(nsINode& aNode, ErrorResult& aErr); void CollapseJS(bool aToStart);
void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr); void SelectNodeJS(nsINode& aNode, ErrorResult& aErr);
void SetStartAfter(nsINode& aNode, ErrorResult& aErr); void SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr);
void SetStartBefore(nsINode& aNode, ErrorResult& aErr); void SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
void SetEndAfterJS(nsINode& aNode, ErrorResult& aErr);
void SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr);
void SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
void SetStartAfterJS(nsINode& aNode, ErrorResult& aErr);
void SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr);
void SurroundContents(nsINode& aNode, ErrorResult& aErr); void SurroundContents(nsINode& aNode, ErrorResult& aErr);
already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true, already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true,
bool aFlushLayout = true); bool aFlushLayout = true);
@ -272,6 +279,17 @@ public:
void GetClientRectsAndTexts( void GetClientRectsAndTexts(
mozilla::dom::ClientRectsAndTexts& aResult, mozilla::dom::ClientRectsAndTexts& aResult,
ErrorResult& aErr); ErrorResult& aErr);
// Following methods should be used for internal use instead of *JS().
void SelectNode(nsINode& aNode, ErrorResult& aErr);
void SelectNodeContents(nsINode& aNode, ErrorResult& aErr);
void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
void SetEndAfter(nsINode& aNode, ErrorResult& aErr);
void SetEndBefore(nsINode& aNode, ErrorResult& aErr);
void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
void SetStartAfter(nsINode& aNode, ErrorResult& aErr);
void SetStartBefore(nsINode& aNode, ErrorResult& aErr);
static void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue, static void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue,
mozilla::ErrorResult& aError, mozilla::ErrorResult& aError,
nsIContent* aStartParent, nsIContent* aStartParent,
@ -392,6 +410,31 @@ protected:
size_t aRangeStart, size_t aRangeStart,
size_t aRangeEnd); size_t aRangeEnd);
// Assume that this is guaranteed that this is held by the caller when
// this is used. (Note that we cannot use AutoRestore for mCalledByJS
// due to a bit field.)
class MOZ_RAII AutoCalledByJSRestore final
{
private:
nsRange& mRange;
bool mOldValue;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
explicit AutoCalledByJSRestore(nsRange& aRange
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mRange(aRange)
, mOldValue(aRange.mCalledByJS)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoCalledByJSRestore()
{
mRange.mCalledByJS = mOldValue;
}
bool SavedValue() const { return mOldValue; }
};
struct MOZ_STACK_CLASS AutoInvalidateSelection struct MOZ_STACK_CLASS AutoInvalidateSelection
{ {
explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange) explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange)
@ -428,6 +471,7 @@ protected:
bool mStartOffsetWasIncremented : 1; bool mStartOffsetWasIncremented : 1;
bool mEndOffsetWasIncremented : 1; bool mEndOffsetWasIncremented : 1;
bool mEnableGravitationOnElementRemoval : 1; bool mEnableGravitationOnElementRemoval : 1;
bool mCalledByJS : 1;
#ifdef DEBUG #ifdef DEBUG
int32_t mAssertNextInsertOrAppendIndex; int32_t mAssertNextInsertOrAppendIndex;
nsINode* mAssertNextInsertOrAppendNode; nsINode* mAssertNextInsertOrAppendNode;

View file

@ -693,6 +693,13 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
NS_ENSURE_TRUE(aEvent->mMessage || !aDOMEvent || aTargets, NS_ENSURE_TRUE(aEvent->mMessage || !aDOMEvent || aTargets,
NS_ERROR_DOM_INVALID_STATE_ERR); NS_ERROR_DOM_INVALID_STATE_ERR);
// Events shall not be fired while we are in stable state to prevent anything
// visible from the scripts.
// See comment in CycleCollectedJSContext::AfterProcessMicrotasks for why we allow it anyway.
// MOZ_ASSERT(!nsContentUtils::IsInStableOrMetaStableState());
// NS_ENSURE_TRUE(!nsContentUtils::IsInStableOrMetaStableState(),
// NS_ERROR_DOM_INVALID_STATE_ERR);
#ifdef MOZ_TASK_TRACER #ifdef MOZ_TASK_TRACER
{ {
if (aDOMEvent) { if (aDOMEvent) {

View file

@ -3105,6 +3105,26 @@ HTMLInputElement::GetFiles()
return mFileList; return mFileList;
} }
void
HTMLInputElement::SetFiles(FileList* aFiles)
{
if (mType != NS_FORM_INPUT_FILE || !aFiles) {
return;
}
// Clear |mFileList| to omit |UpdateFileList|
if (mFileList) {
mFileList->Clear();
mFileList = nullptr;
}
// Update |mFilesOrDirectories|
SetFiles(aFiles, true);
// Update |mFileList| without copy
mFileList = aFiles;
}
/* static */ void /* static */ void
HTMLInputElement::HandleNumberControlSpin(void* aData) HTMLInputElement::HandleNumberControlSpin(void* aData)
{ {

View file

@ -474,6 +474,7 @@ public:
// XPCOM GetForm() is OK // XPCOM GetForm() is OK
FileList* GetFiles(); FileList* GetFiles();
void SetFiles(FileList* aFiles);
// XPCOM GetFormAction() is OK // XPCOM GetFormAction() is OK
void SetFormAction(const nsAString& aValue, ErrorResult& aRv) void SetFormAction(const nsAString& aValue, ErrorResult& aRv)

View file

@ -53,6 +53,7 @@
#include "MediaDecoder.h" #include "MediaDecoder.h"
#include "MediaPrefs.h" #include "MediaPrefs.h"
#include "MediaResource.h" #include "MediaResource.h"
#include "MediaShutdownManager.h"
#include "nsICategoryManager.h" #include "nsICategoryManager.h"
#include "nsIContentPolicy.h" #include "nsIContentPolicy.h"
@ -3203,6 +3204,13 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
NotifyOwnerDocumentActivityChanged(); NotifyOwnerDocumentActivityChanged();
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
// We initialize the MediaShutdownManager as the HTMLMediaElement is always
// constructed on the main thread, and not during stable state.
// (MediaShutdownManager make use of nsIAsyncShutdownClient which is written
// in JS)
MediaShutdownManager::InitStatics();
mWatchManager.Watch(mDownloadSuspendedByCache, &HTMLMediaElement::UpdateReadyStateInternal); mWatchManager.Watch(mDownloadSuspendedByCache, &HTMLMediaElement::UpdateReadyStateInternal);
// Paradoxically, there is a self-edge whereby UpdateReadyStateInternal refuses // Paradoxically, there is a self-edge whereby UpdateReadyStateInternal refuses
// to run until mReadyState reaches at least HAVE_METADATA by some other means. // to run until mReadyState reaches at least HAVE_METADATA by some other means.

View file

@ -264,14 +264,16 @@ void
HTMLTrackElement::DispatchLoadResource() HTMLTrackElement::DispatchLoadResource()
{ {
if (!mLoadResourceDispatched) { if (!mLoadResourceDispatched) {
RefPtr<Runnable> r = NewRunnableMethod(this, &HTMLTrackElement::LoadResource); RefPtr<WebVTTListener> listener = new WebVTTListener(this);
RefPtr<Runnable> r = NewRunnableMethod<RefPtr<WebVTTListener>>(this,
&HTMLTrackElement::LoadResource, std::move(listener));
nsContentUtils::RunInStableState(r.forget()); nsContentUtils::RunInStableState(r.forget());
mLoadResourceDispatched = true; mLoadResourceDispatched = true;
} }
} }
void void
HTMLTrackElement::LoadResource() HTMLTrackElement::LoadResource(RefPtr<WebVTTListener>&& aWebVTTListener)
{ {
mLoadResourceDispatched = false; mLoadResourceDispatched = false;
@ -317,33 +319,46 @@ HTMLTrackElement::LoadResource()
} }
} }
nsCOMPtr<nsIChannel> channel; mListener = std::move(aWebVTTListener);
nsCOMPtr<nsILoadGroup> loadGroup = OwnerDoc()->GetDocumentLoadGroup(); // This will do 6. Set the text track readiness state to loading.
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
static_cast<Element*>(this),
secFlags,
nsIContentPolicy::TYPE_INTERNAL_TRACK,
loadGroup,
nullptr, // aCallbacks
nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI);
NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
mListener = new WebVTTListener(this);
rv = mListener->LoadResource(); rv = mListener->LoadResource();
NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv)); NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
channel->SetNotificationCallbacks(mListener);
LOG(LogLevel::Debug, ("opening webvtt channel")); nsIDocument* doc = OwnerDoc();
rv = channel->AsyncOpen2(mListener); if (!doc) {
if (NS_FAILED(rv)) {
SetReadyState(TextTrackReadyState::FailedToLoad);
return; return;
} }
mChannel = channel; // 9. End the synchronous section, continuing the remaining steps in parallel.
NS_DispatchToMainThread(NS_NewRunnableFunction(
[ self = RefPtr<HTMLTrackElement>(this), uri, secFlags ]() {
if (!self->mListener) {
// Shutdown got called, abort.
return;
}
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsILoadGroup> loadGroup = self->OwnerDoc()->GetDocumentLoadGroup();
nsresult rv = NS_NewChannel(getter_AddRefs(channel),
uri,
static_cast<Element*>(self),
secFlags,
nsIContentPolicy::TYPE_INTERNAL_TRACK,
loadGroup,
nullptr, // aCallbacks
nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI);
NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
channel->SetNotificationCallbacks(self->mListener);
LOG(LogLevel::Debug, ("opening webvtt channel"));
rv = channel->AsyncOpen2(self->mListener);
if (NS_FAILED(rv)) {
self->SetReadyState(TextTrackReadyState::FailedToLoad);
return;
}
self->mChannel = channel;
}));
} }
nsresult nsresult

View file

@ -118,9 +118,6 @@ protected:
virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void OnChannelRedirect(nsIChannel* aChannel, nsIChannel* aNewChannel, void OnChannelRedirect(nsIChannel* aChannel, nsIChannel* aNewChannel,
uint32_t aFlags); uint32_t aFlags);
// Open a new channel to the HTMLTrackElement's src attribute and call
// mListener's LoadResource().
void LoadResource();
friend class TextTrackCue; friend class TextTrackCue;
friend class WebVTTListener; friend class WebVTTListener;
@ -134,6 +131,9 @@ protected:
private: private:
void DispatchLoadResource(); void DispatchLoadResource();
// Open a new channel to the HTMLTrackElement's src attribute and call
// mListener's LoadResource().
void LoadResource(RefPtr<WebVTTListener>&& aWebVTTListener);
bool mLoadResourceDispatched; bool mLoadResourceDispatched;
RefPtr<WindowDestroyObserver> mWindowDestroyObserver; RefPtr<WindowDestroyObserver> mWindowDestroyObserver;

View file

@ -602,6 +602,17 @@ void
TextTrackManager::TimeMarchesOn() TextTrackManager::TimeMarchesOn()
{ {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
if (context && context->IsInStableOrMetaStableState()) {
// FireTimeUpdate can be called while at stable state following a
// current position change which triggered a state watcher in MediaDecoder
// (see bug 1443429).
// TimeMarchesOn() will modify JS attributes which is forbidden while in
// stable state. So we dispatch a task to perform such operation later
// instead.
DispatchTimeMarchesOn();
return;
}
WEBVTT_LOG("TimeMarchesOn"); WEBVTT_LOG("TimeMarchesOn");
mTimeMarchesOnDispatched = false; mTimeMarchesOnDispatched = false;

View file

@ -453,8 +453,6 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
mWatchManager.Watch(mLogicallySeeking, &MediaDecoder::SeekingChanged); mWatchManager.Watch(mLogicallySeeking, &MediaDecoder::SeekingChanged);
mWatchManager.Watch(mIsAudioDataAudible, &MediaDecoder::NotifyAudibleStateChanged); mWatchManager.Watch(mIsAudioDataAudible, &MediaDecoder::NotifyAudibleStateChanged);
MediaShutdownManager::InitStatics();
} }
#undef INIT_MIRROR #undef INIT_MIRROR

View file

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WebVTTListener.h" #include "WebVTTListener.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/dom/TextTrackCue.h" #include "mozilla/dom/TextTrackCue.h"
#include "mozilla/dom/TextTrackRegion.h" #include "mozilla/dom/TextTrackRegion.h"
#include "mozilla/dom/VTTRegionBinding.h" #include "mozilla/dom/VTTRegionBinding.h"
@ -34,9 +35,22 @@ LazyLogModule gTextTrackLog("TextTrack");
WebVTTListener::WebVTTListener(HTMLTrackElement* aElement) WebVTTListener::WebVTTListener(HTMLTrackElement* aElement)
: mElement(aElement) : mElement(aElement)
, mParserWrapperError(NS_OK)
{ {
MOZ_ASSERT(mElement, "Must pass an element to the callback"); MOZ_ASSERT(mElement, "Must pass an element to the callback");
VTT_LOG("WebVTTListener created."); VTT_LOG("WebVTTListener created.");
MOZ_DIAGNOSTIC_ASSERT(
CycleCollectedJSContext::Get() &&
!CycleCollectedJSContext::Get()->IsInStableOrMetaStableState());
mParserWrapper = do_CreateInstance(NS_WEBVTTPARSERWRAPPER_CONTRACTID,
&mParserWrapperError);
if (NS_SUCCEEDED(mParserWrapperError)) {
nsPIDOMWindowInner* window = mElement->OwnerDoc()->GetInnerWindow();
mParserWrapperError = mParserWrapper->LoadParser(window);
}
if (NS_SUCCEEDED(mParserWrapperError)) {
mParserWrapperError = mParserWrapper->Watch(this);
}
} }
WebVTTListener::~WebVTTListener() WebVTTListener::~WebVTTListener()
@ -54,16 +68,8 @@ WebVTTListener::GetInterface(const nsIID &aIID,
nsresult nsresult
WebVTTListener::LoadResource() WebVTTListener::LoadResource()
{ {
nsresult rv; // Exit if we failed to create the WebVTTParserWrapper (vtt.jsm)
mParserWrapper = do_CreateInstance(NS_WEBVTTPARSERWRAPPER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(mParserWrapperError, mParserWrapperError);
NS_ENSURE_SUCCESS(rv, rv);
nsPIDOMWindowInner* window = mElement->OwnerDoc()->GetInnerWindow();
rv = mParserWrapper->LoadParser(window);
NS_ENSURE_SUCCESS(rv, rv);
rv = mParserWrapper->Watch(this);
NS_ENSURE_SUCCESS(rv, rv);
mElement->SetReadyState(TextTrackReadyState::Loading); mElement->SetReadyState(TextTrackReadyState::Loading);
return NS_OK; return NS_OK;

View file

@ -57,6 +57,7 @@ private:
RefPtr<HTMLTrackElement> mElement; RefPtr<HTMLTrackElement> mElement;
nsCOMPtr<nsIWebVTTParserWrapper> mParserWrapper; nsCOMPtr<nsIWebVTTParserWrapper> mParserWrapper;
nsresult mParserWrapperError;
}; };
} // namespace dom } // namespace dom

View file

@ -59,15 +59,30 @@ public:
switch (event) { switch (event) {
case MediaStreamGraphEvent::EVENT_FINISHED: case MediaStreamGraphEvent::EVENT_FINISHED:
{ {
RefPtr<SynthStreamListener> self = this;
if (!mStarted) { if (!mStarted) {
mStarted = true; mStarted = true;
nsCOMPtr<nsIRunnable> startRunnable = nsCOMPtr<nsIRunnable> startRunnable = NS_NewRunnableFunction(
NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted); [self] {
// "start" event will be fired in DoNotifyStarted() which is
// not allowed in stable state, so we do it asynchronously in
// next run.
NS_DispatchToMainThread(NewRunnableMethod(
self,
&SynthStreamListener::DoNotifyStarted));
});
aGraph->DispatchToMainThreadAfterStreamStateUpdate(startRunnable.forget()); aGraph->DispatchToMainThreadAfterStreamStateUpdate(startRunnable.forget());
} }
nsCOMPtr<nsIRunnable> endRunnable = nsCOMPtr<nsIRunnable> endRunnable = NS_NewRunnableFunction(
NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished); [self] {
// "end" event will be fired in DoNotifyFinished() which is
// not allowed in stable state, so we do it asynchronously in
// next run.
NS_DispatchToMainThread(NewRunnableMethod(
self,
&SynthStreamListener::DoNotifyFinished));
});
aGraph->DispatchToMainThreadAfterStreamStateUpdate(endRunnable.forget()); aGraph->DispatchToMainThreadAfterStreamStateUpdate(endRunnable.forget());
} }
break; break;
@ -85,8 +100,16 @@ public:
{ {
if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) { if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) {
mStarted = true; mStarted = true;
nsCOMPtr<nsIRunnable> event = RefPtr<SynthStreamListener> self = this;
NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted); nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
[self] {
// "start" event will be fired in DoNotifyStarted() which is
// not allowed in stable state, so we do it asynchronously in
// next run.
NS_DispatchToMainThread(NewRunnableMethod(
self,
&SynthStreamListener::DoNotifyStarted));
});
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
} }
} }

View file

@ -40,7 +40,7 @@ interface HTMLInputElement : HTMLElement {
attribute boolean disabled; attribute boolean disabled;
readonly attribute HTMLFormElement? form; readonly attribute HTMLFormElement? form;
[Pure] [Pure]
readonly attribute FileList? files; attribute FileList? files;
[CEReactions, Pure, SetterThrows] [CEReactions, Pure, SetterThrows]
attribute DOMString formAction; attribute DOMString formAction;
[CEReactions, Pure, SetterThrows] [CEReactions, Pure, SetterThrows]

View file

@ -30,7 +30,7 @@ interface IntersectionObserverEntry {
Pref="dom.intersectionObserver.enabled"] Pref="dom.intersectionObserver.enabled"]
interface IntersectionObserver { interface IntersectionObserver {
[Constant] [Constant]
readonly attribute Element? root; readonly attribute Node? root;
[Constant] [Constant]
readonly attribute DOMString rootMargin; readonly attribute DOMString rootMargin;
[Constant,Cached] [Constant,Cached]
@ -56,7 +56,7 @@ dictionary IntersectionObserverEntryInit {
}; };
dictionary IntersectionObserverInit { dictionary IntersectionObserverInit {
Element? root = null; (Element or Document)? root = null;
DOMString rootMargin = "0px"; DOMString rootMargin = "0px";
(double or sequence<double>) threshold = 0; (double or sequence<double>) threshold = 0;
}; };

View file

@ -26,22 +26,23 @@ interface Range {
[Throws] [Throws]
readonly attribute Node commonAncestorContainer; readonly attribute Node commonAncestorContainer;
[Throws] [Throws, BinaryName="setStartJS"]
void setStart(Node refNode, unsigned long offset); void setStart(Node refNode, unsigned long offset);
[Throws] [Throws, BinaryName="setEndJS"]
void setEnd(Node refNode, unsigned long offset); void setEnd(Node refNode, unsigned long offset);
[Throws] [Throws, BinaryName="setStartBeforeJS"]
void setStartBefore(Node refNode); void setStartBefore(Node refNode);
[Throws] [Throws, BinaryName="setStartAfterJS"]
void setStartAfter(Node refNode); void setStartAfter(Node refNode);
[Throws] [Throws, BinaryName="setEndBeforeJS"]
void setEndBefore(Node refNode); void setEndBefore(Node refNode);
[Throws] [Throws, BinaryName="setEndAfterJS"]
void setEndAfter(Node refNode); void setEndAfter(Node refNode);
[BinaryName="collapseJS"]
void collapse(optional boolean toStart = false); void collapse(optional boolean toStart = false);
[Throws] [Throws, BinaryName="selectNodeJS"]
void selectNode(Node refNode); void selectNode(Node refNode);
[Throws] [Throws, BinaryName="selectNodeContentsJS"]
void selectNodeContents(Node refNode); void selectNodeContents(Node refNode);
const unsigned short START_TO_START = 0; const unsigned short START_TO_START = 0;

View file

@ -17,17 +17,17 @@ interface Selection {
readonly attribute unsigned long focusOffset; readonly attribute unsigned long focusOffset;
readonly attribute boolean isCollapsed; readonly attribute boolean isCollapsed;
[Throws] [Throws, BinaryName="collapseJS"]
void collapse(Node node, unsigned long offset); void collapse(Node node, unsigned long offset);
[Throws] [Throws, BinaryName="collapseToStartJS"]
void collapseToStart(); void collapseToStart();
[Throws] [Throws, BinaryName="collapseToEndJS"]
void collapseToEnd(); void collapseToEnd();
[Throws] [Throws, BinaryName="extendJS"]
void extend(Node node, unsigned long offset); void extend(Node node, unsigned long offset);
[Throws] [Throws, BinaryName="selectAllChildrenJS"]
void selectAllChildren(Node node); void selectAllChildren(Node node);
[Throws] [Throws]
void deleteFromDocument(); void deleteFromDocument();
@ -36,7 +36,7 @@ interface Selection {
readonly attribute DOMString type; readonly attribute DOMString type;
[Throws] [Throws]
Range getRangeAt(unsigned long index); Range getRangeAt(unsigned long index);
[Throws] [Throws, BinaryName="addRangeJS"]
void addRange(Range range); void addRange(Range range);
[Throws] [Throws]
void removeRange(Range range); void removeRange(Range range);
@ -46,7 +46,7 @@ interface Selection {
[Throws] [Throws]
boolean containsNode(Node node, boolean allowPartialContainment); boolean containsNode(Node node, boolean allowPartialContainment);
[Throws] [Throws, BinaryName="setBaseAndExtentJS"]
void setBaseAndExtent(Node anchorNode, void setBaseAndExtent(Node anchorNode,
unsigned long anchorOffset, unsigned long anchorOffset,
Node focusNode, Node focusNode,

View file

@ -3082,6 +3082,8 @@ void
ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration) ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
AutoTArray<nsCOMPtr<nsIDocument>, 16> documents;
for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) { for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
if (iter.UserData() != aRegistration) { if (iter.UserData() != aRegistration) {
continue; continue;
@ -3092,6 +3094,12 @@ ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegis
continue; continue;
} }
documents.AppendElement(doc);
}
// Fire event after iterating mControlledDocuments is done to prevent
// modification by reentering from the event handlers during iteration.
for (auto& doc : documents) {
FireControllerChangeOnDocument(doc); FireControllerChangeOnDocument(doc);
} }
} }

View file

@ -26,6 +26,7 @@ WORKER_SIMPLE_PREF("browser.dom.window.dump.enabled", DumpEnabled, DUMP)
WORKER_SIMPLE_PREF("canvas.imagebitmap_extensions.enabled", ImageBitmapExtensionsEnabled, IMAGEBITMAP_EXTENSIONS_ENABLED) WORKER_SIMPLE_PREF("canvas.imagebitmap_extensions.enabled", ImageBitmapExtensionsEnabled, IMAGEBITMAP_EXTENSIONS_ENABLED)
WORKER_SIMPLE_PREF("dom.caches.enabled", DOMCachesEnabled, DOM_CACHES) WORKER_SIMPLE_PREF("dom.caches.enabled", DOMCachesEnabled, DOM_CACHES)
WORKER_SIMPLE_PREF("dom.caches.testing.enabled", DOMCachesTestingEnabled, DOM_CACHES_TESTING) WORKER_SIMPLE_PREF("dom.caches.testing.enabled", DOMCachesTestingEnabled, DOM_CACHES_TESTING)
WORKER_SIMPLE_PREF("dom.min_timeout_value", DOMMinTimeoutValue, DOM_MIN_TIMEOUT_VALUE)
WORKER_SIMPLE_PREF("dom.performance.enable_user_timing_logging", PerformanceLoggingEnabled, PERFORMANCE_LOGGING_ENABLED) WORKER_SIMPLE_PREF("dom.performance.enable_user_timing_logging", PerformanceLoggingEnabled, PERFORMANCE_LOGGING_ENABLED)
WORKER_SIMPLE_PREF("dom.webnotifications.enabled", DOMWorkerNotificationEnabled, DOM_WORKERNOTIFICATION) WORKER_SIMPLE_PREF("dom.webnotifications.enabled", DOMWorkerNotificationEnabled, DOM_WORKERNOTIFICATION)
WORKER_SIMPLE_PREF("dom.webnotifications.serviceworker.enabled", DOMServiceWorkerNotificationEnabled, DOM_SERVICEWORKERNOTIFICATION) WORKER_SIMPLE_PREF("dom.webnotifications.serviceworker.enabled", DOMServiceWorkerNotificationEnabled, DOM_SERVICEWORKERNOTIFICATION)

View file

@ -1995,13 +1995,22 @@ struct WorkerPrivate::TimeoutInfo
mNestingLevel = kClampTimeoutNestingLevel; mNestingLevel = kClampTimeoutNestingLevel;
} }
void CalculateTargetTime() { void CalculateTargetTime(JSContext* aCx) {
auto target = mInterval; auto target = mInterval;
int32_t minTimeoutValue;
// We're on a worker thread; go through WorkerPrivate for the pref.
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
if (workerPrivate) {
minTimeoutValue = workerPrivate->DOMMinTimeoutValue();
} else {
// fall back to default 4 ms
minTimeoutValue = 4;
}
// Clamp timeout for workers, except chrome workers // Clamp timeout for workers, except chrome workers
if (mNestingLevel >= kClampTimeoutNestingLevel && !mOnChromeWorker) { if (mNestingLevel >= kClampTimeoutNestingLevel && !mOnChromeWorker) {
target = TimeDuration::Max( target = TimeDuration::Max(mInterval,TimeDuration::FromMilliseconds(minTimeoutValue));
mInterval,
TimeDuration::FromMilliseconds(Preferences::GetInt("dom.min_timeout_value")));
} }
mTargetTime = TimeStamp::Now() + target; mTargetTime = TimeStamp::Now() + target;
} }
@ -6387,7 +6396,7 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
// Reschedule intervals. // Reschedule intervals.
// Reschedule a timeout and, if needed, increase the nesting level. // Reschedule a timeout and, if needed, increase the nesting level.
info->AccumulateNestingLevel(info->mNestingLevel); info->AccumulateNestingLevel(info->mNestingLevel);
info->CalculateTargetTime(); info->CalculateTargetTime(aCx);
// Don't re-sort the list here, we'll do that at the end. // Don't re-sort the list here, we'll do that at the end.
++index; ++index;
} }

View file

@ -1771,6 +1771,9 @@ PresShell::Initialize(nscoord aWidth, nscoord aHeight)
// (Do this in a script runner, since our caller might have a script // (Do this in a script runner, since our caller might have a script
// blocker on the stack.) // blocker on the stack.)
nsContentUtils::AddScriptRunner(new XBLConstructorRunner(mDocument)); nsContentUtils::AddScriptRunner(new XBLConstructorRunner(mDocument));
// XBLConstructorRunner might destroy us.
NS_ENSURE_STATE(!mHaveShutDown);
} }
NS_ASSERTION(rootFrame, "How did that happen?"); NS_ASSERTION(rootFrame, "How did that happen?");

View file

@ -21,8 +21,12 @@
struct CachedOffsetForFrame; struct CachedOffsetForFrame;
class nsAutoScrollTimer; class nsAutoScrollTimer;
class nsIContentIterator; class nsIContentIterator;
class nsIDocument;
class nsIEditor;
class nsIFrame; class nsIFrame;
class nsIHTMLEditor;
class nsFrameSelection; class nsFrameSelection;
class nsPIDOMWindowOuter;
struct SelectionDetails; struct SelectionDetails;
class nsCopySupport; class nsCopySupport;
class nsHTMLCopyEncoder; class nsHTMLCopyEncoder;
@ -170,13 +174,19 @@ public:
uint32_t FocusOffset(); uint32_t FocusOffset();
bool IsCollapsed() const; bool IsCollapsed() const;
void Collapse(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv); // *JS() methods are mapped to Selection.*().
void CollapseToStart(mozilla::ErrorResult& aRv); // They may move focus only when the range represents normal selection.
void CollapseToEnd(mozilla::ErrorResult& aRv); // These methods shouldn't be used by non-JS callers.
void CollapseJS(nsINode& aNode, uint32_t aOffset,
mozilla::ErrorResult& aRv);
void CollapseToStartJS(mozilla::ErrorResult& aRv);
void CollapseToEndJS(mozilla::ErrorResult& aRv);
void Extend(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv); void ExtendJS(nsINode& aNode, uint32_t aOffset,
mozilla::ErrorResult& aRv);
void SelectAllChildrenJS(nsINode& aNode, mozilla::ErrorResult& aRv);
void SelectAllChildren(nsINode& aNode, mozilla::ErrorResult& aRv);
void DeleteFromDocument(mozilla::ErrorResult& aRv); void DeleteFromDocument(mozilla::ErrorResult& aRv);
uint32_t RangeCount() const uint32_t RangeCount() const
@ -187,7 +197,7 @@ public:
void GetType(nsAString& aOutType) const; void GetType(nsAString& aOutType) const;
nsRange* GetRangeAt(uint32_t aIndex, mozilla::ErrorResult& aRv); nsRange* GetRangeAt(uint32_t aIndex, mozilla::ErrorResult& aRv);
void AddRange(nsRange& aRange, mozilla::ErrorResult& aRv); void AddRangeJS(nsRange& aRange, mozilla::ErrorResult& aRv);
void RemoveRange(nsRange& aRange, mozilla::ErrorResult& aRv); void RemoveRange(nsRange& aRange, mozilla::ErrorResult& aRv);
void RemoveAllRanges(mozilla::ErrorResult& aRv); void RemoveAllRanges(mozilla::ErrorResult& aRv);
@ -207,9 +217,9 @@ public:
void Modify(const nsAString& aAlter, const nsAString& aDirection, void Modify(const nsAString& aAlter, const nsAString& aDirection,
const nsAString& aGranularity, mozilla::ErrorResult& aRv); const nsAString& aGranularity, mozilla::ErrorResult& aRv);
void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset, void SetBaseAndExtentJS(nsINode& aAnchorNode, uint32_t aAnchorOffset,
nsINode& aFocusNode, uint32_t aFocusOffset, nsINode& aFocusNode, uint32_t aFocusOffset,
mozilla::ErrorResult& aRv); mozilla::ErrorResult& aRv);
bool GetInterlinePosition(mozilla::ErrorResult& aRv); bool GetInterlinePosition(mozilla::ErrorResult& aRv);
void SetInterlinePosition(bool aValue, mozilla::ErrorResult& aRv); void SetInterlinePosition(bool aValue, mozilla::ErrorResult& aRv);
@ -243,6 +253,17 @@ public:
int16_t aVPercent, int16_t aHPercent, int16_t aVPercent, int16_t aHPercent,
mozilla::ErrorResult& aRv); mozilla::ErrorResult& aRv);
// Non-JS callers should use the following methods.
void Collapse(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
void CollapseToStart(mozilla::ErrorResult& aRv);
void CollapseToEnd(mozilla::ErrorResult& aRv);
void Extend(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
void AddRange(nsRange& aRange, mozilla::ErrorResult& aRv);
void SelectAllChildren(nsINode& aNode, mozilla::ErrorResult& aRv);
void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
nsINode& aFocusNode, uint32_t aFocusOffset,
mozilla::ErrorResult& aRv);
void AddSelectionChangeBlocker(); void AddSelectionChangeBlocker();
void RemoveSelectionChangeBlocker(); void RemoveSelectionChangeBlocker();
bool IsBlockingSelectionChangeEvents() const; bool IsBlockingSelectionChangeEvents() const;
@ -265,7 +286,8 @@ public:
mSelectionType = aSelectionType; mSelectionType = aSelectionType;
} }
nsresult NotifySelectionListeners(); nsresult NotifySelectionListeners(bool aCalledByJS);
nsresult NotifySelectionListeners();
friend struct AutoUserInitiated; friend struct AutoUserInitiated;
struct MOZ_RAII AutoUserInitiated struct MOZ_RAII AutoUserInitiated
@ -341,6 +363,43 @@ private:
*/ */
nsresult AddItemInternal(nsRange* aRange, int32_t* aOutIndex); nsresult AddItemInternal(nsRange* aRange, int32_t* aOutIndex);
nsIDocument* GetDocument() const;
nsPIDOMWindowOuter* GetWindow() const;
nsIEditor* GetEditor() const;
/**
* GetCommonEditingHostForAllRanges() returns common editing host of all
* ranges if there is. If at least one of the ranges is in non-editable
* element, returns nullptr. See following examples for the detail:
*
* <div id="a" contenteditable>
* an[cestor
* <div id="b" contenteditable="false">
* non-editable
* <div id="c" contenteditable>
* desc]endant
* in this case, this returns div#a because div#c is also in div#a.
*
* <div id="a" contenteditable>
* an[ce]stor
* <div id="b" contenteditable="false">
* non-editable
* <div id="c" contenteditable>
* de[sc]endant
* in this case, this returns div#a because second range is also in div#a
* and common ancestor of the range (i.e., div#c) is editable.
*
* <div id="a" contenteditable>
* an[ce]stor
* <div id="b" contenteditable="false">
* [non]-editable
* <div id="c" contenteditable>
* de[sc]endant
* in this case, this returns nullptr because the second range is in
* non-editable area.
*/
Element* GetCommonEditingHostForAllRanges();
// These are the ranges inside this selection. They are kept sorted in order // These are the ranges inside this selection. They are kept sorted in order
// of DOM start position. // of DOM start position.
// //
@ -371,6 +430,12 @@ private:
*/ */
bool mUserInitiated; bool mUserInitiated;
/**
* When the selection change is caused by a call of Selection API,
* mCalledByJS is true. Otherwise, false.
*/
bool mCalledByJS;
// Non-zero if we don't want any changes we make to the selection to be // Non-zero if we don't want any changes we make to the selection to be
// visible to content. If non-zero, content won't be notified about changes. // visible to content. If non-zero, content won't be notified about changes.
uint32_t mSelectionChangeBlockerCount; uint32_t mSelectionChangeBlockerCount;

View file

@ -10,6 +10,7 @@
#include "mozilla/dom/Selection.h" #include "mozilla/dom/Selection.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/EventStates.h" #include "mozilla/EventStates.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
@ -84,6 +85,7 @@ static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
#include "nsIEditor.h" #include "nsIEditor.h"
#include "nsIHTMLEditor.h" #include "nsIHTMLEditor.h"
#include "nsFocusManager.h" #include "nsFocusManager.h"
#include "nsPIDOMWindow.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
@ -1199,7 +1201,7 @@ Selection::ToString(nsAString& aReturn)
aReturn.Truncate(); aReturn.Truncate();
return NS_OK; return NS_OK;
} }
shell->FlushPendingNotifications(Flush_Style); shell->FlushPendingNotifications(Flush_Frames);
return ToStringWithFormat("text/plain", return ToStringWithFormat("text/plain",
nsIDocumentEncoder::SkipInvisibleContent, nsIDocumentEncoder::SkipInvisibleContent,
@ -1876,6 +1878,7 @@ printf(" * TakeFocus - moving into new cell\n");
// Don't notify selection listeners if batching is on: // Don't notify selection listeners if batching is on:
if (GetBatching()) if (GetBatching())
return NS_OK; return NS_OK;
// Be aware, the Selection instance may be destroyed after this call.
return NotifySelectionListeners(SelectionType::eNormal); return NotifySelectionListeners(SelectionType::eNormal);
} }
@ -1916,6 +1919,7 @@ nsFrameSelection::SetDragState(bool aState)
mDragSelectingCells = false; mDragSelectingCells = false;
// Notify that reason is mouse up. // Notify that reason is mouse up.
PostReason(nsISelectionListener::MOUSEUP_REASON); PostReason(nsISelectionListener::MOUSEUP_REASON);
// Be aware, the Selection instance may be destroyed after this call.
NotifySelectionListeners(SelectionType::eNormal); NotifySelectionListeners(SelectionType::eNormal);
} }
} }
@ -2410,6 +2414,7 @@ nsFrameSelection::EndBatchChanges(int16_t aReason)
int16_t postReason = PopReason() | aReason; int16_t postReason = PopReason() | aReason;
PostReason(postReason); PostReason(postReason);
mChangesDuringBatching = false; mChangesDuringBatching = false;
// Be aware, the Selection instance may be destroyed after this call.
NotifySelectionListeners(SelectionType::eNormal); NotifySelectionListeners(SelectionType::eNormal);
} }
} }
@ -2421,7 +2426,8 @@ nsFrameSelection::NotifySelectionListeners(SelectionType aSelectionType)
int8_t index = GetIndexFromSelectionType(aSelectionType); int8_t index = GetIndexFromSelectionType(aSelectionType);
if (index >=0 && mDomSelections[index]) if (index >=0 && mDomSelections[index])
{ {
return mDomSelections[index]->NotifySelectionListeners(); RefPtr<Selection> selection = mDomSelections[index];
return selection->NotifySelectionListeners();
} }
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -3473,6 +3479,7 @@ Selection::Selection()
, mDirection(eDirNext) , mDirection(eDirNext)
, mSelectionType(SelectionType::eNormal) , mSelectionType(SelectionType::eNormal)
, mUserInitiated(false) , mUserInitiated(false)
, mCalledByJS(false)
, mSelectionChangeBlockerCount(0) , mSelectionChangeBlockerCount(0)
{ {
} }
@ -3483,6 +3490,7 @@ Selection::Selection(nsFrameSelection* aList)
, mDirection(eDirNext) , mDirection(eDirNext)
, mSelectionType(SelectionType::eNormal) , mSelectionType(SelectionType::eNormal)
, mUserInitiated(false) , mUserInitiated(false)
, mCalledByJS(false)
, mSelectionChangeBlockerCount(0) , mSelectionChangeBlockerCount(0)
{ {
} }
@ -4943,7 +4951,10 @@ Selection::RemoveAllRanges(ErrorResult& aRv)
RefPtr<nsFrameSelection> frameSelection = mFrameSelection; RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
frameSelection->ClearTableCellSelection(); frameSelection->ClearTableCellSelection();
// Be aware, this instance may be destroyed after this call.
// XXX Why doesn't this call Selection::NotifySelectionListener() directly?
result = frameSelection->NotifySelectionListeners(GetType()); result = frameSelection->NotifySelectionListeners(GetType());
// Also need to notify the frames! // Also need to notify the frames!
// PresShell::CharacterDataChanged should do that on DocumentChanged // PresShell::CharacterDataChanged should do that on DocumentChanged
if (NS_FAILED(result)) { if (NS_FAILED(result)) {
@ -4966,6 +4977,14 @@ Selection::AddRange(nsIDOMRange* aDOMRange)
return result.StealNSResult(); return result.StealNSResult();
} }
void
Selection::AddRangeJS(nsRange& aRange, ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
AddRange(aRange, aRv);
}
void void
Selection::AddRange(nsRange& aRange, ErrorResult& aRv) Selection::AddRange(nsRange& aRange, ErrorResult& aRv)
{ {
@ -5020,6 +5039,8 @@ Selection::AddRangeInternal(nsRange& aRange, nsIDocument* aDocument,
if (!mFrameSelection) if (!mFrameSelection)
return;//nothing to do return;//nothing to do
// Be aware, this instance may be destroyed after this call.
// XXX Why doesn't this call Selection::NotifySelectionListener() directly?
RefPtr<nsFrameSelection> frameSelection = mFrameSelection; RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
result = frameSelection->NotifySelectionListeners(GetType()); result = frameSelection->NotifySelectionListeners(GetType());
if (NS_FAILED(result)) { if (NS_FAILED(result)) {
@ -5115,6 +5136,9 @@ Selection::RemoveRange(nsRange& aRange, ErrorResult& aRv)
if (!mFrameSelection) if (!mFrameSelection)
return;//nothing to do return;//nothing to do
// Be aware, this instance may be destroyed after this call.
// XXX Why doesn't this call Selection::NotifySelectionListener() directly?
RefPtr<nsFrameSelection> frameSelection = mFrameSelection; RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
rv = frameSelection->NotifySelectionListeners(GetType()); rv = frameSelection->NotifySelectionListeners(GetType());
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
@ -5140,6 +5164,14 @@ Selection::CollapseNative(nsINode* aParentNode, int32_t aOffset)
return Collapse(aParentNode, aOffset); return Collapse(aParentNode, aOffset);
} }
void
Selection::CollapseJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
Collapse(aNode, aOffset, aRv);
}
nsresult nsresult
Selection::Collapse(nsINode* aParentNode, int32_t aOffset) Selection::Collapse(nsINode* aParentNode, int32_t aOffset)
{ {
@ -5222,6 +5254,9 @@ Selection::Collapse(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
} }
setAnchorFocusRange(0); setAnchorFocusRange(0);
selectFrames(presContext, range, true); selectFrames(presContext, range, true);
// Be aware, this instance may be destroyed after this call.
// XXX Why doesn't this call Selection::NotifySelectionListener() directly?
result = frameSelection->NotifySelectionListeners(GetType()); result = frameSelection->NotifySelectionListeners(GetType());
if (NS_FAILED(result)) { if (NS_FAILED(result)) {
aRv.Throw(result); aRv.Throw(result);
@ -5240,6 +5275,14 @@ Selection::CollapseToStart()
return result.StealNSResult(); return result.StealNSResult();
} }
void
Selection::CollapseToStartJS(ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
CollapseToStart(aRv);
}
void void
Selection::CollapseToStart(ErrorResult& aRv) Selection::CollapseToStart(ErrorResult& aRv)
{ {
@ -5281,6 +5324,14 @@ Selection::CollapseToEnd()
return result.StealNSResult(); return result.StealNSResult();
} }
void
Selection::CollapseToEndJS(ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
CollapseToEnd(aRv);
}
void void
Selection::CollapseToEnd(ErrorResult& aRv) Selection::CollapseToEnd(ErrorResult& aRv)
{ {
@ -5495,6 +5546,14 @@ Selection::ExtendNative(nsINode* aParentNode, int32_t aOffset)
return Extend(aParentNode, aOffset); return Extend(aParentNode, aOffset);
} }
void
Selection::ExtendJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
Extend(aNode, aOffset, aRv);
}
nsresult nsresult
Selection::Extend(nsINode* aParentNode, int32_t aOffset) Selection::Extend(nsINode* aParentNode, int32_t aOffset)
{ {
@ -5773,6 +5832,9 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
printf ("Sel. Extend to %p %s %d\n", content.get(), printf ("Sel. Extend to %p %s %d\n", content.get(),
nsAtomCString(content->NodeInfo()->NameAtom()).get(), aOffset); nsAtomCString(content->NodeInfo()->NameAtom()).get(), aOffset);
#endif #endif
// Be aware, this instance may be destroyed after this call.
// XXX Why doesn't this call Selection::NotifySelectionListener() directly?
RefPtr<nsFrameSelection> frameSelection = mFrameSelection; RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
res = frameSelection->NotifySelectionListeners(GetType()); res = frameSelection->NotifySelectionListeners(GetType());
if (NS_FAILED(res)) { if (NS_FAILED(res)) {
@ -5790,6 +5852,14 @@ Selection::SelectAllChildren(nsIDOMNode* aParentNode)
return result.StealNSResult(); return result.StealNSResult();
} }
void
Selection::SelectAllChildrenJS(nsINode& aNode, ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
SelectAllChildren(aNode, aRv);
}
void void
Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv) Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv)
{ {
@ -5938,6 +6008,30 @@ Selection::GetPresShell() const
return mFrameSelection->GetShell(); return mFrameSelection->GetShell();
} }
nsIDocument*
Selection::GetDocument() const
{
nsIPresShell* presShell = GetPresShell();
return presShell ? presShell->GetDocument() : nullptr;
}
nsPIDOMWindowOuter*
Selection::GetWindow() const
{
nsIDocument* document = GetDocument();
return document ? document->GetWindow() : nullptr;
}
nsIEditor*
Selection::GetEditor() const
{
nsPresContext* presContext = GetPresContext();
if (!presContext) {
return nullptr;
}
return nsContentUtils::GetHTMLEditor(presContext);
}
nsIFrame * nsIFrame *
Selection::GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect) Selection::GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect)
{ {
@ -6239,12 +6333,101 @@ Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove,
} }
} }
Element*
Selection::GetCommonEditingHostForAllRanges()
{
Element* editingHost = nullptr;
for (RangeData& rangeData : mRanges) {
nsRange* range = rangeData.mRange;
MOZ_ASSERT(range);
nsINode* commonAncestorNode = range->GetCommonAncestor();
if (!commonAncestorNode || !commonAncestorNode->IsContent()) {
return nullptr;
}
nsIContent* commonAncestor = commonAncestorNode->AsContent();
Element* foundEditingHost = commonAncestor->GetEditingHost();
// Even when common ancestor is a non-editable element in a contenteditable
// element, we don't need to move focus to the contenteditable element
// because Chromium doesn't set focus to it.
if (!foundEditingHost) {
return nullptr;
}
if (!editingHost) {
editingHost = foundEditingHost;
continue;
}
if (editingHost == foundEditingHost) {
continue;
}
if (nsContentUtils::ContentIsDescendantOf(foundEditingHost, editingHost)) {
continue;
}
if (nsContentUtils::ContentIsDescendantOf(editingHost, foundEditingHost)) {
editingHost = foundEditingHost;
continue;
}
// editingHost and foundEditingHost are not a descendant of the other.
// So, there is no common editing host.
return nullptr;
}
return editingHost;
}
nsresult
Selection::NotifySelectionListeners(bool aCalledByJS)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = aCalledByJS;
return NotifySelectionListeners();
}
nsresult nsresult
Selection::NotifySelectionListeners() Selection::NotifySelectionListeners()
{ {
if (!mFrameSelection) if (!mFrameSelection)
return NS_OK;//nothing to do return NS_OK;//nothing to do
// Our internal code should not move focus with using this class while
// this moves focus nor from selection listeners.
AutoRestore<bool> calledByJSRestorer(mCalledByJS);
mCalledByJS = false;
// When normal selection is changed by Selection API, we need to move focus
// if common ancestor of all ranges are in an editing host. Note that we
// don't need to move focus *to* the other focusable node, because other
// browsers don't do this, either.
if (mSelectionType == SelectionType::eNormal &&
calledByJSRestorer.SavedValue()) {
nsPIDOMWindowOuter* window = GetWindow();
nsIDocument* document = GetDocument();
// If the document is in design mode or doesn't have contenteditable
// element, we don't need to move focus.
if (window && document && !document->HasFlag(NODE_IS_EDITABLE) &&
GetEditor()) {
RefPtr<Element> newEditingHost = GetCommonEditingHostForAllRanges();
nsFocusManager* fm = nsFocusManager::GetFocusManager();
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsIContent* focusedContent =
fm->GetFocusedDescendant(window, false, getter_AddRefs(focusedWindow));
nsCOMPtr<Element> focusedElement = do_QueryInterface(focusedContent);
// When all selected ranges are in an editing host, it should take focus.
if (newEditingHost && newEditingHost != focusedElement) {
MOZ_ASSERT(!newEditingHost->IsInNativeAnonymousSubtree());
nsCOMPtr<nsIDOMElement> domElementToFocus =
do_QueryInterface(newEditingHost->AsDOMNode());
// Note that don't steal focus from focused window if the window
// doesn't have focus. Additionally, when an element gets focus,
// we usually scroll to the element, but in this case we shouldn't
// do that because Blink&Gecko don't do this.
fm->SetFocus(domElementToFocus, nsIFocusManager::FLAG_NOSWITCHFRAME |
nsIFocusManager::FLAG_NOSCROLL);
}
// Otherwise, we shouldn't move focus since Blink&Gecko don't move
// focus; only the selection range is updated. This is a bit weird but
// it is what it is and we should act the same for parity.
}
}
RefPtr<nsFrameSelection> frameSelection = mFrameSelection; RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
if (frameSelection->GetBatching()) { if (frameSelection->GetBatching()) {
frameSelection->SetDirty(); frameSelection->SetDirty();
@ -6454,6 +6637,19 @@ Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
} }
} }
void
Selection::SetBaseAndExtentJS(nsINode& aAnchorNode,
uint32_t aAnchorOffset,
nsINode& aFocusNode,
uint32_t aFocusOffset,
ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
SetBaseAndExtent(aAnchorNode, aAnchorOffset,
aFocusNode, aFocusOffset, aRv);
}
void void
Selection::SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset, Selection::SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
nsINode& aFocusNode, uint32_t aFocusOffset, nsINode& aFocusNode, uint32_t aFocusOffset,

View file

@ -7,6 +7,8 @@
#include "mozilla/LinkedList.h" #include "mozilla/LinkedList.h"
#include "nsCORSListenerProxy.h" #include "nsCORSListenerProxy.h"
#include "nsQueryObject.h"
#include "nsIChannel.h" #include "nsIChannel.h"
#include "nsIHttpChannel.h" #include "nsIHttpChannel.h"
#include "HttpChannelChild.h" #include "HttpChannelChild.h"
@ -1507,6 +1509,9 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
method, false); method, false);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsIHttpChannel> reqCh = do_QueryObject(aRequestChannel);
RefPtr<nsIHttpChannel> preCh = do_QueryObject(preHttp);
nsTArray<nsCString> preflightHeaders; nsTArray<nsCString> preflightHeaders;
if (!aUnsafeHeaders.IsEmpty()) { if (!aUnsafeHeaders.IsEmpty()) {
for (uint32_t i = 0; i < aUnsafeHeaders.Length(); ++i) { for (uint32_t i = 0; i < aUnsafeHeaders.Length(); ++i) {
@ -1535,6 +1540,23 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
rv = preflightChannel->SetNotificationCallbacks(preflightListener); rv = preflightChannel->SetNotificationCallbacks(preflightListener);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
if (reqCh && preCh) {
// Per https://fetch.spec.whatwg.org/#cors-preflight-fetch step 1, the
// request's referrer and referrer policy should match the original request.
// Note that RFC 9110 says we SHOULD NOT send referrers on insecure requests
// which CORS preflights by definition are, so there's a conflict here, but
// since this is expected behaviour in mainstream implementations, we get and
// send the referrer on CORS preflights here. See issue #2451
uint32_t referrerPolicy = nsIHttpChannel::REFERRER_POLICY_UNSET;
rv = reqCh->GetReferrerPolicy(&referrerPolicy);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> requestReferrerURI;
rv = reqCh->GetReferrer(getter_AddRefs(requestReferrerURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = preCh->SetReferrerWithPolicy(requestReferrerURI, referrerPolicy);
NS_ENSURE_SUCCESS(rv, rv);
}
// Start preflight // Start preflight
rv = preflightChannel->AsyncOpen2(preflightListener); rv = preflightChannel->AsyncOpen2(preflightListener);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);

View file

@ -1394,6 +1394,8 @@ CycleCollectedJSContext::ProcessStableStateQueue()
MOZ_RELEASE_ASSERT(!mDoingStableStates); MOZ_RELEASE_ASSERT(!mDoingStableStates);
mDoingStableStates = true; mDoingStableStates = true;
// When run, one event can add another event to the mStableStateEvents, as
// such you can't use iterators here.
for (uint32_t i = 0; i < mStableStateEvents.Length(); ++i) { for (uint32_t i = 0; i < mStableStateEvents.Length(); ++i) {
nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget(); nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget();
event->Run(); event->Run();
@ -1477,11 +1479,33 @@ CycleCollectedJSContext::AfterProcessMicrotasks()
} }
// Cleanup Indexed Database transactions: // Cleanup Indexed Database transactions:
// https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint // https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
CleanupIDBTransactions(RecursionDepth());
// We should only ever get here from PerformMicroTaskCheckPoint after a task or other
// checkpoint-able state, never from within ProcessStableStateQueue (mDoingStableStates==false).
// However, some buggy XUL addons may dispatch JS Events from runnables in the StableState queue,
// which then perform a checkpoint at their end, ending up here while ProcessStableStateQueue is
// on the stack.
// Specifically catch that here and add the call to CleanupIDBTransactions to the outer queue, to
// be performed when we know we're in a "regular" stable state again.
if (!mDoingStableStates) {
CleanupIDBTransactions(RecursionDepth());
} else {
// Don't need a RefPtr to this here as we know this->ProcessStableStateQueue is on the stack
nsCOMPtr<nsIRunnable> cleanupRunnable = NS_NewRunnableFunction(
[this, rec = RecursionDepth()] {
MOZ_ASSERT(mDoingStableStates);
// As this is called from ProcessStableStateQueue, mDoingStableStates == true.
// Switch the flag while CleanupIDBTransactions executes.
mDoingStableStates = false;
CleanupIDBTransactions(rec);
mDoingStableStates = true;
});
RunInStableState(cleanupRunnable.forget());
};
} }
uint32_t uint32_t
CycleCollectedJSContext::RecursionDepth() CycleCollectedJSContext::RecursionDepth() const
{ {
return mOwningThread->RecursionDepth(); return mOwningThread->RecursionDepth();
} }

View file

@ -357,7 +357,7 @@ public:
virtual void BeforeProcessTask(bool aMightBlock); virtual void BeforeProcessTask(bool aMightBlock);
virtual void AfterProcessTask(uint32_t aRecursionDepth); virtual void AfterProcessTask(uint32_t aRecursionDepth);
uint32_t RecursionDepth(); uint32_t RecursionDepth() const;
// Run in stable state (call through nsContentUtils) // Run in stable state (call through nsContentUtils)
void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable); void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
@ -397,12 +397,12 @@ public:
} }
} }
bool IsInMicroTask() bool IsInMicroTask() const
{ {
return mMicroTaskLevel != 0; return mMicroTaskLevel != 0;
} }
uint32_t MicroTaskLevel() uint32_t MicroTaskLevel() const
{ {
return mMicroTaskLevel; return mMicroTaskLevel;
} }
@ -416,6 +416,11 @@ public:
void PerformDebuggerMicroTaskCheckpoint(); void PerformDebuggerMicroTaskCheckpoint();
bool IsInStableOrMetaStableState() const
{
return mDoingStableStates;
}
// Storage for watching rejected promises waiting for some client to // Storage for watching rejected promises waiting for some client to
// consume their rejection. // consume their rejection.

View file

@ -247,6 +247,9 @@
ERROR(NS_ERROR_REMOTE_XUL, FAILURE(75)), ERROR(NS_ERROR_REMOTE_XUL, FAILURE(75)),
/* The request resulted in an error page being displayed. */ /* The request resulted in an error page being displayed. */
ERROR(NS_ERROR_LOAD_SHOWED_ERRORPAGE, FAILURE(77)), ERROR(NS_ERROR_LOAD_SHOWED_ERRORPAGE, FAILURE(77)),
/* The request occurred in docshell that lacks a treeowner, so it is
* probably in the process of being torn down. */
ERROR(NS_ERROR_DOCSHELL_DYING, FAILURE(78)),
/* FTP specific error codes: */ /* FTP specific error codes: */