Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / ImportManager.cpp
blob9ccab96095d89e73465057b8e9537878884eaf3f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ImportManager.h"
9 #include "mozilla/EventListenerManager.h"
10 #include "HTMLLinkElement.h"
11 #include "nsContentPolicyUtils.h"
12 #include "nsContentUtils.h"
13 #include "nsCORSListenerProxy.h"
14 #include "nsIChannel.h"
15 #include "nsIContentPolicy.h"
16 #include "nsIContentSecurityPolicy.h"
17 #include "nsIDocument.h"
18 #include "nsIDOMDocument.h"
19 #include "nsIDOMEvent.h"
20 #include "nsIPrincipal.h"
21 #include "nsIScriptObjectPrincipal.h"
22 #include "nsScriptLoader.h"
23 #include "nsNetUtil.h"
25 //-----------------------------------------------------------------------------
26 // AutoError
27 //-----------------------------------------------------------------------------
29 class AutoError {
30 public:
31 explicit AutoError(mozilla::dom::ImportLoader* loader, bool scriptsBlocked = true)
32 : mLoader(loader)
33 , mPassed(false)
34 , mScriptsBlocked(scriptsBlocked)
37 ~AutoError()
39 if (!mPassed) {
40 mLoader->Error(mScriptsBlocked);
44 void Pass() { mPassed = true; }
46 private:
47 mozilla::dom::ImportLoader* mLoader;
48 bool mPassed;
49 bool mScriptsBlocked;
52 namespace mozilla {
53 namespace dom {
55 //-----------------------------------------------------------------------------
56 // ImportLoader::Updater
57 //-----------------------------------------------------------------------------
59 void
60 ImportLoader::Updater::GetReferrerChain(nsINode* aNode,
61 nsTArray<nsINode*>& aResult)
63 // We fill up the array backward. First the last link: aNode.
64 MOZ_ASSERT(mLoader->mLinks.Contains(aNode));
66 aResult.AppendElement(aNode);
67 nsINode* node = aNode;
68 nsRefPtr<ImportManager> manager = mLoader->Manager();
69 for (ImportLoader* referrersLoader = manager->Find(node->OwnerDoc());
70 referrersLoader;
71 referrersLoader = manager->Find(node->OwnerDoc()))
73 // Then walking up the main referrer chain and append each link
74 // to the array.
75 node = referrersLoader->GetMainReferrer();
76 MOZ_ASSERT(node);
77 aResult.AppendElement(node);
80 // The reversed order is more useful for consumers.
81 // XXX: This should probably go to nsTArray or some generic utility
82 // lib for our containers that we don't have... I would really like to
83 // get rid of this part...
84 uint32_t l = aResult.Length();
85 for (uint32_t i = 0; i < l / 2; i++) {
86 Swap(aResult[i], aResult[l - i - 1]);
90 bool
91 ImportLoader::Updater::ShouldUpdate(nsTArray<nsINode*>& aNewPath)
93 if (mLoader->Manager()->GetNearestPredecessor(mLoader->GetMainReferrer()) !=
94 mLoader->mBlockingPredecessor) {
95 return true;
97 // Let's walk down on the main referrer chains of both the current main and
98 // the new link, and find the last pair of links that are from the same
99 // document. This is the junction point between the two referrer chain. Their
100 // order in the subimport list of that document will determine if we have to
101 // update the spanning tree or this new edge changes nothing in the script
102 // execution order.
103 nsTArray<nsINode*> oldPath;
104 GetReferrerChain(mLoader->mLinks[mLoader->mMainReferrer], oldPath);
105 uint32_t max = std::min(oldPath.Length(), aNewPath.Length());
106 MOZ_ASSERT(max > 0);
107 uint32_t lastCommonImportAncestor = 0;
109 for (uint32_t i = 0;
110 i < max && oldPath[i]->OwnerDoc() == aNewPath[i]->OwnerDoc();
111 i++)
113 lastCommonImportAncestor = i;
116 MOZ_ASSERT(lastCommonImportAncestor < max);
117 nsINode* oldLink = oldPath[lastCommonImportAncestor];
118 nsINode* newLink = aNewPath[lastCommonImportAncestor];
120 if ((lastCommonImportAncestor == max - 1) &&
121 newLink == oldLink ) {
122 // If one chain contains the other entirely, then this is a simple cycle,
123 // nothing to be done here.
124 MOZ_ASSERT(oldPath.Length() != aNewPath.Length(),
125 "This would mean that new link == main referrer link");
126 return false;
129 MOZ_ASSERT(aNewPath != oldPath,
130 "How could this happen?");
131 nsIDocument* doc = oldLink->OwnerDoc();
132 MOZ_ASSERT(doc->HasSubImportLink(newLink));
133 MOZ_ASSERT(doc->HasSubImportLink(oldLink));
135 return doc->IndexOfSubImportLink(newLink) < doc->IndexOfSubImportLink(oldLink);
138 void
139 ImportLoader::Updater::UpdateMainReferrer(uint32_t aNewIdx)
141 MOZ_ASSERT(aNewIdx < mLoader->mLinks.Length());
142 nsINode* newMainReferrer = mLoader->mLinks[aNewIdx];
144 // This new link means we have to execute our scripts sooner...
145 // Let's make sure that unblocking a loader does not trigger a script execution.
146 // So we start with placing the new blockers and only then will we remove any
147 // blockers.
148 if (mLoader->IsBlocking()) {
149 // Our import parent is changed, let's block the new one and later unblock
150 // the old one.
151 newMainReferrer->OwnerDoc()->ScriptLoader()->AddExecuteBlocker();
152 newMainReferrer->OwnerDoc()->BlockDOMContentLoaded();
155 if (mLoader->mDocument) {
156 // Our nearest predecessor has changed. So let's add the ScriptLoader to the
157 // new one if there is any. And remove it from the old one.
158 nsRefPtr<ImportManager> manager = mLoader->Manager();
159 nsScriptLoader* loader = mLoader->mDocument->ScriptLoader();
160 ImportLoader*& pred = mLoader->mBlockingPredecessor;
161 ImportLoader* newPred = manager->GetNearestPredecessor(newMainReferrer);
162 if (pred) {
163 if (newPred) {
164 newPred->AddBlockedScriptLoader(loader);
166 pred->RemoveBlockedScriptLoader(loader);
170 if (mLoader->IsBlocking()) {
171 mLoader->mImportParent->ScriptLoader()->RemoveExecuteBlocker();
172 mLoader->mImportParent->UnblockDOMContentLoaded();
175 // Finally update mMainReferrer to point to the newly added link.
176 mLoader->mMainReferrer = aNewIdx;
177 mLoader->mImportParent = newMainReferrer->OwnerDoc();
180 nsINode*
181 ImportLoader::Updater::NextDependant(nsINode* aCurrentLink,
182 nsTArray<nsINode*>& aPath,
183 NodeTable& aVisitedNodes, bool aSkipChildren)
185 // Depth first graph traversal.
186 if (!aSkipChildren) {
187 // "first child"
188 ImportLoader* loader = mLoader->Manager()->Find(aCurrentLink);
189 if (loader && loader->GetDocument()) {
190 nsINode* firstSubImport = loader->GetDocument()->GetSubImportLink(0);
191 if (firstSubImport && !aVisitedNodes.Contains(firstSubImport)) {
192 aPath.AppendElement(aCurrentLink);
193 aVisitedNodes.PutEntry(firstSubImport);
194 return firstSubImport;
199 aPath.AppendElement(aCurrentLink);
200 // "(parent's) next sibling"
201 while(aPath.Length() > 1) {
202 aCurrentLink = aPath[aPath.Length() - 1];
203 aPath.RemoveElementAt(aPath.Length() - 1);
205 // Let's find the next "sibling"
206 ImportLoader* loader = mLoader->Manager()->Find(aCurrentLink->OwnerDoc());
207 MOZ_ASSERT(loader && loader->GetDocument(), "How can this happend?");
208 nsIDocument* doc = loader->GetDocument();
209 MOZ_ASSERT(doc->HasSubImportLink(aCurrentLink));
210 uint32_t idx = doc->IndexOfSubImportLink(aCurrentLink);
211 nsINode* next = doc->GetSubImportLink(idx + 1);
212 if (next) {
213 // Note: If we found an already visited link that means the parent links has
214 // closed the circle it's always the "first child" section that should find
215 // the first already visited node. Let's just assert that.
216 MOZ_ASSERT(!aVisitedNodes.Contains(next));
217 aVisitedNodes.PutEntry(next);
218 return next;
222 return nullptr;
225 void
226 ImportLoader::Updater::UpdateDependants(nsINode* aNode,
227 nsTArray<nsINode*>& aPath)
229 NodeTable visitedNodes;
230 nsINode* current = aNode;
231 uint32_t initialLength = aPath.Length();
232 bool neededUpdate = true;
233 while ((current = NextDependant(current, aPath, visitedNodes, !neededUpdate))) {
234 if (!current || aPath.Length() <= initialLength) {
235 break;
237 ImportLoader* loader = mLoader->Manager()->Find(current);
238 if (!loader) {
239 continue;
241 Updater& updater = loader->mUpdater;
242 neededUpdate = updater.ShouldUpdate(aPath);
243 if (neededUpdate) {
244 updater.UpdateMainReferrer(loader->mLinks.IndexOf(current));
249 void
250 ImportLoader::Updater::UpdateSpanningTree(nsINode* aNode)
252 if (mLoader->mReady || mLoader->mStopped) {
253 // Scripts already executed, nothing to be done here.
254 return;
257 if (mLoader->mLinks.Length() == 1) {
258 // If this is the first referrer, let's mark it.
259 mLoader->mMainReferrer = 0;
260 return;
263 nsTArray<nsINode*> newReferrerChain;
264 GetReferrerChain(aNode, newReferrerChain);
265 if (ShouldUpdate(newReferrerChain)) {
266 UpdateMainReferrer(mLoader->mLinks.Length() - 1);
267 UpdateDependants(aNode, newReferrerChain);
271 //-----------------------------------------------------------------------------
272 // ImportLoader
273 //-----------------------------------------------------------------------------
275 NS_INTERFACE_MAP_BEGIN(ImportLoader)
276 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
277 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
278 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(ImportLoader)
279 NS_INTERFACE_MAP_END
281 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImportLoader)
282 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImportLoader)
284 NS_IMPL_CYCLE_COLLECTION(ImportLoader,
285 mDocument,
286 mImportParent,
287 mLinks)
289 ImportLoader::ImportLoader(nsIURI* aURI, nsIDocument* aImportParent)
290 : mURI(aURI)
291 , mImportParent(aImportParent)
292 , mBlockingPredecessor(nullptr)
293 , mReady(false)
294 , mStopped(false)
295 , mBlockingScripts(false)
296 , mUpdater(this)
300 void
301 ImportLoader::BlockScripts()
303 MOZ_ASSERT(!mBlockingScripts);
304 mImportParent->ScriptLoader()->AddExecuteBlocker();
305 mImportParent->BlockDOMContentLoaded();
306 mBlockingScripts = true;
309 void
310 ImportLoader::UnblockScripts()
312 MOZ_ASSERT(mBlockingScripts);
313 mImportParent->ScriptLoader()->RemoveExecuteBlocker();
314 mImportParent->UnblockDOMContentLoaded();
315 for (uint32_t i = 0; i < mBlockedScriptLoaders.Length(); i++) {
316 mBlockedScriptLoaders[i]->RemoveExecuteBlocker();
318 mBlockedScriptLoaders.Clear();
319 mBlockingScripts = false;
322 void
323 ImportLoader::SetBlockingPredecessor(ImportLoader* aLoader)
325 mBlockingPredecessor = aLoader;
328 void
329 ImportLoader::DispatchEventIfFinished(nsINode* aNode)
331 MOZ_ASSERT(!(mReady && mStopped));
332 if (mReady) {
333 DispatchLoadEvent(aNode);
335 if (mStopped) {
336 DispatchErrorEvent(aNode);
340 void
341 ImportLoader::AddBlockedScriptLoader(nsScriptLoader* aScriptLoader)
343 if (mBlockedScriptLoaders.Contains(aScriptLoader)) {
344 return;
347 aScriptLoader->AddExecuteBlocker();
349 // Let's keep track of the pending script loaders.
350 mBlockedScriptLoaders.AppendElement(aScriptLoader);
353 bool
354 ImportLoader::RemoveBlockedScriptLoader(nsScriptLoader* aScriptLoader)
356 aScriptLoader->RemoveExecuteBlocker();
357 return mBlockedScriptLoaders.RemoveElement(aScriptLoader);
360 void
361 ImportLoader::AddLinkElement(nsINode* aNode)
363 // If a new link element is added to the import tree that
364 // refers to an import that is already finished loading or
365 // stopped trying, we need to fire the corresponding event
366 // on it.
367 mLinks.AppendElement(aNode);
368 mUpdater.UpdateSpanningTree(aNode);
369 DispatchEventIfFinished(aNode);
372 void
373 ImportLoader::RemoveLinkElement(nsINode* aNode)
375 mLinks.RemoveElement(aNode);
378 // Events has to be fired with a script runner, so mImport can
379 // be set on the link element before the load event is fired even
380 // if ImportLoader::Get returns an already loaded import and we
381 // fire the load event immediately on the new referring link element.
382 class AsyncEvent : public nsRunnable {
383 public:
384 AsyncEvent(nsINode* aNode, bool aSuccess)
385 : mNode(aNode)
386 , mSuccess(aSuccess)
388 MOZ_ASSERT(mNode);
391 NS_IMETHOD Run() {
392 return nsContentUtils::DispatchTrustedEvent(mNode->OwnerDoc(),
393 mNode,
394 mSuccess ? NS_LITERAL_STRING("load")
395 : NS_LITERAL_STRING("error"),
396 /* aCanBubble = */ false,
397 /* aCancelable = */ false);
400 private:
401 nsCOMPtr<nsINode> mNode;
402 bool mSuccess;
405 void
406 ImportLoader::DispatchErrorEvent(nsINode* aNode)
408 nsContentUtils::AddScriptRunner(new AsyncEvent(aNode, /* aSuccess = */ false));
411 void
412 ImportLoader::DispatchLoadEvent(nsINode* aNode)
414 nsContentUtils::AddScriptRunner(new AsyncEvent(aNode, /* aSuccess = */ true));
417 void
418 ImportLoader::Done()
420 mReady = true;
421 uint32_t l = mLinks.Length();
422 for (uint32_t i = 0; i < l; i++) {
423 DispatchLoadEvent(mLinks[i]);
425 UnblockScripts();
426 ReleaseResources();
429 void
430 ImportLoader::Error(bool aUnblockScripts)
432 mDocument = nullptr;
433 mStopped = true;
434 uint32_t l = mLinks.Length();
435 for (uint32_t i = 0; i < l; i++) {
436 DispatchErrorEvent(mLinks[i]);
438 if (aUnblockScripts) {
439 UnblockScripts();
441 ReleaseResources();
444 // Release all the resources we don't need after there is no more
445 // data available on the channel, and the parser is done.
446 void ImportLoader::ReleaseResources()
448 mParserStreamListener = nullptr;
449 mImportParent = nullptr;
452 nsIPrincipal*
453 ImportLoader::Principal()
455 MOZ_ASSERT(mImportParent);
456 nsCOMPtr<nsIDocument> master = mImportParent->MasterDocument();
457 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(master);
458 MOZ_ASSERT(sop);
459 return sop->GetPrincipal();
462 void
463 ImportLoader::Open()
465 AutoError ae(this, false);
466 // Imports should obey to the master documents CSP.
467 nsCOMPtr<nsIDocument> master = mImportParent->MasterDocument();
468 nsIPrincipal* principal = Principal();
470 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
471 nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SUBDOCUMENT,
472 mURI,
473 principal,
474 mImportParent,
475 NS_LITERAL_CSTRING("text/html"),
476 /* extra = */ nullptr,
477 &shouldLoad,
478 nsContentUtils::GetContentPolicy(),
479 nsContentUtils::GetSecurityManager());
480 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
481 NS_WARN_IF_FALSE(NS_CP_ACCEPTED(shouldLoad), "ImportLoader rejected by CSP");
482 return;
485 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
486 rv = secMan->CheckLoadURIWithPrincipal(principal, mURI,
487 nsIScriptSecurityManager::STANDARD);
488 NS_ENSURE_SUCCESS_VOID(rv);
490 nsCOMPtr<nsILoadGroup> loadGroup = master->GetDocumentLoadGroup();
491 nsCOMPtr<nsIChannel> channel;
492 rv = NS_NewChannel(getter_AddRefs(channel),
493 mURI,
494 mImportParent,
495 nsILoadInfo::SEC_NORMAL,
496 nsIContentPolicy::TYPE_SUBDOCUMENT,
497 loadGroup,
498 nullptr, // aCallbacks
499 nsIRequest::LOAD_BACKGROUND);
501 NS_ENSURE_SUCCESS_VOID(rv);
503 // Init CORSListenerProxy and omit credentials.
504 nsRefPtr<nsCORSListenerProxy> corsListener =
505 new nsCORSListenerProxy(this, principal,
506 /* aWithCredentials */ false);
507 rv = corsListener->Init(channel, true);
508 NS_ENSURE_SUCCESS_VOID(rv);
510 rv = channel->AsyncOpen(corsListener, nullptr);
511 NS_ENSURE_SUCCESS_VOID(rv);
513 BlockScripts();
514 ae.Pass();
517 NS_IMETHODIMP
518 ImportLoader::OnDataAvailable(nsIRequest* aRequest,
519 nsISupports* aContext,
520 nsIInputStream* aStream,
521 uint64_t aOffset,
522 uint32_t aCount)
524 MOZ_ASSERT(mParserStreamListener);
526 AutoError ae(this);
527 nsresult rv;
528 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest, &rv);
529 NS_ENSURE_SUCCESS(rv, rv);
531 rv = mParserStreamListener->OnDataAvailable(channel, aContext,
532 aStream, aOffset,
533 aCount);
534 NS_ENSURE_SUCCESS(rv, rv);
535 ae.Pass();
536 return rv;
539 NS_IMETHODIMP
540 ImportLoader::HandleEvent(nsIDOMEvent *aEvent)
542 #ifdef DEBUG
543 nsAutoString type;
544 aEvent->GetType(type);
545 MOZ_ASSERT(type.EqualsLiteral("DOMContentLoaded"));
546 #endif
547 Done();
548 return NS_OK;
551 NS_IMETHODIMP
552 ImportLoader::OnStopRequest(nsIRequest* aRequest,
553 nsISupports* aContext,
554 nsresult aStatus)
556 // OnStartRequest throws a special error code to let us know that we
557 // shouldn't do anything else.
558 if (aStatus == NS_ERROR_DOM_ABORT_ERR) {
559 // We failed in OnStartRequest, nothing more to do (we've already
560 // dispatched an error event) just return here.
561 MOZ_ASSERT(mStopped);
562 return NS_OK;
565 if (mParserStreamListener) {
566 mParserStreamListener->OnStopRequest(aRequest, aContext, aStatus);
569 if (!mDocument) {
570 // If at this point we don't have a document, then the error was
571 // already reported.
572 return NS_ERROR_DOM_ABORT_ERR;
575 nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mDocument);
576 EventListenerManager* manager = eventTarget->GetOrCreateListenerManager();
577 manager->AddEventListenerByType(this,
578 NS_LITERAL_STRING("DOMContentLoaded"),
579 TrustedEventsAtSystemGroupBubble());
580 return NS_OK;
583 NS_IMETHODIMP
584 ImportLoader::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
586 AutoError ae(this);
587 nsIPrincipal* principal = Principal();
589 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
590 if (!channel) {
591 return NS_ERROR_DOM_ABORT_ERR;
594 if (nsContentUtils::IsSystemPrincipal(principal)) {
595 // We should never import non-system documents and run their scripts with system principal!
596 nsCOMPtr<nsIPrincipal> channelPrincipal;
597 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(channel,
598 getter_AddRefs(channelPrincipal));
599 if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
600 return NS_ERROR_FAILURE;
603 channel->SetOwner(principal);
605 nsAutoCString type;
606 channel->GetContentType(type);
607 if (!type.EqualsLiteral("text/html")) {
608 NS_WARNING("ImportLoader wrong content type");
609 return NS_ERROR_DOM_ABORT_ERR;
612 // The scope object is same for all the imports in an import tree,
613 // let's get it form the import parent.
614 nsCOMPtr<nsIGlobalObject> global = mImportParent->GetScopeObject();
615 nsCOMPtr<nsIDOMDocument> importDoc;
616 nsCOMPtr<nsIURI> baseURI = mImportParent->GetBaseURI();
617 const nsAString& emptyStr = EmptyString();
618 nsresult rv = NS_NewDOMDocument(getter_AddRefs(importDoc),
619 emptyStr, emptyStr, nullptr, mURI,
620 baseURI, principal, false, global,
621 DocumentFlavorHTML);
622 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
624 // The imported document must know which master document it belongs to.
625 mDocument = do_QueryInterface(importDoc);
626 nsCOMPtr<nsIDocument> master = mImportParent->MasterDocument();
627 mDocument->SetMasterDocument(master);
629 // We want to inherit the sandbox flags from the master document.
630 mDocument->SetSandboxFlags(master->GetSandboxFlags());
632 // We have to connect the blank document we created with the channel we opened,
633 // and create its own LoadGroup for it.
634 nsCOMPtr<nsIStreamListener> listener;
635 nsCOMPtr<nsILoadGroup> loadGroup;
636 channel->GetLoadGroup(getter_AddRefs(loadGroup));
637 nsCOMPtr<nsILoadGroup> newLoadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
638 NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
639 newLoadGroup->SetLoadGroup(loadGroup);
640 rv = mDocument->StartDocumentLoad("import", channel, newLoadGroup,
641 nullptr, getter_AddRefs(listener),
642 true);
643 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
645 nsCOMPtr<nsIURI> originalURI;
646 rv = channel->GetOriginalURI(getter_AddRefs(originalURI));
647 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
649 nsCOMPtr<nsIURI> URI;
650 rv = channel->GetURI(getter_AddRefs(URI));
651 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
652 MOZ_ASSERT(URI, "URI of a channel should never be null");
654 bool equals;
655 rv = URI->Equals(originalURI, &equals);
656 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
658 if (!equals) {
659 // In case of a redirection we must add the new URI to the import map.
660 Manager()->AddLoaderWithNewURI(this, URI);
663 // Let's start the parser.
664 mParserStreamListener = listener;
665 rv = listener->OnStartRequest(aRequest, aContext);
666 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
668 ae.Pass();
669 return NS_OK;
672 //-----------------------------------------------------------------------------
673 // ImportManager
674 //-----------------------------------------------------------------------------
676 NS_IMPL_CYCLE_COLLECTION(ImportManager,
677 mImports)
679 NS_INTERFACE_MAP_BEGIN(ImportManager)
680 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(ImportManager)
681 NS_INTERFACE_MAP_END
683 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImportManager)
684 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImportManager)
686 already_AddRefed<ImportLoader>
687 ImportManager::Get(nsIURI* aURI, nsINode* aNode, nsIDocument* aOrigDocument)
689 // Check if we have a loader for that URI, if not create one,
690 // and start it up.
691 nsRefPtr<ImportLoader> loader;
692 mImports.Get(aURI, getter_AddRefs(loader));
693 bool needToStart = false;
694 if (!loader) {
695 loader = new ImportLoader(aURI, aOrigDocument);
696 mImports.Put(aURI, loader);
697 needToStart = true;
700 MOZ_ASSERT(loader);
701 // Let's keep track of the sub imports links in each document. It will
702 // be used later for scrip execution order calculation. (see UpdateSpanningTree)
703 // NOTE: removing and adding back the link to the tree somewhere else will
704 // NOT have an effect on script execution order.
705 if (!aOrigDocument->HasSubImportLink(aNode)) {
706 aOrigDocument->AddSubImportLink(aNode);
709 loader->AddLinkElement(aNode);
711 if (needToStart) {
712 loader->Open();
715 return loader.forget();
718 ImportLoader*
719 ImportManager::Find(nsIDocument* aImport)
721 return mImports.GetWeak(aImport->GetDocumentURIObject());
724 ImportLoader*
725 ImportManager::Find(nsINode* aLink)
727 HTMLLinkElement* linkElement = static_cast<HTMLLinkElement*>(aLink);
728 nsCOMPtr<nsIURI> uri = linkElement->GetHrefURI();
729 return mImports.GetWeak(uri);
732 void
733 ImportManager::AddLoaderWithNewURI(ImportLoader* aLoader, nsIURI* aNewURI)
735 mImports.Put(aNewURI, aLoader);
738 nsRefPtr<ImportLoader> ImportManager::GetNearestPredecessor(nsINode* aNode)
740 // Return the previous link if there is any in the same document.
741 nsIDocument* doc = aNode->OwnerDoc();
742 int32_t idx = doc->IndexOfSubImportLink(aNode);
743 MOZ_ASSERT(idx != -1, "aNode must be a sub import link of its owner document");
745 for (; idx > 0; idx--) {
746 HTMLLinkElement* link =
747 static_cast<HTMLLinkElement*>(doc->GetSubImportLink(idx - 1));
748 nsCOMPtr<nsIURI> uri = link->GetHrefURI();
749 nsRefPtr<ImportLoader> ret;
750 mImports.Get(uri, getter_AddRefs(ret));
751 // Only main referrer links are interesting.
752 if (ret->GetMainReferrer() == link) {
753 return ret;
757 if (idx == 0) {
758 if (doc->IsMasterDocument()) {
759 // If there is no previous one, and it was the master document, then
760 // there is no predecessor.
761 return nullptr;
763 // Else we find the main referrer of the import parent of the link's document.
764 // And do a recursion.
765 ImportLoader* owner = Find(doc);
766 MOZ_ASSERT(owner);
767 nsCOMPtr<nsINode> mainReferrer = owner->GetMainReferrer();
768 return GetNearestPredecessor(mainReferrer);
771 return nullptr;
774 } // namespace dom
775 } // namespace mozilla