js/xpconnect/loader/mozJSSubScriptLoader.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "mozJSSubScriptLoader.h"
michael@0 8 #include "mozJSComponentLoader.h"
michael@0 9 #include "mozJSLoaderUtils.h"
michael@0 10
michael@0 11 #include "nsIURI.h"
michael@0 12 #include "nsIIOService.h"
michael@0 13 #include "nsIChannel.h"
michael@0 14 #include "nsIInputStream.h"
michael@0 15 #include "nsNetCID.h"
michael@0 16 #include "nsNetUtil.h"
michael@0 17 #include "nsIFileURL.h"
michael@0 18 #include "nsScriptLoader.h"
michael@0 19 #include "nsIScriptSecurityManager.h"
michael@0 20 #include "nsThreadUtils.h"
michael@0 21
michael@0 22 #include "jsapi.h"
michael@0 23 #include "jsfriendapi.h"
michael@0 24 #include "js/OldDebugAPI.h"
michael@0 25 #include "nsJSPrincipals.h"
michael@0 26 #include "xpcpublic.h" // For xpc::SystemErrorReporter
michael@0 27 #include "xpcprivate.h" // For xpc::OptionsBase
michael@0 28 #include "jswrapper.h"
michael@0 29
michael@0 30 #include "mozilla/scache/StartupCache.h"
michael@0 31 #include "mozilla/scache/StartupCacheUtils.h"
michael@0 32 #include "mozilla/unused.h"
michael@0 33
michael@0 34 using namespace mozilla::scache;
michael@0 35 using namespace JS;
michael@0 36 using namespace xpc;
michael@0 37 using namespace mozilla;
michael@0 38
michael@0 39 class MOZ_STACK_CLASS LoadSubScriptOptions : public OptionsBase {
michael@0 40 public:
michael@0 41 LoadSubScriptOptions(JSContext *cx = xpc_GetSafeJSContext(),
michael@0 42 JSObject *options = nullptr)
michael@0 43 : OptionsBase(cx, options)
michael@0 44 , target(cx)
michael@0 45 , charset(NullString())
michael@0 46 , ignoreCache(false)
michael@0 47 { }
michael@0 48
michael@0 49 virtual bool Parse() {
michael@0 50 return ParseObject("target", &target) &&
michael@0 51 ParseString("charset", charset) &&
michael@0 52 ParseBoolean("ignoreCache", &ignoreCache);
michael@0 53 }
michael@0 54
michael@0 55 RootedObject target;
michael@0 56 nsString charset;
michael@0 57 bool ignoreCache;
michael@0 58 };
michael@0 59
michael@0 60
michael@0 61 /* load() error msgs, XXX localize? */
michael@0 62 #define LOAD_ERROR_NOSERVICE "Error creating IO Service."
michael@0 63 #define LOAD_ERROR_NOURI "Error creating URI (invalid URL scheme?)"
michael@0 64 #define LOAD_ERROR_NOSCHEME "Failed to get URI scheme. This is bad."
michael@0 65 #define LOAD_ERROR_URI_NOT_LOCAL "Trying to load a non-local URI."
michael@0 66 #define LOAD_ERROR_NOSTREAM "Error opening input stream (invalid filename?)"
michael@0 67 #define LOAD_ERROR_NOCONTENT "ContentLength not available (not a local URL?)"
michael@0 68 #define LOAD_ERROR_BADCHARSET "Error converting to specified charset"
michael@0 69 #define LOAD_ERROR_BADREAD "File Read Error."
michael@0 70 #define LOAD_ERROR_READUNDERFLOW "File Read Error (underflow.)"
michael@0 71 #define LOAD_ERROR_NOPRINCIPALS "Failed to get principals."
michael@0 72 #define LOAD_ERROR_NOSPEC "Failed to get URI spec. This is bad."
michael@0 73 #define LOAD_ERROR_CONTENTTOOBIG "ContentLength is too large"
michael@0 74
michael@0 75 mozJSSubScriptLoader::mozJSSubScriptLoader() : mSystemPrincipal(nullptr)
michael@0 76 {
michael@0 77 // Force construction of the JS component loader. We may need it later.
michael@0 78 nsCOMPtr<xpcIJSModuleLoader> componentLoader =
michael@0 79 do_GetService(MOZJSCOMPONENTLOADER_CONTRACTID);
michael@0 80 }
michael@0 81
michael@0 82 mozJSSubScriptLoader::~mozJSSubScriptLoader()
michael@0 83 {
michael@0 84 /* empty */
michael@0 85 }
michael@0 86
michael@0 87 NS_IMPL_ISUPPORTS(mozJSSubScriptLoader, mozIJSSubScriptLoader)
michael@0 88
michael@0 89 static nsresult
michael@0 90 ReportError(JSContext *cx, const char *msg)
michael@0 91 {
michael@0 92 RootedValue exn(cx, JS::StringValue(JS_NewStringCopyZ(cx, msg)));
michael@0 93 JS_SetPendingException(cx, exn);
michael@0 94 return NS_OK;
michael@0 95 }
michael@0 96
michael@0 97 nsresult
michael@0 98 mozJSSubScriptLoader::ReadScript(nsIURI *uri, JSContext *cx, JSObject *targetObjArg,
michael@0 99 const nsAString &charset, const char *uriStr,
michael@0 100 nsIIOService *serv, nsIPrincipal *principal,
michael@0 101 bool reuseGlobal, JS::MutableHandleScript script,
michael@0 102 JS::MutableHandleFunction function)
michael@0 103 {
michael@0 104 RootedObject target_obj(cx, targetObjArg);
michael@0 105
michael@0 106 script.set(nullptr);
michael@0 107 function.set(nullptr);
michael@0 108
michael@0 109 // Instead of calling NS_OpenURI, we create the channel ourselves and call
michael@0 110 // SetContentType, to avoid expensive MIME type lookups (bug 632490).
michael@0 111 nsCOMPtr<nsIChannel> chan;
michael@0 112 nsCOMPtr<nsIInputStream> instream;
michael@0 113 nsresult rv = NS_NewChannel(getter_AddRefs(chan), uri, serv,
michael@0 114 nullptr, nullptr, nsIRequest::LOAD_NORMAL);
michael@0 115 if (NS_SUCCEEDED(rv)) {
michael@0 116 chan->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
michael@0 117 rv = chan->Open(getter_AddRefs(instream));
michael@0 118 }
michael@0 119
michael@0 120 if (NS_FAILED(rv)) {
michael@0 121 return ReportError(cx, LOAD_ERROR_NOSTREAM);
michael@0 122 }
michael@0 123
michael@0 124 int64_t len = -1;
michael@0 125
michael@0 126 rv = chan->GetContentLength(&len);
michael@0 127 if (NS_FAILED(rv) || len == -1) {
michael@0 128 return ReportError(cx, LOAD_ERROR_NOCONTENT);
michael@0 129 }
michael@0 130
michael@0 131 if (len > INT32_MAX) {
michael@0 132 return ReportError(cx, LOAD_ERROR_CONTENTTOOBIG);
michael@0 133 }
michael@0 134
michael@0 135 nsCString buf;
michael@0 136 rv = NS_ReadInputStreamToString(instream, buf, len);
michael@0 137 if (NS_FAILED(rv))
michael@0 138 return rv;
michael@0 139
michael@0 140 /* set our own error reporter so we can report any bad things as catchable
michael@0 141 * exceptions, including the source/line number */
michael@0 142 JSErrorReporter er = JS_SetErrorReporter(cx, xpc::SystemErrorReporter);
michael@0 143
michael@0 144 JS::CompileOptions options(cx);
michael@0 145 options.setFileAndLine(uriStr, 1);
michael@0 146 if (!charset.IsVoid()) {
michael@0 147 jschar *scriptBuf = nullptr;
michael@0 148 size_t scriptLength = 0;
michael@0 149
michael@0 150 rv = nsScriptLoader::ConvertToUTF16(nullptr, reinterpret_cast<const uint8_t*>(buf.get()), len,
michael@0 151 charset, nullptr, scriptBuf, scriptLength);
michael@0 152
michael@0 153 JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength,
michael@0 154 JS::SourceBufferHolder::GiveOwnership);
michael@0 155
michael@0 156 if (NS_FAILED(rv)) {
michael@0 157 return ReportError(cx, LOAD_ERROR_BADCHARSET);
michael@0 158 }
michael@0 159
michael@0 160 if (!reuseGlobal) {
michael@0 161 JS::Compile(cx, target_obj, options, srcBuf, script);
michael@0 162 } else {
michael@0 163 JS::CompileFunction(cx, target_obj, options,
michael@0 164 nullptr, 0, nullptr,
michael@0 165 srcBuf,
michael@0 166 function);
michael@0 167 }
michael@0 168 } else {
michael@0 169 // We only use lazy source when no special encoding is specified because
michael@0 170 // the lazy source loader doesn't know the encoding.
michael@0 171 if (!reuseGlobal) {
michael@0 172 options.setSourceIsLazy(true);
michael@0 173 script.set(JS::Compile(cx, target_obj, options, buf.get(), len));
michael@0 174 } else {
michael@0 175 function.set(JS::CompileFunction(cx, target_obj, options,
michael@0 176 nullptr, 0, nullptr, buf.get(),
michael@0 177 len));
michael@0 178 }
michael@0 179 }
michael@0 180
michael@0 181 /* repent for our evil deeds */
michael@0 182 JS_SetErrorReporter(cx, er);
michael@0 183
michael@0 184 return NS_OK;
michael@0 185 }
michael@0 186
michael@0 187 NS_IMETHODIMP
michael@0 188 mozJSSubScriptLoader::LoadSubScript(const nsAString &url,
michael@0 189 HandleValue target,
michael@0 190 const nsAString &charset,
michael@0 191 JSContext *cx,
michael@0 192 MutableHandleValue retval)
michael@0 193 {
michael@0 194 /*
michael@0 195 * Loads a local url and evals it into the current cx
michael@0 196 * Synchronous (an async version would be cool too.)
michael@0 197 * url: The url to load. Must be local so that it can be loaded
michael@0 198 * synchronously.
michael@0 199 * target_obj: Optional object to eval the script onto (defaults to context
michael@0 200 * global)
michael@0 201 * charset: Optional character set to use for reading
michael@0 202 * returns: Whatever jsval the script pointed to by the url returns.
michael@0 203 * Should ONLY (O N L Y !) be called from JavaScript code.
michael@0 204 */
michael@0 205 LoadSubScriptOptions options(cx);
michael@0 206 options.charset = charset;
michael@0 207 options.target = target.isObject() ? &target.toObject() : nullptr;
michael@0 208 return DoLoadSubScriptWithOptions(url, options, cx, retval);
michael@0 209 }
michael@0 210
michael@0 211
michael@0 212 NS_IMETHODIMP
michael@0 213 mozJSSubScriptLoader::LoadSubScriptWithOptions(const nsAString &url,
michael@0 214 HandleValue optionsVal,
michael@0 215 JSContext *cx,
michael@0 216 MutableHandleValue retval)
michael@0 217 {
michael@0 218 if (!optionsVal.isObject())
michael@0 219 return NS_ERROR_INVALID_ARG;
michael@0 220 LoadSubScriptOptions options(cx, &optionsVal.toObject());
michael@0 221 if (!options.Parse())
michael@0 222 return NS_ERROR_INVALID_ARG;
michael@0 223 return DoLoadSubScriptWithOptions(url, options, cx, retval);
michael@0 224 }
michael@0 225
michael@0 226 nsresult
michael@0 227 mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString &url,
michael@0 228 LoadSubScriptOptions &options,
michael@0 229 JSContext *cx,
michael@0 230 MutableHandleValue retval)
michael@0 231 {
michael@0 232 nsresult rv = NS_OK;
michael@0 233
michael@0 234 /* set the system principal if it's not here already */
michael@0 235 if (!mSystemPrincipal) {
michael@0 236 nsCOMPtr<nsIScriptSecurityManager> secman =
michael@0 237 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
michael@0 238 if (!secman)
michael@0 239 return NS_OK;
michael@0 240
michael@0 241 rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal));
michael@0 242 if (NS_FAILED(rv) || !mSystemPrincipal)
michael@0 243 return rv;
michael@0 244 }
michael@0 245
michael@0 246 RootedObject targetObj(cx);
michael@0 247 mozJSComponentLoader *loader = mozJSComponentLoader::Get();
michael@0 248 rv = loader->FindTargetObject(cx, &targetObj);
michael@0 249 NS_ENSURE_SUCCESS(rv, rv);
michael@0 250
michael@0 251 // We base reusingGlobal off of what the loader told us, but we may not
michael@0 252 // actually be using that object.
michael@0 253 bool reusingGlobal = !JS_IsGlobalObject(targetObj);
michael@0 254
michael@0 255 if (options.target)
michael@0 256 targetObj = options.target;
michael@0 257
michael@0 258 // Remember an object out of the calling compartment so that we
michael@0 259 // can properly wrap the result later.
michael@0 260 nsCOMPtr<nsIPrincipal> principal = mSystemPrincipal;
michael@0 261 RootedObject result_obj(cx, targetObj);
michael@0 262 targetObj = JS_FindCompilationScope(cx, targetObj);
michael@0 263 if (!targetObj)
michael@0 264 return NS_ERROR_FAILURE;
michael@0 265
michael@0 266 if (targetObj != result_obj)
michael@0 267 principal = GetObjectPrincipal(targetObj);
michael@0 268
michael@0 269 JSAutoCompartment ac(cx, targetObj);
michael@0 270
michael@0 271 /* load up the url. From here on, failures are reflected as ``custom''
michael@0 272 * js exceptions */
michael@0 273 nsCOMPtr<nsIURI> uri;
michael@0 274 nsAutoCString uriStr;
michael@0 275 nsAutoCString scheme;
michael@0 276
michael@0 277 // Figure out who's calling us
michael@0 278 JS::AutoFilename filename;
michael@0 279 if (!JS::DescribeScriptedCaller(cx, &filename)) {
michael@0 280 // No scripted frame means we don't know who's calling, bail.
michael@0 281 return NS_ERROR_FAILURE;
michael@0 282 }
michael@0 283
michael@0 284 // Suppress caching if we're compiling as content.
michael@0 285 StartupCache* cache = (principal == mSystemPrincipal)
michael@0 286 ? StartupCache::GetSingleton()
michael@0 287 : nullptr;
michael@0 288 nsCOMPtr<nsIIOService> serv = do_GetService(NS_IOSERVICE_CONTRACTID);
michael@0 289 if (!serv) {
michael@0 290 return ReportError(cx, LOAD_ERROR_NOSERVICE);
michael@0 291 }
michael@0 292
michael@0 293 // Make sure to explicitly create the URI, since we'll need the
michael@0 294 // canonicalized spec.
michael@0 295 rv = NS_NewURI(getter_AddRefs(uri), NS_LossyConvertUTF16toASCII(url).get(), nullptr, serv);
michael@0 296 if (NS_FAILED(rv)) {
michael@0 297 return ReportError(cx, LOAD_ERROR_NOURI);
michael@0 298 }
michael@0 299
michael@0 300 rv = uri->GetSpec(uriStr);
michael@0 301 if (NS_FAILED(rv)) {
michael@0 302 return ReportError(cx, LOAD_ERROR_NOSPEC);
michael@0 303 }
michael@0 304
michael@0 305 rv = uri->GetScheme(scheme);
michael@0 306 if (NS_FAILED(rv)) {
michael@0 307 return ReportError(cx, LOAD_ERROR_NOSCHEME);
michael@0 308 }
michael@0 309
michael@0 310 if (!scheme.EqualsLiteral("chrome")) {
michael@0 311 // This might be a URI to a local file, though!
michael@0 312 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
michael@0 313 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(innerURI);
michael@0 314 if (!fileURL) {
michael@0 315 return ReportError(cx, LOAD_ERROR_URI_NOT_LOCAL);
michael@0 316 }
michael@0 317
michael@0 318 // For file URIs prepend the filename with the filename of the
michael@0 319 // calling script, and " -> ". See bug 418356.
michael@0 320 nsAutoCString tmp(filename.get());
michael@0 321 tmp.AppendLiteral(" -> ");
michael@0 322 tmp.Append(uriStr);
michael@0 323
michael@0 324 uriStr = tmp;
michael@0 325 }
michael@0 326
michael@0 327 bool writeScript = false;
michael@0 328 JSVersion version = JS_GetVersion(cx);
michael@0 329 nsAutoCString cachePath;
michael@0 330 cachePath.AppendPrintf("jssubloader/%d", version);
michael@0 331 PathifyURI(uri, cachePath);
michael@0 332
michael@0 333 RootedFunction function(cx);
michael@0 334 RootedScript script(cx);
michael@0 335 if (cache && !options.ignoreCache)
michael@0 336 rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
michael@0 337 if (!script) {
michael@0 338 rv = ReadScript(uri, cx, targetObj, options.charset,
michael@0 339 static_cast<const char*>(uriStr.get()), serv,
michael@0 340 principal, reusingGlobal, &script, &function);
michael@0 341 writeScript = !!script;
michael@0 342 }
michael@0 343
michael@0 344 if (NS_FAILED(rv) || (!script && !function))
michael@0 345 return rv;
michael@0 346
michael@0 347 if (function) {
michael@0 348 script = JS_GetFunctionScript(cx, function);
michael@0 349 }
michael@0 350
michael@0 351 loader->NoteSubScript(script, targetObj);
michael@0 352
michael@0 353
michael@0 354 bool ok = false;
michael@0 355 if (function) {
michael@0 356 ok = JS_CallFunction(cx, targetObj, function, JS::HandleValueArray::empty(),
michael@0 357 retval);
michael@0 358 } else {
michael@0 359 ok = JS_ExecuteScriptVersion(cx, targetObj, script, retval, version);
michael@0 360 }
michael@0 361
michael@0 362 if (ok) {
michael@0 363 JSAutoCompartment rac(cx, result_obj);
michael@0 364 if (!JS_WrapValue(cx, retval))
michael@0 365 return NS_ERROR_UNEXPECTED;
michael@0 366 }
michael@0 367
michael@0 368 if (cache && ok && writeScript) {
michael@0 369 WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script);
michael@0 370 }
michael@0 371
michael@0 372 return NS_OK;
michael@0 373 }
michael@0 374
michael@0 375 /**
michael@0 376 * Let us compile scripts from a URI off the main thread.
michael@0 377 */
michael@0 378
michael@0 379 class ScriptPrecompiler : public nsIStreamLoaderObserver
michael@0 380 {
michael@0 381 public:
michael@0 382 NS_DECL_ISUPPORTS
michael@0 383 NS_DECL_NSISTREAMLOADEROBSERVER
michael@0 384
michael@0 385 ScriptPrecompiler(nsIObserver* aObserver,
michael@0 386 nsIPrincipal* aPrincipal,
michael@0 387 nsIChannel* aChannel)
michael@0 388 : mObserver(aObserver)
michael@0 389 , mPrincipal(aPrincipal)
michael@0 390 , mChannel(aChannel)
michael@0 391 , mScriptBuf(nullptr)
michael@0 392 , mScriptLength(0)
michael@0 393 {}
michael@0 394
michael@0 395 virtual ~ScriptPrecompiler()
michael@0 396 {
michael@0 397 if (mScriptBuf) {
michael@0 398 js_free(mScriptBuf);
michael@0 399 }
michael@0 400 }
michael@0 401
michael@0 402 static void OffThreadCallback(void *aToken, void *aData);
michael@0 403
michael@0 404 /* Sends the "done" notification back. Main thread only. */
michael@0 405 void SendObserverNotification();
michael@0 406
michael@0 407 private:
michael@0 408 nsRefPtr<nsIObserver> mObserver;
michael@0 409 nsRefPtr<nsIPrincipal> mPrincipal;
michael@0 410 nsRefPtr<nsIChannel> mChannel;
michael@0 411 jschar* mScriptBuf;
michael@0 412 size_t mScriptLength;
michael@0 413 };
michael@0 414
michael@0 415 NS_IMPL_ISUPPORTS(ScriptPrecompiler, nsIStreamLoaderObserver);
michael@0 416
michael@0 417 class NotifyPrecompilationCompleteRunnable : public nsRunnable
michael@0 418 {
michael@0 419 public:
michael@0 420 NS_DECL_NSIRUNNABLE
michael@0 421
michael@0 422 NotifyPrecompilationCompleteRunnable(ScriptPrecompiler* aPrecompiler)
michael@0 423 : mPrecompiler(aPrecompiler)
michael@0 424 , mToken(nullptr)
michael@0 425 {}
michael@0 426
michael@0 427 void SetToken(void* aToken) {
michael@0 428 MOZ_ASSERT(aToken && !mToken);
michael@0 429 mToken = aToken;
michael@0 430 }
michael@0 431
michael@0 432 protected:
michael@0 433 nsRefPtr<ScriptPrecompiler> mPrecompiler;
michael@0 434 void* mToken;
michael@0 435 };
michael@0 436
michael@0 437 /* RAII helper class to send observer notifications */
michael@0 438 class AutoSendObserverNotification {
michael@0 439 public:
michael@0 440 AutoSendObserverNotification(ScriptPrecompiler* aPrecompiler)
michael@0 441 : mPrecompiler(aPrecompiler)
michael@0 442 {}
michael@0 443
michael@0 444 ~AutoSendObserverNotification() {
michael@0 445 if (mPrecompiler) {
michael@0 446 mPrecompiler->SendObserverNotification();
michael@0 447 }
michael@0 448 }
michael@0 449
michael@0 450 void Disarm() {
michael@0 451 mPrecompiler = nullptr;
michael@0 452 }
michael@0 453
michael@0 454 private:
michael@0 455 ScriptPrecompiler* mPrecompiler;
michael@0 456 };
michael@0 457
michael@0 458 NS_IMETHODIMP
michael@0 459 NotifyPrecompilationCompleteRunnable::Run(void)
michael@0 460 {
michael@0 461 MOZ_ASSERT(NS_IsMainThread());
michael@0 462 MOZ_ASSERT(mPrecompiler);
michael@0 463
michael@0 464 AutoSendObserverNotification notifier(mPrecompiler);
michael@0 465
michael@0 466 if (mToken) {
michael@0 467 JSRuntime *rt = XPCJSRuntime::Get()->Runtime();
michael@0 468 NS_ENSURE_TRUE(rt, NS_ERROR_FAILURE);
michael@0 469 JS::FinishOffThreadScript(nullptr, rt, mToken);
michael@0 470 }
michael@0 471
michael@0 472 return NS_OK;
michael@0 473 }
michael@0 474
michael@0 475 NS_IMETHODIMP
michael@0 476 ScriptPrecompiler::OnStreamComplete(nsIStreamLoader* aLoader,
michael@0 477 nsISupports* aContext,
michael@0 478 nsresult aStatus,
michael@0 479 uint32_t aLength,
michael@0 480 const uint8_t* aString)
michael@0 481 {
michael@0 482 AutoSendObserverNotification notifier(this);
michael@0 483
michael@0 484 // Just notify that we are done with this load.
michael@0 485 NS_ENSURE_SUCCESS(aStatus, NS_OK);
michael@0 486
michael@0 487 // Convert data to jschar* and prepare to call CompileOffThread.
michael@0 488 nsAutoString hintCharset;
michael@0 489 nsresult rv =
michael@0 490 nsScriptLoader::ConvertToUTF16(mChannel, aString, aLength,
michael@0 491 hintCharset, nullptr,
michael@0 492 mScriptBuf, mScriptLength);
michael@0 493
michael@0 494 NS_ENSURE_SUCCESS(rv, NS_OK);
michael@0 495
michael@0 496 // Our goal is to cache persistently the compiled script and to avoid quota
michael@0 497 // checks. Since the caching mechanism decide the persistence type based on
michael@0 498 // the principal, we create a new global with the app's principal.
michael@0 499 // We then enter its compartment to compile with its principal.
michael@0 500 AutoSafeJSContext cx;
michael@0 501 RootedValue v(cx);
michael@0 502 SandboxOptions sandboxOptions;
michael@0 503 sandboxOptions.sandboxName.AssignASCII("asm.js precompilation");
michael@0 504 sandboxOptions.invisibleToDebugger = true;
michael@0 505 sandboxOptions.discardSource = true;
michael@0 506 rv = CreateSandboxObject(cx, &v, mPrincipal, sandboxOptions);
michael@0 507 NS_ENSURE_SUCCESS(rv, NS_OK);
michael@0 508
michael@0 509 JSAutoCompartment ac(cx, js::UncheckedUnwrap(&v.toObject()));
michael@0 510
michael@0 511 JS::CompileOptions options(cx, JSVERSION_DEFAULT);
michael@0 512 options.forceAsync = true;
michael@0 513 options.compileAndGo = true;
michael@0 514 options.installedFile = true;
michael@0 515
michael@0 516 nsCOMPtr<nsIURI> uri;
michael@0 517 mChannel->GetURI(getter_AddRefs(uri));
michael@0 518 nsAutoCString spec;
michael@0 519 uri->GetSpec(spec);
michael@0 520 options.setFile(spec.get());
michael@0 521
michael@0 522 if (!JS::CanCompileOffThread(cx, options, mScriptLength)) {
michael@0 523 NS_WARNING("Can't compile script off thread!");
michael@0 524 return NS_OK;
michael@0 525 }
michael@0 526
michael@0 527 nsRefPtr<NotifyPrecompilationCompleteRunnable> runnable =
michael@0 528 new NotifyPrecompilationCompleteRunnable(this);
michael@0 529
michael@0 530 if (!JS::CompileOffThread(cx, options,
michael@0 531 mScriptBuf, mScriptLength,
michael@0 532 OffThreadCallback,
michael@0 533 static_cast<void*>(runnable))) {
michael@0 534 NS_WARNING("Failed to compile script off thread!");
michael@0 535 return NS_OK;
michael@0 536 }
michael@0 537
michael@0 538 unused << runnable.forget();
michael@0 539 notifier.Disarm();
michael@0 540
michael@0 541 return NS_OK;
michael@0 542 }
michael@0 543
michael@0 544 /* static */
michael@0 545 void
michael@0 546 ScriptPrecompiler::OffThreadCallback(void* aToken, void* aData)
michael@0 547 {
michael@0 548 nsRefPtr<NotifyPrecompilationCompleteRunnable> runnable =
michael@0 549 dont_AddRef(static_cast<NotifyPrecompilationCompleteRunnable*>(aData));
michael@0 550 runnable->SetToken(aToken);
michael@0 551
michael@0 552 NS_DispatchToMainThread(runnable);
michael@0 553 }
michael@0 554
michael@0 555 void
michael@0 556 ScriptPrecompiler::SendObserverNotification()
michael@0 557 {
michael@0 558 MOZ_ASSERT(mChannel && mObserver);
michael@0 559 MOZ_ASSERT(NS_IsMainThread());
michael@0 560
michael@0 561 nsCOMPtr<nsIURI> uri;
michael@0 562 mChannel->GetURI(getter_AddRefs(uri));
michael@0 563 mObserver->Observe(uri, "script-precompiled", nullptr);
michael@0 564 }
michael@0 565
michael@0 566 NS_IMETHODIMP
michael@0 567 mozJSSubScriptLoader::PrecompileScript(nsIURI* aURI,
michael@0 568 nsIPrincipal* aPrincipal,
michael@0 569 nsIObserver *aObserver)
michael@0 570 {
michael@0 571 nsCOMPtr<nsIChannel> channel;
michael@0 572 nsresult rv = NS_NewChannel(getter_AddRefs(channel),
michael@0 573 aURI, nullptr, nullptr, nullptr,
michael@0 574 nsIRequest::LOAD_NORMAL, nullptr);
michael@0 575 NS_ENSURE_SUCCESS(rv, rv);
michael@0 576
michael@0 577 nsRefPtr<ScriptPrecompiler> loadObserver =
michael@0 578 new ScriptPrecompiler(aObserver, aPrincipal, channel);
michael@0 579
michael@0 580 nsCOMPtr<nsIStreamLoader> loader;
michael@0 581 rv = NS_NewStreamLoader(getter_AddRefs(loader), loadObserver);
michael@0 582 NS_ENSURE_SUCCESS(rv, rv);
michael@0 583
michael@0 584 nsCOMPtr<nsIStreamListener> listener = loader.get();
michael@0 585 rv = channel->AsyncOpen(listener, nullptr);
michael@0 586 NS_ENSURE_SUCCESS(rv, rv);
michael@0 587
michael@0 588 return NS_OK;
michael@0 589 }

mercurial