mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-27 20:25:44 +00:00
Bug 953381 - Move CastingApps into a separate JS file r=wesj
This commit is contained in:
parent
affa312e7e
commit
f32c4e5d81
@ -274,6 +274,9 @@ pref("browser.search.noCurrentEngine", true);
|
||||
pref("browser.search.official", true);
|
||||
#endif
|
||||
|
||||
// Control media casting feature
|
||||
pref("browser.casting.enabled", false);
|
||||
|
||||
// Enable sparse localization by setting a few package locale overrides
|
||||
pref("chrome.override_package.global", "browser");
|
||||
pref("chrome.override_package.mozapps", "browser");
|
||||
|
213
mobile/android/chrome/content/CastingApps.js
Normal file
213
mobile/android/chrome/content/CastingApps.js
Normal file
@ -0,0 +1,213 @@
|
||||
/* 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";
|
||||
|
||||
var CastingApps = {
|
||||
_castMenuId: -1,
|
||||
|
||||
init: function ca_init() {
|
||||
if (!this.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)
|
||||
);
|
||||
},
|
||||
|
||||
uninit: function ca_uninit() {
|
||||
NativeWindow.contextmenus.remove(this._castMenuId);
|
||||
},
|
||||
|
||||
isEnabled: function isEnabled() {
|
||||
return Services.prefs.getBoolPref("browser.casting.enabled");
|
||||
},
|
||||
|
||||
makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) {
|
||||
return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
|
||||
},
|
||||
|
||||
getVideo: function(aElement, aX, aY) {
|
||||
// Fast path: Is the given element a video element
|
||||
let video = this._getVideo(aElement);
|
||||
if (video) {
|
||||
return video;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// 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) {
|
||||
video = this._getVideo(element);
|
||||
if (video) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
// Could be null
|
||||
return video;
|
||||
},
|
||||
|
||||
_getVideo: function(aElement) {
|
||||
// Given the hardware support for H264, let's only look for 'mp4' sources
|
||||
if (!aElement instanceof HTMLVideoElement) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function allowableExtension(aURI) {
|
||||
if (aURI && aURI instanceof Ci.nsIURL) {
|
||||
return (aURI.fileExtension == "mp4");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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 (allowableExtension(sourceURI)) {
|
||||
return { video: aElement, source: sourceURI.spec, poster: posterURL };
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (sourceNode.type == "video/mp4" || allowableExtension(sourceURI)) {
|
||||
return { video: aElement, source: sourceURI.spec, poster: posterURL };
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
filterCast: {
|
||||
matches: function(aElement, aX, aY) {
|
||||
if (SimpleServiceDiscovery.services.length == 0)
|
||||
return false;
|
||||
let video = CastingApps.getVideo(aElement, aX, aY);
|
||||
if (CastingApps.session) {
|
||||
return (video && CastingApps.session.data.source != video.source);
|
||||
}
|
||||
return (video != null);
|
||||
}
|
||||
},
|
||||
|
||||
prompt: function(aCallback) {
|
||||
let items = [];
|
||||
SimpleServiceDiscovery.services.forEach(function(aService) {
|
||||
let item = {
|
||||
label: aService.friendlyName,
|
||||
selected: false
|
||||
};
|
||||
items.push(item);
|
||||
});
|
||||
|
||||
let prompt = new Prompt({
|
||||
title: Strings.browser.GetStringFromName("casting.prompt")
|
||||
}).setSingleChoiceItems(items).show(function(data) {
|
||||
let selected = data.button;
|
||||
let service = selected == -1 ? null : SimpleServiceDiscovery.services[selected];
|
||||
if (aCallback)
|
||||
aCallback(service);
|
||||
});
|
||||
},
|
||||
|
||||
openExternal: function(aElement, aX, aY) {
|
||||
// Start a second screen media service
|
||||
let video = this.getVideo(aElement, aX, aY);
|
||||
if (!video) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.prompt(function(aService) {
|
||||
if (!aService)
|
||||
return;
|
||||
|
||||
// Make sure we have a player app for the given service
|
||||
let app = SimpleServiceDiscovery.findAppForService(aService, "video-sharing");
|
||||
if (!app)
|
||||
return;
|
||||
|
||||
video.title = aElement.ownerDocument.defaultView.top.document.title;
|
||||
if (video.element) {
|
||||
// If the video is currently playing on the device, pause it
|
||||
if (!video.element.paused) {
|
||||
video.element.pause();
|
||||
}
|
||||
}
|
||||
|
||||
app.stop(function() {
|
||||
app.start("", function() {
|
||||
app.remoteMedia(function(aRemoteMedia) {
|
||||
this.session = {
|
||||
service: aService,
|
||||
app: app,
|
||||
remoteMedia: aRemoteMedia,
|
||||
data: {
|
||||
title: video.title,
|
||||
source: video.source,
|
||||
poster: video.poster
|
||||
}
|
||||
};
|
||||
}.bind(this), this);
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// RemoteMedia callback API methods
|
||||
onRemoteMediaStart: function(aRemoteMedia) {
|
||||
if (!this.session) {
|
||||
return;
|
||||
}
|
||||
|
||||
aRemoteMedia.load(this.session.data);
|
||||
},
|
||||
|
||||
onRemoteMediaStop: function(aRemoteMedia) {
|
||||
},
|
||||
|
||||
onRemoteMediaStatus: function(aRemoteMedia) {
|
||||
if (!this.session) {
|
||||
return;
|
||||
}
|
||||
|
||||
let status = aRemoteMedia.status;
|
||||
if (status == "completed") {
|
||||
aRemoteMedia.shutdown();
|
||||
this.session.app.stop();
|
||||
delete this.session;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -85,6 +85,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebappManager",
|
||||
["PluginHelper", "chrome://browser/content/PluginHelper.js"],
|
||||
["OfflineApps", "chrome://browser/content/OfflineApps.js"],
|
||||
["Linkifier", "chrome://browser/content/Linkify.js"],
|
||||
["CastingApps", "chrome://browser/content/CastingApps.js"],
|
||||
].forEach(function (aScript) {
|
||||
let [name, script] = aScript;
|
||||
XPCOMUtils.defineLazyGetter(window, name, function() {
|
||||
@ -389,7 +390,7 @@ var BrowserApp = {
|
||||
Reader.init();
|
||||
UserAgentOverrides.init();
|
||||
DesktopUserAgent.init();
|
||||
CastApps.init();
|
||||
CastingApps.init();
|
||||
Distribution.init();
|
||||
Tabs.init();
|
||||
#ifdef ACCESSIBILITY
|
||||
@ -731,7 +732,7 @@ var BrowserApp = {
|
||||
UserAgentOverrides.uninit();
|
||||
DesktopUserAgent.uninit();
|
||||
ExternalApps.uninit();
|
||||
CastApps.uninit();
|
||||
CastingApps.uninit();
|
||||
Distribution.uninit();
|
||||
Tabs.uninit();
|
||||
},
|
||||
@ -8102,233 +8103,6 @@ var ExternalApps = {
|
||||
},
|
||||
};
|
||||
|
||||
var CastApps = {
|
||||
_castMenuId: -1,
|
||||
|
||||
init: function ca_init() {
|
||||
// Search for devices continuously every 120 seconds
|
||||
SimpleServiceDiscovery.search(120000);
|
||||
|
||||
this._castMenuId = NativeWindow.contextmenus.add(
|
||||
Strings.browser.GetStringFromName("contextmenu.castToScreen"),
|
||||
this.filterCast,
|
||||
this.openExternal.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
uninit: function ca_uninit() {
|
||||
NativeWindow.contextmenus.remove(this._castMenuId);
|
||||
},
|
||||
|
||||
makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) {
|
||||
return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
|
||||
},
|
||||
|
||||
getVideo: function(aElement, aX, aY) {
|
||||
dump("XXX start looking for a video")
|
||||
// Fast path: Is the given element a video element
|
||||
let video = this._getVideo(aElement);
|
||||
if (video) {
|
||||
return video;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// 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) {
|
||||
video = this._getVideo(element);
|
||||
if (video) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
// Could be null
|
||||
return video;
|
||||
},
|
||||
|
||||
_getVideo: function(aElement) {
|
||||
dump("XXX getvideo for: " + aElement.localName);
|
||||
// Given the hardware support for H264, let's only look for 'mp4' sources
|
||||
if (!aElement instanceof HTMLVideoElement) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function allowableExtension(aURI) {
|
||||
if (aURI && aURI instanceof Ci.nsIURL) {
|
||||
return (aURI.fileExtension == "mp4");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
dump("XXX got video src: " + sourceURL);
|
||||
if (sourceURL) {
|
||||
// Use the file extension to guess the mime type
|
||||
let sourceURI = this.makeURI(sourceURL, null, this.makeURI(aElement.baseURI));
|
||||
if (allowableExtension(sourceURI)) {
|
||||
dump("XXX: found source via ext: " + sourceURI.spec)
|
||||
return { video: aElement, source: sourceURI.spec, poster: posterURL };
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
if (sourceNode.type == "video/mp4") {
|
||||
dump("XXX: found source via mime: " + sourceURI.spec)
|
||||
return { video: aElement, source: sourceURI.spec, poster: posterURL };
|
||||
}
|
||||
|
||||
// Fallback to using the file extension to guess the mime type
|
||||
if (allowableExtension(sourceURI)) {
|
||||
dump("XXX: found source via ext: " + sourceURI.spec)
|
||||
return { video: aElement, source: sourceURI.spec, poster: posterURL };
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
filterCast: {
|
||||
matches: function(aElement, aX, aY) {
|
||||
dump("XXX filterCast")
|
||||
if (SimpleServiceDiscovery.services.length == 0)
|
||||
return false;
|
||||
let video = CastApps.getVideo(aElement, aX, aY);
|
||||
if (CastApps.session) {
|
||||
return (video && CastApps.session.data.source != video.source);
|
||||
}
|
||||
return (video != null);
|
||||
}
|
||||
},
|
||||
|
||||
prompt: function(aCallback) {
|
||||
let items = [];
|
||||
SimpleServiceDiscovery.services.forEach(function(aService) {
|
||||
let item = {
|
||||
label: aService.friendlyName,
|
||||
selected: false
|
||||
};
|
||||
items.push(item);
|
||||
});
|
||||
|
||||
let prompt = new Prompt({
|
||||
title: Strings.browser.GetStringFromName("casting.prompt")
|
||||
}).setSingleChoiceItems(items).show(function(data) {
|
||||
let selected = data.button;
|
||||
let service = selected == -1 ? null : SimpleServiceDiscovery.services[selected];
|
||||
if (aCallback)
|
||||
aCallback(service);
|
||||
});
|
||||
},
|
||||
|
||||
openExternal: function(aElement, aX, aY) {
|
||||
// Start a second screen media service
|
||||
let video = this.getVideo(aElement, aX, aY);
|
||||
if (video) {
|
||||
this.prompt(function(aService) {
|
||||
if (!aService)
|
||||
return;
|
||||
|
||||
// Make sure we have a player app for the given service
|
||||
let app = SimpleServiceDiscovery.findAppForService(aService, "video-sharing");
|
||||
if (!app)
|
||||
return;
|
||||
|
||||
video.title = aElement.ownerDocument.defaultView.top.document.title;
|
||||
if (video.element) {
|
||||
// If the video is currently playing on the device, pause it
|
||||
if (!video.element.paused) {
|
||||
video.element.pause();
|
||||
}
|
||||
/*
|
||||
// Create a poster
|
||||
if (!video.poster || video.poster.length == 0) {
|
||||
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
canvas.width = 140;
|
||||
canvas.height = 94;
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(video.element, 0, 0);
|
||||
video.poster = canvas.toDataURL("image/jpeg", "");
|
||||
|
||||
// Free the memory
|
||||
canvas.width = 0;
|
||||
canvas.height = 0;
|
||||
canvas = null;
|
||||
}
|
||||
*/
|
||||
}
|
||||
dump("XXX: " + video.poster);
|
||||
|
||||
app.stop(function() {
|
||||
app.start("", function() {
|
||||
app.remoteMedia(function(aRemoteMedia) {
|
||||
dump("XXX starting remote media")
|
||||
this.session = {
|
||||
service: aService,
|
||||
app: app,
|
||||
data: {
|
||||
title: video.title,
|
||||
source: video.source,
|
||||
poster: video.poster
|
||||
}
|
||||
};
|
||||
}.bind(this), this);
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
// RemoteMedia callback API methods
|
||||
onRemoteMediaStart: function(aRemoteMedia) {
|
||||
dump("XXX remote media ready")
|
||||
if (this.session) {
|
||||
dump("XXX remote media load");
|
||||
aRemoteMedia.load(this.session.data);
|
||||
}
|
||||
},
|
||||
|
||||
onRemoteMediaStop: function(aRemoteMedia) {
|
||||
dump("XXX remote media shutting down")
|
||||
},
|
||||
|
||||
onRemoteMediaStatus: function(aRemoteMedia) {
|
||||
dump("XXX remote media status")
|
||||
if (this.session) {
|
||||
let status = aRemoteMedia.status;
|
||||
if (status == "completed") {
|
||||
dump("XXX got a completed");
|
||||
aRemoteMedia.shutdown();
|
||||
this.session.app.stop();
|
||||
delete this.session;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var Distribution = {
|
||||
// File used to store campaign data
|
||||
_file: null,
|
||||
|
@ -53,6 +53,7 @@ chrome.jar:
|
||||
content/FeedHandler.js (content/FeedHandler.js)
|
||||
content/Feedback.js (content/Feedback.js)
|
||||
content/Linkify.js (content/Linkify.js)
|
||||
content/CastingApps.js (content/CastingApps.js)
|
||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||
content/aboutHealthReport.xhtml (content/aboutHealthReport.xhtml)
|
||||
* content/aboutHealthReport.js (content/aboutHealthReport.js)
|
||||
|
Loading…
Reference in New Issue
Block a user