From a2a2c152ab2ebd1054f2a0ea97a2dedb542f7600 Mon Sep 17 00:00:00 2001 From: Shih-Chiang Chien Date: Fri, 26 Aug 2016 10:59:27 +0800 Subject: [PATCH] Bug 1228526 - Part 1, support device filtering by requested presentation URL. r=smaug MozReview-Commit-ID: JrqeavLGub1 --HG-- extra : rebase_source : fc2eca80d59dc36e97a7af8ed3de6597faf38703 --- .../PresentationDeviceManager.cpp | 38 ++++++++++- dom/presentation/PresentationService.cpp | 15 ++++- .../interfaces/nsIPresentationDevice.idl | 7 +++ .../nsIPresentationDeviceManager.idl | 6 +- .../provider/DeviceProviderHelpers.cpp | 57 +++++++++++++++++ .../provider/DeviceProviderHelpers.h | 30 +++++++++ .../provider/DisplayDeviceProvider.cpp | 19 ++++++ .../provider/LegacyMDNSDeviceProvider.cpp | 17 +++++ .../provider/MulticastDNSDeviceProvider.cpp | 22 +++++++ dom/presentation/provider/moz.build | 1 + .../PresentationDeviceInfoChromeScript.js | 6 +- .../PresentationSessionChromeScript.js | 4 ++ .../PresentationSessionChromeScript1UA.js | 4 ++ .../test_multicast_dns_device_provider.js | 63 +++++++++++++++++++ .../test_presentation_device_manager.js | 16 +++++ 15 files changed, 299 insertions(+), 6 deletions(-) create mode 100644 dom/presentation/provider/DeviceProviderHelpers.cpp create mode 100644 dom/presentation/provider/DeviceProviderHelpers.h diff --git a/dom/presentation/PresentationDeviceManager.cpp b/dom/presentation/PresentationDeviceManager.cpp index 2c2b2c8298f6..41bcf709e841 100644 --- a/dom/presentation/PresentationDeviceManager.cpp +++ b/dom/presentation/PresentationDeviceManager.cpp @@ -8,6 +8,7 @@ #include "mozilla/Services.h" #include "MainThreadUtils.h" +#include "nsArrayUtils.h" #include "nsCategoryCache.h" #include "nsCOMPtr.h" #include "nsIMutableArray.h" @@ -148,7 +149,7 @@ PresentationDeviceManager::GetDeviceAvailable(bool* aRetVal) } NS_IMETHODIMP -PresentationDeviceManager::GetAvailableDevices(nsIArray** aRetVal) +PresentationDeviceManager::GetAvailableDevices(nsIArray* aPresentationUrls, nsIArray** aRetVal) { NS_ENSURE_ARG_POINTER(aRetVal); MOZ_ASSERT(NS_IsMainThread()); @@ -158,9 +159,42 @@ PresentationDeviceManager::GetAvailableDevices(nsIArray** aRetVal) NS_DispatchToMainThread( NewRunnableMethod(this, &PresentationDeviceManager::ForceDiscovery)); + nsTArray presentationUrls; + if (aPresentationUrls) { + uint32_t length; + nsresult rv = aPresentationUrls->GetLength(&length); + if (NS_SUCCEEDED(rv)) { + for (uint32_t i = 0; i < length; ++i) { + nsCOMPtr isupportStr = + do_QueryElementAt(aPresentationUrls, i); + + nsAutoString presentationUrl; + rv = isupportStr->GetData(presentationUrl); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + presentationUrls.AppendElement(presentationUrl); + } + } + } + nsCOMPtr devices = do_CreateInstance(NS_ARRAY_CONTRACTID); for (uint32_t i = 0; i < mDevices.Length(); ++i) { - devices->AppendElement(mDevices[i], false); + if (presentationUrls.IsEmpty()) { + devices->AppendElement(mDevices[i], false); + continue; + } + + for (uint32_t j = 0; j < presentationUrls.Length(); ++j) { + bool isSupported; + if (NS_SUCCEEDED(mDevices[i]->IsRequestedUrlSupported(presentationUrls[i], + &isSupported)) && + isSupported) { + devices->AppendElement(mDevices[i], false); + break; + } + } } devices.forget(aRetVal); diff --git a/dom/presentation/PresentationService.cpp b/dom/presentation/PresentationService.cpp index 30f02e451ff3..b5fccdc8e0f9 100644 --- a/dom/presentation/PresentationService.cpp +++ b/dom/presentation/PresentationService.cpp @@ -11,6 +11,7 @@ #include "mozIApplication.h" #include "nsGlobalWindow.h" #include "nsIAppsService.h" +#include "nsIMutableArray.h" #include "nsIObserverService.h" #include "nsIPresentationControlChannel.h" #include "nsIPresentationDeviceManager.h" @@ -19,9 +20,11 @@ #include "nsIPresentationRequestUIGlue.h" #include "nsIPresentationSessionRequest.h" #include "nsIPresentationTerminateRequest.h" +#include "nsISupportsPrimitives.h" #include "nsNetUtil.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" +#include "nsXPCOMCID.h" #include "nsXULAppAPI.h" #include "PresentationLog.h" @@ -612,8 +615,18 @@ PresentationService::StartSession(const nsAString& aUrl, return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR); } + nsCOMPtr presentationUrls + = do_CreateInstance(NS_ARRAY_CONTRACTID); + if (!presentationUrls) { + return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR); + } + nsCOMPtr supportsStr = + do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID); + supportsStr->SetData(aUrl); + presentationUrls->AppendElement(supportsStr, false); + nsCOMPtr devices; - nsresult rv = deviceManager->GetAvailableDevices(getter_AddRefs(devices)); + nsresult rv = deviceManager->GetAvailableDevices(presentationUrls, getter_AddRefs(devices)); if (NS_WARN_IF(NS_FAILED(rv))) { return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR); } diff --git a/dom/presentation/interfaces/nsIPresentationDevice.idl b/dom/presentation/interfaces/nsIPresentationDevice.idl index e9faa9f05751..63e4a52ef661 100644 --- a/dom/presentation/interfaces/nsIPresentationDevice.idl +++ b/dom/presentation/interfaces/nsIPresentationDevice.idl @@ -31,6 +31,13 @@ interface nsIPresentationDevice : nsISupports // Do something when presentation session is disconnected. void disconnect(); + + /* + * Query if requested presentation URL is supported. + * @params requestedUrl the designated URL for a presentation request. + * @returns true if designated URL is supported. + */ + boolean isRequestedUrlSupported(in DOMString requestedUrl); }; diff --git a/dom/presentation/interfaces/nsIPresentationDeviceManager.idl b/dom/presentation/interfaces/nsIPresentationDeviceManager.idl index 25c0a7aa9f27..adff9fc09e0d 100644 --- a/dom/presentation/interfaces/nsIPresentationDeviceManager.idl +++ b/dom/presentation/interfaces/nsIPresentationDeviceManager.idl @@ -40,10 +40,12 @@ interface nsIPresentationDeviceManager : nsISupports void forceDiscovery(); /* - * Retrieve all available devices, return a list of nsIPresentationDevice. + * Retrieve all available devices or all available devices that supports + * designated presentation URLs, return a list of nsIPresentationDevice. * The returned list is a cached device list and could be out-of-date. * Observe device change events to get following updates. + * @param presentationUrls the target presentation URLs for device filtering */ - nsIArray getAvailableDevices(); + nsIArray getAvailableDevices([optional] in nsIArray presentationUrls); }; diff --git a/dom/presentation/provider/DeviceProviderHelpers.cpp b/dom/presentation/provider/DeviceProviderHelpers.cpp new file mode 100644 index 000000000000..00b2c12f190f --- /dev/null +++ b/dom/presentation/provider/DeviceProviderHelpers.cpp @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "DeviceProviderHelpers.h" + +#include "nsCOMPtr.h" +#include "nsIURI.h" +#include "nsNetUtil.h" + +namespace mozilla { +namespace dom { +namespace presentation { + +static const char* const kFxTVPresentationAppUrls[] = { + "app://fling-player.gaiamobile.org/index.html", + "app://notification-receiver.gaiamobile.org/index.html", + nullptr +}; + +/* static */ bool +DeviceProviderHelpers::IsCommonlySupportedScheme(const nsAString& aUrl) +{ + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl); + if (NS_FAILED(rv) || !uri) { + return false; + } + + nsAutoCString scheme; + uri->GetScheme(scheme); + if (scheme.LowerCaseEqualsLiteral("http") || + scheme.LowerCaseEqualsLiteral("https")) { + return true; + } + + return false; +} + +/* static */ bool +DeviceProviderHelpers::IsFxTVSupportedAppUrl(const nsAString& aUrl) +{ + // Check if matched with any presentation Apps on TV. + for (uint32_t i = 0; kFxTVPresentationAppUrls[i]; i++) { + if (aUrl.EqualsASCII(kFxTVPresentationAppUrls[i])) { + return true; + } + } + + return false; +} + +} // namespace presentation +} // namespace dom +} // namespace mozilla diff --git a/dom/presentation/provider/DeviceProviderHelpers.h b/dom/presentation/provider/DeviceProviderHelpers.h new file mode 100644 index 000000000000..4bde09bed8f9 --- /dev/null +++ b/dom/presentation/provider/DeviceProviderHelpers.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_presentation_DeviceProviderHelpers_h +#define mozilla_dom_presentation_DeviceProviderHelpers_h + +#include "nsString.h" + +namespace mozilla { +namespace dom { +namespace presentation { + +class DeviceProviderHelpers final +{ +public: + static bool IsCommonlySupportedScheme(const nsAString& aUrl); + static bool IsFxTVSupportedAppUrl(const nsAString& aUrl); + +private: + DeviceProviderHelpers() = delete; +}; + +} // namespace presentation +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_presentation_DeviceProviderHelpers_h diff --git a/dom/presentation/provider/DisplayDeviceProvider.cpp b/dom/presentation/provider/DisplayDeviceProvider.cpp index 10185b1b890b..3f88aba5e33e 100644 --- a/dom/presentation/provider/DisplayDeviceProvider.cpp +++ b/dom/presentation/provider/DisplayDeviceProvider.cpp @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DisplayDeviceProvider.h" + +#include "DeviceProviderHelpers.h" #include "mozilla/Logging.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" @@ -117,6 +119,23 @@ DisplayDeviceProvider::HDMIDisplayDevice::Disconnect() return NS_OK;; } +NS_IMETHODIMP +DisplayDeviceProvider::HDMIDisplayDevice::IsRequestedUrlSupported( + const nsAString& aRequestedUrl, + bool* aRetVal) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!aRetVal) { + return NS_ERROR_INVALID_POINTER; + } + + // 1-UA device only supports HTTP/HTTPS hosted receiver page. + *aRetVal = DeviceProviderHelpers::IsCommonlySupportedScheme(aRequestedUrl); + + return NS_OK; +} + nsresult DisplayDeviceProvider::HDMIDisplayDevice::OpenTopLevelWindow() { diff --git a/dom/presentation/provider/LegacyMDNSDeviceProvider.cpp b/dom/presentation/provider/LegacyMDNSDeviceProvider.cpp index 7d77a63a0d24..91e48527d111 100644 --- a/dom/presentation/provider/LegacyMDNSDeviceProvider.cpp +++ b/dom/presentation/provider/LegacyMDNSDeviceProvider.cpp @@ -4,6 +4,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "LegacyMDNSDeviceProvider.h" + +#include "DeviceProviderHelpers.h" #include "MainThreadUtils.h" #include "mozilla/Logging.h" #include "mozilla/Preferences.h" @@ -751,6 +753,21 @@ LegacyMDNSDeviceProvider::Device::Disconnect() return NS_OK; } +NS_IMETHODIMP +LegacyMDNSDeviceProvider::Device::IsRequestedUrlSupported( + const nsAString& aRequestedUrl, + bool* aRetVal) +{ + if (!aRetVal) { + return NS_ERROR_INVALID_POINTER; + } + + // Legacy TV 2.5 device only support a fixed set of presentation Apps. + *aRetVal = DeviceProviderHelpers::IsFxTVSupportedAppUrl(aRequestedUrl); + + return NS_OK; +} + } // namespace legacy } // namespace presentation } // namespace dom diff --git a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp index 15ffe9689b27..4bb1e408fcfc 100644 --- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp +++ b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp @@ -4,6 +4,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MulticastDNSDeviceProvider.h" + +#include "DeviceProviderHelpers.h" #include "MainThreadUtils.h" #include "mozilla/Logging.h" #include "mozilla/Preferences.h" @@ -1222,6 +1224,26 @@ MulticastDNSDeviceProvider::Device::Disconnect() return NS_OK; } +NS_IMETHODIMP +MulticastDNSDeviceProvider::Device::IsRequestedUrlSupported( + const nsAString& aRequestedUrl, + bool* aRetVal) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!aRetVal) { + return NS_ERROR_INVALID_POINTER; + } + + // TV 2.6 also supports presentation Apps and HTTP/HTTPS hosted receiver page. + if (DeviceProviderHelpers::IsFxTVSupportedAppUrl(aRequestedUrl) || + DeviceProviderHelpers::IsCommonlySupportedScheme(aRequestedUrl)) { + *aRetVal = true; + } + + return NS_OK; +} + } // namespace presentation } // namespace dom } // namespace mozilla diff --git a/dom/presentation/provider/moz.build b/dom/presentation/provider/moz.build index dd3dd81ab8ab..4b9ac22d64bb 100644 --- a/dom/presentation/provider/moz.build +++ b/dom/presentation/provider/moz.build @@ -10,6 +10,7 @@ EXTRA_COMPONENTS += [ ] UNIFIED_SOURCES += [ + 'DeviceProviderHelpers.cpp', 'DisplayDeviceProvider.cpp', 'MulticastDNSDeviceProvider.cpp', 'PresentationDeviceProviderModule.cpp', diff --git a/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js b/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js index 81623feafb97..6c7c7891636e 100644 --- a/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js +++ b/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js @@ -22,9 +22,13 @@ var testProvider = { var testDevice = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), - establishSessionTransport: function(url, presentationId) { + establishControlChannel: function() { return null; }, + disconnect: function() {}, + isRequestedUrlSupported: function(requestedUrl) { + return true; + }, id: null, name: null, type: null, diff --git a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js index 77d8106a6e65..1594066d771b 100644 --- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js +++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js @@ -185,6 +185,10 @@ const mockedDevice = { sendAsyncMessage('control-channel-established'); return mockedControlChannel; }, + disconnect: function() {}, + isRequestedUrlSupported: function(requestedUrl) { + return true; + }, }; const mockedDevicePrompt = { diff --git a/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js b/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js index 215d0ac8b3b9..5841c8756f91 100644 --- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js +++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js @@ -145,6 +145,10 @@ const mockDevice = { sendAsyncMessage('control-channel-established'); return mockControlChannelOfSender; }, + disconnect: function() {}, + isRequestedUrlSupported: function(requestedUrl) { + return true; + }, }; const mockDevicePrompt = { diff --git a/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js b/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js index 249dc03439b8..3a8fe0a1d9bc 100644 --- a/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js +++ b/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js @@ -426,6 +426,68 @@ function addDevice() { run_next_test(); } +function filterDevice() { + Services.prefs.setBoolPref(PREF_DISCOVERY, true); + + let mockDevice = createDevice("device.local", + 12345, + "service.name", + SERVICE_TYPE); + let mockObj = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), + startDiscovery: function(serviceType, listener) { + listener.onDiscoveryStarted(serviceType); + listener.onServiceFound(createDevice("", + 0, + mockDevice.serviceName, + mockDevice.serviceType)); + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + cancel: function() {} + }; + }, + registerService: function(serviceInfo, listener) {}, + resolveService: function(serviceInfo, listener) { + Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); + Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); + listener.onServiceResolved(createDevice(mockDevice.host, + mockDevice.port, + mockDevice.serviceName, + mockDevice.serviceType)); + } + }; + + let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); + let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, + Ci.nsISupportsWeakReference]), + addDevice: function(device) { + let tests = [ + { requestedUrl: "app://fling-player.gaiamobile.org/index.html", supported: true }, + { requestedUrl: "app://notification-receiver.gaiamobile.org/index.html", supported: true }, + { requestedUrl: "http://example.com", supported: true }, + { requestedUrl: "https://example.com", supported: true }, + { requestedUrl: "ftp://example.com", supported: false }, + { requestedUrl: "app://unknown-app-id", supported: false }, + { requestedUrl: "unknowSchem://example.com", supported: false }, + ]; + + for (let test of tests) { + Assert.equal(device.isRequestedUrlSupported(test.requestedUrl), test.supported); + } + + provider.listener = null; + run_next_test(); + }, + updateDevice: function() {}, + removeDevice: function() {}, + onSessionRequest: function() {}, + }; + + provider.listener = listener; +} + function handleSessionRequest() { Services.prefs.setBoolPref(PREF_DISCOVERY, true); Services.prefs.setBoolPref(PREF_DISCOVERABLE, false); @@ -1239,6 +1301,7 @@ function run_test() { add_test(noRegisterService); add_test(registerServiceDynamically); add_test(addDevice); + add_test(filterDevice); add_test(handleSessionRequest); add_test(handleOnSessionRequest); add_test(handleOnSessionRequestFromUnknownDevice); diff --git a/dom/presentation/tests/xpcshell/test_presentation_device_manager.js b/dom/presentation/tests/xpcshell/test_presentation_device_manager.js index 95067658b9a5..68f07df6563b 100644 --- a/dom/presentation/tests/xpcshell/test_presentation_device_manager.js +++ b/dom/presentation/tests/xpcshell/test_presentation_device_manager.js @@ -40,6 +40,7 @@ var testProvider = { }, }; +const forbiddenRequestedUrl = 'http://example.com'; var testDevice = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), id: 'id', @@ -48,6 +49,10 @@ var testDevice = { establishControlChannel: function(url, presentationId) { return null; }, + disconnect: function() {}, + isRequestedUrlSupported: function(requestedUrl) { + return forbiddenRequestedUrl !== requestedUrl; + }, }; function addProvider() { @@ -122,6 +127,16 @@ function updateDevice() { manager.QueryInterface(Ci.nsIPresentationDeviceListener).updateDevice(testDevice); } +function filterDevice() { + let presentationUrls = Cc['@mozilla.org/array;1'].createInstance(Ci.nsIMutableArray); + let url = Cc['@mozilla.org/supports-string;1'].createInstance(Ci.nsISupportsString); + url.data = forbiddenRequestedUrl; + presentationUrls.appendElement(url, false); + let devices = manager.getAvailableDevices(presentationUrls); + Assert.equal(devices.length, 0, 'expect 0 available device for example.com'); + run_next_test(); +} + function sessionRequest() { let testUrl = 'http://www.example.org/'; let testPresentationId = 'test-presentation-id'; @@ -217,6 +232,7 @@ add_test(addProvider); add_test(forceDiscovery); add_test(addDevice); add_test(updateDevice); +add_test(filterDevice); add_test(sessionRequest); add_test(terminateRequest); add_test(reconnectRequest);