Bug 1529684 - Part 3: Create BrowsingContext for remote iframes in embedder, r=farre
authorNika Layzell <nika@thelayzells.com>
Thu, 14 Mar 2019 18:50:47 +0000
changeset 521927 6e96eaaa1b90
parent 521926 a95dcf86a9b0
child 521928 0a3848628be9
push id10870
push usernbeleuzu@mozilla.com
push dateFri, 15 Mar 2019 20:00:07 +0000
treeherdermozilla-beta@c594aee5b7a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfarre
bugs1529684
milestone67.0a1
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 1529684 - Part 3: Create BrowsingContext for remote iframes in embedder, r=farre Depends on D21096 Differential Revision: https://phabricator.services.mozilla.com/D21097
dom/base/nsFrameLoader.cpp
dom/ipc/BrowserBridgeChild.cpp
dom/ipc/BrowserBridgeChild.h
dom/ipc/BrowserBridgeParent.cpp
dom/ipc/BrowserBridgeParent.h
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -1899,16 +1899,30 @@ static already_AddRefed<BrowsingContext>
   }
 
   BrowsingContext::Type type = aIsContent ? BrowsingContext::Type::Content
                                           : BrowsingContext::Type::Chrome;
 
   return BrowsingContext::Create(aParentContext, aOpenerContext, aName, type);
 }
 
+static void GetFrameName(Element* aOwnerContent, nsAString& aFrameName) {
+  int32_t namespaceID = aOwnerContent->GetNameSpaceID();
+  if (namespaceID == kNameSpaceID_XHTML && !aOwnerContent->IsInHTMLDocument()) {
+    aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aFrameName);
+  } else {
+    aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, aFrameName);
+    // XXX if no NAME then use ID, after a transition period this will be
+    // changed so that XUL only uses ID too (bug 254284).
+    if (aFrameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
+      aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aFrameName);
+    }
+  }
+}
+
 nsresult nsFrameLoader::MaybeCreateDocShell() {
   if (mDocShell) {
     return NS_OK;
   }
   if (IsRemoteFrame()) {
     return NS_OK;
   }
   NS_ENSURE_STATE(!mDestroyCalled);
@@ -1940,28 +1954,17 @@ nsresult nsFrameLoader::MaybeCreateDocSh
     return NS_ERROR_UNEXPECTED;
   }
 
   RefPtr<BrowsingContext> parentBC = parentDocShell->GetBrowsingContext();
   MOZ_ASSERT(parentBC, "docShell must have BrowsingContext");
 
   // Determine the frame name for the new browsing context.
   nsAutoString frameName;
-
-  int32_t namespaceID = mOwnerContent->GetNameSpaceID();
-  if (namespaceID == kNameSpaceID_XHTML && !mOwnerContent->IsInHTMLDocument()) {
-    mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
-  } else {
-    mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, frameName);
-    // XXX if no NAME then use ID, after a transition period this will be
-    // changed so that XUL only uses ID too (bug 254284).
-    if (frameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
-      mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
-    }
-  }
+  GetFrameName(mOwnerContent, frameName);
 
   // Check if our new context is chrome or content
   bool isContent = parentBC->IsContent() ||
                    mOwnerContent->AttrValueIs(kNameSpaceID_None, TypeAttrName(),
                                               nsGkAtoms::content, eIgnoreCase);
 
   // Force mozbrowser frames to always be content, even if the mozbrowser
   // interfaces are disabled.
@@ -2573,18 +2576,27 @@ bool nsFrameLoader::TryRemoteBrowser() {
       Unused << window->GetNextTabParentId(&nextTabParentId);
     }
   }
 
   nsCOMPtr<Element> ownerElement = mOwnerContent;
 
   // If we're in a content process, create a BrowserBridgeChild actor.
   if (XRE_IsContentProcess()) {
+    // Determine the frame name for the new browsing context.
+    nsAutoString frameName;
+    GetFrameName(mOwnerContent, frameName);
+
+    // XXX(nika): due to limitations with Browsing Context Groups and multiple
+    // processes, we can't link up aParent yet! (Bug 1532661)
+    RefPtr<BrowsingContext> browsingContext =
+        CreateBrowsingContext(nullptr, nullptr, frameName, true);
+
     mBrowserBridgeChild = BrowserBridgeChild::Create(
-        this, context, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
+        this, context, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), browsingContext);
     return !!mBrowserBridgeChild;
   }
 
   mRemoteBrowser =
       ContentParent::CreateBrowser(context, ownerElement, openerContentParent,
                                    sameTabGroupAs, nextTabParentId);
   if (!mRemoteBrowser) {
     return false;
@@ -3109,28 +3121,38 @@ already_AddRefed<mozilla::dom::Promise> 
 }
 
 already_AddRefed<nsITabParent> nsFrameLoader::GetTabParent() {
   return do_AddRef(mRemoteBrowser);
 }
 
 already_AddRefed<nsILoadContext> nsFrameLoader::LoadContext() {
   nsCOMPtr<nsILoadContext> loadContext;
-  if (IsRemoteFrame() && (mRemoteBrowser || TryRemoteBrowser())) {
-    loadContext = mRemoteBrowser->GetLoadContext();
+  if (IsRemoteFrame() &&
+      (mRemoteBrowser || mBrowserBridgeChild || TryRemoteBrowser())) {
+    if (mRemoteBrowser) {
+      loadContext = mRemoteBrowser->GetLoadContext();
+    } else {
+      loadContext = mBrowserBridgeChild->GetLoadContext();
+    }
   } else {
     loadContext = do_GetInterface(ToSupports(GetDocShell(IgnoreErrors())));
   }
   return loadContext.forget();
 }
 
 already_AddRefed<BrowsingContext> nsFrameLoader::GetBrowsingContext() {
   RefPtr<BrowsingContext> browsingContext;
-  if (IsRemoteFrame() && (mRemoteBrowser || TryRemoteBrowser())) {
-    browsingContext = mRemoteBrowser->GetBrowsingContext();
+  if (IsRemoteFrame() &&
+      (mRemoteBrowser || mBrowserBridgeChild || TryRemoteBrowser())) {
+    if (mRemoteBrowser) {
+      browsingContext = mRemoteBrowser->GetBrowsingContext();
+    } else {
+      browsingContext = mBrowserBridgeChild->GetBrowsingContext();
+    }
   } else if (GetDocShell(IgnoreErrors())) {
     browsingContext = nsDocShell::Cast(mDocShell)->GetBrowsingContext();
   }
   return browsingContext.forget();
 }
 
 void nsFrameLoader::InitializeBrowserAPI() {
   if (!OwnerIsMozBrowserFrame()) {
--- a/dom/ipc/BrowserBridgeChild.cpp
+++ b/dom/ipc/BrowserBridgeChild.cpp
@@ -1,51 +1,57 @@
 /* -*- 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 "mozilla/dom/BrowserBridgeChild.h"
+#include "mozilla/dom/BrowsingContext.h"
 #include "nsFocusManager.h"
 #include "nsFrameLoader.h"
 #include "nsFrameLoaderOwner.h"
 #include "nsQueryObject.h"
 
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace dom {
 
-BrowserBridgeChild::BrowserBridgeChild(nsFrameLoader* aFrameLoader)
-    : mLayersId{0}, mIPCOpen(true), mFrameLoader(aFrameLoader) {}
+BrowserBridgeChild::BrowserBridgeChild(nsFrameLoader* aFrameLoader,
+                                       BrowsingContext* aBrowsingContext)
+    : mLayersId{0},
+      mIPCOpen(true),
+      mFrameLoader(aFrameLoader),
+      mBrowsingContext(aBrowsingContext) {}
 
 BrowserBridgeChild::~BrowserBridgeChild() {}
 
 already_AddRefed<BrowserBridgeChild> BrowserBridgeChild::Create(
     nsFrameLoader* aFrameLoader, const TabContext& aContext,
-    const nsString& aRemoteType) {
+    const nsString& aRemoteType, BrowsingContext* aBrowsingContext) {
   MOZ_ASSERT(XRE_IsContentProcess());
 
   // Determine our embedder's TabChild actor.
   RefPtr<Element> owner = aFrameLoader->GetOwnerContent();
   MOZ_DIAGNOSTIC_ASSERT(owner);
 
   nsCOMPtr<nsIDocShell> docShell = do_GetInterface(owner->GetOwnerGlobal());
   MOZ_DIAGNOSTIC_ASSERT(docShell);
 
   RefPtr<TabChild> tabChild = TabChild::GetFrom(docShell);
   MOZ_DIAGNOSTIC_ASSERT(tabChild);
 
   RefPtr<BrowserBridgeChild> browserBridge =
-      new BrowserBridgeChild(aFrameLoader);
+      new BrowserBridgeChild(aFrameLoader, aBrowsingContext);
   // Reference is freed in TabChild::DeallocPBrowserBridgeChild.
   tabChild->SendPBrowserBridgeConstructor(
       do_AddRef(browserBridge).take(),
-      PromiseFlatString(aContext.PresentationURL()), aRemoteType);
+      PromiseFlatString(aContext.PresentationURL()), aRemoteType,
+      aBrowsingContext);
   browserBridge->mIPCOpen = true;
 
   return browserBridge.forget();
 }
 
 void BrowserBridgeChild::UpdateDimensions(const nsIntRect& aRect,
                                           const mozilla::ScreenIntSize& aSize) {
   MOZ_DIAGNOSTIC_ASSERT(mIPCOpen);
--- a/dom/ipc/BrowserBridgeChild.h
+++ b/dom/ipc/BrowserBridgeChild.h
@@ -7,34 +7,40 @@
 #ifndef mozilla_dom_BrowserBridgeChild_h
 #define mozilla_dom_BrowserBridgeChild_h
 
 #include "mozilla/dom/PBrowserBridgeChild.h"
 #include "mozilla/dom/TabChild.h"
 
 namespace mozilla {
 namespace dom {
+class BrowsingContext;
 
 /**
  * Child side for a remote frame.
  */
 class BrowserBridgeChild : public PBrowserBridgeChild {
  public:
   NS_INLINE_DECL_REFCOUNTING(BrowserBridgeChild);
 
   TabChild* Manager() {
     MOZ_ASSERT(mIPCOpen);
     return static_cast<TabChild*>(PBrowserBridgeChild::Manager());
   }
 
   mozilla::layers::LayersId GetLayersId() { return mLayersId; }
 
+  BrowsingContext* GetBrowsingContext() { return mBrowsingContext; }
+
+  // XXX(nika): We should have a load context here. (bug 1532664)
+  nsILoadContext* GetLoadContext() { return nullptr; }
+
   static already_AddRefed<BrowserBridgeChild> Create(
       nsFrameLoader* aFrameLoader, const TabContext& aContext,
-      const nsString& aRemoteType);
+      const nsString& aRemoteType, BrowsingContext* aBrowsingContext);
 
   void UpdateDimensions(const nsIntRect& aRect,
                         const mozilla::ScreenIntSize& aSize);
 
   void NavigateByKey(bool aForward, bool aForDocumentNavigation);
 
   void Activate();
 
@@ -53,20 +59,22 @@ class BrowserBridgeChild : public PBrows
   mozilla::ipc::IPCResult RecvRequestFocus(const bool& aCanRaise);
 
   mozilla::ipc::IPCResult RecvMoveFocus(const bool& aForward,
                                         const bool& aForDocumentNavigation);
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
  private:
-  explicit BrowserBridgeChild(nsFrameLoader* aFrameLoader);
+  explicit BrowserBridgeChild(nsFrameLoader* aFrameLoader,
+                              BrowsingContext* aBrowsingContext);
   ~BrowserBridgeChild();
 
   mozilla::layers::LayersId mLayersId;
   bool mIPCOpen;
   RefPtr<nsFrameLoader> mFrameLoader;
+  RefPtr<BrowsingContext> mBrowsingContext;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_BrowserBridgeParent_h)
--- a/dom/ipc/BrowserBridgeParent.cpp
+++ b/dom/ipc/BrowserBridgeParent.cpp
@@ -21,17 +21,18 @@ BrowserBridgeParent::BrowserBridgeParent
 
 BrowserBridgeParent::~BrowserBridgeParent() {
   if (mTabParent) {
     mTabParent->mBrowserBridgeParent = nullptr;
   }
 }
 
 nsresult BrowserBridgeParent::Init(const nsString& aPresentationURL,
-                                   const nsString& aRemoteType) {
+                                   const nsString& aRemoteType,
+                                   CanonicalBrowsingContext* aBrowsingContext) {
   mIPCOpen = true;
 
   // FIXME: This should actually use a non-bogus TabContext, probably inherited
   // from our Manager().
   OriginAttributes attrs;
   attrs.mInIsolatedMozBrowser = false;
   attrs.mAppId = nsIScriptSecurityManager::NO_APP_ID;
   attrs.SyncAttributesWithPrivateBrowsing(false);
@@ -45,43 +46,37 @@ nsresult BrowserBridgeParent::Init(const
   RefPtr<ContentParent> constructorSender =
       ContentParent::GetNewOrUsedBrowserProcess(
           nullptr, aRemoteType, initialPriority, nullptr, false);
   if (NS_WARN_IF(!constructorSender)) {
     MOZ_ASSERT(false, "Unable to allocate content process!");
     return NS_ERROR_FAILURE;
   }
 
-  // FIXME: This BrowsingContext should be provided by our embedder!
-  RefPtr<CanonicalBrowsingContext> browsingContext =
-      BrowsingContext::Create(nullptr, nullptr, EmptyString(),
-                              BrowsingContext::Type::Content)
-          .downcast<CanonicalBrowsingContext>();
-
   // Ensure that our content process is subscribed to our newly created
   // BrowsingContextGroup.
-  browsingContext->Group()->EnsureSubscribed(constructorSender);
-  browsingContext->SetOwnerProcessId(constructorSender->ChildID());
+  aBrowsingContext->Group()->EnsureSubscribed(constructorSender);
+  aBrowsingContext->SetOwnerProcessId(constructorSender->ChildID());
 
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   TabId tabId(nsContentUtils::GenerateTabId());
   cpm->RegisterRemoteFrame(tabId, ContentParentId(0), TabId(0),
                            tabContext.AsIPCTabContext(),
                            constructorSender->ChildID());
 
   // Construct the TabParent object for our subframe.
   uint32_t chromeFlags = 0;
   RefPtr<TabParent> tabParent(new TabParent(constructorSender, tabId,
-                                            tabContext, browsingContext,
+                                            tabContext, aBrowsingContext,
                                             chromeFlags, this));
 
   PBrowserParent* browser = constructorSender->SendPBrowserConstructor(
       // DeallocPBrowserParent() releases this ref.
       tabParent.forget().take(), tabId, TabId(0), tabContext.AsIPCTabContext(),
-      chromeFlags, constructorSender->ChildID(), browsingContext,
+      chromeFlags, constructorSender->ChildID(), aBrowsingContext,
       constructorSender->IsForBrowser());
   if (NS_WARN_IF(!browser)) {
     MOZ_ASSERT(false, "Browser Constructor Failed");
     return NS_ERROR_FAILURE;
   }
 
   // Set our TabParent object to the newly created browser.
   mTabParent = TabParent::GetFrom(browser);
--- a/dom/ipc/BrowserBridgeParent.h
+++ b/dom/ipc/BrowserBridgeParent.h
@@ -15,20 +15,25 @@ namespace dom {
 
 class BrowserBridgeParent : public PBrowserBridgeParent {
  public:
   NS_INLINE_DECL_REFCOUNTING(BrowserBridgeParent);
 
   BrowserBridgeParent();
 
   // Initialize this actor after performing startup.
-  nsresult Init(const nsString& aPresentationURL, const nsString& aRemoteType);
+  nsresult Init(const nsString& aPresentationURL, const nsString& aRemoteType,
+                CanonicalBrowsingContext* aBrowsingContext);
 
   TabParent* GetTabParent() { return mTabParent; }
 
+  CanonicalBrowsingContext* GetBrowsingContext() {
+    return mTabParent->GetBrowsingContext();
+  }
+
   // Get our manager actor.
   TabParent* Manager() {
     MOZ_ASSERT(mIPCOpen);
     return static_cast<TabParent*>(PBrowserBridgeParent::Manager());
   }
 
  protected:
   friend class PBrowserBridgeParent;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -173,17 +173,18 @@ parent:
      * Construct a new WindowGlobal actor for a window global in the given
      * BrowsingContext and with the given principal.
      */
     async PWindowGlobal(WindowGlobalInit init);
 
     /**
      * Construct a new Remote iframe actor.
      */
-    async PBrowserBridge(nsString aPresentationURL, nsString aRemoteType);
+    async PBrowserBridge(nsString aPresentationURL, nsString aRemoteType,
+                         BrowsingContext aBrowsingContext);
 
     /**
      * Sends an NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW to be adopted by the
      * widget's shareable window on the chrome side. Only used on Windows.
      */
     async SetNativeChildOfShareableWindow(uintptr_t childWindow);
 
     /**
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3190,17 +3190,18 @@ PWindowGlobalChild* TabChild::AllocPWind
 
 bool TabChild::DeallocPWindowGlobalChild(PWindowGlobalChild* aActor) {
   // This reference was added in WindowGlobalChild::Create.
   static_cast<WindowGlobalChild*>(aActor)->Release();
   return true;
 }
 
 PBrowserBridgeChild* TabChild::AllocPBrowserBridgeChild(const nsString&,
-                                                        const nsString&) {
+                                                        const nsString&,
+                                                        BrowsingContext*) {
   MOZ_CRASH(
       "We should never be manually allocating PBrowserBridgeChild actors");
   return nullptr;
 }
 
 bool TabChild::DeallocPBrowserBridgeChild(PBrowserBridgeChild* aActor) {
   // This reference was added in BrowserBridgeChild::Create.
   static_cast<BrowserBridgeChild*>(aActor)->Release();
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -669,17 +669,18 @@ class TabChild final : public TabChildBa
   virtual ~TabChild();
 
   virtual PWindowGlobalChild* AllocPWindowGlobalChild(
       const WindowGlobalInit& aInit) override;
 
   virtual bool DeallocPWindowGlobalChild(PWindowGlobalChild* aActor) override;
 
   virtual PBrowserBridgeChild* AllocPBrowserBridgeChild(
-      const nsString& aName, const nsString& aRemoteType) override;
+      const nsString& aName, const nsString& aRemoteType,
+      BrowsingContext* aBrowsingContext) override;
 
   virtual bool DeallocPBrowserBridgeChild(PBrowserBridgeChild* aActor) override;
 
   virtual mozilla::ipc::IPCResult RecvDestroy() override;
 
   virtual mozilla::ipc::IPCResult RecvSetDocShellIsActive(
       const bool& aIsActive) override;
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -144,17 +144,19 @@ namespace mozilla {
 namespace dom {
 
 TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr;
 
 NS_IMPL_ISUPPORTS(TabParent, nsITabParent, nsIAuthPromptProvider,
                   nsISupportsWeakReference)
 
 TabParent::TabParent(ContentParent* aManager, const TabId& aTabId,
-                     const TabContext& aContext, CanonicalBrowsingContext* aBrowsingContext, uint32_t aChromeFlags,
+                     const TabContext& aContext,
+                     CanonicalBrowsingContext* aBrowsingContext,
+                     uint32_t aChromeFlags,
                      BrowserBridgeParent* aBrowserBridgeParent)
     : TabContext(aContext),
       mFrameElement(nullptr),
       mContentCache(*this),
       mRect(0, 0, 0, 0),
       mDimensions(0, 0),
       mOrientation(0),
       mDPI(0),
@@ -1014,23 +1016,25 @@ PWindowGlobalParent* TabParent::AllocPWi
 bool TabParent::DeallocPWindowGlobalParent(PWindowGlobalParent* aActor) {
   // Free reference from AllocPWindowGlobalParent.
   static_cast<WindowGlobalParent*>(aActor)->Release();
   return true;
 }
 
 IPCResult TabParent::RecvPBrowserBridgeConstructor(
     PBrowserBridgeParent* aActor, const nsString& aName,
-    const nsString& aRemoteType) {
-  static_cast<BrowserBridgeParent*>(aActor)->Init(aName, aRemoteType);
+    const nsString& aRemoteType, BrowsingContext* aBrowsingContext) {
+  static_cast<BrowserBridgeParent*>(aActor)->Init(
+      aName, aRemoteType, CanonicalBrowsingContext::Cast(aBrowsingContext));
   return IPC_OK();
 }
 
 PBrowserBridgeParent* TabParent::AllocPBrowserBridgeParent(
-    const nsString& aName, const nsString& aRemoteType) {
+    const nsString& aName, const nsString& aRemoteType,
+    BrowsingContext* aBrowsingContext) {
   // Reference freed in DeallocPBrowserBridgeParent.
   return do_AddRef(new BrowserBridgeParent()).take();
 }
 
 bool TabParent::DeallocPBrowserBridgeParent(PBrowserBridgeParent* aActor) {
   // Free reference from AllocPBrowserBridgeParent.
   static_cast<BrowserBridgeParent*>(aActor)->Release();
   return true;
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -315,23 +315,24 @@ class TabParent final : public PBrowserP
   PWindowGlobalParent* AllocPWindowGlobalParent(const WindowGlobalInit& aInit);
 
   bool DeallocPWindowGlobalParent(PWindowGlobalParent* aActor);
 
   virtual mozilla::ipc::IPCResult RecvPWindowGlobalConstructor(
       PWindowGlobalParent* aActor, const WindowGlobalInit& aInit) override;
 
   PBrowserBridgeParent* AllocPBrowserBridgeParent(
-      const nsString& aPresentationURL, const nsString& aRemoteType);
+      const nsString& aPresentationURL, const nsString& aRemoteType,
+      BrowsingContext* aBrowsingContext);
 
   bool DeallocPBrowserBridgeParent(PBrowserBridgeParent* aActor);
 
   virtual mozilla::ipc::IPCResult RecvPBrowserBridgeConstructor(
       PBrowserBridgeParent* aActor, const nsString& aPresentationURL,
-      const nsString& aRemoteType) override;
+      const nsString& aRemoteType, BrowsingContext* aBrowsingContext) override;
 
   void LoadURL(nsIURI* aURI);
 
   void InitRendering();
   void MaybeShowFrame();
 
   // XXX/cjones: it's not clear what we gain by hiding these
   // message-sending functions under a layer of indirection and