Merge m-c to b2ginbound a=merge

This commit is contained in:
Wes Kocher 2015-04-01 18:20:14 -07:00
commit 406c40e4e0
264 changed files with 7139 additions and 2564 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ TAGS
tags
ID
.DS_Store*
*.pdb
# Vim swap files.
.*.sw[a-z]

View File

@ -145,34 +145,33 @@
#endif
@RESPATH@/components/accessibility.xpt
#endif
@RESPATH@/components/appshell.xpt
@RESPATH@/components/appstartup.xpt
@RESPATH@/components/autocomplete.xpt
@RESPATH@/components/autoconfig.xpt
@RESPATH@/components/browsercompsbase.xpt
@RESPATH@/components/browser-element.xpt
@RESPATH@/components/browser-feeds.xpt
@RESPATH@/components/caps.xpt
@RESPATH@/components/chardet.xpt
@RESPATH@/components/chrome.xpt
@RESPATH@/components/commandhandler.xpt
@RESPATH@/components/commandlines.xpt
@RESPATH@/components/compartments.xpt
@RESPATH@/components/composer.xpt
@RESPATH@/components/content_events.xpt
@RESPATH@/components/content_html.xpt
@RESPATH@/components/content_xslt.xpt
@RESPATH@/components/cookie.xpt
@RESPATH@/components/devtools_security.xpt
@RESPATH@/components/directory.xpt
@RESPATH@/components/diskspacewatcher.xpt
@RESPATH@/components/docshell.xpt
@RESPATH@/components/dom.xpt
@RESPATH@/components/dom_activities.xpt
@RESPATH@/components/dom_apps.xpt
@RESPATH@/components/dom_audiochannel.xpt
@RESPATH@/components/dom_base.xpt
@RESPATH@/components/dom_system.xpt
@BINPATH@/components/appshell.xpt
@BINPATH@/components/appstartup.xpt
@BINPATH@/components/autocomplete.xpt
@BINPATH@/components/autoconfig.xpt
@BINPATH@/components/browsercompsbase.xpt
@BINPATH@/components/browser-element.xpt
@BINPATH@/components/browser-feeds.xpt
@BINPATH@/components/caps.xpt
@BINPATH@/components/chardet.xpt
@BINPATH@/components/chrome.xpt
@BINPATH@/components/commandhandler.xpt
@BINPATH@/components/commandlines.xpt
@BINPATH@/components/composer.xpt
@BINPATH@/components/content_events.xpt
@BINPATH@/components/content_html.xpt
@BINPATH@/components/content_xslt.xpt
@BINPATH@/components/cookie.xpt
@BINPATH@/components/devtools_security.xpt
@BINPATH@/components/directory.xpt
@BINPATH@/components/diskspacewatcher.xpt
@BINPATH@/components/docshell.xpt
@BINPATH@/components/dom.xpt
@BINPATH@/components/dom_activities.xpt
@BINPATH@/components/dom_apps.xpt
@BINPATH@/components/dom_audiochannel.xpt
@BINPATH@/components/dom_base.xpt
@BINPATH@/components/dom_system.xpt
#ifdef MOZ_WIDGET_GONK
@RESPATH@/components/dom_wifi.xpt
@RESPATH@/components/dom_system_gonk.xpt
@ -326,6 +325,7 @@
@RESPATH@/components/toolkit_finalizationwitness.xpt
@RESPATH@/components/toolkit_formautofill.xpt
@RESPATH@/components/toolkit_osfile.xpt
@RESPATH@/components/toolkit_perfmonitoring.xpt
@RESPATH@/components/toolkit_xulstore.xpt
@RESPATH@/components/toolkitprofile.xpt
#ifdef MOZ_ENABLE_XREMOTE

View File

@ -3779,8 +3779,7 @@
};
let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
let event = gContextMenuContentData.event;
let pos = browser.mapScreenCoordinatesFromContent(event.screenX, event.screenY);
popup.openPopupAtScreen(pos.x, pos.y, true);
popup.openPopupAtScreen(event.screenX, event.screenY, true);
break;
}
case "DOMWebNotificationClicked": {

View File

@ -327,7 +327,7 @@ function waitForEvents(event)
if (painted && loaded) {
subwindow.removeEventListener("MozAfterPaint", waitForEvents, false);
subwindow.onload = null;
startTest();
SimpleTest.waitForFocus(startTest, subwindow);
}
}

View File

@ -2,6 +2,7 @@
let AddonManager = Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager;
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
let AddonWatcher = Cu.import("resource://gre/modules/AddonWatcher.jsm", {}).AddonWatcher;
const ADDON_TYPE_SERVICE = "service";
const ID_SUFFIX = "@services.mozilla.org";

View File

@ -972,11 +972,14 @@ html, .fx-embedded, #main,
max-width: 400px;
}
.standalone .room-conversation h2.room-name {
.standalone .room-conversation h2.room-name,
.standalone .room-conversation h2.room-info-failure {
position: absolute;
display: inline-block;
top: 0;
right: 0;
right: 10px;
/* 20px is 10px for left and right margins. */
width: calc(25% - 20px);
color: #fff;
z-index: 2000000;
font-size: 1.2em;

View File

@ -43,7 +43,8 @@ loop.shared.actions = (function() {
* Extract the token information and type for the standalone window
*/
ExtractTokenInfo: Action.define("extractTokenInfo", {
windowPath: String
windowPath: String,
windowHash: String
}),
/**
@ -65,6 +66,7 @@ loop.shared.actions = (function() {
* token.
*/
FetchServerData: Action.define("fetchServerData", {
// cryptoKey: String - Optional.
token: String,
windowType: String
}),
@ -386,6 +388,7 @@ loop.shared.actions = (function() {
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
*/
UpdateRoomInfo: Action.define("updateRoomInfo", {
// context: Object - Optional.
// roomName: String - Optional.
roomOwner: String,
roomUrl: String

View File

@ -11,6 +11,7 @@ loop.store.ActiveRoomStore = (function() {
"use strict";
var sharedActions = loop.shared.actions;
var crypto = loop.crypto;
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
@ -20,6 +21,8 @@ loop.store.ActiveRoomStore = (function() {
var ROOM_STATES = loop.store.ROOM_STATES;
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
/**
* Active room store.
*
@ -76,7 +79,11 @@ loop.store.ActiveRoomStore = (function() {
localVideoDimensions: {},
remoteVideoDimensions: {},
screenSharingState: SCREEN_SHARE_STATES.INACTIVE,
receivingScreenShare: false
receivingScreenShare: false,
// The roomCryptoKey to decode the context data if necessary.
roomCryptoKey: null,
// Room information failed to be obtained for a reason. See ROOM_INFO_FAILURES.
roomInfoFailure: null
};
},
@ -199,6 +206,7 @@ loop.store.ActiveRoomStore = (function() {
this.setStoreState({
roomToken: actionData.token,
roomCryptoKey: actionData.cryptoKey,
roomState: ROOM_STATES.READY
});
@ -207,17 +215,64 @@ loop.store.ActiveRoomStore = (function() {
this._mozLoop.rooms.on("delete:" + actionData.roomToken,
this._handleRoomDelete.bind(this));
this._mozLoop.rooms.get(this._storeState.roomToken,
function(err, result) {
if (err) {
// XXX Bug 1110937 will want to handle the error results here
// e.g. room expired/invalid.
console.error("Failed to get room data:", err);
return;
}
this._getRoomDataForStandalone();
},
this.dispatcher.dispatch(new sharedActions.UpdateRoomInfo(result));
}.bind(this));
_getRoomDataForStandalone: function() {
this._mozLoop.rooms.get(this._storeState.roomToken, function(err, result) {
if (err) {
// XXX Bug 1110937 will want to handle the error results here
// e.g. room expired/invalid.
console.error("Failed to get room data:", err);
return;
}
var roomInfoData = new sharedActions.UpdateRoomInfo({
roomOwner: result.roomOwner,
roomUrl: result.roomUrl
});
if (!result.context && !result.roomName) {
roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.NO_DATA;
this.dispatcher.dispatch(roomInfoData);
return;
}
// This handles 'legacy', non-encrypted room names.
if (result.roomName && !result.context) {
roomInfoData.roomName = result.roomName;
this.dispatcher.dispatch(roomInfoData);
return;
}
if (!crypto.isSupported()) {
roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED;
this.dispatcher.dispatch(roomInfoData);
return;
}
var roomCryptoKey = this.getStoreState("roomCryptoKey");
if (!roomCryptoKey) {
roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.NO_CRYPTO_KEY;
this.dispatcher.dispatch(roomInfoData);
return;
}
var dispatcher = this.dispatcher;
crypto.decryptBytes(roomCryptoKey, result.context.value)
.then(function(decryptedResult) {
var realResult = JSON.parse(decryptedResult);
roomInfoData.roomName = realResult.roomName;
dispatcher.dispatch(roomInfoData);
}, function(err) {
roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.DECRYPT_FAILED;
dispatcher.dispatch(roomInfoData);
});
}.bind(this));
},
/**
@ -254,6 +309,7 @@ loop.store.ActiveRoomStore = (function() {
*/
updateRoomInfo: function(actionData) {
this.setStoreState({
roomInfoFailure: actionData.roomInfoFailure,
roomName: actionData.roomName,
roomOwner: actionData.roomOwner,
roomUrl: actionData.roomUrl

View File

@ -43,6 +43,17 @@ loop.shared.utils = (function(mozL10n) {
UNKNOWN: "reason-unknown"
};
var ROOM_INFO_FAILURES = {
// There's no data available from the server.
NO_DATA: "no_data",
// WebCrypto is unsupported in this browser.
WEB_CRYPTO_UNSUPPORTED: "web_crypto_unsupported",
// The room is missing the crypto key information.
NO_CRYPTO_KEY: "no_crypto_key",
// Decryption failed.
DECRYPT_FAILED: "decrypt_failed"
};
var STREAM_PROPERTIES = {
VIDEO_DIMENSIONS: "videoDimensions",
HAS_AUDIO: "hasAudio",
@ -397,6 +408,7 @@ loop.shared.utils = (function(mozL10n) {
WEBSOCKET_REASONS: WEBSOCKET_REASONS,
STREAM_PROPERTIES: STREAM_PROPERTIES,
SCREEN_SHARE_STATES: SCREEN_SHARE_STATES,
ROOM_INFO_FAILURES: ROOM_INFO_FAILURES,
composeCallUrlEmail: composeCallUrlEmail,
formatDate: formatDate,
getBoolPreference: getBoolPreference,

View File

@ -108,6 +108,7 @@
<!-- app scripts -->
<script type="text/javascript" src="config.js"></script>
<script type="text/javascript" src="shared/js/utils.js"></script>
<script type="text/javascript" src="shared/js/crypto.js"></script>
<script type="text/javascript" src="shared/js/models.js"></script>
<script type="text/javascript" src="shared/js/mixins.js"></script>
<script type="text/javascript" src="shared/js/feedbackApiClient.js"></script>

View File

@ -95,9 +95,21 @@ loop.store.StandaloneAppStore = (function() {
return [windowType, match && match[1] ? match[1] : null];
},
/**
* Extracts the crypto key from the hash for the page.
*/
_extractCryptoKey: function(windowHash) {
if (windowHash && windowHash[0] === "#") {
return windowHash.substring(1, windowHash.length);
}
return null;
},
/**
* Handles the extract token info action - obtains the token information
* and its type; updates the store and notifies interested components.
* and its type; extracts any crypto information; updates the store and
* notifies interested components.
*
* @param {sharedActions.GetWindowData} actionData The action data
*/
@ -135,6 +147,7 @@ loop.store.StandaloneAppStore = (function() {
// it.
if (token) {
this._dispatcher.dispatch(new loop.shared.actions.FetchServerData({
cryptoKey: this._extractCryptoKey(actionData.windowHash),
token: token,
windowType: windowType
}));

View File

@ -12,6 +12,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
"use strict";
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
var ROOM_STATES = loop.store.ROOM_STATES;
var sharedActions = loop.shared.actions;
var sharedMixins = loop.shared.mixins;
@ -198,6 +199,29 @@ loop.standaloneRoomViews = (function(mozL10n) {
}
});
var StandaloneRoomContextView = React.createClass({displayName: "StandaloneRoomContextView",
propTypes: {
roomName: React.PropTypes.string,
roomInfoFailure: React.PropTypes.string
},
render: function() {
if (this.props.roomInfoFailure === ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED) {
return (React.createElement("h2", {className: "room-info-failure"},
mozL10n.get("room_information_failure_unsupported_browser")
));
} else if (this.props.roomInfoFailure) {
return (React.createElement("h2", {className: "room-info-failure"},
mozL10n.get("room_information_failure_not_available")
));
}
return (
React.createElement("h2", {className: "room-name"}, this.props.roomName)
);
}
});
var StandaloneRoomView = React.createClass({displayName: "StandaloneRoomView",
mixins: [
Backbone.Events,
@ -458,7 +482,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
roomUsed: this.state.used}),
React.createElement("div", {className: "video-layout-wrapper"},
React.createElement("div", {className: "conversation room-conversation"},
React.createElement("h2", {className: "room-name"}, this.state.roomName),
React.createElement(StandaloneRoomContextView, {roomName: this.state.roomName,
roomInfoFailure: this.state.roomInfoFailure}),
React.createElement("div", {className: "media nested"},
React.createElement("span", {className: "self-view-hidden-message"},
mozL10n.get("self_view_hidden_message")
@ -491,6 +516,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
});
return {
StandaloneRoomContextView: StandaloneRoomContextView,
StandaloneRoomView: StandaloneRoomView
};
})(navigator.mozL10n);

View File

@ -12,6 +12,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
"use strict";
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
var ROOM_STATES = loop.store.ROOM_STATES;
var sharedActions = loop.shared.actions;
var sharedMixins = loop.shared.mixins;
@ -198,6 +199,29 @@ loop.standaloneRoomViews = (function(mozL10n) {
}
});
var StandaloneRoomContextView = React.createClass({
propTypes: {
roomName: React.PropTypes.string,
roomInfoFailure: React.PropTypes.string
},
render: function() {
if (this.props.roomInfoFailure === ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED) {
return (<h2 className="room-info-failure">
{mozL10n.get("room_information_failure_unsupported_browser")}
</h2>);
} else if (this.props.roomInfoFailure) {
return (<h2 className="room-info-failure">
{mozL10n.get("room_information_failure_not_available")}
</h2>);
}
return (
<h2 className="room-name">{this.props.roomName}</h2>
);
}
});
var StandaloneRoomView = React.createClass({
mixins: [
Backbone.Events,
@ -458,7 +482,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
roomUsed={this.state.used} />
<div className="video-layout-wrapper">
<div className="conversation room-conversation">
<h2 className="room-name">{this.state.roomName}</h2>
<StandaloneRoomContextView roomName={this.state.roomName}
roomInfoFailure={this.state.roomInfoFailure} />
<div className="media nested">
<span className="self-view-hidden-message">
{mozL10n.get("self_view_hidden_message")}
@ -491,6 +516,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
});
return {
StandaloneRoomContextView: StandaloneRoomContextView,
StandaloneRoomView: StandaloneRoomView
};
})(navigator.mozL10n);

View File

@ -1109,7 +1109,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
var locationData = sharedUtils.locationData();
dispatcher.dispatch(new sharedActions.ExtractTokenInfo({
windowPath: locationData.pathname
windowPath: locationData.pathname,
windowHash: locationData.hash
}));
}

View File

@ -1109,7 +1109,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
var locationData = sharedUtils.locationData();
dispatcher.dispatch(new sharedActions.ExtractTokenInfo({
windowPath: locationData.pathname
windowPath: locationData.pathname,
windowHash: locationData.hash
}));
}

View File

@ -127,6 +127,8 @@ rooms_room_join_label=Join the conversation
rooms_display_name_guest=Guest
rooms_unavailable_notification_message=Sorry, you cannot join this conversation. The link may be expired or invalid.
rooms_media_denied_message=We could not get access to your microphone or camera. Please reload the page to try again.
room_information_failure_not_available=No information about this conversation is available. Please request a new link from the person who sent it to you.
room_information_failure_unsupported_browser=Your browser cannot access any information about this conversation. Please make sure you're using the latest version.
## LOCALIZATION_NOTE(standalone_title_with_status): {{clientShortname}} will be
## replaced by the brand name and {{currentStatus}} will be replaced

View File

@ -10,6 +10,7 @@ describe("loop.store.ActiveRoomStore", function () {
var ROOM_STATES = loop.store.ROOM_STATES;
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver;
var fakeMultiplexGum;
@ -347,20 +348,127 @@ describe("loop.store.ActiveRoomStore", function () {
sinon.assert.calledOnce(fakeMozLoop.rooms.get);
});
it("should dispatch UpdateRoomInfo if mozLoop.rooms.get is successful", function() {
var roomDetails = {
roomName: "fakeName",
roomUrl: "http://invalid",
roomOwner: "gavin"
};
fakeMozLoop.rooms.get.callsArgWith(1, null, roomDetails);
it("should dispatch an UpdateRoomInfo message with 'no data' failure if neither roomName nor context are supplied", function() {
fakeMozLoop.rooms.get.callsArgWith(1, null, {
roomOwner: "Dan",
roomUrl: "http://invalid"
});
store.fetchServerData(fetchServerAction);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.UpdateRoomInfo(roomDetails));
new sharedActions.UpdateRoomInfo({
roomInfoFailure: ROOM_INFO_FAILURES.NO_DATA,
roomOwner: "Dan",
roomUrl: "http://invalid"
}));
});
describe("mozLoop.rooms.get returns roomName as a separate field (no context)", function() {
it("should dispatch UpdateRoomInfo if mozLoop.rooms.get is successful", function() {
var roomDetails = {
roomName: "fakeName",
roomUrl: "http://invalid",
roomOwner: "gavin"
};
fakeMozLoop.rooms.get.callsArgWith(1, null, roomDetails);
store.fetchServerData(fetchServerAction);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.UpdateRoomInfo(roomDetails));
});
});
describe("mozLoop.rooms.get returns encryptedContext", function() {
var roomDetails, expectedDetails;
beforeEach(function() {
roomDetails = {
context: {
value: "fakeContext"
},
roomUrl: "http://invalid",
roomOwner: "Mark"
};
expectedDetails = {
roomUrl: "http://invalid",
roomOwner: "Mark"
};
fakeMozLoop.rooms.get.callsArgWith(1, null, roomDetails);
sandbox.stub(loop.crypto, "isSupported").returns(true);
});
it("should dispatch UpdateRoomInfo message with 'unsupported' failure if WebCrypto is unsupported", function() {
loop.crypto.isSupported.returns(false);
store.fetchServerData(fetchServerAction);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.UpdateRoomInfo(_.extend({
roomInfoFailure: ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED
}, expectedDetails)));
});
it("should dispatch UpdateRoomInfo message with 'no crypto key' failure if there is no crypto key", function() {
store.fetchServerData(fetchServerAction);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.UpdateRoomInfo(_.extend({
roomInfoFailure: ROOM_INFO_FAILURES.NO_CRYPTO_KEY
}, expectedDetails)));
});
it("should dispatch UpdateRoomInfo message with 'decrypt failed' failure if decryption failed", function() {
fetchServerAction.cryptoKey = "fakeKey";
// This is a work around to turn promise into a sync action to make handling test failures
// easier.
sandbox.stub(loop.crypto, "decryptBytes", function() {
return {
then: function(resolve, reject) {
reject(new Error("Operation unsupported"));
}
};
});
store.fetchServerData(fetchServerAction);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.UpdateRoomInfo(_.extend({
roomInfoFailure: ROOM_INFO_FAILURES.DECRYPT_FAILED
}, expectedDetails)));
});
it("should dispatch UpdateRoomInfo message with the room name if decryption was successful", function() {
fetchServerAction.cryptoKey = "fakeKey";
// This is a work around to turn promise into a sync action to make handling test failures
// easier.
sandbox.stub(loop.crypto, "decryptBytes", function() {
return {
then: function(resolve, reject) {
resolve(JSON.stringify({roomName: "The wonderful Loopy room"}));
}
};
});
store.fetchServerData(fetchServerAction);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.UpdateRoomInfo(_.extend({
roomName: "The wonderful Loopy room"
}, expectedDetails)));
});
});
});

View File

@ -54,7 +54,8 @@ describe("loop.store.StandaloneAppStore", function () {
beforeEach(function() {
fakeGetWindowData = {
windowPath: ""
windowPath: "",
windowHash: ""
};
sandbox.stub(loop.shared.utils, "getUnsupportedPlatform").returns();
@ -177,7 +178,7 @@ describe("loop.store.StandaloneAppStore", function () {
});
});
it("should set the loopToken on the conversation for call paths",
it("should dispatch a FetchServerData action for call paths",
function() {
fakeGetWindowData.windowPath = "/c/fakecalltoken";
@ -187,14 +188,15 @@ describe("loop.store.StandaloneAppStore", function () {
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.FetchServerData({
cryptoKey: null,
windowType: "outgoing",
token: "fakecalltoken"
}));
});
it("should set the loopToken on the conversation for room paths",
it("should dispatch a FetchServerData action for room paths",
function() {
fakeGetWindowData.windowPath = "/c/fakeroomtoken";
fakeGetWindowData.windowPath = "/fakeroomtoken";
store.extractTokenInfo(
new sharedActions.ExtractTokenInfo(fakeGetWindowData));
@ -202,11 +204,29 @@ describe("loop.store.StandaloneAppStore", function () {
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.FetchServerData({
windowType: "outgoing",
cryptoKey: null,
windowType: "room",
token: "fakeroomtoken"
}));
});
it("should dispatch a FetchServerData action with a crypto key extracted from the hash", function() {
fakeGetWindowData = {
windowPath: "/fakeroomtoken",
windowHash: "#fakeKey"
};
store.extractTokenInfo(
new sharedActions.ExtractTokenInfo(fakeGetWindowData));
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.FetchServerData({
cryptoKey: "fakeKey",
windowType: "room",
token: "fakeroomtoken"
}));
});
});
});

View File

@ -11,6 +11,7 @@ describe("loop.standaloneRoomViews", function() {
var ROOM_STATES = loop.store.ROOM_STATES;
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
var sharedActions = loop.shared.actions;
var sandbox, dispatcher, activeRoomStore, feedbackStore, dispatch;
@ -38,6 +39,44 @@ describe("loop.standaloneRoomViews", function() {
sandbox.restore();
});
describe("StandaloneRoomContextView", function() {
beforeEach(function() {
sandbox.stub(navigator.mozL10n, "get").returnsArg(0);
});
function mountTestComponent(props) {
return TestUtils.renderIntoDocument(
React.createElement(
loop.standaloneRoomViews.StandaloneRoomContextView, props));
}
it("should display the room name if no failures are known", function() {
var view = mountTestComponent({
roomName: "Mike's room"
});
expect(view.getDOMNode().textContent).eql("Mike's room");
});
it("should display an unsupported browser message if crypto is unsupported", function() {
var view = mountTestComponent({
roomName: "Mark's room",
roomInfoFailure: ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED
});
expect(view.getDOMNode().textContent).match(/unsupported/);
});
it("should display a general error message for any other failure", function() {
var view = mountTestComponent({
roomName: "Mark's room",
roomInfoFailure: ROOM_INFO_FAILURES.NO_DATA
});
expect(view.getDOMNode().textContent).match(/not_available/);
});
});
describe("StandaloneRoomView", function() {
function mountTestComponent() {
return TestUtils.renderIntoDocument(

View File

@ -71,10 +71,10 @@ describe("loop.webapp", function() {
}));
});
it("should dispatch a ExtractTokenInfo action with the path",
it("should dispatch a ExtractTokenInfo action with the path and hash",
function() {
sandbox.stub(loop.shared.utils, "locationData").returns({
hash: "",
hash: "#fakeKey",
pathname: "/c/faketoken"
});
@ -83,7 +83,8 @@ describe("loop.webapp", function() {
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
new sharedActions.ExtractTokenInfo({
windowPath: "/c/faketoken"
windowPath: "/c/faketoken",
windowHash: "#fakeKey"
}));
});
});

View File

@ -233,15 +233,17 @@ ReadingListImpl.prototype = {
* an Error on error.
*/
forEachItem: Task.async(function* (callback, ...optsList) {
yield this._forEachItem(callback, optsList, STORE_OPTIONS_IGNORE_DELETED);
let thisCallback = record => callback(this._itemFromRecord(record));
yield this._forEachRecord(thisCallback, optsList, STORE_OPTIONS_IGNORE_DELETED);
}),
/**
* Like forEachItem, but enumerates only previously synced items that are
* marked as being locally deleted.
* Enumerates the GUIDs for previously synced items that are marked as being
* locally deleted.
*/
forEachSyncedDeletedItem: Task.async(function* (callback, ...optsList) {
yield this._forEachItem(callback, optsList, {
forEachSyncedDeletedGUID: Task.async(function* (callback, ...optsList) {
let thisCallback = record => callback(record.guid);
yield this._forEachRecord(thisCallback, optsList, {
syncStatus: SYNC_STATUS_DELETED,
});
}),
@ -252,12 +254,12 @@ ReadingListImpl.prototype = {
* @param storeOptions An options object passed to the store as the "control"
* options.
*/
_forEachItem: Task.async(function* (callback, optsList, storeOptions) {
_forEachRecord: Task.async(function* (callback, optsList, storeOptions) {
let promiseChain = Promise.resolve();
yield this._store.forEachItem(record => {
promiseChain = promiseChain.then(() => {
return new Promise((resolve, reject) => {
let promise = callback(this._itemFromRecord(record));
let promise = callback(record);
if (promise instanceof Promise) {
return promise.then(resolve, reject);
}
@ -317,7 +319,9 @@ ReadingListImpl.prototype = {
record.syncStatus = SYNC_STATUS_NEW;
}
log.debug("addingItem with guid: ${guid}, url: ${url}", record);
yield this._store.addItem(record);
log.trace("added item with guid: ${guid}, url: ${url}", record);
this._invalidateIterators();
let item = this._itemFromRecord(record);
this._callListeners("onItemAdded", item);
@ -345,7 +349,9 @@ ReadingListImpl.prototype = {
throw new Error("The item must have a url");
}
this._ensureItemBelongsToList(item);
log.debug("updatingItem with guid: ${guid}, url: ${url}", item._record);
yield this._store.updateItem(item._record);
log.trace("finished update of item guid: ${guid}, url: ${url}", item._record);
this._invalidateIterators();
this._callListeners("onItemUpdated", item);
}),
@ -367,6 +373,7 @@ ReadingListImpl.prototype = {
// the store. Otherwise mark it as deleted but don't actually delete it so
// that its status can be synced.
if (item._record.syncStatus == SYNC_STATUS_NEW) {
log.debug("deleteItem guid: ${guid}, url: ${url} - item is local so really deleting it", item._record);
yield this._store.deleteItemByURL(item.url);
}
else {
@ -378,12 +385,16 @@ ReadingListImpl.prototype = {
}
newRecord.guid = item._record.guid;
newRecord.syncStatus = SYNC_STATUS_DELETED;
item._record = newRecord;
yield this._store.updateItemByGUID(item._record);
log.debug("deleteItem guid: ${guid}, url: ${url} - item has been synced so updating to deleted state", item._record);
yield this._store.updateItemByGUID(newRecord);
}
log.trace("finished db operation deleting item with guid: ${guid}, url: ${url}", item._record);
item.list = null;
this._itemsByNormalizedURL.delete(item.url);
// failing to remove the item from the map points at something bad!
if (!this._itemsByNormalizedURL.delete(item.url)) {
log.error("Failed to remove item from the map", item);
}
this._invalidateIterators();
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
mm.broadcastAsyncMessage("Reader:Removed", item);
@ -514,6 +525,9 @@ ReadingListImpl.prototype = {
* @return The ReadingListItem.
*/
_itemFromRecord(record) {
if (!record.url) {
throw new Error("record must have a URL");
}
let itemWeakRef = this._itemsByNormalizedURL.get(record.url);
let item = itemWeakRef ? itemWeakRef.get() : null;
if (item) {

View File

@ -296,9 +296,9 @@ SyncImpl.prototype = {
// Get deleted synced local items.
let requests = [];
yield this.list.forEachSyncedDeletedItem(localItem => {
yield this.list.forEachSyncedDeletedGUID(guid => {
requests.push({
path: "/articles/" + localItem.guid,
path: "/articles/" + guid,
});
});
if (!requests.length) {

View File

@ -118,10 +118,12 @@ let RLSidebar = {
log.trace(`onItemDeleted: ${item}`);
let itemNode = this.itemNodesById.get(item.id);
this.itemNodesById.delete(item.id);
this.itemsById.delete(item.id);
itemNode.addEventListener('transitionend', (event) => {
if (event.propertyName == "max-height") {
this.itemNodesById.delete(item.id);
this.itemsById.delete(item.id);
itemNode.remove();
// TODO: ensureListItems doesn't yet cope with needing to add one item.

View File

@ -9,6 +9,10 @@ Cu.import("resource:///modules/readinglist/ReadingList.jsm");
Cu.import("resource:///modules/readinglist/SQLiteStore.jsm");
Cu.import("resource://gre/modules/Sqlite.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/Log.jsm");
Log.repository.getLogger("readinglist.api").level = Log.Level.All;
Log.repository.getLogger("readinglist.api").addAppender(new Log.DumpAppender());
var gList;
var gItems;
@ -294,10 +298,10 @@ add_task(function* forEachSyncedDeletedItem() {
});
deletedItem._record.syncStatus = gList.SyncStatus.SYNCED;
yield gList.deleteItem(deletedItem);
let items = [];
yield gList.forEachSyncedDeletedItem(item => items.push(item));
Assert.equal(items.length, 1);
Assert.equal(items[0].guid, deletedItem.guid);
let guids = [];
yield gList.forEachSyncedDeletedGUID(guid => guids.push(guid));
Assert.equal(guids.length, 1);
Assert.equal(guids[0], deletedItem.guid);
});
add_task(function* forEachItem_promises() {
@ -654,7 +658,7 @@ add_task(function* listeners() {
Assert.equal((yield gList.count()), gItems.length);
});
// This test deletes items so it should probably run last.
// This test deletes items so it should probably run last of the 'gItems' tests...
add_task(function* deleteItem() {
// delete first item with item.delete()
let iter = gList.iterator({
@ -692,6 +696,28 @@ add_task(function* deleteItem() {
checkItems(items, gItems.slice(3));
});
// Check that when we delete an item with a GUID it's no longer available as
// an item
add_task(function* deletedItemRemovedFromMap() {
yield gList.forEachItem(item => item.delete());
Assert.equal((yield gList.count()), 0);
let map = gList._itemsByNormalizedURL;
Assert.equal(gList._itemsByNormalizedURL.size, 0, [for (i of map.keys()) i]);
let record = {
guid: "test-item",
url: "http://localhost",
syncStatus: gList.SyncStatus.SYNCED,
}
let item = yield gList.addItem(record);
Assert.equal(map.size, 1);
yield item.delete();
Assert.equal(gList._itemsByNormalizedURL.size, 0, [for (i of map.keys()) i]);
// Now enumerate deleted items - should not come back.
yield gList.forEachSyncedDeletedGUID(() => {});
Assert.equal(gList._itemsByNormalizedURL.size, 0, [for (i of map.keys()) i]);
});
function checkItems(actualItems, expectedItems) {
Assert.equal(actualItems.length, expectedItems.length);
for (let i = 0; i < expectedItems.length; i++) {

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_frame_script.js

View File

@ -279,7 +279,36 @@ let togglePlayPauseButton = Task.async(function*(widget) {
yield onClicked;
// Wait for the next sate change event to make sure the state is updated
yield widget.player.once(widget.player.AUTO_REFRESH_EVENT);
yield waitForStateCondition(widget.player, state => {
return state.playState === nextState;
}, "after clicking the toggle button");
});
/**
* Wait for a player's auto-refresh events and stop when a condition becomes
* truthy.
* @param {AnimationPlayerFront} player
* @param {Function} conditionCheck Will be called over and over again when the
* player state changes, passing the state as argument. This method must return
* a truthy value to stop waiting.
* @param {String} desc If provided, this will be logged with info(...) every
* time the state is refreshed, until the condition passes.
* @return {Promise} Resolves when the condition passes.
*/
let waitForStateCondition = Task.async(function*(player, conditionCheck, desc="") {
if (desc) {
desc = "(" + desc + ")";
}
info("Waiting for a player's auto-refresh event " + desc);
let def = promise.defer();
player.on(player.AUTO_REFRESH_EVENT, function onNewState() {
info("State refreshed, checking condition ... " + desc);
if (conditionCheck(player.state)) {
player.off(player.AUTO_REFRESH_EVENT, onNewState);
def.resolve();
}
});
return def.promise;
});
/**

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
subsuite = devtools
support-files =

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
skip-if = buildapp == 'b2g'
support-files =
hosted_app.manifest

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_raf-begin.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
subsuite = devtools
support-files =

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
addon1.xpi

View File

@ -21,7 +21,7 @@ function test() {
const BP_LOCATION = {
line: 5,
column: 11
// column: 0
};
function findSource() {

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
color-block.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
browser_fontinspector.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
browser_toolbox_options_disable_js.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_frame_script.js

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
subsuite = devtools
support-files =

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_markup_anonymous.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js

View File

@ -60,6 +60,9 @@ const EVENTS = {
// Fired by the PerformanceController and OptionsView when a pref changes.
PREF_CHANGED: "Performance:PrefChanged",
// Fired by the PerformanceController when the devtools theme changes.
THEME_CHANGED: "Performance:ThemeChanged",
// Emitted by the PerformanceView when the state (display mode) changes,
// for example when switching between "empty", "recording" or "recorded".
// This causes certain panels to be hidden or visible.
@ -177,6 +180,7 @@ let PerformanceController = {
this._onTimelineData = this._onTimelineData.bind(this);
this._onRecordingSelectFromView = this._onRecordingSelectFromView.bind(this);
this._onPrefChanged = this._onPrefChanged.bind(this);
this._onThemeChanged = this._onThemeChanged.bind(this);
// All boolean prefs should be handled via the OptionsView in the
// ToolbarView, so that they may be accessible via the "gear" menu.
@ -198,6 +202,7 @@ let PerformanceController = {
RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
RecordingsView.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
gDevTools.on("pref-changed", this._onThemeChanged);
gFront.on("markers", this._onTimelineData); // timeline markers
gFront.on("frames", this._onTimelineData); // stack frames
gFront.on("memory", this._onTimelineData); // memory measurements
@ -220,6 +225,7 @@ let PerformanceController = {
RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
RecordingsView.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
gDevTools.off("pref-changed", this._onThemeChanged);
gFront.off("markers", this._onTimelineData);
gFront.off("frames", this._onTimelineData);
gFront.off("memory", this._onTimelineData);
@ -227,6 +233,13 @@ let PerformanceController = {
gFront.off("allocations", this._onTimelineData);
},
/**
* Returns the current devtools theme.
*/
getTheme: function () {
return Services.prefs.getCharPref("devtools.theme");
},
/**
* Get a boolean preference setting from `prefName` via the underlying
* OptionsView in the ToolbarView.
@ -414,6 +427,19 @@ let PerformanceController = {
this.emit(EVENTS.PREF_CHANGED, prefName, prefValue);
},
/*
* Called when the developer tools theme changes.
*/
_onThemeChanged: function (_, data) {
// Right now, gDevTools only emits `pref-changed` for the theme,
// but this could change in the future.
if (data.pref !== "devtools.theme") {
return;
}
this.emit(EVENTS.THEME_CHANGED, data.newValue);
},
toString: () => "[object PerformanceController]"
};

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
skip-if = e10s # Handle in Bug 1077464 for profiler
subsuite = devtools
support-files =
@ -81,6 +82,7 @@ support-files =
[browser_perf-recording-selected-02.js]
[browser_perf-recording-selected-03.js]
[browser_perf-recording-selected-04.js]
[browser_perf-theme-toggle-01.js]
[browser_profiler_categories.js]
[browser_profiler_content-check.js]
[browser_profiler_tree-abstract-01.js]

View File

@ -0,0 +1,86 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the markers and memory overviews render with the correct
* theme on load, and rerenders when changed.
*/
const LIGHT_BG = "#fcfcfc";
const DARK_BG = "#14171a";
setTheme("dark");
Services.prefs.setBoolPref(MEMORY_PREF, false);
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, OverviewView, document: doc } = panel.panelWin;
yield startRecording(panel);
is(OverviewView.markersOverview.backgroundColor, DARK_BG,
"correct theme on load for markers.");
yield stopRecording(panel);
let refreshed = once(OverviewView.markersOverview, "refresh");
setTheme("light");
yield refreshed;
ok(true, "markers were rerendered after theme change.");
is(OverviewView.markersOverview.backgroundColor, LIGHT_BG,
"correct theme on after toggle for markers.");
// reset back to dark
refreshed = once(OverviewView.markersOverview, "refresh");
setTheme("dark");
yield refreshed;
info("Testing with memory overview");
Services.prefs.setBoolPref(MEMORY_PREF, true);
yield startRecording(panel);
is(OverviewView.memoryOverview.backgroundColor, DARK_BG,
"correct theme on load for memory.");
yield stopRecording(panel);
refreshed = Promise.all([
once(OverviewView.markersOverview, "refresh"),
once(OverviewView.memoryOverview, "refresh"),
]);
setTheme("light");
yield refreshed;
ok(true, "Both memory and markers were rerendered after theme change.");
is(OverviewView.markersOverview.backgroundColor, LIGHT_BG,
"correct theme on after toggle for markers.");
is(OverviewView.memoryOverview.backgroundColor, LIGHT_BG,
"correct theme on after toggle for memory.");
refreshed = Promise.all([
once(OverviewView.markersOverview, "refresh"),
once(OverviewView.memoryOverview, "refresh"),
]);
// Set theme back to light
setTheme("light");
yield refreshed;
yield teardown(panel);
finish();
}
/**
* Mimics selecting the theme selector in the toolbox;
* sets the preference and emits an event on gDevTools to trigger
* the themeing.
*/
function setTheme (newTheme) {
let oldTheme = Services.prefs.getCharPref("devtools.theme");
info("Setting `devtools.theme` to \"" + newTheme + "\"");
Services.prefs.setCharPref("devtools.theme", newTheme);
gDevTools.emit("pref-changed", {
pref: "devtools.theme",
newValue: newTheme,
oldValue: oldTheme
});
}

View File

@ -37,6 +37,7 @@ let OverviewView = {
this._onRecordingTick = this._onRecordingTick.bind(this);
this._onGraphSelecting = this._onGraphSelecting.bind(this);
this._onPrefChanged = this._onPrefChanged.bind(this);
this._onThemeChanged = this._onThemeChanged.bind(this);
// Toggle the initial visibility of memory and framerate graph containers
// based off of prefs.
@ -44,6 +45,7 @@ let OverviewView = {
$("#time-framerate").hidden = !PerformanceController.getOption("enable-framerate");
PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged);
PerformanceController.on(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.on(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
@ -66,6 +68,7 @@ let OverviewView = {
}
PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
PerformanceController.off(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.off(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
@ -92,6 +95,23 @@ let OverviewView = {
return this._disabled;
},
/**
* Sets the theme for the markers overview and memory overview.
*/
setTheme: function (options={}) {
let theme = options.theme || PerformanceController.getTheme();
if (this.markersOverview) {
this.markersOverview.setTheme(theme);
this.markersOverview.refresh({ force: options.redraw });
}
if (this.memoryOverview) {
this.memoryOverview.setTheme(theme);
this.memoryOverview.refresh({ force: options.redraw });
}
},
/**
* Sets the time interval selection for all graphs in this overview.
*
@ -152,6 +172,7 @@ let OverviewView = {
this.markersOverview.groupPadding = MARKERS_GROUP_VERTICAL_PADDING;
this.markersOverview.on("selecting", this._onGraphSelecting);
yield this.markersOverview.ready();
this.setTheme();
return true;
}),
@ -173,6 +194,7 @@ let OverviewView = {
this.memoryOverview = new MemoryOverview($("#memory-overview"));
this.memoryOverview.fixedHeight = MEMORY_GRAPH_HEIGHT;
yield this.memoryOverview.ready();
this.setTheme();
CanvasGraphUtils.linkAnimation(this.markersOverview, this.memoryOverview);
CanvasGraphUtils.linkSelection(this.markersOverview, this.memoryOverview);
@ -369,6 +391,13 @@ let OverviewView = {
}
}),
/**
* Called when `devtools.theme` changes.
*/
_onThemeChanged: function (_, theme) {
this.setTheme({ theme, redraw: true });
},
toString: () => "[object OverviewView]"
};

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_simple-test.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files = head.js

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_blended-geometry.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
browser_layoutHelpers.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
head =
tail =
firefox-appdir = browser
@ -7,4 +8,4 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_bezierCanvas.js]
[test_cubicBezier.js]
[test_undoStack.js]
[test_VariablesView_getString_promise.js]
[test_VariablesView_getString_promise.js]

View File

@ -69,8 +69,8 @@ MemoryOverview.prototype = Heritage.extend(LineGraphWidget.prototype, {
setTheme: function (theme) {
theme = theme || "light";
this.backgroundColor = getColor("body-background", theme);
this.backgroundGradientStart = setAlpha(getColor("highlight-blue", theme), 0.1);
this.backgroundGradientEnd = setAlpha(getColor("highlight-blue", theme), 0);
this.backgroundGradientStart = setAlpha(getColor("highlight-blue", theme), 0.2);
this.backgroundGradientEnd = setAlpha(getColor("highlight-blue", theme), 0.05);
this.strokeColor = getColor("highlight-blue", theme);
this.selectionBackgroundColor = setAlpha(getColor("selection-background", theme), 0.25);
this.selectionStripesColor = "rgba(255, 255, 255, 0.1)";

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
cm_comment_test.js

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
# Bug 1049888 - storage actors do not work in e10s for now
# Bug 1105803 - permafailing on debug release builds since devedition landed
skip-if = e10s || debug

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
autocomplete.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_content_stylesheet.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
head =
tail =
firefox-appdir = browser

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
skip-if = e10s # Bug 1086492 - Disable tilt for e10s
# Bug 937166 - Make tilt work in E10S mode
subsuite = devtools

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_simple-test.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_simple-context.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
addons/simulators.json

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
skip-if = buildapp == 'b2g'
support-files =
app/index.html

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
../head.js

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = devtools
support-files =
../app/index.html
../app/manifest.webapp

View File

@ -180,7 +180,6 @@
@RESPATH@/components/chrome.xpt
@RESPATH@/components/commandhandler.xpt
@RESPATH@/components/commandlines.xpt
@RESPATH@/components/compartments.xpt
@RESPATH@/components/composer.xpt
@RESPATH@/components/content_events.xpt
@RESPATH@/components/content_html.xpt
@ -328,6 +327,7 @@
@RESPATH@/components/toolkit_finalizationwitness.xpt
@RESPATH@/components/toolkit_formautofill.xpt
@RESPATH@/components/toolkit_osfile.xpt
@RESPATH@/components/toolkit_perfmonitoring.xpt
@RESPATH@/components/toolkit_xulstore.xpt
@RESPATH@/components/toolkitprofile.xpt
#ifdef MOZ_ENABLE_XREMOTE

View File

@ -909,7 +909,7 @@
.line-graph-widget-tooltip {
position: absolute;
background: rgba(255,255,255,0.75);
background: rgba(255,255,255,0.9);
border-radius: 2px;
line-height: 15px;
-moz-padding-start: 6px;

View File

@ -75,7 +75,7 @@ static RedirEntry kRedirMap[] = {
nsIAboutModule::ALLOW_SCRIPT
},
{
"compartments", "chrome://global/content/aboutCompartments.xhtml",
"performance", "chrome://global/content/aboutPerformance.xhtml",
nsIAboutModule::ALLOW_SCRIPT
},
{

View File

@ -166,9 +166,8 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "buildconfig", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "license", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "compartments", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "compartments", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "performance", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },

View File

@ -6,6 +6,7 @@
/* static functions */
const DEBUG = false;
const REQUEST_CPU_LOCK_TIMEOUT = 10 * 1000; // 10 seconds.
function debug(aStr) {
if (DEBUG)
@ -18,8 +19,15 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
"@mozilla.org/power/powermanagerservice;1",
"nsIPowerManagerService");
function AlarmsManager() {
debug("Constructor");
// A <requestId, {cpuLock, timer}> map.
this._cpuLockDict = new Map();
}
AlarmsManager.prototype = {
@ -71,8 +79,10 @@ AlarmsManager.prototype = {
data = JSON.parse(Cu.evalInSandbox("JSON.stringify(data)", sandbox));
}
let request = this.createRequest();
let requestId = this.getRequestId(request);
this._lockCpuForRequest(requestId);
this._cpmm.sendAsyncMessage("AlarmsManager:Add",
{ requestId: this.getRequestId(request),
{ requestId: requestId,
date: aDate,
ignoreTimezone: isIgnoreTimezone,
data: data,
@ -111,6 +121,7 @@ AlarmsManager.prototype = {
switch (aMessage.name) {
case "AlarmsManager:Add:Return:OK":
this._unlockCpuForRequest(json.requestId);
Services.DOMRequest.fireSuccess(request, json.id);
break;
@ -131,6 +142,7 @@ AlarmsManager.prototype = {
break;
case "AlarmsManager:Add:Return:KO":
this._unlockCpuForRequest(json.requestId);
Services.DOMRequest.fireError(request, json.errorMsg);
break;
@ -172,6 +184,44 @@ AlarmsManager.prototype = {
uninit: function uninit() {
debug("uninit()");
},
_lockCpuForRequest: function (aRequestId) {
if (this._cpuLockDict.has(aRequestId)) {
debug('Cpu wakelock for request ' + aRequestId + ' has been acquired. ' +
'You may call this function repeatedly or requestId is collision.');
return;
}
// Acquire a lock for given request and save for lookup lately.
debug('Acquire cpu lock for request ' + aRequestId);
let cpuLockInfo = {
cpuLock: gPowerManagerService.newWakeLock("cpu"),
timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer)
};
this._cpuLockDict.set(aRequestId, cpuLockInfo);
// Start a timer to prevent from non-responding request.
cpuLockInfo.timer.initWithCallback(() => {
debug('Request timeout! Release the cpu lock');
this._unlockCpuForRequest(aRequestId);
}, REQUEST_CPU_LOCK_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
},
_unlockCpuForRequest: function(aRequestId) {
let cpuLockInfo = this._cpuLockDict.get(aRequestId);
if (!cpuLockInfo) {
debug('The cpu lock for requestId ' + aRequestId + ' is either invalid ' +
'or has been released.');
return;
}
// Release the cpu lock and cancel the timer.
debug('Release the cpu lock for ' + aRequestId);
cpuLockInfo.cpuLock.unlock();
cpuLockInfo.timer.cancel();
this._cpuLockDict.delete(aRequestId);
},
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AlarmsManager])

View File

@ -884,8 +884,7 @@ nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
// Don't show remote iframe if we are waiting for the completion of reflow.
if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
nsIntPoint chromeDisp = aFrame->GetChromeDisplacement();
mRemoteBrowser->UpdateDimensions(dimensions, size, chromeDisp);
mRemoteBrowser->UpdateDimensions(dimensions, size);
}
}
@ -1383,6 +1382,12 @@ nsFrameLoader::StartDestroy()
}
}
// If the TabParent has installed any event listeners on the window, this is
// its last chance to remove them while we're still in the document.
if (mRemoteBrowser) {
mRemoteBrowser->RemoveWindowListeners();
}
nsCOMPtr<nsIDocument> doc;
bool dynamicSubframeRemoval = false;
if (mOwnerContent) {
@ -2058,8 +2063,7 @@ nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame *aIFrame)
ScreenIntSize size = aIFrame->GetSubdocumentSize();
nsIntRect dimensions;
NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE);
nsIntPoint chromeDisp = aIFrame->GetChromeDisplacement();
mRemoteBrowser->UpdateDimensions(dimensions, size, chromeDisp);
mRemoteBrowser->UpdateDimensions(dimensions, size);
}
return NS_OK;
}

View File

@ -229,6 +229,9 @@ public:
void ActivateUpdateHitRegion();
void DeactivateUpdateHitRegion();
// Properly retrieves documentSize of any subdocument type.
nsresult GetWindowDimensions(nsIntRect& aRect);
private:
void SetOwnerContent(mozilla::dom::Element* aContent);
@ -284,9 +287,6 @@ private:
nsresult MaybeCreateDocShell();
nsresult EnsureMessageManager();
// Properly retrieves documentSize of any subdocument type.
nsresult GetWindowDimensions(nsIntRect& aRect);
// Updates the subdocument position and size. This gets called only
// when we have our own in-process DocShell.
void UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame);

View File

@ -1265,15 +1265,6 @@ DOMInterfaces = {
'wrapperCache': False,
}],
'VRFieldOfView': {
'wrapperCache': False,
},
'VRFieldOfViewReadOnly': {
'concrete': False,
'wrapperCache': False,
},
'VRDevice': {
'concrete': False
},

View File

@ -694,6 +694,10 @@ void HTMLMediaElement::AbortExistingLoads()
mSuspendedForPreloadNone = false;
mDownloadSuspendedByCache = false;
mMediaInfo = MediaInfo();
mIsEncrypted = false;
#ifdef MOZ_EME
mPendingEncryptedInitData.mInitDatas.Clear();
#endif // MOZ_EME
mSourcePointer = nullptr;
mLastNextFrameStatus = NEXT_FRAME_UNINITIALIZED;
@ -3088,7 +3092,11 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
nsAutoPtr<const MetadataTags> aTags)
{
mMediaInfo = *aInfo;
mIsEncrypted = aInfo->IsEncrypted();
mIsEncrypted = aInfo->IsEncrypted()
#ifdef MOZ_EME
|| mPendingEncryptedInitData.IsEncrypted()
#endif // MOZ_EME
;
mTags = aTags.forget();
mLoadedDataFired = false;
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
@ -3115,8 +3123,12 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
}
#ifdef MOZ_EME
DispatchEncrypted(aInfo->mCrypto.mInitData, aInfo->mCrypto.mType);
#endif
// Dispatch a distinct 'encrypted' event for each initData we have.
for (const auto& initData : mPendingEncryptedInitData.mInitDatas) {
DispatchEncrypted(initData.mInitData, initData.mType);
}
mPendingEncryptedInitData.mInitDatas.Clear();
#endif // MOZ_EME
}
// Expose the tracks to JS directly.
@ -4479,6 +4491,13 @@ void
HTMLMediaElement::DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType)
{
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
// Ready state not HAVE_METADATA (yet), don't dispatch encrypted now.
// Queueing for later dispatch in MetadataLoaded.
mPendingEncryptedInitData.AddInitData(aInitDataType, aInitData);
return;
}
nsRefPtr<MediaEncryptedEvent> event;
if (IsCORSSameOrigin()) {
event = MediaEncryptedEvent::Constructor(this, aInitDataType, aInitData);

View File

@ -561,7 +561,6 @@ public:
void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType) override;
bool IsEventAttributeName(nsIAtom* aName) override;
// Returns the principal of the "top level" document; the origin displayed
@ -1322,6 +1321,11 @@ protected:
// True if the media has encryption information.
bool mIsEncrypted;
#ifdef MOZ_EME
// Init Data that needs to be sent in 'encrypted' events in MetadataLoaded().
EncryptionInfo mPendingEncryptedInitData;
#endif // MOZ_EME
// True if the media's channel's download has been suspended.
bool mDownloadSuspendedByCache;

View File

@ -2026,8 +2026,8 @@ TabChild::RecvUpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
mOrientation = orientation;
ScreenIntSize oldScreenSize = mInnerSize;
mInnerSize = size;
mWidget->Resize(0, 0, size.width, size.height,
true);
mWidget->Resize(rect.x + chromeDisp.x, rect.y + chromeDisp.y, size.width, size.height,
true);
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
baseWin->SetPositionAndSize(0, 0, size.width, size.height,

View File

@ -80,6 +80,7 @@
#include "nsICancelable.h"
#include "gfxPrefs.h"
#include "nsILoginManagerPrompter.h"
#include "nsPIWindowRoot.h"
#include <algorithm>
using namespace mozilla::dom;
@ -267,6 +268,7 @@ TabParent::TabParent(nsIContentParent* aManager,
, mDefaultScale(0)
, mShown(false)
, mUpdatedDimensions(false)
, mChromeOffset(0, 0)
, mManager(aManager)
, mMarkedDestroying(false)
, mIsDestroyed(false)
@ -325,10 +327,36 @@ TabParent::CacheFrameLoader(nsFrameLoader* aFrameLoader)
void
TabParent::SetOwnerElement(Element* aElement)
{
// If we held previous content then unregister for its events.
RemoveWindowListeners();
// Update to the new content, and register to listen for events from it.
mFrameElement = aElement;
if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) {
nsCOMPtr<nsPIDOMWindow> window = mFrameElement->OwnerDoc()->GetWindow();
nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot();
if (eventTarget) {
eventTarget->AddEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
this, false, false);
}
}
TryCacheDPIAndScale();
}
void
TabParent::RemoveWindowListeners()
{
if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) {
nsCOMPtr<nsPIDOMWindow> window = mFrameElement->OwnerDoc()->GetWindow();
nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot();
if (eventTarget) {
eventTarget->RemoveEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
this, false);
}
}
}
void
TabParent::GetAppType(nsAString& aOut)
{
@ -880,8 +908,7 @@ TabParent::RecvSetDimensions(const uint32_t& aFlags,
}
void
TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
const nsIntPoint& aChromeDisp)
TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size)
{
if (mIsDestroyed) {
return;
@ -889,15 +916,25 @@ TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
hal::ScreenConfiguration config;
hal::GetCurrentScreenConfiguration(&config);
ScreenOrientation orientation = config.orientation();
nsIntPoint chromeOffset = -LayoutDevicePixel::ToUntyped(GetChildProcessOffset());
if (!mUpdatedDimensions || mOrientation != orientation ||
mDimensions != size || !mRect.IsEqualEdges(rect)) {
mDimensions != size || !mRect.IsEqualEdges(rect) ||
chromeOffset != mChromeOffset) {
nsCOMPtr<nsIWidget> widget = GetWidget();
nsIntRect contentRect = rect;
if (widget) {
contentRect.x += widget->GetClientOffset().x;
contentRect.y += widget->GetClientOffset().y;
}
mUpdatedDimensions = true;
mRect = rect;
mRect = contentRect;
mDimensions = size;
mOrientation = orientation;
mChromeOffset = chromeOffset;
unused << SendUpdateDimensions(mRect, mDimensions, mOrientation, aChromeDisp);
unused << SendUpdateDimensions(mRect, mDimensions, mOrientation, mChromeOffset);
}
}
@ -2740,6 +2777,27 @@ TabParent::DeallocPPluginWidgetParent(mozilla::plugins::PPluginWidgetParent* aAc
return true;
}
nsresult
TabParent::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString eventType;
aEvent->GetType(eventType);
if (eventType.EqualsLiteral("MozUpdateWindowPos") && !mIsDestroyed) {
// This event is sent when the widget moved. Therefore we only update
// the position.
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
if (!frameLoader) {
return NS_OK;
}
nsIntRect windowDims;
NS_ENSURE_SUCCESS(frameLoader->GetWindowDimensions(windowDims), NS_ERROR_FAILURE);
UpdateDimensions(windowDims, mDimensions);
return NS_OK;
}
return NS_OK;
}
class FakeChannel final : public nsIChannel,
public nsIAuthPromptCallback,
public nsIInterfaceRequestor,

View File

@ -60,6 +60,7 @@ class Element;
struct StructuredCloneData;
class TabParent final : public PBrowserParent
, public nsIDOMEventListener
, public nsITabParent
, public nsIAuthPromptProvider
, public nsISecureBrowserUI
@ -73,6 +74,8 @@ class TabParent final : public PBrowserParent
public:
// nsITabParent
NS_DECL_NSITABPARENT
// nsIDOMEventListener interfaces
NS_DECL_NSIDOMEVENTLISTENER
TabParent(nsIContentParent* aManager,
const TabId& aTabId,
@ -107,6 +110,8 @@ public:
void Destroy();
void RemoveWindowListeners();
virtual bool RecvMoveFocus(const bool& aForward) override;
virtual bool RecvEvent(const RemoteDOMEvent& aEvent) override;
virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& aEvent) override;
@ -225,8 +230,7 @@ public:
// message-sending functions under a layer of indirection and
// eating the return values
void Show(const ScreenIntSize& size, bool aParentIsActive);
void UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
const nsIntPoint& chromeDisp);
void UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size);
void UpdateFrame(const layers::FrameMetrics& aFrameMetrics);
void UIResolutionChanged();
void RequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
@ -427,6 +431,7 @@ protected:
CSSToLayoutDeviceScale mDefaultScale;
bool mShown;
bool mUpdatedDimensions;
nsIntPoint mChromeOffset;
private:
already_AddRefed<nsFrameLoader> GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;

View File

@ -136,11 +136,11 @@ public:
#ifdef MOZ_EME
// Dispatches a "encrypted" event to the HTMLMediaElement, with the
// provided init data.
// provided init data. Actual dispatch may be delayed until HAVE_METADATA.
// Main thread only.
virtual void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType) = 0;
#endif
#endif // MOZ_EME
};
}

View File

@ -105,16 +105,41 @@ public:
class EncryptionInfo {
public:
EncryptionInfo() : mIsEncrypted(false) {}
struct InitData {
template<typename AInitDatas>
InitData(const nsAString& aType, AInitDatas&& aInitData)
: mType(aType)
, mInitData(Forward<AInitDatas>(aInitData))
{
}
// Encryption type to be passed to JS. Usually `cenc'.
nsString mType;
// Encryption type to be passed to JS. Usually `cenc'.
nsString mType;
// Encryption data.
nsTArray<uint8_t> mInitData;
// Encryption data.
nsTArray<uint8_t> mInitData;
};
typedef nsTArray<InitData> InitDatas;
// True if the stream has encryption metadata
bool mIsEncrypted;
bool IsEncrypted() const
{
return !mInitDatas.IsEmpty();
}
template<typename AInitDatas>
void AddInitData(const nsAString& aType, AInitDatas&& aInitData)
{
mInitDatas.AppendElement(InitData(aType, Forward<AInitDatas>(aInitData)));
}
void AddInitData(const EncryptionInfo& aInfo)
{
mInitDatas.AppendElements(aInfo.mInitDatas);
}
// One 'InitData' per encrypted buffer.
InitDatas mInitDatas;
};
class MediaInfo {
@ -131,7 +156,7 @@ public:
bool IsEncrypted() const
{
return mCrypto.mIsEncrypted;
return mCrypto.IsEncrypted();
}
bool HasValidMedia() const

View File

@ -264,6 +264,34 @@ MP4Reader::Init(MediaDecoderReader* aCloneDonor)
return NS_OK;
}
#ifdef MOZ_EME
class DispatchKeyNeededEvent : public nsRunnable {
public:
DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
nsTArray<uint8_t>& aInitData,
const nsString& aInitDataType)
: mDecoder(aDecoder)
, mInitData(aInitData)
, mInitDataType(aInitDataType)
{
}
NS_IMETHOD Run() {
// Note: Null check the owner, as the decoder could have been shutdown
// since this event was dispatched.
MediaDecoderOwner* owner = mDecoder->GetOwner();
if (owner) {
owner->DispatchEncrypted(mInitData, mInitDataType);
}
mDecoder = nullptr;
return NS_OK;
}
private:
nsRefPtr<AbstractMediaDecoder> mDecoder;
nsTArray<uint8_t> mInitData;
nsString mInitDataType;
};
#endif // MOZ_EME
void MP4Reader::RequestCodecResource() {
if (mVideo.mDecoder) {
mVideo.mDecoder->AllocateMediaResources();
@ -368,7 +396,7 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
{
MonitorAutoUnlock unlock(mDemuxerMonitor);
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mInfo.mCrypto.mIsEncrypted = mIsEncrypted = mCrypto.valid;
mIsEncrypted = mCrypto.valid;
}
// Remember that we've initialized the demuxer, so that if we're decoding
@ -400,15 +428,21 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
}
}
if (mIsEncrypted) {
if (mCrypto.valid) {
nsTArray<uint8_t> initData;
ExtractCryptoInitData(initData);
if (initData.Length() == 0) {
return NS_ERROR_FAILURE;
}
mInfo.mCrypto.mInitData = initData;
mInfo.mCrypto.mType = NS_LITERAL_STRING("cenc");
#ifdef MOZ_EME
// Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.
NS_DispatchToMainThread(
new DispatchKeyNeededEvent(mDecoder, initData, NS_LITERAL_STRING("cenc")));
#endif // MOZ_EME
// Add init data to info, will get sent from HTMLMediaElement::MetadataLoaded
// (i.e., when transitioning from HAVE_NOTHING to HAVE_METADATA).
mInfo.mCrypto.AddInitData(NS_LITERAL_STRING("cenc"), Move(initData));
}
// Get the duration, and report it to the decoder if we have it.

View File

@ -275,8 +275,6 @@ private:
layers::LayersBackend mLayersBackendType;
nsTArray<nsTArray<uint8_t>> mInitDataEncountered;
// True if we've read the streams' metadata.
bool mDemuxerInitialized;

View File

@ -1068,22 +1068,6 @@ MediaSourceReader::MaybeNotifyHaveData()
IsSeeking(), haveAudio, haveVideo, ended);
}
static void
CombineEncryptionData(EncryptionInfo& aTo, const EncryptionInfo& aFrom)
{
if (!aFrom.mIsEncrypted) {
return;
}
aTo.mIsEncrypted = true;
if (!aTo.mType.IsEmpty() && !aTo.mType.Equals(aFrom.mType)) {
NS_WARNING("mismatched encryption types");
}
aTo.mType = aFrom.mType;
aTo.mInitData.AppendElements(aFrom.mInitData);
}
nsresult
MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
{
@ -1107,7 +1091,7 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
const MediaInfo& info = GetAudioReader()->GetMediaInfo();
MOZ_ASSERT(info.HasAudio());
mInfo.mAudio = info.mAudio;
CombineEncryptionData(mInfo.mCrypto, info.mCrypto);
mInfo.mCrypto.AddInitData(info.mCrypto);
MSE_DEBUG("audio reader=%p duration=%lld",
mAudioSourceDecoder.get(),
mAudioSourceDecoder->GetReader()->GetDecoder()->GetMediaDuration());
@ -1120,7 +1104,7 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
const MediaInfo& info = GetVideoReader()->GetMediaInfo();
MOZ_ASSERT(info.HasVideo());
mInfo.mVideo = info.mVideo;
CombineEncryptionData(mInfo.mCrypto, info.mCrypto);
mInfo.mCrypto.AddInitData(info.mCrypto);
MSE_DEBUG("video reader=%p duration=%lld",
GetVideoReader(),
GetVideoReader()->GetDecoder()->GetMediaDuration());

View File

@ -664,6 +664,7 @@ var gEMETests = [
"7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
},
sessionType:"temporary",
sessionCount:1,
duration:1.60,
},
{
@ -684,6 +685,7 @@ var gEMETests = [
"7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
},
sessionType:"temporary",
sessionCount:1,
crossOrigin:true,
duration:1.60,
},
@ -714,6 +716,7 @@ var gEMETests = [
"7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
},
sessionType:"temporary",
sessionCount:2,
duration:1.60,
},
{
@ -743,6 +746,7 @@ var gEMETests = [
"7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
},
sessionType:"temporary",
sessionCount:2,
crossOrigin:true,
duration:1.60,
},

View File

@ -48,11 +48,11 @@ function startTest(test, token)
}
);
var gotEncrypted = false;
var gotEncrypted = 0;
var gotPlaying = false;
v.addEventListener("encrypted", function(ev) {
gotEncrypted = true;
gotEncrypted += 1;
});
v.addEventListener("playing", function () { gotPlaying = true; });
@ -66,7 +66,9 @@ function startTest(test, token)
v.addEventListener("ended", function(ev) {
ok(true, TimeStamp(token) + " got ended event");
ok(gotEncrypted, TimeStamp(token) + " encrypted event should have fired");
is(gotEncrypted, test.sessionCount,
TimeStamp(token) + " encrypted events expected: " + test.sessionCount
+ ", actual: " + gotEncrypted);
ok(gotPlaying, TimeStamp(token) + " playing event should have fired");
ok(Math.abs(test.duration - v.duration) < 0.1,

View File

@ -11,6 +11,9 @@
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
// Turn off the authentication dialog blocking for this test.
SpecialPowers.setIntPref("network.auth.allow-subresource-auth", 2)
var tests = [
// Not the same origin no CORS asked for, should have silence
{ url: "http://example.org:80/tests/dom/media/webaudio/test/small-shot.ogg",

View File

@ -790,7 +790,6 @@ NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
tabContentBounds.ScaleInverseRoundOut(scaleFactor);
int32_t windowH = tabContentBounds.height + int(chromeSize.y);
// This is actually relative to window-chrome.
nsPoint pluginPosition = AsNsPoint(pluginFrame->GetScreenRect().TopLeft());
// Convert (sourceX, sourceY) to 'real' (not PuppetWidget) screen space.
@ -800,8 +799,8 @@ NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
nsPoint screenPoint;
switch (sourceSpace) {
case NPCoordinateSpacePlugin:
screenPoint = sourcePoint + pluginFrame->GetContentRectRelativeToSelf().TopLeft() +
chromeSize + pluginPosition + windowPosition;
screenPoint = sourcePoint + pluginPosition +
pluginFrame->GetContentRectRelativeToSelf().TopLeft() / nsPresContext::AppUnitsPerCSSPixel();
break;
case NPCoordinateSpaceWindow:
screenPoint = nsPoint(sourcePoint.x, windowH-sourcePoint.y) +
@ -824,8 +823,8 @@ NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
nsPoint destPoint;
switch (destSpace) {
case NPCoordinateSpacePlugin:
destPoint = screenPoint - pluginFrame->GetContentRectRelativeToSelf().TopLeft() -
chromeSize - pluginPosition - windowPosition;
destPoint = screenPoint - pluginPosition -
pluginFrame->GetContentRectRelativeToSelf().TopLeft() / nsPresContext::AppUnitsPerCSSPixel();
break;
case NPCoordinateSpaceWindow:
destPoint = screenPoint - windowPosition;

View File

@ -17,30 +17,109 @@ using namespace mozilla::gfx;
namespace mozilla {
namespace dom {
VRFieldOfView*
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfViewReadOnly, mParent)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFieldOfViewReadOnly, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFieldOfViewReadOnly, Release)
JSObject*
VRFieldOfViewReadOnly::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return VRFieldOfViewReadOnlyBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<VRFieldOfView>
VRFieldOfView::Constructor(const GlobalObject& aGlobal, const VRFieldOfViewInit& aParams,
ErrorResult& aRV)
{
return new VRFieldOfView(aParams.mUpDegrees, aParams.mRightDegrees,
aParams.mDownDegrees, aParams.mLeftDegrees);
nsRefPtr<VRFieldOfView> fov =
new VRFieldOfView(aGlobal.GetAsSupports(),
aParams.mUpDegrees, aParams.mRightDegrees,
aParams.mDownDegrees, aParams.mLeftDegrees);
return fov.forget();
}
VRFieldOfView*
already_AddRefed<VRFieldOfView>
VRFieldOfView::Constructor(const GlobalObject& aGlobal,
double aUpDegrees, double aRightDegrees,
double aDownDegrees, double aLeftDegrees,
ErrorResult& aRV)
{
return new VRFieldOfView(aUpDegrees, aRightDegrees, aDownDegrees,
aLeftDegrees);
nsRefPtr<VRFieldOfView> fov =
new VRFieldOfView(aGlobal.GetAsSupports(),
aUpDegrees, aRightDegrees, aDownDegrees,
aLeftDegrees);
return fov.forget();
}
bool
JSObject*
VRFieldOfView::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector)
JS::Handle<JSObject*> aGivenProto)
{
return VRFieldOfViewBinding::Wrap(aCx, this, aGivenProto, aReflector);
return VRFieldOfViewBinding::Wrap(aCx, this, aGivenProto);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VREyeParameters, mParent, mMinFOV, mMaxFOV, mRecFOV, mCurFOV, mEyeTranslation, mRenderRect)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VREyeParameters, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VREyeParameters, Release)
VREyeParameters::VREyeParameters(nsISupports* aParent,
const gfx::VRFieldOfView& aMinFOV,
const gfx::VRFieldOfView& aMaxFOV,
const gfx::VRFieldOfView& aRecFOV,
const gfx::Point3D& aEyeTranslation,
const gfx::VRFieldOfView& aCurFOV,
const gfx::IntRect& aRenderRect)
: mParent(aParent)
{
mMinFOV = new VRFieldOfView(aParent, aMinFOV);
mMaxFOV = new VRFieldOfView(aParent, aMaxFOV);
mRecFOV = new VRFieldOfView(aParent, aRecFOV);
mCurFOV = new VRFieldOfView(aParent, aCurFOV);
mEyeTranslation = new DOMPoint(aParent, aEyeTranslation.x, aEyeTranslation.y, aEyeTranslation.z, 0.0);
mRenderRect = new DOMRect(aParent, aRenderRect.x, aRenderRect.y, aRenderRect.width, aRenderRect.height);
}
VRFieldOfView*
VREyeParameters::MinimumFieldOfView()
{
return mMinFOV;
}
VRFieldOfView*
VREyeParameters::MaximumFieldOfView()
{
return mMaxFOV;
}
VRFieldOfView*
VREyeParameters::RecommendedFieldOfView()
{
return mRecFOV;
}
VRFieldOfView*
VREyeParameters::CurrentFieldOfView()
{
return mCurFOV;
}
DOMPoint*
VREyeParameters::EyeTranslation()
{
return mEyeTranslation;
}
DOMRect*
VREyeParameters::RenderRect()
{
return mRenderRect;
}
JSObject*
VREyeParameters::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return VREyeParametersBinding::Wrap(aCx, this, aGivenProto);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRPositionState, mParent)
@ -126,37 +205,8 @@ PositionSensorVRDevice::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenP
return PositionSensorVRDeviceBinding::Wrap(aCx, this, aGivenProto);
}
static void
ReleaseHMDInfoRef(void *, nsIAtom*, void *aPropertyValue, void *)
{
if (aPropertyValue) {
static_cast<VRHMDInfo*>(aPropertyValue)->Release();
}
}
void
HMDVRDevice::XxxToggleElementVR(Element& aElement)
{
VRHMDInfo* hmdPtr = static_cast<VRHMDInfo*>(aElement.GetProperty(nsGkAtoms::vr_state));
if (hmdPtr) {
aElement.DeleteProperty(nsGkAtoms::vr_state);
return;
}
nsRefPtr<VRHMDInfo> hmdRef = mHMD;
aElement.SetProperty(nsGkAtoms::vr_state, hmdRef.forget().take(),
ReleaseHMDInfoRef,
true);
}
namespace {
gfx::VRHMDInfo::Eye
EyeToEye(const VREye& aEye)
{
return aEye == VREye::Left ? gfx::VRHMDInfo::Eye_Left : gfx::VRHMDInfo::Eye_Right;
}
class HMDInfoVRDevice : public HMDVRDevice
{
public:
@ -164,9 +214,21 @@ public:
: HMDVRDevice(aParent, aHMD)
{
// XXX TODO use real names/IDs
mHWID.AppendPrintf("HMDInfo-0x%llx", aHMD);
mDeviceId.AssignLiteral("somedevid");
mDeviceName.AssignLiteral("HMD Device");
uint64_t hmdid = reinterpret_cast<uint64_t>(aHMD);
mHWID.Truncate();
mHWID.AppendPrintf("HMDInfo-0x%llx", hmdid);
mDeviceId.Truncate();
mDeviceId.AppendPrintf("HMDInfo-dev-0x%llx", hmdid);
if (aHMD->GetType() == VRHMDType::Oculus) {
mDeviceName.AssignLiteral("VR HMD Device (oculus)");
} else if (aHMD->GetType() == VRHMDType::Cardboard) {
mDeviceName.AssignLiteral("VR HMD Device (cardboard)");
} else {
mDeviceName.AssignLiteral("VR HMD Device (unknown)");
}
mValid = true;
}
@ -193,46 +255,22 @@ public:
mHMD->SetFOV(left, right, zNear, zFar);
}
virtual already_AddRefed<DOMPoint> GetEyeTranslation(VREye aEye) override
virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye) override
{
gfx::Point3D p = mHMD->GetEyeTranslation(EyeToEye(aEye));
nsRefPtr<DOMPoint> obj = new DOMPoint(mParent, p.x, p.y, p.z, 0.0);
return obj.forget();
}
virtual VRFieldOfView* GetCurrentEyeFieldOfView(VREye aEye) override
{
return CopyFieldOfView(mHMD->GetEyeFOV(EyeToEye(aEye)));
}
virtual VRFieldOfView* GetRecommendedEyeFieldOfView(VREye aEye) override
{
return CopyFieldOfView(mHMD->GetRecommendedEyeFOV(EyeToEye(aEye)));
}
virtual VRFieldOfView* GetMaximumEyeFieldOfView(VREye aEye) override
{
return CopyFieldOfView(mHMD->GetMaximumEyeFOV(EyeToEye(aEye)));
}
virtual already_AddRefed<DOMRect> GetRecommendedEyeRenderRect(VREye aEye) override
{
const IntSize& a(mHMD->SuggestedEyeResolution());
nsRefPtr<DOMRect> obj =
new DOMRect(mParent,
(aEye == VREye::Left) ? 0 : a.width, 0,
a.width, a.height);
return obj.forget();
gfx::IntSize sz(mHMD->SuggestedEyeResolution());
gfx::VRHMDInfo::Eye eye = aEye == VREye::Left ? gfx::VRHMDInfo::Eye_Left : gfx::VRHMDInfo::Eye_Right;
nsRefPtr<VREyeParameters> params =
new VREyeParameters(mParent,
gfx::VRFieldOfView(15, 15, 15, 15), // XXX min?
mHMD->GetMaximumEyeFOV(eye),
mHMD->GetRecommendedEyeFOV(eye),
mHMD->GetEyeTranslation(eye),
mHMD->GetEyeFOV(eye),
gfx::IntRect((aEye == VREye::Left) ? 0 : sz.width, 0, sz.width, sz.height));
return params.forget();
}
protected:
VRFieldOfView*
CopyFieldOfView(const gfx::VRFieldOfView& aSrc)
{
return new VRFieldOfView(aSrc.upDegrees, aSrc.rightDegrees,
aSrc.downDegrees, aSrc.leftDegrees);
}
};
class HMDPositionVRDevice : public PositionSensorVRDevice
@ -244,9 +282,21 @@ public:
, mTracking(false)
{
// XXX TODO use real names/IDs
mHWID.AppendPrintf("HMDInfo-0x%llx", aHMD);
mDeviceId.AssignLiteral("somedevid");
mDeviceName.AssignLiteral("HMD Position Device");
uint64_t hmdid = reinterpret_cast<uint64_t>(aHMD);
mHWID.Truncate();
mHWID.AppendPrintf("HMDInfo-0x%llx", hmdid);
mDeviceId.Truncate();
mDeviceId.AppendPrintf("HMDInfo-dev-0x%llx", hmdid);
if (aHMD->GetType() == VRHMDType::Oculus) {
mDeviceName.AssignLiteral("VR Position Device (oculus)");
} else if (aHMD->GetType() == VRHMDType::Cardboard) {
mDeviceName.AssignLiteral("VR Position Device (cardboard)");
} else {
mDeviceName.AssignLiteral("VR Position Device (unknown)");
}
mValid = true;
}
@ -258,20 +308,33 @@ public:
}
}
virtual already_AddRefed<VRPositionState> GetState(double timeOffset) override
virtual already_AddRefed<VRPositionState> GetState() override
{
if (!mTracking) {
mHMD->StartSensorTracking();
mTracking = true;
}
gfx::VRHMDSensorState state = mHMD->GetSensorState(timeOffset);
gfx::VRHMDSensorState state = mHMD->GetSensorState();
nsRefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
return obj.forget();
}
virtual void ZeroSensor() override
virtual already_AddRefed<VRPositionState> GetImmediateState() override
{
if (!mTracking) {
mHMD->StartSensorTracking();
mTracking = true;
}
gfx::VRHMDSensorState state = mHMD->GetSensorState();
nsRefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
return obj.forget();
}
virtual void ResetSensor() override
{
mHMD->ZeroSensor();
}
@ -286,13 +349,8 @@ protected:
bool
VRDevice::CreateAllKnownVRDevices(nsISupports *aParent, nsTArray<nsRefPtr<VRDevice>>& aDevices)
{
if (!gfx::VRHMDManagerOculus::Init()) {
NS_WARNING("Failed to initialize Oculus HMD Manager");
return false;
}
nsTArray<nsRefPtr<gfx::VRHMDInfo>> hmds;
gfx::VRHMDManagerOculus::GetOculusHMDs(hmds);
gfx::VRHMDManager::GetAllHMDs(hmds);
for (size_t i = 0; i < hmds.Length(); ++i) {
uint32_t sensorBits = hmds[i]->GetSupportedSensorStateBits();

View File

@ -26,24 +26,36 @@ namespace dom {
class Element;
class VRFieldOfViewReadOnly : public NonRefcountedDOMObject
class VRFieldOfViewReadOnly : public nsWrapperCache
{
public:
VRFieldOfViewReadOnly(double aUpDegrees, double aRightDegrees,
VRFieldOfViewReadOnly(nsISupports* aParent,
double aUpDegrees, double aRightDegrees,
double aDownDegrees, double aLeftDegrees)
: mUpDegrees(aUpDegrees)
: mParent(aParent)
, mUpDegrees(aUpDegrees)
, mRightDegrees(aRightDegrees)
, mDownDegrees(aDownDegrees)
, mLeftDegrees(aLeftDegrees)
{
}
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRFieldOfViewReadOnly)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRFieldOfViewReadOnly)
double UpDegrees() const { return mUpDegrees; }
double RightDegrees() const { return mRightDegrees; }
double DownDegrees() const { return mDownDegrees; }
double LeftDegrees() const { return mLeftDegrees; }
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
protected:
virtual ~VRFieldOfViewReadOnly() {}
nsCOMPtr<nsISupports> mParent;
double mUpDegrees;
double mRightDegrees;
double mDownDegrees;
@ -53,22 +65,30 @@ protected:
class VRFieldOfView final : public VRFieldOfViewReadOnly
{
public:
explicit VRFieldOfView(double aUpDegrees = 0.0, double aRightDegrees = 0.0,
double aDownDegrees = 0.0, double aLeftDegrees = 0.0)
: VRFieldOfViewReadOnly(aUpDegrees, aRightDegrees, aDownDegrees, aLeftDegrees)
VRFieldOfView(nsISupports* aParent, const gfx::VRFieldOfView& aSrc)
: VRFieldOfViewReadOnly(aParent,
aSrc.upDegrees, aSrc.rightDegrees,
aSrc.downDegrees, aSrc.leftDegrees)
{}
static VRFieldOfView*
explicit VRFieldOfView(nsISupports* aParent,
double aUpDegrees = 0.0, double aRightDegrees = 0.0,
double aDownDegrees = 0.0, double aLeftDegrees = 0.0)
: VRFieldOfViewReadOnly(aParent,
aUpDegrees, aRightDegrees, aDownDegrees, aLeftDegrees)
{}
static already_AddRefed<VRFieldOfView>
Constructor(const GlobalObject& aGlobal, const VRFieldOfViewInit& aParams,
ErrorResult& aRv);
static VRFieldOfView*
static already_AddRefed<VRFieldOfView>
Constructor(const GlobalObject& aGlobal,
double aUpDegrees, double aRightDegrees,
double aDownDegrees, double aLeftDegrees,
ErrorResult& aRv);
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void SetUpDegrees(double aVal) { mUpDegrees = aVal; }
void SetRightDegrees(double aVal) { mRightDegrees = aVal; }
@ -80,7 +100,7 @@ class VRPositionState final : public nsWrapperCache
{
~VRPositionState() {}
public:
explicit VRPositionState(nsISupports* aParent, const gfx::VRHMDSensorState& aState);
VRPositionState(nsISupports* aParent, const gfx::VRHMDSensorState& aState);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRPositionState)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRPositionState)
@ -117,6 +137,43 @@ protected:
nsRefPtr<DOMPoint> mAngularAcceleration;
};
class VREyeParameters final : public nsWrapperCache
{
public:
VREyeParameters(nsISupports* aParent,
const gfx::VRFieldOfView& aMinFOV,
const gfx::VRFieldOfView& aMaxFOV,
const gfx::VRFieldOfView& aRecFOV,
const gfx::Point3D& aEyeTranslation,
const gfx::VRFieldOfView& aCurFOV,
const gfx::IntRect& aRenderRect);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VREyeParameters)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VREyeParameters)
VRFieldOfView* MinimumFieldOfView();
VRFieldOfView* MaximumFieldOfView();
VRFieldOfView* RecommendedFieldOfView();
DOMPoint* EyeTranslation();
VRFieldOfView* CurrentFieldOfView();
DOMRect* RenderRect();
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
protected:
~VREyeParameters() {}
nsCOMPtr<nsISupports> mParent;
nsRefPtr<VRFieldOfView> mMinFOV;
nsRefPtr<VRFieldOfView> mMaxFOV;
nsRefPtr<VRFieldOfView> mRecFOV;
nsRefPtr<DOMPoint> mEyeTranslation;
nsRefPtr<VRFieldOfView> mCurFOV;
nsRefPtr<DOMRect> mRenderRect;
};
class VRDevice : public nsISupports,
public nsWrapperCache
{
@ -176,20 +233,14 @@ protected:
class HMDVRDevice : public VRDevice
{
public:
virtual already_AddRefed<DOMPoint> GetEyeTranslation(VREye aEye) = 0;
virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye) = 0;
virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV,
const VRFieldOfViewInit& aRightFOV,
double zNear, double zFar) = 0;
virtual VRFieldOfView* GetCurrentEyeFieldOfView(VREye aEye) = 0;
virtual VRFieldOfView* GetRecommendedEyeFieldOfView(VREye aEye) = 0;
virtual VRFieldOfView* GetMaximumEyeFieldOfView(VREye aEye) = 0;
virtual already_AddRefed<DOMRect> GetRecommendedEyeRenderRect(VREye aEye) = 0;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void XxxToggleElementVR(Element& aElement);
gfx::VRHMDInfo *GetHMD() { return mHMD.get(); }
protected:
@ -206,9 +257,11 @@ protected:
class PositionSensorVRDevice : public VRDevice
{
public:
virtual already_AddRefed<VRPositionState> GetState(double timeOffset) = 0;
virtual already_AddRefed<VRPositionState> GetState() = 0;
virtual void ZeroSensor() = 0;
virtual already_AddRefed<VRPositionState> GetImmediateState() = 0;
virtual void ResetSensor() = 0;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;

View File

@ -52,6 +52,20 @@ interface VRPositionState {
readonly attribute DOMPoint? angularAcceleration;
};
[Pref="dom.vr.enabled",
HeaderFile="mozilla/dom/VRDevice.h"]
interface VREyeParameters {
/* These values are expected to be static per-device/per-user */
[Constant, Cached] readonly attribute VRFieldOfView minimumFieldOfView;
[Constant, Cached] readonly attribute VRFieldOfView maximumFieldOfView;
[Constant, Cached] readonly attribute VRFieldOfView recommendedFieldOfView;
[Constant, Cached] readonly attribute DOMPoint eyeTranslation;
/* These values will vary after a FOV has been set */
[Constant, Cached] readonly attribute VRFieldOfView currentFieldOfView;
[Constant, Cached] readonly attribute DOMRect renderRect;
};
[Pref="dom.vr.enabled"]
interface VRDevice {
/**
@ -59,38 +73,26 @@ interface VRDevice {
* VR Device is a part of. All VRDevice/Sensors that come
* from the same hardware will have the same hardwareId
*/
[Pure] readonly attribute DOMString hardwareUnitId;
[Constant] readonly attribute DOMString hardwareUnitId;
/**
* An identifier for this distinct sensor/device on a physical
* hardware device. This shouldn't change across browser
* restrats, allowing configuration data to be saved based on it.
*/
[Pure] readonly attribute DOMString deviceId;
[Constant] readonly attribute DOMString deviceId;
/**
* a device name, a user-readable name identifying it
*/
[Pure] readonly attribute DOMString deviceName;
[Constant] readonly attribute DOMString deviceName;
};
[Pref="dom.vr.enabled",
HeaderFile="mozilla/dom/VRDevice.h"]
interface HMDVRDevice : VRDevice {
/* The translation that should be applied to the view matrix for rendering each eye */
DOMPoint getEyeTranslation(VREye whichEye);
// the FOV that the HMD was configured with
[NewObject]
VRFieldOfView getCurrentEyeFieldOfView(VREye whichEye);
// the recommended FOV, per eye.
[NewObject]
VRFieldOfView getRecommendedEyeFieldOfView(VREye whichEye);
// the maximum FOV, per eye. Above this, rendering will look broken.
[NewObject]
VRFieldOfView getMaximumEyeFieldOfView(VREye whichEye);
// Return the current VREyeParameters for the given eye
VREyeParameters getEyeParameters(VREye whichEye);
// Set a field of view. If either of the fields of view is null,
// or if their values are all zeros, then the recommended field of view
@ -99,34 +101,30 @@ interface HMDVRDevice : VRDevice {
optional VRFieldOfViewInit rightFOV,
optional double zNear = 0.01,
optional double zFar = 10000.0);
// return a recommended rect for this eye. Only useful for Canvas rendering,
// the x/y coordinates will be the location in the canvas where this eye should
// begin, and the width/height are the dimensions. Any canvas in the appropriate
// ratio will work.
DOMRect getRecommendedEyeRenderRect(VREye whichEye);
// hack for testing
void xxxToggleElementVR(Element element);
};
[Pref="dom.vr.enabled" ,
HeaderFile="mozilla/dom/VRDevice.h"]
interface PositionSensorVRDevice : VRDevice {
/*
* Return a VRPositionState dictionary containing the state of this position sensor,
* at an optional past time or predicted for a future time if timeOffset is != 0.
* Return a VRPositionState dictionary containing the state of this position sensor
* for the current frame if within a requestAnimationFrame callback, or for the
* previous frame if not.
*
* The VRPositionState will contain the position, orientation, and velocity
* and acceleration of each of these properties. Use "hasPosition" and "hasOrientation"
* to check if the associated members are valid; if these are false, those members
* will be null.
*/
[NewObject]
VRPositionState getState(optional double timeOffset = 0.0);
[NewObject] VRPositionState getState();
/* Zero this sensor, treating its current position and orientation
/*
* Return the current instantaneous sensor state.
*/
[NewObject] VRPositionState getImmediateState();
/* Reset this sensor, treating its current position and orientation
* as the "origin/zero" values.
*/
void zeroSensor();
void resetSensor();
};

View File

@ -4223,7 +4223,8 @@ WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate)
mCondVar(mMutex, "WorkerDebugger::mCondVar"),
mWorkerPrivate(aWorkerPrivate),
mIsEnabled(false),
mIsInitialized(false)
mIsInitialized(false),
mIsFrozen(false)
{
mWorkerPrivate->AssertIsOnParentThread();
}
@ -4277,6 +4278,21 @@ WorkerDebugger::GetIsChrome(bool* aResult)
return NS_OK;
}
NS_IMETHODIMP
WorkerDebugger::GetIsFrozen(bool* aResult)
{
AssertIsOnMainThread();
MutexAutoLock lock(mMutex);
if (!mWorkerPrivate) {
return NS_ERROR_UNEXPECTED;
}
*aResult = mIsFrozen;
return NS_OK;
}
NS_IMETHODIMP
WorkerDebugger::GetParent(nsIWorkerDebugger** aResult)
{
@ -4474,6 +4490,52 @@ WorkerDebugger::Disable()
NotifyIsEnabled(false);
}
void
WorkerDebugger::Freeze()
{
mWorkerPrivate->AssertIsOnWorkerThread();
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &WorkerDebugger::FreezeOnMainThread);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL)));
}
void
WorkerDebugger::FreezeOnMainThread()
{
AssertIsOnMainThread();
mIsFrozen = true;
for (size_t index = 0; index < mListeners.Length(); ++index) {
mListeners[index]->OnFreeze();
}
}
void
WorkerDebugger::Thaw()
{
mWorkerPrivate->AssertIsOnWorkerThread();
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &WorkerDebugger::ThawOnMainThread);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL)));
}
void
WorkerDebugger::ThawOnMainThread()
{
AssertIsOnMainThread();
mIsFrozen = false;
for (size_t index = 0; index < mListeners.Length(); ++index) {
mListeners[index]->OnThaw();
}
}
void
WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage)
{
@ -5693,6 +5755,7 @@ WorkerPrivate::FreezeInternal(JSContext* aCx)
NS_ASSERTION(!mFrozen, "Already frozen!");
mFrozen = true;
mDebugger->Freeze();
return true;
}
@ -5704,6 +5767,7 @@ WorkerPrivate::ThawInternal(JSContext* aCx)
NS_ASSERTION(mFrozen, "Not yet frozen!");
mFrozen = false;
mDebugger->Thaw();
return true;
}

View File

@ -727,6 +727,7 @@ class WorkerDebugger : public nsIWorkerDebugger {
// Only touched on the main thread.
bool mIsInitialized;
bool mIsFrozen;
nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> mListeners;
public:
@ -747,6 +748,12 @@ public:
void
Disable();
void
Freeze();
void
Thaw();
void
PostMessageToDebugger(const nsAString& aMessage);
@ -761,6 +768,12 @@ private:
void
NotifyIsEnabled(bool aIsEnabled);
void
FreezeOnMainThread();
void
ThawOnMainThread();
void
PostMessageToDebuggerOnMainThread(const nsAString& aMessage);

View File

@ -2,7 +2,7 @@
interface nsIDOMWindow;
[scriptable, uuid(55d54034-1573-4889-b1d9-93ba12fc33c7)]
[scriptable, uuid(530db841-1b2c-485a-beeb-f2b1acb9714e)]
interface nsIWorkerDebuggerListener : nsISupports
{
void onClose();
@ -10,10 +10,14 @@ interface nsIWorkerDebuggerListener : nsISupports
void onError(in DOMString filename, in unsigned long lineno,
in DOMString message);
void onFreeze();
void onMessage(in DOMString message);
void onThaw();
};
[scriptable, builtinclass, uuid(28e0a60c-ff10-446c-8c2a-5fbdc01394ea)]
[scriptable, builtinclass, uuid(d7c73e54-3c41-4393-9d13-fa2ed4Ba6764)]
interface nsIWorkerDebugger : nsISupports
{
const unsigned long TYPE_DEDICATED = 0;
@ -24,6 +28,8 @@ interface nsIWorkerDebugger : nsISupports
readonly attribute bool isChrome;
readonly attribute bool isFrozen;
readonly attribute nsIWorkerDebugger parent;
readonly attribute unsigned long type;

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
var worker = new Worker("WorkerDebugger.isFrozen_worker1.js");
worker.onmessage = function () {
parent.postMessage("ready", "*");
};
</script>
</head>
<body>
This is page 1.
</body>
<html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
var worker = new Worker("WorkerDebugger.isFrozen_worker2.js");
worker.onmessage = function () {
parent.postMessage("ready", "*");
};
</script>
</head>
<body>
This is page 2.
</body>
<html>

View File

@ -0,0 +1,5 @@
"use strict";
onmessage = function () {};
postMessage("ready");

View File

@ -0,0 +1,5 @@
"use strict";
onmessage = function () {};
postMessage("ready");

View File

@ -4,6 +4,10 @@ support-files =
WorkerDebugger.initialize_childWorker.js
WorkerDebugger.initialize_debugger.js
WorkerDebugger.initialize_worker.js
WorkerDebugger.isFrozen_iframe1.html
WorkerDebugger.isFrozen_iframe2.html
WorkerDebugger.isFrozen_worker1.js
WorkerDebugger.isFrozen_worker2.js
WorkerDebugger.postMessage_childWorker.js
WorkerDebugger.postMessage_debugger.js
WorkerDebugger.postMessage_worker.js
@ -47,6 +51,7 @@ support-files =
[test_WorkerDebugger.xul]
[test_WorkerDebugger.initialize.xul]
[test_WorkerDebugger.isFrozen.xul]
[test_WorkerDebugger.postMessage.xul]
[test_WorkerDebuggerGlobalScope.createSandbox.xul]
[test_WorkerDebuggerGlobalScope.enterEventLoop.xul]

View File

@ -130,6 +130,41 @@ function waitForDebuggerMessage(dbg, message) {
});
}
function waitForDebuggerFreeze(dbg) {
return new Promise(function (resolve) {
dbg.addListener({
onFreeze: function () {
dbg.removeListener(this);
resolve();
}
});
});
}
function waitForDebuggerThaw(dbg) {
return new Promise(function (resolve) {
dbg.addListener({
onThaw: function () {
dbg.removeListener(this);
resolve();
}
});
});
}
function waitForWindowMessage(window, message) {
return new Promise(function (resolve) {
let onmessage = function (event) {
if (event.data !== event.data) {
return;
}
window.removeEventListener("message", onmessage, false);
resolve();
};
window.addEventListener("message", onmessage, false);
});
}
function waitForWorkerMessage(worker, message) {
return new Promise(function (resolve) {
worker.addEventListener("message", function onmessage(event) {

View File

@ -0,0 +1,98 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebugger.isFrozen"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const CACHE_SUBFRAMES = "browser.sessionhistory.cache_subframes";
const MAX_TOTAL_VIEWERS = "browser.sessionhistory.max_total_viewers";
const IFRAME1_URL = "WorkerDebugger.isFrozen_iframe1.html";
const IFRAME2_URL = "WorkerDebugger.isFrozen_iframe2.html";
const WORKER1_URL = "WorkerDebugger.isFrozen_worker1.js";
const WORKER2_URL = "WorkerDebugger.isFrozen_worker2.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
var oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS);
SpecialPowers.setBoolPref(CACHE_SUBFRAMES, true);
SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, 10);
let iframe = $("iframe");
let promise = waitForMultiple([
waitForRegister(WORKER1_URL),
waitForWindowMessage(window, "ready"),
]);
iframe.src = IFRAME1_URL;
let [dbg1] = yield promise;
is(dbg1.isClosed, false,
"debugger for worker on page 1 should not be closed");
is(dbg1.isFrozen, false,
"debugger for worker on page 1 should not be frozen");
promise = waitForMultiple([
waitForDebuggerFreeze(dbg1),
waitForRegister(WORKER2_URL),
waitForWindowMessage(window, "ready"),
]);
iframe.src = IFRAME2_URL;
let [_, dbg2] = yield promise;
is(dbg1.isClosed, false,
"debugger for worker on page 1 should not be closed");
is(dbg1.isFrozen, true,
"debugger for worker on page 1 should be frozen");
is(dbg2.isClosed, false,
"debugger for worker on page 2 should not be closed");
is(dbg2.isFrozen, false,
"debugger for worker on page 2 should not be frozen");
promise = waitForMultiple([
waitForDebuggerFreeze(dbg2),
waitForDebuggerThaw(dbg1),
]);
iframe.contentWindow.history.back();
yield promise;
is(dbg1.isClosed, false,
"debugger for worker on page 1 should not be closed")
is(dbg1.isFrozen, false,
"debugger for worker on page 1 should not be frozen");
is(dbg2.isClosed, false,
"debugger for worker on page 2 should not be closed");
is(dbg2.isFrozen, true,
"debugger for worker on page 2 should be frozen");
SpecialPowers.clearUserPref(CACHE_SUBFRAMES);
SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, oldMaxTotalViewers);
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
<iframe id="iframe"></iframe>
</body>
<label id="test-result"/>
</window>

View File

@ -1452,9 +1452,15 @@ ChromeTooltipListener::sTooltipCallback(nsITimer *aTimer,
if (textFound) {
nsString tipText(tooltipText);
LayoutDeviceIntPoint screenDot = widget->WidgetToScreenOffset();
self->ShowTooltip(self->mMouseScreenX - screenDot.x,
self->mMouseScreenY - screenDot.y,
tipText);
double scaleFactor = 1.0;
if (shell->GetPresContext()) {
scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
shell->GetPresContext()->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
}
// ShowTooltip expects widget-relative position.
self->ShowTooltip(self->mMouseScreenX - screenDot.x / scaleFactor,
self->mMouseScreenY - screenDot.y / scaleFactor,
tipText);
}
}

Some files were not shown because too many files have changed in this diff Show More