Bug 1363169 - Add support for native windows share. r=gijs, r=aklotz
authorDale Harvey <dale@arandomurl.com>
Wed, 26 Sep 2018 21:09:00 +0100
changeset 497215 5f197a31d28fb2524167a8cb92c45b2d7a64ea8a
parent 497214 81cff62bc428d4fc54c81d0445b69ad554b31cda
child 497216 7c5bf03859d2fb592ff5cca51552f2d1c0000893
push id9996
push userarchaeopteryx@coole-files.de
push dateThu, 18 Oct 2018 18:37:15 +0000
treeherdermozilla-beta@8efe26839243 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgijs, aklotz
bugs1363169
milestone64.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 1363169 - Add support for native windows share. r=gijs, r=aklotz MozReview-Commit-ID: 7quON7Somvr
browser/base/content/browser-pageActions.js
browser/base/content/test/urlbar/browser.ini
browser/base/content/test/urlbar/browser_page_action_menu_share_win.html
browser/base/content/test/urlbar/browser_page_action_menu_share_win.js
browser/modules/PageActions.jsm
browser/themes/windows/browser.css
browser/themes/windows/jar.mn
browser/themes/windows/share.svg
widget/nsIWindowsUIUtils.idl
widget/windows/WindowsUIUtils.cpp
--- a/browser/base/content/browser-pageActions.js
+++ b/browser/base/content/browser-pageActions.js
@@ -1198,16 +1198,22 @@ BrowserPageActions.addSearchEngine = {
         prompt.alert(title, text);
       },
     });
   },
 };
 
 // share URL
 BrowserPageActions.shareURL = {
+  onCommand(event, buttonNode) {
+    let browser = gBrowser.selectedBrowser;
+    let currentURI = gURLBar.makeURIReadable(browser.currentURI).displaySpec;
+    this._windowsUIUtils.shareUrl(currentURI, browser.contentTitle);
+  },
+
   onShowingInPanel(buttonNode) {
     this._cached = false;
   },
 
   onBeforePlacedInWindow(browserWindow) {
     let action = PageActions.actionForID("shareURL");
     BrowserPageActions.takeActionTitleFromPanel(action);
   },
@@ -1259,12 +1265,12 @@ BrowserPageActions.shareURL = {
       bodyNode.firstChild.remove();
     }
     bodyNode.appendChild(fragment);
     this._cached = true;
   },
 };
 
 // Attach sharingService here so tests can override the implementation
-XPCOMUtils.defineLazyServiceGetter(BrowserPageActions.shareURL,
-                                   "_sharingService",
-                                   "@mozilla.org/widget/macsharingservice;1",
-                                   "nsIMacSharingService");
+XPCOMUtils.defineLazyServiceGetters(BrowserPageActions.shareURL, {
+  _sharingService: ["@mozilla.org/widget/macsharingservice;1", "nsIMacSharingService"],
+  _windowsUIUtils: ["@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"],
+});
--- a/browser/base/content/test/urlbar/browser.ini
+++ b/browser/base/content/test/urlbar/browser.ini
@@ -52,16 +52,20 @@ support-files =
   page_action_menu_add_search_engine_same_names.html
   page_action_menu_add_search_engine_0.xml
   page_action_menu_add_search_engine_1.xml
   page_action_menu_add_search_engine_2.xml
 [browser_page_action_menu_clipboard.js]
 subsuite = clipboard
 [browser_page_action_menu_share_mac.js]
 skip-if = os != "mac" # Mac only feature
+[browser_page_action_menu_share_win.js]
+support-files =
+  browser_page_action_menu_share_win.html
+skip-if = os != "win" # Windows only feature
 [browser_pasteAndGo.js]
 subsuite = clipboard
 [browser_populateAfterPushState.js]
 [browser_removeUnsafeProtocolsFromURLBarPaste.js]
 subsuite = clipboard
 [browser_search_favicon.js]
 [browser_tabMatchesInAwesomebar.js]
 support-files =
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/urlbar/browser_page_action_menu_share_win.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<title>Windows Sharing</title>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/urlbar/browser_page_action_menu_share_win.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* global sinon */
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+
+const TEST_URL = getRootDirectory(gTestPath) + "browser_page_action_menu_share_win.html";
+
+// Keep track of site details we are sharing
+let sharedUrl, sharedTitle;
+
+let stub = sinon.stub(BrowserPageActions.shareURL, "_windowsUIUtils").get(() => {
+  return {
+    shareUrl(url, title) {
+      sharedUrl = url;
+      sharedTitle = title;
+    },
+  };
+});
+
+registerCleanupFunction(async function() {
+  stub.restore();
+  delete window.sinon;
+});
+
+add_task(async function shareURL() {
+
+  if (!AppConstants.isPlatformAndVersionAtLeast("win", "6.4")) {
+    Assert.ok(true, "We only expose share on windows 10 and above");
+    return;
+  }
+
+  await BrowserTestUtils.withNewTab(TEST_URL, async () => {
+    // Open the panel.
+    await promisePageActionPanelOpen();
+
+    // Click Share URL.
+    let shareURLButton = document.getElementById("pageAction-panel-shareURL");
+    let hiddenPromise = promisePageActionPanelHidden();
+    EventUtils.synthesizeMouseAtCenter(shareURLButton, {});
+
+    await hiddenPromise;
+
+    Assert.equal(sharedUrl, TEST_URL, "Shared correct URL");
+    Assert.equal(sharedTitle, "Windows Sharing", "Shared with the correct title");
+  });
+});
--- a/browser/modules/PageActions.jsm
+++ b/browser/modules/PageActions.jsm
@@ -1180,16 +1180,31 @@ if (AppConstants.platform == "macosx") {
     wantsSubview: true,
     onSubviewShowing(panelViewNode) {
         browserPageActions(panelViewNode).shareURL
           .onShowingSubview(panelViewNode);
     },
   });
 }
 
+if (AppConstants.isPlatformAndVersionAtLeast("win", "6.4")) {
+  gBuiltInActions.push(
+  // Share URL
+  {
+    id: "shareURL",
+    title: "shareURL-title",
+    onBeforePlacedInWindow(buttonNode) {
+      browserPageActions(buttonNode).shareURL.onBeforePlacedInWindow(buttonNode);
+    },
+    onCommand(event, buttonNode) {
+      browserPageActions(buttonNode).shareURL.onCommand(event, buttonNode);
+    },
+  });
+}
+
 /**
  * Gets a BrowserPageActions object in a browser window.
  *
  * @param  obj
  *         Either a DOM node or a browser window.
  * @return The BrowserPageActions object in the browser window related to the
  *         given object.
  */
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -592,16 +592,20 @@ html|*.urlbar-input:-moz-lwtheme::placeh
 
 .urlbar-display {
   margin-top: 0;
   margin-bottom: 0;
   margin-inline-start: 0;
   color: GrayText;
 }
 
+#pageAction-panel-shareURL {
+  list-style-image: url("chrome://browser/skin/share.svg");
+}
+
 %include ../shared/urlbarSearchSuggestionsNotification.inc.css
 
 #search-container {
   min-width: calc(54px + 11ch);
 }
 
 /* identity box */
 
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -36,16 +36,17 @@ browser.jar:
   skin/classic/browser/places/livemark-item.png                (places/livemark-item.png)
   skin/classic/browser/preferences/alwaysAsk.png               (preferences/alwaysAsk.png)
   skin/classic/browser/preferences/application.png             (preferences/application.png)
   skin/classic/browser/preferences/saveFile.png                (preferences/saveFile.png)
   skin/classic/browser/preferences/preferences.css             (preferences/preferences.css)
 * skin/classic/browser/preferences/in-content/preferences.css  (preferences/in-content/preferences.css)
 * skin/classic/browser/preferences/in-content/dialog.css       (preferences/in-content/dialog.css)
   skin/classic/browser/preferences/applications.css            (preferences/applications.css)
+  skin/classic/browser/share.svg                               (share.svg)
   skin/classic/browser/tabbrowser/tabDragIndicator.png         (tabbrowser/tabDragIndicator.png)
   skin/classic/browser/window-controls/close.svg                 (window-controls/close.svg)
   skin/classic/browser/window-controls/close-highcontrast.svg    (window-controls/close-highcontrast.svg)
   skin/classic/browser/window-controls/close-themes.svg          (window-controls/close-themes.svg)
   skin/classic/browser/window-controls/maximize.svg              (window-controls/maximize.svg)
   skin/classic/browser/window-controls/maximize-highcontrast.svg (window-controls/maximize-highcontrast.svg)
   skin/classic/browser/window-controls/maximize-themes.svg       (window-controls/maximize-themes.svg)
   skin/classic/browser/window-controls/minimize.svg              (window-controls/minimize.svg)
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/share.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
+  <path fill="context-fill" d="M 15.707 4.293 l -4 -4 a 1 1 0 0 0 -1.414 1.414 L 12.585 4 H 11 a 7.008 7.008 0 0 0 -7 7 a 1 1 0 0 0 2 0 a 5.006 5.006 0 0 1 5 -5 h 1.585 l -2.293 2.293 a 1 1 0 1 0 1.414 1.414 l 4 -4 a 1 1 0 0 0 0.001 -1.414 Z" />
+  <path fill="context-fill" d="M 13 11 a 1 1 0 0 0 -1 1 v 1 a 1 1 0 0 1 -1 1 H 3 a 1 1 0 0 1 -1 -1 V 6 a 1 1 0 0 1 1 -1 h 1 a 1 1 0 0 0 0 -2 H 3 a 3 3 0 0 0 -3 3 v 7 a 3 3 0 0 0 3 3 h 8 a 3 3 0 0 0 3 -3 v -1 a 1 1 0 0 0 -1 -1 Z" />
+</svg>
--- a/widget/nsIWindowsUIUtils.idl
+++ b/widget/nsIWindowsUIUtils.idl
@@ -15,10 +15,14 @@ interface nsIWindowsUIUtils : nsISupport
    * non-Windows and on versions of Windows before win10
    */
   readonly attribute boolean inTabletMode;
 
   /**
    * Update the tablet mode state
    */
   void updateTabletModeState();
+
+  /**
+   * Share URL
+   */
+  void shareUrl(in AString shareTitle, in AString urlToShare);
 };
-
--- a/widget/windows/WindowsUIUtils.cpp
+++ b/widget/windows/WindowsUIUtils.cpp
@@ -30,16 +30,17 @@
 #pragma comment(lib, "runtimeobject.lib")
 
 using namespace mozilla;
 using namespace ABI::Windows::UI;
 using namespace ABI::Windows::UI::ViewManagement;
 using namespace Microsoft::WRL;
 using namespace Microsoft::WRL::Wrappers;
 using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::ApplicationModel::DataTransfer;
 
 /* All of this is win10 stuff and we're compiling against win81 headers
  * for now, so we may need to do some legwork: */
 #if WINVER_MAXVER < 0x0A00
 namespace ABI {
   namespace Windows {
     namespace UI {
       namespace ViewManagement {
@@ -85,16 +86,31 @@ typedef interface IUIViewSettingsInterop
 MIDL_INTERFACE("3694dbf9-8f68-44be-8ff5-195c98ede8a6")
 IUIViewSettingsInterop : public IInspectable
 {
 public:
   virtual HRESULT STDMETHODCALLTYPE GetForWindow(HWND hwnd, REFIID riid, void **ppv) = 0;
 };
 #endif
 
+#ifndef __IDataTransferManagerInterop_INTERFACE_DEFINED__
+#define __IDataTransferManagerInterop_INTERFACE_DEFINED__
+
+typedef interface IDataTransferManagerInterop IDataTransferManagerInterop;
+
+MIDL_INTERFACE("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")
+IDataTransferManagerInterop : public IUnknown
+{
+public:
+    virtual HRESULT STDMETHODCALLTYPE GetForWindow(HWND appWindow, REFIID riid, void **dataTransferManager) = 0;
+    virtual HRESULT STDMETHODCALLTYPE ShowShareUIForWindow(HWND appWindow) = 0;
+};
+
+#endif
+
 #endif
 
 WindowsUIUtils::WindowsUIUtils() :
   mInTabletMode(eTabletModeUnknown)
 {
 }
 
 WindowsUIUtils::~WindowsUIUtils()
@@ -173,8 +189,129 @@ WindowsUIUtils::UpdateTabletModeState()
         }
       }
     }
   }
 #endif
 
   return NS_OK;
 }
+
+struct HStringDeleter
+{
+  typedef HSTRING pointer;
+  void operator()(pointer aString)
+  {
+      WindowsDeleteString(aString);
+  }
+};
+
+typedef mozilla::UniquePtr<HSTRING, HStringDeleter> HStringUniquePtr;
+
+NS_IMETHODIMP
+WindowsUIUtils::ShareUrl(const nsAString& aUrlToShare,
+                         const nsAString& aShareTitle)
+{
+#ifndef __MINGW32__
+  if (!IsWin10OrLater()) {
+    return NS_OK;
+  }
+
+  HSTRING rawTitle;
+  HRESULT hr = WindowsCreateString(PromiseFlatString(aShareTitle).get(), aShareTitle.Length(), &rawTitle);
+  if (FAILED(hr)) {
+    return NS_OK;
+  }
+  HStringUniquePtr title(rawTitle);
+
+  HSTRING rawUrl;
+  hr = WindowsCreateString(PromiseFlatString(aUrlToShare).get(), aUrlToShare.Length(), &rawUrl);
+  if (FAILED(hr)) {
+    return NS_OK;
+  }
+  HStringUniquePtr url(rawUrl);
+
+  ComPtr<IUriRuntimeClassFactory> uriFactory;
+  hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(), &uriFactory);
+  if (FAILED(hr)) {
+    return NS_OK;
+  }
+
+  ComPtr<IUriRuntimeClass> uri;
+  hr = uriFactory->CreateUri(url.get(), &uri);
+  if (FAILED(hr)) {
+    return NS_OK;
+  }
+
+  HWND hwnd = GetForegroundWindow();
+  if (!hwnd) {
+    return NS_OK;
+  }
+
+  ComPtr<IDataTransferManagerInterop> dtmInterop;
+  hr = RoGetActivationFactory(HStringReference(
+    RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager)
+                         .Get(), IID_PPV_ARGS(&dtmInterop));
+  if (FAILED(hr)) {
+    return NS_OK;
+  }
+
+  ComPtr<IDataTransferManager> dtm;
+  hr = dtmInterop->GetForWindow(hwnd, IID_PPV_ARGS(&dtm));
+  if (FAILED(hr)) {
+    return NS_OK;
+  }
+
+  auto callback = Callback < ITypedEventHandler<DataTransferManager*, DataRequestedEventArgs* >> (
+          [uri = std::move(uri), title = std::move(title)](IDataTransferManager*, IDataRequestedEventArgs* pArgs) -> HRESULT
+    {
+      ComPtr<IDataRequest> spDataRequest;
+      HRESULT hr = pArgs->get_Request(&spDataRequest);
+      if (FAILED(hr)) {
+        return hr;
+      }
+
+      ComPtr<IDataPackage> spDataPackage;
+      hr = spDataRequest->get_Data(&spDataPackage);
+      if (FAILED(hr)) {
+        return hr;
+      }
+
+      ComPtr<IDataPackage2> spDataPackage2;
+      hr = spDataPackage->QueryInterface(IID_PPV_ARGS(&spDataPackage2));
+      if (FAILED(hr)) {
+        return hr;
+      }
+
+      ComPtr<IDataPackagePropertySet> spDataPackageProperties;
+      hr = spDataPackage->get_Properties(&spDataPackageProperties);
+      if (FAILED(hr)) {
+        return hr;
+      }
+
+      hr = spDataPackageProperties->put_Title(title.get());
+      if (FAILED(hr)) {
+        return hr;
+      }
+
+      hr = spDataPackage2->SetWebLink(uri.Get());
+      if (FAILED(hr)) {
+        return hr;
+      }
+
+      return S_OK;
+    });
+
+  EventRegistrationToken dataRequestedToken;
+  hr = dtm->add_DataRequested(callback.Get(), &dataRequestedToken);
+  if (FAILED(hr)) {
+    return NS_OK;
+  }
+
+  hr = dtmInterop->ShowShareUIForWindow(hwnd);
+  if (FAILED(hr)) {
+    return NS_OK;
+  }
+
+#endif
+
+  return NS_OK;
+}