зеркало из https://github.com/mozilla/gecko-dev.git
Bug 877072 - Script execution order for nested imports. r=mrbkap
This commit is contained in:
Родитель
43708c49c3
Коммит
5d97edf128
|
@ -133,8 +133,8 @@ typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
|
|||
} // namespace mozilla
|
||||
|
||||
#define NS_IDOCUMENT_IID \
|
||||
{ 0x613ea294, 0x0288, 0x48b4, \
|
||||
{ 0x9e, 0x7b, 0x0f, 0xe9, 0x3f, 0x8c, 0xf8, 0x95 } }
|
||||
{ 0x42a263db, 0x6ac6, 0x40ff, \
|
||||
{ 0x89, 0xe2, 0x25, 0x12, 0xe4, 0xbc, 0x2d, 0x2d } }
|
||||
|
||||
// Enum for requesting a particular type of document when creating a doc
|
||||
enum DocumentFlavor {
|
||||
|
@ -2353,11 +2353,15 @@ public:
|
|||
|
||||
// Each import tree has exactly one master document which is
|
||||
// the root of the tree, and owns the browser context.
|
||||
virtual already_AddRefed<nsIDocument> MasterDocument() = 0;
|
||||
virtual nsIDocument* MasterDocument() = 0;
|
||||
virtual void SetMasterDocument(nsIDocument* master) = 0;
|
||||
virtual bool IsMasterDocument() = 0;
|
||||
virtual already_AddRefed<mozilla::dom::ImportManager> ImportManager() = 0;
|
||||
|
||||
virtual mozilla::dom::ImportManager* ImportManager() = 0;
|
||||
// We keep track of the order of sub imports were added to the document.
|
||||
virtual bool HasSubImportLink(nsINode* aLink) = 0;
|
||||
virtual uint32_t IndexOfSubImportLink(nsINode* aLink) = 0;
|
||||
virtual void AddSubImportLink(nsINode* aLink) = 0;
|
||||
virtual nsINode* GetSubImportLink(uint32_t aIdx) = 0;
|
||||
/*
|
||||
* Given a node, get a weak reference to it and append that reference to
|
||||
* mBlockedTrackingNodes. Can be used later on to look up a node in it.
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
#include "nsScriptLoader.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AutoError
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class AutoError {
|
||||
public:
|
||||
AutoError(mozilla::dom::ImportLoader* loader, bool scriptsBlocked = true)
|
||||
|
@ -49,6 +53,212 @@ private:
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ImportLoader::Updater
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
ImportLoader::Updater::GetReferrerChain(nsINode* aNode,
|
||||
nsTArray<nsINode*>& aResult)
|
||||
{
|
||||
// We fill up the array backward. First the last link: aNode.
|
||||
MOZ_ASSERT(mLoader->mLinks.Contains(aNode));
|
||||
|
||||
aResult.AppendElement(aNode);
|
||||
nsINode* node = aNode;
|
||||
nsRefPtr<ImportManager> manager = mLoader->Manager();
|
||||
for (ImportLoader* referrersLoader = manager->Find(node->OwnerDoc());
|
||||
referrersLoader;
|
||||
referrersLoader = manager->Find(node->OwnerDoc()))
|
||||
{
|
||||
// Then walking up the main referrer chain and append each link
|
||||
// to the array.
|
||||
node = referrersLoader->GetMainReferrer();
|
||||
MOZ_ASSERT(node);
|
||||
aResult.AppendElement(node);
|
||||
}
|
||||
|
||||
// The reversed order is more useful for consumers.
|
||||
// XXX: This should probably go to nsTArray or some generic utility
|
||||
// lib for our containers that we don't have... I would really like to
|
||||
// get rid of this part...
|
||||
uint32_t l = aResult.Length();
|
||||
for (uint32_t i = 0; i < l / 2; i++) {
|
||||
Swap(aResult[i], aResult[l - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ImportLoader::Updater::ShouldUpdate(nsTArray<nsINode*>& aNewPath)
|
||||
{
|
||||
// Let's walk down on the main referrer chains of both the current main and
|
||||
// the new link, and find the last pair of links that are from the same
|
||||
// document. This is the junction point between the two referrer chain. Their
|
||||
// order in the subimport list of that document will determine if we have to
|
||||
// update the spanning tree or this new edge changes nothing in the script
|
||||
// execution order.
|
||||
nsTArray<nsINode*> oldPath;
|
||||
GetReferrerChain(mLoader->mLinks[mLoader->mMainReferrer], oldPath);
|
||||
uint32_t max = std::min(oldPath.Length(), aNewPath.Length());
|
||||
MOZ_ASSERT(max > 0);
|
||||
uint32_t lastCommonImportAncestor = 0;
|
||||
|
||||
for (uint32_t i = 0;
|
||||
i < max && oldPath[i]->OwnerDoc() == aNewPath[i]->OwnerDoc();
|
||||
i++)
|
||||
{
|
||||
lastCommonImportAncestor = i;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(lastCommonImportAncestor < max);
|
||||
nsINode* oldLink = oldPath[lastCommonImportAncestor];
|
||||
nsINode* newLink = aNewPath[lastCommonImportAncestor];
|
||||
|
||||
if ((lastCommonImportAncestor == max - 1) &&
|
||||
newLink == oldLink ) {
|
||||
// If one chain contains the other entirely, then this is a simple cycle,
|
||||
// nothing to be done here.
|
||||
MOZ_ASSERT(oldPath.Length() != aNewPath.Length(),
|
||||
"This would mean that new link == main referrer link");
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aNewPath != oldPath,
|
||||
"How could this happen?");
|
||||
nsIDocument* doc = oldLink->OwnerDoc();
|
||||
MOZ_ASSERT(doc->HasSubImportLink(newLink));
|
||||
MOZ_ASSERT(doc->HasSubImportLink(oldLink));
|
||||
|
||||
return doc->IndexOfSubImportLink(newLink) < doc->IndexOfSubImportLink(oldLink);
|
||||
}
|
||||
|
||||
void
|
||||
ImportLoader::Updater::UpdateMainReferrer(uint32_t aNewIdx)
|
||||
{
|
||||
MOZ_ASSERT(aNewIdx < mLoader->mLinks.Length());
|
||||
nsINode* newMainReferrer = mLoader->mLinks[aNewIdx];
|
||||
// This new link means we have to execute our scripts sooner...
|
||||
if (mLoader->mDocument) {
|
||||
// Our nearest predecessor has changed. So let's remove our pending
|
||||
// ScriptLoader from the old one.
|
||||
nsRefPtr<ImportManager> manager = mLoader->Manager();
|
||||
nsScriptLoader* loader = mLoader->mDocument->ScriptLoader();
|
||||
ImportLoader*& pred = mLoader->mBlockingPredecessor;
|
||||
if (pred) {
|
||||
pred->RemoveBlockedScriptLoader(loader);
|
||||
}
|
||||
// And add it to the new one if there is any.
|
||||
pred = manager->GetNearestPredecessor(newMainReferrer);
|
||||
if (pred) {
|
||||
pred->AddBlockedScriptLoader(loader);
|
||||
}
|
||||
}
|
||||
if (mLoader->IsBlocking()) {
|
||||
// Our import parent is changed as well, let's unblock it and block
|
||||
// the new one.
|
||||
mLoader->mImportParent->ScriptLoader()->RemoveExecuteBlocker();
|
||||
newMainReferrer->OwnerDoc()->ScriptLoader()->AddExecuteBlocker();
|
||||
}
|
||||
// Finally update mMainReferrer to point to the newly added link.
|
||||
mLoader->mMainReferrer = aNewIdx;
|
||||
mLoader->mImportParent = newMainReferrer->OwnerDoc();
|
||||
}
|
||||
|
||||
nsINode*
|
||||
ImportLoader::Updater::NextDependant(nsINode* aCurrentLink,
|
||||
nsTArray<nsINode*>& aPath,
|
||||
NodeTable& aVisitedNodes, bool aSkipChildren)
|
||||
{
|
||||
// Depth first graph traversal.
|
||||
if (!aSkipChildren) {
|
||||
// "first child"
|
||||
ImportLoader* loader = mLoader->Manager()->Find(aCurrentLink);
|
||||
if (loader && loader->GetDocument()) {
|
||||
nsINode* firstSubImport = loader->GetDocument()->GetSubImportLink(0);
|
||||
if (firstSubImport && !aVisitedNodes.Contains(firstSubImport)) {
|
||||
aPath.AppendElement(aCurrentLink);
|
||||
aVisitedNodes.PutEntry(firstSubImport);
|
||||
return firstSubImport;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aPath.AppendElement(aCurrentLink);
|
||||
// "(parent's) next sibling"
|
||||
while(aPath.Length() > 1) {
|
||||
aCurrentLink = aPath[aPath.Length() - 1];
|
||||
aPath.RemoveElementAt(aPath.Length() - 1);
|
||||
|
||||
// Let's find the next "sibling"
|
||||
ImportLoader* loader = mLoader->Manager()->Find(aCurrentLink->OwnerDoc());
|
||||
MOZ_ASSERT(loader && loader->GetDocument(), "How can this happend?");
|
||||
nsIDocument* doc = loader->GetDocument();
|
||||
MOZ_ASSERT(doc->HasSubImportLink(aCurrentLink));
|
||||
uint32_t idx = doc->IndexOfSubImportLink(aCurrentLink);
|
||||
nsINode* next = doc->GetSubImportLink(idx + 1);
|
||||
if (next) {
|
||||
// Note: If we found an already visited link that means the parent links has
|
||||
// closed the circle it's always the "first child" section that should find
|
||||
// the first already visited node. Let's just assert that.
|
||||
MOZ_ASSERT(!aVisitedNodes.Contains(next));
|
||||
aVisitedNodes.PutEntry(next);
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ImportLoader::Updater::UpdateDependants(nsINode* aNode,
|
||||
nsTArray<nsINode*>& aPath)
|
||||
{
|
||||
NodeTable visitedNodes;
|
||||
nsINode* current = aNode;
|
||||
uint32_t initialLength = aPath.Length();
|
||||
bool neededUpdate = true;
|
||||
while ((current = NextDependant(current, aPath, visitedNodes, !neededUpdate))) {
|
||||
if (!current || aPath.Length() <= initialLength) {
|
||||
break;
|
||||
}
|
||||
ImportLoader* loader = mLoader->Manager()->Find(current);
|
||||
if (!loader) {
|
||||
continue;
|
||||
}
|
||||
Updater& updater = loader->mUpdater;
|
||||
neededUpdate = updater.ShouldUpdate(aPath);
|
||||
if (neededUpdate) {
|
||||
updater.UpdateMainReferrer(loader->mLinks.IndexOf(current));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImportLoader::Updater::UpdateSpanningTree(nsINode* aNode)
|
||||
{
|
||||
if (mLoader->mReady || mLoader->mStopped) {
|
||||
// Scripts already executed, nothing to be done here.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mLoader->mLinks.Length() == 1) {
|
||||
// If this is the first referrer, let's mark it.
|
||||
mLoader->mMainReferrer = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<nsINode*> newReferrerChain;
|
||||
GetReferrerChain(aNode, newReferrerChain);
|
||||
if (ShouldUpdate(newReferrerChain)) {
|
||||
UpdateMainReferrer(mLoader->mLinks.Length() - 1);
|
||||
UpdateDependants(aNode, newReferrerChain);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ImportLoader
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(ImportLoader)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
|
||||
|
@ -66,10 +276,13 @@ NS_IMPL_CYCLE_COLLECTION(ImportLoader,
|
|||
ImportLoader::ImportLoader(nsIURI* aURI, nsIDocument* aImportParent)
|
||||
: mURI(aURI)
|
||||
, mImportParent(aImportParent)
|
||||
, mBlockingPredecessor(nullptr)
|
||||
, mReady(false)
|
||||
, mStopped(false)
|
||||
, mBlockingScripts(false)
|
||||
{}
|
||||
, mUpdater(MOZ_THIS_IN_INITIALIZER_LIST())
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ImportLoader::BlockScripts()
|
||||
|
@ -84,9 +297,18 @@ ImportLoader::UnblockScripts()
|
|||
{
|
||||
MOZ_ASSERT(mBlockingScripts);
|
||||
mImportParent->ScriptLoader()->RemoveExecuteBlocker();
|
||||
// We probably should do a clever assertion here to see
|
||||
// if our ScriptLoader unblocked all these ScriptLoaders.
|
||||
mBlockedScriptLoaders.Clear();
|
||||
mBlockingScripts = false;
|
||||
}
|
||||
|
||||
void
|
||||
ImportLoader::SetBlockingPredecessor(ImportLoader* aLoader)
|
||||
{
|
||||
mBlockingPredecessor = aLoader;
|
||||
}
|
||||
|
||||
void
|
||||
ImportLoader::DispatchEventIfFinished(nsINode* aNode)
|
||||
{
|
||||
|
@ -99,6 +321,33 @@ ImportLoader::DispatchEventIfFinished(nsINode* aNode)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImportLoader::AddBlockedScriptLoader(nsScriptLoader* aScriptLoader)
|
||||
{
|
||||
MOZ_ASSERT(!mBlockedScriptLoaders.Contains(aScriptLoader),
|
||||
"Same scripts loader should be added only once");
|
||||
|
||||
aScriptLoader->AddExecuteBlocker();
|
||||
|
||||
if (mDocument) {
|
||||
// If the document is ready we can just add the pending script loader
|
||||
// to it. Otherwise we will add them once the document is created.
|
||||
mDocument->ScriptLoader()->AddPendingChildLoader(aScriptLoader);
|
||||
}
|
||||
// Let's keep track of the pending script loaders.
|
||||
mBlockedScriptLoaders.AppendElement(aScriptLoader);
|
||||
}
|
||||
|
||||
bool
|
||||
ImportLoader::RemoveBlockedScriptLoader(nsScriptLoader* aScriptLoader)
|
||||
{
|
||||
aScriptLoader->RemoveExecuteBlocker();
|
||||
if (mDocument) {
|
||||
mDocument->ScriptLoader()->RemovePendingChildLoader(aScriptLoader);
|
||||
}
|
||||
return mBlockedScriptLoaders.RemoveElement(aScriptLoader);
|
||||
}
|
||||
|
||||
void
|
||||
ImportLoader::AddLinkElement(nsINode* aNode)
|
||||
{
|
||||
|
@ -106,14 +355,15 @@ ImportLoader::AddLinkElement(nsINode* aNode)
|
|||
// refers to an import that is already finished loading or
|
||||
// stopped trying, we need to fire the corresponding event
|
||||
// on it.
|
||||
mLinks.AppendObject(aNode);
|
||||
mLinks.AppendElement(aNode);
|
||||
mUpdater.UpdateSpanningTree(aNode);
|
||||
DispatchEventIfFinished(aNode);
|
||||
}
|
||||
|
||||
void
|
||||
ImportLoader::RemoveLinkElement(nsINode* aNode)
|
||||
{
|
||||
mLinks.RemoveObject(aNode);
|
||||
mLinks.RemoveElement(aNode);
|
||||
}
|
||||
|
||||
// Events has to be fired with a script runner, so mImport can
|
||||
|
@ -159,8 +409,8 @@ void
|
|||
ImportLoader::Done()
|
||||
{
|
||||
mReady = true;
|
||||
uint32_t count = mLinks.Count();
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uint32_t l = mLinks.Length();
|
||||
for (uint32_t i = 0; i < l; i++) {
|
||||
DispatchLoadEvent(mLinks[i]);
|
||||
}
|
||||
UnblockScripts();
|
||||
|
@ -172,8 +422,8 @@ ImportLoader::Error(bool aUnblockScripts)
|
|||
{
|
||||
mDocument = nullptr;
|
||||
mStopped = true;
|
||||
uint32_t count = mLinks.Count();
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uint32_t l = mLinks.Length();
|
||||
for (uint32_t i = 0; i < l; i++) {
|
||||
DispatchErrorEvent(mLinks[i]);
|
||||
}
|
||||
if (aUnblockScripts) {
|
||||
|
@ -375,6 +625,10 @@ ImportLoader::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
|||
nsCOMPtr<nsIDocument> master = mImportParent->MasterDocument();
|
||||
mDocument->SetMasterDocument(master);
|
||||
|
||||
for (uint32_t i = 0; i < mBlockedScriptLoaders.Length(); i++) {
|
||||
mDocument->ScriptLoader()->AddPendingChildLoader(mBlockedScriptLoaders[i]);
|
||||
}
|
||||
|
||||
// We have to connect the blank document we created with the channel we opened,
|
||||
// and create its own LoadGroup for it.
|
||||
nsCOMPtr<nsIStreamListener> listener;
|
||||
|
@ -388,7 +642,25 @@ ImportLoader::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
|||
true);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
|
||||
|
||||
// Let's start parser.
|
||||
nsCOMPtr<nsIURI> originalURI;
|
||||
rv = channel->GetOriginalURI(getter_AddRefs(originalURI));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
|
||||
|
||||
nsCOMPtr<nsIURI> URI;
|
||||
rv = channel->GetURI(getter_AddRefs(URI));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
|
||||
MOZ_ASSERT(URI, "URI of a channel should never be null");
|
||||
|
||||
bool equals;
|
||||
rv = URI->Equals(originalURI, &equals);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
|
||||
|
||||
if (!equals) {
|
||||
// In case of a redirection we must add the new URI to the import map.
|
||||
Manager()->AddLoaderWithNewURI(this, URI);
|
||||
}
|
||||
|
||||
// Let's start the parser.
|
||||
mParserStreamListener = listener;
|
||||
rv = listener->OnStartRequest(aRequest, aContext);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
|
||||
|
@ -397,6 +669,10 @@ ImportLoader::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ImportManager
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(ImportManager,
|
||||
mImports)
|
||||
|
||||
|
@ -414,16 +690,78 @@ ImportManager::Get(nsIURI* aURI, nsINode* aNode, nsIDocument* aOrigDocument)
|
|||
// and start it up.
|
||||
nsRefPtr<ImportLoader> loader;
|
||||
mImports.Get(aURI, getter_AddRefs(loader));
|
||||
|
||||
bool needToStart = false;
|
||||
if (!loader) {
|
||||
loader = new ImportLoader(aURI, aOrigDocument);
|
||||
mImports.Put(aURI, loader);
|
||||
needToStart = true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(loader);
|
||||
// Let's keep track of the sub imports links in each document. It will
|
||||
// be used later for scrip execution order calculation. (see UpdateSpanningTree)
|
||||
// NOTE: removing and adding back the link to the tree somewhere else will
|
||||
// NOT have an effect on script execution order.
|
||||
if (!aOrigDocument->HasSubImportLink(aNode)) {
|
||||
aOrigDocument->AddSubImportLink(aNode);
|
||||
}
|
||||
|
||||
loader->AddLinkElement(aNode);
|
||||
|
||||
if (needToStart) {
|
||||
loader->Open();
|
||||
}
|
||||
loader->AddLinkElement(aNode);
|
||||
MOZ_ASSERT(loader);
|
||||
|
||||
return loader.forget();
|
||||
}
|
||||
|
||||
ImportLoader*
|
||||
ImportManager::Find(nsIDocument* aImport)
|
||||
{
|
||||
return mImports.GetWeak(aImport->GetDocumentURIObject());
|
||||
}
|
||||
|
||||
ImportLoader*
|
||||
ImportManager::Find(nsINode* aLink)
|
||||
{
|
||||
HTMLLinkElement* linkElement = static_cast<HTMLLinkElement*>(aLink);
|
||||
nsCOMPtr<nsIURI> uri = linkElement->GetHrefURI();
|
||||
return mImports.GetWeak(uri);
|
||||
}
|
||||
|
||||
void
|
||||
ImportManager::AddLoaderWithNewURI(ImportLoader* aLoader, nsIURI* aNewURI)
|
||||
{
|
||||
mImports.Put(aNewURI, aLoader);
|
||||
}
|
||||
|
||||
nsRefPtr<ImportLoader> ImportManager::GetNearestPredecessor(nsINode* aNode)
|
||||
{
|
||||
// Return the previous link if there is any in the same document.
|
||||
nsIDocument* doc = aNode->OwnerDoc();
|
||||
int32_t idx = doc->IndexOfSubImportLink(aNode);
|
||||
MOZ_ASSERT(idx != -1, "aNode must be a sub import link of its owner document");
|
||||
if (idx == 0) {
|
||||
if (doc->IsMasterDocument()) {
|
||||
// If there is no previous one, and it was the master document, then
|
||||
// there is no predecessor.
|
||||
return nullptr;
|
||||
}
|
||||
// Else we find the main referrer of the import parent of the link's document.
|
||||
// And do a recursion.
|
||||
ImportLoader* owner = Find(doc);
|
||||
MOZ_ASSERT(owner);
|
||||
nsCOMPtr<nsINode> mainReferrer = owner->GetMainReferrer();
|
||||
return GetNearestPredecessor(mainReferrer);
|
||||
}
|
||||
MOZ_ASSERT(idx > 0);
|
||||
HTMLLinkElement* link =
|
||||
static_cast<HTMLLinkElement*>(doc->GetSubImportLink(idx - 1));
|
||||
nsCOMPtr<nsIURI> uri = link->GetHrefURI();
|
||||
nsRefPtr<ImportLoader> ret;
|
||||
mImports.Get(uri, getter_AddRefs(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -39,12 +39,13 @@
|
|||
#ifndef mozilla_dom_ImportManager_h__
|
||||
#define mozilla_dom_ImportManager_h__
|
||||
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIWeakReferenceUtils.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsScriptLoader.h"
|
||||
#include "nsURIHashKey.h"
|
||||
|
||||
class nsIDocument;
|
||||
|
@ -58,11 +59,70 @@ namespace dom {
|
|||
|
||||
class ImportManager;
|
||||
|
||||
typedef nsTHashtable<nsPtrHashKey<nsINode>> NodeTable;
|
||||
|
||||
class ImportLoader MOZ_FINAL : public nsIStreamListener
|
||||
, public nsIDOMEventListener
|
||||
{
|
||||
|
||||
// A helper inner class to decouple the logic of updating the import graph
|
||||
// after a new import link has been found by one of the parsers.
|
||||
class Updater {
|
||||
|
||||
public:
|
||||
Updater(ImportLoader* aLoader) : mLoader(aLoader)
|
||||
{}
|
||||
|
||||
// After a new link is added that refers to this import, we
|
||||
// have to update the spanning tree, since given this new link the
|
||||
// priority of this import might be higher in the scripts
|
||||
// execution order than before. It updates mMainReferrer, mImportParent,
|
||||
// the corresponding pending ScriptRunners, etc.
|
||||
// It also handles updating additional dependant loaders via the
|
||||
// UpdateDependants calls.
|
||||
// (NOTE: See GetMainReferrer about spanning tree.)
|
||||
void UpdateSpanningTree(nsINode* aNode);
|
||||
|
||||
private:
|
||||
// Returns an array of links that forms a referring chain from
|
||||
// the master document to this import. Each link in the array
|
||||
// is marked as main referrer in the list.
|
||||
void GetReferrerChain(nsINode* aNode, nsTArray<nsINode*>& aResult);
|
||||
|
||||
// Once we find a new referrer path to our import, we have to see if
|
||||
// it changes the load order hence we have to do an update on the graph.
|
||||
bool ShouldUpdate(nsTArray<nsINode*>& aNewPath);
|
||||
void UpdateMainReferrer(uint32_t newIdx);
|
||||
|
||||
// It's a depth first graph traversal algorithm, for UpdateDependants. The
|
||||
// nodes in the graph are the import link elements, and there is a directed
|
||||
// edge from link1 to link2 if link2 is a subimport in the import document
|
||||
// of link1.
|
||||
// If the ImportLoader that aCurrentLink points to didn't need to be updated
|
||||
// the algorithm skips its "children" (subimports). Note, that this graph can
|
||||
// also contain cycles, aVisistedLinks is used to track the already visited
|
||||
// links to avoid an infinite loop.
|
||||
// aPath - (in/out) the referrer link chain of aCurrentLink when called, and
|
||||
// of the next link when the function returns
|
||||
// aVisitedLinks - (in/out) list of links that the traversal already visited
|
||||
// (to handle cycles in the graph)
|
||||
// aSkipChildren - when aCurrentLink points to an import that did not need
|
||||
// to be updated, we can skip its sub-imports ('children')
|
||||
nsINode* NextDependant(nsINode* aCurrentLink,
|
||||
nsTArray<nsINode*>& aPath,
|
||||
NodeTable& aVisitedLinks, bool aSkipChildren);
|
||||
|
||||
// When we find a new link that changes the load order of the known imports,
|
||||
// we also have to check all the subimports of it, to see if they need an
|
||||
// update too. (see test_imports_nested_2.html)
|
||||
void UpdateDependants(nsINode* aNode, nsTArray<nsINode*>& aPath);
|
||||
|
||||
ImportLoader* mLoader;
|
||||
};
|
||||
|
||||
friend class ::AutoError;
|
||||
friend class ImportManager;
|
||||
friend class Updater;
|
||||
|
||||
public:
|
||||
ImportLoader(nsIURI* aURI, nsIDocument* aOriginDocument);
|
||||
|
@ -83,11 +143,46 @@ public:
|
|||
bool IsReady() { return mReady; }
|
||||
bool IsStopped() { return mStopped; }
|
||||
bool IsBlocking() { return mBlockingScripts; }
|
||||
already_AddRefed<nsIDocument> GetImport()
|
||||
{
|
||||
return mReady ? nsCOMPtr<nsIDocument>(mDocument).forget() : nullptr;
|
||||
|
||||
ImportManager* Manager() {
|
||||
MOZ_ASSERT(mDocument || mImportParent, "One of them should be always set");
|
||||
return (mDocument ? mDocument : mImportParent)->ImportManager();
|
||||
}
|
||||
|
||||
// Simply getter for the import document. Can return a partially parsed
|
||||
// document if called too early.
|
||||
nsIDocument* GetDocument()
|
||||
{
|
||||
return mDocument;
|
||||
}
|
||||
|
||||
// Getter for the import document that is used in the spec. Returns
|
||||
// nullptr if the import is not yet ready.
|
||||
nsIDocument* GetImport()
|
||||
{
|
||||
return mReady ? mDocument : nullptr;
|
||||
}
|
||||
|
||||
// There is only one referring link that is marked as primary link per
|
||||
// imports. This is the one that has to be taken into account when
|
||||
// scrip execution order is determined. Links marked as primary link form
|
||||
// a spanning tree in the import graph. (Eliminating the cycles and
|
||||
// multiple parents.) This spanning tree is recalculated every time
|
||||
// a new import link is added to the manager.
|
||||
nsINode* GetMainReferrer()
|
||||
{
|
||||
return mLinks.IsEmpty() ? nullptr : mLinks[mMainReferrer];
|
||||
}
|
||||
|
||||
// An import is not only blocked by its import children, but also
|
||||
// by its predecessors. It's enough to find the closest predecessor
|
||||
// and wait for that to run its scripts. We keep track of all the
|
||||
// ScriptRunners that are waiting for this import. NOTE: updating
|
||||
// the main referrer might change this list.
|
||||
void AddBlockedScriptLoader(nsScriptLoader* aScriptLoader);
|
||||
bool RemoveBlockedScriptLoader(nsScriptLoader* aScriptLoader);
|
||||
void SetBlockingPredecessor(ImportLoader* aLoader);
|
||||
|
||||
private:
|
||||
~ImportLoader() {}
|
||||
|
||||
|
@ -122,12 +217,25 @@ private:
|
|||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIStreamListener> mParserStreamListener;
|
||||
nsCOMPtr<nsIDocument> mImportParent;
|
||||
ImportLoader* mBlockingPredecessor;
|
||||
|
||||
// List of the LinkElements that are referring to this import
|
||||
// we need to keep track of them so we can fire event on them.
|
||||
nsCOMArray<nsINode> mLinks;
|
||||
nsTArray<nsCOMPtr<nsINode>> mLinks;
|
||||
|
||||
// List of pending ScriptLoaders that are waiting for this import
|
||||
// to finish.
|
||||
nsTArray<nsRefPtr<nsScriptLoader>> mBlockedScriptLoaders;
|
||||
|
||||
// There is always exactly one referrer link that is flagged as
|
||||
// the main referrer the primary link. This is the one that is
|
||||
// used in the script execution order calculation.
|
||||
// ("Branch" according to the spec.)
|
||||
uint32_t mMainReferrer;
|
||||
bool mReady;
|
||||
bool mStopped;
|
||||
bool mBlockingScripts;
|
||||
Updater mUpdater;
|
||||
};
|
||||
|
||||
class ImportManager MOZ_FINAL : public nsISupports
|
||||
|
@ -142,9 +250,24 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(ImportManager)
|
||||
|
||||
// Finds the ImportLoader that belongs to aImport in the map.
|
||||
ImportLoader* Find(nsIDocument* aImport);
|
||||
|
||||
// Find the ImportLoader aLink refers to.
|
||||
ImportLoader* Find(nsINode* aLink);
|
||||
|
||||
void AddLoaderWithNewURI(ImportLoader* aLoader, nsIURI* aNewURI);
|
||||
|
||||
// When a new import link is added, this getter either creates
|
||||
// a new ImportLoader for it, or returns an existing one if
|
||||
// it was already created and in the import map.
|
||||
already_AddRefed<ImportLoader> Get(nsIURI* aURI, nsINode* aNode,
|
||||
nsIDocument* aOriginDocument);
|
||||
|
||||
// It finds the predecessor for an import link node that runs its
|
||||
// scripts the latest among its predecessors.
|
||||
nsRefPtr<ImportLoader> GetNearestPredecessor(nsINode* aNode);
|
||||
|
||||
private:
|
||||
ImportMap mImports;
|
||||
};
|
||||
|
|
|
@ -1972,6 +1972,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSubImportLinks)
|
||||
|
||||
for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
|
||||
cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports());
|
||||
|
@ -2036,6 +2038,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMasterDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSubImportLinks)
|
||||
|
||||
tmp->mParentDocument = nullptr;
|
||||
|
||||
|
|
|
@ -1291,10 +1291,10 @@ public:
|
|||
mozilla::ErrorResult& rv) MOZ_OVERRIDE;
|
||||
virtual void UseRegistryFromDocument(nsIDocument* aDocument) MOZ_OVERRIDE;
|
||||
|
||||
virtual already_AddRefed<nsIDocument> MasterDocument()
|
||||
virtual nsIDocument* MasterDocument()
|
||||
{
|
||||
return mMasterDocument ? (nsCOMPtr<nsIDocument>(mMasterDocument)).forget()
|
||||
: (nsCOMPtr<nsIDocument>(this)).forget();
|
||||
return mMasterDocument ? mMasterDocument.get()
|
||||
: this;
|
||||
}
|
||||
|
||||
virtual void SetMasterDocument(nsIDocument* master)
|
||||
|
@ -1307,11 +1307,11 @@ public:
|
|||
return !mMasterDocument;
|
||||
}
|
||||
|
||||
virtual already_AddRefed<mozilla::dom::ImportManager> ImportManager()
|
||||
virtual mozilla::dom::ImportManager* ImportManager()
|
||||
{
|
||||
if (mImportManager) {
|
||||
MOZ_ASSERT(!mMasterDocument, "Only the master document has ImportManager set");
|
||||
return nsRefPtr<mozilla::dom::ImportManager>(mImportManager).forget();
|
||||
return mImportManager.get();
|
||||
}
|
||||
|
||||
if (mMasterDocument) {
|
||||
|
@ -1323,7 +1323,28 @@ public:
|
|||
// master document and this is the first import in it.
|
||||
// Let's create a new manager.
|
||||
mImportManager = new mozilla::dom::ImportManager();
|
||||
return nsRefPtr<mozilla::dom::ImportManager>(mImportManager).forget();
|
||||
return mImportManager.get();
|
||||
}
|
||||
|
||||
virtual bool HasSubImportLink(nsINode* aLink)
|
||||
{
|
||||
return mSubImportLinks.Contains(aLink);
|
||||
}
|
||||
|
||||
virtual uint32_t IndexOfSubImportLink(nsINode* aLink)
|
||||
{
|
||||
return mSubImportLinks.IndexOf(aLink);
|
||||
}
|
||||
|
||||
virtual void AddSubImportLink(nsINode* aLink)
|
||||
{
|
||||
mSubImportLinks.AppendElement(aLink);
|
||||
}
|
||||
|
||||
virtual nsINode* GetSubImportLink(uint32_t aIdx)
|
||||
{
|
||||
return aIdx < mSubImportLinks.Length() ? mSubImportLinks[aIdx].get()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
virtual void UnblockDOMContentLoaded() MOZ_OVERRIDE;
|
||||
|
@ -1749,6 +1770,7 @@ private:
|
|||
|
||||
nsCOMPtr<nsIDocument> mMasterDocument;
|
||||
nsRefPtr<mozilla::dom::ImportManager> mImportManager;
|
||||
nsTArray<nsCOMPtr<nsINode> > mSubImportLinks;
|
||||
|
||||
// Set to true when the document is possibly controlled by the ServiceWorker.
|
||||
// Used to prevent multiple requests to ServiceWorkerManager.
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "nsSandboxFlags.h"
|
||||
#include "nsContentTypeParser.h"
|
||||
#include "nsINetworkPredictor.h"
|
||||
#include "ImportManager.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
|
||||
#include "mozilla/CORSMode.h"
|
||||
|
@ -1227,7 +1228,7 @@ nsScriptLoader::ReadyToExecuteScripts()
|
|||
if (!SelfReadyToExecuteScripts()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
for (nsIDocument* doc = mDocument; doc; doc = doc->GetParentDocument()) {
|
||||
nsScriptLoader* ancestor = doc->ScriptLoader();
|
||||
if (!ancestor->SelfReadyToExecuteScripts() &&
|
||||
|
@ -1237,10 +1238,44 @@ nsScriptLoader::ReadyToExecuteScripts()
|
|||
}
|
||||
}
|
||||
|
||||
if (!mDocument->IsMasterDocument()) {
|
||||
nsRefPtr<ImportManager> im = mDocument->ImportManager();
|
||||
nsRefPtr<ImportLoader> loader = im->Find(mDocument);
|
||||
MOZ_ASSERT(loader, "How can we have an import document without a loader?");
|
||||
|
||||
// The referring link that counts in the execution order calculation
|
||||
// (in spec: flagged as branch)
|
||||
nsCOMPtr<nsINode> referrer = loader->GetMainReferrer();
|
||||
MOZ_ASSERT(referrer, "There has to be a main referring link for each imports");
|
||||
|
||||
// Import documents are blocked by their import predecessors. We need to
|
||||
// wait with script execution until all the predecessors are done.
|
||||
// Technically it means we have to wait for the last one to finish,
|
||||
// which is the neares one to us in the order.
|
||||
nsRefPtr<ImportLoader> lastPred = im->GetNearestPredecessor(referrer);
|
||||
if (!lastPred) {
|
||||
// If there is no predecessor we can run.
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = lastPred->GetDocument();
|
||||
if (!doc || (doc && !doc->ScriptLoader()->SelfReadyToExecuteScripts())) {
|
||||
// Document has not been created yet or it was created but not ready.
|
||||
// Either case we are blocked by it. The ImportLoader will take care
|
||||
// of blocking us, and adding the pending child loader to the blocking
|
||||
// ScriptLoader when it's possible (at this point the blocking loader
|
||||
// might not have created the document/ScriptLoader)
|
||||
lastPred->AddBlockedScriptLoader(this);
|
||||
// As more imports are parsed, this can change, let's cache what we
|
||||
// blocked, so it can be later updated if needed (see: ImportLoader::Updater).
|
||||
loader->SetBlockingPredecessor(lastPred);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// This function was copied from nsParser.cpp. It was simplified a bit.
|
||||
static bool
|
||||
DetectByteOrderMark(const unsigned char* aBytes, int32_t aLen, nsCString& oCharset)
|
||||
|
|
|
@ -249,6 +249,14 @@ public:
|
|||
nsresult ProcessOffThreadRequest(nsScriptLoadRequest *aRequest,
|
||||
void **aOffThreadToken);
|
||||
|
||||
bool AddPendingChildLoader(nsScriptLoader* aChild) {
|
||||
return mPendingChildLoaders.AppendElement(aChild) != nullptr;
|
||||
}
|
||||
|
||||
bool RemovePendingChildLoader(nsScriptLoader* aLoader) {
|
||||
return mPendingChildLoaders.RemoveElement(aLoader);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~nsScriptLoader();
|
||||
|
||||
|
@ -302,10 +310,6 @@ private:
|
|||
return mEnabled && !mBlockerCount;
|
||||
}
|
||||
|
||||
bool AddPendingChildLoader(nsScriptLoader* aChild) {
|
||||
return mPendingChildLoaders.AppendElement(aChild) != nullptr;
|
||||
}
|
||||
|
||||
nsresult AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest);
|
||||
nsresult ProcessRequest(nsScriptLoadRequest* aRequest,
|
||||
void **aOffThreadToken = nullptr);
|
||||
|
|
|
@ -264,15 +264,6 @@ HTMLLinkElement::UpdateImport()
|
|||
return;
|
||||
}
|
||||
|
||||
// Until the script execution order is not sorted out for nested cases
|
||||
// let's not allow them.
|
||||
if (!doc->IsMasterDocument()) {
|
||||
nsContentUtils::LogSimpleConsoleError(
|
||||
NS_LITERAL_STRING("Nested imports are not supported yet"),
|
||||
"Imports");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. rel type should be import.
|
||||
nsAutoString rel;
|
||||
GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel);
|
||||
|
@ -527,7 +518,7 @@ HTMLLinkElement::WrapNode(JSContext* aCx)
|
|||
already_AddRefed<nsIDocument>
|
||||
HTMLLinkElement::GetImport()
|
||||
{
|
||||
return mImportLoader ? mImportLoader->GetImport() : nullptr;
|
||||
return mImportLoader ? nsRefPtr<nsIDocument>(mImportLoader->GetImport()).forget() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importA2.html" id="A2" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("A1");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("A2");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importB2.html" id="B2" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("B1");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("B2");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importC2.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("C1");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importE.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("C10");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importC3.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("C2");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importC4.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("C3");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importC5.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("C4");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importC6.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("C5");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importC7.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("C6");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importC8.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("C7");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importC9.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("C8");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importC10.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("C9");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<body>
|
||||
<script>
|
||||
order.push("D");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="import" href="file_importD.html" onload="loaded()" onerror="failed()"></link>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
order.push("E");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,20 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
file_importA1.html
|
||||
file_importA2.html
|
||||
file_importB1.html
|
||||
file_importB2.html
|
||||
file_importC1.html
|
||||
file_importC2.html
|
||||
file_importC3.html
|
||||
file_importC4.html
|
||||
file_importC5.html
|
||||
file_importC6.html
|
||||
file_importC7.html
|
||||
file_importC8.html
|
||||
file_importC9.html
|
||||
file_importC10.html
|
||||
file_importD.html
|
||||
file_importE.html
|
||||
|
||||
|
|
@ -462,6 +462,8 @@ skip-if = buildapp == 'b2g' || e10s # b2g(multiple concurrent window.open()s fai
|
|||
[test_imports_basics.html]
|
||||
[test_imports_redirect.html]
|
||||
[test_imports_nonhttp.html]
|
||||
[test_imports_nested.html]
|
||||
[test_imports_nested_2.html]
|
||||
[test_li_attributes_reflection.html]
|
||||
[test_link_attributes_reflection.html]
|
||||
[test_link_sizes.html]
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=877072
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 877072</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=877072">Mozilla Bug 877072</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var counter = 0;
|
||||
var failed = 0;
|
||||
var order = [];
|
||||
function loaded() {
|
||||
counter++;
|
||||
}
|
||||
function failed() {
|
||||
failed ++;
|
||||
}
|
||||
</script>
|
||||
|
||||
<link rel="import" href="imports/file_importA1.html" id="A1" onload="loaded()" onerror="failed()"></link>
|
||||
<link rel="import" href="imports/file_importB1.html" id="B1" onload="loaded()" onerror="failed()"></link>
|
||||
<link rel="import" href="imports/file_importB2.html" id="B2_2" onload="loaded()" onerror="failed()"></link>
|
||||
|
||||
<script type="text/javascript">
|
||||
is(counter, 5, "Imports are loaded");
|
||||
is(failed, 0, "No error in imports");
|
||||
var expected = ["A2", "A1", "B2", "B1"];
|
||||
for (i in expected)
|
||||
is(order[i], expected[i], "import " + i + " should be " + expected[i]);
|
||||
SimpleTest.finish();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=877072
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 877072</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=877072">Mozilla Bug 877072</a>
|
||||
|
||||
<script type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var counter = 0;
|
||||
var failed = 0;
|
||||
var order = [];
|
||||
function loaded() {
|
||||
counter++;
|
||||
}
|
||||
function failed() {
|
||||
failed ++;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--
|
||||
|
||||
Master -> c1 -> ... -> C10
|
||||
| | |
|
||||
| - - -> D |
|
||||
| ^ |
|
||||
| | |
|
||||
- - - -> E < - - - - - -
|
||||
|
||||
This test is for testing ImportLoader::UpdateDependants.Because of the long
|
||||
chain to C10, it's very likely that C10->E will be the last edge the ImportLoaders
|
||||
find. At that point it won't only have to update E but also its subimport D.
|
||||
|
||||
-->
|
||||
|
||||
<link rel="import" href="imports/file_importC1.html" onload="loaded()" onerror="failed()"></link>
|
||||
<link rel="import" href="imports/file_importD.html" onload="loaded()" onerror="failed()"></link>
|
||||
<link rel="import" href="imports/file_importE.html" onload="loaded()" onerror="failed()"></link>
|
||||
|
||||
<script type="text/javascript">
|
||||
is(counter, 14, "Imports are loaded"); // 12 imports but 14 link imports...
|
||||
is(failed, 0, "No error in imports");
|
||||
var expected = ["D", "E", "C10", "C9", "C8", "C7", "C6", "C5", "C4", "C3", "C2", "C1"];
|
||||
for (i in expected)
|
||||
is(order[i], expected[i], "import " + i + " should be " + expected[i]);
|
||||
SimpleTest.finish();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче