mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-26 10:27:32 +09:00
Merge remote-tracking branch 'origin/master' into custom
This commit is contained in:
commit
b74770e3f1
35 changed files with 953 additions and 661 deletions
71
dom/base/SimpleTreeIterator.h
Normal file
71
dom/base/SimpleTreeIterator.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This is a light-weight tree iterator for `for` loops when full iterator
|
||||
* functionality isn't required.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_dom_SimpleTreeIterator_h
|
||||
#define mozilla_dom_SimpleTreeIterator_h
|
||||
|
||||
#include "nsINode.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class SimpleTreeIterator {
|
||||
public:
|
||||
/**
|
||||
* Initialize an iterator with aRoot. After that it can be iterated with a
|
||||
* range-based for loop. At the moment, that's the only supported form of use
|
||||
* for this iterator.
|
||||
*/
|
||||
explicit SimpleTreeIterator(nsINode& aRoot)
|
||||
: mCurrent(&aRoot)
|
||||
{
|
||||
mTree.AppendElement(&aRoot);
|
||||
}
|
||||
|
||||
// Basic support for range-based for loops.
|
||||
// This will modify the iterator as it goes.
|
||||
SimpleTreeIterator& begin() { return *this; }
|
||||
|
||||
SimpleTreeIterator end() { return SimpleTreeIterator(); }
|
||||
|
||||
bool operator!=(const SimpleTreeIterator& aOther) {
|
||||
return mCurrent != aOther.mCurrent;
|
||||
}
|
||||
|
||||
void operator++() { Next(); }
|
||||
|
||||
nsINode* operator*() { return mCurrent; }
|
||||
|
||||
private:
|
||||
// Constructor used only for end() to represent a drained iterator.
|
||||
SimpleTreeIterator()
|
||||
: mCurrent(nullptr)
|
||||
{}
|
||||
|
||||
void Next() {
|
||||
MOZ_ASSERT(mCurrent, "Don't call Next() when we have no current node");
|
||||
|
||||
mCurrent = mCurrent->GetNextNode(mTree.LastElement());
|
||||
}
|
||||
|
||||
// The current node.
|
||||
nsINode* mCurrent;
|
||||
|
||||
// The DOM tree that we're inside of right now.
|
||||
AutoTArray<nsINode*, 1> mTree;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_SimpleTreeIterator_h
|
||||
|
|
@ -210,6 +210,7 @@ EXPORTS.mozilla.dom += [
|
|||
'ScreenOrientation.h',
|
||||
'ScriptSettings.h',
|
||||
'ShadowRoot.h',
|
||||
'SimpleTreeIterator.h',
|
||||
'StructuredCloneHolder.h',
|
||||
'StructuredCloneTags.h',
|
||||
'StyleSheetList.h',
|
||||
|
|
|
|||
|
|
@ -1968,22 +1968,10 @@ nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
|
|||
}
|
||||
|
||||
void
|
||||
nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
||||
nsIPrincipal* aPrincipal)
|
||||
{
|
||||
NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
|
||||
|
||||
if (gDocumentLeakPRLog && MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
|
||||
PR_LogPrint("DOCUMENT %p ResetToURI %s", this,
|
||||
aURI->GetSpecOrDefault().get());
|
||||
}
|
||||
|
||||
mSecurityInfo = nullptr;
|
||||
|
||||
mDocumentLoadGroup = nullptr;
|
||||
|
||||
nsDocument::DisconnectNodeTree() {
|
||||
// Delete references to sub-documents and kill the subdocument map,
|
||||
// if any. It holds strong references
|
||||
// if any. This is not strictly needed, but makes the node tree
|
||||
// teardown a bit faster.
|
||||
delete mSubDocuments;
|
||||
mSubDocuments = nullptr;
|
||||
|
||||
|
|
@ -2019,6 +2007,22 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
|||
"After removing all children, there should be no root elem");
|
||||
}
|
||||
mInUnlinkOrDeletion = oldVal;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
||||
nsIPrincipal* aPrincipal)
|
||||
{
|
||||
NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
|
||||
|
||||
if (gDocumentLeakPRLog && MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
|
||||
PR_LogPrint("DOCUMENT %p ResetToURI %s", this,
|
||||
aURI->GetSpecOrDefault().get());
|
||||
}
|
||||
|
||||
mSecurityInfo = nullptr;
|
||||
|
||||
mDocumentLoadGroup = nullptr;
|
||||
|
||||
// Reset our stylesheets
|
||||
ResetStylesheetsToURI(aURI);
|
||||
|
|
@ -2029,6 +2033,8 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
|||
mListenerManager = nullptr;
|
||||
}
|
||||
|
||||
DisconnectNodeTree();
|
||||
|
||||
// Release the stylesheets list.
|
||||
mDOMStyleSheets = nullptr;
|
||||
|
||||
|
|
@ -4506,18 +4512,6 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
|
|||
mLayoutHistoryState = nullptr;
|
||||
SetScopeObject(aScriptGlobalObject);
|
||||
mHasHadDefaultView = true;
|
||||
#ifdef DEBUG
|
||||
if (!mWillReparent) {
|
||||
// We really shouldn't have a wrapper here but if we do we need to make sure
|
||||
// it has the correct parent.
|
||||
JSObject *obj = GetWrapperPreserveColor();
|
||||
if (obj) {
|
||||
JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
|
||||
NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope,
|
||||
"Wrong scope, this is really bad!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mAllowDNSPrefetch) {
|
||||
nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
|
||||
|
|
@ -9077,7 +9071,8 @@ nsDocument::CloneDocHelper(nsDocument* clone) const
|
|||
}
|
||||
|
||||
void
|
||||
nsDocument::SetReadyStateInternal(ReadyState rs)
|
||||
nsDocument::SetReadyStateInternal(ReadyState rs,
|
||||
bool updateTimingInformation)
|
||||
{
|
||||
mReadyState = rs;
|
||||
if (rs == READYSTATE_UNINITIALIZED) {
|
||||
|
|
@ -9086,7 +9081,12 @@ nsDocument::SetReadyStateInternal(ReadyState rs)
|
|||
// transition undetectable by Web content.
|
||||
return;
|
||||
}
|
||||
if (mTiming) {
|
||||
|
||||
if (updateTimingInformation && READYSTATE_LOADING == rs) {
|
||||
mLoadingTimeStamp = mozilla::TimeStamp::Now();
|
||||
}
|
||||
|
||||
if (updateTimingInformation && mTiming) {
|
||||
switch (rs) {
|
||||
case READYSTATE_LOADING:
|
||||
mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
|
||||
|
|
@ -9102,10 +9102,6 @@ nsDocument::SetReadyStateInternal(ReadyState rs)
|
|||
break;
|
||||
}
|
||||
}
|
||||
// At the time of loading start, we don't have timing object, record time.
|
||||
if (READYSTATE_LOADING == rs) {
|
||||
mLoadingTimeStamp = mozilla::TimeStamp::Now();
|
||||
}
|
||||
|
||||
RefPtr<AsyncEventDispatcher> asyncDispatcher =
|
||||
new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"),
|
||||
|
|
|
|||
|
|
@ -704,7 +704,11 @@ public:
|
|||
virtual void BeginLoad() override;
|
||||
virtual void EndLoad() override;
|
||||
|
||||
virtual void SetReadyStateInternal(ReadyState rs) override;
|
||||
// Set the readystate of the document. If updateTimingInformation is true,
|
||||
// this will record relevant timestamps in the document's performance timing.
|
||||
// Some consumers like document.open() don't want to do that.
|
||||
virtual void SetReadyStateInternal(ReadyState rs,
|
||||
bool updateTimingInformation = true) override;
|
||||
|
||||
virtual void ContentStateChanged(nsIContent* aContent,
|
||||
mozilla::EventStates aStateMask)
|
||||
|
|
@ -916,6 +920,14 @@ public:
|
|||
UpdateFrameRequestCallbackSchedulingState();
|
||||
}
|
||||
|
||||
void SetLoadEventFiring(bool aFiring) override { mLoadEventFiring = aFiring; }
|
||||
|
||||
bool SkipLoadEventAfterClose() override {
|
||||
bool skip = mSkipLoadEventAfterClose;
|
||||
mSkipLoadEventAfterClose = false;
|
||||
return skip;
|
||||
}
|
||||
|
||||
virtual nsIDocument* GetTemplateContentsOwner() override;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
|
||||
|
|
@ -1255,6 +1267,11 @@ protected:
|
|||
*/
|
||||
Element* GetTitleElement();
|
||||
|
||||
/**
|
||||
* Perform tree disconnection needed by ResetToURI and document.open()
|
||||
*/
|
||||
void DisconnectNodeTree();
|
||||
|
||||
public:
|
||||
// Get our title
|
||||
virtual void GetTitle(nsString& aTitle) override;
|
||||
|
|
@ -1458,6 +1475,20 @@ public:
|
|||
// additional sheets and sheets from the nsStyleSheetService.
|
||||
bool mStyleSetFilled:1;
|
||||
|
||||
// The HTML spec has a "iframe load in progress" flag, but that doesn't seem
|
||||
// to have the right semantics. See <https://github.com/whatwg/html/issues/4292>.
|
||||
// What we have instead is a flag that is set while the window's 'load' event is
|
||||
// firing if this document is the window's document.
|
||||
bool mLoadEventFiring : 1;
|
||||
|
||||
// The HTML spec has a "mute iframe load" flag, but that doesn't seem to have
|
||||
// the right semantics. See <https://github.com/whatwg/html/issues/4292>.
|
||||
// What we have instead is a flag that is set if completion of our document
|
||||
// via document.close() should skip firing the load event. Note that this
|
||||
// flag is only relevant for HTML documents, but lives here for reasons that
|
||||
// are documented above on SkipLoadEventAfterClose().
|
||||
bool mSkipLoadEventAfterClose : 1;
|
||||
|
||||
uint8_t mPendingFullscreenRequests;
|
||||
|
||||
uint8_t mXMLDeclarationBits;
|
||||
|
|
@ -1615,11 +1646,6 @@ private:
|
|||
// Set to true when the document is possibly controlled by the ServiceWorker.
|
||||
// Used to prevent multiple requests to ServiceWorkerManager.
|
||||
bool mMaybeServiceWorkerControlled;
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
bool mWillReparent;
|
||||
#endif
|
||||
};
|
||||
|
||||
class nsDocumentOnStack
|
||||
|
|
|
|||
|
|
@ -909,10 +909,6 @@ public:
|
|||
*/
|
||||
nsresult GetSrcdocData(nsAString& aSrcdocData);
|
||||
|
||||
bool DidDocumentOpen() {
|
||||
return mDidDocumentOpen;
|
||||
}
|
||||
|
||||
already_AddRefed<mozilla::dom::AnonymousContent>
|
||||
InsertAnonymousContent(mozilla::dom::Element& aElement,
|
||||
mozilla::ErrorResult& aError);
|
||||
|
|
@ -1448,7 +1444,7 @@ public:
|
|||
virtual void EndLoad() = 0;
|
||||
|
||||
enum ReadyState { READYSTATE_UNINITIALIZED = 0, READYSTATE_LOADING = 1, READYSTATE_INTERACTIVE = 3, READYSTATE_COMPLETE = 4};
|
||||
virtual void SetReadyStateInternal(ReadyState rs) = 0;
|
||||
virtual void SetReadyStateInternal(ReadyState rs, bool updateTimingInformation = true) = 0;
|
||||
ReadyState GetReadyStateEnum()
|
||||
{
|
||||
return mReadyState;
|
||||
|
|
@ -2186,6 +2182,19 @@ public:
|
|||
mAllowXULXBL = eTriTrue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag whether we're about to fire the window's load event for this document.
|
||||
*/
|
||||
virtual void SetLoadEventFiring(bool aFiring) = 0;
|
||||
|
||||
/**
|
||||
* Test whether we should be firing a load event for this document after a
|
||||
* document.close().
|
||||
* This method should only be called at the point when the load event is about
|
||||
* to be fired, since it resets `skip`.
|
||||
*/
|
||||
virtual bool SkipLoadEventAfterClose() = 0;
|
||||
|
||||
/**
|
||||
* Returns the template content owner document that owns the content of
|
||||
* HTMLTemplateElement.
|
||||
|
|
@ -3146,11 +3155,6 @@ protected:
|
|||
// Whether the document was created by a srcdoc iframe.
|
||||
bool mIsSrcdocDocument : 1;
|
||||
|
||||
// Records whether we've done a document.open. If this is true, it's possible
|
||||
// for nodes from this document to have outdated wrappers in their wrapper
|
||||
// caches.
|
||||
bool mDidDocumentOpen : 1;
|
||||
|
||||
// Whether this document has a display document and thus is considered to
|
||||
// be a resource document. Normally this is the same as !!mDisplayDocument,
|
||||
// but mDisplayDocument is cleared during Unlink. mHasDisplayDocument is
|
||||
|
|
|
|||
|
|
@ -1549,27 +1549,6 @@ AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
CheckForOutdatedParent(nsINode* aParent, nsINode* aNode)
|
||||
{
|
||||
if (JSObject* existingObjUnrooted = aNode->GetWrapper()) {
|
||||
JS::Rooted<JSObject*> existingObj(RootingCx(), existingObjUnrooted);
|
||||
|
||||
AutoJSContext cx;
|
||||
nsIGlobalObject* global = aParent->OwnerDoc()->GetScopeObject();
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
if (js::GetGlobalForObjectCrossCompartment(existingObj) !=
|
||||
global->GetGlobalJSObject()) {
|
||||
JSAutoCompartment ac(cx, existingObj);
|
||||
nsresult rv = ReparentWrapper(cx, existingObj);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
ReparentWrappersInSubtree(nsIContent* aRoot)
|
||||
{
|
||||
|
|
@ -1631,9 +1610,6 @@ nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
|
|||
if (OwnerDoc() != aKid->OwnerDoc()) {
|
||||
rv = AdoptNodeIntoOwnerDoc(this, aKid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (OwnerDoc()->DidDocumentOpen()) {
|
||||
rv = CheckForOutdatedParent(this, aKid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
uint32_t childCount = aChildArray.ChildCount();
|
||||
|
|
@ -2481,11 +2457,6 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
|||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (doc->DidDocumentOpen()) {
|
||||
aError = CheckForOutdatedParent(this, aNewChild);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -113,19 +113,25 @@ var testFramesLoaded = function() {
|
|||
|
||||
// test that a document can be framed under a javascript: URL opened by the
|
||||
// same site as the frame
|
||||
// We can't set a load event listener before calling document.open/document.write, because those will remove such listeners. So we need to define a function that the new window will be able to call.
|
||||
function frameInJSURILoaded(win) {
|
||||
var test = win.document.getElementById("sameorigin3")
|
||||
.contentDocument.getElementById("test");
|
||||
ok(test != null, "frame under javascript: URL should have loaded.");
|
||||
win.close();
|
||||
|
||||
// run last test
|
||||
if (!isUnique) {
|
||||
testFrameInDataURI();
|
||||
} else {
|
||||
testFrameNotLoadedInDataURI();
|
||||
}
|
||||
}
|
||||
|
||||
var testFrameInJSURI = function() {
|
||||
var html = '<iframe id="sameorigin3" src="http://mochi.test:8888/tests/dom/base/test/file_x-frame-options_page.sjs?testid=sameorigin3&xfo=sameorigin"></iframe>';
|
||||
var win = window.open();
|
||||
win.onload = function() {
|
||||
var test = win.document.getElementById("sameorigin3")
|
||||
.contentDocument.getElementById("test");
|
||||
ok(test != null, "frame under javascript: URL should have loaded.");
|
||||
win.close();
|
||||
|
||||
// run last test
|
||||
testFrameInDataURI();
|
||||
}
|
||||
win.location.href = "javascript:document.write('"+html+"');document.close();";
|
||||
win.location.href = "javascript:document.open(); onload = opener.frameInJSURILoaded.bind(null, window); document.write('"+html+"');document.close();";
|
||||
}
|
||||
|
||||
// test that a document can be framed under a data: URL opened by the
|
||||
|
|
|
|||
|
|
@ -514,8 +514,9 @@ private:
|
|||
{
|
||||
// NS_IF_RELEASE because we might have been unlinked before
|
||||
nsISupports* ptr = GetISupports();
|
||||
NS_IF_RELEASE(ptr);
|
||||
// Clear mPtrBits before the release to prevent reentrance.
|
||||
mPtrBits = 0;
|
||||
NS_IF_RELEASE(ptr);
|
||||
}
|
||||
|
||||
uintptr_t mPtrBits;
|
||||
|
|
|
|||
|
|
@ -166,11 +166,11 @@ EventListenerManager::~EventListenerManager()
|
|||
// XXX azakai: Is there any reason to not just call Disconnect
|
||||
// from right here, if not previously called?
|
||||
NS_ASSERTION(!mTarget, "didn't call Disconnect");
|
||||
RemoveAllListeners();
|
||||
RemoveAllListenersSilently();
|
||||
}
|
||||
|
||||
void
|
||||
EventListenerManager::RemoveAllListeners()
|
||||
EventListenerManager::RemoveAllListenersSilently()
|
||||
{
|
||||
if (mClearingListeners) {
|
||||
return;
|
||||
|
|
@ -1329,7 +1329,7 @@ void
|
|||
EventListenerManager::Disconnect()
|
||||
{
|
||||
mTarget = nullptr;
|
||||
RemoveAllListeners();
|
||||
RemoveAllListenersSilently();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1734,6 +1734,21 @@ EventListenerManager::IsApzAwareEvent(nsIAtom* aEvent)
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
EventListenerManager::RemoveAllListeners()
|
||||
{
|
||||
while (!mListeners.IsEmpty()) {
|
||||
size_t idx = mListeners.Length() - 1;
|
||||
nsCOMPtr<nsIAtom> type = mListeners.ElementAt(idx).mTypeAtom;
|
||||
EventMessage message = mListeners.ElementAt(idx).mEventMessage;
|
||||
mListeners.RemoveElementAt(idx);
|
||||
NotifyEventListenerRemoved(type);
|
||||
if (IsDeviceType(message)) {
|
||||
DisableDevice(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<nsIScriptGlobalObject>
|
||||
EventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -472,6 +472,12 @@ public:
|
|||
bool IsApzAwareListener(Listener* aListener);
|
||||
bool IsApzAwareEvent(nsIAtom* aEvent);
|
||||
|
||||
/**
|
||||
* Remove all event listeners from the event target this EventListenerManager
|
||||
* is for.
|
||||
*/
|
||||
void RemoveAllListeners();
|
||||
|
||||
protected:
|
||||
void HandleEventInternal(nsPresContext* aPresContext,
|
||||
WidgetEvent* aEvent,
|
||||
|
|
@ -604,7 +610,7 @@ protected:
|
|||
const nsAString& aTypeString,
|
||||
const EventListenerFlags& aFlags,
|
||||
bool aAllEvents = false);
|
||||
void RemoveAllListeners();
|
||||
void RemoveAllListenersSilently();
|
||||
void NotifyEventListenerRemoved(nsIAtom* aUserType);
|
||||
const EventTypeData* GetTypeDataForIID(const nsIID& aIID);
|
||||
const EventTypeData* GetTypeDataForEventName(nsIAtom* aName);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "mozilla/dom/HTMLMediaElementBinding.h"
|
||||
#include "mozilla/dom/HTMLSourceElement.h"
|
||||
#include "mozilla/dom/ElementInlines.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
|
|
@ -171,6 +172,22 @@ static const unsigned short MEDIA_ERR_NETWORK = 2;
|
|||
static const unsigned short MEDIA_ERR_DECODE = 3;
|
||||
static const unsigned short MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
|
||||
|
||||
static void
|
||||
ResolvePromisesWithUndefined(const nsTArray<RefPtr<Promise>>& aPromises)
|
||||
{
|
||||
for (auto& promise : aPromises) {
|
||||
promise->MaybeResolveWithUndefined();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
RejectPromises(const nsTArray<RefPtr<Promise>>& aPromises, nsresult aError)
|
||||
{
|
||||
for (auto& promise : aPromises) {
|
||||
promise->MaybeReject(aError);
|
||||
}
|
||||
}
|
||||
|
||||
// Under certain conditions there may be no-one holding references to
|
||||
// a media element from script, DOM parent, etc, but the element may still
|
||||
// fire meaningful events in the future so we can't destroy it yet:
|
||||
|
|
@ -261,6 +278,75 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* If no error is passed while constructing an instance, the instance will
|
||||
* resolve the passed promises with undefined; otherwise, the instance will
|
||||
* reject the passed promises with the passed error.
|
||||
*
|
||||
* The constructor appends the constructed instance into the passed media
|
||||
* element's mPendingPlayPromisesRunners member and once the the runner is run
|
||||
* (whether fulfilled or canceled), it removes itself from
|
||||
* mPendingPlayPromisesRunners.
|
||||
*/
|
||||
class HTMLMediaElement::nsResolveOrRejectPendingPlayPromisesRunner : public nsMediaEvent
|
||||
{
|
||||
nsTArray<RefPtr<Promise>> mPromises;
|
||||
nsresult mError;
|
||||
|
||||
public:
|
||||
nsResolveOrRejectPendingPlayPromisesRunner(HTMLMediaElement* aElement,
|
||||
nsTArray<RefPtr<Promise>>&& aPromises,
|
||||
nsresult aError = NS_OK)
|
||||
: nsMediaEvent(aElement)
|
||||
, mPromises(Move(aPromises))
|
||||
, mError(aError)
|
||||
{
|
||||
mElement->mPendingPlayPromisesRunners.AppendElement(this);
|
||||
}
|
||||
|
||||
void ResolveOrReject()
|
||||
{
|
||||
if (NS_SUCCEEDED(mError)) {
|
||||
ResolvePromisesWithUndefined(mPromises);
|
||||
} else {
|
||||
RejectPromises(mPromises, mError);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
if (!IsCancelled()) {
|
||||
ResolveOrReject();
|
||||
}
|
||||
|
||||
mElement->mPendingPlayPromisesRunners.RemoveElement(this);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class HTMLMediaElement::nsNotifyAboutPlayingRunner : public nsResolveOrRejectPendingPlayPromisesRunner
|
||||
{
|
||||
public:
|
||||
nsNotifyAboutPlayingRunner(HTMLMediaElement* aElement,
|
||||
nsTArray<RefPtr<Promise>>&& aPendingPlayPromises)
|
||||
: nsResolveOrRejectPendingPlayPromisesRunner(aElement,
|
||||
Move(aPendingPlayPromises))
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
if (IsCancelled()) {
|
||||
mElement->mPendingPlayPromisesRunners.RemoveElement(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mElement->DispatchEvent(NS_LITERAL_STRING("playing"));
|
||||
return nsResolveOrRejectPendingPlayPromisesRunner::Run();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class nsSourceErrorEventRunner : public nsMediaEvent
|
||||
{
|
||||
private:
|
||||
|
|
@ -826,6 +912,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTM
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys)
|
||||
#endif
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedVideoStreamTrack)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPlayPromises)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
|
||||
|
|
@ -853,6 +940,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLE
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
|
||||
#endif
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedVideoStreamTrack)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPlayPromises)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
|
||||
|
|
@ -1044,6 +1132,14 @@ void HTMLMediaElement::AbortExistingLoads()
|
|||
// with a different load ID to silently be cancelled.
|
||||
mCurrentLoadID++;
|
||||
|
||||
// Immediately reject or resolve the already-dispatched
|
||||
// nsResolveOrRejectPendingPlayPromisesRunners. These runners won't be
|
||||
// executed again later since the mCurrentLoadID had been changed.
|
||||
for (auto& runner : mPendingPlayPromisesRunners) {
|
||||
runner->ResolveOrReject();
|
||||
}
|
||||
mPendingPlayPromisesRunners.Clear();
|
||||
|
||||
if (mChannelLoader) {
|
||||
mChannelLoader->Cancel();
|
||||
mChannelLoader = nullptr;
|
||||
|
|
@ -1107,7 +1203,10 @@ void HTMLMediaElement::AbortExistingLoads()
|
|||
NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?");
|
||||
// ChangeNetworkState() will call UpdateAudioChannelPlayingState()
|
||||
// indirectly which depends on mPaused. So we need to update mPaused first.
|
||||
mPaused = true;
|
||||
if (!mPaused) {
|
||||
mPaused = true;
|
||||
RejectPromises(TakePendingPlayPromises(), NS_ERROR_DOM_MEDIA_ABORT_ERR);
|
||||
}
|
||||
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
|
||||
|
||||
|
|
@ -1149,6 +1248,7 @@ void HTMLMediaElement::NoSupportedMediaSourceError(const nsACString& aErrorDetai
|
|||
mErrorSink->SetError(MEDIA_ERR_SRC_NOT_SUPPORTED, aErrorDetails);
|
||||
ChangeDelayLoadStatus(false);
|
||||
UpdateAudioChannelPlayingState();
|
||||
RejectPromises(TakePendingPlayPromises(), NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR);
|
||||
}
|
||||
|
||||
typedef void (HTMLMediaElement::*SyncSectionFn)();
|
||||
|
|
@ -1953,14 +2053,7 @@ HTMLMediaElement::Seek(double aTime,
|
|||
// aTime should be non-NaN.
|
||||
MOZ_ASSERT(!mozilla::IsNaN(aTime));
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(OwnerDoc()->GetInnerWindow());
|
||||
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
RefPtr<Promise> promise = CreateDOMPromise(aRv);
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
|
|
@ -2208,6 +2301,7 @@ HTMLMediaElement::Pause(ErrorResult& aRv)
|
|||
if (!oldPaused) {
|
||||
FireTimeUpdate(false);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
|
||||
AsyncRejectPendingPlayPromises(NS_ERROR_DOM_MEDIA_ABORT_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3200,36 +3294,86 @@ HTMLMediaElement::NotifyXPCOMShutdown()
|
|||
ShutdownDecoder();
|
||||
}
|
||||
|
||||
void
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::Play(ErrorResult& aRv)
|
||||
{
|
||||
if (!IsAllowedToPlay()) {
|
||||
MaybeDoLoad();
|
||||
return;
|
||||
|
||||
// A blocked media element will be resumed later, so we return a pending
|
||||
// promise which might be resolved/rejected depends on the result of
|
||||
// resuming the blocked media element.
|
||||
RefPtr<Promise> promise = CreateDOMPromise(aRv);
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mPendingPlayPromises.AppendElement(promise);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsresult rv = PlayInternal();
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
}
|
||||
RefPtr<Promise> promise = PlayInternal(aRv);
|
||||
|
||||
OpenUnsupportedMediaWithExternalAppIfNeeded();
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLMediaElement::PlayInternal()
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::PlayInternal(ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(!aRv.Failed());
|
||||
|
||||
// 4.8.12.8
|
||||
// When the play() method on a media element is invoked, the user agent must
|
||||
// run the following steps.
|
||||
|
||||
// 4.8.12.8 - Step 1:
|
||||
// If the media element is not allowed to play, return a promise rejected
|
||||
// with a "NotAllowedError" DOMException and abort these steps.
|
||||
if (!IsAllowedToPlay()) {
|
||||
// NOTE: for promise-based-play, will return a rejected promise here.
|
||||
aRv.Throw(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 4.8.12.8 - Step 2:
|
||||
// If the media element's error attribute is not null and its code
|
||||
// attribute has the value MEDIA_ERR_SRC_NOT_SUPPORTED, return a promise
|
||||
// rejected with a "NotSupportedError" DOMException and abort these steps.
|
||||
if (GetError() && GetError()->Code() == MEDIA_ERR_SRC_NOT_SUPPORTED) {
|
||||
aRv.Throw(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 4.8.12.8 - Step 3:
|
||||
// Let promise be a new promise and append promise to the list of pending
|
||||
// play promises.
|
||||
RefPtr<Promise> promise = CreateDOMPromise(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
mPendingPlayPromises.AppendElement(promise);
|
||||
|
||||
// Play was not blocked so assume user interacted with the element.
|
||||
mHasUserInteraction = true;
|
||||
|
||||
StopSuspendingAfterFirstFrame();
|
||||
SetPlayedOrSeeked(true);
|
||||
|
||||
// 4.8.12.8 - Step 4:
|
||||
// If the media element's networkState attribute has the value NETWORK_EMPTY,
|
||||
// invoke the media element's resource selection algorithm.
|
||||
MaybeDoLoad();
|
||||
if (mSuspendedForPreloadNone) {
|
||||
ResumeLoad(PRELOAD_ENOUGH);
|
||||
}
|
||||
|
||||
// 4.8.12.8 - Step 5:
|
||||
// If the playback has ended and the direction of playback is forwards,
|
||||
// seek to the earliest possible position of the media resource.
|
||||
|
||||
// Even if we just did Load() or ResumeLoad(), we could already have a decoder
|
||||
// here if we managed to clone an existing decoder.
|
||||
if (mDecoder) {
|
||||
|
|
@ -3239,7 +3383,14 @@ HTMLMediaElement::PlayInternal()
|
|||
if (!mPausedForInactiveDocumentOrChannel) {
|
||||
nsresult rv = mDecoder->Play();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
// We don't need to remove the _promise_ from _mPendingPlayPromises_ here.
|
||||
// If something wrong between |mPendingPlayPromises.AppendElement(promise);|
|
||||
// and here, the _promise_ should already have been rejected. Otherwise,
|
||||
// the _promise_ won't be returned to JS at all, so just leave it in the
|
||||
// _mPendingPlayPromises_ and let it be resolved/rejected with the
|
||||
// following actions and the promise-resolution won't be observed at all.
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3248,7 +3399,7 @@ HTMLMediaElement::PlayInternal()
|
|||
mCurrentPlayRangeStart = CurrentTime();
|
||||
}
|
||||
|
||||
bool oldPaused = mPaused;
|
||||
const bool oldPaused = mPaused;
|
||||
mPaused = false;
|
||||
mAutoplaying = false;
|
||||
SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
|
||||
|
|
@ -3265,8 +3416,27 @@ HTMLMediaElement::PlayInternal()
|
|||
// media, the event would be pending until media is resumed.
|
||||
// TODO: If the playback has ended, then the user agent must set
|
||||
// seek to the effective start.
|
||||
|
||||
// 4.8.12.8 - Step 6:
|
||||
// If the media element's paused attribute is true, run the following steps:
|
||||
if (oldPaused) {
|
||||
// 6.1. Change the value of paused to false. (Already done.)
|
||||
// This step is uplifted because the "block-media-playback" feature needs
|
||||
// the mPaused to be false before UpdateAudioChannelPlayingState() being
|
||||
// called.
|
||||
|
||||
// 6.2. If the show poster flag is true, set the element's show poster flag
|
||||
// to false and run the time marches on steps.
|
||||
|
||||
// 6.3. Queue a task to fire a simple event named play at the element.
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("play"));
|
||||
|
||||
// 6.4. If the media element's readyState attribute has the value
|
||||
// HAVE_NOTHING, HAVE_METADATA, or HAVE_CURRENT_DATA, queue a task to
|
||||
// fire a simple event named waiting at the element.
|
||||
// Otherwise, the media element's readyState attribute has the value
|
||||
// HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA: notify about playing for the
|
||||
// element.
|
||||
switch (mReadyState) {
|
||||
case nsIDOMHTMLMediaElement::HAVE_NOTHING:
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
|
||||
|
|
@ -3279,12 +3449,20 @@ HTMLMediaElement::PlayInternal()
|
|||
case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
|
||||
case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
|
||||
FireTimeUpdate(false);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
|
||||
NotifyAboutPlaying();
|
||||
break;
|
||||
}
|
||||
} else if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
|
||||
// 7. Otherwise, if the media element's readyState attribute has the value
|
||||
// HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA, take pending play promises and
|
||||
// queue a task to resolve pending play promises with the result.
|
||||
AsyncResolvePendingPlayPromises();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
// 8. Set the media element's autoplaying flag to false. (Already done.)
|
||||
|
||||
// 9. Return promise.
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -3302,9 +3480,10 @@ NS_IMETHODIMP HTMLMediaElement::Play()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = PlayInternal();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
ErrorResult rv;
|
||||
RefPtr<Promise> toBeIgnored = PlayInternal(rv);
|
||||
if (rv.Failed()) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
OpenUnsupportedMediaWithExternalAppIfNeeded();
|
||||
|
|
@ -4463,7 +4642,12 @@ void HTMLMediaElement::PlaybackEnded()
|
|||
return;
|
||||
}
|
||||
|
||||
Pause();
|
||||
FireTimeUpdate(false);
|
||||
|
||||
if (!mPaused) {
|
||||
Pause();
|
||||
AsyncRejectPendingPlayPromises(NS_ERROR_DOM_MEDIA_ABORT_ERR);
|
||||
}
|
||||
|
||||
if (mSrcStream) {
|
||||
// A MediaStream that goes from inactive to active shall be eligible for
|
||||
|
|
@ -4471,7 +4655,6 @@ void HTMLMediaElement::PlaybackEnded()
|
|||
mAutoplaying = true;
|
||||
}
|
||||
|
||||
FireTimeUpdate(false);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
|
||||
}
|
||||
|
||||
|
|
@ -4865,7 +5048,7 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
|
|||
DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
|
||||
if (!mPaused) {
|
||||
mWaitingForKey = NOT_WAITING_FOR_KEY;
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
|
||||
NotifyAboutPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5111,7 +5294,14 @@ nsresult HTMLMediaElement::DispatchAsyncEvent(const nsAString& aName)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this);
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
|
||||
if (aName.EqualsLiteral("playing")) {
|
||||
event = new nsNotifyAboutPlayingRunner(this, TakePendingPlayPromises());
|
||||
} else {
|
||||
event = new nsAsyncEventRunner(aName, this);
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(event);
|
||||
|
||||
return NS_OK;
|
||||
|
|
@ -6601,5 +6791,64 @@ HTMLMediaElement::MarkAsContentSource(CallerAPI aAPI)
|
|||
}
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<Promise>>
|
||||
HTMLMediaElement::TakePendingPlayPromises()
|
||||
{
|
||||
return Move(mPendingPlayPromises);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::NotifyAboutPlaying()
|
||||
{
|
||||
// Stick to the DispatchAsyncEvent() call path for now because we want to
|
||||
// trigger some telemetry-related codes in the DispatchAsyncEvent() method.
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::CreateDOMPromise(ErrorResult& aRv) const
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(OwnerDoc()->GetInnerWindow());
|
||||
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Promise::Create(global, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::AsyncResolvePendingPlayPromises()
|
||||
{
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> event
|
||||
= new nsResolveOrRejectPendingPlayPromisesRunner(this,
|
||||
TakePendingPlayPromises());
|
||||
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::AsyncRejectPendingPlayPromises(nsresult aError)
|
||||
{
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> event
|
||||
= new nsResolveOrRejectPendingPlayPromisesRunner(this,
|
||||
TakePendingPlayPromises(),
|
||||
aError);
|
||||
|
||||
NS_DispatchToMainThread(event);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
#include "DecoderTraits.h"
|
||||
#include "nsIAudioChannelAgent.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/TextTrackManager.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "MediaDecoder.h"
|
||||
|
|
@ -78,6 +77,7 @@ namespace dom {
|
|||
|
||||
class MediaError;
|
||||
class MediaSource;
|
||||
class Promise;
|
||||
class TextTrackList;
|
||||
class AudioTrackList;
|
||||
class VideoTrackList;
|
||||
|
|
@ -549,7 +549,7 @@ public:
|
|||
SetHTMLBoolAttr(nsGkAtoms::loop, aValue, aRv);
|
||||
}
|
||||
|
||||
void Play(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> Play(ErrorResult& aRv);
|
||||
|
||||
void Pause(ErrorResult& aRv);
|
||||
|
||||
|
|
@ -835,7 +835,7 @@ protected:
|
|||
nsTArray<Pair<nsString, RefPtr<MediaInputPort>>> mTrackPorts;
|
||||
};
|
||||
|
||||
nsresult PlayInternal();
|
||||
already_AddRefed<Promise> PlayInternal(ErrorResult& aRv);
|
||||
|
||||
/** Use this method to change the mReadyState member, so required
|
||||
* events can be fired.
|
||||
|
|
@ -981,6 +981,7 @@ protected:
|
|||
void AbortExistingLoads();
|
||||
|
||||
/**
|
||||
* These are the dedicated media source failure steps.
|
||||
* Called when all potential resources are exhausted. Changes network
|
||||
* state to NETWORK_NO_SOURCE, and sends error event with code
|
||||
* MEDIA_ERR_SRC_NOT_SUPPORTED.
|
||||
|
|
@ -1285,6 +1286,8 @@ protected:
|
|||
void MaybeNotifyMediaResumed(SuspendTypes aSuspend);
|
||||
|
||||
class nsAsyncEventRunner;
|
||||
class nsNotifyAboutPlayingRunner;
|
||||
class nsResolveOrRejectPendingPlayPromisesRunner;
|
||||
using nsGenericHTMLElement::DispatchEvent;
|
||||
// For nsAsyncEventRunner.
|
||||
nsresult DispatchEvent(const nsAString& aName);
|
||||
|
|
@ -1293,6 +1296,24 @@ protected:
|
|||
// triggers play() after loaded fail. eg. preload the data before start play.
|
||||
void OpenUnsupportedMediaWithExternalAppIfNeeded() const;
|
||||
|
||||
// This method moves the mPendingPlayPromises into a temperate object. So the
|
||||
// mPendingPlayPromises is cleared after this method call.
|
||||
nsTArray<RefPtr<Promise>> TakePendingPlayPromises();
|
||||
|
||||
// This method snapshots the mPendingPlayPromises by TakePendingPlayPromises()
|
||||
// and queues a task to resolve them.
|
||||
void AsyncResolvePendingPlayPromises();
|
||||
|
||||
// This method snapshots the mPendingPlayPromises by TakePendingPlayPromises()
|
||||
// and queues a task to reject them.
|
||||
void AsyncRejectPendingPlayPromises(nsresult aError);
|
||||
|
||||
// This method snapshots the mPendingPlayPromises by TakePendingPlayPromises()
|
||||
// and queues a task to resolve them also to dispatch a "playing" event.
|
||||
void NotifyAboutPlaying();
|
||||
|
||||
already_AddRefed<Promise> CreateDOMPromise(ErrorResult& aRv) const;
|
||||
|
||||
// The current decoder. Load() has been called on this decoder.
|
||||
// At most one of mDecoder and mSrcStream can be non-null.
|
||||
RefPtr<MediaDecoder> mDecoder;
|
||||
|
|
@ -1684,6 +1705,17 @@ private:
|
|||
Visibility mVisibilityState;
|
||||
|
||||
UniquePtr<ErrorSink> mErrorSink;
|
||||
|
||||
// A list of pending play promises. The elements are pushed during the play()
|
||||
// method call and are resolved/rejected during further playback steps.
|
||||
nsTArray<RefPtr<Promise>> mPendingPlayPromises;
|
||||
|
||||
// A list of already-dispatched but not yet run
|
||||
// nsResolveOrRejectPendingPlayPromisesRunners.
|
||||
// Runners whose Run() method is called remove themselves from this list.
|
||||
// We keep track of these because the load algorithm resolves/rejects all
|
||||
// already-dispatched pending play promises.
|
||||
nsTArray<nsResolveOrRejectPendingPlayPromisesRunner*> mPendingPlayPromisesRunners;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "nsPrintfCString.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsIDocumentLoader.h"
|
||||
#include "nsIHTMLContentSink.h"
|
||||
#include "nsIXMLContentSink.h"
|
||||
#include "nsHTMLParts.h"
|
||||
|
|
@ -84,6 +85,7 @@
|
|||
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/FallbackEncoding.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "nsIEditingSession.h"
|
||||
#include "nsIEditor.h"
|
||||
|
|
@ -107,12 +109,14 @@
|
|||
#include "nsIImageDocument.h"
|
||||
#include "mozilla/dom/HTMLBodyElement.h"
|
||||
#include "mozilla/dom/HTMLDocumentBinding.h"
|
||||
#include "mozilla/dom/SimpleTreeIterator.h"
|
||||
#include "nsCharsetSource.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIStructuredCloneContainer.h"
|
||||
#include "nsLayoutStylesheetCache.h"
|
||||
#include "mozilla/StyleSheet.h"
|
||||
#include "mozilla/StyleSheetInlines.h"
|
||||
|
|
@ -842,6 +846,24 @@ nsHTMLDocument::EndLoad()
|
|||
if (turnOnEditing) {
|
||||
EditingStateChanged();
|
||||
}
|
||||
|
||||
if (!GetWindow()) {
|
||||
// This is a document that's not in a window. For example, this could be an
|
||||
// XMLHttpRequest responseXML document, or a document created via DOMParser
|
||||
// or DOMImplementation. We don't reach this code normally for such
|
||||
// documents (which is not obviously correct), but can reach it via
|
||||
// document.open()/document.close().
|
||||
//
|
||||
// Such documents don't fire load events, but per spec should set their
|
||||
// readyState to "complete" when parsing and all loading of subresources is
|
||||
// done. Parsing is done now, and documents not in a window don't load
|
||||
// subresources, so just go ahead and mark ourselves as complete.
|
||||
SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE,
|
||||
/* updateTimingInformation = */ false);
|
||||
|
||||
// Reset mSkipLoadEventAfterClose just in case.
|
||||
mSkipLoadEventAfterClose = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1410,19 +1432,21 @@ already_AddRefed<nsIDocument>
|
|||
nsHTMLDocument::Open(JSContext* cx,
|
||||
const nsAString& aType,
|
||||
const nsAString& aReplace,
|
||||
ErrorResult& rv)
|
||||
ErrorResult& aError)
|
||||
{
|
||||
// Implements the "When called with two arguments (or fewer)" steps here:
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#opening-the-input-stream
|
||||
// Implements
|
||||
// <https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps>
|
||||
|
||||
NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast<nsIDOMHTMLDocument*>(this)),
|
||||
"XOW should have caught this!");
|
||||
|
||||
// Step 1 - Throw if we're the wrong type of document.
|
||||
if (!IsHTMLDocument() || mDisableDocWrite || !IsMasterDocument()) {
|
||||
// No calling document.open() on XHTML
|
||||
rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set up the content type for insertion
|
||||
nsAutoCString contentType;
|
||||
contentType.AssignLiteral("text/html");
|
||||
|
||||
|
|
@ -1435,51 +1459,7 @@ nsHTMLDocument::Open(JSContext* cx,
|
|||
contentType.AssignLiteral("text/plain");
|
||||
}
|
||||
|
||||
// If we already have a parser we ignore the document.open call.
|
||||
if (mParser || mParserAborted) {
|
||||
// The WHATWG spec says: "If the document has an active parser that isn't
|
||||
// a script-created parser, and the insertion point associated with that
|
||||
// parser's input stream is not undefined (that is, it does point to
|
||||
// somewhere in the input stream), then the method does nothing. Abort
|
||||
// these steps and return the Document object on which the method was
|
||||
// invoked."
|
||||
// Note that aborting a parser leaves the parser "active" with its
|
||||
// insertion point "not undefined". We track this using mParserAborted,
|
||||
// because aborting a parser nulls out mParser.
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
// No calling document.open() without a script global object
|
||||
if (!mScriptGlobalObject) {
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
nsPIDOMWindowOuter* outer = GetWindow();
|
||||
if (!outer || (GetInnerWindow() != outer->GetCurrentInnerWindow())) {
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
// check whether we're in the middle of unload. If so, ignore this call.
|
||||
nsCOMPtr<nsIDocShell> shell(mDocumentContainer);
|
||||
if (!shell) {
|
||||
// We won't be able to create a parser anyway.
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
bool inUnload;
|
||||
shell->GetIsInUnload(&inUnload);
|
||||
if (inUnload) {
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
// Note: We want to use GetEntryDocument here because this document
|
||||
// should inherit the security information of the document that's opening us,
|
||||
// (since if it's secure, then it's presumably trusted).
|
||||
// Step 3 - Get the entryDocument for security checks
|
||||
nsCOMPtr<nsIDocument> callerDoc = GetEntryDocument();
|
||||
if (!callerDoc) {
|
||||
// If we're called from C++ or in some other way without an originating
|
||||
|
|
@ -1489,67 +1469,39 @@ nsHTMLDocument::Open(JSContext* cx,
|
|||
// change the principals of a document for security reasons we'll have to
|
||||
// refuse to go ahead with this call.
|
||||
|
||||
rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Grab a reference to the calling documents security info (if any)
|
||||
// and URIs as they may be lost in the call to Reset().
|
||||
nsCOMPtr<nsISupports> securityInfo = callerDoc->GetSecurityInfo();
|
||||
nsCOMPtr<nsIURI> uri = callerDoc->GetDocumentURI();
|
||||
nsCOMPtr<nsIURI> baseURI = callerDoc->GetBaseURI();
|
||||
nsCOMPtr<nsIPrincipal> callerPrincipal = callerDoc->NodePrincipal();
|
||||
nsCOMPtr<nsIChannel> callerChannel = callerDoc->GetChannel();
|
||||
|
||||
// We're called from script. Make sure the script is from the same
|
||||
// origin, not just that the caller can access the document. This is
|
||||
// needed to keep document principals from ever changing, which is
|
||||
// needed because of the way we use our XOW code, and is a sane
|
||||
// thing to do anyways.
|
||||
|
||||
bool equals = false;
|
||||
if (NS_FAILED(callerPrincipal->Equals(NodePrincipal(), &equals)) ||
|
||||
!equals) {
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsIURI> callerDocURI = callerDoc->GetDocumentURI();
|
||||
nsCOMPtr<nsIURI> thisURI = nsIDocument::GetDocumentURI();
|
||||
printf("nsHTMLDocument::Open callerDoc %s this %s\n",
|
||||
callerDocURI ? callerDocURI->GetSpecOrDefault().get() : "",
|
||||
thisURI ? thisURI->GetSpecOrDefault().get() : "");
|
||||
#endif
|
||||
|
||||
rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
// Step 4 - Throw if we're not same-origin
|
||||
if (!callerDoc->NodePrincipal()->Equals(NodePrincipal())) {
|
||||
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Stop current loads targeted at the window this document is in.
|
||||
if (mScriptGlobalObject) {
|
||||
nsCOMPtr<nsIContentViewer> cv;
|
||||
shell->GetContentViewer(getter_AddRefs(cv));
|
||||
|
||||
if (cv) {
|
||||
bool okToUnload;
|
||||
if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) && !okToUnload) {
|
||||
// We don't want to unload, so stop here, but don't throw an
|
||||
// exception.
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
// Now double-check that our invariants still hold.
|
||||
if (!mScriptGlobalObject) {
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
nsPIDOMWindowOuter* outer = GetWindow();
|
||||
if (!outer || (GetInnerWindow() != outer->GetCurrentInnerWindow())) {
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
// Step 5 - If we have an active parser, abort with no-op
|
||||
if (mParser || mParserAborted) {
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
// Step 6 - Check if document.open() is called during unload
|
||||
nsCOMPtr<nsIDocShell> shell(mDocumentContainer);
|
||||
if (shell) {
|
||||
bool inUnload;
|
||||
shell->GetIsInUnload(&inUnload);
|
||||
if (inUnload) {
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 7 - Stop existing navigation of our browsing context (and all
|
||||
// other loads it's doing) if we're the active document of our browsing
|
||||
// context. If there's no existing navigation, we don't want to stop
|
||||
// anything.
|
||||
if (shell && IsCurrentActiveDocument() &&
|
||||
mScriptGlobalObject) {
|
||||
nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(shell));
|
||||
webnav->Stop(nsIWebNavigation::STOP_NETWORK);
|
||||
|
||||
|
|
@ -1560,189 +1512,121 @@ nsHTMLDocument::Open(JSContext* cx,
|
|||
EnsureOnloadBlocker();
|
||||
}
|
||||
|
||||
// The open occurred after the document finished loading.
|
||||
// So we reset the document and then reinitialize it.
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
|
||||
rv = NS_NewChannel(getter_AddRefs(channel),
|
||||
uri,
|
||||
callerDoc,
|
||||
nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
|
||||
nsIContentPolicy::TYPE_OTHER,
|
||||
group);
|
||||
|
||||
if (rv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (callerChannel) {
|
||||
nsLoadFlags callerLoadFlags;
|
||||
rv = callerChannel->GetLoadFlags(&callerLoadFlags);
|
||||
if (rv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsLoadFlags loadFlags;
|
||||
rv = channel->GetLoadFlags(&loadFlags);
|
||||
if (rv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
loadFlags |= callerLoadFlags & nsIRequest::INHIBIT_PERSISTENT_CACHING;
|
||||
|
||||
rv = channel->SetLoadFlags(loadFlags);
|
||||
if (rv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If the user has allowed mixed content on the rootDoc, then we should propogate it
|
||||
// down to the new document channel.
|
||||
bool rootHasSecureConnection = false;
|
||||
bool allowMixedContent = false;
|
||||
bool isDocShellRoot = false;
|
||||
nsresult rvalue = shell->GetAllowMixedContentAndConnectionData(&rootHasSecureConnection, &allowMixedContent, &isDocShellRoot);
|
||||
if (NS_SUCCEEDED(rvalue) && allowMixedContent && isDocShellRoot) {
|
||||
shell->SetMixedContentChannel(channel);
|
||||
// Step 8 - Clear all event listeners out of our DOM tree
|
||||
for (nsINode* node : SimpleTreeIterator(*this)) {
|
||||
if (EventListenerManager* elm = node->GetExistingListenerManager()) {
|
||||
elm->RemoveAllListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Before we reset the doc notify the globalwindow of the change,
|
||||
// but only if we still have a window (i.e. our window object the
|
||||
// current inner window in our outer window).
|
||||
|
||||
// Hold onto ourselves on the offchance that we're down to one ref
|
||||
nsCOMPtr<nsIDocument> kungFuDeathGrip = this;
|
||||
|
||||
if (nsPIDOMWindowInner *window = GetInnerWindow()) {
|
||||
// Remember the old scope in case the call to SetNewDocument changes it.
|
||||
nsCOMPtr<nsIScriptGlobalObject> oldScope(do_QueryReferent(mScopeObject));
|
||||
|
||||
#ifdef DEBUG
|
||||
bool willReparent = mWillReparent;
|
||||
mWillReparent = true;
|
||||
|
||||
nsDocument* templateContentsOwner =
|
||||
static_cast<nsDocument*>(mTemplateContentsOwner.get());
|
||||
|
||||
if (templateContentsOwner) {
|
||||
templateContentsOwner->mWillReparent = true;
|
||||
// Step 9 - Clear event listeners from our window, if we have one.
|
||||
//
|
||||
// Note that we explicitly want the inner window, and only if we're its
|
||||
// document. We want to do this (per spec) even when we're not the "active
|
||||
// document", so we can't go through GetWindow(), because it might forward to
|
||||
// the wrong inner.
|
||||
if (nsPIDOMWindowInner* win = GetInnerWindow()) {
|
||||
if (win->GetExtantDoc() == this) {
|
||||
if (EventListenerManager* elm =
|
||||
nsGlobalWindow::Cast(win)->GetExistingListenerManager()) {
|
||||
elm->RemoveAllListeners();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Per spec, we pass false here so that a new Window is created.
|
||||
rv = window->SetNewDocument(this, nullptr,
|
||||
/* aForceReuseInnerWindow */ false);
|
||||
if (rv.Failed()) {
|
||||
// Step 10 - Remove all of our DOM children without firing any mutation events.
|
||||
DisconnectNodeTree();
|
||||
|
||||
// --- At this point our tree is clean and we can switch to the new URI ---
|
||||
|
||||
// Step 11 - If we're the current document in our docshell, do the
|
||||
// equivalent of pushState() with the new URL we should have.
|
||||
if (shell && IsCurrentActiveDocument()) {
|
||||
nsCOMPtr<nsIURI> newURI = callerDoc->GetDocumentURI();
|
||||
|
||||
// UpdateURLAndHistory might do various member-setting, so make sure we're
|
||||
// holding strong refs to all the refcounted args on the stack. We can
|
||||
// assume that our caller is holding on to "this" already.
|
||||
nsCOMPtr<nsIURI> currentURI = nsIDocument::GetDocumentURI();
|
||||
bool equalURIs;
|
||||
nsresult rv = currentURI->Equals(newURI, &equalURIs);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aError.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
nsCOMPtr<nsIStructuredCloneContainer> stateContainer(mStateObjectContainer);
|
||||
rv = shell->UpdateURLAndHistory(this, newURI, stateContainer, EmptyString(),
|
||||
/* aReplace = */ true, currentURI,
|
||||
equalURIs);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aError.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (templateContentsOwner) {
|
||||
templateContentsOwner->mWillReparent = willReparent;
|
||||
}
|
||||
// And use the security info of the caller document as well, since
|
||||
// it's the thing providing our data.
|
||||
mSecurityInfo = callerDoc->GetSecurityInfo();
|
||||
|
||||
mWillReparent = willReparent;
|
||||
#endif
|
||||
|
||||
// Now make sure we're not flagged as the initial document anymore, now
|
||||
// that we've had stuff done to us. From now on, if anyone tries to
|
||||
// document.open() us, they get a new inner window.
|
||||
// This is not mentioned in the spec, but that's probably a spec bug.
|
||||
// See <https://github.com/whatwg/html/issues/4299>.
|
||||
// Since our URL may be changing away from about:blank here, we really want
|
||||
// to unset this flag on any document.open(), since only about:blank can be
|
||||
// an initial document.
|
||||
SetIsInitialDocument(false);
|
||||
|
||||
nsCOMPtr<nsIScriptGlobalObject> newScope(do_QueryReferent(mScopeObject));
|
||||
JS::Rooted<JSObject*> wrapper(cx, GetWrapper());
|
||||
if (oldScope && newScope != oldScope && wrapper) {
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
rv = mozilla::dom::ReparentWrapper(cx, wrapper);
|
||||
if (rv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
// And let our docloader know that it will need to track our load event.
|
||||
nsDocShell::Cast(shell)->SetDocumentOpenedButNotLoaded();
|
||||
}
|
||||
|
||||
// Also reparent the template contents owner document
|
||||
// because its global is set to the same as this document.
|
||||
if (mTemplateContentsOwner) {
|
||||
JS::Rooted<JSObject*> contentsOwnerWrapper(cx,
|
||||
mTemplateContentsOwner->GetWrapper());
|
||||
if (contentsOwnerWrapper) {
|
||||
rv = mozilla::dom::ReparentWrapper(cx, contentsOwnerWrapper);
|
||||
if (rv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Step 12
|
||||
mSkipLoadEventAfterClose = mLoadEventFiring;
|
||||
|
||||
mDidDocumentOpen = true;
|
||||
// Preliminary to steps 13-16. Set our ready state to uninitialized before
|
||||
// we do anything else, so we can then proceed to later ready state levels.
|
||||
SetReadyStateInternal(READYSTATE_UNINITIALIZED,
|
||||
/* updateTimingInformation = */ false);
|
||||
|
||||
// Call Reset(), this will now do the full reset
|
||||
Reset(channel, group);
|
||||
if (baseURI) {
|
||||
mDocumentBaseURI = baseURI;
|
||||
}
|
||||
|
||||
// Store the security info of the caller now that we're done
|
||||
// resetting the document.
|
||||
mSecurityInfo = securityInfo;
|
||||
// Step 13 - Set our compatibility mode to standards.
|
||||
SetCompatibilityMode(eCompatibility_FullStandards);
|
||||
|
||||
// Step 14 - Create a new parser associated with document.
|
||||
// This also does step 16 implicitly.
|
||||
mParserAborted = false;
|
||||
mParser = nsHtml5Module::NewHtml5Parser();
|
||||
nsHtml5Module::Initialize(mParser, this, uri, shell, channel);
|
||||
nsHtml5Module::Initialize(mParser, this, nsIDocument::GetDocumentURI(), shell, nullptr);
|
||||
if (mReferrerPolicySet) {
|
||||
// CSP may have set the referrer policy, so a speculative parser should
|
||||
// start with the new referrer policy.
|
||||
nsHtml5TreeOpExecutor* executor = nullptr;
|
||||
executor = static_cast<nsHtml5TreeOpExecutor*> (mParser->GetContentSink());
|
||||
executor = static_cast<nsHtml5TreeOpExecutor*>(mParser->GetContentSink());
|
||||
if (executor && mReferrerPolicySet) {
|
||||
executor->SetSpeculationReferrerPolicy(static_cast<ReferrerPolicy>(mReferrerPolicy));
|
||||
executor->SetSpeculationReferrerPolicy(
|
||||
static_cast<ReferrerPolicy>(mReferrerPolicy));
|
||||
}
|
||||
}
|
||||
|
||||
// This will be propagated to the parser when someone actually calls write()
|
||||
SetContentTypeInternal(contentType);
|
||||
if (shell) {
|
||||
// Prepare the docshell and the document viewer for the impending
|
||||
// out-of-band document.write()
|
||||
shell->PrepareForNewContentModel();
|
||||
|
||||
// Prepare the docshell and the document viewer for the impending
|
||||
// out of band document.write()
|
||||
shell->PrepareForNewContentModel();
|
||||
|
||||
// Now check whether we were opened with a "replace" argument. If
|
||||
// so, we need to tell the docshell to not create a new history
|
||||
// entry for this load. Otherwise, make sure that we're doing a normal load,
|
||||
// not whatever type of load was previously done on this docshell.
|
||||
shell->SetLoadType(aReplace.LowerCaseEqualsLiteral("replace") ?
|
||||
LOAD_NORMAL_REPLACE : LOAD_NORMAL);
|
||||
|
||||
nsCOMPtr<nsIContentViewer> cv;
|
||||
shell->GetContentViewer(getter_AddRefs(cv));
|
||||
if (cv) {
|
||||
cv->LoadStart(this);
|
||||
nsCOMPtr<nsIContentViewer> cv;
|
||||
shell->GetContentViewer(getter_AddRefs(cv));
|
||||
if (cv) {
|
||||
cv->LoadStart(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a wyciwyg channel request into the document load group
|
||||
NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Open(): wyciwyg "
|
||||
"channel already exists!");
|
||||
// Step 15.
|
||||
SetReadyStateInternal(nsIDocument::READYSTATE_LOADING,
|
||||
/* updateTimingInformation = */ false);
|
||||
|
||||
// In case the editor is listening and will see the new channel
|
||||
// being added, make sure mWriteLevel is non-zero so that the editor
|
||||
// knows that document.open/write/close() is being called on this
|
||||
// document.
|
||||
++mWriteLevel;
|
||||
// Step 16 happened with step 14 above.
|
||||
|
||||
CreateAndAddWyciwygChannel();
|
||||
|
||||
--mWriteLevel;
|
||||
|
||||
SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
|
||||
|
||||
// After changing everything around, make sure that the principal on the
|
||||
// document's compartment exactly matches NodePrincipal().
|
||||
DebugOnly<JSObject*> wrapper = GetWrapperPreserveColor();
|
||||
MOZ_ASSERT_IF(wrapper,
|
||||
JS_GetCompartmentPrincipals(js::GetObjectCompartment(wrapper)) ==
|
||||
nsJSPrincipals::get(NodePrincipal()));
|
||||
|
||||
return kungFuDeathGrip.forget();
|
||||
}
|
||||
// Step 17.
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLDocument::Clear()
|
||||
|
|
@ -1806,15 +1690,6 @@ nsHTMLDocument::Close(ErrorResult& rv)
|
|||
if (GetShell()) {
|
||||
FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
// Removing the wyciwygChannel here is wrong when document.close() is
|
||||
// called from within the document itself. However, legacy requires the
|
||||
// channel to be removed here. Otherwise, the load event never fires.
|
||||
NS_ASSERTION(mWyciwygChannel, "nsHTMLDocument::Close(): Trying to remove "
|
||||
"nonexistent wyciwyg channel!");
|
||||
RemoveWyciwygChannel();
|
||||
NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Close(): "
|
||||
"nsIWyciwygChannel could not be removed!");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -529,7 +529,6 @@ skip-if = toolkit == 'android' # plugins not supported
|
|||
[test_bug196523.html]
|
||||
[test_bug199692.html]
|
||||
skip-if = toolkit == 'android' #bug 811644
|
||||
[test_bug172261.html]
|
||||
[test_bug255820.html]
|
||||
[test_bug259332.html]
|
||||
[test_bug311681.html]
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=172261
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 172261</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=172261">Mozilla Bug 172261</a>
|
||||
<p id="display">
|
||||
<iframe id="test"></iframe>
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
/** Test for Bug 172261 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
|
||||
var callable = false;
|
||||
function toggleCallable() { callable = true; }
|
||||
|
||||
var doTestInIframe = false;
|
||||
|
||||
// Shouldn't do history stuff from inside onload
|
||||
addLoadEvent(function() { setTimeout(startTest, 10) });
|
||||
|
||||
function startTest() {
|
||||
// First, create a dummy document. Use onunload handlers to make sure
|
||||
// bfcache doesn't screw us up.
|
||||
var doc = $("test").contentDocument;
|
||||
|
||||
doc.write("<html><body onunload=''>First</body></html>");
|
||||
doc.close();
|
||||
|
||||
// Now write our test document
|
||||
doc.write("<html><script>window.onerror = parent.onerror; if (parent.doTestInIframe) { parent.is(document.domain, parent.document.domain, 'Domains should match'); parent.toggleCallable(); } <" + "/script><body>Second</body></html>");
|
||||
doc.close();
|
||||
|
||||
$("test").onload = goForward;
|
||||
history.back();
|
||||
}
|
||||
|
||||
function goForward() {
|
||||
$("test").onload = doTest;
|
||||
doTestInIframe = true;
|
||||
history.forward();
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
is($("test").contentDocument.domain, document.domain,
|
||||
"Domains should match 2");
|
||||
is($("test").contentDocument.location.href, location.href,
|
||||
"Locations should match");
|
||||
is(callable, true, "Subframe should be able to call us");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ SimpleTest.waitForExplicitFinish();
|
|||
is(document.characterSet, "UTF-8",
|
||||
"Unexpected character set for our document");
|
||||
|
||||
var testsLeft = 4;
|
||||
var testsLeft = 3;
|
||||
|
||||
function testFinished() {
|
||||
--testsLeft;
|
||||
|
|
@ -42,29 +42,11 @@ function charsetTestFinished(id, doc, charsetTarget) {
|
|||
testFinished();
|
||||
}
|
||||
|
||||
function f2Continue() {
|
||||
// Commented out pending discussion at the WHATWG
|
||||
// $("f2").
|
||||
// setAttribute("onload",
|
||||
// "charsetTestFinished('f2 reloaded', this.contentDocument, 'us-ascii');");
|
||||
$("f2").
|
||||
setAttribute("onload",
|
||||
"testFinished();");
|
||||
$("f2").contentWindow.location.reload();
|
||||
}
|
||||
|
||||
function f3Continue() {
|
||||
var doc = $("f3").contentDocument;
|
||||
is(doc.defaultView.getComputedStyle(doc.body, "").color, "rgb(0, 180, 0)",
|
||||
"Wrong color before reload");
|
||||
$("f3").
|
||||
setAttribute("onload",
|
||||
'var doc = this.contentDocument; ' +
|
||||
'is(doc.defaultView.getComputedStyle(doc.body, "").color, ' +
|
||||
' "rgb(0, 180, 0)",' +
|
||||
' "Wrong color after reload");' +
|
||||
"charsetTestFinished('f1', this.contentDocument, 'UTF-8')");
|
||||
$("f3").contentWindow.location.reload();
|
||||
"Wrong color");
|
||||
charsetTestFinished('f3', doc, "UTF-8");
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
|
@ -74,12 +56,7 @@ function runTest() {
|
|||
doc.open();
|
||||
doc.write('<html></html>');
|
||||
doc.close();
|
||||
is(doc.characterSet, "UTF-8",
|
||||
"Unexpected character set for first frame after write");
|
||||
$("f1").
|
||||
setAttribute("onload",
|
||||
"charsetTestFinished('f1', this.contentDocument, 'UTF-8')");
|
||||
$("f1").contentWindow.location.reload();
|
||||
charsetTestFinished("f1", doc, "UTF-8");
|
||||
|
||||
doc = $("f2").contentDocument;
|
||||
is(doc.characterSet, "UTF-8",
|
||||
|
|
@ -96,12 +73,11 @@ function runTest() {
|
|||
"Unexpected character set for second frame after write");
|
||||
$("f2").
|
||||
setAttribute("onload",
|
||||
"charsetTestFinished('f2', this.contentDocument, 'UTF-8');" +
|
||||
"f2Continue()");
|
||||
"charsetTestFinished('f2', this.contentDocument, 'UTF-8');");
|
||||
|
||||
doc = $("f3").contentDocument;
|
||||
is(doc.characterSet, "UTF-8",
|
||||
"Unexpected initial character set for first frame");
|
||||
"Unexpected initial character set for third frame");
|
||||
doc.open();
|
||||
var str = '<html><head>';
|
||||
str += '<style>body { color: rgb(255, 0, 0) }</style>';
|
||||
|
|
@ -111,7 +87,7 @@ function runTest() {
|
|||
doc.write(str);
|
||||
doc.close();
|
||||
is(doc.characterSet, "UTF-8",
|
||||
"Unexpected character set for first frame after write");
|
||||
"Unexpected character set for third frame after write");
|
||||
$("f3").setAttribute("onload", "f3Continue()");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ function messageReceiver(evt) {
|
|||
is(testResult, "undefined", "Props on new window's child should go away when loading");
|
||||
break;
|
||||
case 6:
|
||||
is(testResult, "undefined", "Props on new window's child should go away when writing");
|
||||
is(testResult, "6", "Props on new window's child should go away when writing");
|
||||
break;
|
||||
case 7:
|
||||
is(testResult, "7", "Props on different-domain window opened from different-domain new window can stay");
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ interface HTMLMediaElement : HTMLElement {
|
|||
[SetterThrows]
|
||||
attribute boolean loop;
|
||||
[Throws]
|
||||
void play();
|
||||
Promise<void> play();
|
||||
[Throws]
|
||||
void pause();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue