Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | /* |
michael@0 | 7 | * A class that handles loading and evaluation of <script> elements. |
michael@0 | 8 | */ |
michael@0 | 9 | |
michael@0 | 10 | #ifndef __nsScriptLoader_h__ |
michael@0 | 11 | #define __nsScriptLoader_h__ |
michael@0 | 12 | |
michael@0 | 13 | #include "nsCOMPtr.h" |
michael@0 | 14 | #include "nsIScriptElement.h" |
michael@0 | 15 | #include "nsCOMArray.h" |
michael@0 | 16 | #include "nsTArray.h" |
michael@0 | 17 | #include "nsAutoPtr.h" |
michael@0 | 18 | #include "nsIDocument.h" |
michael@0 | 19 | #include "nsIStreamLoader.h" |
michael@0 | 20 | |
michael@0 | 21 | class nsScriptLoadRequest; |
michael@0 | 22 | class nsIURI; |
michael@0 | 23 | |
michael@0 | 24 | namespace JS { |
michael@0 | 25 | class SourceBufferHolder; |
michael@0 | 26 | } |
michael@0 | 27 | |
michael@0 | 28 | ////////////////////////////////////////////////////////////// |
michael@0 | 29 | // Script loader implementation |
michael@0 | 30 | ////////////////////////////////////////////////////////////// |
michael@0 | 31 | |
michael@0 | 32 | class nsScriptLoader : public nsIStreamLoaderObserver |
michael@0 | 33 | { |
michael@0 | 34 | friend class nsScriptRequestProcessor; |
michael@0 | 35 | public: |
michael@0 | 36 | nsScriptLoader(nsIDocument* aDocument); |
michael@0 | 37 | virtual ~nsScriptLoader(); |
michael@0 | 38 | |
michael@0 | 39 | NS_DECL_ISUPPORTS |
michael@0 | 40 | NS_DECL_NSISTREAMLOADEROBSERVER |
michael@0 | 41 | |
michael@0 | 42 | /** |
michael@0 | 43 | * The loader maintains a weak reference to the document with |
michael@0 | 44 | * which it is initialized. This call forces the reference to |
michael@0 | 45 | * be dropped. |
michael@0 | 46 | */ |
michael@0 | 47 | void DropDocumentReference() |
michael@0 | 48 | { |
michael@0 | 49 | mDocument = nullptr; |
michael@0 | 50 | } |
michael@0 | 51 | |
michael@0 | 52 | /** |
michael@0 | 53 | * Add an observer for all scripts loaded through this loader. |
michael@0 | 54 | * |
michael@0 | 55 | * @param aObserver observer for all script processing. |
michael@0 | 56 | */ |
michael@0 | 57 | nsresult AddObserver(nsIScriptLoaderObserver* aObserver) |
michael@0 | 58 | { |
michael@0 | 59 | return mObservers.AppendObject(aObserver) ? NS_OK : |
michael@0 | 60 | NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 61 | } |
michael@0 | 62 | |
michael@0 | 63 | /** |
michael@0 | 64 | * Remove an observer. |
michael@0 | 65 | * |
michael@0 | 66 | * @param aObserver observer to be removed |
michael@0 | 67 | */ |
michael@0 | 68 | void RemoveObserver(nsIScriptLoaderObserver* aObserver) |
michael@0 | 69 | { |
michael@0 | 70 | mObservers.RemoveObject(aObserver); |
michael@0 | 71 | } |
michael@0 | 72 | |
michael@0 | 73 | /** |
michael@0 | 74 | * Process a script element. This will include both loading the |
michael@0 | 75 | * source of the element if it is not inline and evaluating |
michael@0 | 76 | * the script itself. |
michael@0 | 77 | * |
michael@0 | 78 | * If the script is an inline script that can be executed immediately |
michael@0 | 79 | * (i.e. there are no other scripts pending) then ScriptAvailable |
michael@0 | 80 | * and ScriptEvaluated will be called before the function returns. |
michael@0 | 81 | * |
michael@0 | 82 | * If true is returned the script could not be executed immediately. |
michael@0 | 83 | * In this case ScriptAvailable is guaranteed to be called at a later |
michael@0 | 84 | * point (as well as possibly ScriptEvaluated). |
michael@0 | 85 | * |
michael@0 | 86 | * @param aElement The element representing the script to be loaded and |
michael@0 | 87 | * evaluated. |
michael@0 | 88 | */ |
michael@0 | 89 | bool ProcessScriptElement(nsIScriptElement* aElement); |
michael@0 | 90 | |
michael@0 | 91 | /** |
michael@0 | 92 | * Gets the currently executing script. This is useful if you want to |
michael@0 | 93 | * generate a unique key based on the currently executing script. |
michael@0 | 94 | */ |
michael@0 | 95 | nsIScriptElement* GetCurrentScript() |
michael@0 | 96 | { |
michael@0 | 97 | return mCurrentScript; |
michael@0 | 98 | } |
michael@0 | 99 | |
michael@0 | 100 | nsIScriptElement* GetCurrentParserInsertedScript() |
michael@0 | 101 | { |
michael@0 | 102 | return mCurrentParserInsertedScript; |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | /** |
michael@0 | 106 | * Whether the loader is enabled or not. |
michael@0 | 107 | * When disabled, processing of new script elements is disabled. |
michael@0 | 108 | * Any call to ProcessScriptElement() will return false. Note that |
michael@0 | 109 | * this DOES NOT disable currently loading or executing scripts. |
michael@0 | 110 | */ |
michael@0 | 111 | bool GetEnabled() |
michael@0 | 112 | { |
michael@0 | 113 | return mEnabled; |
michael@0 | 114 | } |
michael@0 | 115 | void SetEnabled(bool aEnabled) |
michael@0 | 116 | { |
michael@0 | 117 | if (!mEnabled && aEnabled) { |
michael@0 | 118 | ProcessPendingRequestsAsync(); |
michael@0 | 119 | } |
michael@0 | 120 | mEnabled = aEnabled; |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | /** |
michael@0 | 124 | * Add/remove blocker. Blockers will stop scripts from executing, but not |
michael@0 | 125 | * from loading. |
michael@0 | 126 | */ |
michael@0 | 127 | void AddExecuteBlocker() |
michael@0 | 128 | { |
michael@0 | 129 | ++mBlockerCount; |
michael@0 | 130 | } |
michael@0 | 131 | void RemoveExecuteBlocker() |
michael@0 | 132 | { |
michael@0 | 133 | if (!--mBlockerCount) { |
michael@0 | 134 | ProcessPendingRequestsAsync(); |
michael@0 | 135 | } |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | /** |
michael@0 | 139 | * Convert the given buffer to a UTF-16 string. |
michael@0 | 140 | * @param aChannel Channel corresponding to the data. May be null. |
michael@0 | 141 | * @param aData The data to convert |
michael@0 | 142 | * @param aLength Length of the data |
michael@0 | 143 | * @param aHintCharset Hint for the character set (e.g., from a charset |
michael@0 | 144 | * attribute). May be the empty string. |
michael@0 | 145 | * @param aDocument Document which the data is loaded for. Must not be |
michael@0 | 146 | * null. |
michael@0 | 147 | * @param aBufOut [out] jschar array allocated by ConvertToUTF16 and |
michael@0 | 148 | * containing data converted to unicode. Caller must |
michael@0 | 149 | * js_free() this data when no longer needed. |
michael@0 | 150 | * @param aLengthOut [out] Length of array returned in aBufOut in number |
michael@0 | 151 | * of jschars. |
michael@0 | 152 | */ |
michael@0 | 153 | static nsresult ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, |
michael@0 | 154 | uint32_t aLength, |
michael@0 | 155 | const nsAString& aHintCharset, |
michael@0 | 156 | nsIDocument* aDocument, |
michael@0 | 157 | jschar*& aBufOut, size_t& aLengthOut); |
michael@0 | 158 | |
michael@0 | 159 | /** |
michael@0 | 160 | * Processes any pending requests that are ready for processing. |
michael@0 | 161 | */ |
michael@0 | 162 | void ProcessPendingRequests(); |
michael@0 | 163 | |
michael@0 | 164 | /** |
michael@0 | 165 | * Check whether it's OK to load a script from aURI in |
michael@0 | 166 | * aDocument. |
michael@0 | 167 | */ |
michael@0 | 168 | static nsresult ShouldLoadScript(nsIDocument* aDocument, |
michael@0 | 169 | nsISupports* aContext, |
michael@0 | 170 | nsIURI* aURI, |
michael@0 | 171 | const nsAString &aType); |
michael@0 | 172 | |
michael@0 | 173 | /** |
michael@0 | 174 | * Starts deferring deferred scripts and puts them in the mDeferredRequests |
michael@0 | 175 | * queue instead. |
michael@0 | 176 | */ |
michael@0 | 177 | void BeginDeferringScripts() |
michael@0 | 178 | { |
michael@0 | 179 | mDeferEnabled = true; |
michael@0 | 180 | if (mDocument) { |
michael@0 | 181 | mDocument->BlockOnload(); |
michael@0 | 182 | } |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | /** |
michael@0 | 186 | * Notifies the script loader that parsing is done. If aTerminated is true, |
michael@0 | 187 | * this will drop any pending scripts that haven't run yet. Otherwise, it |
michael@0 | 188 | * will stops deferring scripts and immediately processes the |
michael@0 | 189 | * mDeferredRequests queue. |
michael@0 | 190 | * |
michael@0 | 191 | * WARNING: This function will synchronously execute content scripts, so be |
michael@0 | 192 | * prepared that the world might change around you. |
michael@0 | 193 | */ |
michael@0 | 194 | void ParsingComplete(bool aTerminated); |
michael@0 | 195 | |
michael@0 | 196 | /** |
michael@0 | 197 | * Returns the number of pending scripts, deferred or not. |
michael@0 | 198 | */ |
michael@0 | 199 | uint32_t HasPendingOrCurrentScripts() |
michael@0 | 200 | { |
michael@0 | 201 | return mCurrentScript || mParserBlockingRequest; |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | /** |
michael@0 | 205 | * Adds aURI to the preload list and starts loading it. |
michael@0 | 206 | * |
michael@0 | 207 | * @param aURI The URI of the external script. |
michael@0 | 208 | * @param aCharset The charset parameter for the script. |
michael@0 | 209 | * @param aType The type parameter for the script. |
michael@0 | 210 | * @param aCrossOrigin The crossorigin attribute for the script. |
michael@0 | 211 | * Void if not present. |
michael@0 | 212 | * @param aScriptFromHead Whether or not the script was a child of head |
michael@0 | 213 | */ |
michael@0 | 214 | virtual void PreloadURI(nsIURI *aURI, const nsAString &aCharset, |
michael@0 | 215 | const nsAString &aType, |
michael@0 | 216 | const nsAString &aCrossOrigin, |
michael@0 | 217 | bool aScriptFromHead); |
michael@0 | 218 | |
michael@0 | 219 | /** |
michael@0 | 220 | * Process a request that was deferred so that the script could be compiled |
michael@0 | 221 | * off thread. |
michael@0 | 222 | */ |
michael@0 | 223 | nsresult ProcessOffThreadRequest(nsScriptLoadRequest *aRequest, |
michael@0 | 224 | void **aOffThreadToken); |
michael@0 | 225 | |
michael@0 | 226 | private: |
michael@0 | 227 | /** |
michael@0 | 228 | * Unblocks the creator parser of the parser-blocking scripts. |
michael@0 | 229 | */ |
michael@0 | 230 | void UnblockParser(nsScriptLoadRequest* aParserBlockingRequest); |
michael@0 | 231 | |
michael@0 | 232 | /** |
michael@0 | 233 | * Asynchronously resumes the creator parser of the parser-blocking scripts. |
michael@0 | 234 | */ |
michael@0 | 235 | void ContinueParserAsync(nsScriptLoadRequest* aParserBlockingRequest); |
michael@0 | 236 | |
michael@0 | 237 | |
michael@0 | 238 | /** |
michael@0 | 239 | * Helper function to check the content policy for a given request. |
michael@0 | 240 | */ |
michael@0 | 241 | static nsresult CheckContentPolicy(nsIDocument* aDocument, |
michael@0 | 242 | nsISupports *aContext, |
michael@0 | 243 | nsIURI *aURI, |
michael@0 | 244 | const nsAString &aType); |
michael@0 | 245 | |
michael@0 | 246 | /** |
michael@0 | 247 | * Start a load for aRequest's URI. |
michael@0 | 248 | */ |
michael@0 | 249 | nsresult StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, |
michael@0 | 250 | bool aScriptFromHead); |
michael@0 | 251 | |
michael@0 | 252 | /** |
michael@0 | 253 | * Process any pending requests asynchronously (i.e. off an event) if there |
michael@0 | 254 | * are any. Note that this is a no-op if there aren't any currently pending |
michael@0 | 255 | * requests. |
michael@0 | 256 | * |
michael@0 | 257 | * This function is virtual to allow cross-library calls to SetEnabled() |
michael@0 | 258 | */ |
michael@0 | 259 | virtual void ProcessPendingRequestsAsync(); |
michael@0 | 260 | |
michael@0 | 261 | /** |
michael@0 | 262 | * If true, the loader is ready to execute scripts, and so are all its |
michael@0 | 263 | * ancestors. If the loader itself is ready but some ancestor is not, this |
michael@0 | 264 | * function will add an execute blocker and ask the ancestor to remove it |
michael@0 | 265 | * once it becomes ready. |
michael@0 | 266 | */ |
michael@0 | 267 | bool ReadyToExecuteScripts(); |
michael@0 | 268 | |
michael@0 | 269 | /** |
michael@0 | 270 | * Return whether just this loader is ready to execute scripts. |
michael@0 | 271 | */ |
michael@0 | 272 | bool SelfReadyToExecuteScripts() |
michael@0 | 273 | { |
michael@0 | 274 | return mEnabled && !mBlockerCount; |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | bool AddPendingChildLoader(nsScriptLoader* aChild) { |
michael@0 | 278 | return mPendingChildLoaders.AppendElement(aChild) != nullptr; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | nsresult AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest); |
michael@0 | 282 | nsresult ProcessRequest(nsScriptLoadRequest* aRequest, |
michael@0 | 283 | void **aOffThreadToken = nullptr); |
michael@0 | 284 | void FireScriptAvailable(nsresult aResult, |
michael@0 | 285 | nsScriptLoadRequest* aRequest); |
michael@0 | 286 | void FireScriptEvaluated(nsresult aResult, |
michael@0 | 287 | nsScriptLoadRequest* aRequest); |
michael@0 | 288 | nsresult EvaluateScript(nsScriptLoadRequest* aRequest, |
michael@0 | 289 | JS::SourceBufferHolder& aSrcBuf, |
michael@0 | 290 | void **aOffThreadToken); |
michael@0 | 291 | |
michael@0 | 292 | already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject(); |
michael@0 | 293 | void FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest, |
michael@0 | 294 | JS::Handle<JSObject *> aScopeChain, |
michael@0 | 295 | JS::CompileOptions *aOptions); |
michael@0 | 296 | |
michael@0 | 297 | nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest, |
michael@0 | 298 | nsIStreamLoader* aLoader, |
michael@0 | 299 | nsresult aStatus, |
michael@0 | 300 | uint32_t aStringLen, |
michael@0 | 301 | const uint8_t* aString); |
michael@0 | 302 | |
michael@0 | 303 | void AddDeferRequest(nsScriptLoadRequest* aRequest); |
michael@0 | 304 | bool MaybeRemovedDeferRequests(); |
michael@0 | 305 | |
michael@0 | 306 | nsIDocument* mDocument; // [WEAK] |
michael@0 | 307 | nsCOMArray<nsIScriptLoaderObserver> mObservers; |
michael@0 | 308 | nsTArray<nsRefPtr<nsScriptLoadRequest> > mNonAsyncExternalScriptInsertedRequests; |
michael@0 | 309 | nsTArray<nsRefPtr<nsScriptLoadRequest> > mAsyncRequests; |
michael@0 | 310 | nsTArray<nsRefPtr<nsScriptLoadRequest> > mDeferRequests; |
michael@0 | 311 | nsTArray<nsRefPtr<nsScriptLoadRequest> > mXSLTRequests; |
michael@0 | 312 | nsRefPtr<nsScriptLoadRequest> mParserBlockingRequest; |
michael@0 | 313 | |
michael@0 | 314 | // In mRequests, the additional information here is stored by the element. |
michael@0 | 315 | struct PreloadInfo { |
michael@0 | 316 | nsRefPtr<nsScriptLoadRequest> mRequest; |
michael@0 | 317 | nsString mCharset; |
michael@0 | 318 | }; |
michael@0 | 319 | |
michael@0 | 320 | struct PreloadRequestComparator { |
michael@0 | 321 | bool Equals(const PreloadInfo &aPi, nsScriptLoadRequest * const &aRequest) |
michael@0 | 322 | const |
michael@0 | 323 | { |
michael@0 | 324 | return aRequest == aPi.mRequest; |
michael@0 | 325 | } |
michael@0 | 326 | }; |
michael@0 | 327 | struct PreloadURIComparator { |
michael@0 | 328 | bool Equals(const PreloadInfo &aPi, nsIURI * const &aURI) const; |
michael@0 | 329 | }; |
michael@0 | 330 | nsTArray<PreloadInfo> mPreloads; |
michael@0 | 331 | |
michael@0 | 332 | nsCOMPtr<nsIScriptElement> mCurrentScript; |
michael@0 | 333 | nsCOMPtr<nsIScriptElement> mCurrentParserInsertedScript; |
michael@0 | 334 | // XXXbz do we want to cycle-collect these or something? Not sure. |
michael@0 | 335 | nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders; |
michael@0 | 336 | uint32_t mBlockerCount; |
michael@0 | 337 | bool mEnabled; |
michael@0 | 338 | bool mDeferEnabled; |
michael@0 | 339 | bool mDocumentParsingDone; |
michael@0 | 340 | bool mBlockingDOMContentLoaded; |
michael@0 | 341 | }; |
michael@0 | 342 | |
michael@0 | 343 | class nsAutoScriptLoaderDisabler |
michael@0 | 344 | { |
michael@0 | 345 | public: |
michael@0 | 346 | nsAutoScriptLoaderDisabler(nsIDocument* aDoc) |
michael@0 | 347 | { |
michael@0 | 348 | mLoader = aDoc->ScriptLoader(); |
michael@0 | 349 | mWasEnabled = mLoader->GetEnabled(); |
michael@0 | 350 | if (mWasEnabled) { |
michael@0 | 351 | mLoader->SetEnabled(false); |
michael@0 | 352 | } |
michael@0 | 353 | } |
michael@0 | 354 | |
michael@0 | 355 | ~nsAutoScriptLoaderDisabler() |
michael@0 | 356 | { |
michael@0 | 357 | if (mWasEnabled) { |
michael@0 | 358 | mLoader->SetEnabled(true); |
michael@0 | 359 | } |
michael@0 | 360 | } |
michael@0 | 361 | |
michael@0 | 362 | bool mWasEnabled; |
michael@0 | 363 | nsRefPtr<nsScriptLoader> mLoader; |
michael@0 | 364 | }; |
michael@0 | 365 | |
michael@0 | 366 | #endif //__nsScriptLoader_h__ |