1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/xbl/nsXBLResourceLoader.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,300 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsTArray.h" 1.10 +#include "nsString.h" 1.11 +#include "nsCSSStyleSheet.h" 1.12 +#include "nsIStyleRuleProcessor.h" 1.13 +#include "nsIDocument.h" 1.14 +#include "nsIContent.h" 1.15 +#include "nsIPresShell.h" 1.16 +#include "nsXBLService.h" 1.17 +#include "nsIServiceManager.h" 1.18 +#include "nsXBLResourceLoader.h" 1.19 +#include "nsXBLPrototypeResources.h" 1.20 +#include "nsIDocumentObserver.h" 1.21 +#include "imgILoader.h" 1.22 +#include "imgRequestProxy.h" 1.23 +#include "mozilla/css/Loader.h" 1.24 +#include "nsIURI.h" 1.25 +#include "nsNetUtil.h" 1.26 +#include "nsGkAtoms.h" 1.27 +#include "nsFrameManager.h" 1.28 +#include "nsStyleContext.h" 1.29 +#include "nsXBLPrototypeBinding.h" 1.30 +#include "nsCSSRuleProcessor.h" 1.31 +#include "nsContentUtils.h" 1.32 +#include "nsStyleSet.h" 1.33 +#include "nsIScriptSecurityManager.h" 1.34 + 1.35 +NS_IMPL_CYCLE_COLLECTION(nsXBLResourceLoader, mBoundElements) 1.36 + 1.37 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLResourceLoader) 1.38 + NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) 1.39 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.40 +NS_INTERFACE_MAP_END 1.41 + 1.42 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLResourceLoader) 1.43 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLResourceLoader) 1.44 + 1.45 +struct nsXBLResource 1.46 +{ 1.47 + nsXBLResource* mNext; 1.48 + nsIAtom* mType; 1.49 + nsString mSrc; 1.50 + 1.51 + nsXBLResource(nsIAtom* aType, const nsAString& aSrc) 1.52 + { 1.53 + MOZ_COUNT_CTOR(nsXBLResource); 1.54 + mNext = nullptr; 1.55 + mType = aType; 1.56 + mSrc = aSrc; 1.57 + } 1.58 + 1.59 + ~nsXBLResource() 1.60 + { 1.61 + MOZ_COUNT_DTOR(nsXBLResource); 1.62 + NS_CONTENT_DELETE_LIST_MEMBER(nsXBLResource, this, mNext); 1.63 + } 1.64 +}; 1.65 + 1.66 +nsXBLResourceLoader::nsXBLResourceLoader(nsXBLPrototypeBinding* aBinding, 1.67 + nsXBLPrototypeResources* aResources) 1.68 +:mBinding(aBinding), 1.69 + mResources(aResources), 1.70 + mResourceList(nullptr), 1.71 + mLastResource(nullptr), 1.72 + mLoadingResources(false), 1.73 + mInLoadResourcesFunc(false), 1.74 + mPendingSheets(0) 1.75 +{ 1.76 +} 1.77 + 1.78 +nsXBLResourceLoader::~nsXBLResourceLoader() 1.79 +{ 1.80 + delete mResourceList; 1.81 +} 1.82 + 1.83 +void 1.84 +nsXBLResourceLoader::LoadResources(bool* aResult) 1.85 +{ 1.86 + mInLoadResourcesFunc = true; 1.87 + 1.88 + if (mLoadingResources) { 1.89 + *aResult = (mPendingSheets == 0); 1.90 + mInLoadResourcesFunc = false; 1.91 + return; 1.92 + } 1.93 + 1.94 + mLoadingResources = true; 1.95 + *aResult = true; 1.96 + 1.97 + // Declare our loaders. 1.98 + nsCOMPtr<nsIDocument> doc = mBinding->XBLDocumentInfo()->GetDocument(); 1.99 + 1.100 + mozilla::css::Loader* cssLoader = doc->CSSLoader(); 1.101 + nsIURI *docURL = doc->GetDocumentURI(); 1.102 + nsIPrincipal* docPrincipal = doc->NodePrincipal(); 1.103 + 1.104 + nsCOMPtr<nsIURI> url; 1.105 + 1.106 + for (nsXBLResource* curr = mResourceList; curr; curr = curr->mNext) { 1.107 + if (curr->mSrc.IsEmpty()) 1.108 + continue; 1.109 + 1.110 + if (NS_FAILED(NS_NewURI(getter_AddRefs(url), curr->mSrc, 1.111 + doc->GetDocumentCharacterSet().get(), docURL))) 1.112 + continue; 1.113 + 1.114 + if (curr->mType == nsGkAtoms::image) { 1.115 + if (!nsContentUtils::CanLoadImage(url, doc, doc, docPrincipal)) { 1.116 + // We're not permitted to load this image, move on... 1.117 + continue; 1.118 + } 1.119 + 1.120 + // Now kick off the image load... 1.121 + // Passing nullptr for pretty much everything -- cause we don't care! 1.122 + // XXX: initialDocumentURI is nullptr! 1.123 + nsRefPtr<imgRequestProxy> req; 1.124 + nsContentUtils::LoadImage(url, doc, docPrincipal, docURL, nullptr, 1.125 + nsIRequest::LOAD_BACKGROUND, EmptyString(), 1.126 + getter_AddRefs(req)); 1.127 + } 1.128 + else if (curr->mType == nsGkAtoms::stylesheet) { 1.129 + // Kick off the load of the stylesheet. 1.130 + 1.131 + // Always load chrome synchronously 1.132 + // XXXbz should that still do a content policy check? 1.133 + bool chrome; 1.134 + nsresult rv; 1.135 + if (NS_SUCCEEDED(url->SchemeIs("chrome", &chrome)) && chrome) 1.136 + { 1.137 + rv = nsContentUtils::GetSecurityManager()-> 1.138 + CheckLoadURIWithPrincipal(docPrincipal, url, 1.139 + nsIScriptSecurityManager::ALLOW_CHROME); 1.140 + if (NS_SUCCEEDED(rv)) { 1.141 + nsRefPtr<nsCSSStyleSheet> sheet; 1.142 + rv = cssLoader->LoadSheetSync(url, getter_AddRefs(sheet)); 1.143 + NS_ASSERTION(NS_SUCCEEDED(rv), "Load failed!!!"); 1.144 + if (NS_SUCCEEDED(rv)) 1.145 + { 1.146 + rv = StyleSheetLoaded(sheet, false, NS_OK); 1.147 + NS_ASSERTION(NS_SUCCEEDED(rv), "Processing the style sheet failed!!!"); 1.148 + } 1.149 + } 1.150 + } 1.151 + else 1.152 + { 1.153 + rv = cssLoader->LoadSheet(url, docPrincipal, EmptyCString(), this); 1.154 + if (NS_SUCCEEDED(rv)) 1.155 + ++mPendingSheets; 1.156 + } 1.157 + } 1.158 + } 1.159 + 1.160 + *aResult = (mPendingSheets == 0); 1.161 + mInLoadResourcesFunc = false; 1.162 + 1.163 + // Destroy our resource list. 1.164 + delete mResourceList; 1.165 + mResourceList = nullptr; 1.166 +} 1.167 + 1.168 +// nsICSSLoaderObserver 1.169 +NS_IMETHODIMP 1.170 +nsXBLResourceLoader::StyleSheetLoaded(nsCSSStyleSheet* aSheet, 1.171 + bool aWasAlternate, 1.172 + nsresult aStatus) 1.173 +{ 1.174 + if (!mResources) { 1.175 + // Our resources got destroyed -- just bail out 1.176 + return NS_OK; 1.177 + } 1.178 + 1.179 + mResources->mStyleSheetList.AppendElement(aSheet); 1.180 + 1.181 + if (!mInLoadResourcesFunc) 1.182 + mPendingSheets--; 1.183 + 1.184 + if (mPendingSheets == 0) { 1.185 + // All stylesheets are loaded. 1.186 + mResources->mRuleProcessor = 1.187 + new nsCSSRuleProcessor(mResources->mStyleSheetList, 1.188 + nsStyleSet::eDocSheet, 1.189 + nullptr); 1.190 + 1.191 + // XXX Check for mPendingScripts when scripts also come online. 1.192 + if (!mInLoadResourcesFunc) 1.193 + NotifyBoundElements(); 1.194 + } 1.195 + return NS_OK; 1.196 +} 1.197 + 1.198 +void 1.199 +nsXBLResourceLoader::AddResource(nsIAtom* aResourceType, const nsAString& aSrc) 1.200 +{ 1.201 + nsXBLResource* res = new nsXBLResource(aResourceType, aSrc); 1.202 + if (!res) 1.203 + return; 1.204 + 1.205 + if (!mResourceList) 1.206 + mResourceList = res; 1.207 + else 1.208 + mLastResource->mNext = res; 1.209 + 1.210 + mLastResource = res; 1.211 +} 1.212 + 1.213 +void 1.214 +nsXBLResourceLoader::AddResourceListener(nsIContent* aBoundElement) 1.215 +{ 1.216 + if (aBoundElement) { 1.217 + mBoundElements.AppendObject(aBoundElement); 1.218 + } 1.219 +} 1.220 + 1.221 +void 1.222 +nsXBLResourceLoader::NotifyBoundElements() 1.223 +{ 1.224 + nsXBLService* xblService = nsXBLService::GetInstance(); 1.225 + if (!xblService) 1.226 + return; 1.227 + 1.228 + nsIURI* bindingURI = mBinding->BindingURI(); 1.229 + 1.230 + uint32_t eltCount = mBoundElements.Count(); 1.231 + for (uint32_t j = 0; j < eltCount; j++) { 1.232 + nsCOMPtr<nsIContent> content = mBoundElements.ObjectAt(j); 1.233 + 1.234 + bool ready = false; 1.235 + xblService->BindingReady(content, bindingURI, &ready); 1.236 + 1.237 + if (ready) { 1.238 + // We need the document to flush out frame construction and 1.239 + // such, so we want to use the current document. 1.240 + nsIDocument* doc = content->GetCurrentDoc(); 1.241 + 1.242 + if (doc) { 1.243 + // Flush first to make sure we can get the frame for content 1.244 + doc->FlushPendingNotifications(Flush_Frames); 1.245 + 1.246 + // If |content| is (in addition to having binding |mBinding|) 1.247 + // also a descendant of another element with binding |mBinding|, 1.248 + // then we might have just constructed it due to the 1.249 + // notification of its parent. (We can know about both if the 1.250 + // binding loads were triggered from the DOM rather than frame 1.251 + // construction.) So we have to check both whether the element 1.252 + // has a primary frame and whether it's in the undisplayed map 1.253 + // before sending a ContentInserted notification, or bad things 1.254 + // will happen. 1.255 + nsIPresShell *shell = doc->GetShell(); 1.256 + if (shell) { 1.257 + nsIFrame* childFrame = content->GetPrimaryFrame(); 1.258 + if (!childFrame) { 1.259 + // Check to see if it's in the undisplayed content map. 1.260 + nsStyleContext* sc = 1.261 + shell->FrameManager()->GetUndisplayedContent(content); 1.262 + 1.263 + if (!sc) { 1.264 + shell->RecreateFramesFor(content); 1.265 + } 1.266 + } 1.267 + } 1.268 + 1.269 + // Flush again 1.270 + // XXXbz why is this needed? 1.271 + doc->FlushPendingNotifications(Flush_ContentAndNotify); 1.272 + } 1.273 + } 1.274 + } 1.275 + 1.276 + // Clear out the whole array. 1.277 + mBoundElements.Clear(); 1.278 + 1.279 + // Delete ourselves. 1.280 + mResources->ClearLoader(); 1.281 +} 1.282 + 1.283 +nsresult 1.284 +nsXBLResourceLoader::Write(nsIObjectOutputStream* aStream) 1.285 +{ 1.286 + nsresult rv; 1.287 + 1.288 + for (nsXBLResource* curr = mResourceList; curr; curr = curr->mNext) { 1.289 + if (curr->mType == nsGkAtoms::image) 1.290 + rv = aStream->Write8(XBLBinding_Serialize_Image); 1.291 + else if (curr->mType == nsGkAtoms::stylesheet) 1.292 + rv = aStream->Write8(XBLBinding_Serialize_Stylesheet); 1.293 + else 1.294 + continue; 1.295 + 1.296 + NS_ENSURE_SUCCESS(rv, rv); 1.297 + 1.298 + rv = aStream->WriteWStringZ(curr->mSrc.get()); 1.299 + NS_ENSURE_SUCCESS(rv, rv); 1.300 + } 1.301 + 1.302 + return NS_OK; 1.303 +}