nsJARChannel.cpp (40499B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et tw=80 : */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/BrowsingContext.h" 8 #include "nsJAR.h" 9 #include "nsJARChannel.h" 10 #include "nsJARProtocolHandler.h" 11 #include "nsMimeTypes.h" 12 #include "nsNetUtil.h" 13 #include "nsEscape.h" 14 #include "nsContentUtils.h" 15 #include "nsProxyRelease.h" 16 #include "nsContentSecurityManager.h" 17 #include "nsComponentManagerUtils.h" 18 19 #include "nsIFileURL.h" 20 #include "nsIURIMutator.h" 21 22 #include "mozilla/BasePrincipal.h" 23 #include "mozilla/ErrorNames.h" 24 #include "mozilla/IntegerPrintfMacros.h" 25 #include "mozilla/Preferences.h" 26 #include "mozilla/ScopeExit.h" 27 #include "mozilla/StaticPrefs_network.h" 28 #include "mozilla/glean/LibjarMetrics.h" 29 #include "private/pprio.h" 30 #include "nsInputStreamPump.h" 31 #include "nsThreadUtils.h" 32 #include "nsJARProtocolHandler.h" 33 34 using namespace mozilla; 35 using namespace mozilla::net; 36 37 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID); 38 39 // the entry for a directory will either be empty (in the case of the 40 // top-level directory) or will end with a slash 41 #define ENTRY_IS_DIRECTORY(_entry) \ 42 ((_entry).IsEmpty() || '/' == (_entry).Last()) 43 44 //----------------------------------------------------------------------------- 45 46 // 47 // set MOZ_LOG=nsJarProtocol:5 48 // 49 static LazyLogModule gJarProtocolLog("nsJarProtocol"); 50 51 // Ignore any LOG macro that we inherit from arbitrary headers. (We define our 52 // own LOG macro below.) 53 #ifdef LOG 54 # undef LOG 55 #endif 56 #ifdef LOG_ENABLED 57 # undef LOG_ENABLED 58 #endif 59 60 #define LOG(args) MOZ_LOG(gJarProtocolLog, mozilla::LogLevel::Debug, args) 61 #define LOG_ENABLED() MOZ_LOG_TEST(gJarProtocolLog, mozilla::LogLevel::Debug) 62 63 //----------------------------------------------------------------------------- 64 // nsJARInputThunk 65 // 66 // this class allows us to do some extra work on the stream transport thread. 67 //----------------------------------------------------------------------------- 68 69 class nsJARInputThunk : public nsIInputStream { 70 public: 71 NS_DECL_THREADSAFE_ISUPPORTS 72 NS_DECL_NSIINPUTSTREAM 73 74 nsJARInputThunk(nsIZipReader* zipReader, const nsACString& jarEntry, 75 bool usingJarCache) 76 : mUsingJarCache(usingJarCache), 77 mJarReader(zipReader), 78 mJarEntry(jarEntry), 79 mContentLength(-1) { 80 MOZ_DIAGNOSTIC_ASSERT(zipReader, "zipReader must not be null"); 81 } 82 83 int64_t GetContentLength() { return mContentLength; } 84 85 nsresult Init(); 86 87 private: 88 virtual ~nsJARInputThunk() { Close(); } 89 90 bool mUsingJarCache; 91 nsCOMPtr<nsIZipReader> mJarReader; 92 nsCOMPtr<nsIInputStream> mJarStream; 93 nsCString mJarEntry; 94 int64_t mContentLength; 95 }; 96 97 NS_IMPL_ISUPPORTS(nsJARInputThunk, nsIInputStream) 98 99 nsresult nsJARInputThunk::Init() { 100 if (!mJarReader) { 101 return NS_ERROR_INVALID_ARG; 102 } 103 nsresult rv = 104 mJarReader->GetInputStream(mJarEntry, getter_AddRefs(mJarStream)); 105 if (NS_FAILED(rv)) { 106 return rv; 107 } 108 109 // ask the JarStream for the content length 110 uint64_t avail; 111 rv = mJarStream->Available((uint64_t*)&avail); 112 if (NS_FAILED(rv)) return rv; 113 114 mContentLength = avail < INT64_MAX ? (int64_t)avail : -1; 115 116 return NS_OK; 117 } 118 119 NS_IMETHODIMP 120 nsJARInputThunk::Close() { 121 nsresult rv = NS_OK; 122 123 if (mJarStream) rv = mJarStream->Close(); 124 125 if (!mUsingJarCache && mJarReader) mJarReader->Close(); 126 127 mJarReader = nullptr; 128 129 return rv; 130 } 131 132 NS_IMETHODIMP 133 nsJARInputThunk::Available(uint64_t* avail) { 134 return mJarStream->Available(avail); 135 } 136 137 NS_IMETHODIMP 138 nsJARInputThunk::StreamStatus() { return mJarStream->StreamStatus(); } 139 140 NS_IMETHODIMP 141 nsJARInputThunk::Read(char* buf, uint32_t count, uint32_t* countRead) { 142 return mJarStream->Read(buf, count, countRead); 143 } 144 145 NS_IMETHODIMP 146 nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void* closure, 147 uint32_t count, uint32_t* countRead) { 148 // stream transport does only calls Read() 149 return NS_ERROR_NOT_IMPLEMENTED; 150 } 151 152 NS_IMETHODIMP 153 nsJARInputThunk::IsNonBlocking(bool* nonBlocking) { 154 *nonBlocking = false; 155 return NS_OK; 156 } 157 158 //----------------------------------------------------------------------------- 159 // nsJARChannel 160 //----------------------------------------------------------------------------- 161 162 nsJARChannel::nsJARChannel() { 163 LOG(("nsJARChannel::nsJARChannel [this=%p]\n", this)); 164 // hold an owning reference to the jar handler 165 mJarHandler = gJarHandler; 166 } 167 168 nsJARChannel::~nsJARChannel() { 169 LOG(("nsJARChannel::~nsJARChannel [this=%p]\n", this)); 170 if (NS_IsMainThread()) { 171 return; 172 } 173 174 // Proxy release the following members to main thread. 175 NS_ReleaseOnMainThread("nsJARChannel::mLoadInfo", mLoadInfo.forget()); 176 NS_ReleaseOnMainThread("nsJARChannel::mCallbacks", mCallbacks.forget()); 177 NS_ReleaseOnMainThread("nsJARChannel::mProgressSink", mProgressSink.forget()); 178 NS_ReleaseOnMainThread("nsJARChannel::mLoadGroup", mLoadGroup.forget()); 179 NS_ReleaseOnMainThread("nsJARChannel::mListener", mListener.forget()); 180 } 181 182 NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel, nsHashPropertyBag, nsIRequest, 183 nsIChannel, nsIStreamListener, nsIRequestObserver, 184 nsIThreadRetargetableRequest, 185 nsIThreadRetargetableStreamListener, nsIJARChannel) 186 187 nsresult nsJARChannel::Init(nsIURI* uri) { 188 LOG(("nsJARChannel::Init [this=%p]\n", this)); 189 nsresult rv; 190 191 mWorker = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); 192 if (NS_FAILED(rv)) { 193 return rv; 194 } 195 196 mJarURI = do_QueryInterface(uri, &rv); 197 if (NS_FAILED(rv)) return rv; 198 199 mOriginalURI = mJarURI; 200 201 // Prevent loading jar:javascript URIs (see bug 290982). 202 nsCOMPtr<nsIURI> innerURI; 203 rv = mJarURI->GetJARFile(getter_AddRefs(innerURI)); 204 if (NS_FAILED(rv)) { 205 return rv; 206 } 207 208 if (innerURI->SchemeIs("javascript")) { 209 NS_WARNING("blocking jar:javascript:"); 210 return NS_ERROR_INVALID_ARG; 211 } 212 213 mJarURI->GetSpec(mSpec); 214 return rv; 215 } 216 217 nsresult nsJARChannel::CreateJarInput(nsIZipReaderCache* jarCache, 218 nsJARInputThunk** resultInput) { 219 LOG(("nsJARChannel::CreateJarInput [this=%p]\n", this)); 220 MOZ_ASSERT(resultInput); 221 MOZ_ASSERT(mJarFile); 222 223 // important to pass a clone of the file since the nsIFile impl is not 224 // necessarily MT-safe 225 nsCOMPtr<nsIFile> clonedFile; 226 nsresult rv = NS_OK; 227 if (mJarFile) { 228 rv = mJarFile->Clone(getter_AddRefs(clonedFile)); 229 if (NS_FAILED(rv)) return rv; 230 } 231 232 nsCOMPtr<nsIZipReader> reader; 233 if (mPreCachedJarReader) { 234 reader = mPreCachedJarReader; 235 } else if (jarCache) { 236 if (mInnerJarEntry.IsEmpty()) 237 rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader)); 238 else 239 rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry, 240 getter_AddRefs(reader)); 241 } else { 242 // create an uncached jar reader 243 nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv); 244 if (NS_FAILED(rv)) return rv; 245 246 rv = outerReader->Open(clonedFile); 247 if (NS_FAILED(rv)) return rv; 248 249 if (mInnerJarEntry.IsEmpty()) 250 reader = outerReader; 251 else { 252 reader = do_CreateInstance(kZipReaderCID, &rv); 253 if (NS_FAILED(rv)) return rv; 254 255 rv = reader->OpenInner(outerReader, mInnerJarEntry); 256 } 257 } 258 if (NS_FAILED(rv)) return rv; 259 260 RefPtr<nsJARInputThunk> input = 261 new nsJARInputThunk(reader, mJarEntry, jarCache != nullptr); 262 rv = input->Init(); 263 if (NS_FAILED(rv)) { 264 return rv; 265 } 266 267 // Make GetContentLength meaningful 268 mContentLength = input->GetContentLength(); 269 270 input.forget(resultInput); 271 return NS_OK; 272 } 273 274 nsresult nsJARChannel::LookupFile() { 275 LOG(("nsJARChannel::LookupFile [this=%p %s]\n", this, mSpec.get())); 276 277 if (mJarFile) return NS_OK; 278 279 nsresult rv; 280 281 rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI)); 282 if (NS_FAILED(rv)) return rv; 283 284 rv = mJarURI->GetJAREntry(mJarEntry); 285 if (NS_FAILED(rv)) return rv; 286 287 // The name of the JAR entry must not contain URL-escaped characters: 288 // we're moving from URL domain to a filename domain here. nsStandardURL 289 // does basic escaping by default, which breaks reading zipped files which 290 // have e.g. spaces in their filenames. 291 NS_UnescapeURL(mJarEntry); 292 293 if (mJarFileOverride) { 294 mJarFile = mJarFileOverride; 295 LOG(("nsJARChannel::LookupFile [this=%p] Overriding mJarFile\n", this)); 296 return NS_OK; 297 } 298 299 // try to get a nsIFile directly from the url, which will often succeed. 300 { 301 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI); 302 if (fileURL) fileURL->GetFile(getter_AddRefs(mJarFile)); 303 } 304 305 // try to handle a nested jar 306 if (!mJarFile) { 307 nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI); 308 if (jarURI) { 309 nsCOMPtr<nsIFileURL> fileURL; 310 nsCOMPtr<nsIURI> innerJarURI; 311 rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI)); 312 if (NS_SUCCEEDED(rv)) fileURL = do_QueryInterface(innerJarURI); 313 if (fileURL) { 314 fileURL->GetFile(getter_AddRefs(mJarFile)); 315 jarURI->GetJAREntry(mInnerJarEntry); 316 } 317 } 318 } 319 320 return rv; 321 } 322 323 nsresult CreateLocalJarInput(nsIZipReaderCache* aJarCache, nsIFile* aFile, 324 const nsACString& aInnerJarEntry, 325 const nsACString& aJarEntry, 326 nsJARInputThunk** aResultInput) { 327 LOG(("nsJARChannel::CreateLocalJarInput [aJarCache=%p, %s, %s]\n", aJarCache, 328 PromiseFlatCString(aInnerJarEntry).get(), 329 PromiseFlatCString(aJarEntry).get())); 330 331 MOZ_ASSERT(!NS_IsMainThread()); 332 MOZ_ASSERT(aJarCache); 333 MOZ_ASSERT(aResultInput); 334 335 nsresult rv; 336 337 nsCOMPtr<nsIZipReader> reader; 338 if (aInnerJarEntry.IsEmpty()) { 339 rv = aJarCache->GetZip(aFile, getter_AddRefs(reader)); 340 } else { 341 rv = aJarCache->GetInnerZip(aFile, aInnerJarEntry, getter_AddRefs(reader)); 342 } 343 if (NS_WARN_IF(NS_FAILED(rv))) { 344 return rv; 345 } 346 347 RefPtr<nsJARInputThunk> input = 348 new nsJARInputThunk(reader, aJarEntry, aJarCache != nullptr); 349 rv = input->Init(); 350 if (NS_FAILED(rv)) { 351 return rv; 352 } 353 354 input.forget(aResultInput); 355 return NS_OK; 356 } 357 358 nsresult nsJARChannel::OpenLocalFile() { 359 LOG(("nsJARChannel::OpenLocalFile [this=%p]\n", this)); 360 361 MOZ_ASSERT(NS_IsMainThread()); 362 363 MOZ_ASSERT(mWorker); 364 MOZ_ASSERT(mIsPending); 365 MOZ_ASSERT(mJarFile); 366 367 nsresult rv; 368 369 // Set mLoadGroup and mOpened before AsyncOpen return, and set back if 370 // if failed when callback. 371 if (mLoadGroup) { 372 mLoadGroup->AddRequest(this, nullptr); 373 } 374 SetOpened(); 375 376 if (mPreCachedJarReader || !mEnableOMT) { 377 RefPtr<nsJARInputThunk> input; 378 rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input)); 379 if (NS_WARN_IF(NS_FAILED(rv))) { 380 return OnOpenLocalFileComplete(rv, true); 381 } 382 return ContinueOpenLocalFile(input, true); 383 } 384 385 nsCOMPtr<nsIZipReaderCache> jarCache = gJarHandler->JarCache(); 386 if (NS_WARN_IF(!jarCache)) { 387 return NS_ERROR_UNEXPECTED; 388 } 389 390 nsCOMPtr<nsIFile> clonedFile; 391 rv = mJarFile->Clone(getter_AddRefs(clonedFile)); 392 if (NS_WARN_IF(NS_FAILED(rv))) { 393 return rv; 394 } 395 396 nsAutoCString jarEntry(mJarEntry); 397 nsAutoCString innerJarEntry(mInnerJarEntry); 398 399 RefPtr<nsJARChannel> self = this; 400 return mWorker->Dispatch(NS_NewRunnableFunction( 401 "nsJARChannel::OpenLocalFile", 402 [self, jarCache, clonedFile, jarEntry, innerJarEntry]() mutable { 403 RefPtr<nsJARInputThunk> input; 404 nsresult rv = CreateLocalJarInput(jarCache, clonedFile, innerJarEntry, 405 jarEntry, getter_AddRefs(input)); 406 407 nsCOMPtr<nsIRunnable> target; 408 if (NS_SUCCEEDED(rv)) { 409 target = NewRunnableMethod<RefPtr<nsJARInputThunk>, bool>( 410 "nsJARChannel::ContinueOpenLocalFile", self, 411 &nsJARChannel::ContinueOpenLocalFile, input, false); 412 } else { 413 target = NewRunnableMethod<nsresult, bool>( 414 "nsJARChannel::OnOpenLocalFileComplete", self, 415 &nsJARChannel::OnOpenLocalFileComplete, rv, false); 416 } 417 418 // nsJARChannel must be release on main thread, and sometimes 419 // this still hold nsJARChannel after dispatched. 420 self = nullptr; 421 422 NS_DispatchToMainThread(target.forget()); 423 })); 424 } 425 426 nsresult nsJARChannel::ContinueOpenLocalFile(nsJARInputThunk* aInput, 427 bool aIsSyncCall) { 428 LOG(("nsJARChannel::ContinueOpenLocalFile [this=%p %p]\n", this, aInput)); 429 430 MOZ_ASSERT(NS_IsMainThread()); 431 MOZ_ASSERT(mIsPending); 432 433 // Make GetContentLength meaningful 434 mContentLength = aInput->GetContentLength(); 435 436 nsresult rv; 437 RefPtr<nsJARInputThunk> input = aInput; 438 // Create input stream pump and call AsyncRead as a block. 439 rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget()); 440 if (NS_SUCCEEDED(rv)) { 441 rv = mPump->AsyncRead(this); 442 } 443 444 if (NS_SUCCEEDED(rv)) { 445 rv = CheckPendingEvents(); 446 } 447 448 return OnOpenLocalFileComplete(rv, aIsSyncCall); 449 } 450 451 nsresult nsJARChannel::OnOpenLocalFileComplete(nsresult aResult, 452 bool aIsSyncCall) { 453 LOG(("nsJARChannel::OnOpenLocalFileComplete [this=%p %08x]\n", this, 454 static_cast<uint32_t>(aResult))); 455 456 MOZ_ASSERT(NS_IsMainThread()); 457 MOZ_ASSERT(mIsPending); 458 459 if (NS_FAILED(aResult)) { 460 if (aResult == NS_ERROR_FILE_NOT_FOUND) { 461 CheckForBrokenChromeURL(mLoadInfo, mOriginalURI); 462 } 463 if (!aIsSyncCall) { 464 NotifyError(aResult); 465 } 466 467 if (mLoadGroup) { 468 mLoadGroup->RemoveRequest(this, nullptr, aResult); 469 } 470 471 mOpened = false; 472 mIsPending = false; 473 mListener = nullptr; 474 mCallbacks = nullptr; 475 mProgressSink = nullptr; 476 477 return aResult; 478 } 479 480 return NS_OK; 481 } 482 483 nsresult nsJARChannel::CheckPendingEvents() { 484 MOZ_ASSERT(NS_IsMainThread()); 485 MOZ_ASSERT(mIsPending); 486 MOZ_ASSERT(mPump); 487 488 nsresult rv; 489 490 uint32_t suspendCount = mPendingEvent.suspendCount; 491 while (suspendCount--) { 492 if (NS_WARN_IF(NS_FAILED(rv = mPump->Suspend()))) { 493 return rv; 494 } 495 } 496 497 if (mPendingEvent.isCanceled) { 498 if (NS_WARN_IF(NS_FAILED(rv = mPump->Cancel(mStatus)))) { 499 return rv; 500 } 501 mPendingEvent.isCanceled = false; 502 } 503 504 return NS_OK; 505 } 506 507 void nsJARChannel::NotifyError(nsresult aError) { 508 MOZ_ASSERT(NS_FAILED(aError)); 509 510 mStatus = aError; 511 512 OnStartRequest(nullptr); 513 OnStopRequest(nullptr, aError); 514 } 515 516 void nsJARChannel::FireOnProgress(uint64_t aProgress) { 517 MOZ_ASSERT(NS_IsMainThread()); 518 MOZ_ASSERT(mProgressSink); 519 520 mProgressSink->OnProgress(this, aProgress, mContentLength); 521 } 522 523 //----------------------------------------------------------------------------- 524 // nsIRequest 525 //----------------------------------------------------------------------------- 526 527 NS_IMETHODIMP 528 nsJARChannel::GetName(nsACString& result) { return mJarURI->GetSpec(result); } 529 530 NS_IMETHODIMP 531 nsJARChannel::IsPending(bool* result) { 532 *result = mIsPending; 533 return NS_OK; 534 } 535 536 NS_IMETHODIMP 537 nsJARChannel::GetStatus(nsresult* status) { 538 if (mPump && NS_SUCCEEDED(mStatus)) 539 mPump->GetStatus(status); 540 else 541 *status = mStatus; 542 return NS_OK; 543 } 544 545 NS_IMETHODIMP nsJARChannel::SetCanceledReason(const nsACString& aReason) { 546 return SetCanceledReasonImpl(aReason); 547 } 548 549 NS_IMETHODIMP nsJARChannel::GetCanceledReason(nsACString& aReason) { 550 return GetCanceledReasonImpl(aReason); 551 } 552 553 NS_IMETHODIMP nsJARChannel::CancelWithReason(nsresult aStatus, 554 const nsACString& aReason) { 555 return CancelWithReasonImpl(aStatus, aReason); 556 } 557 558 NS_IMETHODIMP 559 nsJARChannel::Cancel(nsresult status) { 560 mCanceled = true; 561 mStatus = status; 562 if (mPump) { 563 return mPump->Cancel(status); 564 } 565 566 if (mIsPending) { 567 mPendingEvent.isCanceled = true; 568 } 569 570 return NS_OK; 571 } 572 573 NS_IMETHODIMP 574 nsJARChannel::GetCanceled(bool* aCanceled) { 575 *aCanceled = mCanceled; 576 return NS_OK; 577 } 578 579 NS_IMETHODIMP 580 nsJARChannel::Suspend() { 581 ++mPendingEvent.suspendCount; 582 583 if (mPump) { 584 return mPump->Suspend(); 585 } 586 587 return NS_OK; 588 } 589 590 NS_IMETHODIMP 591 nsJARChannel::Resume() { 592 if (NS_WARN_IF(mPendingEvent.suspendCount == 0)) { 593 return NS_ERROR_UNEXPECTED; 594 } 595 --mPendingEvent.suspendCount; 596 597 if (mPump) { 598 return mPump->Resume(); 599 } 600 601 return NS_OK; 602 } 603 604 NS_IMETHODIMP 605 nsJARChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) { 606 *aLoadFlags = mLoadFlags; 607 return NS_OK; 608 } 609 610 NS_IMETHODIMP 611 nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags) { 612 mLoadFlags = aLoadFlags; 613 return NS_OK; 614 } 615 616 NS_IMETHODIMP 617 nsJARChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) { 618 return GetTRRModeImpl(aTRRMode); 619 } 620 621 NS_IMETHODIMP 622 nsJARChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) { 623 return SetTRRModeImpl(aTRRMode); 624 } 625 626 NS_IMETHODIMP 627 nsJARChannel::GetIsDocument(bool* aIsDocument) { 628 return NS_GetIsDocumentChannel(this, aIsDocument); 629 } 630 631 NS_IMETHODIMP 632 nsJARChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) { 633 NS_IF_ADDREF(*aLoadGroup = mLoadGroup); 634 return NS_OK; 635 } 636 637 NS_IMETHODIMP 638 nsJARChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) { 639 mLoadGroup = aLoadGroup; 640 return NS_OK; 641 } 642 643 //----------------------------------------------------------------------------- 644 // nsIChannel 645 //----------------------------------------------------------------------------- 646 647 NS_IMETHODIMP 648 nsJARChannel::GetOriginalURI(nsIURI** aURI) { 649 *aURI = mOriginalURI; 650 NS_ADDREF(*aURI); 651 return NS_OK; 652 } 653 654 NS_IMETHODIMP 655 nsJARChannel::SetOriginalURI(nsIURI* aURI) { 656 NS_ENSURE_ARG_POINTER(aURI); 657 mOriginalURI = aURI; 658 return NS_OK; 659 } 660 661 NS_IMETHODIMP 662 nsJARChannel::GetURI(nsIURI** aURI) { 663 NS_IF_ADDREF(*aURI = mJarURI); 664 665 return NS_OK; 666 } 667 668 NS_IMETHODIMP 669 nsJARChannel::GetOwner(nsISupports** aOwner) { 670 // JAR signatures are not processed to avoid main-thread network I/O (bug 671 // 726125) 672 *aOwner = mOwner; 673 NS_IF_ADDREF(*aOwner); 674 return NS_OK; 675 } 676 677 NS_IMETHODIMP 678 nsJARChannel::SetOwner(nsISupports* aOwner) { 679 mOwner = aOwner; 680 return NS_OK; 681 } 682 683 NS_IMETHODIMP 684 nsJARChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) { 685 NS_IF_ADDREF(*aLoadInfo = mLoadInfo); 686 return NS_OK; 687 } 688 689 NS_IMETHODIMP 690 nsJARChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) { 691 MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null"); 692 mLoadInfo = aLoadInfo; 693 return NS_OK; 694 } 695 696 NS_IMETHODIMP 697 nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) { 698 NS_IF_ADDREF(*aCallbacks = mCallbacks); 699 return NS_OK; 700 } 701 702 NS_IMETHODIMP 703 nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) { 704 mCallbacks = aCallbacks; 705 return NS_OK; 706 } 707 708 NS_IMETHODIMP 709 nsJARChannel::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) { 710 MOZ_ASSERT(aSecurityInfo, "Null out param"); 711 *aSecurityInfo = nullptr; 712 return NS_OK; 713 } 714 715 bool nsJARChannel::GetContentTypeGuess(nsACString& aResult) const { 716 const char *ext = nullptr, *fileName = mJarEntry.get(); 717 int32_t len = mJarEntry.Length(); 718 719 // check if we're displaying a directory 720 // mJarEntry will be empty if we're trying to display 721 // the topmost directory in a zip, e.g. jar:foo.zip!/ 722 if (ENTRY_IS_DIRECTORY(mJarEntry)) { 723 aResult.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT); 724 return true; 725 } 726 727 // Not a directory, take a guess by its extension 728 for (int32_t i = len - 1; i >= 0; i--) { 729 if (fileName[i] == '.') { 730 ext = &fileName[i + 1]; 731 break; 732 } 733 } 734 if (!ext) { 735 return false; 736 } 737 nsIMIMEService* mimeServ = gJarHandler->MimeService(); 738 if (!mimeServ) { 739 return false; 740 } 741 mimeServ->GetTypeFromExtension(nsDependentCString(ext), aResult); 742 return !aResult.IsEmpty(); 743 } 744 745 NS_IMETHODIMP 746 nsJARChannel::GetContentType(nsACString& aResult) { 747 // If the Jar file has not been open yet, 748 // We return application/x-unknown-content-type 749 if (!mOpened) { 750 aResult.AssignLiteral(UNKNOWN_CONTENT_TYPE); 751 return NS_OK; 752 } 753 754 aResult = mContentType; 755 return NS_OK; 756 } 757 758 NS_IMETHODIMP 759 nsJARChannel::SetContentType(const nsACString& aContentType) { 760 // We behave like HTTP channels (treat this as a hint if called before open, 761 // and override the charset if called after open). 762 // mContentCharset is unchanged if not parsed 763 NS_ParseResponseContentType(aContentType, mContentType, mContentCharset); 764 return NS_OK; 765 } 766 767 NS_IMETHODIMP 768 nsJARChannel::GetContentCharset(nsACString& aContentCharset) { 769 // If someone gives us a charset hint we should just use that charset. 770 // So we don't care when this is being called. 771 aContentCharset = mContentCharset; 772 if (mContentCharset.IsEmpty() && (mOriginalURI->SchemeIs("chrome") || 773 mOriginalURI->SchemeIs("resource"))) { 774 aContentCharset.AssignLiteral("UTF-8"); 775 } 776 return NS_OK; 777 } 778 779 NS_IMETHODIMP 780 nsJARChannel::SetContentCharset(const nsACString& aContentCharset) { 781 mContentCharset = aContentCharset; 782 return NS_OK; 783 } 784 785 NS_IMETHODIMP 786 nsJARChannel::GetContentDisposition(uint32_t* aContentDisposition) { 787 return NS_ERROR_NOT_AVAILABLE; 788 } 789 790 NS_IMETHODIMP 791 nsJARChannel::SetContentDisposition(uint32_t aContentDisposition) { 792 return NS_ERROR_NOT_AVAILABLE; 793 } 794 795 NS_IMETHODIMP 796 nsJARChannel::GetContentDispositionFilename( 797 nsAString& aContentDispositionFilename) { 798 return NS_ERROR_NOT_AVAILABLE; 799 } 800 801 NS_IMETHODIMP 802 nsJARChannel::SetContentDispositionFilename( 803 const nsAString& aContentDispositionFilename) { 804 return NS_ERROR_NOT_AVAILABLE; 805 } 806 807 NS_IMETHODIMP 808 nsJARChannel::GetContentDispositionHeader( 809 nsACString& aContentDispositionHeader) { 810 return NS_ERROR_NOT_AVAILABLE; 811 } 812 813 NS_IMETHODIMP 814 nsJARChannel::GetContentLength(int64_t* result) { 815 *result = mContentLength; 816 return NS_OK; 817 } 818 819 NS_IMETHODIMP 820 nsJARChannel::SetContentLength(int64_t aContentLength) { 821 // XXX does this really make any sense at all? 822 mContentLength = aContentLength; 823 return NS_OK; 824 } 825 826 static void RecordZeroLengthEvent(bool aIsSync, const nsCString& aSpec, 827 nsresult aStatus, bool aCanceled, 828 const nsCString& aCanceledReason, 829 nsILoadInfo* aLoadInfo) { 830 if (!StaticPrefs::network_jar_record_failure_reason()) { 831 return; 832 } 833 834 // If the BrowsingContext performing the load has already been discarded, and 835 // we're getting a zero-length event due to the channel being canceled, this 836 // event isn't interesting for YSOD analysis, so can be skipped. 837 if (RefPtr<mozilla::dom::BrowsingContext> targetBC = 838 aLoadInfo->GetTargetBrowsingContext()) { 839 if (targetBC->IsDiscarded() && aCanceled) { 840 return; 841 } 842 } 843 844 // The Legacy Telemetry event can only hold 80 characters. 845 // We only save the file name and path inside the jar. 846 auto findFilenameStart = [](const nsCString& aSpec) -> uint32_t { 847 int32_t pos = aSpec.Find("!/"); 848 if (pos == kNotFound) { 849 MOZ_ASSERT(false, "This should not happen"); 850 return 0; 851 } 852 int32_t from = aSpec.RFindChar('/', pos); 853 if (from == kNotFound) { 854 MOZ_ASSERT(false, "This should not happen"); 855 return 0; 856 } 857 // Skip over the slash 858 from++; 859 return from; 860 }; 861 862 // If for some reason we are unable to extract the filename we report the 863 // entire string, or 80 characters of it, to make sure we don't miss any 864 // events. 865 uint32_t from = findFilenameStart(aSpec); 866 const auto fileName = Substring(aSpec, from); 867 868 nsAutoCString errorCString; 869 mozilla::GetErrorName(aStatus, errorCString); 870 871 // To test this telemetry we use a zip file and we want to make 872 // sure don't filter it out. 873 bool isTest = fileName.Find("test_empty_file.zip!") != -1; 874 bool isOmniJa = StringBeginsWith(fileName, "omni.ja!"_ns); 875 876 if (StringEndsWith(fileName, ".ftl"_ns)) { 877 // FTL uses I/O to test for file presence, so we get 878 // a high volume of events from it, but it is not erronous. 879 // Also, Fluent is resilient to empty loads, so even if any 880 // of the errors are real errors, they don't cause YSOD. 881 // We can investigate them separately. 882 if (!isTest && aStatus == NS_ERROR_FILE_NOT_FOUND) { 883 return; 884 } 885 886 glean::zero_byte_load::LoadFtlExtra extra = { 887 .cancelReason = Some(aCanceledReason), 888 .cancelled = Some(aCanceled), 889 .fileName = Some(fileName), 890 .status = Some(errorCString), 891 .sync = Some(aIsSync), 892 }; 893 glean::zero_byte_load::load_ftl.Record(Some(extra)); 894 } else if (StringEndsWith(fileName, ".dtd"_ns)) { 895 // We're going to skip reporting telemetry on res DTDs. 896 // See Bug 1693711 for investigation into those empty loads. 897 if (!isTest && StringBeginsWith(fileName, "omni.ja!/res/dtd"_ns)) { 898 return; 899 } 900 901 glean::zero_byte_load::LoadDtdExtra extra = { 902 .cancelReason = Some(aCanceledReason), 903 .cancelled = Some(aCanceled), 904 .fileName = Some(fileName), 905 .status = Some(errorCString), 906 .sync = Some(aIsSync), 907 }; 908 glean::zero_byte_load::load_dtd.Record(Some(extra)); 909 } else if (StringEndsWith(fileName, ".properties"_ns)) { 910 glean::zero_byte_load::LoadPropertiesExtra extra = { 911 .cancelReason = Some(aCanceledReason), 912 .cancelled = Some(aCanceled), 913 .fileName = Some(fileName), 914 .status = Some(errorCString), 915 .sync = Some(aIsSync), 916 }; 917 glean::zero_byte_load::load_properties.Record(Some(extra)); 918 } else if (StringEndsWith(fileName, ".js"_ns) || 919 StringEndsWith(fileName, ".jsm"_ns) || 920 StringEndsWith(fileName, ".mjs"_ns)) { 921 // We're going to skip reporting telemetry on JS loads 922 // coming not from omni.ja. 923 // See Bug 1693711 for investigation into those empty loads. 924 if (!isTest && !isOmniJa) { 925 return; 926 } 927 928 glean::zero_byte_load::LoadJsExtra extra = { 929 .cancelReason = Some(aCanceledReason), 930 .cancelled = Some(aCanceled), 931 .fileName = Some(fileName), 932 .status = Some(errorCString), 933 .sync = Some(aIsSync), 934 }; 935 glean::zero_byte_load::load_js.Record(Some(extra)); 936 } else if (StringEndsWith(fileName, ".xml"_ns)) { 937 glean::zero_byte_load::LoadXmlExtra extra = { 938 .cancelReason = Some(aCanceledReason), 939 .cancelled = Some(aCanceled), 940 .fileName = Some(fileName), 941 .status = Some(errorCString), 942 .sync = Some(aIsSync), 943 }; 944 glean::zero_byte_load::load_xml.Record(Some(extra)); 945 } else if (StringEndsWith(fileName, ".xhtml"_ns)) { 946 // This error seems to be very common and is not strongly 947 // correlated to YSOD. 948 if (aStatus == NS_ERROR_PARSED_DATA_CACHED) { 949 return; 950 } 951 952 // We're not investigating YSODs from extensions for now. 953 if (!isOmniJa) { 954 return; 955 } 956 957 glean::zero_byte_load::LoadXhtmlExtra extra = { 958 .cancelReason = Some(aCanceledReason), 959 .cancelled = Some(aCanceled), 960 .fileName = Some(fileName), 961 .status = Some(errorCString), 962 .sync = Some(aIsSync), 963 }; 964 glean::zero_byte_load::load_xhtml.Record(Some(extra)); 965 } else if (StringEndsWith(fileName, ".css"_ns)) { 966 // Bug 1702937: Filter out svg+'css'+'png'/NS_BINDING_ABORTED combo. 967 if (aStatus == NS_BINDING_ABORTED) { 968 return; 969 } 970 971 // Bug 1702937: Filter css/NS_ERROR_CORRUPTED_CONTENT that is coming from 972 // outside of omni.ja. 973 if (!isOmniJa && aStatus == NS_ERROR_CORRUPTED_CONTENT) { 974 return; 975 } 976 977 glean::zero_byte_load::LoadCssExtra extra = { 978 .cancelReason = Some(aCanceledReason), 979 .cancelled = Some(aCanceled), 980 .fileName = Some(fileName), 981 .status = Some(errorCString), 982 .sync = Some(aIsSync), 983 }; 984 glean::zero_byte_load::load_css.Record(Some(extra)); 985 } else if (StringEndsWith(fileName, ".json"_ns)) { 986 // FTL uses I/O to test for file presence, so we get 987 // a high volume of events from it, but it is not erronous. 988 // Also, Fluent is resilient to empty loads, so even if any 989 // of the errors are real errors, they don't cause YSOD. 990 // We can investigate them separately. 991 if (!isTest && aStatus == NS_ERROR_FILE_NOT_FOUND) { 992 return; 993 } 994 995 glean::zero_byte_load::LoadJsonExtra extra = { 996 .cancelReason = Some(aCanceledReason), 997 .cancelled = Some(aCanceled), 998 .fileName = Some(fileName), 999 .status = Some(errorCString), 1000 .sync = Some(aIsSync), 1001 }; 1002 glean::zero_byte_load::load_json.Record(Some(extra)); 1003 } else if (StringEndsWith(fileName, ".html"_ns)) { 1004 // See bug 1695560. Filter out non-omni.ja HTML. 1005 if (!isOmniJa) { 1006 return; 1007 } 1008 1009 // See bug 1695560. "activity-stream-noscripts.html" with NS_ERROR_FAILURE 1010 // is filtered out. 1011 if (fileName.EqualsLiteral("omni.ja!/chrome/browser/res/newtab/" 1012 "prerendered/activity-stream-noscripts.html") && 1013 aStatus == NS_ERROR_FAILURE) { 1014 return; 1015 } 1016 1017 glean::zero_byte_load::LoadHtmlExtra extra = { 1018 .cancelReason = Some(aCanceledReason), 1019 .cancelled = Some(aCanceled), 1020 .fileName = Some(fileName), 1021 .status = Some(errorCString), 1022 .sync = Some(aIsSync), 1023 }; 1024 glean::zero_byte_load::load_html.Record(Some(extra)); 1025 } else if (StringEndsWith(fileName, ".png"_ns)) { 1026 // See bug 1695560. 1027 // Bug 1702937: Filter out svg+'css'+'png'/NS_BINDING_ABORTED combo. 1028 if (!isOmniJa || aStatus == NS_BINDING_ABORTED) { 1029 return; 1030 } 1031 1032 glean::zero_byte_load::LoadPngExtra extra = { 1033 .cancelReason = Some(aCanceledReason), 1034 .cancelled = Some(aCanceled), 1035 .fileName = Some(fileName), 1036 .status = Some(errorCString), 1037 .sync = Some(aIsSync), 1038 }; 1039 glean::zero_byte_load::load_png.Record(Some(extra)); 1040 } else if (StringEndsWith(fileName, ".svg"_ns)) { 1041 // See bug 1695560. 1042 // Bug 1702937: Filter out svg+'css'+'png'/NS_BINDING_ABORTED combo. 1043 if (!isOmniJa || aStatus == NS_BINDING_ABORTED) { 1044 return; 1045 } 1046 glean::zero_byte_load::LoadSvgExtra extra = { 1047 .cancelReason = Some(aCanceledReason), 1048 .cancelled = Some(aCanceled), 1049 .fileName = Some(fileName), 1050 .status = Some(errorCString), 1051 .sync = Some(aIsSync), 1052 }; 1053 glean::zero_byte_load::load_svg.Record(Some(extra)); 1054 } else { // All others 1055 // We're going to, for now, filter out `other` category. 1056 // See Bug 1693711 for investigation into those empty loads. 1057 // Bug 1702937: Filter other/*.ico/NS_BINDING_ABORTED. 1058 if (!isTest && (!isOmniJa || (aStatus == NS_BINDING_ABORTED && 1059 StringEndsWith(fileName, ".ico"_ns)))) { 1060 return; 1061 } 1062 1063 // See bug 1695560. "search-extensions/google/favicon.ico" with 1064 // NS_BINDING_ABORTED is filtered out. 1065 if (fileName.EqualsLiteral( 1066 "omni.ja!/chrome/browser/search-extensions/google/favicon.ico") && 1067 aStatus == NS_BINDING_ABORTED) { 1068 return; 1069 } 1070 1071 glean::zero_byte_load::LoadOthersExtra extra = { 1072 .cancelReason = Some(aCanceledReason), 1073 .cancelled = Some(aCanceled), 1074 .fileName = Some(fileName), 1075 .status = Some(errorCString), 1076 .sync = Some(aIsSync), 1077 }; 1078 glean::zero_byte_load::load_others.Record(Some(extra)); 1079 } 1080 } 1081 1082 NS_IMETHODIMP 1083 nsJARChannel::Open(nsIInputStream** aStream) { 1084 LOG(("nsJARChannel::Open [this=%p]\n", this)); 1085 nsCOMPtr<nsIStreamListener> listener; 1086 nsresult rv = 1087 nsContentSecurityManager::doContentSecurityCheck(this, listener); 1088 NS_ENSURE_SUCCESS(rv, rv); 1089 1090 auto recordEvent = MakeScopeExit([&] { 1091 if (mContentLength <= 0 || NS_FAILED(rv)) { 1092 RecordZeroLengthEvent(true, mSpec, rv, mCanceled, mCanceledReason, 1093 mLoadInfo); 1094 } 1095 }); 1096 1097 LOG(("nsJARChannel::Open [this=%p]\n", this)); 1098 1099 NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS); 1100 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); 1101 1102 mJarFile = nullptr; 1103 1104 rv = LookupFile(); 1105 if (NS_FAILED(rv)) return rv; 1106 1107 // If mJarFile was not set by LookupFile, we can't open a channel. 1108 if (!mJarFile) { 1109 MOZ_ASSERT_UNREACHABLE("only file-backed jars are supported"); 1110 return NS_ERROR_NOT_IMPLEMENTED; 1111 } 1112 1113 RefPtr<nsJARInputThunk> input; 1114 rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input)); 1115 if (NS_FAILED(rv)) return rv; 1116 1117 input.forget(aStream); 1118 SetOpened(); 1119 1120 return NS_OK; 1121 } 1122 1123 void nsJARChannel::SetOpened() { 1124 MOZ_ASSERT(!mOpened, "Opening channel twice?"); 1125 mOpened = true; 1126 // Compute the content type now. 1127 if (!GetContentTypeGuess(mContentType)) { 1128 mContentType.Assign(UNKNOWN_CONTENT_TYPE); 1129 } 1130 } 1131 1132 NS_IMETHODIMP 1133 nsJARChannel::AsyncOpen(nsIStreamListener* aListener) { 1134 LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this)); 1135 nsCOMPtr<nsIStreamListener> listener = aListener; 1136 nsresult rv = 1137 nsContentSecurityManager::doContentSecurityCheck(this, listener); 1138 if (NS_FAILED(rv)) { 1139 mIsPending = false; 1140 mListener = nullptr; 1141 mCallbacks = nullptr; 1142 mProgressSink = nullptr; 1143 return rv; 1144 } 1145 1146 LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this)); 1147 MOZ_ASSERT( 1148 mLoadInfo->GetSecurityMode() == 0 || 1149 mLoadInfo->GetInitialSecurityCheckDone() || 1150 (mLoadInfo->GetSecurityMode() == 1151 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL && 1152 mLoadInfo->GetLoadingPrincipal() && 1153 mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()), 1154 "security flags in loadInfo but doContentSecurityCheck() not called"); 1155 1156 NS_ENSURE_ARG_POINTER(listener); 1157 NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS); 1158 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); 1159 1160 mJarFile = nullptr; 1161 1162 // Initialize mProgressSink 1163 NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink); 1164 1165 mListener = listener; 1166 mIsPending = true; 1167 1168 rv = LookupFile(); 1169 if (NS_FAILED(rv) || !mJarFile) { 1170 // Not a local file... 1171 mIsPending = false; 1172 mListener = nullptr; 1173 mCallbacks = nullptr; 1174 mProgressSink = nullptr; 1175 return mJarFile ? rv : NS_ERROR_UNSAFE_CONTENT_TYPE; 1176 } 1177 1178 rv = OpenLocalFile(); 1179 if (NS_FAILED(rv)) { 1180 mIsPending = false; 1181 mListener = nullptr; 1182 mCallbacks = nullptr; 1183 mProgressSink = nullptr; 1184 return rv; 1185 } 1186 1187 return NS_OK; 1188 } 1189 1190 //----------------------------------------------------------------------------- 1191 // nsIJARChannel 1192 //----------------------------------------------------------------------------- 1193 NS_IMETHODIMP 1194 nsJARChannel::GetJarFile(nsIFile** aFile) { 1195 NS_IF_ADDREF(*aFile = mJarFile); 1196 return NS_OK; 1197 } 1198 1199 NS_IMETHODIMP 1200 nsJARChannel::SetJarFile(nsIFile* aFile) { 1201 if (mOpened) { 1202 return NS_ERROR_IN_PROGRESS; 1203 } 1204 mJarFileOverride = aFile; 1205 return NS_OK; 1206 } 1207 1208 NS_IMETHODIMP 1209 nsJARChannel::EnsureCached(bool* aIsCached) { 1210 nsresult rv; 1211 *aIsCached = false; 1212 1213 if (mOpened) { 1214 return NS_ERROR_ALREADY_OPENED; 1215 } 1216 1217 if (mPreCachedJarReader) { 1218 // We've already been called and found the JAR is cached 1219 *aIsCached = true; 1220 return NS_OK; 1221 } 1222 1223 nsCOMPtr<nsIURI> innerFileURI; 1224 rv = mJarURI->GetJARFile(getter_AddRefs(innerFileURI)); 1225 NS_ENSURE_SUCCESS(rv, rv); 1226 1227 nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv); 1228 NS_ENSURE_SUCCESS(rv, rv); 1229 1230 nsCOMPtr<nsIFile> jarFile; 1231 rv = innerFileURL->GetFile(getter_AddRefs(jarFile)); 1232 NS_ENSURE_SUCCESS(rv, rv); 1233 1234 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); 1235 NS_ENSURE_SUCCESS(rv, rv); 1236 1237 nsCOMPtr<nsIProtocolHandler> handler; 1238 rv = ioService->GetProtocolHandler("jar", getter_AddRefs(handler)); 1239 NS_ENSURE_SUCCESS(rv, rv); 1240 1241 auto jarHandler = static_cast<nsJARProtocolHandler*>(handler.get()); 1242 MOZ_ASSERT(jarHandler); 1243 1244 nsIZipReaderCache* jarCache = jarHandler->JarCache(); 1245 1246 rv = jarCache->GetZipIfCached(jarFile, getter_AddRefs(mPreCachedJarReader)); 1247 if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) { 1248 return NS_OK; 1249 } 1250 NS_ENSURE_SUCCESS(rv, rv); 1251 1252 *aIsCached = true; 1253 return NS_OK; 1254 } 1255 1256 NS_IMETHODIMP 1257 nsJARChannel::GetZipEntry(nsIZipEntry** aZipEntry) { 1258 nsresult rv = LookupFile(); 1259 if (NS_FAILED(rv)) return rv; 1260 1261 if (!mJarFile) return NS_ERROR_NOT_AVAILABLE; 1262 1263 nsCOMPtr<nsIZipReader> reader; 1264 rv = gJarHandler->JarCache()->GetZip(mJarFile, getter_AddRefs(reader)); 1265 if (NS_FAILED(rv)) return rv; 1266 1267 return reader->GetEntry(mJarEntry, aZipEntry); 1268 } 1269 1270 //----------------------------------------------------------------------------- 1271 // nsIStreamListener 1272 //----------------------------------------------------------------------------- 1273 1274 NS_IMETHODIMP 1275 nsJARChannel::OnStartRequest(nsIRequest* req) { 1276 LOG(("nsJARChannel::OnStartRequest [this=%p %s]\n", this, mSpec.get())); 1277 1278 mRequest = req; 1279 nsresult rv = mListener->OnStartRequest(this); 1280 if (NS_FAILED(rv)) { 1281 return rv; 1282 } 1283 1284 // Restrict loadable content types. 1285 nsAutoCString contentType; 1286 GetContentType(contentType); 1287 auto contentPolicyType = mLoadInfo->GetExternalContentPolicyType(); 1288 if (contentType.Equals(APPLICATION_HTTP_INDEX_FORMAT) && 1289 contentPolicyType != ExtContentPolicy::TYPE_DOCUMENT && 1290 contentPolicyType != ExtContentPolicy::TYPE_FETCH) { 1291 return NS_ERROR_CORRUPTED_CONTENT; 1292 } 1293 if (contentPolicyType == ExtContentPolicy::TYPE_STYLESHEET && 1294 !contentType.EqualsLiteral(TEXT_CSS)) { 1295 return NS_ERROR_CORRUPTED_CONTENT; 1296 } 1297 if (contentPolicyType == ExtContentPolicy::TYPE_SCRIPT && 1298 !nsContentUtils::IsJavascriptMIMEType( 1299 NS_ConvertUTF8toUTF16(contentType))) { 1300 return NS_ERROR_CORRUPTED_CONTENT; 1301 } 1302 1303 return rv; 1304 } 1305 1306 NS_IMETHODIMP 1307 nsJARChannel::OnStopRequest(nsIRequest* req, nsresult status) { 1308 LOG(("nsJARChannel::OnStopRequest [this=%p %s status=%" PRIx32 "]\n", this, 1309 mSpec.get(), static_cast<uint32_t>(status))); 1310 1311 if (NS_SUCCEEDED(mStatus)) mStatus = status; 1312 1313 if (mListener) { 1314 if (!mOnDataCalled || NS_FAILED(status)) { 1315 RecordZeroLengthEvent(false, mSpec, status, mCanceled, mCanceledReason, 1316 mLoadInfo); 1317 } 1318 1319 mListener->OnStopRequest(this, status); 1320 mListener = nullptr; 1321 } 1322 1323 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, status); 1324 1325 mRequest = nullptr; 1326 mPump = nullptr; 1327 mIsPending = false; 1328 1329 // Drop notification callbacks to prevent cycles. 1330 mCallbacks = nullptr; 1331 mProgressSink = nullptr; 1332 1333 #if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA) 1334 #else 1335 // To deallocate file descriptor by RemoteOpenFileChild destructor. 1336 mJarFile = nullptr; 1337 #endif 1338 1339 return NS_OK; 1340 } 1341 1342 NS_IMETHODIMP 1343 nsJARChannel::OnDataAvailable(nsIRequest* req, nsIInputStream* stream, 1344 uint64_t offset, uint32_t count) { 1345 LOG(("nsJARChannel::OnDataAvailable [this=%p %s]\n", this, mSpec.get())); 1346 1347 nsresult rv; 1348 1349 // don't send out OnDataAvailable notifications if we've been canceled. 1350 if (mCanceled) { 1351 return mStatus; 1352 } 1353 1354 mOnDataCalled = true; 1355 rv = mListener->OnDataAvailable(this, stream, offset, count); 1356 1357 // simply report progress here instead of hooking ourselves up as a 1358 // nsITransportEventSink implementation. 1359 // XXX do the 64-bit stuff for real 1360 if (mProgressSink && NS_SUCCEEDED(rv)) { 1361 if (NS_IsMainThread()) { 1362 FireOnProgress(offset + count); 1363 } else { 1364 NS_DispatchToMainThread(NewRunnableMethod<uint64_t>( 1365 "nsJARChannel::FireOnProgress", this, &nsJARChannel::FireOnProgress, 1366 offset + count)); 1367 } 1368 } 1369 1370 return rv; // let the pump cancel on failure 1371 } 1372 1373 NS_IMETHODIMP 1374 nsJARChannel::RetargetDeliveryTo(nsISerialEventTarget* aEventTarget) { 1375 MOZ_ASSERT(NS_IsMainThread()); 1376 1377 nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest); 1378 if (!request) { 1379 return NS_ERROR_NO_INTERFACE; 1380 } 1381 1382 return request->RetargetDeliveryTo(aEventTarget); 1383 } 1384 1385 NS_IMETHODIMP 1386 nsJARChannel::GetDeliveryTarget(nsISerialEventTarget** aEventTarget) { 1387 MOZ_ASSERT(NS_IsMainThread()); 1388 1389 nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest); 1390 if (!request) { 1391 return NS_ERROR_NO_INTERFACE; 1392 } 1393 1394 return request->GetDeliveryTarget(aEventTarget); 1395 } 1396 1397 NS_IMETHODIMP 1398 nsJARChannel::CheckListenerChain() { 1399 MOZ_ASSERT(NS_IsMainThread()); 1400 1401 nsCOMPtr<nsIThreadRetargetableStreamListener> listener = 1402 do_QueryInterface(mListener); 1403 if (!listener) { 1404 return NS_ERROR_NO_INTERFACE; 1405 } 1406 1407 return listener->CheckListenerChain(); 1408 } 1409 1410 NS_IMETHODIMP 1411 nsJARChannel::OnDataFinished(nsresult aStatus) { 1412 nsCOMPtr<nsIThreadRetargetableStreamListener> listener = 1413 do_QueryInterface(mListener); 1414 if (listener) { 1415 return listener->OnDataFinished(aStatus); 1416 } 1417 1418 return NS_OK; 1419 }