Bug 1525762: Part 1l - Don't delay serving image resources until extensions are ready. r=aswan
authorKris Maglione <maglione.k@gmail.com>
Sat, 23 Mar 2019 15:26:03 -0700
changeset 466971 f31ce378439a073fb7a54c44ffe5dc68e1c03b77
parent 466970 e289c1b047d7e2c02845decf7618f31bf145dea6
child 466972 b62c3bde4bc3f7cba452ad57f8d728b175c15a62
push id35789
push userbtara@mozilla.com
push dateSun, 31 Mar 2019 09:00:52 +0000
treeherdermozilla-central@c06dfc552c64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1525762: Part 1l - Don't delay serving image resources until extensions are ready. r=aswan Since we want themes to be loaded before the main browser window is loaded, we really need their resources to be ready as soon as possible. We typically delay serving moz-extension: requests until the extension is ready, since extension page and CSS loads rely on the extension being fully initialized. Image loads, though, are perfectly safe to load as early as we need, so this patch whitelists them to bypass the delayed load logic.
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "ExtensionProtocolHandler.h"
+#include "mozilla/BinarySearch.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/Promise-inl.h"
 #include "mozilla/ExtensionPolicyService.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/ipc/URIParams.h"
@@ -36,29 +37,54 @@
 #include "nsIInputStreamPump.h"
 #include "nsIJARURI.h"
 #include "nsIStreamListener.h"
 #include "nsIThread.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsIStreamConverterService.h"
 #include "nsNetUtil.h"
+#include "nsReadableUtils.h"
 #include "nsURLHelper.h"
 #include "prio.h"
 #include "SimpleChannel.h"
 #if defined(XP_WIN)
 #  include "nsILocalFileWin.h"
 #  include "WinUtils.h"
 #define EXTENSION_SCHEME "moz-extension"
 using mozilla::dom::Promise;
 using mozilla::ipc::FileDescriptor;
+// A list of file extensions containing purely static data, which can be loaded
+// from an extension before the extension is fully ready. The main purpose of
+// this is to allow image resources from theme XPIs to load quickly during
+// browser startup.
+// The layout of this array is chosen in order to prevent the need for runtime
+// relocation, which an array of char* pointers would require. It also has the
+// benefit of being more compact when the difference in length between the
+// longest and average string is less than 8 bytes. The length of the
+// char[] array must match the size of the longest entry in the list.
+// This list must be kept sorted.
+static const char sStaticFileExtensions[][5] = {
+  // clang-format off
+  "bmp",
+  "gif",
+  "ico",
+  "jpeg",
+  "jpg",
+  "png",
+  "svg",
+  // clang-format on
 namespace mozilla {
 namespace net {
 using extensions::URLInfo;
 LazyLogModule gExtProtocolLog("ExtProtocol");
 #undef LOG
@@ -455,19 +481,20 @@ nsresult ExtensionProtocolHandler::Subst
   RefPtr<dom::Promise> readyPromise(policy->ReadyPromise());
   nsresult rv;
   nsCOMPtr<nsIURL> url = do_QueryInterface(aURI, &rv);
   nsAutoCString ext;
+  ToLowerCase(ext);
   nsCOMPtr<nsIChannel> channel;
-  if (ext.LowerCaseEqualsLiteral("css")) {
+  if (ext.EqualsLiteral("css")) {
     // Filter CSS files to replace locale message tokens with localized strings.
     static const auto convert = [](nsIStreamListener* listener,
                                    nsIChannel* channel,
                                    nsIChannel* origChannel) -> nsresult {
       nsresult rv;
       nsCOMPtr<nsIStreamConverterService> convService =
@@ -497,16 +524,26 @@ nsresult ExtensionProtocolHandler::Subst
                   return convert(aListener, chan, aChannel);
           } else {
             MOZ_TRY(convert(listener, channel, origChannel));
           return RequestOrReason(origChannel);
   } else if (readyPromise) {
+    size_t matchIdx;
+    if (BinarySearchIf(
+            sStaticFileExtensions, 0, ArrayLength(sStaticFileExtensions),
+            [&ext](const char* aOther) { return ext.Compare(aOther); },
+            &matchIdx)) {
+      // This is a static resource that shouldn't depend on the extension being
+      // ready. Don't bother waiting for it.
+      return NS_OK;
+    }
     channel = NS_NewSimpleChannel(
         aURI, aLoadInfo, *result,
         [readyPromise](nsIStreamListener* listener, nsIChannel* channel,
                        nsIChannel* origChannel) -> RequestOrReason {
           OpenWhenReady(readyPromise, listener, origChannel,
                         [](nsIStreamListener* aListener, nsIChannel* aChannel) {
                           return aChannel->AsyncOpen(aListener);