diff options
Diffstat (limited to 'mobile/android/chrome/content/CastingApps.js')
-rw-r--r-- | mobile/android/chrome/content/CastingApps.js | 850 |
1 files changed, 0 insertions, 850 deletions
diff --git a/mobile/android/chrome/content/CastingApps.js b/mobile/android/chrome/content/CastingApps.js deleted file mode 100644 index 76773c4d8..000000000 --- a/mobile/android/chrome/content/CastingApps.js +++ /dev/null @@ -1,850 +0,0 @@ -// -*- 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"; - -XPCOMUtils.defineLazyModuleGetter(this, "PageActions", - "resource://gre/modules/PageActions.jsm"); - -// Define service devices. We should consider moving these to their respective -// JSM files, but we left them here to allow for better lazy JSM loading. -var rokuDevice = { - id: "roku:ecp", - target: "roku:ecp", - factory: function(aService) { - Cu.import("resource://gre/modules/RokuApp.jsm"); - return new RokuApp(aService); - }, - types: ["video/mp4"], - extensions: ["mp4"] -}; - -var mediaPlayerDevice = { - id: "media:router", - target: "media:router", - factory: function(aService) { - Cu.import("resource://gre/modules/MediaPlayerApp.jsm"); - return new MediaPlayerApp(aService); - }, - types: ["video/mp4", "video/webm", "application/x-mpegurl"], - extensions: ["mp4", "webm", "m3u", "m3u8"], - init: function() { - Services.obs.addObserver(this, "MediaPlayer:Added", false); - Services.obs.addObserver(this, "MediaPlayer:Changed", false); - Services.obs.addObserver(this, "MediaPlayer:Removed", false); - }, - observe: function(subject, topic, data) { - if (topic === "MediaPlayer:Added") { - let service = this.toService(JSON.parse(data)); - SimpleServiceDiscovery.addService(service); - } else if (topic === "MediaPlayer:Changed") { - let service = this.toService(JSON.parse(data)); - SimpleServiceDiscovery.updateService(service); - } else if (topic === "MediaPlayer:Removed") { - SimpleServiceDiscovery.removeService(data); - } - }, - toService: function(display) { - // Convert the native data into something matching what is created in _processService() - return { - location: display.location, - target: "media:router", - friendlyName: display.friendlyName, - uuid: display.uuid, - manufacturer: display.manufacturer, - modelName: display.modelName, - mirror: display.mirror - }; - } -}; - -var fxOSTVDevice = { - id: "app://fling-player.gaiamobile.org", - target: "app://fling-player.gaiamobile.org/index.html", - factory: function(aService) { - Cu.import("resource://gre/modules/PresentationApp.jsm"); - let request = new window.PresentationRequest(this.target); - return new PresentationApp(aService, request); - }, - init: function() { - Services.obs.addObserver(this, "presentation-device-change", false); - SimpleServiceDiscovery.addExternalDiscovery(this); - }, - observe: function(subject, topic, data) { - let device = subject.QueryInterface(Ci.nsIPresentationDevice); - let service = this.toService(device); - switch (data) { - case "add": - SimpleServiceDiscovery.addService(service); - break; - case "update": - SimpleServiceDiscovery.updateService(service); - break; - case "remove": - if(SimpleServiceDiscovery.findServiceForID(device.id)) { - SimpleServiceDiscovery.removeService(device.id); - } - break; - } - }, - toService: function(device) { - return { - location: device.id, - target: fxOSTVDevice.target, - friendlyName: device.name, - uuid: device.id, - manufacturer: "Firefox OS TV", - modelName: "Firefox OS TV", - }; - }, - startDiscovery: function() { - window.navigator.mozPresentationDeviceInfo.forceDiscovery(); - - // need to update the lastPing time for known device. - window.navigator.mozPresentationDeviceInfo.getAll() - .then(function(devices) { - for (let device of devices) { - let service = fxOSTVDevice.toService(device); - SimpleServiceDiscovery.addService(service); - } - }); - }, - stopDiscovery: function() { - // do nothing - }, - types: ["video/mp4", "video/webm"], - extensions: ["mp4", "webm"], -}; - -var CastingApps = { - _castMenuId: -1, - mirrorStartMenuId: -1, - mirrorStopMenuId: -1, - _blocked: null, - _bound: null, - _interval: 120 * 1000, // 120 seconds - - init: function ca_init() { - if (!this.isCastingEnabled()) { - return; - } - - // Register targets - SimpleServiceDiscovery.registerDevice(rokuDevice); - - // MediaPlayerDevice will notify us any time the native device list changes. - mediaPlayerDevice.init(); - SimpleServiceDiscovery.registerDevice(mediaPlayerDevice); - - // Presentation Device will notify us any time the available device list changes. - if (window.PresentationRequest) { - fxOSTVDevice.init(); - SimpleServiceDiscovery.registerDevice(fxOSTVDevice); - } - - // Search for devices continuously - SimpleServiceDiscovery.search(this._interval); - - this._castMenuId = NativeWindow.contextmenus.add( - Strings.browser.GetStringFromName("contextmenu.sendToDevice"), - this.filterCast, - this.handleContextMenu.bind(this) - ); - - Services.obs.addObserver(this, "Casting:Play", false); - Services.obs.addObserver(this, "Casting:Pause", false); - Services.obs.addObserver(this, "Casting:Stop", false); - Services.obs.addObserver(this, "Casting:Mirror", false); - Services.obs.addObserver(this, "ssdp-service-found", false); - Services.obs.addObserver(this, "ssdp-service-lost", false); - Services.obs.addObserver(this, "application-background", false); - Services.obs.addObserver(this, "application-foreground", false); - - BrowserApp.deck.addEventListener("TabSelect", this, true); - BrowserApp.deck.addEventListener("pageshow", this, true); - BrowserApp.deck.addEventListener("playing", this, true); - BrowserApp.deck.addEventListener("ended", this, true); - BrowserApp.deck.addEventListener("MozAutoplayMediaBlocked", this, true); - // Note that the XBL binding is untrusted - BrowserApp.deck.addEventListener("MozNoControlsVideoBindingAttached", this, true, true); - }, - - _mirrorStarted: function(stopMirrorCallback) { - this.stopMirrorCallback = stopMirrorCallback; - NativeWindow.menu.update(this.mirrorStartMenuId, { visible: false }); - NativeWindow.menu.update(this.mirrorStopMenuId, { visible: true }); - }, - - serviceAdded: function(aService) { - if (this.isMirroringEnabled() && aService.mirror && this.mirrorStartMenuId == -1) { - this.mirrorStartMenuId = NativeWindow.menu.add({ - name: Strings.browser.GetStringFromName("casting.mirrorTab"), - callback: function() { - let callbackFunc = function(aService) { - let app = SimpleServiceDiscovery.findAppForService(aService); - if (app) { - app.mirror(function() {}, window, BrowserApp.selectedTab.getViewport(), this._mirrorStarted.bind(this), window.BrowserApp.selectedBrowser.contentWindow); - } - }.bind(this); - - this.prompt(callbackFunc, aService => aService.mirror); - }.bind(this), - parent: NativeWindow.menu.toolsMenuID - }); - - this.mirrorStopMenuId = NativeWindow.menu.add({ - name: Strings.browser.GetStringFromName("casting.mirrorTabStop"), - callback: function() { - if (this.tabMirror) { - this.tabMirror.stop(); - this.tabMirror = null; - } else if (this.stopMirrorCallback) { - this.stopMirrorCallback(); - this.stopMirrorCallback = null; - } - NativeWindow.menu.update(this.mirrorStartMenuId, { visible: true }); - NativeWindow.menu.update(this.mirrorStopMenuId, { visible: false }); - }.bind(this), - }); - } - if (this.mirrorStartMenuId != -1) { - NativeWindow.menu.update(this.mirrorStopMenuId, { visible: false }); - } - }, - - serviceLost: function(aService) { - if (aService.mirror && this.mirrorStartMenuId != -1) { - let haveMirror = false; - SimpleServiceDiscovery.services.forEach(function(service) { - if (service.mirror) { - haveMirror = true; - } - }); - if (!haveMirror) { - NativeWindow.menu.remove(this.mirrorStartMenuId); - this.mirrorStartMenuId = -1; - } - } - }, - - isCastingEnabled: function isCastingEnabled() { - return Services.prefs.getBoolPref("browser.casting.enabled"); - }, - - isMirroringEnabled: function isMirroringEnabled() { - return Services.prefs.getBoolPref("browser.mirroring.enabled"); - }, - - observe: function (aSubject, aTopic, aData) { - switch (aTopic) { - case "Casting:Play": - if (this.session && this.session.remoteMedia.status == "paused") { - this.session.remoteMedia.play(); - } - break; - case "Casting:Pause": - if (this.session && this.session.remoteMedia.status == "started") { - this.session.remoteMedia.pause(); - } - break; - case "Casting:Stop": - if (this.session) { - this.closeExternal(); - } - break; - case "Casting:Mirror": - { - Cu.import("resource://gre/modules/TabMirror.jsm"); - this.tabMirror = new TabMirror(aData, window); - NativeWindow.menu.update(this.mirrorStartMenuId, { visible: false }); - NativeWindow.menu.update(this.mirrorStopMenuId, { visible: true }); - } - break; - case "ssdp-service-found": - this.serviceAdded(SimpleServiceDiscovery.findServiceForID(aData)); - break; - case "ssdp-service-lost": - this.serviceLost(SimpleServiceDiscovery.findServiceForID(aData)); - break; - case "application-background": - // Turn off polling while in the background - this._interval = SimpleServiceDiscovery.search(0); - SimpleServiceDiscovery.stopSearch(); - break; - case "application-foreground": - // Turn polling on when app comes back to foreground - SimpleServiceDiscovery.search(this._interval); - break; - } - }, - - handleEvent: function(aEvent) { - switch (aEvent.type) { - case "TabSelect": { - let tab = BrowserApp.getTabForBrowser(aEvent.target); - this._updatePageActionForTab(tab, aEvent); - break; - } - case "pageshow": { - let tab = BrowserApp.getTabForWindow(aEvent.originalTarget.defaultView); - this._updatePageActionForTab(tab, aEvent); - break; - } - case "playing": - case "ended": { - let video = aEvent.target; - if (video instanceof HTMLVideoElement) { - // If playing, send the <video>, but if ended we send nothing to shutdown the pageaction - this._updatePageActionForVideo(aEvent.type === "playing" ? video : null); - } - break; - } - case "MozAutoplayMediaBlocked": { - if (this._bound && this._bound.has(aEvent.target)) { - aEvent.target.dispatchEvent(new CustomEvent("MozNoControlsBlockedVideo")); - } else { - if (!this._blocked) { - this._blocked = new WeakMap; - } - this._blocked.set(aEvent.target, true); - } - break; - } - case "MozNoControlsVideoBindingAttached": { - if (!this._bound) { - this._bound = new WeakMap; - } - this._bound.set(aEvent.target, true); - if (this._blocked && this._blocked.has(aEvent.target)) { - this._blocked.delete(aEvent.target); - aEvent.target.dispatchEvent(new CustomEvent("MozNoControlsBlockedVideo")); - } - break; - } - } - }, - - _sendEventToVideo: function _sendEventToVideo(aElement, aData) { - let event = aElement.ownerDocument.createEvent("CustomEvent"); - event.initCustomEvent("media-videoCasting", false, true, JSON.stringify(aData)); - aElement.dispatchEvent(event); - }, - - handleVideoBindingAttached: function handleVideoBindingAttached(aTab, aEvent) { - // Let's figure out if we have everything needed to cast a video. The binding - // defaults to |false| so we only need to send an event if |true|. - let video = aEvent.target; - if (!(video instanceof HTMLVideoElement)) { - return; - } - - if (SimpleServiceDiscovery.services.length == 0) { - return; - } - - this.getVideo(video, 0, 0, (aBundle) => { - // Let the binding know casting is allowed - if (aBundle) { - this._sendEventToVideo(aBundle.element, { allow: true }); - } - }); - }, - - handleVideoBindingCast: function handleVideoBindingCast(aTab, aEvent) { - // The binding wants to start a casting session - let video = aEvent.target; - if (!(video instanceof HTMLVideoElement)) { - return; - } - - // Close an existing session first. closeExternal has checks for an exsting - // session and handles remote and video binding shutdown. - this.closeExternal(); - - // Start the new session - UITelemetry.addEvent("cast.1", "button", null); - this.openExternal(video, 0, 0); - }, - - makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) { - return Services.io.newURI(aURL, aOriginCharset, aBaseURI); - }, - - allowableExtension: function(aURI, aExtensions) { - return (aURI instanceof Ci.nsIURL) && aExtensions.indexOf(aURI.fileExtension) != -1; - }, - - allowableMimeType: function(aType, aTypes) { - return aTypes.indexOf(aType) != -1; - }, - - // This method will look at the aElement (or try to find a video at aX, aY) that has - // a castable source. If found, aCallback will be called with a JSON meta bundle. If - // no castable source was found, aCallback is called with null. - getVideo: function(aElement, aX, aY, aCallback) { - let extensions = SimpleServiceDiscovery.getSupportedExtensions(); - let types = SimpleServiceDiscovery.getSupportedMimeTypes(); - - // Fast path: Is the given element a video element? - if (aElement instanceof HTMLVideoElement) { - // If we found a video element, no need to look further, even if no - // castable video source is found. - this._getVideo(aElement, types, extensions, aCallback); - return; - } - - // Maybe this is an overlay, with the video element under it. - // Use the (x, y) location to guess at a <video> element. - - // The context menu system will keep walking up the DOM giving us a chance - // to find an element we match. When it hits <html> things can go BOOM. - try { - let elements = aElement.ownerDocument.querySelectorAll("video"); - for (let element of elements) { - // Look for a video element contained in the overlay bounds - let rect = element.getBoundingClientRect(); - if (aY >= rect.top && aX >= rect.left && aY <= rect.bottom && aX <= rect.right) { - // Once we find a <video> under the overlay, we check it and exit. - this._getVideo(element, types, extensions, aCallback); - return; - } - } - } catch(e) {} - }, - - _getContentTypeForURI: function(aURI, aElement, aCallback) { - let channel; - try { - let secFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS; - if (aElement.crossOrigin) { - secFlags = Ci.nsILoadInfo.SEC_REQUIRE_CORS_DATA_INHERITS; - if (aElement.crossOrigin === "use-credentials") { - secFlags |= Ci.nsILoadInfo.SEC_COOKIES_INCLUDE; - } - } - channel = NetUtil.newChannel({ - uri: aURI, - loadingNode: aElement, - securityFlags: secFlags, - contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_VIDEO - }); - } catch(e) { - aCallback(null); - return; - } - - let listener = { - onStartRequest: function(request, context) { - switch (channel.responseStatus) { - case 301: - case 302: - case 303: - request.cancel(0); - let location = channel.getResponseHeader("Location"); - CastingApps._getContentTypeForURI(CastingApps.makeURI(location), aElement, aCallback); - break; - default: - aCallback(channel.contentType); - request.cancel(0); - break; - } - }, - onStopRequest: function(request, context, statusCode) {}, - onDataAvailable: function(request, context, stream, offset, count) {} - }; - - if (channel) { - channel.asyncOpen2(listener); - } else { - aCallback(null); - } - }, - - // Because this method uses a callback, make sure we return ASAP if we know - // we have a castable video source. - _getVideo: function(aElement, aTypes, aExtensions, aCallback) { - // Keep a list of URIs we need for an async mimetype check - let asyncURIs = []; - - // Grab the poster attribute from the <video> - let posterURL = aElement.poster; - - // First, look to see if the <video> has a src attribute - let sourceURL = aElement.src; - - // If empty, try the currentSrc - if (!sourceURL) { - sourceURL = aElement.currentSrc; - } - - if (sourceURL) { - // Use the file extension to guess the mime type - let sourceURI = this.makeURI(sourceURL, null, this.makeURI(aElement.baseURI)); - if (this.allowableExtension(sourceURI, aExtensions)) { - aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI}); - return; - } - - if (aElement.type) { - // Fast sync check - if (this.allowableMimeType(aElement.type, aTypes)) { - aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: aElement.type }); - return; - } - } - - // Delay the async check until we sync scan all possible URIs - asyncURIs.push(sourceURI); - } - - // Next, look to see if there is a <source> child element that meets - // our needs - let sourceNodes = aElement.getElementsByTagName("source"); - for (let sourceNode of sourceNodes) { - let sourceURI = this.makeURI(sourceNode.src, null, this.makeURI(sourceNode.baseURI)); - - // Using the type attribute is our ideal way to guess the mime type. Otherwise, - // fallback to using the file extension to guess the mime type - if (this.allowableExtension(sourceURI, aExtensions)) { - aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: sourceNode.type }); - return; - } - - if (sourceNode.type) { - // Fast sync check - if (this.allowableMimeType(sourceNode.type, aTypes)) { - aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: sourceNode.type }); - return; - } - } - - // Delay the async check until we sync scan all possible URIs - asyncURIs.push(sourceURI); - } - - // Helper method that walks the array of possible URIs, fetching the mimetype as we go. - // As soon as we find a good sourceURL, avoid firing the callback any further - var _getContentTypeForURIs = (aURIs) => { - // Do an async fetch to figure out the mimetype of the source video - let sourceURI = aURIs.pop(); - this._getContentTypeForURI(sourceURI, aElement, (aType) => { - if (this.allowableMimeType(aType, aTypes)) { - // We found a supported mimetype. - aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: aType }); - } else { - // This URI was not a supported mimetype, so let's try the next, if we have more. - if (aURIs.length > 0) { - _getContentTypeForURIs(aURIs); - } else { - // We were not able to find a supported mimetype. - aCallback(null); - } - } - }); - } - - // If we didn't find a good URI directly, let's look using async methods. - if (asyncURIs.length > 0) { - _getContentTypeForURIs(asyncURIs); - } - }, - - // This code depends on handleVideoBindingAttached setting mozAllowCasting - // so we can quickly figure out if the video is castable - isVideoCastable: function(aElement, aX, aY) { - // Use the flag set when the <video> binding was created as the check - if (aElement instanceof HTMLVideoElement) { - return aElement.mozAllowCasting; - } - - // This is called by the context menu system and the system will keep - // walking up the DOM giving us a chance to find an element we match. - // When it hits <html> things can go BOOM. - try { - // Maybe this is an overlay, with the video element under it - // Use the (x, y) location to guess at a <video> element - let elements = aElement.ownerDocument.querySelectorAll("video"); - for (let element of elements) { - // Look for a video element contained in the overlay bounds - let rect = element.getBoundingClientRect(); - if (aY >= rect.top && aX >= rect.left && aY <= rect.bottom && aX <= rect.right) { - // Use the flag set when the <video> binding was created as the check - return element.mozAllowCasting; - } - } - } catch(e) {} - - return false; - }, - - filterCast: { - matches: function(aElement, aX, aY) { - // This behavior matches the pageaction: As long as a video is castable, - // we can cast it, even if it's already being cast to a device. - if (SimpleServiceDiscovery.services.length == 0) - return false; - return CastingApps.isVideoCastable(aElement, aX, aY); - } - }, - - pageAction: { - click: function() { - // Since this is a pageaction, we use the selected browser - let browser = BrowserApp.selectedBrowser; - if (!browser) { - return; - } - - // Look for a castable <video> that is playing, and start casting it - let videos = browser.contentDocument.querySelectorAll("video"); - for (let video of videos) { - if (!video.paused && video.mozAllowCasting) { - UITelemetry.addEvent("cast.1", "pageaction", null); - 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) { - if (video.mozIsCasting) { - // This <video> is cast-active. Break out of loop. - return video; - } - - if (!video.paused && video.mozAllowCasting) { - // This <video> is cast-ready. Keep looking so cast-active could be found. - castableVideo = video; - } - } - - // Could be null - return castableVideo; - }, - - _updatePageActionForTab: function _updatePageActionForTab(aTab, aEvent) { - // We only care about events on the selected tab - if (aTab != BrowserApp.selectedTab) { - return; - } - - // Update the page action, scanning for a castable <video> - this._updatePageAction(); - }, - - _updatePageActionForVideo: function _updatePageActionForVideo(aVideo) { - this._updatePageAction(aVideo); - }, - - _updatePageAction: function _updatePageAction(aVideo) { - // Remove any exising pageaction first, in case state changes or we don't have - // a castable video - if (this.pageAction.id) { - PageActions.remove(this.pageAction.id); - delete this.pageAction.id; - } - - if (!aVideo) { - aVideo = this._findCastableVideo(BrowserApp.selectedBrowser); - if (!aVideo) { - return; - } - } - - // We only show pageactions if the <video> is from the selected tab - if (BrowserApp.selectedTab != BrowserApp.getTabForWindow(aVideo.ownerDocument.defaultView.top)) { - return; - } - - // We check for two state here: - // 1. The video is actively being cast - // 2. The video is allowed to be cast and is currently playing - // Both states have the same action: Show the cast page action - if (aVideo.mozIsCasting) { - this.pageAction.id = PageActions.add({ - title: Strings.browser.GetStringFromName("contextmenu.sendToDevice"), - icon: "drawable://casting_active", - clickCallback: this.pageAction.click, - important: true - }); - } else if (aVideo.mozAllowCasting) { - this.pageAction.id = PageActions.add({ - title: Strings.browser.GetStringFromName("contextmenu.sendToDevice"), - icon: "drawable://casting", - clickCallback: this.pageAction.click, - important: true - }); - } - }, - - prompt: function(aCallback, aFilterFunc) { - let items = []; - let filteredServices = []; - SimpleServiceDiscovery.services.forEach(function(aService) { - let item = { - label: aService.friendlyName, - selected: false - }; - if (!aFilterFunc || aFilterFunc(aService)) { - filteredServices.push(aService); - items.push(item); - } - }); - - if (items.length == 0) { - return; - } - - let prompt = new Prompt({ - title: Strings.browser.GetStringFromName("casting.sendToDevice") - }).setSingleChoiceItems(items).show(function(data) { - let selected = data.button; - let service = selected == -1 ? null : filteredServices[selected]; - if (aCallback) - aCallback(service); - }); - }, - - handleContextMenu: function(aElement, aX, aY) { - UITelemetry.addEvent("action.1", "contextmenu", null, "web_cast"); - UITelemetry.addEvent("cast.1", "contextmenu", null); - this.openExternal(aElement, aX, aY); - }, - - openExternal: function(aElement, aX, aY) { - // Start a second screen media service - this.getVideo(aElement, aX, aY, this._openExternal.bind(this)); - }, - - _openExternal: function(aVideo) { - if (!aVideo) { - return; - } - - function filterFunc(aService) { - return this.allowableExtension(aVideo.sourceURI, aService.extensions) || this.allowableMimeType(aVideo.type, aService.types); - } - - this.prompt(function(aService) { - if (!aService) - return; - - // Make sure we have a player app for the given service - let app = SimpleServiceDiscovery.findAppForService(aService); - if (!app) - return; - - if (aVideo.element) { - aVideo.title = aVideo.element.ownerDocument.defaultView.top.document.title; - - // If the video is currently playing on the device, pause it - if (!aVideo.element.paused) { - aVideo.element.pause(); - } - } - - app.stop(function() { - app.start(function(aStarted) { - if (!aStarted) { - dump("CastingApps: Unable to start app"); - return; - } - - app.remoteMedia(function(aRemoteMedia) { - if (!aRemoteMedia) { - dump("CastingApps: Failed to create remotemedia"); - return; - } - - this.session = { - service: aService, - app: app, - remoteMedia: aRemoteMedia, - data: { - title: aVideo.title, - source: aVideo.source, - poster: aVideo.poster - }, - videoRef: Cu.getWeakReference(aVideo.element) - }; - }.bind(this), this); - }.bind(this)); - }.bind(this)); - }.bind(this), filterFunc.bind(this)); - }, - - closeExternal: function() { - if (!this.session) { - return; - } - - this.session.remoteMedia.shutdown(); - this._shutdown(); - }, - - _shutdown: function() { - if (!this.session) { - return; - } - - this.session.app.stop(); - let video = this.session.videoRef.get(); - if (video) { - this._sendEventToVideo(video, { active: false }); - this._updatePageAction(); - } - - delete this.session; - }, - - // RemoteMedia callback API methods - onRemoteMediaStart: function(aRemoteMedia) { - if (!this.session) { - return; - } - - aRemoteMedia.load(this.session.data); - Messaging.sendRequest({ type: "Casting:Started", device: this.session.service.friendlyName }); - - let video = this.session.videoRef.get(); - if (video) { - this._sendEventToVideo(video, { active: true }); - this._updatePageAction(video); - } - }, - - onRemoteMediaStop: function(aRemoteMedia) { - Messaging.sendRequest({ type: "Casting:Stopped" }); - this._shutdown(); - }, - - onRemoteMediaStatus: function(aRemoteMedia) { - if (!this.session) { - return; - } - - let status = aRemoteMedia.status; - switch (status) { - case "started": - Messaging.sendRequest({ type: "Casting:Playing" }); - break; - case "paused": - Messaging.sendRequest({ type: "Casting:Paused" }); - break; - case "completed": - this.closeExternal(); - break; - } - } -}; |