From 8c75399e91bc27c7b5e6fcc231ac0dfd6a6b2ed8 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Wed, 15 Oct 2014 18:24:34 -0400 Subject: [PATCH] bug 1054959 - Add 'Send Video To Device' to the context menu for sending videos from desktop to a second screen r=gavin, ui-r=madhava --- browser/base/content/browser-context.inc | 5 + browser/base/content/browser.js | 6 + browser/base/content/nsContextMenu.js | 29 ++++ browser/components/nsBrowserGlue.js | 25 +++ .../locales/en-US/chrome/browser/browser.dtd | 2 + browser/modules/CastingApps.jsm | 160 ++++++++++++++++++ browser/modules/moz.build | 1 + .../secondscreen/SimpleServiceDiscovery.jsm | 4 +- 8 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 browser/modules/CastingApps.jsm diff --git a/browser/base/content/browser-context.inc b/browser/base/content/browser-context.inc index e8a58892b3b..44fb0cf3ef0 100644 --- a/browser/base/content/browser-context.inc +++ b/browser/base/content/browser-context.inc @@ -240,6 +240,11 @@ label="&emailVideoCmd.label;" accesskey="&emailVideoCmd.accesskey;" oncommand="gContextMenu.sendMedia();"/> + + + 0 && + CastingApps.getServicesForVideo(this.target).length > 0; + this.setItemAttr("context-castvideo", "disabled", !shouldShowCast); }, initViewItems: function CM_initViewItems() { @@ -1316,6 +1326,25 @@ nsContextMenu.prototype = { MailIntegration.sendMessage(this.mediaURL, ""); }, + castVideo: function() { + CastingApps.openExternal(this.target, window); + }, + + populateCastVideoMenu: function(popup) { + let videoEl = this.target; + popup.innerHTML = null; + let doc = popup.ownerDocument; + let services = CastingApps.getServicesForVideo(videoEl); + services.forEach(service => { + let item = doc.createElement("menuitem"); + item.setAttribute("label", service.friendlyName); + item.addEventListener("command", event => { + CastingApps.sendVideoToService(videoEl, service); + }); + popup.appendChild(item); + }); + }, + playPlugin: function() { gPluginHandler.contextMenuCommand(this.browser, this.target, "play"); }, diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index abd2504a910..5acb094f716 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -99,6 +99,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown", XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent", "resource://gre/modules/LoginManagerParent.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "SimpleServiceDiscovery", + "resource://gre/modules/SimpleServiceDiscovery.jsm"); + #ifdef NIGHTLY_BUILD XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX", "resource:///modules/SignInToWebsite.jsm"); @@ -747,8 +750,30 @@ BrowserGlue.prototype = { FormValidationHandler.uninit(); }, + _initServiceDiscovery: function () { + var rokuDevice = { + id: "roku:ecp", + target: "roku:ecp", + factory: function(aService) { + Cu.import("resource://gre/modules/RokuApp.jsm"); + return new RokuApp(aService); + }, + mirror: false, + types: ["video/mp4"], + extensions: ["mp4"] + }; + + // Register targets + SimpleServiceDiscovery.registerDevice(rokuDevice); + + // Search for devices continuously every 120 seconds + SimpleServiceDiscovery.search(120 * 1000); + }, + // All initial windows have opened. _onWindowsRestored: function BG__onWindowsRestored() { + this._initServiceDiscovery(); + // Show update notification, if needed. if (Services.prefs.prefHasUserValue("app.update.postupdate")) this._showUpdateNotification(); diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd index 29f515d814c..e84a5512a5c 100644 --- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -484,6 +484,8 @@ These should match what Safari and other Apple applications use on OS X Lion. -- + + diff --git a/browser/modules/CastingApps.jsm b/browser/modules/CastingApps.jsm new file mode 100644 index 00000000000..df974f04f60 --- /dev/null +++ b/browser/modules/CastingApps.jsm @@ -0,0 +1,160 @@ +// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* 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"; +this.EXPORTED_SYMBOLS = ["CastingApps"]; + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm"); + + +var CastingApps = { + _sendEventToVideo: function (element, data) { + let event = element.ownerDocument.createEvent("CustomEvent"); + event.initCustomEvent("media-videoCasting", false, true, JSON.stringify(data)); + element.dispatchEvent(event); + }, + + makeURI: function (url, charset, baseURI) { + return Services.io.newURI(url, charset, baseURI); + }, + + getVideo: function (element) { + if (!element) { + return null; + } + + let extensions = SimpleServiceDiscovery.getSupportedExtensions(); + let types = SimpleServiceDiscovery.getSupportedMimeTypes(); + + // Grab the poster attribute from the