1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/xbl/nsXBLDocumentInfo.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,366 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/DebugOnly.h" 1.11 + 1.12 +#include "nsXBLDocumentInfo.h" 1.13 +#include "nsIDocument.h" 1.14 +#include "nsXBLPrototypeBinding.h" 1.15 +#include "nsIScriptObjectPrincipal.h" 1.16 +#include "nsIScriptContext.h" 1.17 +#include "nsIDOMDocument.h" 1.18 +#include "nsIDOMScriptObjectFactory.h" 1.19 +#include "jsapi.h" 1.20 +#include "jsfriendapi.h" 1.21 +#include "nsIURI.h" 1.22 +#include "nsIConsoleService.h" 1.23 +#include "nsIScriptError.h" 1.24 +#include "nsIChromeRegistry.h" 1.25 +#include "nsIPrincipal.h" 1.26 +#include "nsJSPrincipals.h" 1.27 +#include "nsIScriptSecurityManager.h" 1.28 +#include "nsContentUtils.h" 1.29 +#include "nsCxPusher.h" 1.30 +#include "nsDOMJSUtils.h" 1.31 +#include "mozilla/Services.h" 1.32 +#include "xpcpublic.h" 1.33 +#include "mozilla/scache/StartupCache.h" 1.34 +#include "mozilla/scache/StartupCacheUtils.h" 1.35 +#include "nsCCUncollectableMarker.h" 1.36 +#include "mozilla/dom/BindingUtils.h" 1.37 +#include "mozilla/dom/URL.h" 1.38 + 1.39 +using namespace mozilla; 1.40 +using namespace mozilla::scache; 1.41 +using namespace mozilla::dom; 1.42 + 1.43 +static const char kXBLCachePrefix[] = "xblcache"; 1.44 + 1.45 +/* Implementation file */ 1.46 + 1.47 +static PLDHashOperator 1.48 +TraverseProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) 1.49 +{ 1.50 + nsCycleCollectionTraversalCallback *cb = 1.51 + static_cast<nsCycleCollectionTraversalCallback*>(aClosure); 1.52 + aProto->Traverse(*cb); 1.53 + return PL_DHASH_NEXT; 1.54 +} 1.55 + 1.56 +static PLDHashOperator 1.57 +UnlinkProtoJSObjects(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) 1.58 +{ 1.59 + aProto->UnlinkJSObjects(); 1.60 + return PL_DHASH_NEXT; 1.61 +} 1.62 + 1.63 +struct ProtoTracer 1.64 +{ 1.65 + const TraceCallbacks &mCallbacks; 1.66 + void *mClosure; 1.67 +}; 1.68 + 1.69 +static PLDHashOperator 1.70 +TraceProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) 1.71 +{ 1.72 + ProtoTracer* closure = static_cast<ProtoTracer*>(aClosure); 1.73 + aProto->Trace(closure->mCallbacks, closure->mClosure); 1.74 + return PL_DHASH_NEXT; 1.75 +} 1.76 + 1.77 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo) 1.78 + 1.79 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo) 1.80 + if (tmp->mBindingTable) { 1.81 + tmp->mBindingTable->EnumerateRead(UnlinkProtoJSObjects, nullptr); 1.82 + } 1.83 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) 1.84 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.85 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo) 1.86 + if (tmp->mDocument && 1.87 + nsCCUncollectableMarker::InGeneration(cb, tmp->mDocument->GetMarkedCCGeneration())) { 1.88 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.89 + return NS_SUCCESS_INTERRUPTED_TRAVERSE; 1.90 + } 1.91 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) 1.92 + if (tmp->mBindingTable) { 1.93 + tmp->mBindingTable->EnumerateRead(TraverseProtos, &cb); 1.94 + } 1.95 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.96 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.97 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo) 1.98 + if (tmp->mBindingTable) { 1.99 + ProtoTracer closure = { aCallbacks, aClosure }; 1.100 + tmp->mBindingTable->EnumerateRead(TraceProtos, &closure); 1.101 + } 1.102 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.103 + 1.104 +static void 1.105 +UnmarkXBLJSObject(void* aP, const char* aName, void* aClosure) 1.106 +{ 1.107 + JS::ExposeObjectToActiveJS(static_cast<JSObject*>(aP)); 1.108 +} 1.109 + 1.110 +static PLDHashOperator 1.111 +UnmarkProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) 1.112 +{ 1.113 + aProto->Trace(TraceCallbackFunc(UnmarkXBLJSObject), nullptr); 1.114 + return PL_DHASH_NEXT; 1.115 +} 1.116 + 1.117 +void 1.118 +nsXBLDocumentInfo::MarkInCCGeneration(uint32_t aGeneration) 1.119 +{ 1.120 + if (mDocument) { 1.121 + mDocument->MarkUncollectableForCCGeneration(aGeneration); 1.122 + } 1.123 + // Unmark any JS we hold 1.124 + if (mBindingTable) { 1.125 + mBindingTable->EnumerateRead(UnmarkProtos, nullptr); 1.126 + } 1.127 +} 1.128 + 1.129 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLDocumentInfo) 1.130 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.131 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.132 +NS_INTERFACE_MAP_END 1.133 + 1.134 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLDocumentInfo) 1.135 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLDocumentInfo) 1.136 + 1.137 +nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument* aDocument) 1.138 + : mDocument(aDocument), 1.139 + mScriptAccess(true), 1.140 + mIsChrome(false), 1.141 + mFirstBinding(nullptr) 1.142 +{ 1.143 + nsIURI* uri = aDocument->GetDocumentURI(); 1.144 + if (IsChromeURI(uri)) { 1.145 + // Cache whether or not this chrome XBL can execute scripts. 1.146 + nsCOMPtr<nsIXULChromeRegistry> reg = 1.147 + mozilla::services::GetXULChromeRegistryService(); 1.148 + if (reg) { 1.149 + bool allow = true; 1.150 + reg->AllowScriptsForPackage(uri, &allow); 1.151 + mScriptAccess = allow; 1.152 + } 1.153 + mIsChrome = true; 1.154 + } else { 1.155 + // If this binding isn't running with system principal, then it's running 1.156 + // from a remote-XUL whitelisted domain. This is already a not-really- 1.157 + // supported configuration (among other things, we don't use XBL scopes in 1.158 + // that configuration for compatibility reasons). But we should still at 1.159 + // least make an effort to prevent binding code from running if content 1.160 + // script is disabled or if the source domain is blacklisted (since the 1.161 + // source domain for remote XBL must always be the same as the source domain 1.162 + // of the bound content). 1.163 + // 1.164 + // If we just ask the binding document if script is enabled, it will 1.165 + // discover that it has no inner window, and return false. So instead, we 1.166 + // short-circuit the normal compartment-managed script-disabling machinery, 1.167 + // and query the policy for the URI directly. 1.168 + bool allow; 1.169 + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); 1.170 + nsresult rv = ssm->PolicyAllowsScript(uri, &allow); 1.171 + mScriptAccess = NS_SUCCEEDED(rv) && allow; 1.172 + } 1.173 +} 1.174 + 1.175 +nsXBLDocumentInfo::~nsXBLDocumentInfo() 1.176 +{ 1.177 + mozilla::DropJSObjects(this); 1.178 +} 1.179 + 1.180 +nsXBLPrototypeBinding* 1.181 +nsXBLDocumentInfo::GetPrototypeBinding(const nsACString& aRef) 1.182 +{ 1.183 + if (!mBindingTable) 1.184 + return nullptr; 1.185 + 1.186 + if (aRef.IsEmpty()) { 1.187 + // Return our first binding 1.188 + return mFirstBinding; 1.189 + } 1.190 + 1.191 + return mBindingTable->Get(aRef); 1.192 +} 1.193 + 1.194 +nsresult 1.195 +nsXBLDocumentInfo::SetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding* aBinding) 1.196 +{ 1.197 + if (!mBindingTable) { 1.198 + mBindingTable = new nsClassHashtable<nsCStringHashKey, nsXBLPrototypeBinding>(); 1.199 + mozilla::HoldJSObjects(this); 1.200 + } 1.201 + 1.202 + NS_ENSURE_STATE(!mBindingTable->Get(aRef)); 1.203 + mBindingTable->Put(aRef, aBinding); 1.204 + 1.205 + return NS_OK; 1.206 +} 1.207 + 1.208 +void 1.209 +nsXBLDocumentInfo::RemovePrototypeBinding(const nsACString& aRef) 1.210 +{ 1.211 + if (mBindingTable) { 1.212 + nsAutoPtr<nsXBLPrototypeBinding> bindingToRemove; 1.213 + mBindingTable->RemoveAndForget(aRef, bindingToRemove); 1.214 + 1.215 + // We do not want to destroy the binding, so just forget it. 1.216 + bindingToRemove.forget(); 1.217 + } 1.218 +} 1.219 + 1.220 +// Callback to enumerate over the bindings from this document and write them 1.221 +// out to the cache. 1.222 +static PLDHashOperator 1.223 +WriteBinding(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) 1.224 +{ 1.225 + aProto->Write((nsIObjectOutputStream*)aClosure); 1.226 + 1.227 + return PL_DHASH_NEXT; 1.228 +} 1.229 + 1.230 +// static 1.231 +nsresult 1.232 +nsXBLDocumentInfo::ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo) 1.233 +{ 1.234 + *aDocInfo = nullptr; 1.235 + 1.236 + nsAutoCString spec(kXBLCachePrefix); 1.237 + nsresult rv = PathifyURI(aURI, spec); 1.238 + NS_ENSURE_SUCCESS(rv, rv); 1.239 + 1.240 + StartupCache* startupCache = StartupCache::GetSingleton(); 1.241 + NS_ENSURE_TRUE(startupCache, NS_ERROR_FAILURE); 1.242 + 1.243 + nsAutoArrayPtr<char> buf; 1.244 + uint32_t len; 1.245 + rv = startupCache->GetBuffer(spec.get(), getter_Transfers(buf), &len); 1.246 + // GetBuffer will fail if the binding is not in the cache. 1.247 + if (NS_FAILED(rv)) 1.248 + return rv; 1.249 + 1.250 + nsCOMPtr<nsIObjectInputStream> stream; 1.251 + rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(stream)); 1.252 + NS_ENSURE_SUCCESS(rv, rv); 1.253 + buf.forget(); 1.254 + 1.255 + // The file compatibility.ini stores the build id. This is checked in 1.256 + // nsAppRunner.cpp and will delete the cache if a different build is 1.257 + // present. However, we check that the version matches here to be safe. 1.258 + uint32_t version; 1.259 + rv = stream->Read32(&version); 1.260 + NS_ENSURE_SUCCESS(rv, rv); 1.261 + if (version != XBLBinding_Serialize_Version) { 1.262 + // The version that exists is different than expected, likely created with a 1.263 + // different build, so invalidate the cache. 1.264 + startupCache->InvalidateCache(); 1.265 + return NS_ERROR_NOT_AVAILABLE; 1.266 + } 1.267 + 1.268 + nsCOMPtr<nsIPrincipal> principal; 1.269 + nsContentUtils::GetSecurityManager()-> 1.270 + GetSystemPrincipal(getter_AddRefs(principal)); 1.271 + 1.272 + nsCOMPtr<nsIDOMDocument> domdoc; 1.273 + rv = NS_NewXBLDocument(getter_AddRefs(domdoc), aURI, nullptr, principal); 1.274 + NS_ENSURE_SUCCESS(rv, rv); 1.275 + 1.276 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc); 1.277 + NS_ASSERTION(doc, "Must have a document!"); 1.278 + nsRefPtr<nsXBLDocumentInfo> docInfo = new nsXBLDocumentInfo(doc); 1.279 + 1.280 + while (1) { 1.281 + uint8_t flags; 1.282 + nsresult rv = stream->Read8(&flags); 1.283 + NS_ENSURE_SUCCESS(rv, rv); 1.284 + if (flags == XBLBinding_Serialize_NoMoreBindings) 1.285 + break; 1.286 + 1.287 + rv = nsXBLPrototypeBinding::ReadNewBinding(stream, docInfo, doc, flags); 1.288 + if (NS_FAILED(rv)) { 1.289 + return rv; 1.290 + } 1.291 + } 1.292 + 1.293 + docInfo.swap(*aDocInfo); 1.294 + return NS_OK; 1.295 +} 1.296 + 1.297 +nsresult 1.298 +nsXBLDocumentInfo::WritePrototypeBindings() 1.299 +{ 1.300 + // Only write out bindings with the system principal 1.301 + if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) 1.302 + return NS_OK; 1.303 + 1.304 + nsAutoCString spec(kXBLCachePrefix); 1.305 + nsresult rv = PathifyURI(DocumentURI(), spec); 1.306 + NS_ENSURE_SUCCESS(rv, rv); 1.307 + 1.308 + StartupCache* startupCache = StartupCache::GetSingleton(); 1.309 + NS_ENSURE_TRUE(startupCache, rv); 1.310 + 1.311 + nsCOMPtr<nsIObjectOutputStream> stream; 1.312 + nsCOMPtr<nsIStorageStream> storageStream; 1.313 + rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(stream), 1.314 + getter_AddRefs(storageStream), 1.315 + true); 1.316 + NS_ENSURE_SUCCESS(rv, rv); 1.317 + 1.318 + rv = stream->Write32(XBLBinding_Serialize_Version); 1.319 + NS_ENSURE_SUCCESS(rv, rv); 1.320 + 1.321 + if (mBindingTable) { 1.322 + mBindingTable->EnumerateRead(WriteBinding, stream); 1.323 + } 1.324 + 1.325 + // write a end marker at the end 1.326 + rv = stream->Write8(XBLBinding_Serialize_NoMoreBindings); 1.327 + NS_ENSURE_SUCCESS(rv, rv); 1.328 + 1.329 + stream->Close(); 1.330 + NS_ENSURE_SUCCESS(rv, rv); 1.331 + 1.332 + uint32_t len; 1.333 + nsAutoArrayPtr<char> buf; 1.334 + rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len); 1.335 + NS_ENSURE_SUCCESS(rv, rv); 1.336 + 1.337 + return startupCache->PutBuffer(spec.get(), buf, len); 1.338 +} 1.339 + 1.340 +void 1.341 +nsXBLDocumentInfo::SetFirstPrototypeBinding(nsXBLPrototypeBinding* aBinding) 1.342 +{ 1.343 + mFirstBinding = aBinding; 1.344 +} 1.345 + 1.346 +static PLDHashOperator 1.347 +FlushScopedSkinSheets(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) 1.348 +{ 1.349 + aProto->FlushSkinSheets(); 1.350 + return PL_DHASH_NEXT; 1.351 +} 1.352 + 1.353 +void 1.354 +nsXBLDocumentInfo::FlushSkinStylesheets() 1.355 +{ 1.356 + if (mBindingTable) { 1.357 + mBindingTable->EnumerateRead(FlushScopedSkinSheets, nullptr); 1.358 + } 1.359 +} 1.360 + 1.361 +#ifdef DEBUG 1.362 +void 1.363 +AssertInCompilationScope() 1.364 +{ 1.365 + AutoJSContext cx; 1.366 + // Note - Inverting the order of these operands is a rooting hazard. 1.367 + MOZ_ASSERT(xpc::GetCompilationScope() == JS::CurrentGlobalOrNull(cx)); 1.368 +} 1.369 +#endif