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