Back to index

lightning-sunbird  0.9+nobinonly
nsXBLDocumentInfo.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim: set ts=2 sw=2 et tw=80: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsXBLDocumentInfo.h"
00040 #include "nsHashtable.h"
00041 #include "nsIDocument.h"
00042 #include "nsXBLPrototypeBinding.h"
00043 #include "nsIScriptObjectPrincipal.h"
00044 #include "nsIScriptGlobalObject.h"
00045 #include "nsIScriptContext.h"
00046 #include "nsIDOMScriptObjectFactory.h"
00047 #include "jsapi.h"
00048 #include "nsIURI.h"
00049 #include "nsIConsoleService.h"
00050 #include "nsIScriptError.h"
00051 #include "nsIChromeRegistry.h"
00052 #include "nsIPrincipal.h"
00053 #include "nsIScriptSecurityManager.h"
00054 #include "nsContentUtils.h"
00055 
00056 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
00057 
00058 // An XBLDocumentInfo object has a special context associated with it which we can use to pre-compile 
00059 // properties and methods of XBL bindings against.
00060 class nsXBLDocGlobalObject : public nsIScriptGlobalObject,
00061                              public nsIScriptObjectPrincipal
00062 {
00063 public:
00064   nsXBLDocGlobalObject();
00065 
00066   // nsISupports interface
00067   NS_DECL_ISUPPORTS
00068   
00069   // nsIScriptGlobalObject methods
00070   virtual void SetContext(nsIScriptContext *aContext);
00071   virtual nsIScriptContext *GetContext();
00072   virtual nsresult SetNewDocument(nsIDOMDocument *aDocument,
00073                                   nsISupports *aState,
00074                                   PRBool aRemoveEventListeners,
00075                                   PRBool aClearScope);
00076   virtual void SetDocShell(nsIDocShell *aDocShell);
00077   virtual nsIDocShell *GetDocShell();
00078   virtual void SetOpenerWindow(nsIDOMWindowInternal *aOpener);
00079   virtual void SetGlobalObjectOwner(nsIScriptGlobalObjectOwner* aOwner);
00080   virtual nsIScriptGlobalObjectOwner *GetGlobalObjectOwner();
00081   virtual nsresult HandleDOMEvent(nsPresContext* aPresContext, 
00082                                   nsEvent* aEvent, 
00083                                   nsIDOMEvent** aDOMEvent,
00084                                   PRUint32 aFlags,
00085                                   nsEventStatus* aEventStatus);
00086   virtual JSObject *GetGlobalJSObject();
00087   virtual void OnFinalize(JSObject *aObject);
00088   virtual void SetScriptsEnabled(PRBool aEnabled, PRBool aFireTimeouts);
00089   virtual nsresult SetNewArguments(PRUint32 aArgc, void* aArgv);
00090 
00091   // nsIScriptObjectPrincipal methods
00092   virtual nsIPrincipal* GetPrincipal();
00093 
00094   static JSBool doCheckAccess(JSContext *cx, JSObject *obj, jsval id,
00095                               PRUint32 accessType);
00096 
00097 protected:
00098   virtual ~nsXBLDocGlobalObject();
00099 
00100   nsCOMPtr<nsIScriptContext> mScriptContext;
00101   JSObject *mJSObject;    // XXX JS language rabies bigotry badness
00102 
00103   nsIScriptGlobalObjectOwner* mGlobalObjectOwner; // weak reference
00104   static JSClass gSharedGlobalClass;
00105 };
00106 
00107 JSBool
00108 nsXBLDocGlobalObject::doCheckAccess(JSContext *cx, JSObject *obj, jsval id, PRUint32 accessType)
00109 {
00110   nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
00111   if (!ssm) {
00112     ::JS_ReportError(cx, "Unable to verify access to a global object property.");
00113     return JS_FALSE;
00114   }
00115 
00116   // Make sure to actually operate on our object, and not some object further
00117   // down on the proto chain.
00118   while (JS_GET_CLASS(cx, obj) != &nsXBLDocGlobalObject::gSharedGlobalClass) {
00119     obj = ::JS_GetPrototype(cx, obj);
00120     if (!obj) {
00121       ::JS_ReportError(cx, "Invalid access to a global object property.");
00122       return JS_FALSE;
00123     }
00124   }
00125 
00126   nsresult rv = ssm->CheckPropertyAccess(cx, obj, JS_GET_CLASS(cx, obj)->name,
00127                                          id, accessType);
00128   return NS_SUCCEEDED(rv);
00129 }
00130 
00131 PR_STATIC_CALLBACK(JSBool)
00132 nsXBLDocGlobalObject_getProperty(JSContext *cx, JSObject *obj,
00133                                  jsval id, jsval *vp)
00134 {
00135   return nsXBLDocGlobalObject::
00136     doCheckAccess(cx, obj, id, nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
00137 }
00138 
00139 PR_STATIC_CALLBACK(JSBool)
00140 nsXBLDocGlobalObject_setProperty(JSContext *cx, JSObject *obj,
00141                                  jsval id, jsval *vp)
00142 {
00143   return nsXBLDocGlobalObject::
00144     doCheckAccess(cx, obj, id, nsIXPCSecurityManager::ACCESS_SET_PROPERTY);
00145 }
00146 
00147 PR_STATIC_CALLBACK(JSBool)
00148 nsXBLDocGlobalObject_checkAccess(JSContext *cx, JSObject *obj, jsval id,
00149                                  JSAccessMode mode, jsval *vp)
00150 {
00151   PRUint32 translated;
00152   if (mode & JSACC_WRITE) {
00153     translated = nsIXPCSecurityManager::ACCESS_SET_PROPERTY;
00154   } else {
00155     translated = nsIXPCSecurityManager::ACCESS_GET_PROPERTY;
00156   }
00157 
00158   return nsXBLDocGlobalObject::
00159     doCheckAccess(cx, obj, id, translated);
00160 }
00161 
00162 PR_STATIC_CALLBACK(void)
00163 nsXBLDocGlobalObject_finalize(JSContext *cx, JSObject *obj)
00164 {
00165   nsISupports *nativeThis = (nsISupports*)JS_GetPrivate(cx, obj);
00166 
00167   nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(nativeThis));
00168 
00169   if (sgo)
00170       sgo->OnFinalize(obj);
00171 
00172   // The addref was part of JSObject construction
00173   NS_RELEASE(nativeThis);
00174 }
00175 
00176 PR_STATIC_CALLBACK(JSBool)
00177 nsXBLDocGlobalObject_resolve(JSContext *cx, JSObject *obj, jsval id)
00178 {
00179   JSBool did_resolve = JS_FALSE;
00180   return JS_ResolveStandardClass(cx, obj, id, &did_resolve);
00181 }
00182 
00183 
00184 JSClass nsXBLDocGlobalObject::gSharedGlobalClass = {
00185     "nsXBLPrototypeScript compilation scope",
00186     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_GLOBAL_FLAGS,
00187     JS_PropertyStub,  JS_PropertyStub,
00188     nsXBLDocGlobalObject_getProperty, nsXBLDocGlobalObject_setProperty,
00189     JS_EnumerateStub, nsXBLDocGlobalObject_resolve,
00190     JS_ConvertStub, nsXBLDocGlobalObject_finalize,
00191     NULL, nsXBLDocGlobalObject_checkAccess
00192 };
00193 
00194 //----------------------------------------------------------------------
00195 //
00196 // nsXBLDocGlobalObject
00197 //
00198 
00199 nsXBLDocGlobalObject::nsXBLDocGlobalObject()
00200     : mJSObject(nsnull),
00201       mGlobalObjectOwner(nsnull)
00202 {
00203 }
00204 
00205 
00206 nsXBLDocGlobalObject::~nsXBLDocGlobalObject()
00207 {}
00208 
00209 
00210 NS_IMPL_ISUPPORTS2(nsXBLDocGlobalObject, nsIScriptGlobalObject, nsIScriptObjectPrincipal)
00211 
00212 void JS_DLL_CALLBACK
00213 XBL_ProtoErrorReporter(JSContext *cx,
00214                        const char *message,
00215                        JSErrorReport *report)
00216 {
00217   // Make an nsIScriptError and populate it with information from
00218   // this error.
00219   nsCOMPtr<nsIScriptError>
00220     errorObject(do_CreateInstance("@mozilla.org/scripterror;1"));
00221   nsCOMPtr<nsIConsoleService>
00222     consoleService(do_GetService("@mozilla.org/consoleservice;1"));
00223 
00224   if (errorObject && consoleService) {
00225     PRUint32 column = report->uctokenptr - report->uclinebuf;
00226 
00227     errorObject->Init
00228          (NS_REINTERPRET_CAST(const PRUnichar*, report->ucmessage),
00229           NS_ConvertUTF8toUCS2(report->filename).get(),
00230           NS_REINTERPRET_CAST(const PRUnichar*, report->uclinebuf),
00231           report->lineno, column, report->flags,
00232           "xbl javascript"
00233           );
00234     consoleService->LogMessage(errorObject);
00235   }
00236 }
00237 
00238 //----------------------------------------------------------------------
00239 //
00240 // nsIScriptGlobalObject methods
00241 //
00242 
00243 void
00244 nsXBLDocGlobalObject::SetContext(nsIScriptContext *aContext)
00245 {
00246   mScriptContext = aContext;
00247   if (mScriptContext) {
00248     JSContext* cx = (JSContext *)mScriptContext->GetNativeContext();
00249     JS_SetErrorReporter(cx, XBL_ProtoErrorReporter);
00250   }
00251 }
00252 
00253 
00254 nsIScriptContext *
00255 nsXBLDocGlobalObject::GetContext()
00256 {
00257   // This whole fragile mess is predicated on the fact that
00258   // GetContext() will be called before GetScriptObject() is.
00259   if (! mScriptContext) {
00260     nsCOMPtr<nsIDOMScriptObjectFactory> factory = do_GetService(kDOMScriptObjectFactoryCID);
00261     NS_ENSURE_TRUE(factory, nsnull);
00262 
00263     nsresult rv =  factory->NewScriptContext(nsnull, getter_AddRefs(mScriptContext));
00264     if (NS_FAILED(rv))
00265         return nsnull;
00266 
00267     JSContext *cx = (JSContext *)mScriptContext->GetNativeContext();
00268 
00269     JS_SetErrorReporter(cx, XBL_ProtoErrorReporter);
00270     mJSObject = ::JS_NewObject(cx, &gSharedGlobalClass, nsnull, nsnull);
00271     if (!mJSObject)
00272         return nsnull;
00273 
00274     ::JS_SetGlobalObject(cx, mJSObject);
00275 
00276     // Add an owning reference from JS back to us. This'll be
00277     // released when the JSObject is finalized.
00278     ::JS_SetPrivate(cx, mJSObject, this);
00279     NS_ADDREF(this);
00280   }
00281 
00282   return mScriptContext;
00283 }
00284 
00285 
00286 nsresult
00287 nsXBLDocGlobalObject::SetNewDocument(nsIDOMDocument *aDocument,
00288                                      nsISupports *aState,
00289                                      PRBool aRemoveEventListeners,
00290                                      PRBool aClearScope)
00291 {
00292   NS_NOTREACHED("waaah!");
00293   return NS_ERROR_UNEXPECTED;
00294 }
00295 
00296 
00297 void
00298 nsXBLDocGlobalObject::SetDocShell(nsIDocShell *aDocShell)
00299 {
00300   NS_NOTREACHED("waaah!");
00301 }
00302 
00303 
00304 nsIDocShell *
00305 nsXBLDocGlobalObject::GetDocShell()
00306 {
00307   NS_WARNING("waaah!");
00308   return nsnull;
00309 }
00310 
00311 
00312 void
00313 nsXBLDocGlobalObject::SetOpenerWindow(nsIDOMWindowInternal *aOpener)
00314 {
00315   NS_NOTREACHED("waaah!");
00316 }
00317 
00318 
00319 void
00320 nsXBLDocGlobalObject::SetGlobalObjectOwner(nsIScriptGlobalObjectOwner* aOwner)
00321 {
00322   mGlobalObjectOwner = aOwner; // weak reference
00323 }
00324 
00325 
00326 nsIScriptGlobalObjectOwner *
00327 nsXBLDocGlobalObject::GetGlobalObjectOwner()
00328 {
00329   return mGlobalObjectOwner;
00330 }
00331 
00332 
00333 nsresult
00334 nsXBLDocGlobalObject::HandleDOMEvent(nsPresContext* aPresContext, 
00335                                        nsEvent* aEvent, 
00336                                        nsIDOMEvent** aDOMEvent,
00337                                        PRUint32 aFlags,
00338                                        nsEventStatus* aEventStatus)
00339 {
00340   NS_NOTREACHED("waaah!");
00341   return NS_ERROR_UNEXPECTED;
00342 }
00343 
00344 JSObject *
00345 nsXBLDocGlobalObject::GetGlobalJSObject()
00346 {
00347   // The prototype document has its own special secret script object
00348   // that can be used to compile scripts and event handlers.
00349 
00350   if (!mScriptContext)
00351     return nsnull;
00352 
00353   JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
00354                                       mScriptContext->GetNativeContext());
00355   if (!cx)
00356     return nsnull;
00357 
00358   return ::JS_GetGlobalObject(cx);
00359 }
00360 
00361 void
00362 nsXBLDocGlobalObject::OnFinalize(JSObject *aObject)
00363 {
00364   NS_ASSERTION(aObject == mJSObject, "Wrong object finalized!");
00365 
00366   mJSObject = nsnull;
00367 }
00368 
00369 void
00370 nsXBLDocGlobalObject::SetScriptsEnabled(PRBool aEnabled, PRBool aFireTimeouts)
00371 {
00372     // We don't care...
00373 }
00374 
00375 nsresult
00376 nsXBLDocGlobalObject::SetNewArguments(PRUint32 aArgc, void* aArgv)
00377 {
00378   NS_NOTREACHED("waaah!");
00379   return NS_ERROR_UNEXPECTED;
00380 }
00381 
00382 //----------------------------------------------------------------------
00383 //
00384 // nsIScriptObjectPrincipal methods
00385 //
00386 
00387 nsIPrincipal*
00388 nsXBLDocGlobalObject::GetPrincipal()
00389 {
00390   nsresult rv = NS_OK;
00391   if (!mGlobalObjectOwner) {
00392     return nsnull;
00393   }
00394 
00395   nsCOMPtr<nsIXBLDocumentInfo> docInfo = do_QueryInterface(mGlobalObjectOwner, &rv);
00396   NS_ENSURE_SUCCESS(rv, nsnull);
00397 
00398   nsCOMPtr<nsIDocument> document;
00399   rv = docInfo->GetDocument(getter_AddRefs(document));
00400   NS_ENSURE_SUCCESS(rv, nsnull);
00401 
00402   return document->GetPrincipal();
00403 }
00404 
00405 static PRBool IsChromeURI(nsIURI* aURI)
00406 {
00407   PRBool isChrome = PR_FALSE;
00408   if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)))
00409       return isChrome;
00410   return PR_FALSE;
00411 }
00412 
00413 /* Implementation file */
00414 NS_IMPL_ISUPPORTS3(nsXBLDocumentInfo, nsIXBLDocumentInfo, nsIScriptGlobalObjectOwner, nsISupportsWeakReference)
00415 
00416 nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument* aDocument)
00417   : mDocument(aDocument),
00418     mScriptAccess(PR_TRUE),
00419     mIsChrome(PR_FALSE),
00420     mBindingTable(nsnull)
00421 {
00422   nsIURI* uri = aDocument->GetDocumentURI();
00423   if (IsChromeURI(uri)) {
00424     // Cache whether or not this chrome XBL can execute scripts.
00425     nsCOMPtr<nsIXULChromeRegistry> reg(do_GetService(NS_CHROMEREGISTRY_CONTRACTID));
00426     if (reg) {
00427       PRBool allow = PR_TRUE;
00428       reg->AllowScriptsForPackage(uri, &allow);
00429       mScriptAccess = allow;
00430     }
00431     mIsChrome = PR_TRUE;
00432   }
00433 }
00434 
00435 nsXBLDocumentInfo::~nsXBLDocumentInfo()
00436 {
00437   /* destructor code */
00438   if (mGlobalObject) {
00439     mGlobalObject->SetContext(nsnull); // remove circular reference
00440     mGlobalObject->SetGlobalObjectOwner(nsnull); // just in case
00441   }
00442   delete mBindingTable;
00443 }
00444 
00445 NS_IMETHODIMP
00446 nsXBLDocumentInfo::GetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding** aResult)
00447 {
00448   *aResult = nsnull;
00449   if (!mBindingTable)
00450     return NS_OK;
00451 
00452   const nsPromiseFlatCString& flat = PromiseFlatCString(aRef);
00453   nsCStringKey key(flat.get());
00454   *aResult = NS_STATIC_CAST(nsXBLPrototypeBinding*, mBindingTable->Get(&key));
00455 
00456   return NS_OK;
00457 }
00458 
00459 static PRBool PR_CALLBACK
00460 DeletePrototypeBinding(nsHashKey* aKey, void* aData, void* aClosure)
00461 {
00462   nsXBLPrototypeBinding* binding = NS_STATIC_CAST(nsXBLPrototypeBinding*, aData);
00463   delete binding;
00464   return PR_TRUE;
00465 }
00466 
00467 NS_IMETHODIMP
00468 nsXBLDocumentInfo::SetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding* aBinding)
00469 {
00470   if (!mBindingTable)
00471     mBindingTable = new nsObjectHashtable(nsnull, nsnull, DeletePrototypeBinding, nsnull);
00472 
00473   const nsPromiseFlatCString& flat = PromiseFlatCString(aRef);
00474   nsCStringKey key(flat.get());
00475   mBindingTable->Put(&key, aBinding);
00476 
00477   return NS_OK;
00478 }
00479 
00480 PRBool PR_CALLBACK FlushScopedSkinSheets(nsHashKey* aKey, void* aData, void* aClosure)
00481 {
00482   nsXBLPrototypeBinding* proto = (nsXBLPrototypeBinding*)aData;
00483   proto->FlushSkinSheets();
00484   return PR_TRUE;
00485 }
00486 
00487 NS_IMETHODIMP
00488 nsXBLDocumentInfo::FlushSkinStylesheets()
00489 {
00490   if (mBindingTable)
00491     mBindingTable->Enumerate(FlushScopedSkinSheets);
00492   return NS_OK;
00493 
00494 }
00495 
00496 //----------------------------------------------------------------------
00497 //
00498 // nsIScriptGlobalObjectOwner methods
00499 //
00500 
00501 nsIScriptGlobalObject*
00502 nsXBLDocumentInfo::GetScriptGlobalObject()
00503 {
00504   if (!mGlobalObject) {
00505     
00506     mGlobalObject = new nsXBLDocGlobalObject();
00507     
00508     if (!mGlobalObject)
00509       return nsnull;
00510 
00511     mGlobalObject->SetGlobalObjectOwner(this); // does not refcount
00512   }
00513 
00514   return mGlobalObject;
00515 }
00516 
00517 nsresult NS_NewXBLDocumentInfo(nsIDocument* aDocument, nsIXBLDocumentInfo** aResult)
00518 {
00519   NS_PRECONDITION(aDocument, "Must have a document!");
00520 
00521   *aResult = new nsXBLDocumentInfo(aDocument);
00522   if (!*aResult) {
00523     return NS_ERROR_OUT_OF_MEMORY;
00524   }
00525 
00526   NS_ADDREF(*aResult);
00527   return NS_OK;
00528 }