1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsScriptLoader.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,366 @@ 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 +/* 1.10 + * A class that handles loading and evaluation of <script> elements. 1.11 + */ 1.12 + 1.13 +#ifndef __nsScriptLoader_h__ 1.14 +#define __nsScriptLoader_h__ 1.15 + 1.16 +#include "nsCOMPtr.h" 1.17 +#include "nsIScriptElement.h" 1.18 +#include "nsCOMArray.h" 1.19 +#include "nsTArray.h" 1.20 +#include "nsAutoPtr.h" 1.21 +#include "nsIDocument.h" 1.22 +#include "nsIStreamLoader.h" 1.23 + 1.24 +class nsScriptLoadRequest; 1.25 +class nsIURI; 1.26 + 1.27 +namespace JS { 1.28 + class SourceBufferHolder; 1.29 +} 1.30 + 1.31 +////////////////////////////////////////////////////////////// 1.32 +// Script loader implementation 1.33 +////////////////////////////////////////////////////////////// 1.34 + 1.35 +class nsScriptLoader : public nsIStreamLoaderObserver 1.36 +{ 1.37 + friend class nsScriptRequestProcessor; 1.38 +public: 1.39 + nsScriptLoader(nsIDocument* aDocument); 1.40 + virtual ~nsScriptLoader(); 1.41 + 1.42 + NS_DECL_ISUPPORTS 1.43 + NS_DECL_NSISTREAMLOADEROBSERVER 1.44 + 1.45 + /** 1.46 + * The loader maintains a weak reference to the document with 1.47 + * which it is initialized. This call forces the reference to 1.48 + * be dropped. 1.49 + */ 1.50 + void DropDocumentReference() 1.51 + { 1.52 + mDocument = nullptr; 1.53 + } 1.54 + 1.55 + /** 1.56 + * Add an observer for all scripts loaded through this loader. 1.57 + * 1.58 + * @param aObserver observer for all script processing. 1.59 + */ 1.60 + nsresult AddObserver(nsIScriptLoaderObserver* aObserver) 1.61 + { 1.62 + return mObservers.AppendObject(aObserver) ? NS_OK : 1.63 + NS_ERROR_OUT_OF_MEMORY; 1.64 + } 1.65 + 1.66 + /** 1.67 + * Remove an observer. 1.68 + * 1.69 + * @param aObserver observer to be removed 1.70 + */ 1.71 + void RemoveObserver(nsIScriptLoaderObserver* aObserver) 1.72 + { 1.73 + mObservers.RemoveObject(aObserver); 1.74 + } 1.75 + 1.76 + /** 1.77 + * Process a script element. This will include both loading the 1.78 + * source of the element if it is not inline and evaluating 1.79 + * the script itself. 1.80 + * 1.81 + * If the script is an inline script that can be executed immediately 1.82 + * (i.e. there are no other scripts pending) then ScriptAvailable 1.83 + * and ScriptEvaluated will be called before the function returns. 1.84 + * 1.85 + * If true is returned the script could not be executed immediately. 1.86 + * In this case ScriptAvailable is guaranteed to be called at a later 1.87 + * point (as well as possibly ScriptEvaluated). 1.88 + * 1.89 + * @param aElement The element representing the script to be loaded and 1.90 + * evaluated. 1.91 + */ 1.92 + bool ProcessScriptElement(nsIScriptElement* aElement); 1.93 + 1.94 + /** 1.95 + * Gets the currently executing script. This is useful if you want to 1.96 + * generate a unique key based on the currently executing script. 1.97 + */ 1.98 + nsIScriptElement* GetCurrentScript() 1.99 + { 1.100 + return mCurrentScript; 1.101 + } 1.102 + 1.103 + nsIScriptElement* GetCurrentParserInsertedScript() 1.104 + { 1.105 + return mCurrentParserInsertedScript; 1.106 + } 1.107 + 1.108 + /** 1.109 + * Whether the loader is enabled or not. 1.110 + * When disabled, processing of new script elements is disabled. 1.111 + * Any call to ProcessScriptElement() will return false. Note that 1.112 + * this DOES NOT disable currently loading or executing scripts. 1.113 + */ 1.114 + bool GetEnabled() 1.115 + { 1.116 + return mEnabled; 1.117 + } 1.118 + void SetEnabled(bool aEnabled) 1.119 + { 1.120 + if (!mEnabled && aEnabled) { 1.121 + ProcessPendingRequestsAsync(); 1.122 + } 1.123 + mEnabled = aEnabled; 1.124 + } 1.125 + 1.126 + /** 1.127 + * Add/remove blocker. Blockers will stop scripts from executing, but not 1.128 + * from loading. 1.129 + */ 1.130 + void AddExecuteBlocker() 1.131 + { 1.132 + ++mBlockerCount; 1.133 + } 1.134 + void RemoveExecuteBlocker() 1.135 + { 1.136 + if (!--mBlockerCount) { 1.137 + ProcessPendingRequestsAsync(); 1.138 + } 1.139 + } 1.140 + 1.141 + /** 1.142 + * Convert the given buffer to a UTF-16 string. 1.143 + * @param aChannel Channel corresponding to the data. May be null. 1.144 + * @param aData The data to convert 1.145 + * @param aLength Length of the data 1.146 + * @param aHintCharset Hint for the character set (e.g., from a charset 1.147 + * attribute). May be the empty string. 1.148 + * @param aDocument Document which the data is loaded for. Must not be 1.149 + * null. 1.150 + * @param aBufOut [out] jschar array allocated by ConvertToUTF16 and 1.151 + * containing data converted to unicode. Caller must 1.152 + * js_free() this data when no longer needed. 1.153 + * @param aLengthOut [out] Length of array returned in aBufOut in number 1.154 + * of jschars. 1.155 + */ 1.156 + static nsresult ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, 1.157 + uint32_t aLength, 1.158 + const nsAString& aHintCharset, 1.159 + nsIDocument* aDocument, 1.160 + jschar*& aBufOut, size_t& aLengthOut); 1.161 + 1.162 + /** 1.163 + * Processes any pending requests that are ready for processing. 1.164 + */ 1.165 + void ProcessPendingRequests(); 1.166 + 1.167 + /** 1.168 + * Check whether it's OK to load a script from aURI in 1.169 + * aDocument. 1.170 + */ 1.171 + static nsresult ShouldLoadScript(nsIDocument* aDocument, 1.172 + nsISupports* aContext, 1.173 + nsIURI* aURI, 1.174 + const nsAString &aType); 1.175 + 1.176 + /** 1.177 + * Starts deferring deferred scripts and puts them in the mDeferredRequests 1.178 + * queue instead. 1.179 + */ 1.180 + void BeginDeferringScripts() 1.181 + { 1.182 + mDeferEnabled = true; 1.183 + if (mDocument) { 1.184 + mDocument->BlockOnload(); 1.185 + } 1.186 + } 1.187 + 1.188 + /** 1.189 + * Notifies the script loader that parsing is done. If aTerminated is true, 1.190 + * this will drop any pending scripts that haven't run yet. Otherwise, it 1.191 + * will stops deferring scripts and immediately processes the 1.192 + * mDeferredRequests queue. 1.193 + * 1.194 + * WARNING: This function will synchronously execute content scripts, so be 1.195 + * prepared that the world might change around you. 1.196 + */ 1.197 + void ParsingComplete(bool aTerminated); 1.198 + 1.199 + /** 1.200 + * Returns the number of pending scripts, deferred or not. 1.201 + */ 1.202 + uint32_t HasPendingOrCurrentScripts() 1.203 + { 1.204 + return mCurrentScript || mParserBlockingRequest; 1.205 + } 1.206 + 1.207 + /** 1.208 + * Adds aURI to the preload list and starts loading it. 1.209 + * 1.210 + * @param aURI The URI of the external script. 1.211 + * @param aCharset The charset parameter for the script. 1.212 + * @param aType The type parameter for the script. 1.213 + * @param aCrossOrigin The crossorigin attribute for the script. 1.214 + * Void if not present. 1.215 + * @param aScriptFromHead Whether or not the script was a child of head 1.216 + */ 1.217 + virtual void PreloadURI(nsIURI *aURI, const nsAString &aCharset, 1.218 + const nsAString &aType, 1.219 + const nsAString &aCrossOrigin, 1.220 + bool aScriptFromHead); 1.221 + 1.222 + /** 1.223 + * Process a request that was deferred so that the script could be compiled 1.224 + * off thread. 1.225 + */ 1.226 + nsresult ProcessOffThreadRequest(nsScriptLoadRequest *aRequest, 1.227 + void **aOffThreadToken); 1.228 + 1.229 +private: 1.230 + /** 1.231 + * Unblocks the creator parser of the parser-blocking scripts. 1.232 + */ 1.233 + void UnblockParser(nsScriptLoadRequest* aParserBlockingRequest); 1.234 + 1.235 + /** 1.236 + * Asynchronously resumes the creator parser of the parser-blocking scripts. 1.237 + */ 1.238 + void ContinueParserAsync(nsScriptLoadRequest* aParserBlockingRequest); 1.239 + 1.240 + 1.241 + /** 1.242 + * Helper function to check the content policy for a given request. 1.243 + */ 1.244 + static nsresult CheckContentPolicy(nsIDocument* aDocument, 1.245 + nsISupports *aContext, 1.246 + nsIURI *aURI, 1.247 + const nsAString &aType); 1.248 + 1.249 + /** 1.250 + * Start a load for aRequest's URI. 1.251 + */ 1.252 + nsresult StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, 1.253 + bool aScriptFromHead); 1.254 + 1.255 + /** 1.256 + * Process any pending requests asynchronously (i.e. off an event) if there 1.257 + * are any. Note that this is a no-op if there aren't any currently pending 1.258 + * requests. 1.259 + * 1.260 + * This function is virtual to allow cross-library calls to SetEnabled() 1.261 + */ 1.262 + virtual void ProcessPendingRequestsAsync(); 1.263 + 1.264 + /** 1.265 + * If true, the loader is ready to execute scripts, and so are all its 1.266 + * ancestors. If the loader itself is ready but some ancestor is not, this 1.267 + * function will add an execute blocker and ask the ancestor to remove it 1.268 + * once it becomes ready. 1.269 + */ 1.270 + bool ReadyToExecuteScripts(); 1.271 + 1.272 + /** 1.273 + * Return whether just this loader is ready to execute scripts. 1.274 + */ 1.275 + bool SelfReadyToExecuteScripts() 1.276 + { 1.277 + return mEnabled && !mBlockerCount; 1.278 + } 1.279 + 1.280 + bool AddPendingChildLoader(nsScriptLoader* aChild) { 1.281 + return mPendingChildLoaders.AppendElement(aChild) != nullptr; 1.282 + } 1.283 + 1.284 + nsresult AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest); 1.285 + nsresult ProcessRequest(nsScriptLoadRequest* aRequest, 1.286 + void **aOffThreadToken = nullptr); 1.287 + void FireScriptAvailable(nsresult aResult, 1.288 + nsScriptLoadRequest* aRequest); 1.289 + void FireScriptEvaluated(nsresult aResult, 1.290 + nsScriptLoadRequest* aRequest); 1.291 + nsresult EvaluateScript(nsScriptLoadRequest* aRequest, 1.292 + JS::SourceBufferHolder& aSrcBuf, 1.293 + void **aOffThreadToken); 1.294 + 1.295 + already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject(); 1.296 + void FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest, 1.297 + JS::Handle<JSObject *> aScopeChain, 1.298 + JS::CompileOptions *aOptions); 1.299 + 1.300 + nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest, 1.301 + nsIStreamLoader* aLoader, 1.302 + nsresult aStatus, 1.303 + uint32_t aStringLen, 1.304 + const uint8_t* aString); 1.305 + 1.306 + void AddDeferRequest(nsScriptLoadRequest* aRequest); 1.307 + bool MaybeRemovedDeferRequests(); 1.308 + 1.309 + nsIDocument* mDocument; // [WEAK] 1.310 + nsCOMArray<nsIScriptLoaderObserver> mObservers; 1.311 + nsTArray<nsRefPtr<nsScriptLoadRequest> > mNonAsyncExternalScriptInsertedRequests; 1.312 + nsTArray<nsRefPtr<nsScriptLoadRequest> > mAsyncRequests; 1.313 + nsTArray<nsRefPtr<nsScriptLoadRequest> > mDeferRequests; 1.314 + nsTArray<nsRefPtr<nsScriptLoadRequest> > mXSLTRequests; 1.315 + nsRefPtr<nsScriptLoadRequest> mParserBlockingRequest; 1.316 + 1.317 + // In mRequests, the additional information here is stored by the element. 1.318 + struct PreloadInfo { 1.319 + nsRefPtr<nsScriptLoadRequest> mRequest; 1.320 + nsString mCharset; 1.321 + }; 1.322 + 1.323 + struct PreloadRequestComparator { 1.324 + bool Equals(const PreloadInfo &aPi, nsScriptLoadRequest * const &aRequest) 1.325 + const 1.326 + { 1.327 + return aRequest == aPi.mRequest; 1.328 + } 1.329 + }; 1.330 + struct PreloadURIComparator { 1.331 + bool Equals(const PreloadInfo &aPi, nsIURI * const &aURI) const; 1.332 + }; 1.333 + nsTArray<PreloadInfo> mPreloads; 1.334 + 1.335 + nsCOMPtr<nsIScriptElement> mCurrentScript; 1.336 + nsCOMPtr<nsIScriptElement> mCurrentParserInsertedScript; 1.337 + // XXXbz do we want to cycle-collect these or something? Not sure. 1.338 + nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders; 1.339 + uint32_t mBlockerCount; 1.340 + bool mEnabled; 1.341 + bool mDeferEnabled; 1.342 + bool mDocumentParsingDone; 1.343 + bool mBlockingDOMContentLoaded; 1.344 +}; 1.345 + 1.346 +class nsAutoScriptLoaderDisabler 1.347 +{ 1.348 +public: 1.349 + nsAutoScriptLoaderDisabler(nsIDocument* aDoc) 1.350 + { 1.351 + mLoader = aDoc->ScriptLoader(); 1.352 + mWasEnabled = mLoader->GetEnabled(); 1.353 + if (mWasEnabled) { 1.354 + mLoader->SetEnabled(false); 1.355 + } 1.356 + } 1.357 + 1.358 + ~nsAutoScriptLoaderDisabler() 1.359 + { 1.360 + if (mWasEnabled) { 1.361 + mLoader->SetEnabled(true); 1.362 + } 1.363 + } 1.364 + 1.365 + bool mWasEnabled; 1.366 + nsRefPtr<nsScriptLoader> mLoader; 1.367 +}; 1.368 + 1.369 +#endif //__nsScriptLoader_h__