1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
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 : #include "nsXBLDocumentInfo.h"
40 : #include "nsHashtable.h"
41 : #include "nsIDocument.h"
42 : #include "nsXBLPrototypeBinding.h"
43 : #include "nsIScriptObjectPrincipal.h"
44 : #include "nsIScriptGlobalObject.h"
45 : #include "nsIScriptContext.h"
46 : #include "nsIScriptRuntime.h"
47 : #include "nsIDOMScriptObjectFactory.h"
48 : #include "jsapi.h"
49 : #include "nsIURI.h"
50 : #include "nsIConsoleService.h"
51 : #include "nsIScriptError.h"
52 : #include "nsIChromeRegistry.h"
53 : #include "nsIPrincipal.h"
54 : #include "nsIScriptSecurityManager.h"
55 : #include "nsContentUtils.h"
56 : #include "nsDOMJSUtils.h"
57 : #include "mozilla/Services.h"
58 : #include "xpcpublic.h"
59 : #include "mozilla/scache/StartupCache.h"
60 : #include "mozilla/scache/StartupCacheUtils.h"
61 : #include "nsCCUncollectableMarker.h"
62 :
63 : using namespace mozilla::scache;
64 :
65 : static const char kXBLCachePrefix[] = "xblcache";
66 :
67 : static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
68 :
69 : // An XBLDocumentInfo object has a special context associated with it which we can use to pre-compile
70 : // properties and methods of XBL bindings against.
71 : class nsXBLDocGlobalObject : public nsIScriptGlobalObject,
72 : public nsIScriptObjectPrincipal
73 : {
74 : public:
75 : nsXBLDocGlobalObject(nsIScriptGlobalObjectOwner *aGlobalObjectOwner);
76 :
77 : // nsISupports interface
78 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
79 :
80 : // nsIScriptGlobalObject methods
81 : virtual nsresult EnsureScriptEnvironment(PRUint32 aLangID);
82 : virtual nsresult SetScriptContext(PRUint32 lang_id, nsIScriptContext *aContext);
83 :
84 : virtual nsIScriptContext *GetContext();
85 : virtual JSObject *GetGlobalJSObject();
86 : virtual void OnFinalize(JSObject* aObject);
87 : virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts);
88 :
89 : // nsIScriptObjectPrincipal methods
90 : virtual nsIPrincipal* GetPrincipal();
91 :
92 : static JSBool doCheckAccess(JSContext *cx, JSObject *obj, jsid id,
93 : PRUint32 accessType);
94 :
95 1464 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXBLDocGlobalObject,
96 : nsIScriptGlobalObject)
97 :
98 : void ClearGlobalObjectOwner();
99 :
100 : protected:
101 : virtual ~nsXBLDocGlobalObject();
102 :
103 : void SetContext(nsIScriptContext *aContext);
104 : nsIScriptContext *GetScriptContext(PRUint32 language);
105 :
106 : nsCOMPtr<nsIScriptContext> mScriptContext;
107 : JSObject *mJSObject; // XXX JS language rabies bigotry badness
108 :
109 : nsIScriptGlobalObjectOwner* mGlobalObjectOwner; // weak reference
110 : static JSClass gSharedGlobalClass;
111 : };
112 :
113 : JSBool
114 0 : nsXBLDocGlobalObject::doCheckAccess(JSContext *cx, JSObject *obj, jsid id, PRUint32 accessType)
115 : {
116 0 : nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
117 0 : if (!ssm) {
118 0 : ::JS_ReportError(cx, "Unable to verify access to a global object property.");
119 0 : return JS_FALSE;
120 : }
121 :
122 : // Make sure to actually operate on our object, and not some object further
123 : // down on the proto chain.
124 0 : while (JS_GetClass(obj) != &nsXBLDocGlobalObject::gSharedGlobalClass) {
125 0 : obj = ::JS_GetPrototype(obj);
126 0 : if (!obj) {
127 0 : ::JS_ReportError(cx, "Invalid access to a global object property.");
128 0 : return JS_FALSE;
129 : }
130 : }
131 :
132 0 : nsresult rv = ssm->CheckPropertyAccess(cx, obj, JS_GetClass(obj)->name,
133 0 : id, accessType);
134 0 : return NS_SUCCEEDED(rv);
135 : }
136 :
137 : static JSBool
138 0 : nsXBLDocGlobalObject_getProperty(JSContext *cx, JSObject *obj,
139 : jsid id, jsval *vp)
140 : {
141 : return nsXBLDocGlobalObject::
142 0 : doCheckAccess(cx, obj, id, nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
143 : }
144 :
145 : static JSBool
146 0 : nsXBLDocGlobalObject_setProperty(JSContext *cx, JSObject *obj,
147 : jsid id, JSBool strict, jsval *vp)
148 : {
149 : return nsXBLDocGlobalObject::
150 0 : doCheckAccess(cx, obj, id, nsIXPCSecurityManager::ACCESS_SET_PROPERTY);
151 : }
152 :
153 : static JSBool
154 0 : nsXBLDocGlobalObject_checkAccess(JSContext *cx, JSObject *obj, jsid id,
155 : JSAccessMode mode, jsval *vp)
156 : {
157 : PRUint32 translated;
158 0 : if (mode & JSACC_WRITE) {
159 0 : translated = nsIXPCSecurityManager::ACCESS_SET_PROPERTY;
160 : } else {
161 0 : translated = nsIXPCSecurityManager::ACCESS_GET_PROPERTY;
162 : }
163 :
164 : return nsXBLDocGlobalObject::
165 0 : doCheckAccess(cx, obj, id, translated);
166 : }
167 :
168 : static void
169 0 : nsXBLDocGlobalObject_finalize(JSContext *cx, JSObject *obj)
170 : {
171 0 : nsISupports *nativeThis = (nsISupports*)JS_GetPrivate(obj);
172 :
173 0 : nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(nativeThis));
174 :
175 0 : if (sgo)
176 0 : sgo->OnFinalize(obj);
177 :
178 : // The addref was part of JSObject construction
179 0 : NS_RELEASE(nativeThis);
180 0 : }
181 :
182 : static JSBool
183 0 : nsXBLDocGlobalObject_resolve(JSContext *cx, JSObject *obj, jsid id)
184 : {
185 0 : JSBool did_resolve = JS_FALSE;
186 0 : return JS_ResolveStandardClass(cx, obj, id, &did_resolve);
187 : }
188 :
189 :
190 : JSClass nsXBLDocGlobalObject::gSharedGlobalClass = {
191 : "nsXBLPrototypeScript compilation scope",
192 : XPCONNECT_GLOBAL_FLAGS,
193 : JS_PropertyStub, JS_PropertyStub,
194 : nsXBLDocGlobalObject_getProperty, nsXBLDocGlobalObject_setProperty,
195 : JS_EnumerateStub, nsXBLDocGlobalObject_resolve,
196 : JS_ConvertStub, nsXBLDocGlobalObject_finalize,
197 : nsXBLDocGlobalObject_checkAccess, NULL, NULL, NULL,
198 : TraceXPCGlobal
199 : };
200 :
201 : //----------------------------------------------------------------------
202 : //
203 : // nsXBLDocGlobalObject
204 : //
205 :
206 0 : nsXBLDocGlobalObject::nsXBLDocGlobalObject(nsIScriptGlobalObjectOwner *aGlobalObjectOwner)
207 : : mJSObject(nsnull),
208 0 : mGlobalObjectOwner(aGlobalObjectOwner) // weak reference
209 : {
210 0 : }
211 :
212 :
213 0 : nsXBLDocGlobalObject::~nsXBLDocGlobalObject()
214 0 : {}
215 :
216 :
217 1464 : NS_IMPL_CYCLE_COLLECTION_1(nsXBLDocGlobalObject, mScriptContext)
218 :
219 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLDocGlobalObject)
220 0 : NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
221 0 : NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
222 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptGlobalObject)
223 0 : NS_INTERFACE_MAP_END
224 :
225 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLDocGlobalObject)
226 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLDocGlobalObject)
227 :
228 : void
229 0 : XBL_ProtoErrorReporter(JSContext *cx,
230 : const char *message,
231 : JSErrorReport *report)
232 : {
233 : // Make an nsIScriptError and populate it with information from
234 : // this error.
235 : nsCOMPtr<nsIScriptError>
236 0 : errorObject(do_CreateInstance("@mozilla.org/scripterror;1"));
237 : nsCOMPtr<nsIConsoleService>
238 0 : consoleService(do_GetService("@mozilla.org/consoleservice;1"));
239 :
240 0 : if (errorObject && consoleService) {
241 0 : PRUint32 column = report->uctokenptr - report->uclinebuf;
242 :
243 0 : errorObject->Init
244 : (reinterpret_cast<const PRUnichar*>(report->ucmessage),
245 0 : NS_ConvertUTF8toUTF16(report->filename).get(),
246 : reinterpret_cast<const PRUnichar*>(report->uclinebuf),
247 : report->lineno, column, report->flags,
248 : "xbl javascript"
249 0 : );
250 0 : consoleService->LogMessage(errorObject);
251 : }
252 0 : }
253 :
254 : //----------------------------------------------------------------------
255 : //
256 : // nsIScriptGlobalObject methods
257 : //
258 :
259 : void
260 0 : nsXBLDocGlobalObject::SetContext(nsIScriptContext *aScriptContext)
261 : {
262 0 : if (!aScriptContext) {
263 0 : mScriptContext = nsnull;
264 0 : return;
265 : }
266 0 : NS_ASSERTION(aScriptContext->GetScriptTypeID() ==
267 : nsIProgrammingLanguage::JAVASCRIPT,
268 : "xbl is not multi-language");
269 0 : aScriptContext->WillInitializeContext();
270 : // NOTE: We init this context with a NULL global, so we automatically
271 : // hook up to the existing nsIScriptGlobalObject global setup by
272 : // nsGlobalWindow.
273 : nsresult rv;
274 0 : rv = aScriptContext->InitContext();
275 0 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Script Language's InitContext failed");
276 0 : aScriptContext->SetGCOnDestruction(false);
277 0 : aScriptContext->DidInitializeContext();
278 : // and we set up our global manually
279 0 : mScriptContext = aScriptContext;
280 : }
281 :
282 : nsresult
283 0 : nsXBLDocGlobalObject::SetScriptContext(PRUint32 lang_id, nsIScriptContext *aContext)
284 : {
285 0 : NS_ASSERTION(lang_id == nsIProgrammingLanguage::JAVASCRIPT, "Only JS allowed!");
286 0 : SetContext(aContext);
287 0 : return NS_OK;
288 : }
289 :
290 : nsIScriptContext *
291 0 : nsXBLDocGlobalObject::GetScriptContext(PRUint32 language)
292 : {
293 : // This impl still assumes JS
294 0 : NS_ENSURE_TRUE(language==nsIProgrammingLanguage::JAVASCRIPT, nsnull);
295 0 : return GetContext();
296 : }
297 :
298 : nsresult
299 0 : nsXBLDocGlobalObject::EnsureScriptEnvironment(PRUint32 aLangID)
300 : {
301 0 : if (aLangID != nsIProgrammingLanguage::JAVASCRIPT) {
302 0 : NS_WARNING("XBL still JS only");
303 0 : return NS_ERROR_INVALID_ARG;
304 : }
305 0 : if (mScriptContext)
306 0 : return NS_OK; // already initialized for this lang
307 0 : nsCOMPtr<nsIDOMScriptObjectFactory> factory = do_GetService(kDOMScriptObjectFactoryCID);
308 0 : NS_ENSURE_TRUE(factory, NS_OK);
309 :
310 : nsresult rv;
311 :
312 0 : nsCOMPtr<nsIScriptRuntime> scriptRuntime;
313 0 : rv = NS_GetScriptRuntimeByID(aLangID, getter_AddRefs(scriptRuntime));
314 0 : NS_ENSURE_SUCCESS(rv, rv);
315 0 : nsCOMPtr<nsIScriptContext> newCtx = scriptRuntime->CreateContext();
316 0 : rv = SetScriptContext(aLangID, newCtx);
317 :
318 0 : JSContext *cx = mScriptContext->GetNativeContext();
319 0 : JSAutoRequest ar(cx);
320 :
321 : // nsJSEnvironment set the error reporter to NS_ScriptErrorReporter so
322 : // we must apparently override that with our own (although it isn't clear
323 : // why - see bug 339647)
324 0 : JS_SetErrorReporter(cx, XBL_ProtoErrorReporter);
325 :
326 0 : nsIPrincipal *principal = GetPrincipal();
327 : JSCompartment *compartment;
328 :
329 : rv = xpc_CreateGlobalObject(cx, &gSharedGlobalClass, principal, nsnull,
330 0 : false, &mJSObject, &compartment);
331 0 : NS_ENSURE_SUCCESS(rv, NS_OK);
332 :
333 0 : ::JS_SetGlobalObject(cx, mJSObject);
334 :
335 : // Add an owning reference from JS back to us. This'll be
336 : // released when the JSObject is finalized.
337 0 : ::JS_SetPrivate(mJSObject, this);
338 0 : NS_ADDREF(this);
339 0 : return NS_OK;
340 : }
341 :
342 : nsIScriptContext *
343 0 : nsXBLDocGlobalObject::GetContext()
344 : {
345 : // This whole fragile mess is predicated on the fact that
346 : // GetContext() will be called before GetScriptObject() is.
347 0 : if (! mScriptContext) {
348 0 : nsresult rv = EnsureScriptEnvironment(nsIProgrammingLanguage::JAVASCRIPT);
349 : // JS is builtin so we make noise if it fails to initialize.
350 0 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to setup JS!?");
351 0 : NS_ENSURE_SUCCESS(rv, nsnull);
352 0 : NS_ASSERTION(mScriptContext, "Failed to find a script context!?");
353 : }
354 0 : return mScriptContext;
355 : }
356 :
357 : void
358 0 : nsXBLDocGlobalObject::ClearGlobalObjectOwner()
359 : {
360 0 : mGlobalObjectOwner = nsnull;
361 0 : }
362 :
363 : JSObject *
364 0 : nsXBLDocGlobalObject::GetGlobalJSObject()
365 : {
366 : // The prototype document has its own special secret script object
367 : // that can be used to compile scripts and event handlers.
368 :
369 0 : if (!mScriptContext)
370 0 : return nsnull;
371 :
372 0 : JSContext* cx = mScriptContext->GetNativeContext();
373 0 : if (!cx)
374 0 : return nsnull;
375 :
376 0 : JSObject *ret = ::JS_GetGlobalObject(cx);
377 0 : NS_ASSERTION(mJSObject == ret, "How did this magic switch happen?");
378 0 : return ret;
379 : }
380 :
381 : void
382 0 : nsXBLDocGlobalObject::OnFinalize(JSObject* aObject)
383 : {
384 0 : NS_ASSERTION(aObject == mJSObject, "Wrong object finalized!");
385 0 : mJSObject = NULL;
386 0 : }
387 :
388 : void
389 0 : nsXBLDocGlobalObject::SetScriptsEnabled(bool aEnabled, bool aFireTimeouts)
390 : {
391 : // We don't care...
392 0 : }
393 :
394 : //----------------------------------------------------------------------
395 : //
396 : // nsIScriptObjectPrincipal methods
397 : //
398 :
399 : nsIPrincipal*
400 0 : nsXBLDocGlobalObject::GetPrincipal()
401 : {
402 0 : if (!mGlobalObjectOwner) {
403 : // XXXbz this should really save the principal when
404 : // ClearGlobalObjectOwner() happens.
405 0 : return nsnull;
406 : }
407 :
408 : nsRefPtr<nsXBLDocumentInfo> docInfo =
409 0 : static_cast<nsXBLDocumentInfo*>(mGlobalObjectOwner);
410 :
411 0 : nsCOMPtr<nsIDocument> document = docInfo->GetDocument();
412 0 : if (!document)
413 0 : return NULL;
414 :
415 0 : return document->NodePrincipal();
416 : }
417 :
418 0 : static bool IsChromeURI(nsIURI* aURI)
419 : {
420 0 : bool isChrome = false;
421 0 : if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)))
422 0 : return isChrome;
423 0 : return false;
424 : }
425 :
426 : /* Implementation file */
427 :
428 : static bool
429 0 : TraverseProtos(nsHashKey *aKey, void *aData, void* aClosure)
430 : {
431 : nsCycleCollectionTraversalCallback *cb =
432 0 : static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
433 0 : nsXBLPrototypeBinding *proto = static_cast<nsXBLPrototypeBinding*>(aData);
434 0 : proto->Traverse(*cb);
435 0 : return kHashEnumerateNext;
436 : }
437 :
438 : static bool
439 0 : UnlinkProtoJSObjects(nsHashKey *aKey, void *aData, void* aClosure)
440 : {
441 0 : nsXBLPrototypeBinding *proto = static_cast<nsXBLPrototypeBinding*>(aData);
442 0 : proto->UnlinkJSObjects();
443 0 : return kHashEnumerateNext;
444 : }
445 :
446 : struct ProtoTracer
447 : {
448 : TraceCallback mCallback;
449 : void *mClosure;
450 : };
451 :
452 : static bool
453 0 : TraceProtos(nsHashKey *aKey, void *aData, void* aClosure)
454 : {
455 0 : ProtoTracer* closure = static_cast<ProtoTracer*>(aClosure);
456 0 : nsXBLPrototypeBinding *proto = static_cast<nsXBLPrototypeBinding*>(aData);
457 0 : proto->Trace(closure->mCallback, closure->mClosure);
458 0 : return kHashEnumerateNext;
459 : }
460 :
461 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo)
462 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo)
463 0 : if (tmp->mBindingTable) {
464 0 : tmp->mBindingTable->Enumerate(UnlinkProtoJSObjects, nsnull);
465 : }
466 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
467 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGlobalObject)
468 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
469 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo)
470 0 : if (tmp->mDocument &&
471 0 : nsCCUncollectableMarker::InGeneration(cb, tmp->mDocument->GetMarkedCCGeneration())) {
472 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
473 0 : return NS_SUCCESS_INTERRUPTED_TRAVERSE;
474 : }
475 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
476 0 : if (tmp->mBindingTable) {
477 0 : tmp->mBindingTable->Enumerate(TraverseProtos, &cb);
478 : }
479 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mGlobalObject");
480 0 : cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObject*>(tmp->mGlobalObject));
481 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
482 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
483 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo)
484 0 : if (tmp->mBindingTable) {
485 0 : ProtoTracer closure = { aCallback, aClosure };
486 0 : tmp->mBindingTable->Enumerate(TraceProtos, &closure);
487 : }
488 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
489 :
490 : static void
491 0 : UnmarkXBLJSObject(PRUint32 aLangID, void* aP, const char* aName, void* aClosure)
492 : {
493 0 : if (aLangID == nsIProgrammingLanguage::JAVASCRIPT) {
494 0 : xpc_UnmarkGrayObject(static_cast<JSObject*>(aP));
495 : }
496 0 : }
497 :
498 : static bool
499 0 : UnmarkProtos(nsHashKey* aKey, void* aData, void* aClosure)
500 : {
501 0 : nsXBLPrototypeBinding* proto = static_cast<nsXBLPrototypeBinding*>(aData);
502 0 : proto->Trace(UnmarkXBLJSObject, nsnull);
503 0 : return kHashEnumerateNext;
504 : }
505 :
506 : void
507 0 : nsXBLDocumentInfo::MarkInCCGeneration(PRUint32 aGeneration)
508 : {
509 0 : if (mDocument) {
510 0 : mDocument->MarkUncollectableForCCGeneration(aGeneration);
511 : }
512 0 : if (mBindingTable) {
513 0 : mBindingTable->Enumerate(UnmarkProtos, nsnull);
514 : }
515 0 : }
516 :
517 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLDocumentInfo)
518 0 : NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
519 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
520 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptGlobalObjectOwner)
521 0 : NS_INTERFACE_MAP_END
522 :
523 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLDocumentInfo)
524 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLDocumentInfo)
525 :
526 0 : nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument* aDocument)
527 : : mDocument(aDocument),
528 : mScriptAccess(true),
529 : mIsChrome(false),
530 : mBindingTable(nsnull),
531 0 : mFirstBinding(nsnull)
532 : {
533 0 : nsIURI* uri = aDocument->GetDocumentURI();
534 0 : if (IsChromeURI(uri)) {
535 : // Cache whether or not this chrome XBL can execute scripts.
536 : nsCOMPtr<nsIXULChromeRegistry> reg =
537 0 : mozilla::services::GetXULChromeRegistryService();
538 0 : if (reg) {
539 0 : bool allow = true;
540 0 : reg->AllowScriptsForPackage(uri, &allow);
541 0 : mScriptAccess = allow;
542 : }
543 0 : mIsChrome = true;
544 : }
545 0 : }
546 :
547 0 : nsXBLDocumentInfo::~nsXBLDocumentInfo()
548 : {
549 : /* destructor code */
550 0 : if (mGlobalObject) {
551 : // remove circular reference
552 0 : mGlobalObject->SetScriptContext(nsIProgrammingLanguage::JAVASCRIPT, nsnull);
553 0 : mGlobalObject->ClearGlobalObjectOwner(); // just in case
554 : }
555 0 : if (mBindingTable) {
556 0 : NS_DROP_JS_OBJECTS(this, nsXBLDocumentInfo);
557 0 : delete mBindingTable;
558 : }
559 0 : }
560 :
561 : nsXBLPrototypeBinding*
562 0 : nsXBLDocumentInfo::GetPrototypeBinding(const nsACString& aRef)
563 : {
564 0 : if (!mBindingTable)
565 0 : return NULL;
566 :
567 0 : if (aRef.IsEmpty()) {
568 : // Return our first binding
569 0 : return mFirstBinding;
570 : }
571 :
572 0 : const nsPromiseFlatCString& flat = PromiseFlatCString(aRef);
573 0 : nsCStringKey key(flat.get());
574 0 : return static_cast<nsXBLPrototypeBinding*>(mBindingTable->Get(&key));
575 : }
576 :
577 : static bool
578 0 : DeletePrototypeBinding(nsHashKey* aKey, void* aData, void* aClosure)
579 : {
580 0 : nsXBLPrototypeBinding* binding = static_cast<nsXBLPrototypeBinding*>(aData);
581 0 : delete binding;
582 0 : return true;
583 : }
584 :
585 : nsresult
586 0 : nsXBLDocumentInfo::SetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding* aBinding)
587 : {
588 0 : if (!mBindingTable) {
589 0 : mBindingTable = new nsObjectHashtable(nsnull, nsnull, DeletePrototypeBinding, nsnull);
590 :
591 0 : NS_HOLD_JS_OBJECTS(this, nsXBLDocumentInfo);
592 : }
593 :
594 0 : const nsPromiseFlatCString& flat = PromiseFlatCString(aRef);
595 0 : nsCStringKey key(flat.get());
596 0 : NS_ENSURE_STATE(!mBindingTable->Get(&key));
597 0 : mBindingTable->Put(&key, aBinding);
598 :
599 0 : return NS_OK;
600 : }
601 :
602 : void
603 0 : nsXBLDocumentInfo::RemovePrototypeBinding(const nsACString& aRef)
604 : {
605 0 : if (mBindingTable) {
606 : // Use a flat string to avoid making a copy.
607 0 : const nsPromiseFlatCString& flat = PromiseFlatCString(aRef);
608 0 : nsCStringKey key(flat);
609 0 : mBindingTable->Remove(&key);
610 : }
611 0 : }
612 :
613 : // Callback to enumerate over the bindings from this document and write them
614 : // out to the cache.
615 : bool
616 0 : WriteBinding(nsHashKey *aKey, void *aData, void* aClosure)
617 : {
618 0 : nsXBLPrototypeBinding* binding = static_cast<nsXBLPrototypeBinding *>(aData);
619 0 : binding->Write((nsIObjectOutputStream*)aClosure);
620 :
621 0 : return kHashEnumerateNext;
622 : }
623 :
624 : // static
625 : nsresult
626 0 : nsXBLDocumentInfo::ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo)
627 : {
628 0 : *aDocInfo = nsnull;
629 :
630 0 : nsCAutoString spec(kXBLCachePrefix);
631 0 : nsresult rv = PathifyURI(aURI, spec);
632 0 : NS_ENSURE_SUCCESS(rv, rv);
633 :
634 0 : StartupCache* startupCache = StartupCache::GetSingleton();
635 0 : NS_ENSURE_TRUE(startupCache, NS_ERROR_FAILURE);
636 :
637 0 : nsAutoArrayPtr<char> buf;
638 : PRUint32 len;
639 0 : rv = startupCache->GetBuffer(spec.get(), getter_Transfers(buf), &len);
640 : // GetBuffer will fail if the binding is not in the cache.
641 0 : if (NS_FAILED(rv))
642 0 : return rv;
643 :
644 0 : nsCOMPtr<nsIObjectInputStream> stream;
645 0 : rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(stream));
646 0 : NS_ENSURE_SUCCESS(rv, rv);
647 0 : buf.forget();
648 :
649 : // The file compatibility.ini stores the build id. This is checked in
650 : // nsAppRunner.cpp and will delete the cache if a different build is
651 : // present. However, we check that the version matches here to be safe.
652 : PRUint32 version;
653 0 : rv = stream->Read32(&version);
654 0 : NS_ENSURE_SUCCESS(rv, rv);
655 0 : if (version != XBLBinding_Serialize_Version) {
656 : // The version that exists is different than expected, likely created with a
657 : // different build, so invalidate the cache.
658 0 : startupCache->InvalidateCache();
659 0 : return NS_ERROR_NOT_AVAILABLE;
660 : }
661 :
662 0 : nsCOMPtr<nsIPrincipal> principal;
663 0 : nsContentUtils::GetSecurityManager()->
664 0 : GetSystemPrincipal(getter_AddRefs(principal));
665 :
666 0 : nsCOMPtr<nsIDOMDocument> domdoc;
667 0 : rv = NS_NewXBLDocument(getter_AddRefs(domdoc), aURI, nsnull, principal);
668 0 : NS_ENSURE_SUCCESS(rv, rv);
669 :
670 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
671 0 : nsRefPtr<nsXBLDocumentInfo> docInfo = NS_NewXBLDocumentInfo(doc);
672 :
673 0 : while (1) {
674 : PRUint8 flags;
675 0 : nsresult rv = stream->Read8(&flags);
676 0 : NS_ENSURE_SUCCESS(rv, rv);
677 0 : if (flags == XBLBinding_Serialize_NoMoreBindings)
678 : break;
679 :
680 0 : nsXBLPrototypeBinding* binding = new nsXBLPrototypeBinding();
681 0 : rv = binding->Read(stream, docInfo, doc, flags);
682 0 : if (NS_FAILED(rv)) {
683 0 : delete binding;
684 0 : return rv;
685 : }
686 : }
687 :
688 0 : docInfo.swap(*aDocInfo);
689 0 : return NS_OK;
690 : }
691 :
692 : nsresult
693 0 : nsXBLDocumentInfo::WritePrototypeBindings()
694 : {
695 : // Only write out bindings with the system principal
696 0 : if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal()))
697 0 : return NS_OK;
698 :
699 0 : nsCAutoString spec(kXBLCachePrefix);
700 0 : nsresult rv = PathifyURI(DocumentURI(), spec);
701 0 : NS_ENSURE_SUCCESS(rv, rv);
702 :
703 0 : StartupCache* startupCache = StartupCache::GetSingleton();
704 0 : NS_ENSURE_TRUE(startupCache, rv);
705 :
706 0 : nsCOMPtr<nsIObjectOutputStream> stream;
707 0 : nsCOMPtr<nsIStorageStream> storageStream;
708 0 : rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(stream),
709 0 : getter_AddRefs(storageStream),
710 0 : true);
711 0 : NS_ENSURE_SUCCESS(rv, rv);
712 :
713 0 : rv = stream->Write32(XBLBinding_Serialize_Version);
714 0 : NS_ENSURE_SUCCESS(rv, rv);
715 :
716 0 : if (mBindingTable)
717 0 : mBindingTable->Enumerate(WriteBinding, stream);
718 :
719 : // write a end marker at the end
720 0 : rv = stream->Write8(XBLBinding_Serialize_NoMoreBindings);
721 0 : NS_ENSURE_SUCCESS(rv, rv);
722 :
723 0 : stream->Close();
724 0 : NS_ENSURE_SUCCESS(rv, rv);
725 :
726 : PRUint32 len;
727 0 : nsAutoArrayPtr<char> buf;
728 0 : rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len);
729 0 : NS_ENSURE_SUCCESS(rv, rv);
730 :
731 0 : return startupCache->PutBuffer(spec.get(), buf, len);
732 : }
733 :
734 : void
735 0 : nsXBLDocumentInfo::SetFirstPrototypeBinding(nsXBLPrototypeBinding* aBinding)
736 : {
737 0 : mFirstBinding = aBinding;
738 0 : }
739 :
740 0 : bool FlushScopedSkinSheets(nsHashKey* aKey, void* aData, void* aClosure)
741 : {
742 0 : nsXBLPrototypeBinding* proto = (nsXBLPrototypeBinding*)aData;
743 0 : proto->FlushSkinSheets();
744 0 : return true;
745 : }
746 :
747 : void
748 0 : nsXBLDocumentInfo::FlushSkinStylesheets()
749 : {
750 0 : if (mBindingTable)
751 0 : mBindingTable->Enumerate(FlushScopedSkinSheets);
752 0 : }
753 :
754 : //----------------------------------------------------------------------
755 : //
756 : // nsIScriptGlobalObjectOwner methods
757 : //
758 :
759 : nsIScriptGlobalObject*
760 0 : nsXBLDocumentInfo::GetScriptGlobalObject()
761 : {
762 0 : if (!mGlobalObject) {
763 0 : nsXBLDocGlobalObject *global = new nsXBLDocGlobalObject(this);
764 0 : if (!global)
765 0 : return nsnull;
766 :
767 0 : mGlobalObject = global;
768 : }
769 :
770 0 : return mGlobalObject;
771 : }
772 :
773 0 : nsXBLDocumentInfo* NS_NewXBLDocumentInfo(nsIDocument* aDocument)
774 : {
775 0 : NS_PRECONDITION(aDocument, "Must have a document!");
776 :
777 : nsXBLDocumentInfo* result;
778 :
779 0 : result = new nsXBLDocumentInfo(aDocument);
780 0 : NS_ADDREF(result);
781 0 : return result;
782 4392 : }
|