Backout aa955afe7507:49f1c6fa27f8 (bug 953381) for robocop orange on this CLOSED TREE
2
CLOBBER
|
@ -22,4 +22,4 @@
|
||||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||||
# don't change CLOBBER for WebIDL changes any more.
|
# don't change CLOBBER for WebIDL changes any more.
|
||||||
|
|
||||||
Bug 953381 requires a clobber due to bug 961339.
|
Bug 917896 requires a clobber due to bug 961339.
|
||||||
|
|
|
@ -274,9 +274,6 @@ pref("browser.search.noCurrentEngine", true);
|
||||||
pref("browser.search.official", true);
|
pref("browser.search.official", true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Control media casting feature
|
|
||||||
pref("browser.casting.enabled", false);
|
|
||||||
|
|
||||||
// Enable sparse localization by setting a few package locale overrides
|
// Enable sparse localization by setting a few package locale overrides
|
||||||
pref("chrome.override_package.global", "browser");
|
pref("chrome.override_package.global", "browser");
|
||||||
pref("chrome.override_package.mozapps", "browser");
|
pref("chrome.override_package.mozapps", "browser");
|
||||||
|
|
|
@ -156,7 +156,6 @@ abstract public class BrowserApp extends GeckoApp
|
||||||
};
|
};
|
||||||
|
|
||||||
private FindInPageBar mFindInPageBar;
|
private FindInPageBar mFindInPageBar;
|
||||||
private MediaCastingBar mMediaCastingBar;
|
|
||||||
|
|
||||||
private boolean mAccessibilityEnabled = false;
|
private boolean mAccessibilityEnabled = false;
|
||||||
|
|
||||||
|
@ -531,7 +530,6 @@ abstract public class BrowserApp extends GeckoApp
|
||||||
}
|
}
|
||||||
|
|
||||||
mFindInPageBar = (FindInPageBar) findViewById(R.id.find_in_page);
|
mFindInPageBar = (FindInPageBar) findViewById(R.id.find_in_page);
|
||||||
mMediaCastingBar = (MediaCastingBar) findViewById(R.id.media_casting);
|
|
||||||
|
|
||||||
registerEventListener("CharEncoding:Data");
|
registerEventListener("CharEncoding:Data");
|
||||||
registerEventListener("CharEncoding:State");
|
registerEventListener("CharEncoding:State");
|
||||||
|
@ -832,11 +830,6 @@ abstract public class BrowserApp extends GeckoApp
|
||||||
mFindInPageBar = null;
|
mFindInPageBar = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mMediaCastingBar != null) {
|
|
||||||
mMediaCastingBar.onDestroy();
|
|
||||||
mMediaCastingBar = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mSharedPreferencesHelper != null) {
|
if (mSharedPreferencesHelper != null) {
|
||||||
mSharedPreferencesHelper.uninit();
|
mSharedPreferencesHelper.uninit();
|
||||||
mSharedPreferencesHelper = null;
|
mSharedPreferencesHelper = null;
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
package org.mozilla.gecko;
|
|
||||||
|
|
||||||
import org.mozilla.gecko.util.GeckoEventListener;
|
|
||||||
import org.mozilla.gecko.util.ThreadUtils;
|
|
||||||
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
public class MediaCastingBar extends RelativeLayout implements View.OnClickListener, GeckoEventListener {
|
|
||||||
private static final String LOGTAG = "MediaCastingBar";
|
|
||||||
|
|
||||||
private TextView mCastingTo;
|
|
||||||
private ImageButton mMediaPlay;
|
|
||||||
private ImageButton mMediaPause;
|
|
||||||
private ImageButton mMediaStop;
|
|
||||||
|
|
||||||
private boolean mInflated = false;
|
|
||||||
|
|
||||||
public MediaCastingBar(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
|
|
||||||
GeckoAppShell.getEventDispatcher().registerEventListener("Casting:Started", this);
|
|
||||||
GeckoAppShell.getEventDispatcher().registerEventListener("Casting:Stopped", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void inflateContent() {
|
|
||||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
|
||||||
View content = inflater.inflate(R.layout.media_casting, this);
|
|
||||||
|
|
||||||
mMediaPlay = (ImageButton) content.findViewById(R.id.media_play);
|
|
||||||
mMediaPlay.setOnClickListener(this);
|
|
||||||
mMediaPause = (ImageButton) content.findViewById(R.id.media_pause);
|
|
||||||
mMediaPause.setOnClickListener(this);
|
|
||||||
mMediaStop = (ImageButton) content.findViewById(R.id.media_stop);
|
|
||||||
mMediaStop.setOnClickListener(this);
|
|
||||||
|
|
||||||
mCastingTo = (TextView) content.findViewById(R.id.media_casting_to);
|
|
||||||
|
|
||||||
// Capture clicks on the rest of the view to prevent them from
|
|
||||||
// leaking into other views positioned below.
|
|
||||||
content.setOnClickListener(this);
|
|
||||||
|
|
||||||
mInflated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void show() {
|
|
||||||
if (!mInflated)
|
|
||||||
inflateContent();
|
|
||||||
|
|
||||||
setVisibility(VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void hide() {
|
|
||||||
setVisibility(GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onDestroy() {
|
|
||||||
GeckoAppShell.getEventDispatcher().unregisterEventListener("Casting:Started", this);
|
|
||||||
GeckoAppShell.getEventDispatcher().unregisterEventListener("Casting:Stopped", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// View.OnClickListener implementation
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
final int viewId = v.getId();
|
|
||||||
|
|
||||||
if (viewId == R.id.media_play) {
|
|
||||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Casting:Play", ""));
|
|
||||||
mMediaPlay.setVisibility(GONE);
|
|
||||||
mMediaPause.setVisibility(VISIBLE);
|
|
||||||
} else if (viewId == R.id.media_pause) {
|
|
||||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Casting:Pause", ""));
|
|
||||||
mMediaPause.setVisibility(GONE);
|
|
||||||
mMediaPlay.setVisibility(VISIBLE);
|
|
||||||
} else if (viewId == R.id.media_stop) {
|
|
||||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Casting:Stop", ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GeckoEventListener implementation
|
|
||||||
@Override
|
|
||||||
public void handleMessage(final String event, final JSONObject message) {
|
|
||||||
final String device = message.optString("device");
|
|
||||||
|
|
||||||
ThreadUtils.postToUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (event.equals("Casting:Started")) {
|
|
||||||
show();
|
|
||||||
if (!TextUtils.isEmpty(device)) {
|
|
||||||
mCastingTo.setText(device);
|
|
||||||
} else {
|
|
||||||
// Should not happen
|
|
||||||
mCastingTo.setText("");
|
|
||||||
Log.d(LOGTAG, "Device name is empty.");
|
|
||||||
}
|
|
||||||
mMediaPlay.setVisibility(GONE);
|
|
||||||
mMediaPause.setVisibility(VISIBLE);
|
|
||||||
} else if (event.equals("Casting:Stopped")) {
|
|
||||||
hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -219,13 +219,6 @@ size. -->
|
||||||
<!ENTITY find_next "Next">
|
<!ENTITY find_next "Next">
|
||||||
<!ENTITY find_close "Close">
|
<!ENTITY find_close "Close">
|
||||||
|
|
||||||
<!-- Localization note (media_casting_to, media_play, media_pause, media_stop) : These strings are used
|
|
||||||
as alternate text for accessibility. They are not visible in the UI. -->
|
|
||||||
<!ENTITY media_casting_to "Casting to Device">
|
|
||||||
<!ENTITY media_play "Play">
|
|
||||||
<!ENTITY media_pause "Pause">
|
|
||||||
<!ENTITY media_stop "Stop">
|
|
||||||
|
|
||||||
<!ENTITY contextmenu_open_new_tab "Open in New Tab">
|
<!ENTITY contextmenu_open_new_tab "Open in New Tab">
|
||||||
<!ENTITY contextmenu_open_private_tab "Open in Private Tab">
|
<!ENTITY contextmenu_open_private_tab "Open in Private Tab">
|
||||||
<!ENTITY contextmenu_open_in_reader "Open in Reader">
|
<!ENTITY contextmenu_open_in_reader "Open in Reader">
|
||||||
|
|
|
@ -248,7 +248,6 @@ gbjar.sources += [
|
||||||
'LightweightTheme.java',
|
'LightweightTheme.java',
|
||||||
'LightweightThemeDrawable.java',
|
'LightweightThemeDrawable.java',
|
||||||
'LocaleManager.java',
|
'LocaleManager.java',
|
||||||
'MediaCastingBar.java',
|
|
||||||
'MemoryMonitor.java',
|
'MemoryMonitor.java',
|
||||||
'menu/GeckoMenu.java',
|
'menu/GeckoMenu.java',
|
||||||
'menu/GeckoMenuInflater.java',
|
'menu/GeckoMenuInflater.java',
|
||||||
|
|
Двоичные данные
mobile/android/base/resources/drawable-hdpi/media_bar_pause.png
До Ширина: | Высота: | Размер: 362 B |
Двоичные данные
mobile/android/base/resources/drawable-hdpi/media_bar_play.png
До Ширина: | Высота: | Размер: 670 B |
Двоичные данные
mobile/android/base/resources/drawable-hdpi/media_bar_stop.png
До Ширина: | Высота: | Размер: 763 B |
Двоичные данные
mobile/android/base/resources/drawable-mdpi/media_bar_pause.png
До Ширина: | Высота: | Размер: 252 B |
Двоичные данные
mobile/android/base/resources/drawable-mdpi/media_bar_play.png
До Ширина: | Высота: | Размер: 463 B |
Двоичные данные
mobile/android/base/resources/drawable-mdpi/media_bar_stop.png
До Ширина: | Высота: | Размер: 497 B |
Двоичные данные
mobile/android/base/resources/drawable-xhdpi/media_bar_pause.png
До Ширина: | Высота: | Размер: 389 B |
Двоичные данные
mobile/android/base/resources/drawable-xhdpi/media_bar_play.png
До Ширина: | Высота: | Размер: 853 B |
Двоичные данные
mobile/android/base/resources/drawable-xhdpi/media_bar_stop.png
До Ширина: | Высота: | Размер: 1.2 KiB |
|
@ -48,13 +48,6 @@
|
||||||
style="@style/FindBar"
|
style="@style/FindBar"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<org.mozilla.gecko.MediaCastingBar android:id="@+id/media_casting"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentBottom="true"
|
|
||||||
style="@style/FindBar"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
<RelativeLayout android:id="@+id/camera_layout"
|
<RelativeLayout android:id="@+id/camera_layout"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<RelativeLayout android:id="@+id/media_controls"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerInParent="true">
|
|
||||||
|
|
||||||
<ImageButton android:id="@+id/media_play"
|
|
||||||
style="@style/FindBar.ImageButton"
|
|
||||||
android:contentDescription="@string/media_play"
|
|
||||||
android:src="@drawable/media_bar_play"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
<ImageButton android:id="@+id/media_pause"
|
|
||||||
style="@style/FindBar.ImageButton"
|
|
||||||
android:contentDescription="@string/media_pause"
|
|
||||||
android:src="@drawable/media_bar_pause"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/media_casting_to"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="5dip"
|
|
||||||
android:layout_marginRight="5dip"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_toLeftOf="@id/media_controls"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:textColor="#FFFFFFFF"
|
|
||||||
android:contentDescription="@string/media_casting_to"/>
|
|
||||||
|
|
||||||
<ImageButton android:id="@+id/media_stop"
|
|
||||||
style="@style/FindBar.ImageButton"
|
|
||||||
android:contentDescription="@string/media_stop"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:src="@drawable/media_bar_stop"/>
|
|
||||||
|
|
||||||
</merge>
|
|
|
@ -83,11 +83,6 @@
|
||||||
<string name="find_next">&find_next;</string>
|
<string name="find_next">&find_next;</string>
|
||||||
<string name="find_close">&find_close;</string>
|
<string name="find_close">&find_close;</string>
|
||||||
|
|
||||||
<string name="media_casting_to">&media_casting_to;</string>
|
|
||||||
<string name="media_play">&media_play;</string>
|
|
||||||
<string name="media_pause">&media_pause;</string>
|
|
||||||
<string name="media_stop">&media_stop;</string>
|
|
||||||
|
|
||||||
<string name="settings">&settings;</string>
|
<string name="settings">&settings;</string>
|
||||||
<string name="settings_title">&settings_title;</string>
|
<string name="settings_title">&settings_title;</string>
|
||||||
<string name="pref_category_advanced">&pref_category_advanced;</string>
|
<string name="pref_category_advanced">&pref_category_advanced;</string>
|
||||||
|
|
|
@ -79,7 +79,6 @@ skip-if = processor == "x86"
|
||||||
[testSharedPreferences]
|
[testSharedPreferences]
|
||||||
[testSimpleDiscovery]
|
[testSimpleDiscovery]
|
||||||
[testUITelemetry]
|
[testUITelemetry]
|
||||||
[testVideoDiscovery]
|
|
||||||
|
|
||||||
# Used for Talos, please don't use in mochitest
|
# Used for Talos, please don't use in mochitest
|
||||||
#[testPan]
|
#[testPan]
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.mozilla.gecko.tests;
|
|
||||||
|
|
||||||
import org.mozilla.gecko.*;
|
|
||||||
|
|
||||||
|
|
||||||
public class testVideoDiscovery extends JavascriptTest {
|
|
||||||
public testVideoDiscovery() {
|
|
||||||
super("testVideoDiscovery.js");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
// -*- Mode: js2; 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";
|
|
||||||
|
|
||||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
|
|
||||||
function ok(passed, text) {
|
|
||||||
do_report_result(passed, text, Components.stack.caller, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The chrome window
|
|
||||||
let chromeWin;
|
|
||||||
|
|
||||||
// Track the <browser> where the tests are happening
|
|
||||||
let browser;
|
|
||||||
|
|
||||||
function middle(element) {
|
|
||||||
let rect = element.getBoundingClientRect();
|
|
||||||
let x = (rect.right - rect.left) / 2 + rect.left;
|
|
||||||
let y = (rect.bottom - rect.top) / 2 + rect.top;
|
|
||||||
return [x, y];
|
|
||||||
}
|
|
||||||
|
|
||||||
add_test(function setup_browser() {
|
|
||||||
chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
|
|
||||||
let BrowserApp = chromeWin.BrowserApp;
|
|
||||||
|
|
||||||
do_register_cleanup(function cleanup() {
|
|
||||||
BrowserApp.closeTab(BrowserApp.getTabForBrowser(browser));
|
|
||||||
});
|
|
||||||
|
|
||||||
let url = "http://mochi.test:8888/tests/robocop/video_discovery.html";
|
|
||||||
browser = BrowserApp.addTab(url, { selected: true, parentId: BrowserApp.selectedTab.id }).browser;
|
|
||||||
browser.addEventListener("load", function startTests(event) {
|
|
||||||
browser.removeEventListener("load", startTests, true);
|
|
||||||
Services.tm.mainThread.dispatch(run_next_test, Ci.nsIThread.DISPATCH_NORMAL);
|
|
||||||
}, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
let videoDiscoveryTests = [
|
|
||||||
{ id: "simple-mp4", source: "http://mochi.test:8888/simple.mp4", poster: "http://mochi.test:8888/simple.png", text: "simple video with mp4 src" },
|
|
||||||
{ id: "simple-fail", pass: false, text: "simple video with no mp4 src" },
|
|
||||||
{ id: "with-sources-mp4", source: "http://mochi.test:8888/simple.mp4", text: "video with mp4 extension source child" },
|
|
||||||
{ id: "with-sources-fail", pass: false, text: "video with no mp4 extension source child" },
|
|
||||||
{ id: "with-sources-mimetype", source: "http://mochi.test:8888/simple-video-mp4", text: "video with mp4 mimetype source child" },
|
|
||||||
{ id: "video-overlay", source: "http://mochi.test:8888/simple.mp4", text: "div overlay covering a simple video with mp4 src" }
|
|
||||||
];
|
|
||||||
|
|
||||||
function execute_video_test(test) {
|
|
||||||
let element = browser.contentDocument.getElementById(test.id);
|
|
||||||
if (element) {
|
|
||||||
let [x, y] = middle(element);
|
|
||||||
let video = chromeWin.CastApps.getVideo(element, x, y);
|
|
||||||
if (video) {
|
|
||||||
let matchPoster = (test.poster == video.poster);
|
|
||||||
let matchSource = (test.source == video.source);
|
|
||||||
ok(matchPoster && matchSource && test.pass, test.text);
|
|
||||||
} else {
|
|
||||||
ok(!test.pass, test.text);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ok(false, "test element not found: [" + test.id + "]");
|
|
||||||
}
|
|
||||||
run_next_test();
|
|
||||||
}
|
|
||||||
|
|
||||||
let videoTest;
|
|
||||||
while ((videoTest = videoDiscoveryTests.shift())) {
|
|
||||||
if (!("poster" in videoTest)) {
|
|
||||||
videoTest.poster = "";
|
|
||||||
}
|
|
||||||
if (!("pass" in videoTest)) {
|
|
||||||
videoTest.pass = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_test(execute_video_test.bind(this, videoTest));
|
|
||||||
}
|
|
||||||
|
|
||||||
run_next_test();
|
|
|
@ -1,60 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Video Discovery Test</title>
|
|
||||||
<style type="text/css">
|
|
||||||
#video-box {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
#video-overlay, #video-player {
|
|
||||||
width: 640px;
|
|
||||||
min-height: 370px;
|
|
||||||
}
|
|
||||||
#video-overlay {
|
|
||||||
position: absolute;
|
|
||||||
float: left;
|
|
||||||
background-color:#f00;
|
|
||||||
z-index:10;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- PASS: src uses a mp4 extension -->
|
|
||||||
<video id="simple-mp4" poster="/simple.png" src="/simple.mp4"></video>
|
|
||||||
|
|
||||||
<!-- FAIL: src uses a ogg extension -->
|
|
||||||
<video id="simple-fail" src="/simple.ogg"></video>
|
|
||||||
|
|
||||||
<!-- PASS: source list uses a mp4 extension -->
|
|
||||||
<video id="with-sources-mp4">
|
|
||||||
<source src="/simple.ogg">
|
|
||||||
<source src="/simple.mp4">
|
|
||||||
</video>
|
|
||||||
|
|
||||||
<!-- FAIL: source list uses a mp4 extension -->
|
|
||||||
<video id="with-sources-fail">
|
|
||||||
<source src="/simple.ogg">
|
|
||||||
<source src="/simple.webm">
|
|
||||||
</video>
|
|
||||||
|
|
||||||
<!-- PASS: source list uses a mp4 mimetype -->
|
|
||||||
<video id="with-sources-mimetype">
|
|
||||||
<source src="/simple-video-ogg" type="video/ogg">
|
|
||||||
<source src="/simple-video-mp4" type="video/mp4">
|
|
||||||
</video>
|
|
||||||
|
|
||||||
<!-- PASS: source list uses a mp4 mimetype and extra data -->
|
|
||||||
<video id="with-sources-mimetype-plus">
|
|
||||||
<source src="/simple-video-ogg" type="video/ogg">
|
|
||||||
<source src="/simple-video-mp4" type="video/mp4; codecs='avc1.42E01E, mp4a.40.2'">
|
|
||||||
</video>
|
|
||||||
|
|
||||||
<!-- PASS: div overlay covers a video with mp4 src -->
|
|
||||||
<div id="video-box">
|
|
||||||
<div id="video-overlay"></div>
|
|
||||||
<div>
|
|
||||||
<video id="video-player" src="/simple.mp4"></video>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,243 +0,0 @@
|
||||||
/* 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)
|
|
||||||
);
|
|
||||||
|
|
||||||
Services.obs.addObserver(this, "Casting:Play", false);
|
|
||||||
Services.obs.addObserver(this, "Casting:Pause", false);
|
|
||||||
Services.obs.addObserver(this, "Casting:Stop", false);
|
|
||||||
},
|
|
||||||
|
|
||||||
uninit: function ca_uninit() {
|
|
||||||
Services.obs.removeObserver(this, "Casting:Play");
|
|
||||||
Services.obs.removeObserver(this, "Casting:Pause");
|
|
||||||
Services.obs.removeObserver(this, "Casting:Stop");
|
|
||||||
|
|
||||||
NativeWindow.contextmenus.remove(this._castMenuId);
|
|
||||||
},
|
|
||||||
|
|
||||||
isEnabled: function isEnabled() {
|
|
||||||
return Services.prefs.getBoolPref("browser.casting.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;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
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);
|
|
||||||
sendMessageToJava({ type: "Casting:Started", device: this.session.service.friendlyName });
|
|
||||||
},
|
|
||||||
|
|
||||||
onRemoteMediaStop: function(aRemoteMedia) {
|
|
||||||
sendMessageToJava({ type: "Casting:Stopped" });
|
|
||||||
},
|
|
||||||
|
|
||||||
onRemoteMediaStatus: function(aRemoteMedia) {
|
|
||||||
if (!this.session) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let status = aRemoteMedia.status;
|
|
||||||
if (status == "completed") {
|
|
||||||
aRemoteMedia.shutdown();
|
|
||||||
this.session.app.stop();
|
|
||||||
delete this.session;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -63,9 +63,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||||
"@mozilla.org/uuid-generator;1",
|
"@mozilla.org/uuid-generator;1",
|
||||||
"nsIUUIDGenerator");
|
"nsIUUIDGenerator");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "SimpleServiceDiscovery",
|
|
||||||
"resource://gre/modules/SimpleServiceDiscovery.jsm");
|
|
||||||
|
|
||||||
#ifdef NIGHTLY_BUILD
|
#ifdef NIGHTLY_BUILD
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils",
|
XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils",
|
||||||
"resource://shumway/ShumwayUtils.jsm");
|
"resource://shumway/ShumwayUtils.jsm");
|
||||||
|
@ -85,7 +82,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebappManager",
|
||||||
["PluginHelper", "chrome://browser/content/PluginHelper.js"],
|
["PluginHelper", "chrome://browser/content/PluginHelper.js"],
|
||||||
["OfflineApps", "chrome://browser/content/OfflineApps.js"],
|
["OfflineApps", "chrome://browser/content/OfflineApps.js"],
|
||||||
["Linkifier", "chrome://browser/content/Linkify.js"],
|
["Linkifier", "chrome://browser/content/Linkify.js"],
|
||||||
["CastingApps", "chrome://browser/content/CastingApps.js"],
|
|
||||||
].forEach(function (aScript) {
|
].forEach(function (aScript) {
|
||||||
let [name, script] = aScript;
|
let [name, script] = aScript;
|
||||||
XPCOMUtils.defineLazyGetter(window, name, function() {
|
XPCOMUtils.defineLazyGetter(window, name, function() {
|
||||||
|
@ -390,7 +386,6 @@ var BrowserApp = {
|
||||||
Reader.init();
|
Reader.init();
|
||||||
UserAgentOverrides.init();
|
UserAgentOverrides.init();
|
||||||
DesktopUserAgent.init();
|
DesktopUserAgent.init();
|
||||||
CastingApps.init();
|
|
||||||
Distribution.init();
|
Distribution.init();
|
||||||
Tabs.init();
|
Tabs.init();
|
||||||
#ifdef ACCESSIBILITY
|
#ifdef ACCESSIBILITY
|
||||||
|
@ -732,7 +727,6 @@ var BrowserApp = {
|
||||||
UserAgentOverrides.uninit();
|
UserAgentOverrides.uninit();
|
||||||
DesktopUserAgent.uninit();
|
DesktopUserAgent.uninit();
|
||||||
ExternalApps.uninit();
|
ExternalApps.uninit();
|
||||||
CastingApps.uninit();
|
|
||||||
Distribution.uninit();
|
Distribution.uninit();
|
||||||
Tabs.uninit();
|
Tabs.uninit();
|
||||||
},
|
},
|
||||||
|
|
|
@ -53,7 +53,6 @@ chrome.jar:
|
||||||
content/FeedHandler.js (content/FeedHandler.js)
|
content/FeedHandler.js (content/FeedHandler.js)
|
||||||
content/Feedback.js (content/Feedback.js)
|
content/Feedback.js (content/Feedback.js)
|
||||||
content/Linkify.js (content/Linkify.js)
|
content/Linkify.js (content/Linkify.js)
|
||||||
content/CastingApps.js (content/CastingApps.js)
|
|
||||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||||
content/aboutHealthReport.xhtml (content/aboutHealthReport.xhtml)
|
content/aboutHealthReport.xhtml (content/aboutHealthReport.xhtml)
|
||||||
* content/aboutHealthReport.js (content/aboutHealthReport.js)
|
* content/aboutHealthReport.js (content/aboutHealthReport.js)
|
||||||
|
|
|
@ -159,9 +159,6 @@ intl.charsetmenu.browser.static=iso-8859-1,utf-8,big5,iso-2022-jp,shift_jis,euc-
|
||||||
# Text Selection
|
# Text Selection
|
||||||
selectionHelper.textCopied=Text copied to clipboard
|
selectionHelper.textCopied=Text copied to clipboard
|
||||||
|
|
||||||
# Casting
|
|
||||||
casting.prompt=Cast to Device
|
|
||||||
|
|
||||||
# Context menu
|
# Context menu
|
||||||
contextmenu.openInNewTab=Open Link in New Tab
|
contextmenu.openInNewTab=Open Link in New Tab
|
||||||
contextmenu.openInPrivateTab=Open Link in Private Tab
|
contextmenu.openInPrivateTab=Open Link in Private Tab
|
||||||
|
@ -194,7 +191,6 @@ contextmenu.unmute=Unmute
|
||||||
contextmenu.saveVideo=Save Video
|
contextmenu.saveVideo=Save Video
|
||||||
contextmenu.saveAudio=Save Audio
|
contextmenu.saveAudio=Save Audio
|
||||||
contextmenu.addToContacts=Add to Contacts
|
contextmenu.addToContacts=Add to Contacts
|
||||||
contextmenu.castToScreen=Cast to Screen
|
|
||||||
|
|
||||||
contextmenu.copy=Copy
|
contextmenu.copy=Copy
|
||||||
contextmenu.cut=Cut
|
contextmenu.cut=Cut
|
||||||
|
|