Bug 1044736 - Part 6. Widget should only require embed-widgets permission. r=fabrice
☠☠ backed out by 6ccbc46c33db ☠ ☠
authorKan-Ru Chen (陳侃如) <kanru@kanru.info>
Wed, 12 Nov 2014 14:20:19 +0800
changeset 215295 920775ddfb85bebc35d2b65f60ad41069516dd0c
parent 215294 a3604ab2b3cae0cb23291de4ed2e2e6876283b66
child 215296 6ccbc46c33db65a03431c720cb1d331fdacace57
push id51731
push userryanvm@gmail.com
push dateWed, 12 Nov 2014 20:52:34 +0000
treeherdermozilla-inbound@90731dbaab2d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice
bugs1044736
milestone36.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 1044736 - Part 6. Widget should only require embed-widgets permission. r=fabrice As discussed on dev-webapi[1] the app that wants to use widgets only need the "embed-widgets" permission to use <iframe mozbrowser mozwidget> If the app also wants to implement a browser, it could request the "browser" separately. A <iframe mozbrowser mozwidget> will have restricted mozbrowser API defined on the prototype if the embedder has the "brower" permission; they will always throw when used. [1]: https://groups.google.com/d/msg/mozilla.dev.webapi/uQweGWtVKRA/Bj1jZq3LN-0J
dom/apps/tests/file_test_widget.js
dom/apps/tests/mochitest.ini
dom/apps/tests/test_widget.html
dom/apps/tests/test_widget_browser.html
dom/html/nsBrowserElement.cpp
dom/html/nsBrowserElement.h
dom/html/nsGenericHTMLFrameElement.cpp
--- a/dom/apps/tests/file_test_widget.js
+++ b/dom/apps/tests/file_test_widget.js
@@ -1,11 +1,12 @@
 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;
+var gHasBrowserPermission;
 
 function onError() {
   ok(false, "Error callback invoked");
   finish();
 }
 
 function installApp(path) {
   var request = navigator.mozApps.install(path);
@@ -77,31 +78,44 @@ function testApp(isValidWidget) {
     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'
+    { api: 'sendMouseEvent'      , args: ['mousedown', 0, 0, 0, 0, 0] },
+    { api: 'sendTouchEvent'      , args: ['touchstart', [0], [0], [0], [1], [1], [0], [1], 1, 0] },
+    { api: 'goBack'              , args: [] },
+    { api: 'goForward'           , args: [] },
+    { api: 'reload'              , args: [] },
+    { api: 'stop'                , args: [] },
+    { api: 'download'            , args: ['http://example.org'] },
+    { api: 'purgeHistory'        , args: [] },
+    { api: 'getScreenshot'       , args: [0, 0] },
+    { api: 'zoom'                , args: [0.1] },
+    { api: 'getCanGoBack'        , args: [] },
+    { api: 'getCanGoForward'     , args: [] },
+    { api: 'getContentDimensions', args: [] }
   ];
   securitySensitiveCalls.forEach( function(call) {
-    is(typeof ifr[call], "undefined", call + " should be hidden for widget");
+    if (gHasBrowserPermission) {
+      isnot(typeof ifr[call.api], "undefined", call.api + " should be defined");
+      var didThrow;
+      try {
+        ifr[call.api].apply(ifr, call.args);
+      } catch (e) {
+        ok(e instanceof DOMException, "throw right exception type");
+        didThrow = e.code;
+      }
+      is(didThrow, DOMException.INVALID_NODE_TYPE_ERR, "call " + call.api + " should throw exception");
+    } else {
+      is(typeof ifr[call.api], "undefined", call.api + " should be hidden for widget");
+    }
   });
 }
 
 function loadFrameScript(mm) {
   var script = 'data:,\
   function ok(p, msg) { \
   if (p) { \
   sendAsyncMessage("OK", msg); \
@@ -149,17 +163,17 @@ function loadFrameScript(mm) {
   ';
   mm.loadFrameScript(script, /* allowDelayedLoad = */ false);
 }
 
 var tests = [
   // Permissions
   function() {
     SpecialPowers.pushPermissions(
-      [{ "type": "browser", "allow": 1, "context": document },
+      [{ "type": "browser", "allow": gHasBrowserPermission ? 1 : 0, "context": document },
        { "type": "embed-widgets", "allow": 1, "context": document },
        { "type": "webapps-manage", "allow": 1, "context": document }], runTest);
   },
 
   // Preferences
   function() {
     SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true],
                                        ["dom.enable_widgets", true],
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -40,8 +40,10 @@ skip-if = (toolkit == 'android' && proce
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_receipt_operations.html]
 [test_signed_pkg_install.html]
 [test_uninstall_errors.html]
 [test_theme_role.html]
 [test_web_app_install.html]
 [test_widget.html]
 skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
+[test_widget_browser.html]
+skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
--- a/dom/apps/tests/test_widget.html
+++ b/dom/apps/tests/test_widget.html
@@ -6,12 +6,13 @@
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="file_test_widget.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">
   SimpleTest.waitForExplicitFinish();
+  gHasBrowserPermission = false;
   runTest();
   </script>
 </body>
 </html>
copy from dom/apps/tests/test_widget.html
copy to dom/apps/tests/test_widget_browser.html
--- a/dom/apps/tests/test_widget.html
+++ b/dom/apps/tests/test_widget_browser.html
@@ -6,12 +6,13 @@
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="file_test_widget.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">
   SimpleTest.waitForExplicitFinish();
+  gHasBrowserPermission = true;
   runTest();
   </script>
 </body>
 </html>
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -106,36 +106,49 @@ nsBrowserElement::IsBrowserElementOrThro
 {
   if (mBrowserElementAPI) {
     return true;
   }
   aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
   return false;
 }
 
+bool
+nsBrowserElement::IsNotWidgetOrThrow(ErrorResult& aRv)
+{
+  if (!mOwnerIsWidget) {
+    return true;
+  }
+  aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
+  return false;
+}
+
 void
 nsBrowserElement::InitBrowserElementAPI()
 {
   bool isBrowserOrApp;
   nsCOMPtr<nsIFrameLoader> frameLoader = GetFrameLoader();
   NS_ENSURE_TRUE_VOID(frameLoader);
   nsresult rv = frameLoader->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp);
   NS_ENSURE_SUCCESS_VOID(rv);
+  rv = frameLoader->GetOwnerIsWidget(&mOwnerIsWidget);
+  NS_ENSURE_SUCCESS_VOID(rv);
 
   if (!isBrowserOrApp) {
     return;
   }
 
   mBrowserElementAPI = do_CreateInstance("@mozilla.org/dom/browser-element-api;1");
   if (mBrowserElementAPI) {
     mBrowserElementAPI->SetFrameLoader(frameLoader);
   }
 }
 
 nsBrowserElement::nsBrowserElement()
+  : mOwnerIsWidget(false)
 {
   mObserver = new BrowserShownObserver(this);
   mObserver->AddObserver();
 }
 
 nsBrowserElement::~nsBrowserElement()
 {
   mObserver->RemoveObserver();
@@ -202,16 +215,17 @@ nsBrowserElement::SendMouseEvent(const n
                                  uint32_t aX,
                                  uint32_t aY,
                                  uint32_t aButton,
                                  uint32_t aClickCount,
                                  uint32_t aModifiers,
                                  ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
+  NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
 
   nsresult rv = mBrowserElementAPI->SendMouseEvent(aType,
                                                    aX,
                                                    aY,
                                                    aButton,
                                                    aClickCount,
                                                    aModifiers);
 
@@ -229,16 +243,17 @@ nsBrowserElement::SendTouchEvent(const n
                                  const dom::Sequence<uint32_t>& aRys,
                                  const dom::Sequence<float>& aRotationAngles,
                                  const dom::Sequence<float>& aForces,
                                  uint32_t aCount,
                                  uint32_t aModifiers,
                                  ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
+  NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
 
   if (aIdentifiers.Length() != aCount ||
       aXs.Length() != aCount ||
       aYs.Length() != aCount ||
       aRxs.Length() != aCount ||
       aRys.Length() != aCount ||
       aRotationAngles.Length() != aCount ||
       aForces.Length() != aCount) {
@@ -261,66 +276,71 @@ nsBrowserElement::SendTouchEvent(const n
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 }
 
 void
 nsBrowserElement::GoBack(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
+  NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
 
   nsresult rv = mBrowserElementAPI->GoBack();
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 }
 
 void
 nsBrowserElement::GoForward(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
+  NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
 
   nsresult rv = mBrowserElementAPI->GoForward();
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 }
 
 void
 nsBrowserElement::Reload(bool aHardReload, ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
+  NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
 
   nsresult rv = mBrowserElementAPI->Reload(aHardReload);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 }
 
 void
 nsBrowserElement::Stop(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
+  NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
 
   nsresult rv = mBrowserElementAPI->Stop();
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 }
 
 already_AddRefed<dom::DOMRequest>
 nsBrowserElement::Download(const nsAString& aUrl,
                            const dom::BrowserElementDownloadOptions& aOptions,
                            ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
+  NS_ENSURE_TRUE(IsNotWidgetOrThrow(aRv), nullptr);
 
   nsCOMPtr<nsIDOMDOMRequest> req;
   AutoJSAPI jsapi;
   jsapi.Init();
   JS::Rooted<JS::Value> options(jsapi.cx());
   if (!ToJSValue(jsapi.cx(), aOptions, &options)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
@@ -334,16 +354,17 @@ nsBrowserElement::Download(const nsAStri
 
   return req.forget().downcast<DOMRequest>();
 }
 
 already_AddRefed<dom::DOMRequest>
 nsBrowserElement::PurgeHistory(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
+  NS_ENSURE_TRUE(IsNotWidgetOrThrow(aRv), nullptr);
 
   nsCOMPtr<nsIDOMDOMRequest> req;
   nsresult rv = mBrowserElementAPI->PurgeHistory(getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
@@ -353,16 +374,17 @@ nsBrowserElement::PurgeHistory(ErrorResu
 
 already_AddRefed<dom::DOMRequest>
 nsBrowserElement::GetScreenshot(uint32_t aWidth,
                                 uint32_t aHeight,
                                 const nsAString& aMimeType,
                                 ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
+  NS_ENSURE_TRUE(IsNotWidgetOrThrow(aRv), nullptr);
 
   nsCOMPtr<nsIDOMDOMRequest> req;
   nsresult rv = mBrowserElementAPI->GetScreenshot(aWidth, aHeight, aMimeType,
                                                   getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     if (rv == NS_ERROR_INVALID_ARG) {
       aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
@@ -374,59 +396,64 @@ nsBrowserElement::GetScreenshot(uint32_t
 
   return req.forget().downcast<DOMRequest>();
 }
 
 void
 nsBrowserElement::Zoom(float aZoom, ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
+  NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
+
   nsresult rv = mBrowserElementAPI->Zoom(aZoom);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 }
 
 already_AddRefed<dom::DOMRequest>
 nsBrowserElement::GetCanGoBack(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
+  NS_ENSURE_TRUE(IsNotWidgetOrThrow(aRv), nullptr);
 
   nsCOMPtr<nsIDOMDOMRequest> req;
   nsresult rv = mBrowserElementAPI->GetCanGoBack(getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
   return req.forget().downcast<DOMRequest>();
 }
 
 already_AddRefed<dom::DOMRequest>
 nsBrowserElement::GetCanGoForward(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
+  NS_ENSURE_TRUE(IsNotWidgetOrThrow(aRv), nullptr);
 
   nsCOMPtr<nsIDOMDOMRequest> req;
   nsresult rv = mBrowserElementAPI->GetCanGoForward(getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
   return req.forget().downcast<DOMRequest>();
 }
 
 already_AddRefed<dom::DOMRequest>
 nsBrowserElement::GetContentDimensions(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
+  NS_ENSURE_TRUE(IsNotWidgetOrThrow(aRv), nullptr);
 
   nsCOMPtr<nsIDOMDOMRequest> req;
   nsresult rv = mBrowserElementAPI->GetContentDimensions(getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
--- a/dom/html/nsBrowserElement.h
+++ b/dom/html/nsBrowserElement.h
@@ -91,16 +91,18 @@ public:
 
 protected:
   virtual already_AddRefed<nsFrameLoader> GetFrameLoader() = 0;
   nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
 
 private:
   void InitBrowserElementAPI();
   bool IsBrowserElementOrThrow(ErrorResult& aRv);
+  bool IsNotWidgetOrThrow(ErrorResult& aRv);
+  bool mOwnerIsWidget;
 
   class BrowserShownObserver;
   friend class BrowserShownObserver;
   nsRefPtr<BrowserShownObserver> mObserver;
 };
 
 } // namespace mozilla
 
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -302,16 +302,20 @@ nsGenericHTMLFrameElement::GetReallyIsBr
   nsIPrincipal *principal = NodePrincipal();
   nsCOMPtr<nsIPermissionManager> permMgr =
     services::GetPermissionManager();
   NS_ENSURE_TRUE(permMgr, NS_OK);
 
   uint32_t permission = nsIPermissionManager::DENY_ACTION;
   nsresult rv = permMgr->TestPermissionFromPrincipal(principal, "browser", &permission);
   NS_ENSURE_SUCCESS(rv, NS_OK);
+  if (permission != nsIPermissionManager::ALLOW_ACTION) {
+    rv = permMgr->TestPermissionFromPrincipal(principal, "embed-widgets", &permission);
+    NS_ENSURE_SUCCESS(rv, NS_OK);
+  }
   *aOut = permission == nsIPermissionManager::ALLOW_ACTION;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsGenericHTMLFrameElement::GetReallyIsApp(bool *aOut)
 {
   nsAutoString manifestURL;