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