Bug 781725: Refactor TabChild to allow pre-created instances, and then use a pre-created instance to pre-load and compile BrowserElementChild.js. r=smaug
authorChris Jones <jones.chris.g@gmail.com>
Wed, 29 Aug 2012 12:26:18 -0300
changeset 103787 3925750b682fb35b7bd639c6a1b031adda991ecf
parent 103786 88d2dbb78bf5824e87744208a7d9c8fdb415e2e6
child 103788 c05251c5a7e3e96d7cd507e27db09f802a1f8a53
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs781725
milestone18.0a1
Bug 781725: Refactor TabChild to allow pre-created instances, and then use a pre-created instance to pre-load and compile BrowserElementChild.js. r=smaug
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsFrameMessageManager.h
dom/ipc/ContentChild.cpp
dom/ipc/Makefile.in
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
widget/xpwidgets/PuppetWidget.cpp
widget/xpwidgets/PuppetWidget.h
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -817,33 +817,43 @@ nsFrameScriptExecutor::Shutdown()
 void
 nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL)
 {
   if (!mGlobal || !mCx || !sCachedScripts) {
     return;
   }
 
   nsFrameJSScriptExecutorHolder* holder = sCachedScripts->Get(aURL);
+  if (!holder) {
+    TryCacheLoadAndCompileScript(aURL, EXECUTE_IF_CANT_CACHE);
+    holder = sCachedScripts->Get(aURL);
+  }
+
   if (holder) {
     nsContentUtils::ThreadJSContextStack()->Push(mCx);
     {
       // Need to scope JSAutoRequest to happen after Push but before Pop,
       // at least for now. See bug 584673.
       JSAutoRequest ar(mCx);
       JSObject* global = nullptr;
       mGlobal->GetJSObject(&global);
       if (global) {
         (void) JS_ExecuteScript(mCx, global, holder->mScript, nullptr);
       }
     }
     JSContext* unused;
     nsContentUtils::ThreadJSContextStack()->Pop(&unused);
     return;
   }
+}
 
+void
+nsFrameScriptExecutor::TryCacheLoadAndCompileScript(const nsAString& aURL,
+                                                    CacheFailedBehavior aBehavior)
+{
   nsCString url = NS_ConvertUTF16toUTF8(aURL);
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
   if (NS_FAILED(rv)) {
     return;
   }
   
   bool hasFlags;
@@ -902,18 +912,19 @@ nsFrameScriptExecutor::LoadFrameScriptIn
           // We don't cache data: scripts!
           if (!scheme.EqualsLiteral("data")) {
             nsFrameJSScriptExecutorHolder* holder =
               new nsFrameJSScriptExecutorHolder(script);
             // Root the object also for caching.
             JS_AddNamedScriptRoot(mCx, &(holder->mScript),
                                   "Cached message manager script");
             sCachedScripts->Put(aURL, holder);
+          } else if (aBehavior == EXECUTE_IF_CANT_CACHE) {
+            (void) JS_ExecuteScript(mCx, global, script, nullptr);
           }
-          (void) JS_ExecuteScript(mCx, global, script, nullptr);
         }
       }
     } 
     JSContext* unused;
     nsContentUtils::ThreadJSContextStack()->Pop(&unused);
   }
 }
 
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -219,16 +219,19 @@ protected:
                             mDelayedCxDestroy(false)
   { MOZ_COUNT_CTOR(nsFrameScriptExecutor); }
   ~nsFrameScriptExecutor()
   { MOZ_COUNT_DTOR(nsFrameScriptExecutor); }
   void DidCreateCx();
   // Call this when you want to destroy mCx.
   void DestroyCx();
   void LoadFrameScriptInternal(const nsAString& aURL);
+  enum CacheFailedBehavior { EXECUTE_IF_CANT_CACHE, DONT_EXECUTE };
+  void TryCacheLoadAndCompileScript(const nsAString& aURL,
+                                    CacheFailedBehavior aBehavior = DONT_EXECUTE);
   bool InitTabChildGlobalInternal(nsISupports* aScope);
   static void Traverse(nsFrameScriptExecutor *tmp,
                        nsCycleCollectionTraversalCallback &cb);
   nsCOMPtr<nsIXPConnectJSObjectHolder> mGlobal;
   JSContext* mCx;
   uint32_t mCxStackRefCnt;
   bool mDelayedCxDestroy;
   nsCOMPtr<nsIPrincipal> mPrincipal;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -423,20 +423,20 @@ ContentChild::AllocPImageBridge(mozilla:
 {
     return ImageBridgeChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
 
 PBrowserChild*
 ContentChild::AllocPBrowser(const uint32_t& aChromeFlags,
                             const bool& aIsBrowserElement, const AppId& aApp)
 {
-    uint32_t appId = aApp.get_uint32_t();
-    nsRefPtr<TabChild> iframe = new TabChild(aChromeFlags, aIsBrowserElement,
-                                             appId);
-    return NS_SUCCEEDED(iframe->Init()) ? iframe.forget().get() : NULL;
+    nsRefPtr<TabChild> child =
+        TabChild::Create(aChromeFlags, aIsBrowserElement, aApp.get_uint32_t());
+    // The ref here is released below.
+    return child.forget().get();
 }
 
 bool
 ContentChild::DeallocPBrowser(PBrowserChild* iframe)
 {
     TabChild* child = static_cast<TabChild*>(iframe);
     NS_RELEASE(child);
     return true;
@@ -932,16 +932,18 @@ ContentChild::RecvCycleCollect()
     return true;
 }
 
 static void
 PreloadSlowThings()
 {
     // This fetches and creates all the built-in stylesheets.
     nsLayoutStylesheetCache::UserContentSheet();
+
+    TabChild::PreloadSlowThings();
 }
 
 bool
 ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID)
 {
     mAppInfo.version.Assign(version);
     mAppInfo.buildID.Assign(buildID);
 
--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -95,16 +95,17 @@ LOCAL_INCLUDES += \
 	-I$(topsrcdir)/dom/indexedDB \
 	-I$(topsrcdir)/dom/indexedDB/ipc \
 	-I$(topsrcdir)/extensions/cookie \
 	-I$(topsrcdir)/dom/base \
 	-I$(topsrcdir)/toolkit/xre \
 	-I$(topsrcdir)/hal/sandbox \
 	-I$(topsrcdir)/dom/sms/src/ipc \
 	-I$(topsrcdir)/dom/devicestorage \
+	-I$(topsrcdir)/widget/xpwidgets \
 	$(NULL)
 
 DEFINES += -DBIN_SUFFIX='"$(BIN_SUFFIX)"'
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),$(findstring $(MOZ_WIDGET_TOOLKIT),android gtk2 gonk qt))
 DEFINES += -DMOZ_ENABLE_FREETYPE
 endif
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -7,24 +7,26 @@
 #include "base/basictypes.h"
 
 #include "TabChild.h"
 
 #include "BasicLayers.h"
 #include "Blob.h"
 #include "ContentChild.h"
 #include "IndexedDBChild.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/IntentionalCrash.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/PContentChild.h"
 #include "mozilla/dom/PContentDialogChild.h"
 #include "mozilla/ipc/DocumentRendererChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/PLayersChild.h"
 #include "mozilla/layout/RenderFrameChild.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #include "nsComponentManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsEmbedCID.h"
 #include "nsEventListenerManager.h"
 #include "nsIBaseWindow.h"
 #include "nsIComponentManager.h"
@@ -57,19 +59,23 @@
 #include "nsPIWindowRoot.h"
 #include "nsPresContext.h"
 #include "nsPrintfCString.h"
 #include "nsScriptLoader.h"
 #include "nsSerializationHelper.h"
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
 #include "PCOMContentPermissionRequestChild.h"
+#include "PuppetWidget.h"
 #include "StructuredCloneUtils.h"
 #include "xpcpublic.h"
 
+#define BROWSER_ELEMENT_CHILD_SCRIPT \
+    NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::widget;
@@ -88,28 +94,71 @@ ContentListener::HandleEvent(nsIDOMEvent
 
 class ContentDialogChild : public PContentDialogChild
 {
 public:
   virtual bool Recv__delete__(const InfallibleTArray<int>& aIntParams,
                               const InfallibleTArray<nsString>& aStringParams);
 };
 
+StaticRefPtr<TabChild> sPreallocatedTab;
+
+/*static*/ void
+TabChild::PreloadSlowThings()
+{
+    MOZ_ASSERT(!sPreallocatedTab);
+
+    nsRefPtr<TabChild> tab(new TabChild(0, false,
+                                        nsIScriptSecurityManager::NO_APP_ID));
+    if (!NS_SUCCEEDED(tab->Init()) ||
+        !tab->InitTabChildGlobal(DONT_LOAD_SCRIPTS)) {
+        return;
+    }
+    tab->TryCacheLoadAndCompileScript(BROWSER_ELEMENT_CHILD_SCRIPT);
+
+    sPreallocatedTab = tab;
+    ClearOnShutdown(&sPreallocatedTab);
+}
+
+/*static*/ already_AddRefed<TabChild>
+TabChild::Create(uint32_t aChromeFlags,
+                 bool aIsBrowserElement, uint32_t aAppId)
+{
+    if (sPreallocatedTab &&
+        sPreallocatedTab->mChromeFlags == aChromeFlags &&
+        (aIsBrowserElement || 
+         aAppId != nsIScriptSecurityManager::NO_APP_ID)) {
+        nsRefPtr<TabChild> child = sPreallocatedTab.get();
+        sPreallocatedTab = nullptr;
+
+        MOZ_ASSERT(!child->mTriedBrowserInit);
+
+        child->SetAppBrowserConfig(aIsBrowserElement, aAppId);
+
+        return child.forget();
+    }
+
+    nsRefPtr<TabChild> iframe = new TabChild(aChromeFlags, aIsBrowserElement,
+                                             aAppId);
+    return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr;
+}
+
 
 TabChild::TabChild(uint32_t aChromeFlags, bool aIsBrowserElement,
                    uint32_t aAppId)
   : mRemoteFrame(nullptr)
   , mTabChildGlobal(nullptr)
   , mChromeFlags(aChromeFlags)
   , mOuterRect(0, 0, 0, 0)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
+  , mAppId(aAppId)
   , mDidFakeShow(false)
   , mIsBrowserElement(aIsBrowserElement)
   , mNotified(false)
-  , mAppId(aAppId)
+  , mTriedBrowserInit(false)
 {
     printf("creating %d!\n", NS_IsMainThread());
 }
 
 nsresult
 TabChild::Observe(nsISupports *aSubject,
                   const char *aTopic,
                   const PRUnichar *aData)
@@ -145,32 +194,72 @@ TabChild::Init()
   }
 
   webBrowser->SetContainerWindow(this);
   mWebNav = do_QueryInterface(webBrowser);
   NS_ASSERTION(mWebNav, "nsWebBrowser doesn't implement nsIWebNavigation?");
 
   nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(mWebNav));
   docShellItem->SetItemType(nsIDocShellTreeItem::typeContentWrapper);
-
-  nsCOMPtr<nsIObserverService> observerService =
-    do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
+  
+  nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(mWebNav);
+  if (!baseWindow) {
+    NS_ERROR("mWebNav doesn't QI to nsIBaseWindow");
+    return false;
+  }
 
-  if (observerService) {
-    observerService->AddObserver(this,
-                                 "cancel-default-pan-zoom",
-                                 false);
-    observerService->AddObserver(this,
-                                 "browser-zoom-to-rect",
-                                 false);
+  mWidget = nsIWidget::CreatePuppetWidget(this);
+  if (!mWidget) {
+    NS_ERROR("couldn't create fake widget");
+    return false;
+  }
+  mWidget->Create(
+    nullptr, 0,              // no parents
+    nsIntRect(nsIntPoint(0, 0), nsIntSize(0, 0)),
+    nullptr,                 // HandleWidgetEvent
+    nullptr                  // nsDeviceContext
+  );
+
+  baseWindow->InitWindow(0, mWidget, 0, 0, 0, 0);
+  baseWindow->Create();
+
+  SetAppBrowserConfig(mIsBrowserElement, mAppId);
+
+  // IPC uses a WebBrowser object for which DNS prefetching is turned off
+  // by default. But here we really want it, so enable it explicitly
+  nsCOMPtr<nsIWebBrowserSetup> webBrowserSetup =
+    do_QueryInterface(baseWindow);
+  if (webBrowserSetup) {
+    webBrowserSetup->SetProperty(nsIWebBrowserSetup::SETUP_ALLOW_DNS_PREFETCH,
+                                 true);
+  } else {
+    NS_WARNING("baseWindow doesn't QI to nsIWebBrowserSetup, skipping "
+               "DNS prefetching enable step.");
   }
 
   return NS_OK;
 }
 
+void
+TabChild::SetAppBrowserConfig(bool aIsBrowserElement, uint32_t aAppId)
+{
+    mIsBrowserElement = aIsBrowserElement;
+    mAppId = aAppId;
+
+    nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mWebNav);
+    MOZ_ASSERT(docShell);
+
+    if (docShell) {
+        docShell->SetAppId(mAppId);
+        if (mIsBrowserElement) {
+            docShell->SetIsBrowserElement();
+        }
+    }
+}
+
 NS_INTERFACE_MAP_BEGIN(TabChild)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2)
   NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
@@ -564,16 +653,18 @@ TabChild::ActorDestroy(ActorDestroyReaso
     static_cast<nsFrameMessageManager*>
       (mTabChildGlobal->mMessageManager.get())->Disconnect();
     mTabChildGlobal->mMessageManager = nullptr;
   }
 }
 
 TabChild::~TabChild()
 {
+    DestroyWindow();
+
     nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(mWebNav);
     if (webBrowser) {
       webBrowser->SetContainerWindow(nullptr);
     }
     if (mCx) {
       DestroyCx();
     }
     
@@ -606,63 +697,39 @@ TabChild::DoFakeShow()
 {
   RecvShow(nsIntSize(0, 0));
   mDidFakeShow = true;
 }
 
 bool
 TabChild::RecvShow(const nsIntSize& size)
 {
+
     if (mDidFakeShow) {
         return true;
     }
 
     printf("[TabChild] SHOW (w,h)= (%d, %d)\n", size.width, size.height);
 
     nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(mWebNav);
     if (!baseWindow) {
         NS_ERROR("mWebNav doesn't QI to nsIBaseWindow");
         return false;
     }
 
-    if (!InitWidget(size)) {
+    if (!InitRenderingState()) {
         // We can fail to initialize our widget if the <browser
         // remote> has already been destroyed, and we couldn't hook
         // into the parent-process's layer system.  That's not a fatal
         // error.
         return true;
     }
 
-    baseWindow->InitWindow(0, mWidget,
-                           0, 0, size.width, size.height);
-    baseWindow->Create();
-
-    nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mWebNav);
-    MOZ_ASSERT(docShell);
-
-    if (docShell) {
-      docShell->SetAppId(mAppId);
-      if (mIsBrowserElement) {
-        docShell->SetIsBrowserElement();
-      }
-    }
-
     baseWindow->SetVisibility(true);
 
-    // IPC uses a WebBrowser object for which DNS prefetching is turned off
-    // by default. But here we really want it, so enable it explicitly
-    nsCOMPtr<nsIWebBrowserSetup> webBrowserSetup = do_QueryInterface(baseWindow);
-    if (webBrowserSetup) {
-      webBrowserSetup->SetProperty(nsIWebBrowserSetup::SETUP_ALLOW_DNS_PREFETCH,
-                                   true);
-    } else {
-        NS_WARNING("baseWindow doesn't QI to nsIWebBrowserSetup, skipping "
-                   "DNS prefetching enable step.");
-    }
-
     return InitTabChildGlobal();
 }
 
 bool
 TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size)
 {
 #ifdef DEBUG
     printf("[TabChild] Update Dimensions to (x,y,w,h)= (%ud, %ud, %ud, %ud) and move to (w,h)= (%ud, %ud)\n", rect.x, rect.y, rect.width, rect.height, size.width, size.height);
@@ -1155,68 +1222,57 @@ TabChild::AllocPRenderFrame(ScrollingBeh
 bool
 TabChild::DeallocPRenderFrame(PRenderFrameChild* aFrame)
 {
     delete aFrame;
     return true;
 }
 
 bool
-TabChild::InitTabChildGlobal()
+TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading)
 {
-  if (mCx && mTabChildGlobal)
-    return true;
+  if (!mCx && !mTabChildGlobal) {
+    nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
+    NS_ENSURE_TRUE(window, false);
+    nsCOMPtr<nsIDOMEventTarget> chromeHandler =
+      do_QueryInterface(window->GetChromeEventHandler());
+    NS_ENSURE_TRUE(chromeHandler, false);
 
-  nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
-  NS_ENSURE_TRUE(window, false);
-  nsCOMPtr<nsIDOMEventTarget> chromeHandler =
-    do_QueryInterface(window->GetChromeEventHandler());
-  NS_ENSURE_TRUE(chromeHandler, false);
+    nsRefPtr<TabChildGlobal> scope = new TabChildGlobal(this);
+    NS_ENSURE_TRUE(scope, false);
 
-  nsRefPtr<TabChildGlobal> scope = new TabChildGlobal(this);
-  NS_ENSURE_TRUE(scope, false);
-
-  mTabChildGlobal = scope;
+    mTabChildGlobal = scope;
 
-  nsISupports* scopeSupports =
-    NS_ISUPPORTS_CAST(nsIDOMEventTarget*, scope);
+    nsISupports* scopeSupports = NS_ISUPPORTS_CAST(nsIDOMEventTarget*, scope);
   
-  NS_ENSURE_TRUE(InitTabChildGlobalInternal(scopeSupports), false); 
+    NS_ENSURE_TRUE(InitTabChildGlobalInternal(scopeSupports), false); 
 
-  scope->Init();
+    scope->Init();
 
-  nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler);
-  NS_ENSURE_TRUE(root, false);
-  root->SetParentTarget(scope);
+    nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler);
+    NS_ENSURE_TRUE(root, false);
+    root->SetParentTarget(scope);
+  }
 
-  // Initialize the child side of the browser element machinery, if appropriate.
-  if (mIsBrowserElement || mAppId != nsIScriptSecurityManager::NO_APP_ID) {
-    RecvLoadRemoteScript(
-      NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"));
+  if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) {
+    mTriedBrowserInit = true;
+    // Initialize the child side of the browser element machinery,
+    // if appropriate.
+    if (mIsBrowserElement || mAppId != nsIScriptSecurityManager::NO_APP_ID) {
+      RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT);
+    }
   }
 
   return true;
 }
 
 bool
-TabChild::InitWidget(const nsIntSize& size)
+TabChild::InitRenderingState()
 {
-    NS_ABORT_IF_FALSE(!mWidget && !mRemoteFrame, "CreateWidget twice?");
-
-    mWidget = nsIWidget::CreatePuppetWidget(this);
-    if (!mWidget) {
-        NS_ERROR("couldn't create fake widget");
-        return false;
-    }
-    mWidget->Create(
-        nullptr, 0,              // no parents
-        nsIntRect(nsIntPoint(0, 0), size),
-        nullptr,                 // HandleWidgetEvent
-        nullptr                  // nsDeviceContext
-        );
+    static_cast<PuppetWidget*>(mWidget.get())->InitIMEState();
 
     LayersBackend be;
     uint64_t id;
     int32_t maxTextureSize;
     RenderFrameChild* remoteFrame =
         static_cast<RenderFrameChild*>(SendPRenderFrameConstructor(
                                            &mScrolling, &be, &maxTextureSize, &id));
     if (!remoteFrame) {
@@ -1247,29 +1303,53 @@ TabChild::InitWidget(const nsIntSize& si
     ShadowLayerForwarder* lf =
         mWidget->GetLayerManager(shadowManager, be)->AsShadowForwarder();
     NS_ABORT_IF_FALSE(lf && lf->HasShadowManager(),
                       "PuppetWidget should have shadow manager");
     lf->SetParentBackendType(be);
     lf->SetMaxTextureSize(maxTextureSize);
 
     mRemoteFrame = remoteFrame;
+
+    nsCOMPtr<nsIObserverService> observerService =
+        do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
+
+    if (observerService) {
+        observerService->AddObserver(this,
+                                     "cancel-default-pan-zoom",
+                                     false);
+        observerService->AddObserver(this,
+                                     "browser-zoom-to-rect",
+                                     false);
+    }
+
     return true;
 }
 
 void
 TabChild::SetBackgroundColor(const nscolor& aColor)
 {
   if (mLastBackgroundColor != aColor) {
     mLastBackgroundColor = aColor;
     SendSetBackgroundColor(mLastBackgroundColor);
   }
 }
 
 void
+TabChild::GetDPI(float* aDPI)
+{
+    *aDPI = -1.0;
+    if (!mRemoteFrame) {
+        return;
+    }
+
+    SendGetDPI(aDPI);
+}
+
+void
 TabChild::NotifyPainted()
 {
     if (UseDirectCompositor() && !mNotified) {
         mRemoteFrame->SendNotifyCompositorTransaction();
         mNotified = true;
     }
 }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -144,26 +144,28 @@ class TabChild : public PBrowserChild,
                  public nsIDialogCreator,
                  public nsITabChild,
                  public nsIObserver
 {
     typedef mozilla::layout::RenderFrameChild RenderFrameChild;
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
 
 public:
-    /**
-     * Create a new TabChild object.
-     *
-     * |aIsBrowserElement| indicates whether the tab is inside an <iframe mozbrowser>.
-     * |aAppId| is the app id of the app containing this tab. If the tab isn't
-     * contained in an app, aAppId will be nsIScriptSecurityManager::NO_APP_ID.
+    /** 
+     * This is expected to be called off the critical path to content
+     * startup.  This is an opportunity to load things that are slow
+     * on the critical path.
      */
-    TabChild(uint32_t aChromeFlags, bool aIsBrowserElement, uint32_t aAppId);
+    static void PreloadSlowThings();
+
+    /** Return a TabChild with the given attributes. */
+    static already_AddRefed<TabChild> 
+    Create(uint32_t aChromeFlags, bool aIsBrowserElement, uint32_t aAppId);
+
     virtual ~TabChild();
-    nsresult Init();
 
     uint32_t GetAppId() { return mAppId; }
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBBROWSERCHROME
     NS_DECL_NSIWEBBROWSERCHROME2
     NS_DECL_NSIEMBEDDINGSITEWINDOW
     NS_DECL_NSIWEBBROWSERCHROMEFOCUS
@@ -254,16 +256,19 @@ public:
     virtual bool DeallocPOfflineCacheUpdate(POfflineCacheUpdateChild* offlineCacheUpdate);
 
     nsIWebNavigation* WebNavigation() { return mWebNav; }
 
     JSContext* GetJSContext() { return mCx; }
 
     nsIPrincipal* GetPrincipal() { return mPrincipal; }
 
+    /** Return the DPI of the widget this TabChild draws to. */
+    void GetDPI(float* aDPI);
+
     void SetBackgroundColor(const nscolor& aColor);
 
     void NotifyPainted();
 
     bool IsAsyncPanZoomEnabled();
 
 protected:
     virtual PRenderFrameChild* AllocPRenderFrame(ScrollingBehavior* aScrolling,
@@ -276,22 +281,36 @@ protected:
     nsEventStatus DispatchWidgetEvent(nsGUIEvent& event);
 
     virtual PIndexedDBChild* AllocPIndexedDB(const nsCString& aASCIIOrigin,
                                              bool* /* aAllowed */);
 
     virtual bool DeallocPIndexedDB(PIndexedDBChild* aActor);
 
 private:
+    /**
+     * Create a new TabChild object.
+     *
+     * |aIsBrowserElement| indicates whether the tab is inside an <iframe mozbrowser>.
+     * |aAppId| is the app id of the app containing this tab. If the tab isn't
+     * contained in an app, aAppId will be nsIScriptSecurityManager::NO_APP_ID.
+     */
+    TabChild(uint32_t aChromeFlags, bool aIsBrowserElement, uint32_t aAppId);
+
+    nsresult Init();
+
+    void SetAppBrowserConfig(bool aIsBrowserElement, uint32_t aAppId);
+
     bool UseDirectCompositor();
 
     void ActorDestroy(ActorDestroyReason why);
 
-    bool InitTabChildGlobal();
-    bool InitWidget(const nsIntSize& size);
+    enum FrameScriptLoading { DONT_LOAD_SCRIPTS, DEFAULT_LOAD_SCRIPTS };
+    bool InitTabChildGlobal(FrameScriptLoading aScriptLoading = DEFAULT_LOAD_SCRIPTS);
+    bool InitRenderingState();
     void DestroyWindow();
 
     // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
     void DoFakeShow();
 
     // Wraps up a JSON object as a structured clone and sends it to the browser
     // chrome script.
     //
@@ -311,20 +330,21 @@ private:
     nsCOMPtr<nsIWebNavigation> mWebNav;
     nsCOMPtr<nsIWidget> mWidget;
     RenderFrameChild* mRemoteFrame;
     nsRefPtr<TabChildGlobal> mTabChildGlobal;
     uint32_t mChromeFlags;
     nsIntRect mOuterRect;
     nscolor mLastBackgroundColor;
     ScrollingBehavior mScrolling;
+    uint32_t mAppId;
     bool mDidFakeShow;
     bool mIsBrowserElement;
     bool mNotified;
-    uint32_t mAppId;
+    bool mTriedBrowserInit;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 inline TabChild*
 GetTabChildFrom(nsIDocShell* aDocShell)
 {
     nsCOMPtr<nsITabChild> tc = do_GetInterface(aDocShell);
--- a/widget/xpwidgets/PuppetWidget.cpp
+++ b/widget/xpwidgets/PuppetWidget.cpp
@@ -94,34 +94,41 @@ PuppetWidget::Create(nsIWidget        *a
   mEnabled = true;
   mVisible = true;
 
   mSurface = gfxPlatform::GetPlatform()
              ->CreateOffscreenSurface(gfxIntSize(1, 1),
                                       gfxASurface::ContentFromFormat(gfxASurface::ImageFormatARGB32));
 
   mIMEComposing = false;
-  if (MightNeedIMEFocus(aInitData)) {
-    uint32_t chromeSeqno;
-    mTabChild->SendNotifyIMEFocus(false, &mIMEPreference, &chromeSeqno);
-    mIMELastBlurSeqno = mIMELastReceivedSeqno = chromeSeqno;
-  }
+  mNeedIMEStateInit = MightNeedIMEFocus(aInitData);
 
   PuppetWidget* parent = static_cast<PuppetWidget*>(aParent);
   if (parent) {
     parent->SetChild(this);
     mLayerManager = parent->GetLayerManager();
   }
   else {
     Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false);
   }
 
   return NS_OK;
 }
 
+void
+PuppetWidget::InitIMEState()
+{
+  if (mNeedIMEStateInit) {
+    uint32_t chromeSeqno;
+    mTabChild->SendNotifyIMEFocus(false, &mIMEPreference, &chromeSeqno);
+    mIMELastBlurSeqno = mIMELastReceivedSeqno = chromeSeqno;
+    mNeedIMEStateInit = false;
+  }
+}
+
 already_AddRefed<nsIWidget>
 PuppetWidget::CreateChild(const nsIntRect  &aRect,
                           nsDeviceContext *aContext,
                           nsWidgetInitData *aInitData,
                           bool             aForceUseIWidgetParent)
 {
   bool isPopup = IsPopup(aInitData);
   nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(mTabChild);
@@ -532,17 +539,17 @@ PuppetWidget::PaintTask::Run()
   return NS_OK;
 }
 
 float
 PuppetWidget::GetDPI()
 {
   if (mDPI < 0) {
     NS_ABORT_IF_FALSE(mTabChild, "Need TabChild to get the DPI from!");
-    mTabChild->SendGetDPI(&mDPI);
+    mTabChild->GetDPI(&mDPI);
   }
 
   return mDPI;
 }
 
 void*
 PuppetWidget::GetNativeData(uint32_t aDataType)
 {
--- a/widget/xpwidgets/PuppetWidget.h
+++ b/widget/xpwidgets/PuppetWidget.h
@@ -47,16 +47,18 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD Create(nsIWidget*        aParent,
                     nsNativeWidget    aNativeParent,
                     const nsIntRect&  aRect,
                     nsDeviceContext*  aContext,
                     nsWidgetInitData* aInitData = nullptr);
 
+  void InitIMEState();
+
   virtual already_AddRefed<nsIWidget>
   CreateChild(const nsIntRect  &aRect,
               nsDeviceContext  *aContext,
               nsWidgetInitData *aInitData = nullptr,
               bool             aForceUseIWidgetParent = false);
 
   NS_IMETHOD Destroy();
 
@@ -201,16 +203,17 @@ private:
   bool mIMEComposing;
   // Latest seqno received through events
   uint32_t mIMELastReceivedSeqno;
   // Chrome's seqno value when last blur occurred
   // arriving events with seqno up to this should be discarded
   // Note that if seqno overflows (~50 days at 1 ms increment rate),
   // events will be discarded until new focus/blur occurs
   uint32_t mIMELastBlurSeqno;
+  bool mNeedIMEStateInit;
 
   // The DPI of the screen corresponding to this widget
   float mDPI;
 };
 
 class PuppetScreen : public nsBaseScreen
 {
 public: