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;
}
if (rv == NS_ERROR_REMOTE_XUL) {
if (rv == NS_ERROR_REMOTE_XUL || rv == NS_ERROR_DOCSHELL_DYING) {
aRequest->Cancel(rv);
*aAbortProcess = true;
return NS_OK;

View file

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

View file

@ -80,7 +80,14 @@ DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
RefPtr<DOMIntersectionObserver> observer =
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)) {
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));
}
// 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 {
SimilarOriginBrowsingContext,
DifferentOriginBrowsingContext,
@ -267,14 +298,12 @@ enum class BrowsingContextInfo {
void
DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time)
{
Element* root = nullptr;
nsINode* root = mRoot;
nsIFrame* rootFrame = nullptr;
nsRect rootRect;
if (mRoot) {
root = mRoot;
rootFrame = root->GetPrimaryFrame();
if (rootFrame) {
if (mRoot && mRoot->IsElement()) {
if ((rootFrame = mRoot->AsElement()->GetPrimaryFrame())) {
nsRect rootRectRelativeToRootFrame;
if (rootFrame->GetType() == nsGkAtoms::scrollFrame) {
// rootRectRelativeToRootFrame should be the content rect of rootFrame, not including the scrollbars.
@ -292,30 +321,18 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time
containingBlock);
}
} else {
nsCOMPtr<nsIPresShell> presShell = aDocument->GetShell();
if (presShell) {
rootFrame = presShell->GetRootScrollFrame();
if (rootFrame) {
nsPresContext* presContext = rootFrame->PresContext();
while (!presContext->IsRootContentDocument()) {
// Walk up the tree
presContext = presContext->GetParentPresContext();
if (!presContext) {
break;
MOZ_ASSERT(!mRoot || mRoot->IsInUncomposedDoc());
nsIDocument* rootDocument =
mRoot ? mRoot->GetUncomposedDoc() : GetTopLevelDocument(*aDocument);
if (rootDocument) {
if (nsIPresShell* presShell = rootDocument->GetShell()) {
rootFrame = presShell->GetRootScrollFrame();
if (rootFrame) {
root = rootFrame->GetContent()->AsElement();
nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame);
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;
}
Element* GetRoot() const {
nsINode* GetRoot() const {
return mRoot;
}
@ -178,7 +178,7 @@ protected:
nsCOMPtr<nsPIDOMWindowInner> mOwner;
RefPtr<nsIDocument> mDocument;
RefPtr<mozilla::dom::IntersectionCallback> mCallback;
RefPtr<Element> mRoot;
RefPtr<nsINode> mRoot;
nsCSSRect mRootMargin;
nsTArray<double> mThresholds;

View file

@ -100,6 +100,7 @@ nsContentSink::nsContentSink()
, mDeferredFlushTags(0)
, mIsDocumentObserver(0)
, mRunsToCompletion(0)
, mIsBlockingOnload(false)
, mDeflectedCount(0)
, mHasPendingEvent(false)
, mCurrentParseEndTime(0)
@ -1539,8 +1540,14 @@ nsContentSink::DropParserAndPerfHint(void)
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);
mIsBlockingOnload = false;
}
}
@ -1595,6 +1602,7 @@ nsContentSink::WillBuildModelImpl()
{
if (!mRunsToCompletion) {
mDocument->BlockOnload();
mIsBlockingOnload = true;
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.
// XML DOMParser leaves this to false for now!
uint8_t mRunsToCompletion : 1;
// True if we are blocking load event.
bool mIsBlockingOnload : 1;
//
// -- Can interrupt parsing members --

View file

@ -5444,6 +5444,14 @@ nsContentUtils::AddPendingIDBTransaction(already_AddRefed<nsIRunnable> aTransact
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.
*

View file

@ -1779,6 +1779,11 @@ public:
*/
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
* and zoom of a page on mobile devices. We stick the information in
* the document header and use it later on after rendering.

View file

@ -269,6 +269,7 @@ nsRange::nsRange(nsINode* aNode)
, mStartOffsetWasIncremented(false)
, mEndOffsetWasIncremented(false)
, mEnableGravitationOnElementRemoval(true)
, mCalledByJS(false)
#ifdef DEBUG
, mAssertNextInsertOrAppendIndex(-1)
, 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
// could be observed by a selection listener while the range was in an invalid state.
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;
}
void
nsRange::SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetStart(aNode, aOffset, aErr);
}
void
nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
{
@ -1279,6 +1297,14 @@ nsRange::SetStart(nsINode* aParent, uint32_t aOffset)
return NS_OK;
}
void
nsRange::SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetStartBefore(aNode, aErr);
}
void
nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv)
{
@ -1310,6 +1336,14 @@ nsRange::SetStartBefore(nsIDOMNode* aSibling)
return rv.StealNSResult();
}
void
nsRange::SetStartAfterJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetStartAfter(aNode, aErr);
}
void
nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv)
{
@ -1341,6 +1375,14 @@ nsRange::SetStartAfter(nsIDOMNode* aSibling)
return rv.StealNSResult();
}
void
nsRange::SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetEnd(aNode, aOffset, aErr);
}
void
nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
{
@ -1455,6 +1497,14 @@ nsRange::SetStartAndEnd(nsINode* aStartParent, uint32_t aStartOffset,
return NS_OK;
}
void
nsRange::SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetEndBefore(aNode, aErr);
}
void
nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv)
{
@ -1486,6 +1536,14 @@ nsRange::SetEndBefore(nsIDOMNode* aSibling)
return rv.StealNSResult();
}
void
nsRange::SetEndAfterJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SetEndAfter(aNode, aErr);
}
void
nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv)
{
@ -1532,6 +1590,14 @@ nsRange::Collapse(bool aToStart)
return NS_OK;
}
void
nsRange::CollapseJS(bool aToStart)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
Unused << Collapse(aToStart);
}
NS_IMETHODIMP
nsRange::SelectNode(nsIDOMNode* aN)
{
@ -1543,6 +1609,14 @@ nsRange::SelectNode(nsIDOMNode* aN)
return rv.StealNSResult();
}
void
nsRange::SelectNodeJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SelectNode(aNode, aErr);
}
void
nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv)
{
@ -1582,6 +1656,14 @@ nsRange::SelectNodeContents(nsIDOMNode* aN)
return rv.StealNSResult();
}
void
nsRange::SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSRestore calledByJSRestorer(*this);
mCalledByJS = true;
SelectNodeContents(aNode, aErr);
}
void
nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv)
{

View file

@ -20,6 +20,7 @@
#include "nsStubMutationObserver.h"
#include "nsWrapperCache.h"
#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
namespace mozilla {
class ErrorResult;
@ -256,14 +257,20 @@ public:
void InsertNode(nsINode& aNode, ErrorResult& aErr);
bool IntersectsNode(nsINode& aNode, ErrorResult& aRv);
bool IsPointInRange(nsINode& aParent, uint32_t aOffset, ErrorResult& aErr);
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);
// *JS() methods are mapped to Range.*() of DOM.
// They may move focus only when the range represents normal selection.
// These methods shouldn't be used from internal.
void CollapseJS(bool aToStart);
void SelectNodeJS(nsINode& aNode, ErrorResult& aErr);
void SelectNodeContentsJS(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);
already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true,
bool aFlushLayout = true);
@ -272,6 +279,17 @@ public:
void GetClientRectsAndTexts(
mozilla::dom::ClientRectsAndTexts& aResult,
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,
mozilla::ErrorResult& aError,
nsIContent* aStartParent,
@ -392,6 +410,31 @@ protected:
size_t aRangeStart,
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
{
explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange)
@ -428,6 +471,7 @@ protected:
bool mStartOffsetWasIncremented : 1;
bool mEndOffsetWasIncremented : 1;
bool mEnableGravitationOnElementRemoval : 1;
bool mCalledByJS : 1;
#ifdef DEBUG
int32_t mAssertNextInsertOrAppendIndex;
nsINode* mAssertNextInsertOrAppendNode;

View file

@ -693,6 +693,13 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
NS_ENSURE_TRUE(aEvent->mMessage || !aDOMEvent || aTargets,
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
{
if (aDOMEvent) {

View file

@ -3105,6 +3105,26 @@ HTMLInputElement::GetFiles()
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
HTMLInputElement::HandleNumberControlSpin(void* aData)
{

View file

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

View file

@ -53,6 +53,7 @@
#include "MediaDecoder.h"
#include "MediaPrefs.h"
#include "MediaResource.h"
#include "MediaShutdownManager.h"
#include "nsICategoryManager.h"
#include "nsIContentPolicy.h"
@ -3203,6 +3204,13 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
NotifyOwnerDocumentActivityChanged();
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);
// Paradoxically, there is a self-edge whereby UpdateReadyStateInternal refuses
// to run until mReadyState reaches at least HAVE_METADATA by some other means.

View file

@ -264,14 +264,16 @@ void
HTMLTrackElement::DispatchLoadResource()
{
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());
mLoadResourceDispatched = true;
}
}
void
HTMLTrackElement::LoadResource()
HTMLTrackElement::LoadResource(RefPtr<WebVTTListener>&& aWebVTTListener)
{
mLoadResourceDispatched = false;
@ -317,33 +319,46 @@ HTMLTrackElement::LoadResource()
}
}
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsILoadGroup> loadGroup = OwnerDoc()->GetDocumentLoadGroup();
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);
mListener = std::move(aWebVTTListener);
// This will do 6. Set the text track readiness state to loading.
rv = mListener->LoadResource();
NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
channel->SetNotificationCallbacks(mListener);
LOG(LogLevel::Debug, ("opening webvtt channel"));
rv = channel->AsyncOpen2(mListener);
if (NS_FAILED(rv)) {
SetReadyState(TextTrackReadyState::FailedToLoad);
nsIDocument* doc = OwnerDoc();
if (!doc) {
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

View file

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

View file

@ -602,6 +602,17 @@ void
TextTrackManager::TimeMarchesOn()
{
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");
mTimeMarchesOnDispatched = false;

View file

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

View file

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

View file

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

View file

@ -59,15 +59,30 @@ public:
switch (event) {
case MediaStreamGraphEvent::EVENT_FINISHED:
{
RefPtr<SynthStreamListener> self = this;
if (!mStarted) {
mStarted = true;
nsCOMPtr<nsIRunnable> startRunnable =
NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
nsCOMPtr<nsIRunnable> startRunnable = 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(startRunnable.forget());
}
nsCOMPtr<nsIRunnable> endRunnable =
NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
nsCOMPtr<nsIRunnable> endRunnable = NS_NewRunnableFunction(
[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());
}
break;
@ -85,8 +100,16 @@ public:
{
if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) {
mStarted = true;
nsCOMPtr<nsIRunnable> event =
NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
RefPtr<SynthStreamListener> self = this;
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());
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -3082,6 +3082,8 @@ void
ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
{
AssertIsOnMainThread();
AutoTArray<nsCOMPtr<nsIDocument>, 16> documents;
for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
if (iter.UserData() != aRegistration) {
continue;
@ -3092,6 +3094,12 @@ ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegis
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);
}
}

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("dom.caches.enabled", DOMCachesEnabled, DOM_CACHES)
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.webnotifications.enabled", DOMWorkerNotificationEnabled, DOM_WORKERNOTIFICATION)
WORKER_SIMPLE_PREF("dom.webnotifications.serviceworker.enabled", DOMServiceWorkerNotificationEnabled, DOM_SERVICEWORKERNOTIFICATION)

View file

@ -1995,13 +1995,22 @@ struct WorkerPrivate::TimeoutInfo
mNestingLevel = kClampTimeoutNestingLevel;
}
void CalculateTargetTime() {
void CalculateTargetTime(JSContext* aCx) {
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
if (mNestingLevel >= kClampTimeoutNestingLevel && !mOnChromeWorker) {
target = TimeDuration::Max(
mInterval,
TimeDuration::FromMilliseconds(Preferences::GetInt("dom.min_timeout_value")));
target = TimeDuration::Max(mInterval,TimeDuration::FromMilliseconds(minTimeoutValue));
}
mTargetTime = TimeStamp::Now() + target;
}
@ -6387,7 +6396,7 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
// Reschedule intervals.
// Reschedule a timeout and, if needed, increase the nesting level.
info->AccumulateNestingLevel(info->mNestingLevel);
info->CalculateTargetTime();
info->CalculateTargetTime(aCx);
// Don't re-sort the list here, we'll do that at the end.
++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
// blocker on the stack.)
nsContentUtils::AddScriptRunner(new XBLConstructorRunner(mDocument));
// XBLConstructorRunner might destroy us.
NS_ENSURE_STATE(!mHaveShutDown);
}
NS_ASSERTION(rootFrame, "How did that happen?");

View file

@ -21,8 +21,12 @@
struct CachedOffsetForFrame;
class nsAutoScrollTimer;
class nsIContentIterator;
class nsIDocument;
class nsIEditor;
class nsIFrame;
class nsIHTMLEditor;
class nsFrameSelection;
class nsPIDOMWindowOuter;
struct SelectionDetails;
class nsCopySupport;
class nsHTMLCopyEncoder;
@ -170,13 +174,19 @@ public:
uint32_t FocusOffset();
bool IsCollapsed() const;
void Collapse(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
void CollapseToStart(mozilla::ErrorResult& aRv);
void CollapseToEnd(mozilla::ErrorResult& aRv);
// *JS() methods are mapped to Selection.*().
// They may move focus only when the range represents normal selection.
// These methods shouldn't be used by non-JS callers.
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);
uint32_t RangeCount() const
@ -187,7 +197,7 @@ public:
void GetType(nsAString& aOutType) const;
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 RemoveAllRanges(mozilla::ErrorResult& aRv);
@ -207,9 +217,9 @@ public:
void Modify(const nsAString& aAlter, const nsAString& aDirection,
const nsAString& aGranularity, mozilla::ErrorResult& aRv);
void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
nsINode& aFocusNode, uint32_t aFocusOffset,
mozilla::ErrorResult& aRv);
void SetBaseAndExtentJS(nsINode& aAnchorNode, uint32_t aAnchorOffset,
nsINode& aFocusNode, uint32_t aFocusOffset,
mozilla::ErrorResult& aRv);
bool GetInterlinePosition(mozilla::ErrorResult& aRv);
void SetInterlinePosition(bool aValue, mozilla::ErrorResult& aRv);
@ -243,6 +253,17 @@ public:
int16_t aVPercent, int16_t aHPercent,
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 RemoveSelectionChangeBlocker();
bool IsBlockingSelectionChangeEvents() const;
@ -265,7 +286,8 @@ public:
mSelectionType = aSelectionType;
}
nsresult NotifySelectionListeners();
nsresult NotifySelectionListeners(bool aCalledByJS);
nsresult NotifySelectionListeners();
friend struct AutoUserInitiated;
struct MOZ_RAII AutoUserInitiated
@ -341,6 +363,43 @@ private:
*/
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
// of DOM start position.
//
@ -371,6 +430,12 @@ private:
*/
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
// visible to content. If non-zero, content won't be notified about changes.
uint32_t mSelectionChangeBlockerCount;

View file

@ -10,6 +10,7 @@
#include "mozilla/dom/Selection.h"
#include "mozilla/Attributes.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/EventStates.h"
#include "nsCOMPtr.h"
@ -84,6 +85,7 @@ static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
#include "nsIEditor.h"
#include "nsIHTMLEditor.h"
#include "nsFocusManager.h"
#include "nsPIDOMWindow.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -1199,7 +1201,7 @@ Selection::ToString(nsAString& aReturn)
aReturn.Truncate();
return NS_OK;
}
shell->FlushPendingNotifications(Flush_Style);
shell->FlushPendingNotifications(Flush_Frames);
return ToStringWithFormat("text/plain",
nsIDocumentEncoder::SkipInvisibleContent,
@ -1876,6 +1878,7 @@ printf(" * TakeFocus - moving into new cell\n");
// Don't notify selection listeners if batching is on:
if (GetBatching())
return NS_OK;
// Be aware, the Selection instance may be destroyed after this call.
return NotifySelectionListeners(SelectionType::eNormal);
}
@ -1916,6 +1919,7 @@ nsFrameSelection::SetDragState(bool aState)
mDragSelectingCells = false;
// Notify that reason is mouse up.
PostReason(nsISelectionListener::MOUSEUP_REASON);
// Be aware, the Selection instance may be destroyed after this call.
NotifySelectionListeners(SelectionType::eNormal);
}
}
@ -2410,6 +2414,7 @@ nsFrameSelection::EndBatchChanges(int16_t aReason)
int16_t postReason = PopReason() | aReason;
PostReason(postReason);
mChangesDuringBatching = false;
// Be aware, the Selection instance may be destroyed after this call.
NotifySelectionListeners(SelectionType::eNormal);
}
}
@ -2421,7 +2426,8 @@ nsFrameSelection::NotifySelectionListeners(SelectionType aSelectionType)
int8_t index = GetIndexFromSelectionType(aSelectionType);
if (index >=0 && mDomSelections[index])
{
return mDomSelections[index]->NotifySelectionListeners();
RefPtr<Selection> selection = mDomSelections[index];
return selection->NotifySelectionListeners();
}
return NS_ERROR_FAILURE;
}
@ -3473,6 +3479,7 @@ Selection::Selection()
, mDirection(eDirNext)
, mSelectionType(SelectionType::eNormal)
, mUserInitiated(false)
, mCalledByJS(false)
, mSelectionChangeBlockerCount(0)
{
}
@ -3483,6 +3490,7 @@ Selection::Selection(nsFrameSelection* aList)
, mDirection(eDirNext)
, mSelectionType(SelectionType::eNormal)
, mUserInitiated(false)
, mCalledByJS(false)
, mSelectionChangeBlockerCount(0)
{
}
@ -4943,7 +4951,10 @@ Selection::RemoveAllRanges(ErrorResult& aRv)
RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
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());
// Also need to notify the frames!
// PresShell::CharacterDataChanged should do that on DocumentChanged
if (NS_FAILED(result)) {
@ -4966,6 +4977,14 @@ Selection::AddRange(nsIDOMRange* aDOMRange)
return result.StealNSResult();
}
void
Selection::AddRangeJS(nsRange& aRange, ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
AddRange(aRange, aRv);
}
void
Selection::AddRange(nsRange& aRange, ErrorResult& aRv)
{
@ -5020,6 +5039,8 @@ Selection::AddRangeInternal(nsRange& aRange, nsIDocument* aDocument,
if (!mFrameSelection)
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;
result = frameSelection->NotifySelectionListeners(GetType());
if (NS_FAILED(result)) {
@ -5115,6 +5136,9 @@ Selection::RemoveRange(nsRange& aRange, ErrorResult& aRv)
if (!mFrameSelection)
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;
rv = frameSelection->NotifySelectionListeners(GetType());
if (NS_FAILED(rv)) {
@ -5140,6 +5164,14 @@ Selection::CollapseNative(nsINode* aParentNode, int32_t 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
Selection::Collapse(nsINode* aParentNode, int32_t aOffset)
{
@ -5222,6 +5254,9 @@ Selection::Collapse(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv)
}
setAnchorFocusRange(0);
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());
if (NS_FAILED(result)) {
aRv.Throw(result);
@ -5240,6 +5275,14 @@ Selection::CollapseToStart()
return result.StealNSResult();
}
void
Selection::CollapseToStartJS(ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
CollapseToStart(aRv);
}
void
Selection::CollapseToStart(ErrorResult& aRv)
{
@ -5281,6 +5324,14 @@ Selection::CollapseToEnd()
return result.StealNSResult();
}
void
Selection::CollapseToEndJS(ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
CollapseToEnd(aRv);
}
void
Selection::CollapseToEnd(ErrorResult& aRv)
{
@ -5495,6 +5546,14 @@ Selection::ExtendNative(nsINode* aParentNode, int32_t 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
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(),
nsAtomCString(content->NodeInfo()->NameAtom()).get(), aOffset);
#endif
// Be aware, this instance may be destroyed after this call.
// XXX Why doesn't this call Selection::NotifySelectionListener() directly?
RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
res = frameSelection->NotifySelectionListeners(GetType());
if (NS_FAILED(res)) {
@ -5790,6 +5852,14 @@ Selection::SelectAllChildren(nsIDOMNode* aParentNode)
return result.StealNSResult();
}
void
Selection::SelectAllChildrenJS(nsINode& aNode, ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
SelectAllChildren(aNode, aRv);
}
void
Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv)
{
@ -5938,6 +6008,30 @@ Selection::GetPresShell() const
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 *
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
Selection::NotifySelectionListeners()
{
if (!mFrameSelection)
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;
if (frameSelection->GetBatching()) {
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
Selection::SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
nsINode& aFocusNode, uint32_t aFocusOffset,

View file

@ -7,6 +7,8 @@
#include "mozilla/LinkedList.h"
#include "nsCORSListenerProxy.h"
#include "nsQueryObject.h"
#include "nsIChannel.h"
#include "nsIHttpChannel.h"
#include "HttpChannelChild.h"
@ -1507,6 +1509,9 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
method, false);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsIHttpChannel> reqCh = do_QueryObject(aRequestChannel);
RefPtr<nsIHttpChannel> preCh = do_QueryObject(preHttp);
nsTArray<nsCString> preflightHeaders;
if (!aUnsafeHeaders.IsEmpty()) {
for (uint32_t i = 0; i < aUnsafeHeaders.Length(); ++i) {
@ -1535,6 +1540,23 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
rv = preflightChannel->SetNotificationCallbacks(preflightListener);
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
rv = preflightChannel->AsyncOpen2(preflightListener);
NS_ENSURE_SUCCESS(rv, rv);

View file

@ -1394,6 +1394,8 @@ CycleCollectedJSContext::ProcessStableStateQueue()
MOZ_RELEASE_ASSERT(!mDoingStableStates);
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) {
nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget();
event->Run();
@ -1477,11 +1479,33 @@ CycleCollectedJSContext::AfterProcessMicrotasks()
}
// Cleanup Indexed Database transactions:
// 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
CycleCollectedJSContext::RecursionDepth()
CycleCollectedJSContext::RecursionDepth() const
{
return mOwningThread->RecursionDepth();
}

View file

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

View file

@ -247,6 +247,9 @@
ERROR(NS_ERROR_REMOTE_XUL, FAILURE(75)),
/* The request resulted in an error page being displayed. */
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: */