Bug 846906 - Implement nsAppShellService::CreateWindowlessBrowser; r=bz; sr=benjamin
authorEddy Bruel <ejpbruel@mozilla.com>
Thu, 04 Apr 2013 15:24:32 +0200
changeset 127713 429e15d02de3ac6933b20456a855b79ccbeabf14
parent 127712 4170679cb85ae13bfed63596c5b6504c0343a6dc
child 127714 d68412295d0fb4c38601dc8f4e3f3ad9ac38432b
push id24512
push userryanvm@gmail.com
push dateFri, 05 Apr 2013 20:13:49 +0000
treeherdermozilla-central@139b6ba547fa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, benjamin
bugs846906
milestone23.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 846906 - Implement nsAppShellService::CreateWindowlessBrowser; r=bz; sr=benjamin
docshell/test/chrome/Makefile.in
docshell/test/chrome/bug846906.html
docshell/test/chrome/test_bug846906.xul
widget/xpwidgets/PuppetWidget.cpp
xpfe/appshell/public/nsIAppShellService.idl
xpfe/appshell/src/nsAppShellService.cpp
--- a/docshell/test/chrome/Makefile.in
+++ b/docshell/test/chrome/Makefile.in
@@ -92,16 +92,18 @@ MOCHITEST_CHROME_FILES =	\
 		bug311007_window.xul \
 		test_principalInherit.xul \
 		test_mozFrameType.xul \
 		mozFrameType_window.xul \
 		test_bug789773.xul \
 		test_private_hidden_window.html \
     docshell_helpers.js \
     generic.html \
+    test_bug846906.xul \
+    bug846906.html \
     $(NULL)
 
 ifneq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 MOCHITEST_CHROME_FILES += \
 		test_bug454235.xul \
 		$(NULL)
 else
 $(filter disabled-temporarily--bug-684176, test_bug454235.xul)
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/bug846906.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>
+    </title>
+  </head>
+  <body>
+    <div id="div1" style="width:1024px; height:768px; border:none;">
+	</div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/test_bug846906.xul
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=846906
+-->
+<window title="Mozilla Bug 846906"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <title>Test for Bug 846906</title>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 846906 **/
+  SimpleTest.waitForExplicitFinish();
+
+  var appShellService = Components.classes["@mozilla.org/appshell/appShellService;1"]
+                                  .getService(Components.interfaces.nsIAppShellService);
+  ok(appShellService, "Should be able to get app shell service");
+
+  var webNavigation = appShellService.createWindowlessBrowser();
+  ok(webNavigation, "Should be able to create windowless browser");
+
+  var interfaceRequestor = webNavigation.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+  ok(interfaceRequestor, "Should be able to query interface requestor interface");
+
+  var docShell = interfaceRequestor.getInterface(Components.interfaces.nsIDocShell);
+  ok(docShell, "Should be able to get doc shell interface");
+ 
+  var document = webNavigation.document;
+  ok(document, "Should be able to get document");
+
+  var iframe = document.createElement("iframe");
+  ok(iframe, "Should be able to create iframe");
+
+  iframe.onload = function () {
+    ok(true, "Should receive initial onload event");
+
+    iframe.onload = function () {
+        ok(true, "Should receive onload event");
+
+        var contentDocument = iframe.contentDocument;
+        ok(contentDocument, "Should be able to get content document");
+
+        var div = contentDocument.getElementById("div1");
+        ok(div, "Should be able to get element by id");
+
+        var rect = div.getBoundingClientRect();
+        ok(rect, "Should be able to get bounding client rect");
+
+        // xxx: can we do better than hardcoding these values here?
+        is(rect.width, 1024);
+        is(rect.height, 768);
+
+        SimpleTest.finish();
+    };
+    iframe.setAttribute("src", "http://mochi.test:8888/chrome/docshell/test/chrome/bug846906.html");
+  };
+  document.documentElement.appendChild(iframe);
+    
+  ]]>
+  </script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=846906"
+     target="_blank">Mozilla Bug 846906</a>
+  </body>
+</window>
--- a/widget/xpwidgets/PuppetWidget.cpp
+++ b/widget/xpwidgets/PuppetWidget.cpp
@@ -31,17 +31,17 @@ InvalidateRegion(nsIWidget* aWidget, con
   while(const nsIntRect* r = it.Next()) {
     aWidget->Invalidate(*r);
   }
 }
 
 /*static*/ already_AddRefed<nsIWidget>
 nsIWidget::CreatePuppetWidget(TabChild* aTabChild)
 {
-  NS_ABORT_IF_FALSE(nsIWidget::UsePuppetWidgets(),
+  NS_ABORT_IF_FALSE(!aTabChild || nsIWidget::UsePuppetWidgets(),
                     "PuppetWidgets not allowed in this configuration");
 
   nsCOMPtr<nsIWidget> widget = new PuppetWidget(aTabChild);
   return widget.forget();
 }
 
 namespace mozilla {
 namespace widget {
--- a/xpfe/appshell/public/nsIAppShellService.idl
+++ b/xpfe/appshell/public/nsIAppShellService.idl
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsISupports.idl"
 
 interface nsIXULWindow;
+interface nsIWebNavigation;
 interface nsIURI;
 interface nsIDOMWindow;
 interface nsIAppShell;
 
 [ptr] native JSContext(JSContext);
 
 %{C++
 struct JSContext;
@@ -38,16 +39,18 @@ interface nsIAppShellService : nsISuppor
    */
   const long SIZE_TO_CONTENT = -1;
   nsIXULWindow createTopLevelWindow(in nsIXULWindow aParent,
                                     in nsIURI aUrl, 
                                     in uint32_t aChromeMask,
                                     in long aInitialWidth,
                                     in long aInitialHeight);
 
+  nsIWebNavigation createWindowlessBrowser();
+
   [noscript]
   void createHiddenWindow();
 
   void destroyHiddenWindow();
 
   /**
    * Return the (singleton) application hidden window, automatically created
    * and maintained by this AppShellService.
--- a/xpfe/appshell/src/nsAppShellService.cpp
+++ b/xpfe/appshell/src/nsAppShellService.cpp
@@ -41,16 +41,20 @@
 #include "nsIUnicodeDecoder.h"
 #include "nsIChromeRegistry.h"
 #include "nsILoadContext.h"
 #include "nsIWebNavigation.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/StartupTimeline.h"
 
+#include "nsEmbedCID.h"
+#include "nsIWebBrowser.h"
+#include "nsIDocShell.h"
+
 using namespace mozilla;
 
 // Default URL for the hidden window, can be overridden by a pref on Mac
 #define DEFAULT_HIDDENWINDOW_URL "resource://gre-resources/hiddenWindow.html"
 
 class nsIAppShell;
 
 nsAppShellService::nsAppShellService() : 
@@ -198,16 +202,172 @@ nsAppShellService::CreateTopLevelWindow(
     if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT)
       parent = aParent;
     (*aResult)->SetZLevel(CalculateWindowZLevel(parent, aChromeMask));
   }
 
   return rv;
 }
 
+/*
+ * This class provides a stub implementation of nsIWebBrowserChrome2, as needed
+ * by nsAppShellService::CreateWindowlessBrowser
+ */
+class WebBrowserChrome2Stub : public nsIWebBrowserChrome2,
+                              public nsIInterfaceRequestor {
+public:
+    virtual ~WebBrowserChrome2Stub() {}
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIWEBBROWSERCHROME
+    NS_DECL_NSIWEBBROWSERCHROME2
+    NS_DECL_NSIINTERFACEREQUESTOR
+};
+
+NS_INTERFACE_MAP_BEGIN(WebBrowserChrome2Stub)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome)
+  NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
+  NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2)
+  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(WebBrowserChrome2Stub)
+NS_IMPL_RELEASE(WebBrowserChrome2Stub)
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetStatus(uint32_t aStatusType, const PRUnichar* aStatus)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetWebBrowser(nsIWebBrowser** aWebBrowser)
+{
+  NS_NOTREACHED("WebBrowserChrome2Stub::GetWebBrowser is not supported");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetWebBrowser(nsIWebBrowser* aWebBrowser)
+{
+  NS_NOTREACHED("WebBrowserChrome2Stub::SetWebBrowser is not supported");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetChromeFlags(uint32_t* aChromeFlags)
+{
+  *aChromeFlags = 0;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetChromeFlags(uint32_t aChromeFlags)
+{
+  NS_NOTREACHED("WebBrowserChrome2Stub::SetChromeFlags is not supported");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::DestroyBrowserWindow()
+{
+  NS_NOTREACHED("WebBrowserChrome2Stub::DestroyBrowserWindow is not supported");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SizeBrowserTo(int32_t aCX, int32_t aCY)
+{
+  NS_NOTREACHED("WebBrowserChrome2Stub::SizeBrowserTo is not supported");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::ShowAsModal()
+{
+  NS_NOTREACHED("WebBrowserChrome2Stub::ShowAsModal is not supported");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::IsWindowModal(bool* aResult)
+{
+  *aResult = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::ExitModalEventLoop(nsresult aStatus)
+{
+  NS_NOTREACHED("WebBrowserChrome2Stub::ExitModalEventLoop is not supported");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetStatusWithContext(uint32_t aStatusType,
+                                            const nsAString& aStatusText,
+                                            nsISupports* aStatusContext)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetInterface(const nsIID & aIID, void **aSink)
+{
+    return QueryInterface(aIID, aSink);
+}
+
+NS_IMETHODIMP
+nsAppShellService::CreateWindowlessBrowser(nsIWebNavigation **aResult)
+{
+  /* First, we create an instance of nsWebBrowser. Instances of this class have
+   * an associated doc shell, which is what we're interested in.
+   */
+  nsCOMPtr<nsIWebBrowser> browser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID);
+  if (!browser) {
+    NS_ERROR("Couldn't create instance of nsWebBrowser!");
+    return NS_ERROR_FAILURE;
+  }
+
+  /* Next, we set the container window for our instance of nsWebBrowser. Since
+   * we don't actually have a window, we instead set the container window to be
+   * an instance of WebBrowserChrome2Stub, which provides a stub implementation
+   * of nsIWebBrowserChrome2.
+   */
+  nsRefPtr<WebBrowserChrome2Stub> stub = new WebBrowserChrome2Stub();
+  if (!stub) {
+    NS_ERROR("Couldn't create instance of WebBrowserChrome2Stub!");
+    return NS_ERROR_FAILURE;
+  }
+  browser->SetContainerWindow(stub);
+
+  nsCOMPtr<nsIWebNavigation> navigation = do_QueryInterface(browser);
+
+  nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(navigation);
+  item->SetItemType(nsIDocShellTreeItem::typeContentWrapper);
+
+  /* A windowless web browser doesn't have an associated OS level window. To
+   * accomplish this, we initialize the window associated with our instance of
+   * nsWebBrowser with an instance of PuppetWidget, which provides a stub
+   * implementation of nsIWidget.
+   */
+  nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(nullptr);
+  if (!widget) {
+    NS_ERROR("Couldn't create instance of PuppetWidget");
+    return NS_ERROR_FAILURE;
+  }
+  widget->Create(nullptr, 0, nsIntRect(nsIntPoint(0, 0), nsIntSize(0, 0)),
+                 nullptr, nullptr);
+  nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(navigation);
+  window->InitWindow(0, widget, 0, 0, 0, 0);
+  window->Create();
+
+  navigation.forget(aResult);
+  return NS_OK;
+}
+
 uint32_t
 nsAppShellService::CalculateWindowZLevel(nsIXULWindow *aParent,
                                          uint32_t      aChromeMask)
 {
   uint32_t zLevel;
 
   zLevel = nsIXULWindow::normalZ;
   if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RAISED)