Bug 1016785 - Add more structured filtering to SSDP r=wesj
--- a/mobile/android/base/tests/testSimpleDiscovery.js
+++ b/mobile/android/base/tests/testSimpleDiscovery.js
@@ -19,23 +19,32 @@ function discovery_observer(subject, top
do_check_eq(service.friendlyName, "Pretend Device");
do_check_eq(service.uuid, "uuid:5ec9ff92-e8b2-4a94-a72c-76b34e6dabb1");
do_check_eq(service.manufacturer, "Copy Cat Inc.");
do_check_eq(service.modelName, "Eureka Dongle");
run_next_test();
};
+var testTarget = {
+ target: "test:service",
+ factory: function(service) { /* dummy */ }
+};
+
add_test(function test_default() {
do_register_cleanup(function cleanup() {
+ SimpleServiceDiscovery.unregisterTarget(testTarget);
Services.obs.removeObserver(discovery_observer, "ssdp-service-found");
});
Services.obs.addObserver(discovery_observer, "ssdp-service-found", false);
+ // We need to register a target or processService will ignore us
+ SimpleServiceDiscovery.registerTarget(testTarget);
+
// Create a pretend service
let service = {
location: "http://mochi.test:8888/tests/robocop/simpleservice.xml",
target: "test:service"
};
do_print("Force a detailed ping from a pretend service");
--- a/mobile/android/chrome/content/CastingApps.js
+++ b/mobile/android/chrome/content/CastingApps.js
@@ -1,26 +1,33 @@
/* 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/. */
"use strict";
+// Define service targets. We should consider moving these to their respective
+// JSM files, but we left them here to allow for better lazy JSM loading.
+var rokuTarget = {
+ target: "roku:ecp",
+ factory: function(aService) {
+ Cu.import("resource://gre/modules/RokuApp.jsm");
+ return new RokuApp(aService);
+ }
+};
+
var CastingApps = {
_castMenuId: -1,
init: function ca_init() {
if (!this.isEnabled()) {
return;
}
- // Register a service target
- SimpleServiceDiscovery.registerTarget("roku:ecp", function(aService) {
- Cu.import("resource://gre/modules/RokuApp.jsm");
- return new RokuApp(aService);
- });
+ // Register targets
+ SimpleServiceDiscovery.registerTarget(rokuTarget);
// Search for devices continuously every 120 seconds
SimpleServiceDiscovery.search(120 * 1000);
this._castMenuId = NativeWindow.contextmenus.add(
Strings.browser.GetStringFromName("contextmenu.castToScreen"),
this.filterCast,
this.openExternal.bind(this)
@@ -247,16 +254,20 @@ var CastingApps = {
CastingApps.openExternal(video, 0, 0);
return;
}
}
}
},
_findCastableVideo: function _findCastableVideo(aBrowser) {
+ if (!aBrowser) {
+ return null;
+ }
+
// Scan for a <video> being actively cast. Also look for a castable <video>
// on the page.
let castableVideo = null;
let videos = aBrowser.contentDocument.querySelectorAll("video");
for (let video of videos) {
let unwrappedVideo = XPCNativeWrapper.unwrap(video);
if (unwrappedVideo.mozIsCasting) {
// This <video> is cast-active. Break out of loop.
@@ -446,9 +457,8 @@ var CastingApps = {
}
let status = aRemoteMedia.status;
if (status == "completed") {
this.closeExternal();
}
}
};
-
--- a/mobile/android/modules/SimpleServiceDiscovery.jsm
+++ b/mobile/android/modules/SimpleServiceDiscovery.jsm
@@ -220,20 +220,38 @@ var SimpleServiceDiscovery = {
if (service.lastPing != this._searchTimestamp) {
Services.obs.notifyObservers(null, EVENT_SERVICE_LOST, service.uuid);
this._services.delete(service.uuid);
}
}
}
},
- registerTarget: function registerTarget(aTarget, aAppFactory) {
+ registerTarget: function registerTarget(aTarget) {
+ // We must have "target" and "factory" defined
+ if (!("target" in aTarget) || !("factory" in aTarget)) {
+ // Fatal for registration
+ throw "Registration requires a target and a location";
+ }
+
// Only add if we don't already know about this target
- if (!this._targets.has(aTarget)) {
- this._targets.set(aTarget, { target: aTarget, factory: aAppFactory });
+ if (!this._targets.has(aTarget.target)) {
+ this._targets.set(aTarget.target, aTarget);
+ }
+ },
+
+ unregisterTarget: function unregisterTarget(aTarget) {
+ // We must have "target" and "factory" defined
+ if (!("target" in aTarget) || !("factory" in aTarget)) {
+ return;
+ }
+
+ // Only remove if we know about this target
+ if (this._targets.has(aTarget.target)) {
+ this._targets.delete(aTarget.target);
}
},
findAppForService: function findAppForService(aService) {
if (!aService || !aService.target) {
return null;
}
@@ -255,16 +273,39 @@ var SimpleServiceDiscovery = {
get services() {
let array = [];
for (let [key, service] of this._services) {
array.push(service);
}
return array;
},
+ // Returns false if the service does not match the target's filters
+ _filterService: function _filterService(aService) {
+ let target = this._targets.get(aService.target);
+ if (!target) {
+ return false;
+ }
+
+ // If we have no filter, everything passes
+ if (!("filters" in target)) {
+ return true;
+ }
+
+ // If any filter fails, the service fails
+ let filters = target.filters;
+ for (let filter in filters) {
+ if (filter in aService && aService[filter] != filters[filter]) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
_processService: function _processService(aService) {
// Use the REST api to request more information about this service
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
xhr.open("GET", aService.location, true);
xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
xhr.overrideMimeType("text/xml");
xhr.addEventListener("load", (function() {
@@ -273,16 +314,21 @@ var SimpleServiceDiscovery = {
aService.appsURL = xhr.getResponseHeader("Application-URL");
if (aService.appsURL && !aService.appsURL.endsWith("/"))
aService.appsURL += "/";
aService.friendlyName = doc.querySelector("friendlyName").textContent;
aService.uuid = doc.querySelector("UDN").textContent;
aService.manufacturer = doc.querySelector("manufacturer").textContent;
aService.modelName = doc.querySelector("modelName").textContent;
+ // Filter out services that do not match the target filter
+ if (!this._filterService(aService)) {
+ return;
+ }
+
// Only add and notify if we don't already know about this service
if (!this._services.has(aService.uuid)) {
this._services.set(aService.uuid, aService);
Services.obs.notifyObservers(null, EVENT_SERVICE_FOUND, aService.uuid);
}
// Make sure we remember this service is not stale
this._services.get(aService.uuid).lastPing = this._searchTimestamp;