зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2ginbound a=merge
This commit is contained in:
Коммит
53af4f8662
|
@ -8,6 +8,7 @@ TAGS
|
|||
tags
|
||||
ID
|
||||
.DS_Store*
|
||||
*.pdb
|
||||
|
||||
# Vim swap files.
|
||||
.*.sw[a-z]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -327,7 +327,7 @@ function waitForEvents(event)
|
|||
if (painted && loaded) {
|
||||
subwindow.removeEventListener("MozAfterPaint", waitForEvents, false);
|
||||
subwindow.onload = null;
|
||||
startTest();
|
||||
SimpleTest.waitForFocus(startTest, subwindow);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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"
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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"
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_frame_script.js
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
skip-if = buildapp == 'b2g'
|
||||
support-files =
|
||||
hosted_app.manifest
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_raf-begin.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
addon1.xpi
|
||||
|
|
|
@ -21,7 +21,7 @@ function test() {
|
|||
|
||||
const BP_LOCATION = {
|
||||
line: 5,
|
||||
column: 11
|
||||
// column: 0
|
||||
};
|
||||
|
||||
function findSource() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
color-block.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
browser_fontinspector.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
browser_toolbox_options_disable_js.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_frame_script.js
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_markup_anonymous.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
head.js
|
||||
|
|
|
@ -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]"
|
||||
};
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
|
@ -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]"
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_simple-test.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
head.js
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
head.js
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files = head.js
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_blended-geometry.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
browser_layoutHelpers.html
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)";
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
cm_comment_test.js
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
autocomplete.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_content_stylesheet.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
head =
|
||||
tail =
|
||||
firefox-appdir = browser
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_simple-test.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_simple-context.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
head.js
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
addons/simulators.json
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
skip-if = buildapp == 'b2g'
|
||||
support-files =
|
||||
app/index.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
../head.js
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
support-files =
|
||||
../app/index.html
|
||||
../app/manifest.webapp
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1265,15 +1265,6 @@ DOMInterfaces = {
|
|||
'wrapperCache': False,
|
||||
}],
|
||||
|
||||
'VRFieldOfView': {
|
||||
'wrapperCache': False,
|
||||
},
|
||||
|
||||
'VRFieldOfViewReadOnly': {
|
||||
'concrete': False,
|
||||
'wrapperCache': False,
|
||||
},
|
||||
|
||||
'VRDevice': {
|
||||
'concrete': False
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -275,8 +275,6 @@ private:
|
|||
|
||||
layers::LayersBackend mLayersBackendType;
|
||||
|
||||
nsTArray<nsTArray<uint8_t>> mInitDataEncountered;
|
||||
|
||||
// True if we've read the streams' metadata.
|
||||
bool mDemuxerInitialized;
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
onmessage = function () {};
|
||||
|
||||
postMessage("ready");
|
|
@ -0,0 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
onmessage = function () {};
|
||||
|
||||
postMessage("ready");
|
|
@ -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]
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче