2019-03-09 04:00:23 +03:00
|
|
|
/* -*- 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 https://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "mozilla/dom/PrototypeDocumentContentSink.h"
|
|
|
|
#include "nsIParser.h"
|
|
|
|
#include "mozilla/dom/Document.h"
|
|
|
|
#include "nsIContent.h"
|
|
|
|
#include "nsIURI.h"
|
|
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "nsHTMLParts.h"
|
|
|
|
#include "nsCRT.h"
|
|
|
|
#include "mozilla/StyleSheetInlines.h"
|
|
|
|
#include "mozilla/css/Loader.h"
|
|
|
|
#include "nsGkAtoms.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
#include "nsDocElementCreatedNotificationRunner.h"
|
|
|
|
#include "nsIScriptContext.h"
|
|
|
|
#include "nsNameSpaceManager.h"
|
|
|
|
#include "nsIScriptError.h"
|
|
|
|
#include "prtime.h"
|
|
|
|
#include "mozilla/Logging.h"
|
|
|
|
#include "nsRect.h"
|
|
|
|
#include "nsIScriptElement.h"
|
|
|
|
#include "nsReadableUtils.h"
|
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
#include "nsIChannel.h"
|
|
|
|
#include "nsNodeInfoManager.h"
|
|
|
|
#include "nsContentCreatorFunctions.h"
|
|
|
|
#include "nsIContentPolicy.h"
|
|
|
|
#include "nsContentPolicyUtils.h"
|
|
|
|
#include "nsError.h"
|
|
|
|
#include "nsIScriptGlobalObject.h"
|
|
|
|
#include "mozAutoDocUpdate.h"
|
|
|
|
#include "nsMimeTypes.h"
|
|
|
|
#include "nsHtml5SVGLoadDispatcher.h"
|
|
|
|
#include "nsTextNode.h"
|
2021-03-05 18:29:49 +03:00
|
|
|
#include "mozilla/dom/AutoEntryScript.h"
|
2019-03-09 04:00:23 +03:00
|
|
|
#include "mozilla/dom/CDATASection.h"
|
|
|
|
#include "mozilla/dom/Comment.h"
|
|
|
|
#include "mozilla/dom/DocumentType.h"
|
|
|
|
#include "mozilla/dom/Element.h"
|
|
|
|
#include "mozilla/dom/HTMLTemplateElement.h"
|
|
|
|
#include "mozilla/dom/ProcessingInstruction.h"
|
2020-05-21 03:40:31 +03:00
|
|
|
#include "mozilla/dom/XMLStylesheetProcessingInstruction.h"
|
2019-03-09 04:00:23 +03:00
|
|
|
#include "mozilla/dom/ScriptLoader.h"
|
|
|
|
#include "mozilla/LoadInfo.h"
|
2019-03-29 18:12:47 +03:00
|
|
|
#include "mozilla/PresShell.h"
|
Bug 1691589 - Reduce reliance on GeckoProfiler.h when only labels (and maybe markers) are needed - r=necko-reviewers,geckoview-reviewers,sg,agi,florian
There are no code changes, only #include changes.
It was a fairly mechanical process: Search for all "AUTO_PROFILER_LABEL", and in each file, if only labels are used, convert "GeckoProfiler.h" into "ProfilerLabels.h" (or just add that last one where needed).
In some files, there were also some marker calls but no other profiler-related calls, in these cases "GeckoProfiler.h" was replaced with both "ProfilerLabels.h" and "ProfilerMarkers.h", which still helps in reducing the use of the all-encompassing "GeckoProfiler.h".
Differential Revision: https://phabricator.services.mozilla.com/D104588
2021-02-16 07:44:19 +03:00
|
|
|
#include "mozilla/ProfilerLabels.h"
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
#include "nsXULPrototypeCache.h"
|
|
|
|
#include "nsXULElement.h"
|
|
|
|
#include "mozilla/CycleCollectedJSContext.h"
|
|
|
|
#include "js/CompilationAndEvaluation.h"
|
|
|
|
|
|
|
|
using namespace mozilla;
|
|
|
|
using namespace mozilla::dom;
|
|
|
|
|
|
|
|
LazyLogModule PrototypeDocumentContentSink::gLog("PrototypeDocument");
|
|
|
|
|
|
|
|
nsresult NS_NewPrototypeDocumentContentSink(nsIContentSink** aResult,
|
|
|
|
Document* aDoc, nsIURI* aURI,
|
|
|
|
nsISupports* aContainer,
|
|
|
|
nsIChannel* aChannel) {
|
|
|
|
MOZ_ASSERT(nullptr != aResult, "null ptr");
|
|
|
|
if (nullptr == aResult) {
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
RefPtr<PrototypeDocumentContentSink> it = new PrototypeDocumentContentSink();
|
|
|
|
|
|
|
|
nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
it.forget(aResult);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-01-18 16:48:34 +03:00
|
|
|
namespace mozilla::dom {
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
PrototypeDocumentContentSink::PrototypeDocumentContentSink()
|
|
|
|
: mNextSrcLoadWaiter(nullptr),
|
|
|
|
mCurrentScriptProto(nullptr),
|
|
|
|
mOffThreadCompiling(false),
|
|
|
|
mOffThreadCompileStringBuf(nullptr),
|
|
|
|
mOffThreadCompileStringLength(0),
|
|
|
|
mStillWalking(false),
|
|
|
|
mPendingSheets(0) {}
|
|
|
|
|
|
|
|
PrototypeDocumentContentSink::~PrototypeDocumentContentSink() {
|
|
|
|
NS_ASSERTION(
|
|
|
|
mNextSrcLoadWaiter == nullptr,
|
|
|
|
"unreferenced document still waiting for script source to load?");
|
|
|
|
|
|
|
|
if (mOffThreadCompileStringBuf) {
|
|
|
|
js_free(mOffThreadCompileStringBuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::Init(Document* aDoc, nsIURI* aURI,
|
|
|
|
nsISupports* aContainer,
|
|
|
|
nsIChannel* aChannel) {
|
|
|
|
MOZ_ASSERT(aDoc, "null ptr");
|
|
|
|
MOZ_ASSERT(aURI, "null ptr");
|
|
|
|
|
|
|
|
mDocument = aDoc;
|
|
|
|
|
|
|
|
mDocument->SetDelayFrameLoaderInitialization(true);
|
|
|
|
mDocument->SetMayStartLayout(false);
|
|
|
|
|
2020-04-08 14:48:56 +03:00
|
|
|
// Get the URI. this should match the uri used for the OnNewURI call in
|
|
|
|
// nsDocShell::CreateContentViewer.
|
2019-03-09 04:00:23 +03:00
|
|
|
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
mScriptLoader = mDocument->ScriptLoader();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION(PrototypeDocumentContentSink, mParser, mDocumentURI,
|
2020-04-05 21:36:00 +03:00
|
|
|
mDocument, mScriptLoader, mCurrentPrototype)
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PrototypeDocumentContentSink)
|
|
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentSink)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIContentSink)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIOffThreadScriptReceiver)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(PrototypeDocumentContentSink)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(PrototypeDocumentContentSink)
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// nsIContentSink interface
|
|
|
|
//
|
|
|
|
|
|
|
|
void PrototypeDocumentContentSink::SetDocumentCharset(
|
|
|
|
NotNull<const Encoding*> aEncoding) {
|
|
|
|
if (mDocument) {
|
|
|
|
mDocument->SetDocumentCharacterSet(aEncoding);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsISupports* PrototypeDocumentContentSink::GetTarget() {
|
|
|
|
return ToSupports(mDocument);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PrototypeDocumentContentSink::IsScriptExecuting() {
|
|
|
|
return !!mScriptLoader->GetCurrentScript();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PrototypeDocumentContentSink::SetParser(nsParserBase* aParser) {
|
|
|
|
MOZ_ASSERT(aParser, "Should have a parser here!");
|
|
|
|
mParser = aParser;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIParser* PrototypeDocumentContentSink::GetParser() {
|
|
|
|
return static_cast<nsIParser*>(mParser.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled() {
|
|
|
|
if (mParser && mParser->IsParserEnabled()) {
|
|
|
|
GetParser()->ContinueInterruptedParsing();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrototypeDocumentContentSink::ContinueInterruptedParsingAsync() {
|
|
|
|
nsCOMPtr<nsIRunnable> ev = NewRunnableMethod(
|
|
|
|
"PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled", this,
|
|
|
|
&PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled);
|
|
|
|
|
|
|
|
mDocument->Dispatch(mozilla::TaskCategory::Other, ev.forget());
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PrototypeDocumentContentSink::ContextStack
|
|
|
|
//
|
|
|
|
|
|
|
|
PrototypeDocumentContentSink::ContextStack::ContextStack()
|
|
|
|
: mTop(nullptr), mDepth(0) {}
|
|
|
|
|
|
|
|
PrototypeDocumentContentSink::ContextStack::~ContextStack() {
|
|
|
|
while (mTop) {
|
|
|
|
Entry* doomed = mTop;
|
|
|
|
mTop = mTop->mNext;
|
|
|
|
NS_IF_RELEASE(doomed->mElement);
|
|
|
|
delete doomed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::ContextStack::Push(
|
|
|
|
nsXULPrototypeElement* aPrototype, nsIContent* aElement) {
|
|
|
|
Entry* entry = new Entry;
|
|
|
|
entry->mPrototype = aPrototype;
|
|
|
|
entry->mElement = aElement;
|
|
|
|
NS_IF_ADDREF(entry->mElement);
|
|
|
|
entry->mIndex = 0;
|
|
|
|
|
|
|
|
entry->mNext = mTop;
|
|
|
|
mTop = entry;
|
|
|
|
|
|
|
|
++mDepth;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::ContextStack::Pop() {
|
|
|
|
if (mDepth == 0) return NS_ERROR_UNEXPECTED;
|
|
|
|
|
|
|
|
Entry* doomed = mTop;
|
|
|
|
mTop = mTop->mNext;
|
|
|
|
--mDepth;
|
|
|
|
|
|
|
|
NS_IF_RELEASE(doomed->mElement);
|
|
|
|
delete doomed;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::ContextStack::Peek(
|
|
|
|
nsXULPrototypeElement** aPrototype, nsIContent** aElement,
|
|
|
|
int32_t* aIndex) {
|
|
|
|
if (mDepth == 0) return NS_ERROR_UNEXPECTED;
|
|
|
|
|
|
|
|
*aPrototype = mTop->mPrototype;
|
|
|
|
*aElement = mTop->mElement;
|
|
|
|
NS_IF_ADDREF(*aElement);
|
|
|
|
*aIndex = mTop->mIndex;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::ContextStack::SetTopIndex(
|
|
|
|
int32_t aIndex) {
|
|
|
|
if (mDepth == 0) return NS_ERROR_UNEXPECTED;
|
|
|
|
|
|
|
|
mTop->mIndex = aIndex;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Content model walking routines
|
|
|
|
//
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::OnPrototypeLoadDone(
|
|
|
|
nsXULPrototypeDocument* aPrototype) {
|
|
|
|
mCurrentPrototype = aPrototype;
|
|
|
|
mDocument->SetPrototypeDocument(aPrototype);
|
|
|
|
|
|
|
|
nsresult rv = PrepareToWalk();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
rv = ResumeWalk();
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::PrepareToWalk() {
|
|
|
|
MOZ_ASSERT(mCurrentPrototype);
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
mStillWalking = true;
|
|
|
|
|
|
|
|
// Notify document that the load is beginning
|
|
|
|
mDocument->BeginLoad();
|
|
|
|
|
|
|
|
// Get the prototype's root element and initialize the context
|
|
|
|
// stack for the prototype walk.
|
|
|
|
nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement();
|
|
|
|
|
|
|
|
if (!proto) {
|
|
|
|
if (MOZ_LOG_TEST(gLog, LogLevel::Error)) {
|
|
|
|
nsCOMPtr<nsIURI> url = mCurrentPrototype->GetURI();
|
|
|
|
|
|
|
|
nsAutoCString urlspec;
|
|
|
|
rv = url->GetSpec(urlspec);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
MOZ_LOG(gLog, LogLevel::Error,
|
|
|
|
("prototype: error parsing '%s'", urlspec.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsINode* nodeToInsertBefore = mDocument->GetFirstChild();
|
|
|
|
|
|
|
|
const nsTArray<RefPtr<nsXULPrototypePI> >& processingInstructions =
|
|
|
|
mCurrentPrototype->GetProcessingInstructions();
|
|
|
|
|
|
|
|
uint32_t total = processingInstructions.Length();
|
|
|
|
for (uint32_t i = 0; i < total; ++i) {
|
|
|
|
rv = CreateAndInsertPI(processingInstructions[i], mDocument,
|
|
|
|
nodeToInsertBefore);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do one-time initialization.
|
|
|
|
RefPtr<Element> root;
|
|
|
|
|
|
|
|
// Add the root element
|
2020-04-05 21:36:00 +03:00
|
|
|
rv = CreateElementFromPrototype(proto, getter_AddRefs(root), nullptr);
|
2019-03-09 04:00:23 +03:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
rv = mDocument->AppendChildTo(root, false);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2019-04-17 22:25:50 +03:00
|
|
|
// TODO(emilio): Should this really notify? We don't notify of appends anyhow,
|
|
|
|
// and we just appended the root so no styles can possibly depend on it.
|
|
|
|
mDocument->UpdateDocumentStates(NS_DOCUMENT_STATE_RTL_LOCALE, true);
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
nsContentUtils::AddScriptRunner(
|
|
|
|
new nsDocElementCreatedNotificationRunner(mDocument));
|
|
|
|
|
|
|
|
// There'd better not be anything on the context stack at this
|
|
|
|
// point! This is the basis case for our "induction" in
|
|
|
|
// ResumeWalk(), below, which'll assume that there's always a
|
|
|
|
// content element on the context stack if we're in the document.
|
|
|
|
NS_ASSERTION(mContextStack.Depth() == 0,
|
|
|
|
"something's on the context stack already");
|
|
|
|
if (mContextStack.Depth() != 0) return NS_ERROR_UNEXPECTED;
|
|
|
|
|
|
|
|
rv = mContextStack.Push(proto, root);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::CreateAndInsertPI(
|
|
|
|
const nsXULPrototypePI* aProtoPI, nsINode* aParent, nsINode* aBeforeThis) {
|
|
|
|
MOZ_ASSERT(aProtoPI, "null ptr");
|
|
|
|
MOZ_ASSERT(aParent, "null ptr");
|
|
|
|
|
2020-04-05 21:36:00 +03:00
|
|
|
RefPtr<ProcessingInstruction> node =
|
|
|
|
NS_NewXMLProcessingInstruction(aParent->OwnerDoc()->NodeInfoManager(),
|
|
|
|
aProtoPI->mTarget, aProtoPI->mData);
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) {
|
2020-05-21 06:07:16 +03:00
|
|
|
MOZ_ASSERT(LinkStyle::FromNode(*node),
|
|
|
|
"XML Stylesheet node does not implement LinkStyle!");
|
2020-05-21 03:40:31 +03:00
|
|
|
auto* pi = static_cast<XMLStylesheetProcessingInstruction*>(node.get());
|
|
|
|
rv = InsertXMLStylesheetPI(aProtoPI, aParent, aBeforeThis, pi);
|
2019-03-09 04:00:23 +03:00
|
|
|
} else {
|
|
|
|
// No special processing, just add the PI to the document.
|
|
|
|
rv = aParent->InsertChildBefore(
|
|
|
|
node->AsContent(), aBeforeThis ? aBeforeThis->AsContent() : nullptr,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::InsertXMLStylesheetPI(
|
|
|
|
const nsXULPrototypePI* aProtoPI, nsINode* aParent, nsINode* aBeforeThis,
|
2020-05-21 03:40:31 +03:00
|
|
|
XMLStylesheetProcessingInstruction* aPINode) {
|
2019-03-09 04:00:23 +03:00
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
// We want to be notified when the style sheet finishes loading, so
|
|
|
|
// disable style sheet loading for now.
|
2020-05-21 03:40:31 +03:00
|
|
|
aPINode->SetEnableUpdates(false);
|
|
|
|
aPINode->OverrideBaseURI(mCurrentPrototype->GetURI());
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
rv = aParent->InsertChildBefore(
|
2020-05-21 06:07:16 +03:00
|
|
|
aPINode, aBeforeThis ? aBeforeThis->AsContent() : nullptr, false);
|
2019-03-09 04:00:23 +03:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2020-05-21 03:40:31 +03:00
|
|
|
aPINode->SetEnableUpdates(true);
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
// load the stylesheet if necessary, passing ourselves as
|
|
|
|
// nsICSSObserver
|
2020-05-21 03:40:31 +03:00
|
|
|
auto result = aPINode->UpdateStyleSheet(this);
|
2019-03-09 04:00:23 +03:00
|
|
|
if (result.isErr()) {
|
|
|
|
// Ignore errors from UpdateStyleSheet; we don't want failure to
|
|
|
|
// do that to break the XUL document load. But do propagate out
|
|
|
|
// NS_ERROR_OUT_OF_MEMORY.
|
|
|
|
if (result.unwrapErr() == NS_ERROR_OUT_OF_MEMORY) {
|
|
|
|
return result.unwrapErr();
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto update = result.unwrap();
|
|
|
|
if (update.ShouldBlock()) {
|
|
|
|
++mPendingSheets;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2021-01-18 20:38:56 +03:00
|
|
|
void PrototypeDocumentContentSink::CloseElement(Element* aElement,
|
|
|
|
bool aHadChildren) {
|
2019-09-06 23:27:09 +03:00
|
|
|
if (nsIContent::RequiresDoneAddingChildren(
|
|
|
|
aElement->NodeInfo()->NamespaceID(),
|
|
|
|
aElement->NodeInfo()->NameAtom())) {
|
2019-03-09 04:00:23 +03:00
|
|
|
aElement->DoneAddingChildren(false);
|
|
|
|
}
|
2021-01-18 20:38:56 +03:00
|
|
|
|
|
|
|
if (!aHadChildren) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See bug 370111 and bug 1495946. We don't cache inline styles nor module
|
|
|
|
// scripts in the prototype cache, and we don't notify on node insertion, so
|
|
|
|
// we need to do this for the stylesheet / script to be properly processed.
|
|
|
|
// This kinda sucks, but notifying was a pretty sizeable perf regression so...
|
|
|
|
if (aElement->IsHTMLElement(nsGkAtoms::script) ||
|
|
|
|
aElement->IsSVGElement(nsGkAtoms::script)) {
|
|
|
|
nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aElement);
|
|
|
|
MOZ_ASSERT(sele, "Node didn't QI to script.");
|
|
|
|
if (sele->GetScriptIsModule()) {
|
|
|
|
DebugOnly<bool> block = sele->AttemptToExecute();
|
|
|
|
MOZ_ASSERT(!block, "<script type=module> shouldn't block the parser");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aElement->IsHTMLElement(nsGkAtoms::style) ||
|
|
|
|
aElement->IsSVGElement(nsGkAtoms::style)) {
|
|
|
|
auto* linkStyle = LinkStyle::FromNode(*aElement);
|
|
|
|
NS_ASSERTION(linkStyle,
|
|
|
|
"<html:style> doesn't implement "
|
|
|
|
"nsIStyleSheetLinkingElement?");
|
|
|
|
Unused << linkStyle->UpdateStyleSheet(nullptr);
|
|
|
|
}
|
2019-03-09 04:00:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::ResumeWalk() {
|
2019-04-15 13:56:03 +03:00
|
|
|
nsresult rv = ResumeWalkInternal();
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
nsContentUtils::ReportToConsoleNonLocalized(
|
|
|
|
u"Failed to load document from prototype document."_ns,
|
|
|
|
nsIScriptError::errorFlag, "Prototype Document"_ns, mDocument,
|
|
|
|
mDocumentURI);
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::ResumeWalkInternal() {
|
2019-03-09 04:00:23 +03:00
|
|
|
MOZ_ASSERT(mStillWalking);
|
|
|
|
// Walk the prototype and build the delegate content model. The
|
|
|
|
// walk is performed in a top-down, left-to-right fashion. That
|
|
|
|
// is, a parent is built before any of its children; a node is
|
|
|
|
// only built after all of its siblings to the left are fully
|
|
|
|
// constructed.
|
|
|
|
//
|
|
|
|
// It is interruptable so that transcluded documents (e.g.,
|
|
|
|
// <html:script src="..." />) can be properly re-loaded if the
|
|
|
|
// cached copy of the document becomes stale.
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIURI> docURI =
|
|
|
|
mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
// Begin (or resume) walking the current prototype.
|
|
|
|
|
|
|
|
while (mContextStack.Depth() > 0) {
|
|
|
|
// Look at the top of the stack to determine what we're
|
|
|
|
// currently working on.
|
|
|
|
// This will always be a node already constructed and
|
|
|
|
// inserted to the actual document.
|
|
|
|
nsXULPrototypeElement* proto;
|
|
|
|
nsCOMPtr<nsIContent> element;
|
2019-04-09 22:50:00 +03:00
|
|
|
nsCOMPtr<nsIContent> nodeToPushTo;
|
2019-03-09 04:00:23 +03:00
|
|
|
int32_t indx; // all children of proto before indx (not
|
|
|
|
// inclusive) have already been constructed
|
|
|
|
rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
if (indx >= (int32_t)proto->mChildren.Length()) {
|
|
|
|
if (element) {
|
|
|
|
// We've processed all of the prototype's children.
|
2021-01-18 20:38:56 +03:00
|
|
|
CloseElement(element->AsElement(), /* aHadChildren = */ true);
|
2019-03-09 04:00:23 +03:00
|
|
|
}
|
|
|
|
// Now pop the context stack back up to the parent
|
|
|
|
// element and continue the prototype walk.
|
|
|
|
mContextStack.Pop();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-04-09 22:50:00 +03:00
|
|
|
nodeToPushTo = element;
|
|
|
|
// For template elements append the content to the template's document
|
|
|
|
// fragment.
|
2020-04-05 21:36:00 +03:00
|
|
|
if (auto* templateElement = HTMLTemplateElement::FromNode(element)) {
|
2019-04-09 22:50:00 +03:00
|
|
|
nodeToPushTo = templateElement->Content();
|
|
|
|
}
|
|
|
|
|
2019-03-09 04:00:23 +03:00
|
|
|
// Grab the next child, and advance the current context stack
|
|
|
|
// to the next sibling to our right.
|
|
|
|
nsXULPrototypeNode* childproto = proto->mChildren[indx];
|
|
|
|
mContextStack.SetTopIndex(++indx);
|
|
|
|
|
|
|
|
switch (childproto->mType) {
|
|
|
|
case nsXULPrototypeNode::eType_Element: {
|
|
|
|
// An 'element', which may contain more content.
|
2020-04-05 21:36:00 +03:00
|
|
|
auto* protoele = static_cast<nsXULPrototypeElement*>(childproto);
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
RefPtr<Element> child;
|
|
|
|
|
|
|
|
rv = CreateElementFromPrototype(protoele, getter_AddRefs(child),
|
2020-04-05 21:36:00 +03:00
|
|
|
nodeToPushTo);
|
2019-03-09 04:00:23 +03:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
// ...and append it to the content model.
|
2019-04-09 22:50:00 +03:00
|
|
|
rv = nodeToPushTo->AppendChildTo(child, false);
|
2019-03-09 04:00:23 +03:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2019-09-06 23:27:09 +03:00
|
|
|
if (nsIContent::RequiresDoneCreatingElement(
|
|
|
|
protoele->mNodeInfo->NamespaceID(),
|
|
|
|
protoele->mNodeInfo->NameAtom())) {
|
|
|
|
child->DoneCreatingElement();
|
|
|
|
}
|
|
|
|
|
2019-03-09 04:00:23 +03:00
|
|
|
// If it has children, push the element onto the context
|
|
|
|
// stack and begin to process them.
|
|
|
|
if (protoele->mChildren.Length() > 0) {
|
|
|
|
rv = mContextStack.Push(protoele, child);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
} else {
|
|
|
|
// If there are no children, close the element immediately.
|
2021-01-18 20:38:56 +03:00
|
|
|
CloseElement(child, /* aHadChildren = */ false);
|
2019-03-09 04:00:23 +03:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case nsXULPrototypeNode::eType_Script: {
|
|
|
|
// A script reference. Execute the script immediately;
|
|
|
|
// this may have side effects in the content model.
|
2020-04-05 21:36:00 +03:00
|
|
|
auto* scriptproto = static_cast<nsXULPrototypeScript*>(childproto);
|
2019-03-09 04:00:23 +03:00
|
|
|
if (scriptproto->mSrcURI) {
|
|
|
|
// A transcluded script reference; this may
|
|
|
|
// "block" our prototype walk if the script isn't
|
|
|
|
// cached, or the cached copy of the script is
|
|
|
|
// stale and must be reloaded.
|
|
|
|
bool blocked;
|
|
|
|
rv = LoadScript(scriptproto, &blocked);
|
|
|
|
// If the script cannot be loaded, just keep going!
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(rv) && blocked) return NS_OK;
|
|
|
|
} else if (scriptproto->HasScriptObject()) {
|
|
|
|
// An inline script
|
|
|
|
rv = ExecuteScript(scriptproto);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case nsXULPrototypeNode::eType_Text: {
|
2020-04-05 21:36:00 +03:00
|
|
|
nsNodeInfoManager* nim = nodeToPushTo->NodeInfo()->NodeInfoManager();
|
2019-03-09 04:00:23 +03:00
|
|
|
// A simple text node.
|
2020-04-05 21:36:00 +03:00
|
|
|
RefPtr<nsTextNode> text = new (nim) nsTextNode(nim);
|
2019-03-09 04:00:23 +03:00
|
|
|
|
2020-04-05 21:36:00 +03:00
|
|
|
auto* textproto = static_cast<nsXULPrototypeText*>(childproto);
|
2019-03-09 04:00:23 +03:00
|
|
|
text->SetText(textproto->mValue, false);
|
|
|
|
|
2019-04-09 22:50:00 +03:00
|
|
|
rv = nodeToPushTo->AppendChildTo(text, false);
|
2019-03-09 04:00:23 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case nsXULPrototypeNode::eType_PI: {
|
2020-04-05 21:36:00 +03:00
|
|
|
auto* piProto = static_cast<nsXULPrototypePI*>(childproto);
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
// <?xml-stylesheet?> doesn't have an effect
|
|
|
|
// outside the prolog, like it used to. Issue a warning.
|
|
|
|
|
|
|
|
if (piProto->mTarget.EqualsLiteral("xml-stylesheet")) {
|
2019-06-09 00:26:12 +03:00
|
|
|
AutoTArray<nsString, 1> params = {piProto->mTarget};
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
|
|
|
"XUL Document"_ns, nullptr,
|
|
|
|
nsContentUtils::eXUL_PROPERTIES,
|
|
|
|
"PINotInProlog", params, docURI);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent* parent = element.get();
|
|
|
|
if (parent) {
|
|
|
|
// an inline script could have removed the root element
|
|
|
|
rv = CreateAndInsertPI(piProto, parent, nullptr);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
MOZ_ASSERT_UNREACHABLE("Unexpected nsXULPrototypeNode::Type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Once we get here, the context stack will have been
|
|
|
|
// depleted. That means that the entire prototype has been
|
|
|
|
// walked and content has been constructed.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mStillWalking = false;
|
|
|
|
return MaybeDoneWalking();
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:55:26 +03:00
|
|
|
void PrototypeDocumentContentSink::InitialTranslationCompleted() {
|
2019-03-09 04:00:23 +03:00
|
|
|
MaybeDoneWalking();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::MaybeDoneWalking() {
|
|
|
|
if (mPendingSheets > 0 || mStillWalking) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mDocument->HasPendingInitialTranslation()) {
|
2020-04-30 20:56:11 +03:00
|
|
|
mDocument->OnParsingCompleted();
|
2019-03-09 04:00:23 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return DoneWalking();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::DoneWalking() {
|
|
|
|
MOZ_ASSERT(mPendingSheets == 0, "there are sheets to be loaded");
|
|
|
|
MOZ_ASSERT(!mStillWalking, "walk not done");
|
|
|
|
MOZ_ASSERT(!mDocument->HasPendingInitialTranslation(), "translation pending");
|
|
|
|
|
|
|
|
if (mDocument) {
|
|
|
|
MOZ_ASSERT(mDocument->GetReadyStateEnum() == Document::READYSTATE_LOADING,
|
|
|
|
"Bad readyState");
|
|
|
|
mDocument->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
|
|
|
|
mDocument->NotifyPossibleTitleChange(false);
|
|
|
|
|
2020-06-01 20:11:48 +03:00
|
|
|
nsContentUtils::DispatchEventOnlyToChrome(mDocument, ToSupports(mDocument),
|
2019-03-09 04:00:23 +03:00
|
|
|
u"MozBeforeInitialXULLayout"_ns,
|
|
|
|
CanBubble::eYes, Cancelable::eNo);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mScriptLoader) {
|
|
|
|
mScriptLoader->ParsingComplete(false);
|
2020-03-20 16:33:11 +03:00
|
|
|
mScriptLoader->DeferCheckpointReached();
|
2019-03-09 04:00:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
StartLayout();
|
|
|
|
|
|
|
|
if (IsChromeURI(mDocumentURI) &&
|
|
|
|
nsXULPrototypeCache::GetInstance()->IsEnabled()) {
|
|
|
|
bool isCachedOnDisk;
|
|
|
|
nsXULPrototypeCache::GetInstance()->HasData(mDocumentURI, &isCachedOnDisk);
|
|
|
|
if (!isCachedOnDisk) {
|
|
|
|
nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mDocument->SetDelayFrameLoaderInitialization(false);
|
|
|
|
mDocument->MaybeInitializeFinalizeFrameLoaders();
|
|
|
|
|
|
|
|
// If the document we are loading has a reference or it is a
|
|
|
|
// frameset document, disable the scroll bars on the views.
|
|
|
|
|
|
|
|
mDocument->SetScrollToRef(mDocument->GetDocumentURI());
|
|
|
|
|
|
|
|
mDocument->EndLoad();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrototypeDocumentContentSink::StartLayout() {
|
|
|
|
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
|
|
|
|
"PrototypeDocumentContentSink::StartLayout", LAYOUT,
|
|
|
|
mDocumentURI->GetSpecOrDefault());
|
|
|
|
mDocument->SetMayStartLayout(true);
|
2019-03-29 18:12:47 +03:00
|
|
|
RefPtr<PresShell> presShell = mDocument->GetPresShell();
|
|
|
|
if (presShell && !presShell->DidInitialize()) {
|
|
|
|
nsresult rv = presShell->Initialize();
|
2019-03-09 04:00:23 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PrototypeDocumentContentSink::StyleSheetLoaded(StyleSheet* aSheet,
|
|
|
|
bool aWasDeferred,
|
|
|
|
nsresult aStatus) {
|
|
|
|
if (!aWasDeferred) {
|
|
|
|
// Don't care about when alternate sheets finish loading
|
|
|
|
MOZ_ASSERT(mPendingSheets > 0, "Unexpected StyleSheetLoaded notification");
|
|
|
|
|
|
|
|
--mPendingSheets;
|
|
|
|
|
|
|
|
return MaybeDoneWalking();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::LoadScript(
|
|
|
|
nsXULPrototypeScript* aScriptProto, bool* aBlock) {
|
|
|
|
// Load a transcluded script
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
bool isChromeDoc = IsChromeURI(mDocumentURI);
|
|
|
|
|
|
|
|
if (isChromeDoc && aScriptProto->HasScriptObject()) {
|
|
|
|
rv = ExecuteScript(aScriptProto);
|
|
|
|
|
|
|
|
// Ignore return value from execution, and don't block
|
|
|
|
*aBlock = false;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try the XUL script cache, in case two XUL documents source the same
|
|
|
|
// .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
|
|
|
|
// XXXbe the cache relies on aScriptProto's GC root!
|
|
|
|
bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
|
|
|
|
|
|
|
|
if (isChromeDoc && useXULCache) {
|
|
|
|
JSScript* newScriptObject =
|
|
|
|
nsXULPrototypeCache::GetInstance()->GetScript(aScriptProto->mSrcURI);
|
|
|
|
if (newScriptObject) {
|
|
|
|
// The script language for a proto must remain constant - we
|
|
|
|
// can't just change it for this unexpected language.
|
|
|
|
aScriptProto->Set(newScriptObject);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aScriptProto->HasScriptObject()) {
|
|
|
|
rv = ExecuteScript(aScriptProto);
|
|
|
|
|
|
|
|
// Ignore return value from execution, and don't block
|
|
|
|
*aBlock = false;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Release script objects from FastLoad since we decided against using them
|
|
|
|
aScriptProto->UnlinkJSObjects();
|
|
|
|
|
|
|
|
// Set the current script prototype so that OnStreamComplete can report
|
|
|
|
// the right file if there are errors in the script.
|
|
|
|
NS_ASSERTION(!mCurrentScriptProto,
|
|
|
|
"still loading a script when starting another load?");
|
|
|
|
mCurrentScriptProto = aScriptProto;
|
|
|
|
|
|
|
|
if (isChromeDoc && aScriptProto->mSrcLoading) {
|
2019-08-09 20:47:41 +03:00
|
|
|
// Another document load has started, which is still in progress.
|
2019-03-09 04:00:23 +03:00
|
|
|
// Remember to ResumeWalk this document when the load completes.
|
|
|
|
mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
|
|
|
|
aScriptProto->mSrcLoadWaiters = this;
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
} else {
|
|
|
|
nsCOMPtr<nsILoadGroup> group =
|
|
|
|
mDocument
|
|
|
|
->GetDocumentLoadGroup(); // found in
|
|
|
|
// mozilla::dom::Document::SetScriptGlobalObject
|
|
|
|
|
|
|
|
// Note: the loader will keep itself alive while it's loading.
|
|
|
|
nsCOMPtr<nsIStreamLoader> loader;
|
2020-07-15 14:20:45 +03:00
|
|
|
rv = NS_NewStreamLoader(
|
|
|
|
getter_AddRefs(loader), aScriptProto->mSrcURI,
|
|
|
|
this, // aObserver
|
|
|
|
mDocument, // aRequestingContext
|
|
|
|
nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
|
|
|
|
nsIContentPolicy::TYPE_INTERNAL_SCRIPT, group);
|
2019-03-09 04:00:23 +03:00
|
|
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
mCurrentScriptProto = nullptr;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
aScriptProto->mSrcLoading = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Block until OnStreamComplete resumes us.
|
|
|
|
*aBlock = true;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PrototypeDocumentContentSink::OnStreamComplete(nsIStreamLoader* aLoader,
|
|
|
|
nsISupports* context,
|
|
|
|
nsresult aStatus,
|
|
|
|
uint32_t stringLen,
|
|
|
|
const uint8_t* string) {
|
|
|
|
nsCOMPtr<nsIRequest> request;
|
|
|
|
aLoader->GetRequest(getter_AddRefs(request));
|
|
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// print a load error on bad status
|
|
|
|
if (NS_FAILED(aStatus)) {
|
|
|
|
if (channel) {
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
channel->GetURI(getter_AddRefs(uri));
|
|
|
|
if (uri) {
|
|
|
|
printf("Failed to load %s\n", uri->GetSpecOrDefault().get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// This is the completion routine that will be called when a
|
|
|
|
// transcluded script completes. Compile and execute the script
|
|
|
|
// if the load was successful, then continue building content
|
|
|
|
// from the prototype.
|
|
|
|
nsresult rv = aStatus;
|
|
|
|
|
|
|
|
NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
|
|
|
|
"script source not loading on unichar stream complete?");
|
|
|
|
if (!mCurrentScriptProto) {
|
|
|
|
// XXX Wallpaper for bug 270042
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(aStatus)) {
|
2019-08-09 20:47:41 +03:00
|
|
|
// If the including document is a FastLoad document, and we're
|
2019-03-09 04:00:23 +03:00
|
|
|
// compiling an out-of-line script (one with src=...), then we must
|
|
|
|
// be writing a new FastLoad file. If we were reading this script
|
|
|
|
// from the FastLoad file, XULContentSinkImpl::OpenScript (over in
|
|
|
|
// nsXULContentSink.cpp) would have already deserialized a non-null
|
|
|
|
// script->mScriptObject, causing control flow at the top of LoadScript
|
|
|
|
// not to reach here.
|
|
|
|
nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI;
|
|
|
|
|
|
|
|
// XXX should also check nsIHttpChannel::requestSucceeded
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mOffThreadCompiling && (mOffThreadCompileStringLength == 0 &&
|
|
|
|
!mOffThreadCompileStringBuf),
|
2019-08-09 20:47:41 +03:00
|
|
|
"PrototypeDocument can't load multiple scripts at once");
|
2019-03-09 04:00:23 +03:00
|
|
|
|
2020-09-23 18:17:15 +03:00
|
|
|
rv = ScriptLoader::ConvertToUTF16(channel, string, stringLen, u""_ns,
|
2019-03-09 04:00:23 +03:00
|
|
|
mDocument, mOffThreadCompileStringBuf,
|
|
|
|
mOffThreadCompileStringLength);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
// Pass ownership of the buffer, carefully emptying the existing
|
|
|
|
// fields in the process. Note that the |Compile| function called
|
|
|
|
// below always takes ownership of the buffer.
|
|
|
|
char16_t* units = nullptr;
|
|
|
|
size_t unitsLength = 0;
|
|
|
|
|
|
|
|
std::swap(units, mOffThreadCompileStringBuf);
|
|
|
|
std::swap(unitsLength, mOffThreadCompileStringLength);
|
|
|
|
|
|
|
|
rv = mCurrentScriptProto->Compile(units, unitsLength,
|
|
|
|
JS::SourceOwnership::TakeOwnership, uri,
|
|
|
|
1, mDocument, this);
|
|
|
|
if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasScriptObject()) {
|
|
|
|
mOffThreadCompiling = true;
|
|
|
|
mDocument->BlockOnload();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PrototypeDocumentContentSink::OnScriptCompileComplete(JSScript* aScript,
|
|
|
|
nsresult aStatus) {
|
|
|
|
// When compiling off thread the script will not have been attached to the
|
|
|
|
// script proto yet.
|
|
|
|
if (aScript && !mCurrentScriptProto->HasScriptObject())
|
|
|
|
mCurrentScriptProto->Set(aScript);
|
|
|
|
|
|
|
|
// Allow load events to be fired once off thread compilation finishes.
|
|
|
|
if (mOffThreadCompiling) {
|
|
|
|
mOffThreadCompiling = false;
|
|
|
|
mDocument->UnblockOnload(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// After compilation finishes the script's characters are no longer needed.
|
|
|
|
if (mOffThreadCompileStringBuf) {
|
|
|
|
js_free(mOffThreadCompileStringBuf);
|
|
|
|
mOffThreadCompileStringBuf = nullptr;
|
|
|
|
mOffThreadCompileStringLength = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear mCurrentScriptProto now, but save it first for use below in
|
|
|
|
// the execute code, and in the while loop that resumes walks of other
|
|
|
|
// documents that raced to load this script.
|
|
|
|
nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
|
|
|
|
mCurrentScriptProto = nullptr;
|
|
|
|
|
|
|
|
// Clear the prototype's loading flag before executing the script or
|
|
|
|
// resuming document walks, in case any of those control flows starts a
|
|
|
|
// new script load.
|
|
|
|
scriptProto->mSrcLoading = false;
|
|
|
|
|
|
|
|
nsresult rv = aStatus;
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
rv = ExecuteScript(scriptProto);
|
|
|
|
|
|
|
|
// If the XUL cache is enabled, save the script object there in
|
|
|
|
// case different XUL documents source the same script.
|
|
|
|
//
|
|
|
|
// But don't save the script in the cache unless the master XUL
|
|
|
|
// document URL is a chrome: URL. It is valid for a URL such as
|
|
|
|
// about:config to translate into a master document URL, whose
|
|
|
|
// prototype document nodes -- including prototype scripts that
|
|
|
|
// hold GC roots protecting their mJSObject pointers -- are not
|
|
|
|
// cached in the XUL prototype cache. See StartDocumentLoad,
|
|
|
|
// the fillXULCache logic.
|
|
|
|
//
|
|
|
|
// A document such as about:config is free to load a script via
|
|
|
|
// a URL such as chrome://global/content/config.js, and we must
|
|
|
|
// not cache that script object without a prototype cache entry
|
|
|
|
// containing a companion nsXULPrototypeScript node that owns a
|
|
|
|
// GC root protecting the script object. Otherwise, the script
|
|
|
|
// cache entry will dangle once the uncached prototype document
|
2019-08-09 20:47:41 +03:00
|
|
|
// is released when its owning document is unloaded.
|
2019-03-09 04:00:23 +03:00
|
|
|
//
|
|
|
|
// (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
|
|
|
|
// the true crime story.)
|
|
|
|
bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
|
|
|
|
|
|
|
|
if (useXULCache && IsChromeURI(mDocumentURI) &&
|
|
|
|
scriptProto->HasScriptObject()) {
|
|
|
|
JS::Rooted<JSScript*> script(RootingCx(), scriptProto->GetScriptObject());
|
|
|
|
nsXULPrototypeCache::GetInstance()->PutScript(scriptProto->mSrcURI,
|
|
|
|
script);
|
|
|
|
}
|
|
|
|
// ignore any evaluation errors
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = ResumeWalk();
|
|
|
|
|
2019-08-09 20:47:41 +03:00
|
|
|
// Load a pointer to the prototype-script's list of documents who
|
2019-03-09 04:00:23 +03:00
|
|
|
// raced to load the same script
|
|
|
|
PrototypeDocumentContentSink** docp = &scriptProto->mSrcLoadWaiters;
|
|
|
|
|
|
|
|
// Resume walking other documents that waited for this one's load, first
|
|
|
|
// executing the script we just compiled, in each doc's script context
|
|
|
|
PrototypeDocumentContentSink* doc;
|
|
|
|
while ((doc = *docp) != nullptr) {
|
|
|
|
NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
|
|
|
|
"waiting for wrong script to load?");
|
|
|
|
doc->mCurrentScriptProto = nullptr;
|
|
|
|
|
|
|
|
// Unlink doc from scriptProto's list before executing and resuming
|
|
|
|
*docp = doc->mNextSrcLoadWaiter;
|
|
|
|
doc->mNextSrcLoadWaiter = nullptr;
|
|
|
|
|
|
|
|
if (aStatus == NS_BINDING_ABORTED && !scriptProto->HasScriptObject()) {
|
|
|
|
// If the previous doc load was aborted, we want to try loading
|
|
|
|
// again for the next doc. Otherwise, one abort would lead to all
|
|
|
|
// subsequent waiting docs to abort as well.
|
|
|
|
bool block = false;
|
|
|
|
doc->LoadScript(scriptProto, &block);
|
|
|
|
NS_RELEASE(doc);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute only if we loaded and compiled successfully, then resume
|
|
|
|
if (NS_SUCCEEDED(aStatus) && scriptProto->HasScriptObject()) {
|
|
|
|
doc->ExecuteScript(scriptProto);
|
|
|
|
}
|
|
|
|
doc->ResumeWalk();
|
|
|
|
NS_RELEASE(doc);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::ExecuteScript(
|
|
|
|
nsXULPrototypeScript* aScript) {
|
|
|
|
MOZ_ASSERT(aScript != nullptr, "null ptr");
|
|
|
|
NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER);
|
|
|
|
|
|
|
|
nsIScriptGlobalObject* scriptGlobalObject;
|
|
|
|
bool aHasHadScriptHandlingObject;
|
|
|
|
scriptGlobalObject =
|
|
|
|
mDocument->GetScriptHandlingObject(aHasHadScriptHandlingObject);
|
|
|
|
|
|
|
|
NS_ENSURE_TRUE(scriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
rv = scriptGlobalObject->EnsureScriptEnvironment();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
// Execute the precompiled script with the given version
|
|
|
|
nsAutoMicroTask mt;
|
|
|
|
|
|
|
|
// We're about to run script via JS::CloneAndExecuteScript, so we need an
|
|
|
|
// AutoEntryScript. This is Gecko specific and not in any spec.
|
|
|
|
AutoEntryScript aes(scriptGlobalObject, "precompiled XUL <script> element");
|
|
|
|
JSContext* cx = aes.cx();
|
|
|
|
|
|
|
|
JS::Rooted<JSScript*> scriptObject(cx, aScript->GetScriptObject());
|
|
|
|
NS_ENSURE_TRUE(scriptObject, NS_ERROR_UNEXPECTED);
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
|
|
|
|
NS_ENSURE_TRUE(xpc::Scriptability::Get(global).Allowed(), NS_OK);
|
|
|
|
|
|
|
|
// The script is in the compilation scope. Clone it into the target scope
|
|
|
|
// and execute it. On failure, ~AutoScriptEntry will handle exceptions, so
|
|
|
|
// there is no need to manually check the return value.
|
|
|
|
JS::RootedValue rval(cx);
|
|
|
|
JS::CloneAndExecuteScript(cx, scriptObject, &rval);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::CreateElementFromPrototype(
|
2020-04-05 21:36:00 +03:00
|
|
|
nsXULPrototypeElement* aPrototype, Element** aResult, nsIContent* aParent) {
|
2019-03-09 04:00:23 +03:00
|
|
|
// Create a content model element from a prototype element.
|
2020-04-05 21:36:00 +03:00
|
|
|
MOZ_ASSERT(aPrototype, "null ptr");
|
2019-03-09 04:00:23 +03:00
|
|
|
if (!aPrototype) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aResult = nullptr;
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
|
|
|
|
MOZ_LOG(
|
|
|
|
gLog, LogLevel::Debug,
|
|
|
|
("prototype: creating <%s> from prototype",
|
|
|
|
NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<Element> result;
|
|
|
|
|
2020-04-05 21:36:00 +03:00
|
|
|
Document* doc = aParent ? aParent->OwnerDoc() : mDocument.get();
|
2019-03-09 04:00:23 +03:00
|
|
|
if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
|
2020-04-05 21:36:00 +03:00
|
|
|
const bool isRoot = !aParent;
|
2019-03-09 04:00:23 +03:00
|
|
|
// If it's a XUL element, it'll be lightweight until somebody
|
|
|
|
// monkeys with it.
|
2020-04-05 21:36:00 +03:00
|
|
|
rv = nsXULElement::CreateFromPrototype(aPrototype, doc, true, isRoot,
|
2019-03-09 04:00:23 +03:00
|
|
|
getter_AddRefs(result));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
} else {
|
|
|
|
// If it's not a XUL element, it's gonna be heavyweight no matter
|
|
|
|
// what. So we need to copy everything out of the prototype
|
|
|
|
// into the element. Get a nodeinfo from our nodeinfo manager
|
|
|
|
// for this node.
|
2020-04-05 21:36:00 +03:00
|
|
|
RefPtr<NodeInfo> newNodeInfo = doc->NodeInfoManager()->GetNodeInfo(
|
2019-03-09 04:00:23 +03:00
|
|
|
aPrototype->mNodeInfo->NameAtom(),
|
|
|
|
aPrototype->mNodeInfo->GetPrefixAtom(),
|
|
|
|
aPrototype->mNodeInfo->NamespaceID(), nsINode::ELEMENT_NODE);
|
2020-04-05 21:36:00 +03:00
|
|
|
if (!newNodeInfo) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
const bool isScript =
|
|
|
|
newNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
|
|
|
|
newNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG);
|
2019-03-09 04:00:23 +03:00
|
|
|
if (aPrototype->mIsAtom &&
|
|
|
|
newNodeInfo->NamespaceID() == kNameSpaceID_XHTML) {
|
|
|
|
rv = NS_NewHTMLElement(getter_AddRefs(result), newNodeInfo.forget(),
|
|
|
|
NOT_FROM_PARSER, aPrototype->mIsAtom);
|
|
|
|
} else {
|
|
|
|
rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(),
|
|
|
|
NOT_FROM_PARSER);
|
|
|
|
}
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
rv = AddAttributes(aPrototype, result);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
2019-04-09 00:01:29 +03:00
|
|
|
|
2020-04-05 21:36:00 +03:00
|
|
|
if (isScript) {
|
2019-04-09 00:01:29 +03:00
|
|
|
nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(result);
|
|
|
|
MOZ_ASSERT(sele, "Node didn't QI to script.");
|
2021-01-18 20:38:56 +03:00
|
|
|
|
|
|
|
sele->FreezeExecutionAttrs(doc);
|
2019-04-09 00:01:29 +03:00
|
|
|
// Script loading is handled by the this content sink, so prevent the
|
|
|
|
// script from loading when it is bound to the document.
|
2021-01-18 20:38:56 +03:00
|
|
|
//
|
|
|
|
// NOTE(emilio): This is only done for non-module scripts, because we
|
|
|
|
// don't support caching modules properly yet, see the comment in
|
|
|
|
// XULContentSinkImpl::OpenScript. For non-inline scripts, this is enough,
|
|
|
|
// since we can start the load when the node is inserted. Non-inline
|
|
|
|
// scripts need another special-case in CloseElement.
|
|
|
|
if (!sele->GetScriptIsModule()) {
|
|
|
|
sele->PreventExecution();
|
|
|
|
}
|
2019-04-09 00:01:29 +03:00
|
|
|
}
|
2019-03-09 04:00:23 +03:00
|
|
|
}
|
|
|
|
|
2020-04-05 21:36:00 +03:00
|
|
|
// FIXME(bug 1627474): Is this right if this is inside an <html:template>?
|
2019-07-26 23:19:26 +03:00
|
|
|
if (result->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nid)) {
|
2021-02-26 12:11:46 +03:00
|
|
|
mDocument->mL10nProtoElements.InsertOrUpdate(result, RefPtr{aPrototype});
|
2019-07-26 23:19:26 +03:00
|
|
|
result->SetElementCreatedFromPrototypeAndHasUnmodifiedL10n();
|
|
|
|
}
|
2019-03-09 04:00:23 +03:00
|
|
|
result.forget(aResult);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PrototypeDocumentContentSink::AddAttributes(
|
|
|
|
nsXULPrototypeElement* aPrototype, Element* aElement) {
|
|
|
|
nsresult rv;
|
|
|
|
|
2019-07-26 23:16:22 +03:00
|
|
|
for (size_t i = 0; i < aPrototype->mAttributes.Length(); ++i) {
|
2019-03-09 04:00:23 +03:00
|
|
|
nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
|
|
|
|
nsAutoString valueStr;
|
|
|
|
protoattr->mValue.ToString(valueStr);
|
|
|
|
|
|
|
|
rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
|
|
|
|
protoattr->mName.LocalName(),
|
|
|
|
protoattr->mName.GetPrefix(), valueStr, false);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-01-18 16:48:34 +03:00
|
|
|
} // namespace mozilla::dom
|