Bug 983920 - Port window.sidebar and window.external to WebIDL; r=bzbarsky
authorEhsan Akhgari <ehsan@mozilla.com>
Fri, 28 Mar 2014 00:03:03 -0400
changeset 175832 d9e6a6c40a57393d318e236558f799481cf7074f
parent 175831 1843b416780650776b58f594da4b57fbd6128231
child 175833 7d5848e153010a645c109343cf57cd118c4e078b
push id26500
push userkwierso@gmail.com
push dateFri, 28 Mar 2014 23:16:18 +0000
treeherdermozilla-central@88ae1bfaaf3d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs983920
milestone31.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 983920 - Port window.sidebar and window.external to WebIDL; r=bzbarsky
browser/components/sidebar/nsSidebar.js
browser/components/sidebar/nsSidebar.manifest
browser/metro/components/Sidebar.js
browser/metro/components/components.manifest
dom/base/moz.build
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/moz.build
dom/interfaces/sidebar/moz.build
dom/interfaces/sidebar/nsISidebar.idl
dom/tests/mochitest/bugs/test_bug857555.html
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/External.webidl
dom/webidl/Window.webidl
dom/webidl/moz.build
mobile/android/components/MobileComponents.manifest
mobile/android/components/Sidebar.js
--- a/browser/components/sidebar/nsSidebar.js
+++ b/browser/components/sidebar/nsSidebar.js
@@ -5,19 +5,16 @@
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const DEBUG = false; /* set to false to suppress debug messages */
 
 const SIDEBAR_CONTRACTID        = "@mozilla.org/sidebar;1";
 const SIDEBAR_CID               = Components.ID("{22117140-9c6e-11d3-aaf1-00805f8a4905}");
-const nsISidebar                = Components.interfaces.nsISidebar;
-const nsISidebarExternal        = Components.interfaces.nsISidebarExternal;
-const nsIClassInfo              = Components.interfaces.nsIClassInfo;
 
 // File extension for Sherlock search plugin description files
 const SHERLOCK_FILE_EXT_REGEXP = /\.src$/i;
 
 function nsSidebar()
 {
 }
 
@@ -114,23 +111,17 @@ function (aDescriptionURL)
 // http://msdn.microsoft.com/en-us/library/aa342526%28VS.85%29.aspx .
 // XXX Implement this!
 nsSidebar.prototype.IsSearchProviderInstalled =
 function (aSearchURL)
 {
   return 0;
 }
 
-nsSidebar.prototype.classInfo = XPCOMUtils.generateCI({classID: SIDEBAR_CID,
-                                                       contractID: SIDEBAR_CONTRACTID,
-                                                       classDescription: "Sidebar",
-                                                       interfaces: [nsISidebar, nsISidebarExternal],
-                                                       flags: nsIClassInfo.DOM_OBJECT});
-
-nsSidebar.prototype.QueryInterface = XPCOMUtils.generateQI([nsISidebar, nsISidebarExternal]);
+nsSidebar.prototype.QueryInterface = XPCOMUtils.generateQI([Components.interfaces.nsISupports]);
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsSidebar]);
 
 /* static functions */
 if (DEBUG)
     debug = function (s) { dump("-*- sidebar component: " + s + "\n"); }
 else
     debug = function (s) {}
--- a/browser/components/sidebar/nsSidebar.manifest
+++ b/browser/components/sidebar/nsSidebar.manifest
@@ -1,4 +1,2 @@
 component {22117140-9c6e-11d3-aaf1-00805f8a4905} nsSidebar.js
 contract @mozilla.org/sidebar;1 {22117140-9c6e-11d3-aaf1-00805f8a4905}
-category JavaScript-global-property sidebar @mozilla.org/sidebar;1
-category JavaScript-global-property external @mozilla.org/sidebar;1
--- a/browser/metro/components/Sidebar.js
+++ b/browser/metro/components/Sidebar.js
@@ -64,17 +64,16 @@ Sidebar.prototype = {
                                                   [brandName], 1);
       Services.prompt.alert(null, title, msg);
       return false;
     }
     
     return true;
   },
 
-  // =========================== nsISidebar ===========================
   // The suggestedTitle and suggestedCategory parameters are ignored, but remain
   // for backward compatibility.
   addSearchEngine: function addSearchEngine(engineURL, iconURL, suggestedTitle,
                                             suggestedCategory) {
     if (!this._validateSearchEngine(engineURL, iconURL))
       return;
 
     // File extension for Sherlock search plugin description files
@@ -87,17 +86,16 @@ Sidebar.prototype = {
     if (SHERLOCK_FILE_EXT_REGEXP.test(engineURL))
       dataType = Ci.nsISearchEngine.DATA_TEXT;
     else
       dataType = Ci.nsISearchEngine.DATA_XML;
 
     Services.search.addEngine(engineURL, dataType, iconURL, true);
   },
 
-  // =========================== nsISidebarExternal ===========================
   // This function exists to implement window.external.AddSearchProvider(),
   // to match other browsers' APIs.  The capitalization, although nonstandard here,
   // is therefore important.
   AddSearchProvider: function AddSearchProvider(aDescriptionURL) {
     if (!this._validateSearchEngine(aDescriptionURL, ""))
       return;
 
     if (this.inContentProcess) {
@@ -117,25 +115,16 @@ Sidebar.prototype = {
   // stemming from difficulties in determining what domain issued the request.
   // See bug 340604 and
   // http://msdn.microsoft.com/en-us/library/aa342526%28VS.85%29.aspx .
   // XXX Implement this!
   IsSearchProviderInstalled: function IsSearchProviderInstalled(aSearchURL) {
     return 0;
   },
 
-  // =========================== nsIClassInfo ===========================
-  classInfo: XPCOMUtils.generateCI({classID: SIDEBAR_CID,
-                                    contractID: SIDEBAR_CONTRACTID,
-                                    interfaces: [Ci.nsISidebar,
-                                                 Ci.nsISidebarExternal],
-                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
-                                    classDescription: "Sidebar"}),
-
   // =========================== nsISupports ===========================
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISidebar,
-                                         Ci.nsISidebarExternal]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
 
   // XPCOMUtils stuff
   classID: SIDEBAR_CID,
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Sidebar]);
--- a/browser/metro/components/components.manifest
+++ b/browser/metro/components/components.manifest
@@ -17,18 +17,16 @@ contract @mozilla.org/network/protocol/a
 # DirectoryProvider.js
 component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js
 contract @mozilla.org/browser/directory-provider;1 {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b}
 category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1
 
 # Sidebar.js
 component {22117140-9c6e-11d3-aaf1-00805f8a4905} Sidebar.js
 contract @mozilla.org/sidebar;1 {22117140-9c6e-11d3-aaf1-00805f8a4905}
-category JavaScript-global-property sidebar @mozilla.org/sidebar;1
-category JavaScript-global-property external @mozilla.org/sidebar;1
 category wakeup-request Sidebar @mozilla.org/sidebar;1,nsISidebarExternal,getService,Sidebar:AddSearchProvider
 
 # SessionStore.js
 component {8c1f07d6-cba3-4226-a315-8bd43d67d032} SessionStore.js
 contract @mozilla.org/browser/sessionstore;1 {8c1f07d6-cba3-4226-a315-8bd43d67d032}
 category app-startup SessionStore service,@mozilla.org/browser/sessionstore;1
 
 # BrowserStartup.js
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -169,8 +169,11 @@ FINAL_LIBRARY = 'gklayout'
 LOCAL_INCLUDES += [
     '/js/xpconnect/src',
     '/js/xpconnect/wrappers',
 ]
 
 for var in ('MOZ_JSDEBUGGER', 'MOZ_B2G_RIL', 'MOZ_B2G_FM'):
     if CONFIG[var]:
         DEFINES[var] = True
+
+if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
+    DEFINES['HAVE_SIDEBAR'] = True
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -211,16 +211,19 @@
 #include "mozilla/dom/AudioContext.h"
 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
+#ifdef HAVE_SIDEBAR
+#include "mozilla/dom/ExternalBinding.h"
+#endif
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesis.h"
 #endif
 
 #ifdef MOZ_JSDEBUGGER
 #include "jsdIDebuggerService.h"
 #endif
@@ -1441,16 +1444,18 @@ nsGlobalWindow::CleanUp()
   mHistory = nullptr;
   mFrames = nullptr;
   mWindowUtils = nullptr;
   mApplicationCache = nullptr;
   mIndexedDB = nullptr;
 
   mConsole = nullptr;
 
+  mExternal = nullptr;
+
   mPerformance = nullptr;
 
 #ifdef MOZ_WEBSPEECH
   mSpeechSynthesis = nullptr;
 #endif
 
   ClearControllers();
 
@@ -1756,16 +1761,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
   nsGlobalWindow::CleanupCachedXBLHandlers(tmp);
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
@@ -1814,16 +1820,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 #ifdef DEBUG
 void
 nsGlobalWindow::RiskyUnlink()
 {
   NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
 }
@@ -13492,16 +13499,64 @@ nsGlobalWindow::GetConsole(ErrorResult& 
 
   if (!mConsole) {
     mConsole = new Console(this);
   }
 
   return mConsole;
 }
 
+already_AddRefed<External>
+nsGlobalWindow::GetExternal(ErrorResult& aRv)
+{
+  FORWARD_TO_INNER_OR_THROW(GetExternal, (aRv), aRv, nullptr);
+
+#ifdef HAVE_SIDEBAR
+  if (!mExternal) {
+    AutoJSContext cx;
+    JS::Rooted<JSObject*> jsImplObj(cx);
+    ConstructJSImplementation(cx, "@mozilla.org/sidebar;1",
+                              this, &jsImplObj, aRv);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+    mExternal = new External(jsImplObj, this);
+  }
+
+  nsRefPtr<External> external = static_cast<External*>(mExternal.get());
+  return external.forget();
+#else
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  return nullptr;
+#endif
+}
+
+void
+nsGlobalWindow::GetSidebar(OwningExternalOrWindowProxy& aResult,
+                           ErrorResult& aRv)
+{
+  FORWARD_TO_INNER_OR_THROW(GetSidebar, (aResult, aRv), aRv, );
+
+#ifdef HAVE_SIDEBAR
+  // First check for a named frame named "sidebar"
+  nsCOMPtr<nsIDOMWindow> domWindow = GetChildWindow(NS_LITERAL_STRING("sidebar"));
+  if (domWindow) {
+    aResult.SetAsWindowProxy() = domWindow.forget();
+    return;
+  }
+
+  nsRefPtr<External> external = GetExternal(aRv);
+  if (external) {
+    aResult.SetAsExternal() = external;
+  }
+#else
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+#endif
+}
+
 #ifdef MOZ_B2G
 void
 nsGlobalWindow::EnableNetworkEvent(uint32_t aType)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIPermissionManager> permMgr =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -102,20 +102,22 @@ struct nsRect;
 
 class nsWindowSizes;
 
 namespace mozilla {
 class Selection;
 namespace dom {
 class BarProp;
 class Console;
+class External;
 class Function;
 class Gamepad;
 class MediaQueryList;
 class Navigator;
+class OwningExternalOrWindowProxy;
 class SpeechSynthesis;
 class WakeLock;
 namespace indexedDB {
 class IDBFactory;
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
 
@@ -818,16 +820,20 @@ public:
                                       const nsAString& aName,
                                       const nsAString& aOptions,
                                       mozilla::ErrorResult& aError);
   mozilla::dom::Navigator* GetNavigator(mozilla::ErrorResult& aError);
   nsIDOMOfflineResourceList* GetApplicationCache(mozilla::ErrorResult& aError);
 
   mozilla::dom::Console* GetConsole(mozilla::ErrorResult& aRv);
 
+  void GetSidebar(mozilla::dom::OwningExternalOrWindowProxy& aResult,
+                  mozilla::ErrorResult& aRv);
+  already_AddRefed<mozilla::dom::External> GetExternal(mozilla::ErrorResult& aRv);
+
 protected:
   bool AlertOrConfirm(bool aAlert, const nsAString& aMessage,
                       mozilla::ErrorResult& aError);
 
 public:
   void Alert(const nsAString& aMessage, mozilla::ErrorResult& aError);
   bool Confirm(const nsAString& aMessage, mozilla::ErrorResult& aError);
   void Prompt(const nsAString& aMessage, const nsAString& aInitial,
@@ -1450,16 +1456,21 @@ protected:
   nsRefPtr<mozilla::dom::BarProp> mStatusbar;
   nsRefPtr<mozilla::dom::BarProp> mScrollbars;
   nsRefPtr<nsDOMWindowUtils>    mWindowUtils;
   nsString                      mStatus;
   nsString                      mDefaultStatus;
   nsGlobalWindowObserver*       mObserver; // Inner windows only.
   nsCOMPtr<nsIDOMCrypto>        mCrypto;
   nsRefPtr<mozilla::dom::Console> mConsole;
+  // We need to store an nsISupports pointer to this object because the
+  // mozilla::dom::External class doesn't exist on b2g and using the type
+  // forward declared here means that ~nsGlobalWindow wouldn't compile because
+  // it wouldn't see the ~External function's declaration.
+  nsCOMPtr<nsISupports>         mExternal;
 
   nsCOMPtr<nsIDOMStorage>      mLocalStorage;
   nsCOMPtr<nsIDOMStorage>      mSessionStorage;
 
   nsCOMPtr<nsIXPConnectJSObjectHolder> mInnerWindowHolder;
 
   // These member variable are used only on inner windows.
   nsRefPtr<mozilla::EventListenerManager> mListenerManager;
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1983,63 +1983,74 @@ ConstructJSImplementation(JSContext* aCx
 {
   // Get the window to use as a parent and for initialization.
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
+  ConstructJSImplementation(aCx, aContractId, window, aObject, aRv);
+
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+  return window.forget();
+}
+
+void
+ConstructJSImplementation(JSContext* aCx, const char* aContractId,
+                          nsPIDOMWindow* aWindow,
+                          JS::MutableHandle<JSObject*> aObject,
+                          ErrorResult& aRv)
+{
   // Make sure to divorce ourselves from the calling JS while creating and
   // initializing the object, so exceptions from that will get reported
   // properly, since those are never exceptions that a spec wants to be thrown.
   {
     AutoSystemCaller asc;
 
     // Get the XPCOM component containing the JS implementation.
     nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId);
     if (!implISupports) {
       NS_WARNING("Failed to get JS implementation for contract");
       aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
+      return;
     }
     // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer.
     nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi =
       do_QueryInterface(implISupports);
     if (gpi) {
       JS::Rooted<JS::Value> initReturn(aCx);
-      nsresult rv = gpi->Init(window, &initReturn);
+      nsresult rv = gpi->Init(aWindow, &initReturn);
       if (NS_FAILED(rv)) {
         aRv.Throw(rv);
-        return nullptr;
+        return;
       }
       // With JS-implemented WebIDL, the return value of init() is not used to determine
       // if init() failed, so init() should only return undefined. Any kind of permission
       // or pref checking must happen by adding an attribute to the WebIDL interface.
       if (!initReturn.isUndefined()) {
         MOZ_ASSERT(false, "The init() method for JS-implemented WebIDL should not return anything");
         MOZ_CRASH();
       }
     }
     // Extract the JS implementation from the XPCOM object.
     nsCOMPtr<nsIXPConnectWrappedJS> implWrapped =
       do_QueryInterface(implISupports);
     MOZ_ASSERT(implWrapped, "Failed to get wrapped JS from XPCOM component.");
     if (!implWrapped) {
       aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
+      return;
     }
     aObject.set(implWrapped->GetJSObject());
     if (!aObject) {
       aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
     }
   }
-
-  return window.forget();
 }
 
 bool
 NonVoidByteStringToJsval(JSContext *cx, const nsACString &str,
                          JS::MutableHandle<JS::Value> rval)
 {
     // ByteStrings are not UTF-8 encoded.
     JSString* jsStr = JS_NewStringCopyN(cx, str.Data(), str.Length());
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2288,16 +2288,22 @@ GetUnforgeableHolder(JSObject* aGlobal, 
 
 // Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
 // interface, get the nsPIDOMWindow corresponding to the content side, if any.
 // A false return means an exception was thrown.
 bool
 GetWindowForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
                                 nsPIDOMWindow** window);
 
+void
+ConstructJSImplementation(JSContext* aCx, const char* aContractId,
+                          nsPIDOMWindow* aWindow,
+                          JS::MutableHandle<JSObject*> aObject,
+                          ErrorResult& aRv);
+
 already_AddRefed<nsPIDOMWindow>
 ConstructJSImplementation(JSContext* aCx, const char* aContractId,
                           const GlobalObject& aGlobal,
                           JS::MutableHandle<JSObject*> aObject,
                           ErrorResult& aRv);
 
 /**
  * Convert an nsCString to jsval, returning true on success.
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -85,8 +85,12 @@ if CONFIG['MOZ_AUDIO_CHANNEL_MANAGER']:
     LOCAL_INCLUDES += [
         '/dom/system/gonk',
     ]
 
 FINAL_LIBRARY = 'xul'
 
 SPHINX_TREES['webidl'] = 'docs'
 SPHINX_PYTHON_PACKAGE_DIRS += ['mozwebidlcodegen']
+
+if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
+    # This is needed for Window.webidl
+    DEFINES['HAVE_SIDEBAR'] = True
--- a/dom/interfaces/sidebar/moz.build
+++ b/dom/interfaces/sidebar/moz.build
@@ -1,13 +1,12 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 XPIDL_SOURCES += [
-    'nsISidebar.idl',
     'nsIWebContentHandlerRegistrar.idl',
 ]
 
 XPIDL_MODULE = 'dom_sidebar'
 
deleted file mode 100644
--- a/dom/interfaces/sidebar/nsISidebar.idl
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * 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"
-
-[scriptable, uuid(351887ca-56b2-4458-96fc-88baeb57b6e7)]
-interface nsISidebar : nsISupports
-{
-  void addSearchEngine(in DOMString engineURL, in DOMString iconURL,
-                       in DOMString suggestedTitle, in DOMString suggestedCategory);
-};
-
-[scriptable, uuid(5895076f-e28e-434a-9fdb-a69f94eb323f)]
-interface nsISidebarExternal : nsISupports
-{
-  void AddSearchProvider(in DOMString aDescriptionURL);
-  unsigned long IsSearchProviderInstalled(in DOMString aSearchURL);
-};
-
-%{ C++
-// {577CB744-8CAF-11d3-AAEF-00805F8A4905} 
-#define NS_SIDEBAR_CID \
-{ 0x577cb744, 0x8caf, 0x11d3, { 0xaa, 0xef, 0x0, 0x80, 0x5f, 0x8a, 0x49, 0x5 } }
-
-#define NS_SIDEBAR_CONTRACTID "@mozilla.org/sidebar;1"
-%}
--- a/dom/tests/mochitest/bugs/test_bug857555.html
+++ b/dom/tests/mochitest/bugs/test_bug857555.html
@@ -11,25 +11,23 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript">
 
   /** Test for Bug 857555 **/
   SimpleTest.waitForExplicitFinish();
 
   addLoadEvent(function() {
     is(content, $("t").contentWindow, "'content' as iframe name should work");
     is(sidebar, $("u").contentWindow, "'sidebar' as iframe name should work");
-    is(external, $("v").contentWindow, "'external' as iframe name should work");
     SimpleTest.finish();
   });
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=857555">Mozilla Bug 857555</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   <iframe name="content" id="t"></iframe>
   <iframe name="sidebar" id="u"></iframe>
-  <iframe name="external" id="v"></iframe>
 </div>
 <pre id="test">
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -343,16 +343,18 @@ var interfaceNamesInGlobalScope =
     "Event",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "EventListenerInfo",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "EventSource",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "EventTarget",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "External", b2g: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "File",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileHandle",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileReader",
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/External.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; 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/.
+ */
+
+[JSImplementation="@mozilla.org/sidebar;1"]
+interface External
+{
+  void AddSearchProvider(DOMString aDescriptionURL);
+  unsigned long IsSearchProviderInstalled(DOMString aSearchURL);
+};
+
+// Mozilla extension
+partial interface External {
+  void addSearchEngine(DOMString engineURL, DOMString iconURL,
+                       DOMString suggestedTitle, DOMString suggestedCategory);
+};
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -60,17 +60,19 @@ typedef any Transferable;
   [Throws] WindowProxy? open(optional DOMString url = "", optional DOMString target = "", optional DOMString features = "");
   // We think the indexed getter is a bug in the spec, it actually needs to live
   // on the WindowProxy
   //getter WindowProxy (unsigned long index);
   //getter object (DOMString name);
 
   // the user agent
   [Throws] readonly attribute Navigator navigator; 
-  //(Not implemented)readonly attribute External external;
+#ifdef HAVE_SIDEBAR
+  [Replaceable, Throws] readonly attribute External external;
+#endif
   [Throws] readonly attribute ApplicationCache applicationCache;
 
   // user prompts
   [Throws] void alert(optional DOMString message = "");
   [Throws] boolean confirm(optional DOMString message = "");
   [Throws] DOMString? prompt(optional DOMString message = "", optional DOMString default = "");
   [Throws] void print();
   //[Throws] any showModalDialog(DOMString url, optional any argument);
@@ -343,16 +345,23 @@ Window implements TouchEventHandlers;
 Window implements OnErrorEventHandlerForWindow;
 
 // ConsoleAPI
 partial interface Window {
   [Replaceable, GetterThrows]
   readonly attribute Console console;
 };
 
+#ifdef HAVE_SIDEBAR
+// Mozilla extension
+partial interface Window {
+  [Replaceable, Throws]
+  readonly attribute (External or WindowProxy) sidebar;
+};
+#endif
 
 [ChromeOnly] interface ChromeWindow {
   [Func="nsGlobalWindow::IsChromeWindow"]
   const unsigned short STATE_MAXIMIZED = 1;
   [Func="nsGlobalWindow::IsChromeWindow"]
   const unsigned short STATE_MINIMIZED = 2;
   [Func="nsGlobalWindow::IsChromeWindow"]
   const unsigned short STATE_NORMAL = 3;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -616,8 +616,13 @@ if CONFIG['MOZ_B2G_BT']:
         'BluetoothDeviceEvent.webidl',
         'BluetoothStatusChangedEvent.webidl',
     ]
 
 if CONFIG['MOZ_BUILD_APP'] in ['browser', 'xulrunner']:
     WEBIDL_FILES += [
         'BrowserFeedWriter.webidl',
     ]
+
+if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
+    WEBIDL_FILES += [
+        'External.webidl',
+    ]
--- a/mobile/android/components/MobileComponents.manifest
+++ b/mobile/android/components/MobileComponents.manifest
@@ -22,18 +22,16 @@ contract @mozilla.org/network/protocol/a
 # DirectoryProvider.js
 component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js
 contract @mozilla.org/browser/directory-provider;1 {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b}
 category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1
 
 # Sidebar.js
 component {22117140-9c6e-11d3-aaf1-00805f8a4905} Sidebar.js
 contract @mozilla.org/sidebar;1 {22117140-9c6e-11d3-aaf1-00805f8a4905}
-category JavaScript-global-property sidebar @mozilla.org/sidebar;1
-category JavaScript-global-property external @mozilla.org/sidebar;1
 category wakeup-request Sidebar @mozilla.org/sidebar;1,nsISidebarExternal,getService,Sidebar:AddSearchProvider
 
 # SessionStore.js
 component {8c1f07d6-cba3-4226-a315-8bd43d67d032} SessionStore.js
 contract @mozilla.org/browser/sessionstore;1 {8c1f07d6-cba3-4226-a315-8bd43d67d032}
 category app-startup SessionStore service,@mozilla.org/browser/sessionstore;1
 
 # stylesheets
--- a/mobile/android/components/Sidebar.js
+++ b/mobile/android/components/Sidebar.js
@@ -86,17 +86,16 @@ Sidebar.prototype = {
     if (SHERLOCK_FILE_EXT_REGEXP.test(engineURL))
       dataType = Ci.nsISearchEngine.DATA_TEXT;
     else
       dataType = Ci.nsISearchEngine.DATA_XML;
 
     Services.search.addEngine(engineURL, dataType, iconURL, true);
   },
 
-  // =========================== nsISidebarExternal ===========================
   // This function exists to implement window.external.AddSearchProvider(),
   // to match other browsers' APIs.  The capitalization, although nonstandard here,
   // is therefore important.
   AddSearchProvider: function AddSearchProvider(aDescriptionURL) {
     if (!this._validateSearchEngine(aDescriptionURL, ""))
       return;
 
     if (this.inContentProcess) {
@@ -116,25 +115,16 @@ Sidebar.prototype = {
   // stemming from difficulties in determining what domain issued the request.
   // See bug 340604 and
   // http://msdn.microsoft.com/en-us/library/aa342526%28VS.85%29.aspx .
   // XXX Implement this!
   IsSearchProviderInstalled: function IsSearchProviderInstalled(aSearchURL) {
     return 0;
   },
 
-  // =========================== nsIClassInfo ===========================
-  classInfo: XPCOMUtils.generateCI({classID: SIDEBAR_CID,
-                                    contractID: SIDEBAR_CONTRACTID,
-                                    interfaces: [Ci.nsISidebar,
-                                                 Ci.nsISidebarExternal],
-                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
-                                    classDescription: "Sidebar"}),
-
   // =========================== nsISupports ===========================
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISidebar,
-                                         Ci.nsISidebarExternal]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
 
   // XPCOMUtils stuff
   classID: SIDEBAR_CID,
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Sidebar]);