merge autoland to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-10-27 16:42:35 +02:00
Родитель 97e4b18efa ac8b575853
Коммит 6e3860b156
46 изменённых файлов: 1087 добавлений и 142 удалений

Просмотреть файл

@ -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,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++) {

34
mfbt/tests/TestArray.cpp Normal file
Просмотреть файл

@ -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: