зеркало из https://github.com/mozilla/gecko-dev.git
merge autoland to mozilla-central a=merge
This commit is contained in:
Коммит
6e3860b156
|
@ -57,6 +57,7 @@ b2g/locales/en-US/b2g-l10n.js
|
|||
|
||||
# browser/ exclusions
|
||||
browser/app/**
|
||||
browser/branding/**/firefox-branding.js
|
||||
browser/base/content/browser-social.js
|
||||
browser/base/content/nsContextMenu.js
|
||||
browser/base/content/sanitizeDialog.js
|
||||
|
|
|
@ -3,6 +3,8 @@ skip-if = buildapp == "mulet"
|
|||
tags = usercontextid firstpartyisolation originattributes
|
||||
support-files =
|
||||
dummy.html
|
||||
file_broadcastChannel.html
|
||||
file_broadcastChanneliFrame.html
|
||||
file_favicon.html
|
||||
file_favicon.png
|
||||
file_favicon.png^headers^
|
||||
|
@ -30,6 +32,7 @@ support-files =
|
|||
worker_blobify.js
|
||||
worker_deblobify.js
|
||||
|
||||
[browser_broadcastChannel.js]
|
||||
[browser_favicon_firstParty.js]
|
||||
[browser_favicon_userContextId.js]
|
||||
[browser_firstPartyIsolation.js]
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Bug 1264571 - A test case of broadcast channels for first party isolation.
|
||||
*/
|
||||
|
||||
const TEST_DOMAIN = "http://example.net/";
|
||||
const TEST_PATH = TEST_DOMAIN + "browser/browser/components/originattributes/test/browser/";
|
||||
const TEST_PAGE = TEST_PATH + "file_broadcastChannel.html";
|
||||
|
||||
function* doTest(aBrowser) {
|
||||
let response = yield ContentTask.spawn(aBrowser, null, function* () {
|
||||
|
||||
let displayItem = content.document.getElementById("display");
|
||||
|
||||
// If there is nothing in the 'display', we will try to send a message to
|
||||
// the broadcast channel and wait until this message has been delivered.
|
||||
// The way that how we make sure the message is delivered is based on an
|
||||
// iframe which will reply everything it receives from the broadcast channel
|
||||
// to the current window through the postMessage. So, we can know that the
|
||||
// boradcast message is sent successfully when the window receives a message
|
||||
// from the iframe.
|
||||
if (displayItem.innerHTML === "") {
|
||||
let data = Math.random().toString();
|
||||
|
||||
let receivedData = yield new Promise(resolve => {
|
||||
let listenFunc = event => {
|
||||
content.removeEventListener("message", listenFunc);
|
||||
resolve(event.data);
|
||||
};
|
||||
|
||||
let bc = new content.BroadcastChannel("testBroadcastChannel");
|
||||
|
||||
content.addEventListener("message", listenFunc, false);
|
||||
bc.postMessage(data);
|
||||
});
|
||||
|
||||
is(receivedData, data, "The value should be the same.");
|
||||
|
||||
return receivedData;
|
||||
}
|
||||
|
||||
return displayItem.innerHTML;
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
IsolationTestTools.runTests(TEST_PAGE, doTest);
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<title>Page broadcast channel creator for first party isolation</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="display" style="white-space:pre; font-family:monospace; display:inline;"></div>
|
||||
<iframe id="iframe" src="file_broadcastChanneliFrame.html"></iframe>>
|
||||
<script type="text/javascript;version=1.7">
|
||||
let bc = new BroadcastChannel("testBroadcastChannel");
|
||||
bc.onmessage = function (e) {
|
||||
document.getElementById("display").innerHTML = e.data;
|
||||
};
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<title>Page broadcast channel responder for first party isolation</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="display" style="white-space:pre; font-family:monospace; display:inline;"></div>
|
||||
<script type="text/javascript;version=1.7">
|
||||
let bc = new BroadcastChannel("testBroadcastChannel");
|
||||
bc.onmessage = function (e) {
|
||||
window.parent.postMessage(e.data, "*");
|
||||
};
|
||||
</script>
|
||||
</body>
|
|
@ -107,6 +107,8 @@ module.exports = createClass({
|
|||
// Update the viewport store with the new width and height.
|
||||
this.props.onResizeViewport(width, height);
|
||||
// Change the device selector back to an unselected device
|
||||
// TODO: Bug 1313140: We should avoid calling this for every resize event, since it
|
||||
// triggers RDP calls each time.
|
||||
this.props.onChangeViewportDevice({ name: "" });
|
||||
|
||||
this.setState({
|
||||
|
|
|
@ -471,12 +471,14 @@ ResponsiveUI.prototype = {
|
|||
this.emit("network-throttling-changed");
|
||||
}),
|
||||
|
||||
onChangeViewportDevice(event) {
|
||||
onChangeViewportDevice: Task.async(function* (event) {
|
||||
let { userAgent, pixelRatio, touch } = event.data.device;
|
||||
this.updateUserAgent(userAgent);
|
||||
this.updateDPPX(pixelRatio);
|
||||
this.updateTouchSimulation(touch);
|
||||
},
|
||||
yield this.updateUserAgent(userAgent);
|
||||
yield this.updateDPPX(pixelRatio);
|
||||
yield this.updateTouchSimulation(touch);
|
||||
// Used by tests
|
||||
this.emit("viewport-device-changed");
|
||||
}),
|
||||
|
||||
onContentResize(event) {
|
||||
let { width, height } = event.data;
|
||||
|
@ -496,13 +498,13 @@ ResponsiveUI.prototype = {
|
|||
this.updateTouchSimulation(enabled);
|
||||
},
|
||||
|
||||
updateDPPX(dppx) {
|
||||
updateDPPX: Task.async(function* (dppx) {
|
||||
if (!dppx) {
|
||||
this.emulationFront.clearDPPXOverride();
|
||||
yield this.emulationFront.clearDPPXOverride();
|
||||
return;
|
||||
}
|
||||
this.emulationFront.setDPPXOverride(dppx);
|
||||
},
|
||||
yield this.emulationFront.setDPPXOverride(dppx);
|
||||
}),
|
||||
|
||||
updateNetworkThrottling: Task.async(function* (enabled, profile) {
|
||||
if (!enabled) {
|
||||
|
@ -518,13 +520,13 @@ ResponsiveUI.prototype = {
|
|||
});
|
||||
}),
|
||||
|
||||
updateUserAgent(userAgent) {
|
||||
updateUserAgent: Task.async(function* (userAgent) {
|
||||
if (!userAgent) {
|
||||
this.emulationFront.clearUserAgentOverride();
|
||||
yield this.emulationFront.clearUserAgentOverride();
|
||||
return;
|
||||
}
|
||||
this.emulationFront.setUserAgentOverride(userAgent);
|
||||
},
|
||||
yield this.emulationFront.setUserAgentOverride(userAgent);
|
||||
}),
|
||||
|
||||
updateTouchSimulation: Task.async(function* (enabled) {
|
||||
if (!enabled) {
|
||||
|
|
|
@ -50,8 +50,10 @@ addRDMTask(TEST_URL, function* ({ ui, manager }) {
|
|||
yield testTouchEventsOverride(ui, true);
|
||||
|
||||
// Test resetting device when resizing viewport
|
||||
let deviceChanged = once(ui, "viewport-device-changed");
|
||||
yield testViewportResize(ui, ".viewport-vertical-resize-handle",
|
||||
[-10, -10], [testDevice.width, testDevice.height - 10], [0, -10], ui);
|
||||
yield deviceChanged;
|
||||
yield testUserAgent(ui, DEFAULT_UA);
|
||||
yield testDevicePixelRatio(ui, DEFAULT_DPPX);
|
||||
yield testTouchEventsOverride(ui, false);
|
||||
|
|
|
@ -25,9 +25,7 @@ addRDMTask(TEST_URL, function* ({ ui, manager }) {
|
|||
yield testThrottlingProfile(ui, "Regular 3G");
|
||||
|
||||
// Test switching back to no throttling
|
||||
let changed = once(ui, "network-throttling-changed");
|
||||
yield switchNetworkThrottling(ui, "No throttling");
|
||||
yield changed;
|
||||
testNetworkThrottlingSelectorLabel(ui, "No throttling");
|
||||
yield testNetworkThrottlingState(ui, null);
|
||||
});
|
||||
|
@ -46,9 +44,7 @@ var testNetworkThrottlingState = Task.async(function* (ui, expected) {
|
|||
});
|
||||
|
||||
var testThrottlingProfile = Task.async(function* (ui, profile) {
|
||||
let changed = once(ui, "network-throttling-changed");
|
||||
yield switchNetworkThrottling(ui, profile);
|
||||
yield changed;
|
||||
testNetworkThrottlingSelectorLabel(ui, profile);
|
||||
let data = throttlingProfiles.find(({ id }) => id == profile);
|
||||
let { download, upload, latency } = data;
|
||||
|
|
|
@ -198,9 +198,11 @@ function* testViewportResize(ui, selector, moveBy,
|
|||
expectedViewportSize, expectedHandleMove) {
|
||||
let win = ui.toolWindow;
|
||||
|
||||
let changed = once(ui, "viewport-device-changed");
|
||||
let resized = waitForViewportResizeTo(ui, ...expectedViewportSize);
|
||||
let startRect = dragElementBy(selector, ...moveBy, win);
|
||||
yield resized;
|
||||
yield changed;
|
||||
|
||||
let endRect = getElRect(selector, win);
|
||||
is(endRect.left - startRect.left, expectedHandleMove[0],
|
||||
|
@ -256,13 +258,17 @@ function switchSelector({ toolWindow }, selector, value) {
|
|||
});
|
||||
}
|
||||
|
||||
function switchDevice(ui, value) {
|
||||
return switchSelector(ui, ".viewport-device-selector", value);
|
||||
}
|
||||
let switchDevice = Task.async(function* (ui, value) {
|
||||
let changed = once(ui, "viewport-device-changed");
|
||||
yield switchSelector(ui, ".viewport-device-selector", value);
|
||||
yield changed;
|
||||
});
|
||||
|
||||
function switchNetworkThrottling(ui, value) {
|
||||
return switchSelector(ui, "#global-network-throttling-selector", value);
|
||||
}
|
||||
let switchNetworkThrottling = Task.async(function* (ui, value) {
|
||||
let changed = once(ui, "network-throttling-changed");
|
||||
yield switchSelector(ui, "#global-network-throttling-selector", value);
|
||||
yield changed;
|
||||
});
|
||||
|
||||
function getSessionHistory(browser) {
|
||||
return ContentTask.spawn(browser, {}, function* () {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<script src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<style>
|
||||
@keyframes empty { }
|
||||
@keyframes animTransform {
|
||||
|
@ -28,7 +29,13 @@ function waitForDocLoad() {
|
|||
});
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
function waitForPaints() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
waitForAllPaintsFlushed(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
|
||||
|
@ -48,30 +55,26 @@ async_test(function(t) {
|
|||
// As a result, it's better to wait until we have a more stable state before
|
||||
// continuing.
|
||||
var promiseCallbackDone = false;
|
||||
waitForDocLoad().then(function() {
|
||||
return waitForDocLoad().then(function() {
|
||||
div.style.animation = 'empty 1000s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
return animation.ready.then(function() {
|
||||
promiseCallbackDone = true;
|
||||
}).catch(function() {
|
||||
assert_unreached('ready promise was rejected');
|
||||
});
|
||||
|
||||
// We need to wait for up to three frames. This is because in some
|
||||
// cases it can take up to two frames for the initial layout
|
||||
// to take place. Even after that happens we don't actually resolve the
|
||||
// ready promise until the following tick.
|
||||
})
|
||||
.then(waitForFrame)
|
||||
.then(waitForFrame)
|
||||
.then(waitForFrame)
|
||||
.then(t.step_func(function() {
|
||||
}).then(function() {
|
||||
// We need to wait for up to three frames. This is because in some
|
||||
// cases it can take up to two frames for the initial layout
|
||||
// to take place. Even after that happens we don't actually resolve the
|
||||
// ready promise until the following tick.
|
||||
return waitForAnimationFrames(3);
|
||||
}).then(function() {
|
||||
assert_true(promiseCallbackDone,
|
||||
'ready promise for an empty animation was resolved'
|
||||
+ ' within three animation frames');
|
||||
t.done();
|
||||
}));
|
||||
});
|
||||
}, 'Animation.ready is resolved for an empty animation');
|
||||
|
||||
// Test that compositor animations with delays get synced correctly
|
||||
|
@ -79,10 +82,9 @@ async_test(function(t) {
|
|||
// NOTE: It is important that we DON'T use
|
||||
// SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh here since that takes
|
||||
// us through a different code path.
|
||||
async_test(function(t) {
|
||||
promise_test(function(t) {
|
||||
// This test only applies to compositor animations
|
||||
if (!isOMTAEnabled()) {
|
||||
t.done();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -92,7 +94,7 @@ async_test(function(t) {
|
|||
div.style.animation = 'animTransform 100s -50s forwards';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
return waitForPaints(function() {
|
||||
var transformStr =
|
||||
SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
|
||||
|
||||
|
@ -111,8 +113,7 @@ async_test(function(t) {
|
|||
assert_true(matrixComponents[4] >= 50,
|
||||
'Animation is at least half-way through on the compositor'
|
||||
+ ' (got translation of ' + matrixComponents[4] + ')');
|
||||
t.done();
|
||||
}));
|
||||
});
|
||||
}, 'Starting an animation with a delay starts from the correct point');
|
||||
|
||||
done();
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsIRequest.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIXPConnect.h"
|
||||
|
@ -5945,6 +5947,7 @@ HTMLMediaElement::SetAudioChannelSuspended(SuspendTypes aSuspend)
|
|||
return;
|
||||
}
|
||||
|
||||
MaybeNotifyMediaResumed(aSuspend);
|
||||
mAudioChannelSuspended = aSuspend;
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("HTMLMediaElement, SetAudioChannelSuspended, this = %p, "
|
||||
|
@ -6427,6 +6430,37 @@ HTMLMediaElement::IsAudible() const
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::MaybeNotifyMediaResumed(SuspendTypes aSuspend)
|
||||
{
|
||||
// In fennec, we should send the notification when media is resumed from the
|
||||
// pause-disposable which was called by media control.
|
||||
if (mAudioChannelSuspended != nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE &&
|
||||
aSuspend != nsISuspendedTypes::NONE_SUSPENDED) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t windowID = mAudioChannelAgent->WindowID();
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction([windowID]() -> void {
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
services::GetObserverService();
|
||||
if (NS_WARN_IF(!observerService)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupportsPRUint64> wrapper =
|
||||
do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
|
||||
if (NS_WARN_IF(!wrapper)) {
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper->SetData(windowID);
|
||||
observerService->NotifyObservers(wrapper,
|
||||
"media-playback-resumed",
|
||||
u"active");
|
||||
}));
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLMediaElement::HaveFailedWithSourceNotSupportedError() const
|
||||
{
|
||||
|
|
|
@ -1268,6 +1268,10 @@ protected:
|
|||
|
||||
void OpenUnsupportedMediaWithExtenalAppIfNeeded();
|
||||
|
||||
// It's used for fennec only, send the notification when the user resumes the
|
||||
// media which was paused by media control.
|
||||
void MaybeNotifyMediaResumed(SuspendTypes aSuspend);
|
||||
|
||||
class nsAsyncEventRunner;
|
||||
using nsGenericHTMLElement::DispatchEvent;
|
||||
// For nsAsyncEventRunner.
|
||||
|
|
|
@ -477,26 +477,42 @@ MediaKeySession::Close(ErrorResult& aRv)
|
|||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!IsCallable()) {
|
||||
// If this object's callable value is false, return a promise rejected
|
||||
// with a new DOMException whose name is InvalidStateError.
|
||||
EME_LOG("MediaKeySession[%p,''] Close() called before sessionId set by CDM", this);
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("MediaKeySession.Close() called before sessionId set by CDM"));
|
||||
return promise.forget();
|
||||
}
|
||||
if (IsClosed() || !mKeys->GetCDMProxy()) {
|
||||
// 1. Let session be the associated MediaKeySession object.
|
||||
// 2. If session is closed, return a resolved promise.
|
||||
if (IsClosed()) {
|
||||
EME_LOG("MediaKeySession[%p,'%s'] Close() already closed",
|
||||
this, NS_ConvertUTF16toUTF8(mSessionId).get());
|
||||
promise->MaybeResolveWithUndefined();
|
||||
return promise.forget();
|
||||
}
|
||||
// 3. If session's callable value is false, return a promise rejected
|
||||
// with an InvalidStateError.
|
||||
if (!IsCallable()) {
|
||||
EME_LOG("MediaKeySession[%p,''] Close() called before sessionId set by CDM", this);
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("MediaKeySession.Close() called before sessionId set by CDM"));
|
||||
return promise.forget();
|
||||
}
|
||||
if (!mKeys->GetCDMProxy()) {
|
||||
EME_LOG("MediaKeySession[%p,'%s'] Close() null CDMProxy",
|
||||
this, NS_ConvertUTF16toUTF8(mSessionId).get());
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("MediaKeySession.Close() lost reference to CDM"));
|
||||
return promise.forget();
|
||||
}
|
||||
// 4. Let promise be a new promise.
|
||||
PromiseId pid = mKeys->StorePromise(promise);
|
||||
// 5. Run the following steps in parallel:
|
||||
// 5.1 Let cdm be the CDM instance represented by session's cdm instance value.
|
||||
// 5.2 Use cdm to close the session associated with session.
|
||||
mKeys->GetCDMProxy()->CloseSession(mSessionId, pid);
|
||||
|
||||
EME_LOG("MediaKeySession[%p,'%s'] Close() sent to CDM, promiseId=%d",
|
||||
this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
|
||||
|
||||
// Session Closed algorithm is run when CDM causes us to run OnSessionClosed().
|
||||
|
||||
// 6. Return promise.
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
|
|
|
@ -245,6 +245,7 @@ GMPExToNsresult(GMPDOMException aDomException) {
|
|||
case kGMPAbortError: return NS_ERROR_DOM_ABORT_ERR;
|
||||
case kGMPQuotaExceededError: return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
|
||||
case kGMPTimeoutError: return NS_ERROR_DOM_TIMEOUT_ERR;
|
||||
case kGMPTypeError: return NS_ERROR_DOM_TYPE_ERR;
|
||||
default: return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ struct GMPDomExceptionValidator {
|
|||
case kGMPAbortError:
|
||||
case kGMPQuotaExceededError:
|
||||
case kGMPTimeoutError:
|
||||
case kGMPTypeError:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
@ -79,7 +79,8 @@ enum GMPDOMException {
|
|||
kGMPSecurityError = 18,
|
||||
kGMPAbortError = 20,
|
||||
kGMPQuotaExceededError = 22,
|
||||
kGMPTimeoutError = 23
|
||||
kGMPTimeoutError = 23,
|
||||
kGMPTypeError = 52
|
||||
};
|
||||
|
||||
enum GMPSessionMessageType {
|
||||
|
|
|
@ -321,7 +321,12 @@ ToGMPDOMException(cdm::Error aError)
|
|||
switch (aError) {
|
||||
case kNotSupportedError: return kGMPNotSupportedError;
|
||||
case kInvalidStateError: return kGMPInvalidStateError;
|
||||
case kInvalidAccessError: return kGMPInvalidAccessError;
|
||||
case kInvalidAccessError:
|
||||
// Note: Chrome converts kInvalidAccessError to TypeError, since the
|
||||
// Chromium CDM API doesn't have a type error enum value. The EME spec
|
||||
// requires TypeError in some places, so we do the same conversion.
|
||||
// See bug 1313202.
|
||||
return kGMPTypeError;
|
||||
case kQuotaExceededError: return kGMPQuotaExceededError;
|
||||
case kUnknownError: return kGMPInvalidModificationError; // Note: Unique placeholder.
|
||||
case kClientError: return kGMPAbortError; // Note: Unique placeholder.
|
||||
|
|
|
@ -33,14 +33,10 @@
|
|||
#ifdef DEBUG
|
||||
#undef NOISY_INLINEDIR_ALIGN
|
||||
#undef NOISY_BLOCKDIR_ALIGN
|
||||
#undef REALLY_NOISY_BLOCKDIR_ALIGN
|
||||
#undef NOISY_REFLOW
|
||||
#undef REALLY_NOISY_REFLOW
|
||||
#undef NOISY_PUSHING
|
||||
#undef REALLY_NOISY_PUSHING
|
||||
#undef DEBUG_ADD_TEXT
|
||||
#undef NOISY_MAX_ELEMENT_SIZE
|
||||
#undef REALLY_NOISY_MAX_ELEMENT_SIZE
|
||||
#undef NOISY_CAN_PLACE_FRAME
|
||||
#undef NOISY_TRIM
|
||||
#undef REALLY_NOISY_TRIM
|
||||
|
@ -304,8 +300,8 @@ nsLineLayout::UpdateBand(WritingMode aWM,
|
|||
ContainerSize());
|
||||
#ifdef REALLY_NOISY_REFLOW
|
||||
printf("nsLL::UpdateBand %d, %d, %d, %d, (converted to %d, %d, %d, %d); frame=%p\n will set mImpacted to true\n",
|
||||
aNewAvailSpace.x, aNewAvailSpace.y,
|
||||
aNewAvailSpace.width, aNewAvailSpace.height,
|
||||
aNewAvailSpace.IStart(aWM), aNewAvailSpace.BStart(aWM),
|
||||
aNewAvailSpace.ISize(aWM), aNewAvailSpace.BSize(aWM),
|
||||
availSpace.IStart(lineWM), availSpace.BStart(lineWM),
|
||||
availSpace.ISize(lineWM), availSpace.BSize(lineWM),
|
||||
aFloatFrame);
|
||||
|
@ -1797,7 +1793,6 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
|||
spanFramePFD->mBounds.BSize(lineWM),
|
||||
emptyContinuation ? "yes" : "no");
|
||||
if (psd != mRootSpan) {
|
||||
WritingMode frameWM = spanFramePFD->mWritingMode;
|
||||
printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d",
|
||||
spanFramePFD->mBorderPadding.Top(lineWM),
|
||||
spanFramePFD->mBorderPadding.Right(lineWM),
|
||||
|
@ -2004,7 +1999,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
|||
uint8_t verticalAlignEnum = frame->VerticalAlignEnum();
|
||||
#ifdef NOISY_BLOCKDIR_ALIGN
|
||||
printf(" [frame]");
|
||||
nsFrame::ListTag(stdout, mFrame);
|
||||
nsFrame::ListTag(stdout, frame);
|
||||
printf(": verticalAlignUnit=%d (enum == %d",
|
||||
verticalAlign.GetUnit(),
|
||||
((eStyleUnit_Enumerated == verticalAlign.GetUnit())
|
||||
|
@ -2386,7 +2381,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
|||
#ifdef NOISY_BLOCKDIR_ALIGN
|
||||
printf(" [span]adjusting for zeroEffectiveSpanBox\n");
|
||||
printf(" Original: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
|
||||
minBCoord, maxBCoord, spanFramePFD->mBounds.BSize(frameWM),
|
||||
minBCoord, maxBCoord, spanFramePFD->mBounds.BSize(lineWM),
|
||||
spanFramePFD->mAscent,
|
||||
psd->mLogicalBSize, psd->mBStartLeading, psd->mBEndLeading);
|
||||
#endif
|
||||
|
|
|
@ -301,8 +301,8 @@ ClearKeySessionManager::CloseSession(uint32_t aPromiseId,
|
|||
assert(session);
|
||||
|
||||
ClearInMemorySessionData(session);
|
||||
mCallback->ResolvePromise(aPromiseId);
|
||||
mCallback->SessionClosed(aSessionId, aSessionIdLength);
|
||||
mCallback->ResolvePromise(aPromiseId);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -745,6 +745,7 @@ MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
|
|||
case MP4PARSE_CODEC_FLAC: codec_string = "flac"; break;
|
||||
case MP4PARSE_CODEC_AVC: codec_string = "h.264"; break;
|
||||
case MP4PARSE_CODEC_VP9: codec_string = "vp9"; break;
|
||||
case MP4PARSE_CODEC_MP3: codec_string = "mp3"; break;
|
||||
}
|
||||
MOZ_LOG(sLog, LogLevel::Debug, ("track codec %s (%u)\n",
|
||||
codec_string, info.codec));
|
||||
|
@ -778,6 +779,8 @@ MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
|
|||
track->mMimeType = MEDIA_MIMETYPE_AUDIO_AAC;
|
||||
} else if (info.codec == MP4PARSE_CODEC_FLAC) {
|
||||
track->mMimeType = MEDIA_MIMETYPE_AUDIO_FLAC;
|
||||
} else if (info.codec == MP4PARSE_CODEC_MP3) {
|
||||
track->mMimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
|
||||
}
|
||||
track->mCodecSpecificConfig->AppendElements(
|
||||
audio.codec_specific_config.data,
|
||||
|
|
11
mfbt/Array.h
11
mfbt/Array.h
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/ReverseIterator.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
@ -23,6 +24,16 @@ class Array
|
|||
T mArr[Length];
|
||||
|
||||
public:
|
||||
Array() {}
|
||||
|
||||
template <typename... Args>
|
||||
MOZ_IMPLICIT Array(Args&&... aArgs)
|
||||
: mArr{mozilla::Forward<Args>(aArgs)...}
|
||||
{
|
||||
static_assert(sizeof...(aArgs) == Length,
|
||||
"The number of arguments should be equal to the template parameter Length");
|
||||
}
|
||||
|
||||
T& operator[](size_t aIndex)
|
||||
{
|
||||
MOZ_ASSERT(aIndex < Length);
|
||||
|
|
|
@ -54,6 +54,11 @@ private:
|
|||
public:
|
||||
EnumeratedArray() {}
|
||||
|
||||
template <typename... Args>
|
||||
MOZ_IMPLICIT EnumeratedArray(Args&&... aArgs)
|
||||
: mArray{mozilla::Forward<Args>(aArgs)...}
|
||||
{}
|
||||
|
||||
explicit EnumeratedArray(const EnumeratedArray& aOther)
|
||||
{
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/Array.h"
|
||||
|
||||
void TestInitialValueByConstructor()
|
||||
{
|
||||
using namespace mozilla;
|
||||
// Style 1
|
||||
Array<int32_t, 3> arr1(1, 2, 3);
|
||||
MOZ_RELEASE_ASSERT(arr1[0] == 1);
|
||||
MOZ_RELEASE_ASSERT(arr1[1] == 2);
|
||||
MOZ_RELEASE_ASSERT(arr1[2] == 3);
|
||||
// Style 2
|
||||
Array<int32_t, 3> arr2{5, 6, 7};
|
||||
MOZ_RELEASE_ASSERT(arr2[0] == 5);
|
||||
MOZ_RELEASE_ASSERT(arr2[1] == 6);
|
||||
MOZ_RELEASE_ASSERT(arr2[2] == 7);
|
||||
// Style 3
|
||||
Array<int32_t, 3> arr3({8, 9, 10});
|
||||
MOZ_RELEASE_ASSERT(arr3[0] == 8);
|
||||
MOZ_RELEASE_ASSERT(arr3[1] == 9);
|
||||
MOZ_RELEASE_ASSERT(arr3[2] == 10);
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
TestInitialValueByConstructor();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
|
||||
enum class AnimalSpecies
|
||||
{
|
||||
Cow,
|
||||
Sheep,
|
||||
Pig,
|
||||
Count
|
||||
};
|
||||
|
||||
void TestInitialValueByConstructor()
|
||||
{
|
||||
using namespace mozilla;
|
||||
// Style 1
|
||||
EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int> headCount(1, 2, 3);
|
||||
MOZ_RELEASE_ASSERT(headCount[AnimalSpecies::Cow] == 1);
|
||||
MOZ_RELEASE_ASSERT(headCount[AnimalSpecies::Sheep] == 2);
|
||||
MOZ_RELEASE_ASSERT(headCount[AnimalSpecies::Pig] == 3);
|
||||
// Style 2
|
||||
EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int> headCount2{5, 6, 7};
|
||||
MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Cow] == 5);
|
||||
MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Sheep] == 6);
|
||||
MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Pig] == 7);
|
||||
// Style 3
|
||||
EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int> headCount3({8, 9, 10});
|
||||
MOZ_RELEASE_ASSERT(headCount3[AnimalSpecies::Cow] == 8);
|
||||
MOZ_RELEASE_ASSERT(headCount3[AnimalSpecies::Sheep] == 9);
|
||||
MOZ_RELEASE_ASSERT(headCount3[AnimalSpecies::Pig] == 10);
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
TestInitialValueByConstructor();
|
||||
return 0;
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
CppUnitTests([
|
||||
'TestArray',
|
||||
'TestArrayUtils',
|
||||
'TestAtomics',
|
||||
'TestBinarySearch',
|
||||
|
@ -16,6 +17,7 @@ CppUnitTests([
|
|||
'TestCountPopulation',
|
||||
'TestCountZeroes',
|
||||
'TestEndian',
|
||||
'TestEnumeratedArray',
|
||||
'TestEnumSet',
|
||||
'TestEnumTypeTraits',
|
||||
'TestFastBernoulliTrial',
|
||||
|
|
|
@ -105,8 +105,10 @@ android {
|
|||
}
|
||||
|
||||
if (!mozconfig.substs.MOZ_NATIVE_DEVICES) {
|
||||
exclude 'org/mozilla/gecko/ChromeCast.java'
|
||||
exclude 'org/mozilla/gecko/ChromeCastDisplay.java'
|
||||
exclude 'org/mozilla/gecko/ChromeCastPlayer.java'
|
||||
exclude 'org/mozilla/gecko/GeckoMediaPlayer.java'
|
||||
exclude 'org/mozilla/gecko/GeckoPresentationDisplay.java'
|
||||
exclude 'org/mozilla/gecko/MediaPlayerManager.java'
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
#ifdef MOZ_NATIVE_DEVICES
|
||||
<!-- This resources comes from Google Play Services. Required for casting support. -->
|
||||
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
|
||||
<service android:name="org.mozilla.gecko.RemotePresentationService" android:exported="false"/>
|
||||
|
||||
#endif
|
||||
|
||||
<!-- This activity handles all incoming Intents and dispatches them to other activities. -->
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* vim: ts=4 sw=4 expandtab:
|
||||
* 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.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
|
||||
import com.google.android.gms.cast.CastDevice;
|
||||
import com.google.android.gms.cast.CastRemoteDisplayLocalService;
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GooglePlayServicesUtil;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.v7.media.MediaRouter.RouteInfo;
|
||||
import android.util.Log;
|
||||
|
||||
public class ChromeCastDisplay implements GeckoPresentationDisplay {
|
||||
|
||||
static final String REMOTE_DISPLAY_APP_ID = "4574A331";
|
||||
|
||||
private static final String LOGTAG = "GeckoChromeCastDisplay";
|
||||
private final Context context;
|
||||
private final RouteInfo route;
|
||||
private CastDevice castDevice;
|
||||
|
||||
public ChromeCastDisplay(Context context, RouteInfo route) {
|
||||
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
|
||||
if (status != ConnectionResult.SUCCESS) {
|
||||
throw new IllegalStateException("Play services are required for Chromecast support (got status code " + status + ")");
|
||||
}
|
||||
|
||||
this.context = context;
|
||||
this.route = route;
|
||||
this.castDevice = CastDevice.getFromBundle(route.getExtras());
|
||||
}
|
||||
|
||||
public JSONObject toJSON() {
|
||||
final JSONObject obj = new JSONObject();
|
||||
try {
|
||||
if (castDevice == null) {
|
||||
return null;
|
||||
}
|
||||
obj.put("uuid", route.getId());
|
||||
obj.put("friendlyName", castDevice.getFriendlyName());
|
||||
obj.put("type", "chromecast");
|
||||
} catch (JSONException ex) {
|
||||
Log.d(LOGTAG, "Error building route", ex);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(final EventCallback callback) {
|
||||
|
||||
if (CastRemoteDisplayLocalService.getInstance() != null) {
|
||||
Log.d(LOGTAG, "CastRemoteDisplayLocalService already existed.");
|
||||
GeckoAppShell.notifyObservers("presentation-view-ready", route.getId());
|
||||
callback.sendSuccess("Succeed to start presentation.");
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(context, RemotePresentationService.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
PendingIntent notificationPendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
|
||||
|
||||
CastRemoteDisplayLocalService.NotificationSettings settings =
|
||||
new CastRemoteDisplayLocalService.NotificationSettings.Builder()
|
||||
.setNotificationPendingIntent(notificationPendingIntent).build();
|
||||
|
||||
CastRemoteDisplayLocalService.startService(
|
||||
context,
|
||||
RemotePresentationService.class,
|
||||
REMOTE_DISPLAY_APP_ID,
|
||||
castDevice,
|
||||
settings,
|
||||
new CastRemoteDisplayLocalService.Callbacks() {
|
||||
@Override
|
||||
public void onServiceCreated(CastRemoteDisplayLocalService service) {
|
||||
((RemotePresentationService) service).setDeviceId(route.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteDisplaySessionStarted(CastRemoteDisplayLocalService service) {
|
||||
Log.d(LOGTAG, "Remote presentation launched!");
|
||||
callback.sendSuccess("Succeed to start presentation.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteDisplaySessionError(Status errorReason) {
|
||||
int code = errorReason.getStatusCode();
|
||||
callback.sendError("Fail to start presentation. Error code: " + code);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(EventCallback callback) {
|
||||
CastRemoteDisplayLocalService.stopService();
|
||||
callback.sendSuccess("Succeed to stop presentation.");
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ import android.support.v7.media.MediaRouter.RouteInfo;
|
|||
import android.util.Log;
|
||||
|
||||
/* Implementation of GeckoMediaPlayer for talking to ChromeCast devices */
|
||||
class ChromeCast implements GeckoMediaPlayer {
|
||||
class ChromeCastPlayer implements GeckoMediaPlayer {
|
||||
private static final boolean SHOW_DEBUG = false;
|
||||
|
||||
static final String MIRROR_RECEIVER_APP_ID = "08FF1091";
|
||||
|
@ -168,7 +168,7 @@ class ChromeCast implements GeckoMediaPlayer {
|
|||
}
|
||||
}
|
||||
|
||||
public ChromeCast(Context context, RouteInfo route) {
|
||||
public ChromeCastPlayer(Context context, RouteInfo route) {
|
||||
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
|
||||
if (status != ConnectionResult.SUCCESS) {
|
||||
throw new IllegalStateException("Play services are required for Chromecast support (got status code " + status + ")");
|
||||
|
@ -493,7 +493,7 @@ class ChromeCast implements GeckoMediaPlayer {
|
|||
apiClient.connect();
|
||||
}
|
||||
|
||||
private static final String LOGTAG = "GeckoChromeCast";
|
||||
private static final String LOGTAG = "GeckoChromeCastPlayer";
|
||||
private void debug(String msg, Exception e) {
|
||||
if (SHOW_DEBUG) {
|
||||
Log.e(LOGTAG, msg, e);
|
|
@ -0,0 +1,22 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* 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.json.JSONObject;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
|
||||
/**
|
||||
* Wrapper for MediaRouter types supported by Android to use for
|
||||
* Presentation API, such as Chromecast, Miracast, etc.
|
||||
*/
|
||||
interface GeckoPresentationDisplay {
|
||||
/**
|
||||
* Can return null.
|
||||
*/
|
||||
JSONObject toJSON();
|
||||
void start(EventCallback callback);
|
||||
void stop(EventCallback callback);
|
||||
}
|
|
@ -45,6 +45,7 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
|
|||
}
|
||||
|
||||
private static final String LOGTAG = "GeckoMediaPlayerManager";
|
||||
protected boolean isPresentationMode = false; // Used to prevent mirroring when Presentation API is used.
|
||||
|
||||
@ReflectionTarget
|
||||
public static final String MEDIA_PLAYER_TAG = "MPManagerFragment";
|
||||
|
@ -64,7 +65,8 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
|
|||
}
|
||||
|
||||
protected MediaRouter mediaRouter = null;
|
||||
protected final Map<String, GeckoMediaPlayer> displays = new HashMap<String, GeckoMediaPlayer>();
|
||||
protected final Map<String, GeckoMediaPlayer> players = new HashMap<String, GeckoMediaPlayer>();
|
||||
protected final Map<String, GeckoPresentationDisplay> displays = new HashMap<String, GeckoPresentationDisplay>(); // used for Presentation API
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -77,7 +79,9 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
|
|||
"MediaPlayer:Pause",
|
||||
"MediaPlayer:End",
|
||||
"MediaPlayer:Mirror",
|
||||
"MediaPlayer:Message");
|
||||
"MediaPlayer:Message",
|
||||
"AndroidCastDevice:Start",
|
||||
"AndroidCastDevice:Stop");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,42 +96,61 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
|
|||
"MediaPlayer:Pause",
|
||||
"MediaPlayer:End",
|
||||
"MediaPlayer:Mirror",
|
||||
"MediaPlayer:Message");
|
||||
"MediaPlayer:Message",
|
||||
"AndroidCastDevice:Start",
|
||||
"AndroidCastDevice:Stop");
|
||||
}
|
||||
|
||||
// GeckoEventListener implementation
|
||||
@Override
|
||||
public void handleMessage(String event, final NativeJSObject message, final EventCallback callback) {
|
||||
debug(event);
|
||||
|
||||
final GeckoMediaPlayer display = displays.get(message.getString("id"));
|
||||
if (display == null) {
|
||||
Log.e(LOGTAG, "Couldn't find a display for this id: " + message.getString("id") + " for message: " + event);
|
||||
if (callback != null) {
|
||||
callback.sendError(null);
|
||||
if (event.startsWith("MediaPlayer:")) {
|
||||
final GeckoMediaPlayer player = players.get(message.getString("id"));
|
||||
if (player == null) {
|
||||
Log.e(LOGTAG, "Couldn't find a player for this id: " + message.getString("id") + " for message: " + event);
|
||||
if (callback != null) {
|
||||
callback.sendError(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ("MediaPlayer:Play".equals(event)) {
|
||||
player.play(callback);
|
||||
} else if ("MediaPlayer:Start".equals(event)) {
|
||||
player.start(callback);
|
||||
} else if ("MediaPlayer:Stop".equals(event)) {
|
||||
player.stop(callback);
|
||||
} else if ("MediaPlayer:Pause".equals(event)) {
|
||||
player.pause(callback);
|
||||
} else if ("MediaPlayer:End".equals(event)) {
|
||||
player.end(callback);
|
||||
} else if ("MediaPlayer:Mirror".equals(event)) {
|
||||
player.mirror(callback);
|
||||
} else if ("MediaPlayer:Message".equals(event) && message.has("data")) {
|
||||
player.message(message.getString("data"), callback);
|
||||
} else if ("MediaPlayer:Load".equals(event)) {
|
||||
final String url = message.optString("source", "");
|
||||
final String type = message.optString("type", "video/mp4");
|
||||
final String title = message.optString("title", "");
|
||||
player.load(title, url, type, callback);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ("MediaPlayer:Play".equals(event)) {
|
||||
display.play(callback);
|
||||
} else if ("MediaPlayer:Start".equals(event)) {
|
||||
display.start(callback);
|
||||
} else if ("MediaPlayer:Stop".equals(event)) {
|
||||
display.stop(callback);
|
||||
} else if ("MediaPlayer:Pause".equals(event)) {
|
||||
display.pause(callback);
|
||||
} else if ("MediaPlayer:End".equals(event)) {
|
||||
display.end(callback);
|
||||
} else if ("MediaPlayer:Mirror".equals(event)) {
|
||||
display.mirror(callback);
|
||||
} else if ("MediaPlayer:Message".equals(event) && message.has("data")) {
|
||||
display.message(message.getString("data"), callback);
|
||||
} else if ("MediaPlayer:Load".equals(event)) {
|
||||
final String url = message.optString("source", "");
|
||||
final String type = message.optString("type", "video/mp4");
|
||||
final String title = message.optString("title", "");
|
||||
display.load(title, url, type, callback);
|
||||
if (event.startsWith("AndroidCastDevice:")) {
|
||||
final GeckoPresentationDisplay display = displays.get(message.getString("id"));
|
||||
if (display == null) {
|
||||
Log.e(LOGTAG, "Couldn't find a display for this id: " + message.getString("id") + " for message: " + event);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("AndroidCastDevice:Start".equals(event)) {
|
||||
display.start(callback);
|
||||
isPresentationMode = true;
|
||||
} else if ("AndroidCastDevice:Stop".equals(event)) {
|
||||
display.stop(callback);
|
||||
isPresentationMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,9 +159,15 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
|
|||
@Override
|
||||
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
|
||||
debug("onRouteRemoved: route=" + route);
|
||||
displays.remove(route.getId());
|
||||
|
||||
// Remove from media player list.
|
||||
players.remove(route.getId());
|
||||
GeckoAppShell.notifyObservers("MediaPlayer:Removed", route.getId());
|
||||
updatePresentation();
|
||||
|
||||
// Remove from presentation display list.
|
||||
displays.remove(route.getId());
|
||||
GeckoAppShell.notifyObservers("AndroidCastDevice:Removed", route.getId());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -164,21 +193,44 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
|
|||
@Override
|
||||
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
|
||||
debug("onRouteAdded: route=" + route);
|
||||
final GeckoMediaPlayer display = getMediaPlayerForRoute(route);
|
||||
saveAndNotifyOfDisplay("MediaPlayer:Added", route, display);
|
||||
final GeckoMediaPlayer player = getMediaPlayerForRoute(route);
|
||||
saveAndNotifyOfPlayer("MediaPlayer:Added", route, player);
|
||||
updatePresentation();
|
||||
|
||||
final GeckoPresentationDisplay display = getPresentationDisplayForRoute(route);
|
||||
saveAndNotifyOfDisplay("AndroidCastDevice:Added", route, display);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
|
||||
debug("onRouteChanged: route=" + route);
|
||||
final GeckoMediaPlayer display = displays.get(route.getId());
|
||||
saveAndNotifyOfDisplay("MediaPlayer:Changed", route, display);
|
||||
final GeckoMediaPlayer player = players.get(route.getId());
|
||||
saveAndNotifyOfPlayer("MediaPlayer:Changed", route, player);
|
||||
updatePresentation();
|
||||
|
||||
final GeckoPresentationDisplay display = displays.get(route.getId());
|
||||
saveAndNotifyOfDisplay("AndroidCastDevice:Changed", route, display);
|
||||
}
|
||||
|
||||
private void saveAndNotifyOfPlayer(final String eventName,
|
||||
MediaRouter.RouteInfo route,
|
||||
final GeckoMediaPlayer player) {
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final JSONObject json = player.toJSON();
|
||||
if (json == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
players.put(route.getId(), player);
|
||||
GeckoAppShell.notifyObservers(eventName, json.toString());
|
||||
}
|
||||
|
||||
private void saveAndNotifyOfDisplay(final String eventName,
|
||||
MediaRouter.RouteInfo route, final GeckoMediaPlayer display) {
|
||||
MediaRouter.RouteInfo route,
|
||||
final GeckoPresentationDisplay display) {
|
||||
if (display == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -196,7 +248,7 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
|
|||
private GeckoMediaPlayer getMediaPlayerForRoute(MediaRouter.RouteInfo route) {
|
||||
try {
|
||||
if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
|
||||
return new ChromeCast(getActivity(), route);
|
||||
return new ChromeCastPlayer(getActivity(), route);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
debug("Error handling presentation", ex);
|
||||
|
@ -205,6 +257,17 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
|
|||
return null;
|
||||
}
|
||||
|
||||
private GeckoPresentationDisplay getPresentationDisplayForRoute(MediaRouter.RouteInfo route) {
|
||||
try {
|
||||
if (route.supportsControlCategory(CastMediaControlIntent.categoryForCast(ChromeCastDisplay.REMOTE_DISPLAY_APP_ID))) {
|
||||
return new ChromeCastDisplay(getActivity(), route);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
debug("Error handling presentation", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
@ -225,7 +288,8 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
|
|||
final MediaRouteSelector selectorBuilder = new MediaRouteSelector.Builder()
|
||||
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
|
||||
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
|
||||
.addControlCategory(CastMediaControlIntent.categoryForCast(ChromeCast.MIRROR_RECEIVER_APP_ID))
|
||||
.addControlCategory(CastMediaControlIntent.categoryForCast(ChromeCastPlayer.MIRROR_RECEIVER_APP_ID))
|
||||
.addControlCategory(CastMediaControlIntent.categoryForCast(ChromeCastDisplay.REMOTE_DISPLAY_APP_ID))
|
||||
.build();
|
||||
mediaRouter.addCallback(selectorBuilder, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
|
||||
}
|
||||
|
|
|
@ -54,6 +54,10 @@ public class PresentationMediaPlayerManager extends MediaPlayerManager {
|
|||
return;
|
||||
}
|
||||
|
||||
if (isPresentationMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
|
||||
Display display = route != null ? route.getPresentationDisplay() : null;
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* vim: ts=4 sw=4 expandtab:
|
||||
* 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.annotation.WrapForJNI;
|
||||
import org.mozilla.gecko.GeckoThread;
|
||||
import org.mozilla.gecko.GeckoView;
|
||||
import org.mozilla.gecko.ScreenManagerHelper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
public class PresentationView extends GeckoView {
|
||||
private static final String LOGTAG = "PresentationView";
|
||||
private static final String presentationViewURI = "chrome://browser/content/PresentationView.xul";
|
||||
|
||||
public PresentationView(Context context, String deviceId, int screenId) {
|
||||
super(context);
|
||||
this.chromeURI = presentationViewURI + "#" + deviceId;
|
||||
this.screenId = screenId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* vim: ts=4 sw=4 expandtab:
|
||||
* 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.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.PresentationView;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.ScreenManagerHelper;
|
||||
import org.mozilla.gecko.annotation.JNITarget;
|
||||
import org.mozilla.gecko.annotation.ReflectionTarget;
|
||||
import org.mozilla.gecko.annotation.WrapForJNI;
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.NativeEventListener;
|
||||
import org.mozilla.gecko.util.NativeJSObject;
|
||||
|
||||
import com.google.android.gms.cast.CastMediaControlIntent;
|
||||
import com.google.android.gms.cast.CastPresentation;
|
||||
import com.google.android.gms.cast.CastRemoteDisplayLocalService;
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GooglePlayServicesUtil;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.media.MediaControlIntent;
|
||||
import android.support.v7.media.MediaRouteSelector;
|
||||
import android.support.v7.media.MediaRouter.RouteInfo;
|
||||
import android.support.v7.media.MediaRouter;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Service to keep the remote display running even when the app goes into the background
|
||||
*/
|
||||
public class RemotePresentationService extends CastRemoteDisplayLocalService {
|
||||
|
||||
private static final String LOGTAG = "RemotePresentationService";
|
||||
private CastPresentation presentation;
|
||||
private String deviceId;
|
||||
private int screenId;
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePresentation(Display display) {
|
||||
createPresentation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismissPresentation() {
|
||||
dismissPresentation();
|
||||
}
|
||||
|
||||
private void dismissPresentation() {
|
||||
if (presentation != null) {
|
||||
presentation.dismiss();
|
||||
presentation = null;
|
||||
ScreenManagerHelper.removeDisplay(screenId);
|
||||
}
|
||||
}
|
||||
|
||||
private void createPresentation() {
|
||||
dismissPresentation();
|
||||
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
getDisplay().getMetrics(metrics);
|
||||
screenId = ScreenManagerHelper.addDisplay(ScreenManagerHelper.DISPLAY_VIRTUAL,
|
||||
metrics.widthPixels,
|
||||
metrics.heightPixels,
|
||||
metrics.density);
|
||||
|
||||
VirtualPresentation virtualPresentation = new VirtualPresentation(this, getDisplay());
|
||||
virtualPresentation.setDeviceId(deviceId);
|
||||
virtualPresentation.setScreenId(screenId);
|
||||
presentation = (CastPresentation) virtualPresentation;
|
||||
|
||||
try {
|
||||
presentation.show();
|
||||
} catch (WindowManager.InvalidDisplayException ex) {
|
||||
Log.e(LOGTAG, "Unable to show presentation, display was removed.", ex);
|
||||
dismissPresentation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VirtualPresentation extends CastPresentation {
|
||||
private final String LOGTAG = "VirtualPresentation";
|
||||
private RelativeLayout layout;
|
||||
private PresentationView view;
|
||||
private String deviceId;
|
||||
private int screenId;
|
||||
|
||||
public VirtualPresentation(Context context, Display display) {
|
||||
super(context, display);
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
|
||||
public void setScreenId(int screenId) { this.screenId = screenId; }
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
/*
|
||||
* NOTICE: The context get from getContext() is different to the context
|
||||
* of the application. Presentaion has its own context to get correct
|
||||
* resources.
|
||||
*/
|
||||
|
||||
// Create new PresentationView
|
||||
view = new PresentationView(getContext(), deviceId, screenId);
|
||||
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT));
|
||||
|
||||
// Create new layout to put the GeckoView
|
||||
layout = new RelativeLayout(getContext());
|
||||
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT));
|
||||
layout.addView(view);
|
||||
|
||||
setContentView(layout);
|
||||
}
|
||||
}
|
|
@ -582,8 +582,13 @@ public class Tabs implements GeckoEventListener {
|
|||
tab.setIsAudioPlaying(message.getBoolean("isAudioPlaying"));
|
||||
notifyListeners(tab, TabEvents.AUDIO_PLAYING_CHANGE);
|
||||
} else if (event.equals("Tab:MediaPlaybackChange")) {
|
||||
tab.setIsMediaPlaying(message.getBoolean("active"));
|
||||
notifyListeners(tab, TabEvents.MEDIA_PLAYING_CHANGE);
|
||||
final String status = message.getString("status");
|
||||
if (status.equals("resume")) {
|
||||
notifyListeners(tab, TabEvents.MEDIA_PLAYING_RESUME);
|
||||
} else {
|
||||
tab.setIsMediaPlaying(status.equals("start"));
|
||||
notifyListeners(tab, TabEvents.MEDIA_PLAYING_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
|
@ -644,6 +649,7 @@ public class Tabs implements GeckoEventListener {
|
|||
AUDIO_PLAYING_CHANGE,
|
||||
OPENED_FROM_TABS_TRAY,
|
||||
MEDIA_PLAYING_CHANGE,
|
||||
MEDIA_PLAYING_RESUME
|
||||
}
|
||||
|
||||
public void notifyListeners(Tab tab, TabEvents msg) {
|
||||
|
|
|
@ -33,7 +33,6 @@ public class MediaControlService extends Service implements Tabs.OnTabsChangedLi
|
|||
private static final String LOGTAG = "MediaControlService";
|
||||
|
||||
public static final String ACTION_INIT = "action_init";
|
||||
public static final String ACTION_START = "action_start";
|
||||
public static final String ACTION_RESUME = "action_resume";
|
||||
public static final String ACTION_PAUSE = "action_pause";
|
||||
public static final String ACTION_STOP = "action_stop";
|
||||
|
@ -100,18 +99,27 @@ public class MediaControlService extends Service implements Tabs.OnTabsChangedLi
|
|||
final Tab playingTab = mTabReference.get();
|
||||
switch (msg) {
|
||||
case MEDIA_PLAYING_CHANGE:
|
||||
// The 'MEDIA_PLAYING_CHANGE' would only be received when the
|
||||
// media starts or ends.
|
||||
if (playingTab != tab && tab.isMediaPlaying()) {
|
||||
mTabReference = new WeakReference<>(tab);
|
||||
mController.getTransportControls().sendCustomAction(ACTION_START, null);
|
||||
notifyControlInterfaceChanged(ACTION_PAUSE);
|
||||
} else if (playingTab == tab && !tab.isMediaPlaying()) {
|
||||
mController.getTransportControls().stop();
|
||||
notifyControlInterfaceChanged(ACTION_STOP);
|
||||
mTabReference = new WeakReference<>(null);
|
||||
}
|
||||
break;
|
||||
case MEDIA_PLAYING_RESUME:
|
||||
// user resume the paused-by-control media from page so that we
|
||||
// should make the control interface consistent.
|
||||
if (playingTab == tab && !isMediaPlaying()) {
|
||||
mController.getTransportControls().play();
|
||||
}
|
||||
break;
|
||||
|
||||
case CLOSED:
|
||||
if (playingTab == null || playingTab == tab) {
|
||||
// Remove the controls when the playing tab disappeared or was closed.
|
||||
mController.getTransportControls().stop();
|
||||
notifyControlInterfaceChanged(ACTION_STOP);
|
||||
}
|
||||
break;
|
||||
case FAVICON:
|
||||
|
@ -125,8 +133,7 @@ public class MediaControlService extends Service implements Tabs.OnTabsChangedLi
|
|||
}
|
||||
|
||||
private boolean isMediaPlaying() {
|
||||
return mActionState.equals(ACTION_RESUME) ||
|
||||
mActionState.equals(ACTION_RESUME_BY_AUDIO_FOCUS);
|
||||
return mActionState.equals(ACTION_RESUME);
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
|
@ -173,10 +180,8 @@ public class MediaControlService extends Service implements Tabs.OnTabsChangedLi
|
|||
switch (intent.getAction()) {
|
||||
case ACTION_INIT :
|
||||
// This action is used to create a service and do the initialization,
|
||||
// the actual operation would be executed via tab events.
|
||||
break;
|
||||
case ACTION_START :
|
||||
mController.getTransportControls().sendCustomAction(ACTION_START, null);
|
||||
// the actual operation would be executed via control interface's
|
||||
// pending intent.
|
||||
break;
|
||||
case ACTION_RESUME :
|
||||
mController.getTransportControls().play();
|
||||
|
@ -235,18 +240,12 @@ public class MediaControlService extends Service implements Tabs.OnTabsChangedLi
|
|||
mSession.setCallback(new MediaSession.Callback() {
|
||||
@Override
|
||||
public void onCustomAction(String action, Bundle extras) {
|
||||
if (action.equals(ACTION_START)) {
|
||||
Log.d(LOGTAG, "Controller, onStart");
|
||||
notifyControlInterfaceChanged(ACTION_PAUSE);
|
||||
mActionState = ACTION_RESUME;
|
||||
} else if (action.equals(ACTION_PAUSE_BY_AUDIO_FOCUS)) {
|
||||
if (action.equals(ACTION_PAUSE_BY_AUDIO_FOCUS)) {
|
||||
Log.d(LOGTAG, "Controller, pause by audio focus changed");
|
||||
notifyControlInterfaceChanged(ACTION_RESUME);
|
||||
mActionState = ACTION_PAUSE_BY_AUDIO_FOCUS;
|
||||
} else if (action.equals(ACTION_RESUME_BY_AUDIO_FOCUS)) {
|
||||
Log.d(LOGTAG, "Controller, resume by audio focus changed");
|
||||
notifyControlInterfaceChanged(ACTION_PAUSE);
|
||||
mActionState = ACTION_RESUME_BY_AUDIO_FOCUS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,7 +255,6 @@ public class MediaControlService extends Service implements Tabs.OnTabsChangedLi
|
|||
super.onPlay();
|
||||
notifyControlInterfaceChanged(ACTION_PAUSE);
|
||||
notifyObservers("MediaControl", "resumeMedia");
|
||||
mActionState = ACTION_RESUME;
|
||||
// To make sure we always own audio focus during playing.
|
||||
AudioFocusAgent.notifyStartedPlaying();
|
||||
}
|
||||
|
@ -267,7 +265,6 @@ public class MediaControlService extends Service implements Tabs.OnTabsChangedLi
|
|||
super.onPause();
|
||||
notifyControlInterfaceChanged(ACTION_RESUME);
|
||||
notifyObservers("MediaControl", "mediaControlPaused");
|
||||
mActionState = ACTION_PAUSE;
|
||||
AudioFocusAgent.notifyStoppedPlaying();
|
||||
}
|
||||
|
||||
|
@ -277,7 +274,6 @@ public class MediaControlService extends Service implements Tabs.OnTabsChangedLi
|
|||
super.onStop();
|
||||
notifyControlInterfaceChanged(ACTION_STOP);
|
||||
notifyObservers("MediaControl", "mediaControlStopped");
|
||||
mActionState = ACTION_STOP;
|
||||
mTabReference = new WeakReference<>(null);
|
||||
}
|
||||
});
|
||||
|
@ -291,16 +287,17 @@ public class MediaControlService extends Service implements Tabs.OnTabsChangedLi
|
|||
return action.equals(ACTION_STOP);
|
||||
}
|
||||
|
||||
private void notifyControlInterfaceChanged(final String action) {
|
||||
private void notifyControlInterfaceChanged(final String uiAction) {
|
||||
if (!mInitialize) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(LOGTAG, "notifyControlInterfaceChanged, action = " + action);
|
||||
Log.d(LOGTAG, "notifyControlInterfaceChanged, action = " + uiAction);
|
||||
|
||||
if (isNeedToRemoveControlInterface(action)) {
|
||||
if (isNeedToRemoveControlInterface(uiAction)) {
|
||||
stopForeground(false);
|
||||
NotificationManagerCompat.from(this).cancel(MEDIA_CONTROL_ID);
|
||||
setActionState(uiAction);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -320,14 +317,30 @@ public class MediaControlService extends Service implements Tabs.OnTabsChangedLi
|
|||
return;
|
||||
}
|
||||
|
||||
setActionState(uiAction);
|
||||
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateNotification(tab, action);
|
||||
updateNotification(tab, uiAction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setActionState(final String uiAction) {
|
||||
switch (uiAction) {
|
||||
case ACTION_PAUSE:
|
||||
mActionState = ACTION_RESUME;
|
||||
break;
|
||||
case ACTION_RESUME:
|
||||
mActionState = ACTION_PAUSE;
|
||||
break;
|
||||
case ACTION_STOP:
|
||||
mActionState = ACTION_STOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNotification(Tab tab, String action) {
|
||||
ThreadUtils.assertNotOnUiThread();
|
||||
|
||||
|
|
|
@ -618,6 +618,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
|
|||
'preferences/SearchPreferenceCategory.java',
|
||||
'preferences/SetHomepagePreference.java',
|
||||
'preferences/SyncPreference.java',
|
||||
'PresentationView.java',
|
||||
'PrintHelper.java',
|
||||
'PrivateTab.java',
|
||||
'promotion/AddToHomeScreenPromotion.java',
|
||||
|
@ -839,10 +840,13 @@ moz_native_devices_jars = [
|
|||
CONFIG['ANDROID_PLAY_SERVICES_CAST_AAR_LIB'],
|
||||
]
|
||||
moz_native_devices_sources = ['java/org/mozilla/gecko/' + x for x in [
|
||||
'ChromeCast.java',
|
||||
'ChromeCastDisplay.java',
|
||||
'ChromeCastPlayer.java',
|
||||
'GeckoMediaPlayer.java',
|
||||
'GeckoPresentationDisplay.java',
|
||||
'MediaPlayerManager.java',
|
||||
'PresentationMediaPlayerManager.java',
|
||||
'RemotePresentationService.java',
|
||||
]]
|
||||
if CONFIG['MOZ_NATIVE_DEVICES']:
|
||||
gbjar.extra_jars += moz_native_devices_jars
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/* 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, results: Cr} = Components;
|
||||
|
||||
// globals Services
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function log(str) {
|
||||
// dump("-*- PresentationView.js -*-: " + str + "\n");
|
||||
}
|
||||
|
||||
let PresentationView = {
|
||||
_id: null,
|
||||
|
||||
startup: function startup() {
|
||||
// use hash as the ID of this top level window
|
||||
this._id = window.location.hash.substr(1);
|
||||
},
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.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/. -->
|
||||
|
||||
<window id="presentation-window"
|
||||
onload="PresentationView.startup();"
|
||||
windowtype="navigator:browser"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<browser id="content" type="content-targetable" src="about:blank" flex="1"/>
|
||||
|
||||
<script type="application/javascript" src="chrome://browser/content/PresentationView.js"/>
|
||||
</window>
|
|
@ -3519,6 +3519,7 @@ Tab.prototype = {
|
|||
|
||||
Services.obs.addObserver(this, "before-first-paint", false);
|
||||
Services.obs.addObserver(this, "media-playback", false);
|
||||
Services.obs.addObserver(this, "media-playback-resumed", false);
|
||||
|
||||
// Always intialise new tabs with basic session store data to avoid
|
||||
// problems with functions that always expect it to be present
|
||||
|
@ -3629,6 +3630,7 @@ Tab.prototype = {
|
|||
|
||||
Services.obs.removeObserver(this, "before-first-paint");
|
||||
Services.obs.removeObserver(this, "media-playback", false);
|
||||
Services.obs.removeObserver(this, "media-playback-resumed", false);
|
||||
|
||||
// Make sure the previously selected panel remains selected. The selected panel of a deck is
|
||||
// not stable when panels are removed.
|
||||
|
@ -4395,16 +4397,24 @@ Tab.prototype = {
|
|||
break;
|
||||
|
||||
case "media-playback":
|
||||
case "media-playback-resumed":
|
||||
if (!aSubject) {
|
||||
return;
|
||||
}
|
||||
|
||||
let winId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
if (this.browser.outerWindowID == winId) {
|
||||
let status;
|
||||
if (aTopic == "media-playback") {
|
||||
status = aData === "active" ? "start" : "end";
|
||||
} else if (aTopic == "media-playback-resumed") {
|
||||
status = "resume";
|
||||
}
|
||||
|
||||
Messaging.sendRequest({
|
||||
type: "Tab:MediaPlaybackChange",
|
||||
tabID: this.id,
|
||||
active: aData === "active"
|
||||
status: status
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -29,6 +29,8 @@ chrome.jar:
|
|||
content/browser.js (content/browser.js)
|
||||
content/geckoview.xul (content/geckoview.xul)
|
||||
content/geckoview.js (content/geckoview.js)
|
||||
content/PresentationView.xul (content/PresentationView.xul)
|
||||
content/PresentationView.js (content/PresentationView.js)
|
||||
content/bindings/checkbox.xml (content/bindings/checkbox.xml)
|
||||
content/bindings/settings.xml (content/bindings/settings.xml)
|
||||
content/netError.xhtml (content/netError.xhtml)
|
||||
|
|
|
@ -48,8 +48,9 @@ public class GeckoView extends LayerView
|
|||
|
||||
private InputConnectionListener mInputConnectionListener;
|
||||
|
||||
private boolean onAttachedToWindowCalled;
|
||||
private int screenId = 0; // default to the primary screen
|
||||
protected boolean onAttachedToWindowCalled;
|
||||
protected String chromeURI = getGeckoInterface().getDefaultChromeURI();
|
||||
protected int screenId = 0; // default to the primary screen
|
||||
|
||||
@Override
|
||||
public void handleMessage(final String event, final JSONObject message) {
|
||||
|
@ -111,7 +112,7 @@ public class GeckoView extends LayerView
|
|||
}
|
||||
|
||||
@WrapForJNI(dispatchTo = "proxy")
|
||||
private static final class Window extends JNIObject {
|
||||
protected static final class Window extends JNIObject {
|
||||
@WrapForJNI(skip = true)
|
||||
/* package */ Window() {}
|
||||
|
||||
|
@ -168,7 +169,7 @@ public class GeckoView extends LayerView
|
|||
};
|
||||
}
|
||||
|
||||
private Window window;
|
||||
protected Window window;
|
||||
private boolean stateSaved;
|
||||
|
||||
public GeckoView(Context context) {
|
||||
|
@ -226,8 +227,7 @@ public class GeckoView extends LayerView
|
|||
super.onRestoreInstanceState(stateBinder.superState);
|
||||
}
|
||||
|
||||
private void openWindow() {
|
||||
final String chromeURI = getGeckoInterface().getDefaultChromeURI();
|
||||
protected void openWindow() {
|
||||
|
||||
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
|
||||
Window.open(window, this, getCompositor(),
|
||||
|
@ -239,7 +239,7 @@ public class GeckoView extends LayerView
|
|||
}
|
||||
}
|
||||
|
||||
private void reattachWindow() {
|
||||
protected void reattachWindow() {
|
||||
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
|
||||
window.reattach(this, getCompositor());
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[ShowAlignments]
|
||||
[ShowSSEConfig]
|
||||
[TestAppShellSteadyState]
|
||||
[TestArray]
|
||||
[TestArrayUtils]
|
||||
[TestAtomics]
|
||||
[TestAudioBuffers]
|
||||
|
@ -32,6 +33,7 @@ skip-if = os == 'b2g' || (os == 'android' && debug) # Bug 1054249
|
|||
[TestDllInterceptor]
|
||||
skip-if = os != 'win'
|
||||
[TestEndian]
|
||||
[TestEnumeratedArray]
|
||||
[TestEnumSet]
|
||||
[TestEnumTypeTraits]
|
||||
[TestFastBernoulliTrial]
|
||||
|
|
|
@ -6,8 +6,9 @@ const { classes: Cc, interfaces: Ci, results: Cr } = Components;
|
|||
var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"]
|
||||
.getService(Ci.nsIUrlClassifierDBService);
|
||||
|
||||
var timer;
|
||||
function setTimeout(callback, delay) {
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback({ notify: callback },
|
||||
delay,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
|
|
|
@ -13,6 +13,243 @@ Find files duplicated in a given packaged directory, independently of its
|
|||
package format.
|
||||
'''
|
||||
|
||||
# Known duplicate files
|
||||
# This list is ideally empty, but some existing files will be grandfathered in
|
||||
# See bug 1303184
|
||||
#
|
||||
# PLEASE DO NOT ADD MORE EXCEPTIONS TO THIS LIST
|
||||
#
|
||||
ALLOWED_DUPES = set([
|
||||
# updater on osx is bug 1311194
|
||||
'LaunchServices/org.mozilla.updater',
|
||||
'updater.app/Contents/MacOS/org.mozilla.updater',
|
||||
'updater.app/Contents/PkgInfo',
|
||||
'browser/chrome.manifest',
|
||||
# browser branding / themes is bug 1313106
|
||||
'browser/chrome/browser/content/branding/icon128.png',
|
||||
'browser/chrome/browser/content/branding/icon16.png',
|
||||
'browser/chrome/browser/content/branding/icon32.png',
|
||||
'browser/chrome/browser/content/branding/icon48.png',
|
||||
'browser/chrome/browser/content/browser/defaultthemes/5.footer.png',
|
||||
'browser/chrome/browser/content/browser/defaultthemes/5.header.png',
|
||||
'browser/chrome/browser/content/browser/extension.svg',
|
||||
'browser/chrome/browser/content/browser/places/bookmarkProperties.xul',
|
||||
'browser/chrome/browser/content/browser/places/bookmarkProperties2.xul',
|
||||
'browser/chrome/browser/skin/classic/browser/addons/addon-install-confirm.svg',
|
||||
'browser/chrome/browser/skin/classic/browser/connection-secure.svg',
|
||||
'browser/chrome/browser/skin/classic/browser/controlcenter/warning-gray.svg',
|
||||
'browser/chrome/browser/skin/classic/browser/newtab/close.png',
|
||||
'browser/chrome/browser/skin/classic/browser/theme-switcher-icon.png',
|
||||
# devtools reduction is bug 1311178
|
||||
'browser/chrome/devtools/content/dom/content/dom-view.css',
|
||||
'browser/chrome/devtools/content/dom/dom.html',
|
||||
'browser/chrome/devtools/content/dom/main.js',
|
||||
'browser/chrome/devtools/content/framework/toolbox-options.js',
|
||||
'browser/chrome/devtools/content/inspector/fonts/fonts.js',
|
||||
'browser/chrome/devtools/content/inspector/inspector.xhtml',
|
||||
'browser/chrome/devtools/content/memory/initializer.js',
|
||||
'browser/chrome/devtools/content/projecteditor/lib/helpers/readdir.js',
|
||||
'browser/chrome/devtools/content/shared/frame-script-utils.js',
|
||||
'browser/chrome/devtools/content/shared/theme-switching.js',
|
||||
'browser/chrome/devtools/modules/devtools/client/dom/content/dom-view.css',
|
||||
'browser/chrome/devtools/modules/devtools/client/dom/dom.html',
|
||||
'browser/chrome/devtools/modules/devtools/client/dom/main.js',
|
||||
'browser/chrome/devtools/modules/devtools/client/framework/toolbox-options.js',
|
||||
'browser/chrome/devtools/modules/devtools/client/inspector/fonts/fonts.js',
|
||||
'browser/chrome/devtools/modules/devtools/client/inspector/inspector.xhtml',
|
||||
'browser/chrome/devtools/modules/devtools/client/jsonview/css/controls.png',
|
||||
'browser/chrome/devtools/modules/devtools/client/jsonview/css/controls@2x.png',
|
||||
'browser/chrome/devtools/modules/devtools/client/memory/initializer.js',
|
||||
'browser/chrome/devtools/modules/devtools/client/projecteditor/lib/helpers/readdir.js',
|
||||
'browser/chrome/devtools/modules/devtools/client/shared/frame-script-utils.js',
|
||||
'browser/chrome/devtools/modules/devtools/client/shared/theme-switching.js',
|
||||
'browser/chrome/devtools/modules/devtools/client/themes/common.css',
|
||||
'browser/chrome/devtools/modules/devtools/client/themes/variables.css',
|
||||
'browser/chrome/devtools/skin/common.css',
|
||||
'browser/chrome/devtools/skin/images/command-scratchpad.svg',
|
||||
'browser/chrome/devtools/skin/images/controls.png',
|
||||
'browser/chrome/devtools/skin/images/controls@2x.png',
|
||||
'browser/chrome/devtools/skin/images/debugger-blackbox.svg',
|
||||
'browser/chrome/devtools/skin/images/debugger-prettyprint.svg',
|
||||
'browser/chrome/devtools/skin/images/filetypes/store.svg',
|
||||
'browser/chrome/devtools/skin/images/itemToggle.svg',
|
||||
'browser/chrome/devtools/skin/images/security-state-broken.svg',
|
||||
'browser/chrome/devtools/skin/images/security-state-local.svg',
|
||||
'browser/chrome/devtools/skin/images/security-state-secure.svg',
|
||||
'browser/chrome/devtools/skin/images/tabs-icon.svg',
|
||||
'browser/chrome/devtools/skin/images/tool-scratchpad.svg',
|
||||
'browser/chrome/devtools/skin/images/tool-storage.svg',
|
||||
'browser/chrome/devtools/skin/images/tool-styleeditor.svg',
|
||||
'browser/chrome/devtools/skin/promisedebugger.css',
|
||||
'browser/chrome/devtools/skin/variables.css',
|
||||
'modules/devtools/Console.jsm',
|
||||
'modules/devtools/Loader.jsm',
|
||||
'modules/devtools/Simulator.jsm',
|
||||
'modules/devtools/shared/Console.jsm',
|
||||
'modules/devtools/shared/Loader.jsm',
|
||||
'modules/devtools/shared/apps/Simulator.jsm',
|
||||
'browser/modules/devtools/client/framework/gDevTools.jsm',
|
||||
'browser/modules/devtools/gDevTools.jsm',
|
||||
'browser/chrome/icons/default/default16.png',
|
||||
'browser/chrome/icons/default/default32.png',
|
||||
'browser/chrome/icons/default/default48.png',
|
||||
'browser/chrome/pdfjs/content/web/images/findbarButton-next-rtl.png',
|
||||
'browser/chrome/pdfjs/content/web/images/findbarButton-next-rtl@2x.png',
|
||||
'browser/chrome/pdfjs/content/web/images/findbarButton-next.png',
|
||||
'browser/chrome/pdfjs/content/web/images/findbarButton-next@2x.png',
|
||||
'browser/chrome/pdfjs/content/web/images/findbarButton-previous-rtl.png',
|
||||
'browser/chrome/pdfjs/content/web/images/findbarButton-previous-rtl@2x.png',
|
||||
'browser/chrome/pdfjs/content/web/images/findbarButton-previous.png',
|
||||
'browser/chrome/pdfjs/content/web/images/findbarButton-previous@2x.png',
|
||||
'browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/icon.png',
|
||||
'browser/features/firefox@getpocket.com/chrome/skin/linux/menuPanel.png',
|
||||
'browser/features/firefox@getpocket.com/chrome/skin/linux/menuPanel@2x.png',
|
||||
'browser/features/firefox@getpocket.com/chrome/skin/windows/menuPanel.png',
|
||||
'browser/features/firefox@getpocket.com/chrome/skin/windows/menuPanel@2x.png',
|
||||
# flyweb reduction is bug 1313107
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/linux/flyweb.css',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/linux/icon-16.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/linux/icon-32-anchored.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/linux/icon-32.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/linux/icon-64-anchored.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/linux/icon-64.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/osx/flyweb.css',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/osx/icon-32-anchored.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/osx/icon-64-anchored.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/windows/flyweb.css',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/windows/icon-16.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/windows/icon-32-anchored.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/windows/icon-32.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/windows/icon-64-anchored.png',
|
||||
'browser/features/flyweb@mozilla.org/chrome/skin/windows/icon-64.png',
|
||||
'browser/icons/mozicon128.png',
|
||||
'chrome.manifest',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/AccessFu.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/about.dtd',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/aboutAbout.dtd',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/aboutReader.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/aboutRights.dtd',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/charsetMenu.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/commonDialogs.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/crashreporter/crashes.dtd',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/crashreporter/crashes.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/dom/dom.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/global.dtd',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/global/aboutSupport.dtd',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/global/aboutSupport.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/global/aboutTelemetry.dtd',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/global/aboutTelemetry.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/global/aboutWebrtc.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/global/mozilla.dtd',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/intl.css',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/intl.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/passwordmgr.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/plugins.properties',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/plugins/pluginproblem.dtd',
|
||||
'chrome/en-US/locale/en-US/browser/overrides/search/search.properties',
|
||||
'chrome/en-US/locale/en-US/global-platform/mac/intl.properties',
|
||||
'chrome/en-US/locale/en-US/global-platform/unix/accessible.properties',
|
||||
'chrome/en-US/locale/en-US/global-platform/unix/intl.properties',
|
||||
'chrome/en-US/locale/en-US/global-platform/unix/platformKeys.properties',
|
||||
'chrome/en-US/locale/en-US/global-platform/win/accessible.properties',
|
||||
'chrome/en-US/locale/en-US/global-platform/win/intl.properties',
|
||||
'chrome/en-US/locale/en-US/global-platform/win/platformKeys.properties',
|
||||
'chrome/en-US/locale/en-US/global/AccessFu.properties',
|
||||
'chrome/en-US/locale/en-US/global/about.dtd',
|
||||
'chrome/en-US/locale/en-US/global/aboutAbout.dtd',
|
||||
'chrome/en-US/locale/en-US/global/aboutReader.properties',
|
||||
'chrome/en-US/locale/en-US/global/aboutRights.dtd',
|
||||
'chrome/en-US/locale/en-US/global/aboutSupport.dtd',
|
||||
'chrome/en-US/locale/en-US/global/aboutSupport.properties',
|
||||
'chrome/en-US/locale/en-US/global/aboutTelemetry.dtd',
|
||||
'chrome/en-US/locale/en-US/global/aboutTelemetry.properties',
|
||||
'chrome/en-US/locale/en-US/global/aboutWebrtc.properties',
|
||||
'chrome/en-US/locale/en-US/global/charsetMenu.properties',
|
||||
'chrome/en-US/locale/en-US/global/commonDialogs.properties',
|
||||
'chrome/en-US/locale/en-US/global/crashes.dtd',
|
||||
'chrome/en-US/locale/en-US/global/crashes.properties',
|
||||
'chrome/en-US/locale/en-US/global/dom/dom.properties',
|
||||
'chrome/en-US/locale/en-US/global/global.dtd',
|
||||
'chrome/en-US/locale/en-US/global/intl.css',
|
||||
'chrome/en-US/locale/en-US/global/intl.properties',
|
||||
'chrome/en-US/locale/en-US/global/mozilla.dtd',
|
||||
'chrome/en-US/locale/en-US/global/plugins.properties',
|
||||
'chrome/en-US/locale/en-US/global/search/search.properties',
|
||||
'chrome/en-US/locale/en-US/passwordmgr/passwordmgr.properties',
|
||||
'chrome/en-US/locale/en-US/pluginproblem/pluginproblem.dtd',
|
||||
'chrome/toolkit/skin/classic/global/autocomplete.css',
|
||||
'chrome/toolkit/skin/classic/global/button.css',
|
||||
'chrome/toolkit/skin/classic/global/checkbox.css',
|
||||
'chrome/toolkit/skin/classic/global/dialog.css',
|
||||
'chrome/toolkit/skin/classic/global/dropmarker.css',
|
||||
'chrome/toolkit/skin/classic/global/global.css',
|
||||
'chrome/toolkit/skin/classic/global/groupbox.css',
|
||||
'chrome/toolkit/skin/classic/global/icons/close-XPVista7.png',
|
||||
'chrome/toolkit/skin/classic/global/icons/tabprompts-bgtexture.png',
|
||||
'chrome/toolkit/skin/classic/global/listbox.css',
|
||||
'chrome/toolkit/skin/classic/global/media/clicktoplay-bgtexture.png',
|
||||
'chrome/toolkit/skin/classic/global/menu.css',
|
||||
'chrome/toolkit/skin/classic/global/menulist.css',
|
||||
'chrome/toolkit/skin/classic/global/numberbox.css',
|
||||
'chrome/toolkit/skin/classic/global/popup.css',
|
||||
'chrome/toolkit/skin/classic/global/preferences.css',
|
||||
'chrome/toolkit/skin/classic/global/progressmeter.css',
|
||||
'chrome/toolkit/skin/classic/global/radio.css',
|
||||
'chrome/toolkit/skin/classic/global/resizer.css',
|
||||
'chrome/toolkit/skin/classic/global/richlistbox.css',
|
||||
'chrome/toolkit/skin/classic/global/scale.css',
|
||||
'chrome/toolkit/skin/classic/global/scrollbars.css',
|
||||
'chrome/toolkit/skin/classic/global/scrollbox.css',
|
||||
'chrome/toolkit/skin/classic/global/spinbuttons.css',
|
||||
'chrome/toolkit/skin/classic/global/splitter.css',
|
||||
'chrome/toolkit/skin/classic/global/tabbox.css',
|
||||
'chrome/toolkit/skin/classic/global/textbox.css',
|
||||
'chrome/toolkit/skin/classic/global/toolbar.css',
|
||||
'chrome/toolkit/skin/classic/global/toolbarbutton.css',
|
||||
'chrome/toolkit/skin/classic/global/tree.css',
|
||||
'chrome/toolkit/skin/classic/global/wizard.css',
|
||||
'chrome/toolkit/skin/classic/mozapps/downloads/buttons.png',
|
||||
'chrome/toolkit/skin/classic/mozapps/downloads/downloadButtons-XP.png',
|
||||
'chrome/toolkit/skin/classic/mozapps/downloads/downloadButtons.png',
|
||||
'chrome/toolkit/skin/classic/mozapps/extensions/category-dictionaries.png',
|
||||
'chrome/toolkit/skin/classic/mozapps/extensions/category-experiments.png',
|
||||
'chrome/toolkit/skin/classic/mozapps/extensions/dictionaryGeneric.png',
|
||||
'chrome/toolkit/skin/classic/mozapps/extensions/experimentGeneric.png',
|
||||
'chrome/toolkit/skin/classic/mozapps/update/buttons.png',
|
||||
'chrome/toolkit/skin/classic/mozapps/update/downloadButtons-XP.png',
|
||||
'chrome/toolkit/skin/classic/mozapps/update/downloadButtons.png',
|
||||
'components/FxAccountsPush.js',
|
||||
'crashreporter.app/Contents/Resources/English.lproj/MainMenu.nib/classes.nib',
|
||||
'crashreporter.app/Contents/Resources/English.lproj/MainMenuRTL.nib/classes.nib',
|
||||
# firefox/firefox-bin is bug 658850
|
||||
'firefox',
|
||||
'firefox-bin',
|
||||
'modules/FxAccountsPush.js',
|
||||
'modules/commonjs/index.js',
|
||||
'modules/commonjs/sdk/ui/button/view/events.js',
|
||||
'modules/commonjs/sdk/ui/state/events.js',
|
||||
'plugin-container.app/Contents/PkgInfo',
|
||||
'res/table-remove-column-active.gif',
|
||||
'res/table-remove-column-hover.gif',
|
||||
'res/table-remove-column.gif',
|
||||
'res/table-remove-row-active.gif',
|
||||
'res/table-remove-row-hover.gif',
|
||||
'res/table-remove-row.gif',
|
||||
])
|
||||
|
||||
|
||||
def normalize_osx_path(p):
|
||||
'''
|
||||
Strips the first 3 elements of an OSX app path
|
||||
|
||||
>>> normalize_osx_path('Nightly.app/foo/bar/baz')
|
||||
'baz'
|
||||
'''
|
||||
bits = p.split('/')
|
||||
if len(bits) > 3 and bits[0].endswith('.app'):
|
||||
return '/'.join(bits[3:])
|
||||
return p
|
||||
|
||||
|
||||
def find_dupes(source):
|
||||
md5s = OrderedDict()
|
||||
|
@ -29,6 +266,7 @@ def find_dupes(source):
|
|||
total = 0
|
||||
total_compressed = 0
|
||||
num_dupes = 0
|
||||
unexpected_dupes = []
|
||||
for m, (size, compressed, paths) in sorted(md5s.iteritems(),
|
||||
key=lambda x: x[1][1]):
|
||||
if len(paths) > 1:
|
||||
|
@ -39,12 +277,20 @@ def find_dupes(source):
|
|||
total += (len(paths) - 1) * size
|
||||
total_compressed += (len(paths) - 1) * compressed
|
||||
num_dupes += 1
|
||||
|
||||
unexpected_dupes.extend([p for p in paths if normalize_osx_path(p) not in ALLOWED_DUPES])
|
||||
|
||||
if num_dupes:
|
||||
print "WARNING: Found %d duplicated files taking %d bytes (%s)" % \
|
||||
(num_dupes, total,
|
||||
'%d compressed' % total_compressed if total_compressed != total
|
||||
else 'uncompressed')
|
||||
|
||||
if unexpected_dupes:
|
||||
print "ERROR: The following duplicated files are not allowed:"
|
||||
print "\n".join(unexpected_dupes)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
|
|
Загрузка…
Ссылка в новой задаче