Bug 1005818 - Part 2: Only limited browser API are available to a widget. r=kanru
☠☠ backed out by 860f2941b071 ☠ ☠
authorJunior Hsu <juhsu@mozilla.com>
Mon, 04 Aug 2014 09:06:26 +0800
changeset 200261 e71a3cac1b3dd3d77ce23cf7edb37f627bb8020b
parent 200260 e020d647d6d371b4da16dcb6f07b2b4c5d7add23
child 200262 721eb8e94100c8947b6c00e22ea69b4462d6050b
push id47860
push userryanvm@gmail.com
push dateTue, 19 Aug 2014 12:42:37 +0000
treeherdermozilla-inbound@bb91698edd20 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskanru
bugs1005818
milestone34.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 1005818 - Part 2: Only limited browser API are available to a widget. r=kanru 1. Add |ownerIsWidget| in nsIFrameLoader.idl 2. Add |GetReallyIsWidget| in nsIMozBrowserFrame.idl 3. Hide the methods of browser API of a widget 4. Hide security-sensitive mozbrowser events of a widget
content/base/public/nsIFrameLoader.idl
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameLoader.h
content/html/content/src/nsGenericHTMLFrameElement.cpp
dom/apps/tests/file_app.sjs
dom/apps/tests/file_invalidWidget_app.template.webapp
dom/apps/tests/file_widget_app.template.html
dom/apps/tests/mochitest.ini
dom/apps/tests/test_widget.html
dom/browser-element/BrowserElementParent.cpp
dom/browser-element/BrowserElementParent.jsm
dom/interfaces/html/nsIMozBrowserFrame.idl
--- a/content/base/public/nsIFrameLoader.idl
+++ b/content/base/public/nsIFrameLoader.idl
@@ -107,17 +107,17 @@ interface nsIContentViewManager : nsISup
                          [retval, array, size_is(aLength)] out nsIContentView aResult);
 
   /**
    * The root content view.
    */
   readonly attribute nsIContentView rootContentView;
 };
 
-[scriptable, builtinclass, uuid(d52ca6a8-8237-4ae0-91d7-7be4f1db24ef)]
+[scriptable, builtinclass, uuid(55a772b8-855a-4c5f-b4a1-284b6b3bec28)]
 interface nsIFrameLoader : nsISupports
 {
   /**
    * Get the docshell from the frame loader.
    */
   readonly attribute nsIDocShell docShell;
 
   /**
@@ -286,16 +286,23 @@ interface nsIFrameLoader : nsISupports
    */
   [infallible] attribute boolean visible;
 
   /**
    * Find out whether the owner content really is a browser or app frame
    * Especially, a widget frame is regarded as an app frame.
    */
   readonly attribute boolean ownerIsBrowserOrAppFrame;
+
+  /**
+   * Find out whether the owner content really is a widget. If this attribute
+   * returns true, |ownerIsBrowserOrAppFrame| must return true.
+   */
+  readonly attribute boolean ownerIsWidget;
+
 };
 
 %{C++
 class nsFrameLoader;
 %}
 
 native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
 
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -1470,16 +1470,32 @@ nsFrameLoader::OwnerIsBrowserOrAppFrame(
 NS_IMETHODIMP
 nsFrameLoader::GetOwnerIsBrowserOrAppFrame(bool* aResult)
 {
   *aResult = OwnerIsBrowserOrAppFrame();
   return NS_OK;
 }
 
 bool
+nsFrameLoader::OwnerIsWidget()
+{
+  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
+  return browserFrame ? browserFrame->GetReallyIsWidget() : false;
+}
+
+
+// The xpcom getter version
+NS_IMETHODIMP
+nsFrameLoader::GetOwnerIsWidget(bool* aResult)
+{
+  *aResult = OwnerIsWidget();
+  return NS_OK;
+}
+
+bool
 nsFrameLoader::OwnerIsAppFrame()
 {
   nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
   return browserFrame ? browserFrame->GetReallyIsApp() : false;
 }
 
 bool
 nsFrameLoader::OwnerIsBrowserFrame()
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -329,16 +329,22 @@ private:
   /**
    * Is this a frameloader for a bona fide <iframe mozbrowser> or
    * <iframe mozapp>?  (I.e., does the frame return true for
    * nsIMozBrowserFrame::GetReallyIsBrowserOrApp()?)
    */
   bool OwnerIsBrowserOrAppFrame();
 
   /**
+   * Is this a frameloader for a bona fide <iframe mozwidget>?  (I.e., does the
+   * frame return true for nsIMozBrowserFrame::GetReallyIsWidget()?)
+   */
+  bool OwnerIsWidget();
+
+  /**
    * Is this a frameloader for a bona fide <iframe mozapp>?  (I.e., does the
    * frame return true for nsIMozBrowserFrame::GetReallyIsApp()?)
    */
   bool OwnerIsAppFrame();
 
   /**
    * Is this a frame loader for a bona fide <iframe mozbrowser>?
    */
--- a/content/html/content/src/nsGenericHTMLFrameElement.cpp
+++ b/content/html/content/src/nsGenericHTMLFrameElement.cpp
@@ -314,16 +314,54 @@ nsGenericHTMLFrameElement::GetReallyIsAp
 {
   nsAutoString manifestURL;
   GetAppManifestURL(manifestURL);
 
   *aOut = !manifestURL.IsEmpty();
   return NS_OK;
 }
 
+namespace {
+
+bool WidgetsEnabled()
+{
+  static bool sMozWidgetsEnabled = false;
+  static bool sBoolVarCacheInitialized = false;
+
+  if (!sBoolVarCacheInitialized) {
+    sBoolVarCacheInitialized = true;
+    Preferences::AddBoolVarCache(&sMozWidgetsEnabled,
+                                 "dom.enable_widgets");
+  }
+
+  return sMozWidgetsEnabled;
+}
+
+} // anonymous namespace
+
+/* [infallible] */ NS_IMETHODIMP
+nsGenericHTMLFrameElement::GetReallyIsWidget(bool *aOut)
+{
+  *aOut = false;
+  if (!WidgetsEnabled()) {
+    return NS_OK;
+  }
+
+  nsAutoString appManifestURL;
+  GetManifestURLByType(nsGkAtoms::mozapp, appManifestURL);
+  bool isApp = !appManifestURL.IsEmpty();
+
+  nsAutoString widgetManifestURL;
+  GetManifestURLByType(nsGkAtoms::mozwidget, widgetManifestURL);
+  bool isWidget = !widgetManifestURL.IsEmpty();
+
+  *aOut = isWidget && !isApp;
+  return NS_OK;
+}
+
 /* [infallible] */ NS_IMETHODIMP
 nsGenericHTMLFrameElement::GetIsExpectingSystemMessage(bool *aOut)
 {
   *aOut = false;
 
   if (!nsIMozBrowserFrame::GetReallyIsApp()) {
     return NS_OK;
   }
@@ -399,17 +437,17 @@ nsGenericHTMLFrameElement::GetAppManifes
     return NS_OK;
   }
 
   nsAutoString appManifestURL;
   nsAutoString widgetManifestURL;
 
   GetManifestURLByType(nsGkAtoms::mozapp, appManifestURL);
 
-  if (Preferences::GetBool("dom.enable_widgets")) {
+  if (WidgetsEnabled()) {
     GetManifestURLByType(nsGkAtoms::mozwidget, widgetManifestURL);
   }
 
   bool isApp = !appManifestURL.IsEmpty();
   bool isWidget = !widgetManifestURL.IsEmpty();
 
   if (!isApp && !isWidget) {
     // No valid case to get manifest
--- a/dom/apps/tests/file_app.sjs
+++ b/dom/apps/tests/file_app.sjs
@@ -41,17 +41,17 @@ function handleRequest(request, response
     response.setHeader("Content-Type", "text/html", false);
     response.setHeader("Access-Control-Allow-Origin", "*", false);
     response.write('OK');
     return;
   }
 
   // Get the app type.
   var apptype = query.apptype;
-  if (apptype != 'hosted' && apptype != 'cached' && apptype != 'widget')
+  if (apptype != 'hosted' && apptype != 'cached' && apptype != 'widget'  && apptype != 'invalidWidget')
     throw "Invalid app type: " + apptype;
 
   // Get the version from server state and handle the etag.
   var version = Number(getState('version'));
   var etag = getEtag(request, version);
   dump("Server Etag: " + etag + "\n");
 
   if (etagMatches(request, etag)) {
@@ -78,17 +78,17 @@ function handleRequest(request, response
   //
   // NB: Among other reasons, we use the same sjs file here so that the version
   //     state is shared.
   if (apptype == 'cached' && 'getappcache' in query) {
     response.setHeader("Content-Type", "text/cache-manifest", false);
     response.write(makeResource(gAppcacheTemplatePath, version, apptype));
     return;
   }
-  else if (apptype == 'widget')
+  else if (apptype == 'widget' || apptype == 'invalidWidget')
   {
     response.setHeader("Content-Type", "text/html", false);
     response.write(makeResource(gWidgetTemplatePath, version, apptype));
     return;
   }
   // Generate the app.
   response.setHeader("Content-Type", "text/html", false);
   response.write(makeResource(gAppTemplatePath, version, apptype));
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/file_invalidWidget_app.template.webapp
@@ -0,0 +1,11 @@
+{
+  "name": "Really Rapid Release (invalid widget)",
+  "description": "Updated even faster than <a href='http://mozilla.org'>Firefox</a>, just to annoy slashdotters.",
+  "launch_path": "/tests/dom/apps/tests/file_app.sjs?apptype=invalidWidget",
+  "icons": {
+    "128": "ICONTOKEN"
+  },
+  "widgetPages": [
+    "/tests/dom/apps/tests/file_app.sjs?apptype=widget"
+  ]
+}
--- a/dom/apps/tests/file_widget_app.template.html
+++ b/dom/apps/tests/file_widget_app.template.html
@@ -1,68 +1,8 @@
 <html>
 <head>
-<script>
-
-function sendMessage(msg) {
-  alert(msg);
-}
-
-function ok(p, msg) {
-  if (p)
-    sendMessage("OK: " + msg);
-  else
-    sendMessage("KO: " + msg);
-}
-
-function is(a, b, msg) {
-  if (a == b)
-    sendMessage("OK: " + a + " == " + b + " - " + msg);
-  else
-    sendMessage("KO: " + a + " != " + b + " - " + msg);
-}
-
-function installed(p) {
-  if (p)
-    sendMessage("IS_INSTALLED");
-  else
-    sendMessage("NOT_INSTALLED");
-}
-
-function finish() {
-  sendMessage("VERSION: MyWebApp vVERSIONTOKEN");
-  sendMessage("DONE");
-}
-
-function cbError() {
-  ok(false, "Error callback invoked");
-  finish();
-}
-
-function go() {
-  ok(true, "Launched app");
-  var request = window.navigator.mozApps.getSelf();
-  request.onsuccess = function() {
-    var widget = request.result;
-    ok(widget,"Should be a widget");
-    checkWidget(widget);
-  }
-  request.onerror = cbError;
-}
-
-function checkWidget(widget) {
-  // If the widget is installed, |widget| will be non-null. If it is, verify its state.
-  installed(!!widget);
-  if (widget) {
-    var widgetName = "Really Rapid Release (APPTYPETOKEN)";
-    var manifest = SpecialPowers.wrap(widget.manifest);
-    is(manifest.name, widgetName, "Manifest name should be correct");
-    is(widget.origin, "http://test", "Widget origin should be correct");
-    is(widget.installOrigin, "http://mochi.test:8888", "Install origin should be correct");
-  }
-  finish();
-}
-
-</script>
 </head>
-<body onload="go();">
+<body>
 App Body. Version: VERSIONTOKEN
-</body></html>
+<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
+</body>
+</html>
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -2,16 +2,17 @@
 skip-if = e10s
 support-files =
   chromeAddCert.js
   file_app.sjs
   file_app.template.html
   file_cached_app.template.appcache
   file_cached_app.template.webapp
   file_hosted_app.template.webapp
+  file_invalidWidget_app.template.webapp
   file_packaged_app.sjs
   file_packaged_app.template.html
   file_packaged_app.template.webapp
   file_widget_app.template.webapp
   file_widget_app.template.html
   signed_app.sjs
   signed_app_template.webapp
   signed/*
--- a/dom/apps/tests/test_widget.html
+++ b/dom/apps/tests/test_widget.html
@@ -6,70 +6,165 @@
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <div id="container"></div>
   <script type="application/javascript;version=1.7">
 
   var gWidgetManifestURL = 'http://test/tests/dom/apps/tests/file_app.sjs?apptype=widget&getmanifest=true';
+  var gInvalidWidgetManifestURL = 'http://test/tests/dom/apps/tests/file_app.sjs?apptype=invalidWidget&getmanifest=true';
   var gApp;
 
-  function cbError() {
+  function onError() {
     ok(false, "Error callback invoked");
     finish();
   }
 
-  function installApp() {
-    var request = navigator.mozApps.install(gWidgetManifestURL);
-    request.onerror = cbError;
+  function installApp(path) {
+    var request = navigator.mozApps.install(path);
+    request.onerror = onError;
     request.onsuccess = function() {
       gApp = request.result;
 
       runTest();
     }
   }
 
   function uninstallApp() {
     // Uninstall the app.
     var request = navigator.mozApps.mgmt.uninstall(gApp);
-    request.onerror = cbError;
+    request.onerror = onError;
     request.onsuccess = function() {
       // All done.
       info("All done");
+
       runTest();
     }
   }
 
-  function testApp() {
+  function testApp(isValidWidget) {
+    info("Test widget feature. IsValidWidget: " + isValidWidget);
+
     var ifr = document.createElement('iframe');
     ifr.setAttribute('mozbrowser', 'true');
     ifr.setAttribute('mozwidget', gApp.manifestURL);
     ifr.setAttribute('src', gApp.origin+gApp.manifest.launch_path);
 
     var domParent = document.getElementById('container');
+    domParent.appendChild(ifr);
 
-    // Set us up to listen for messages from the app.
-    var listener = function(e) {
-      var message = e.detail.message;
-      if (/^OK/.exec(message)) {
-        ok(true, "Message from widget: " + message);
-      } else if (/KO/.exec(message)) {
-        ok(false, "Message from widget: " + message);
-      } else if (/DONE/.exec(message)) {
-        ok(true, "Message from widget complete");
-        ifr.removeEventListener('mozbrowsershowmodalprompt', listener);
-        domParent.removeChild(ifr);
-        runTest();
+    var mm = SpecialPowers.getBrowserFrameMessageManager(ifr);
+    mm.addMessageListener('OK', function(msg) {
+      ok(isValidWidget, "Message from widget: " + SpecialPowers.wrap(msg).json);
+    });
+    mm.addMessageListener('KO', function(msg) {
+      ok(!isValidWidget, "Message from widget: " + SpecialPowers.wrap(msg).json);
+    });
+    mm.addMessageListener('DONE', function(msg) {
+      ok(true, "Message from widget complete: "+SpecialPowers.wrap(msg).json);
+      domParent.removeChild(ifr);
+      runTest();
+    });
+
+    ifr.addEventListener('mozbrowserloadend', function() {
+      ok(true, "receive mozbrowserloadend");
+
+      // Test limited browser API feature only for valid widget case
+      if (isValidWidget) {
+        testLimitedBrowserAPI(ifr);
       }
+      SimpleTest.executeSoon(()=>loadFrameScript(mm));
+    }, false);
+
+    // Test limited browser API feature only for valid widget case
+    if (!isValidWidget) {
+      return;
     }
 
-    // This event is triggered when the app calls "alert".
-    ifr.addEventListener('mozbrowsershowmodalprompt', listener, false);
-    domParent.appendChild(ifr);
+    [
+      'mozbrowsertitlechange',
+      'mozbrowseropenwindow',
+      'mozbrowserscroll',
+      'mozbrowserasyncscroll'
+    ].forEach( function(topic) {
+      ifr.addEventListener(topic, function() {
+        ok(false, topic + " should be hidden");
+      }, false);
+    });
+  }
+
+  function testLimitedBrowserAPI(ifr) {
+    var securitySensitiveCalls = [
+      'sendMouseEvent',
+      'sendTouchEvent',
+      'goBack',
+      'goForward',
+      'reload',
+      'stop',
+      'download',
+      'purgeHistory',
+      'getScreenshot',
+      'zoom',
+      'getCanGoBack',
+      'getCanGoForward'
+    ];
+    securitySensitiveCalls.forEach( function(call) {
+      is(typeof ifr[call], "undefined", call + " should be hidden for widget");
+    });
+  }
+
+  function loadFrameScript(mm) {
+    var script = 'data:,\
+      function ok(p, msg) { \
+        if (p) { \
+          sendAsyncMessage("OK", msg); \
+        } else { \
+          sendAsyncMessage("KO", msg); \
+        } \
+      } \
+      \
+      function is(a, b, msg) { \
+        if (a == b) { \
+          sendAsyncMessage("OK", a + " == " + b + " - " + msg); \
+        } else { \
+          sendAsyncMessage("KO", a + " != " + b + " - " + msg); \
+        } \
+      } \
+      \
+      function finish() { \
+          sendAsyncMessage("DONE",""); \
+      } \
+      \
+      function onError() { \
+        ok(false, "Error callback invoked"); \
+        finish(); \
+      } \
+      \
+      function checkWidget(widget) { \
+        /*For invalid widget case, ignore the following check*/\
+        if (widget) { \
+          var widgetName = "Really Rapid Release (APPTYPETOKEN)"; \
+          is(widget.origin, "http://test", "Widget origin should be correct"); \
+          is(widget.installOrigin, "http://mochi.test:8888", "Install origin should be correct"); \
+        } \
+        finish(); \
+      } \
+      \
+      var request = content.window.navigator.mozApps.getSelf(); \
+      request.onsuccess = function() { \
+        var widget = request.result; \
+        ok(widget,"Should be a widget"); \
+        checkWidget(widget); \
+      }; \
+      request.onerror = onError; \
+      content.window.open("about:blank"); /*test mozbrowseropenwindow*/ \
+      content.window.scrollTo(4000, 4000); /*test mozbrowser(async)scroll*/ \
+      ';
+    mm.loadFrameScript(script, /* allowDelayedLoad = */ false);
   }
 
   var tests = [
     // Permissions
     function() {
       SpecialPowers.pushPermissions(
         [{ "type": "browser", "allow": 1, "context": document },
          { "type": "embed-widgets", "allow": 1, "context": document },
@@ -88,20 +183,29 @@
     },
 
     // No confirmation needed when an app is installed
     function() {
       SpecialPowers.autoConfirmAppInstall(runTest);
     },
 
     // Installing the app
-    installApp,
+    ()=>installApp(gWidgetManifestURL),
 
     // Run tests in app
-    testApp,
+    ()=>testApp(true),
+
+    // Uninstall the app
+    uninstallApp,
+
+    // Installing the app for invalid widget case
+    ()=>installApp(gInvalidWidgetManifestURL),
+
+    // Run tests in app for invalid widget case
+    ()=>testApp(false),
 
     // Uninstall the app
     uninstallApp
   ];
 
   function runTest() {
     if (!tests.length) {
       finish();
--- a/dom/browser-element/BrowserElementParent.cpp
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -159,16 +159,23 @@ BrowserElementParent::DispatchOpenWindow
 
   JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
   JSAutoCompartment ac(cx, global);
   if (!ToJSValue(cx, detail, &val)) {
     MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
     return BrowserElementParent::OPEN_WINDOW_IGNORED;
   }
 
+  // Do not dispatch a mozbrowseropenwindow event of a widget to its embedder
+  nsCOMPtr<nsIMozBrowserFrame> browserFrame =
+    do_QueryInterface(aOpenerFrameElement);
+  if (browserFrame && browserFrame->GetReallyIsWidget()) {
+    return BrowserElementParent::OPEN_WINDOW_CANCELLED;
+  }
+
   nsEventStatus status;
   bool dispatchSucceeded =
     DispatchCustomDOMEvent(aOpenerFrameElement,
                            NS_LITERAL_STRING("mozbrowseropenwindow"),
                            cx,
                            val, &status);
 
   if (dispatchSucceeded) {
@@ -344,15 +351,23 @@ NS_IMETHODIMP DispatchAsyncScrollEventRu
   return NS_OK;
 }
 
 bool
 BrowserElementParent::DispatchAsyncScrollEvent(TabParent* aTabParent,
                                                const CSSRect& aContentRect,
                                                const CSSSize& aContentSize)
 {
+  // Do not dispatch a mozbrowserasyncscroll event of a widget to its embedder
+  nsCOMPtr<Element> frameElement = aTabParent->GetOwnerElement();
+  NS_ENSURE_TRUE(frameElement, false);
+  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(frameElement);
+  if (browserFrame && browserFrame->GetReallyIsWidget()) {
+    return true;
+  }
+
   nsRefPtr<DispatchAsyncScrollEventRunnable> runnable =
     new DispatchAsyncScrollEventRunnable(aTabParent, aContentRect,
                                          aContentSize);
   return NS_SUCCEEDED(NS_DispatchToMainThread(runnable));
 }
 
 } // namespace mozilla
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -111,34 +111,40 @@ function BrowserElementParent(frameLoade
     XPCNativeWrapper.unwrap(self._frameElement)[domName] = function() {
       return self._sendDOMRequest(msgName);
     };
   }
 
   // Define methods on the frame element.
   defineNoReturnMethod('setVisible', this._setVisible);
   defineDOMRequestMethod('getVisible', 'get-visible');
-  defineNoReturnMethod('sendMouseEvent', this._sendMouseEvent);
+
+  // Not expose security sensitive browser API for widgets
+  if (!this._frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsWidget) {
+    defineNoReturnMethod('sendMouseEvent', this._sendMouseEvent);
 
-  // 0 = disabled, 1 = enabled, 2 - auto detect
-  if (getIntPref(TOUCH_EVENTS_ENABLED_PREF, 0) != 0) {
-    defineNoReturnMethod('sendTouchEvent', this._sendTouchEvent);
+    // 0 = disabled, 1 = enabled, 2 - auto detect
+    if (getIntPref(TOUCH_EVENTS_ENABLED_PREF, 0) != 0) {
+      defineNoReturnMethod('sendTouchEvent', this._sendTouchEvent);
+    }
+    defineNoReturnMethod('goBack', this._goBack);
+    defineNoReturnMethod('goForward', this._goForward);
+    defineNoReturnMethod('reload', this._reload);
+    defineNoReturnMethod('stop', this._stop);
+    defineMethod('download', this._download);
+    defineDOMRequestMethod('purgeHistory', 'purge-history');
+    defineMethod('getScreenshot', this._getScreenshot);
+    defineNoReturnMethod('zoom', this._zoom);
+
+    defineDOMRequestMethod('getCanGoBack', 'get-can-go-back');
+    defineDOMRequestMethod('getCanGoForward', 'get-can-go-forward');
   }
-  defineNoReturnMethod('goBack', this._goBack);
-  defineNoReturnMethod('goForward', this._goForward);
-  defineNoReturnMethod('reload', this._reload);
-  defineNoReturnMethod('stop', this._stop);
-  defineNoReturnMethod('zoom', this._zoom);
-  defineMethod('download', this._download);
-  defineDOMRequestMethod('purgeHistory', 'purge-history');
-  defineMethod('getScreenshot', this._getScreenshot);
+
   defineMethod('addNextPaintListener', this._addNextPaintListener);
   defineMethod('removeNextPaintListener', this._removeNextPaintListener);
-  defineDOMRequestMethod('getCanGoBack', 'get-can-go-back');
-  defineDOMRequestMethod('getCanGoForward', 'get-can-go-forward');
 
   let principal = this._frameElement.ownerDocument.nodePrincipal;
   let perm = Services.perms
              .testExactPermissionFromPrincipal(principal, "input-manage");
   if (perm === Ci.nsIPermissionManager.ALLOW_ACTION) {
     defineMethod('setInputMethodActive', this._setInputMethodActive);
   }
 
@@ -212,59 +218,72 @@ BrowserElementParent.prototype = {
         DOMApplicationRegistry.registerBrowserElementParentForApp(this, appId);
       }
     }
   },
 
   _setupMessageListener: function() {
     this._mm = this._frameLoader.messageManager;
     let self = this;
+    let isWidget = this._frameLoader
+                       .QueryInterface(Ci.nsIFrameLoader)
+                       .ownerIsWidget;
 
     // Messages we receive are handed to functions which take a (data) argument,
     // where |data| is the message manager's data object.
     // We use a single message and dispatch to various function based
     // on data.msg_name
     let mmCalls = {
       "hello": this._recvHello,
-      "contextmenu": this._fireCtxMenuEvent,
-      "locationchange": this._fireEventFromMsg,
       "loadstart": this._fireProfiledEventFromMsg,
       "loadend": this._fireProfiledEventFromMsg,
-      "titlechange": this._fireProfiledEventFromMsg,
-      "iconchange": this._fireEventFromMsg,
-      "manifestchange": this._fireEventFromMsg,
-      "metachange": this._fireEventFromMsg,
       "close": this._fireEventFromMsg,
-      "resize": this._fireEventFromMsg,
-      "activitydone": this._fireEventFromMsg,
-      "opensearch": this._fireEventFromMsg,
-      "securitychange": this._fireEventFromMsg,
       "error": this._fireEventFromMsg,
-      "scroll": this._fireEventFromMsg,
       "firstpaint": this._fireProfiledEventFromMsg,
       "documentfirstpaint": this._fireProfiledEventFromMsg,
       "nextpaint": this._recvNextPaint,
       "keyevent": this._fireKeyEvent,
-      "showmodalprompt": this._handleShowModalPrompt,
       "got-purge-history": this._gotDOMRequestResult,
       "got-screenshot": this._gotDOMRequestResult,
       "got-can-go-back": this._gotDOMRequestResult,
       "got-can-go-forward": this._gotDOMRequestResult,
       "fullscreen-origin-change": this._remoteFullscreenOriginChange,
       "rollback-fullscreen": this._remoteFrameFullscreenReverted,
       "exit-fullscreen": this._exitFullscreen,
       "got-visible": this._gotDOMRequestResult,
       "visibilitychange": this._childVisibilityChange,
       "got-set-input-method-active": this._gotDOMRequestResult,
       "selectionchange": this._handleSelectionChange
     };
 
+    let mmSecuritySensitiveCalls = {
+      "showmodalprompt": this._handleShowModalPrompt,
+      "contextmenu": this._fireCtxMenuEvent,
+      "securitychange": this._fireEventFromMsg,
+      "locationchange": this._fireEventFromMsg,
+      "iconchange": this._fireEventFromMsg,
+      "titlechange": this._fireProfiledEventFromMsg,
+      "opensearch": this._fireEventFromMsg,
+      "manifestchange": this._fireEventFromMsg,
+      "metachange": this._fireEventFromMsg,
+      "resize": this._fireEventFromMsg,
+      "activitydone": this._fireEventFromMsg,
+      "scroll": this._fireEventFromMsg
+    };
+
     this._mm.addMessageListener('browser-element-api:call', function(aMsg) {
-      if (self._isAlive() && (aMsg.data.msg_name in mmCalls)) {
+      if (!self._isAlive()) {
+        return;
+      }
+
+      if (aMsg.data.msg_name in mmCalls) {
         return mmCalls[aMsg.data.msg_name].apply(self, arguments);
+      } else if (!isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) {
+        return mmSecuritySensitiveCalls[aMsg.data.msg_name]
+                 .apply(self, arguments);
       }
     });
   },
 
   /**
    * You shouldn't touch this._frameElement or this._window if _isAlive is
    * false.  (You'll likely get an exception if you do.)
    */
@@ -289,35 +308,38 @@ BrowserElementParent.prototype = {
     let callbackCalled = false;
     let cancelCallback = function() {
       if (!callbackCalled) {
         callbackCalled = true;
         callback(false, null, null);
       }
     };
 
-    if (authDetail.isOnlyPassword) {
-      // We don't handle password-only prompts, so just cancel it.
+    // 1. We don't handle password-only prompts.
+    // 2. We don't handle for widget case because of security concern.
+    if (authDetail.isOnlyPassword ||
+        this._frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsWidget) {
       cancelCallback();
       return;
-    } else { /* username and password */
-      let detail = {
-        host:     authDetail.host,
-        realm:    authDetail.realm
-      };
+    }
+
+    /* username and password */
+    let detail = {
+      host:     authDetail.host,
+      realm:    authDetail.realm
+    };
 
-      evt = this._createEvent('usernameandpasswordrequired', detail,
-                              /* cancelable */ true);
-      Cu.exportFunction(function(username, password) {
-        if (callbackCalled)
-          return;
-        callbackCalled = true;
-        callback(true, username, password);
-      }, evt.detail, { defineAs: 'authenticate' });
-    }
+    evt = this._createEvent('usernameandpasswordrequired', detail,
+                            /* cancelable */ true);
+    Cu.exportFunction(function(username, password) {
+      if (callbackCalled)
+        return;
+      callbackCalled = true;
+      callback(true, username, password);
+    }, evt.detail, { defineAs: 'authenticate' });
 
     Cu.exportFunction(cancelCallback, evt.detail, { defineAs: 'cancel' });
 
     this._frameElement.dispatchEvent(evt);
 
     if (!evt.defaultPrevented) {
       cancelCallback();
     }
--- a/dom/interfaces/html/nsIMozBrowserFrame.idl
+++ b/dom/interfaces/html/nsIMozBrowserFrame.idl
@@ -4,17 +4,17 @@
 /* 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 "nsIDOMMozBrowserFrame.idl"
 
 interface nsITabParent;
 
-[scriptable, builtinclass, uuid(929AED00-3E15-49B7-8CA2-75003715B7E7)]
+[scriptable, builtinclass, uuid(0c0a862c-1a47-43c0-ae9e-d51835e3e1a6)]
 interface nsIMozBrowserFrame : nsIDOMMozBrowserFrame
 {
   /**
    * Gets whether this frame really is a browser or app frame.
    *
    * In order to really be a browser frame, this frame's
    * nsIDOMMozBrowserFrame::mozbrowser attribute must be true, and the frame
    * may have to pass various security checks.
@@ -29,16 +29,26 @@ interface nsIMozBrowserFrame : nsIDOMMoz
    * and only one of the following conditions:
    * 1. the frame's mozapp attribute must point to the manifest of a valid app
    * 2. the frame's mozwidget attribute must point to the manifest of a valid
    * app, and the src should be in the |widgetPages| specified by the manifest.
    */
   [infallible] readonly attribute boolean reallyIsApp;
 
   /**
+   * Gets whether this frame really is a widget frame.
+   *
+   * In order to really be a frame, this frame must really be a browser
+   * frame (this requirement will go away eventually), the frame's mozwidget
+   * attribute must point to the manifest of a valid app, and the src should
+   * be in the |widgetPages| specified by the manifest.
+   */
+  [infallible] readonly attribute boolean reallyIsWidget;
+
+  /**
    * This corresponds to the expecting-system-message attribute, which tells us
    * whether we should expect that this frame will receive a system message once
    * it starts up.
    *
    * It's the embedder's job to set this attribute on a frame.  Its presence
    * might cause us to increase the priority of the frame's process.
    */
   [infallible] readonly attribute boolean isExpectingSystemMessage;