michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsTArray.h" michael@0: #include "nsString.h" michael@0: #include "nsCSSStyleSheet.h" michael@0: #include "nsIStyleRuleProcessor.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsXBLService.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsXBLResourceLoader.h" michael@0: #include "nsXBLPrototypeResources.h" michael@0: #include "nsIDocumentObserver.h" michael@0: #include "imgILoader.h" michael@0: #include "imgRequestProxy.h" michael@0: #include "mozilla/css/Loader.h" michael@0: #include "nsIURI.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsFrameManager.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsXBLPrototypeBinding.h" michael@0: #include "nsCSSRuleProcessor.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(nsXBLResourceLoader, mBoundElements) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLResourceLoader) michael@0: NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLResourceLoader) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLResourceLoader) michael@0: michael@0: struct nsXBLResource michael@0: { michael@0: nsXBLResource* mNext; michael@0: nsIAtom* mType; michael@0: nsString mSrc; michael@0: michael@0: nsXBLResource(nsIAtom* aType, const nsAString& aSrc) michael@0: { michael@0: MOZ_COUNT_CTOR(nsXBLResource); michael@0: mNext = nullptr; michael@0: mType = aType; michael@0: mSrc = aSrc; michael@0: } michael@0: michael@0: ~nsXBLResource() michael@0: { michael@0: MOZ_COUNT_DTOR(nsXBLResource); michael@0: NS_CONTENT_DELETE_LIST_MEMBER(nsXBLResource, this, mNext); michael@0: } michael@0: }; michael@0: michael@0: nsXBLResourceLoader::nsXBLResourceLoader(nsXBLPrototypeBinding* aBinding, michael@0: nsXBLPrototypeResources* aResources) michael@0: :mBinding(aBinding), michael@0: mResources(aResources), michael@0: mResourceList(nullptr), michael@0: mLastResource(nullptr), michael@0: mLoadingResources(false), michael@0: mInLoadResourcesFunc(false), michael@0: mPendingSheets(0) michael@0: { michael@0: } michael@0: michael@0: nsXBLResourceLoader::~nsXBLResourceLoader() michael@0: { michael@0: delete mResourceList; michael@0: } michael@0: michael@0: void michael@0: nsXBLResourceLoader::LoadResources(bool* aResult) michael@0: { michael@0: mInLoadResourcesFunc = true; michael@0: michael@0: if (mLoadingResources) { michael@0: *aResult = (mPendingSheets == 0); michael@0: mInLoadResourcesFunc = false; michael@0: return; michael@0: } michael@0: michael@0: mLoadingResources = true; michael@0: *aResult = true; michael@0: michael@0: // Declare our loaders. michael@0: nsCOMPtr doc = mBinding->XBLDocumentInfo()->GetDocument(); michael@0: michael@0: mozilla::css::Loader* cssLoader = doc->CSSLoader(); michael@0: nsIURI *docURL = doc->GetDocumentURI(); michael@0: nsIPrincipal* docPrincipal = doc->NodePrincipal(); michael@0: michael@0: nsCOMPtr url; michael@0: michael@0: for (nsXBLResource* curr = mResourceList; curr; curr = curr->mNext) { michael@0: if (curr->mSrc.IsEmpty()) michael@0: continue; michael@0: michael@0: if (NS_FAILED(NS_NewURI(getter_AddRefs(url), curr->mSrc, michael@0: doc->GetDocumentCharacterSet().get(), docURL))) michael@0: continue; michael@0: michael@0: if (curr->mType == nsGkAtoms::image) { michael@0: if (!nsContentUtils::CanLoadImage(url, doc, doc, docPrincipal)) { michael@0: // We're not permitted to load this image, move on... michael@0: continue; michael@0: } michael@0: michael@0: // Now kick off the image load... michael@0: // Passing nullptr for pretty much everything -- cause we don't care! michael@0: // XXX: initialDocumentURI is nullptr! michael@0: nsRefPtr req; michael@0: nsContentUtils::LoadImage(url, doc, docPrincipal, docURL, nullptr, michael@0: nsIRequest::LOAD_BACKGROUND, EmptyString(), michael@0: getter_AddRefs(req)); michael@0: } michael@0: else if (curr->mType == nsGkAtoms::stylesheet) { michael@0: // Kick off the load of the stylesheet. michael@0: michael@0: // Always load chrome synchronously michael@0: // XXXbz should that still do a content policy check? michael@0: bool chrome; michael@0: nsresult rv; michael@0: if (NS_SUCCEEDED(url->SchemeIs("chrome", &chrome)) && chrome) michael@0: { michael@0: rv = nsContentUtils::GetSecurityManager()-> michael@0: CheckLoadURIWithPrincipal(docPrincipal, url, michael@0: nsIScriptSecurityManager::ALLOW_CHROME); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsRefPtr sheet; michael@0: rv = cssLoader->LoadSheetSync(url, getter_AddRefs(sheet)); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "Load failed!!!"); michael@0: if (NS_SUCCEEDED(rv)) michael@0: { michael@0: rv = StyleSheetLoaded(sheet, false, NS_OK); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "Processing the style sheet failed!!!"); michael@0: } michael@0: } michael@0: } michael@0: else michael@0: { michael@0: rv = cssLoader->LoadSheet(url, docPrincipal, EmptyCString(), this); michael@0: if (NS_SUCCEEDED(rv)) michael@0: ++mPendingSheets; michael@0: } michael@0: } michael@0: } michael@0: michael@0: *aResult = (mPendingSheets == 0); michael@0: mInLoadResourcesFunc = false; michael@0: michael@0: // Destroy our resource list. michael@0: delete mResourceList; michael@0: mResourceList = nullptr; michael@0: } michael@0: michael@0: // nsICSSLoaderObserver michael@0: NS_IMETHODIMP michael@0: nsXBLResourceLoader::StyleSheetLoaded(nsCSSStyleSheet* aSheet, michael@0: bool aWasAlternate, michael@0: nsresult aStatus) michael@0: { michael@0: if (!mResources) { michael@0: // Our resources got destroyed -- just bail out michael@0: return NS_OK; michael@0: } michael@0: michael@0: mResources->mStyleSheetList.AppendElement(aSheet); michael@0: michael@0: if (!mInLoadResourcesFunc) michael@0: mPendingSheets--; michael@0: michael@0: if (mPendingSheets == 0) { michael@0: // All stylesheets are loaded. michael@0: mResources->mRuleProcessor = michael@0: new nsCSSRuleProcessor(mResources->mStyleSheetList, michael@0: nsStyleSet::eDocSheet, michael@0: nullptr); michael@0: michael@0: // XXX Check for mPendingScripts when scripts also come online. michael@0: if (!mInLoadResourcesFunc) michael@0: NotifyBoundElements(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsXBLResourceLoader::AddResource(nsIAtom* aResourceType, const nsAString& aSrc) michael@0: { michael@0: nsXBLResource* res = new nsXBLResource(aResourceType, aSrc); michael@0: if (!res) michael@0: return; michael@0: michael@0: if (!mResourceList) michael@0: mResourceList = res; michael@0: else michael@0: mLastResource->mNext = res; michael@0: michael@0: mLastResource = res; michael@0: } michael@0: michael@0: void michael@0: nsXBLResourceLoader::AddResourceListener(nsIContent* aBoundElement) michael@0: { michael@0: if (aBoundElement) { michael@0: mBoundElements.AppendObject(aBoundElement); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsXBLResourceLoader::NotifyBoundElements() michael@0: { michael@0: nsXBLService* xblService = nsXBLService::GetInstance(); michael@0: if (!xblService) michael@0: return; michael@0: michael@0: nsIURI* bindingURI = mBinding->BindingURI(); michael@0: michael@0: uint32_t eltCount = mBoundElements.Count(); michael@0: for (uint32_t j = 0; j < eltCount; j++) { michael@0: nsCOMPtr content = mBoundElements.ObjectAt(j); michael@0: michael@0: bool ready = false; michael@0: xblService->BindingReady(content, bindingURI, &ready); michael@0: michael@0: if (ready) { michael@0: // We need the document to flush out frame construction and michael@0: // such, so we want to use the current document. michael@0: nsIDocument* doc = content->GetCurrentDoc(); michael@0: michael@0: if (doc) { michael@0: // Flush first to make sure we can get the frame for content michael@0: doc->FlushPendingNotifications(Flush_Frames); michael@0: michael@0: // If |content| is (in addition to having binding |mBinding|) michael@0: // also a descendant of another element with binding |mBinding|, michael@0: // then we might have just constructed it due to the michael@0: // notification of its parent. (We can know about both if the michael@0: // binding loads were triggered from the DOM rather than frame michael@0: // construction.) So we have to check both whether the element michael@0: // has a primary frame and whether it's in the undisplayed map michael@0: // before sending a ContentInserted notification, or bad things michael@0: // will happen. michael@0: nsIPresShell *shell = doc->GetShell(); michael@0: if (shell) { michael@0: nsIFrame* childFrame = content->GetPrimaryFrame(); michael@0: if (!childFrame) { michael@0: // Check to see if it's in the undisplayed content map. michael@0: nsStyleContext* sc = michael@0: shell->FrameManager()->GetUndisplayedContent(content); michael@0: michael@0: if (!sc) { michael@0: shell->RecreateFramesFor(content); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Flush again michael@0: // XXXbz why is this needed? michael@0: doc->FlushPendingNotifications(Flush_ContentAndNotify); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Clear out the whole array. michael@0: mBoundElements.Clear(); michael@0: michael@0: // Delete ourselves. michael@0: mResources->ClearLoader(); michael@0: } michael@0: michael@0: nsresult michael@0: nsXBLResourceLoader::Write(nsIObjectOutputStream* aStream) michael@0: { michael@0: nsresult rv; michael@0: michael@0: for (nsXBLResource* curr = mResourceList; curr; curr = curr->mNext) { michael@0: if (curr->mType == nsGkAtoms::image) michael@0: rv = aStream->Write8(XBLBinding_Serialize_Image); michael@0: else if (curr->mType == nsGkAtoms::stylesheet) michael@0: rv = aStream->Write8(XBLBinding_Serialize_Stylesheet); michael@0: else michael@0: continue; michael@0: michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = aStream->WriteWStringZ(curr->mSrc.get()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: }