mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-inbound a=merge
This commit is contained in:
commit
4b5a807a6e
@ -121,7 +121,7 @@ static bool IsArg(const char* arg, const char* s)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#if defined(XP_WIN) && defined(MOZ_METRO)
|
||||||
/*
|
/*
|
||||||
* AttachToTestHarness - Windows helper for when we are running
|
* AttachToTestHarness - Windows helper for when we are running
|
||||||
* in the immersive environment. Firefox is launched by Windows in
|
* in the immersive environment. Firefox is launched by Windows in
|
||||||
|
@ -1669,6 +1669,7 @@ pref("loop.oauth.google.scope", "https://www.google.com/m8/feeds");
|
|||||||
pref("loop.rooms.enabled", true);
|
pref("loop.rooms.enabled", true);
|
||||||
pref("loop.fxa_oauth.tokendata", "");
|
pref("loop.fxa_oauth.tokendata", "");
|
||||||
pref("loop.fxa_oauth.profile", "");
|
pref("loop.fxa_oauth.profile", "");
|
||||||
|
pref("loop.support_url", "https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc");
|
||||||
|
|
||||||
// serverURL to be assigned by services team
|
// serverURL to be assigned by services team
|
||||||
pref("services.push.serverURL", "wss://push.services.mozilla.com/");
|
pref("services.push.serverURL", "wss://push.services.mozilla.com/");
|
||||||
|
@ -12,6 +12,22 @@ function parseQueryString() {
|
|||||||
|
|
||||||
document.title = parseQueryString();
|
document.title = parseQueryString();
|
||||||
|
|
||||||
|
addEventListener("DOMContentLoaded", () => {
|
||||||
|
let tryAgain = document.getElementById("tryAgain");
|
||||||
|
let sendCrashReport = document.getElementById("checkSendReport");
|
||||||
|
|
||||||
|
tryAgain.addEventListener("click", () => {
|
||||||
|
let event = new CustomEvent("AboutTabCrashedTryAgain", {
|
||||||
|
bubbles: true,
|
||||||
|
detail: {
|
||||||
|
sendCrashReport: sendCrashReport.checked,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Error pages are loaded as LOAD_BACKGROUND, so they don't get load events.
|
// Error pages are loaded as LOAD_BACKGROUND, so they don't get load events.
|
||||||
var event = new CustomEvent("AboutTabCrashedLoad", {bubbles:true});
|
var event = new CustomEvent("AboutTabCrashedLoad", {bubbles:true});
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
|
@ -1142,6 +1142,28 @@ var gBrowserInit = {
|
|||||||
#endif
|
#endif
|
||||||
}, false, true);
|
}, false, true);
|
||||||
|
|
||||||
|
gBrowser.addEventListener("AboutTabCrashedTryAgain", function(event) {
|
||||||
|
let ownerDoc = event.originalTarget;
|
||||||
|
|
||||||
|
if (!ownerDoc.documentURI.startsWith("about:tabcrashed")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let isTopFrame = (ownerDoc.defaultView.parent === ownerDoc.defaultView);
|
||||||
|
if (!isTopFrame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let browser = gBrowser.getBrowserForDocument(ownerDoc);
|
||||||
|
#ifdef MOZ_CRASHREPORTER
|
||||||
|
if (event.detail.sendCrashReport) {
|
||||||
|
TabCrashReporter.submitCrashReport(browser);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
let tab = gBrowser.getTabForBrowser(browser);
|
||||||
|
SessionStore.reviveCrashedTab(tab);
|
||||||
|
}, false, true);
|
||||||
|
|
||||||
if (uriToLoad && uriToLoad != "about:blank") {
|
if (uriToLoad && uriToLoad != "about:blank") {
|
||||||
if (uriToLoad instanceof Ci.nsISupportsArray) {
|
if (uriToLoad instanceof Ci.nsISupportsArray) {
|
||||||
let count = uriToLoad.Count();
|
let count = uriToLoad.Count();
|
||||||
@ -2606,9 +2628,6 @@ let BrowserOnClick = {
|
|||||||
ownerDoc.documentURI.toLowerCase() == "about:newtab") {
|
ownerDoc.documentURI.toLowerCase() == "about:newtab") {
|
||||||
this.onE10sAboutNewTab(event, ownerDoc);
|
this.onE10sAboutNewTab(event, ownerDoc);
|
||||||
}
|
}
|
||||||
else if (ownerDoc.documentURI.startsWith("about:tabcrashed")) {
|
|
||||||
this.onAboutTabCrashed(event, ownerDoc);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
receiveMessage: function (msg) {
|
receiveMessage: function (msg) {
|
||||||
@ -2869,29 +2888,6 @@ let BrowserOnClick = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* The about:tabcrashed can't do window.reload() because that
|
|
||||||
* would reload the page but not use a remote browser.
|
|
||||||
*/
|
|
||||||
onAboutTabCrashed: function(event, ownerDoc) {
|
|
||||||
let isTopFrame = (ownerDoc.defaultView.parent === ownerDoc.defaultView);
|
|
||||||
if (!isTopFrame) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let button = event.originalTarget;
|
|
||||||
if (button.id == "tryAgain") {
|
|
||||||
let browser = gBrowser.getBrowserForDocument(ownerDoc);
|
|
||||||
#ifdef MOZ_CRASHREPORTER
|
|
||||||
if (ownerDoc.getElementById("checkSendReport").checked) {
|
|
||||||
TabCrashReporter.submitCrashReport(browser);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
let tab = gBrowser.getTabForBrowser(browser);
|
|
||||||
SessionStore.reviveCrashedTab(tab);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ignoreWarningButton: function (isMalware) {
|
ignoreWarningButton: function (isMalware) {
|
||||||
// Allow users to override and continue through to the site,
|
// Allow users to override and continue through to the site,
|
||||||
// but add a notify bar as a reminder, so that they don't lose
|
// but add a notify bar as a reminder, so that they don't lose
|
||||||
|
@ -38,6 +38,8 @@
|
|||||||
<script type="text/javascript" src="loop/shared/js/roomStore.js"></script>
|
<script type="text/javascript" src="loop/shared/js/roomStore.js"></script>
|
||||||
<script type="text/javascript" src="loop/shared/js/conversationStore.js"></script>
|
<script type="text/javascript" src="loop/shared/js/conversationStore.js"></script>
|
||||||
<script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
|
<script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
|
||||||
|
<script type="text/javascript" src="loop/shared/js/feedbackStore.js"></script>
|
||||||
|
<script type="text/javascript" src="loop/shared/js/feedbackViews.js"></script>
|
||||||
<script type="text/javascript" src="loop/js/conversationViews.js"></script>
|
<script type="text/javascript" src="loop/js/conversationViews.js"></script>
|
||||||
<script type="text/javascript" src="loop/shared/js/websocket.js"></script>
|
<script type="text/javascript" src="loop/shared/js/websocket.js"></script>
|
||||||
<script type="text/javascript" src="loop/js/conversationAppStore.js"></script>
|
<script type="text/javascript" src="loop/js/conversationAppStore.js"></script>
|
||||||
|
@ -229,7 +229,8 @@ loop.conversation = (function(mozL10n) {
|
|||||||
.isRequired,
|
.isRequired,
|
||||||
sdk: React.PropTypes.object.isRequired,
|
sdk: React.PropTypes.object.isRequired,
|
||||||
conversationAppStore: React.PropTypes.instanceOf(
|
conversationAppStore: React.PropTypes.instanceOf(
|
||||||
loop.store.ConversationAppStore).isRequired
|
loop.store.ConversationAppStore).isRequired,
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -301,21 +302,9 @@ loop.conversation = (function(mozL10n) {
|
|||||||
|
|
||||||
document.title = mozL10n.get("conversation_has_ended");
|
document.title = mozL10n.get("conversation_has_ended");
|
||||||
|
|
||||||
var feebackAPIBaseUrl = navigator.mozLoop.getLoopPref(
|
|
||||||
"feedback.baseUrl");
|
|
||||||
|
|
||||||
var appVersionInfo = navigator.mozLoop.appVersionInfo;
|
|
||||||
|
|
||||||
var feedbackClient = new loop.FeedbackAPIClient(feebackAPIBaseUrl, {
|
|
||||||
product: navigator.mozLoop.getLoopPref("feedback.product"),
|
|
||||||
platform: appVersionInfo.OS,
|
|
||||||
channel: appVersionInfo.channel,
|
|
||||||
version: appVersionInfo.version
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
sharedViews.FeedbackView({
|
sharedViews.FeedbackView({
|
||||||
feedbackApiClient: feedbackClient,
|
feedbackStore: this.props.feedbackStore,
|
||||||
onAfterFeedbackReceived: this.closeWindow.bind(this)}
|
onAfterFeedbackReceived: this.closeWindow.bind(this)}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -562,7 +551,8 @@ loop.conversation = (function(mozL10n) {
|
|||||||
conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore)
|
conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore)
|
||||||
.isRequired,
|
.isRequired,
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||||
roomStore: React.PropTypes.instanceOf(loop.store.RoomStore)
|
roomStore: React.PropTypes.instanceOf(loop.store.RoomStore),
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -590,26 +580,26 @@ loop.conversation = (function(mozL10n) {
|
|||||||
client: this.props.client,
|
client: this.props.client,
|
||||||
conversation: this.props.conversation,
|
conversation: this.props.conversation,
|
||||||
sdk: this.props.sdk,
|
sdk: this.props.sdk,
|
||||||
conversationAppStore: this.props.conversationAppStore}
|
conversationAppStore: this.props.conversationAppStore,
|
||||||
|
feedbackStore: this.props.feedbackStore}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
case "outgoing": {
|
case "outgoing": {
|
||||||
return (OutgoingConversationView({
|
return (OutgoingConversationView({
|
||||||
store: this.props.conversationStore,
|
store: this.props.conversationStore,
|
||||||
dispatcher: this.props.dispatcher}
|
dispatcher: this.props.dispatcher,
|
||||||
|
feedbackStore: this.props.feedbackStore}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
case "room": {
|
case "room": {
|
||||||
return (DesktopRoomConversationView({
|
return (DesktopRoomConversationView({
|
||||||
dispatcher: this.props.dispatcher,
|
dispatcher: this.props.dispatcher,
|
||||||
roomStore: this.props.roomStore,
|
roomStore: this.props.roomStore,
|
||||||
dispatcher: this.props.dispatcher}
|
feedbackStore: this.props.feedbackStore}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
case "failed": {
|
case "failed": {
|
||||||
return (GenericFailureView({
|
return GenericFailureView({cancelCall: this.closeWindow});
|
||||||
cancelCall: this.closeWindow}
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
// If we don't have a windowType, we don't know what we are yet,
|
// If we don't have a windowType, we don't know what we are yet,
|
||||||
@ -646,6 +636,14 @@ loop.conversation = (function(mozL10n) {
|
|||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
sdk: OT
|
sdk: OT
|
||||||
});
|
});
|
||||||
|
var appVersionInfo = navigator.mozLoop.appVersionInfo;
|
||||||
|
var feedbackClient = new loop.FeedbackAPIClient(
|
||||||
|
navigator.mozLoop.getLoopPref("feedback.baseUrl"), {
|
||||||
|
product: navigator.mozLoop.getLoopPref("feedback.product"),
|
||||||
|
platform: appVersionInfo.OS,
|
||||||
|
channel: appVersionInfo.channel,
|
||||||
|
version: appVersionInfo.version
|
||||||
|
});
|
||||||
|
|
||||||
// Create the stores.
|
// Create the stores.
|
||||||
var conversationAppStore = new loop.store.ConversationAppStore({
|
var conversationAppStore = new loop.store.ConversationAppStore({
|
||||||
@ -665,6 +663,9 @@ loop.conversation = (function(mozL10n) {
|
|||||||
mozLoop: navigator.mozLoop,
|
mozLoop: navigator.mozLoop,
|
||||||
activeRoomStore: activeRoomStore
|
activeRoomStore: activeRoomStore
|
||||||
});
|
});
|
||||||
|
var feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: feedbackClient
|
||||||
|
});
|
||||||
|
|
||||||
// XXX Old class creation for the incoming conversation view, whilst
|
// XXX Old class creation for the incoming conversation view, whilst
|
||||||
// we transition across (bug 1072323).
|
// we transition across (bug 1072323).
|
||||||
@ -697,6 +698,7 @@ loop.conversation = (function(mozL10n) {
|
|||||||
React.renderComponent(AppControllerView({
|
React.renderComponent(AppControllerView({
|
||||||
conversationAppStore: conversationAppStore,
|
conversationAppStore: conversationAppStore,
|
||||||
roomStore: roomStore,
|
roomStore: roomStore,
|
||||||
|
feedbackStore: feedbackStore,
|
||||||
conversationStore: conversationStore,
|
conversationStore: conversationStore,
|
||||||
client: client,
|
client: client,
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
|
@ -229,7 +229,8 @@ loop.conversation = (function(mozL10n) {
|
|||||||
.isRequired,
|
.isRequired,
|
||||||
sdk: React.PropTypes.object.isRequired,
|
sdk: React.PropTypes.object.isRequired,
|
||||||
conversationAppStore: React.PropTypes.instanceOf(
|
conversationAppStore: React.PropTypes.instanceOf(
|
||||||
loop.store.ConversationAppStore).isRequired
|
loop.store.ConversationAppStore).isRequired,
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -301,21 +302,9 @@ loop.conversation = (function(mozL10n) {
|
|||||||
|
|
||||||
document.title = mozL10n.get("conversation_has_ended");
|
document.title = mozL10n.get("conversation_has_ended");
|
||||||
|
|
||||||
var feebackAPIBaseUrl = navigator.mozLoop.getLoopPref(
|
|
||||||
"feedback.baseUrl");
|
|
||||||
|
|
||||||
var appVersionInfo = navigator.mozLoop.appVersionInfo;
|
|
||||||
|
|
||||||
var feedbackClient = new loop.FeedbackAPIClient(feebackAPIBaseUrl, {
|
|
||||||
product: navigator.mozLoop.getLoopPref("feedback.product"),
|
|
||||||
platform: appVersionInfo.OS,
|
|
||||||
channel: appVersionInfo.channel,
|
|
||||||
version: appVersionInfo.version
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<sharedViews.FeedbackView
|
<sharedViews.FeedbackView
|
||||||
feedbackApiClient={feedbackClient}
|
feedbackStore={this.props.feedbackStore}
|
||||||
onAfterFeedbackReceived={this.closeWindow.bind(this)}
|
onAfterFeedbackReceived={this.closeWindow.bind(this)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -562,7 +551,8 @@ loop.conversation = (function(mozL10n) {
|
|||||||
conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore)
|
conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore)
|
||||||
.isRequired,
|
.isRequired,
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||||
roomStore: React.PropTypes.instanceOf(loop.store.RoomStore)
|
roomStore: React.PropTypes.instanceOf(loop.store.RoomStore),
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -591,25 +581,25 @@ loop.conversation = (function(mozL10n) {
|
|||||||
conversation={this.props.conversation}
|
conversation={this.props.conversation}
|
||||||
sdk={this.props.sdk}
|
sdk={this.props.sdk}
|
||||||
conversationAppStore={this.props.conversationAppStore}
|
conversationAppStore={this.props.conversationAppStore}
|
||||||
|
feedbackStore={this.props.feedbackStore}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
case "outgoing": {
|
case "outgoing": {
|
||||||
return (<OutgoingConversationView
|
return (<OutgoingConversationView
|
||||||
store={this.props.conversationStore}
|
store={this.props.conversationStore}
|
||||||
dispatcher={this.props.dispatcher}
|
dispatcher={this.props.dispatcher}
|
||||||
|
feedbackStore={this.props.feedbackStore}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
case "room": {
|
case "room": {
|
||||||
return (<DesktopRoomConversationView
|
return (<DesktopRoomConversationView
|
||||||
dispatcher={this.props.dispatcher}
|
dispatcher={this.props.dispatcher}
|
||||||
roomStore={this.props.roomStore}
|
roomStore={this.props.roomStore}
|
||||||
dispatcher={this.props.dispatcher}
|
feedbackStore={this.props.feedbackStore}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
case "failed": {
|
case "failed": {
|
||||||
return (<GenericFailureView
|
return <GenericFailureView cancelCall={this.closeWindow} />;
|
||||||
cancelCall={this.closeWindow}
|
|
||||||
/>);
|
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
// If we don't have a windowType, we don't know what we are yet,
|
// If we don't have a windowType, we don't know what we are yet,
|
||||||
@ -646,6 +636,14 @@ loop.conversation = (function(mozL10n) {
|
|||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
sdk: OT
|
sdk: OT
|
||||||
});
|
});
|
||||||
|
var appVersionInfo = navigator.mozLoop.appVersionInfo;
|
||||||
|
var feedbackClient = new loop.FeedbackAPIClient(
|
||||||
|
navigator.mozLoop.getLoopPref("feedback.baseUrl"), {
|
||||||
|
product: navigator.mozLoop.getLoopPref("feedback.product"),
|
||||||
|
platform: appVersionInfo.OS,
|
||||||
|
channel: appVersionInfo.channel,
|
||||||
|
version: appVersionInfo.version
|
||||||
|
});
|
||||||
|
|
||||||
// Create the stores.
|
// Create the stores.
|
||||||
var conversationAppStore = new loop.store.ConversationAppStore({
|
var conversationAppStore = new loop.store.ConversationAppStore({
|
||||||
@ -665,6 +663,9 @@ loop.conversation = (function(mozL10n) {
|
|||||||
mozLoop: navigator.mozLoop,
|
mozLoop: navigator.mozLoop,
|
||||||
activeRoomStore: activeRoomStore
|
activeRoomStore: activeRoomStore
|
||||||
});
|
});
|
||||||
|
var feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: feedbackClient
|
||||||
|
});
|
||||||
|
|
||||||
// XXX Old class creation for the incoming conversation view, whilst
|
// XXX Old class creation for the incoming conversation view, whilst
|
||||||
// we transition across (bug 1072323).
|
// we transition across (bug 1072323).
|
||||||
@ -697,6 +698,7 @@ loop.conversation = (function(mozL10n) {
|
|||||||
React.renderComponent(<AppControllerView
|
React.renderComponent(<AppControllerView
|
||||||
conversationAppStore={conversationAppStore}
|
conversationAppStore={conversationAppStore}
|
||||||
roomStore={roomStore}
|
roomStore={roomStore}
|
||||||
|
feedbackStore={feedbackStore}
|
||||||
conversationStore={conversationStore}
|
conversationStore={conversationStore}
|
||||||
client={client}
|
client={client}
|
||||||
conversation={conversation}
|
conversation={conversation}
|
||||||
|
@ -356,7 +356,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||||||
nameDisplayMode: "off",
|
nameDisplayMode: "off",
|
||||||
videoDisabledDisplayMode: "off"
|
videoDisabledDisplayMode: "off"
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -431,7 +431,8 @@ loop.conversationViews = (function(mozL10n) {
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||||
store: React.PropTypes.instanceOf(
|
store: React.PropTypes.instanceOf(
|
||||||
loop.store.ConversationStore).isRequired
|
loop.store.ConversationStore).isRequired,
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -462,22 +463,9 @@ loop.conversationViews = (function(mozL10n) {
|
|||||||
_renderFeedbackView: function() {
|
_renderFeedbackView: function() {
|
||||||
document.title = mozL10n.get("conversation_has_ended");
|
document.title = mozL10n.get("conversation_has_ended");
|
||||||
|
|
||||||
// XXX Bug 1076754 Feedback view should be redone in the Flux style.
|
|
||||||
var feebackAPIBaseUrl = navigator.mozLoop.getLoopPref(
|
|
||||||
"feedback.baseUrl");
|
|
||||||
|
|
||||||
var appVersionInfo = navigator.mozLoop.appVersionInfo;
|
|
||||||
|
|
||||||
var feedbackClient = new loop.FeedbackAPIClient(feebackAPIBaseUrl, {
|
|
||||||
product: navigator.mozLoop.getLoopPref("feedback.product"),
|
|
||||||
platform: appVersionInfo.OS,
|
|
||||||
channel: appVersionInfo.channel,
|
|
||||||
version: appVersionInfo.version
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
sharedViews.FeedbackView({
|
sharedViews.FeedbackView({
|
||||||
feedbackApiClient: feedbackClient,
|
feedbackStore: this.props.feedbackStore,
|
||||||
onAfterFeedbackReceived: this._closeWindow.bind(this)}
|
onAfterFeedbackReceived: this._closeWindow.bind(this)}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -356,7 +356,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||||||
nameDisplayMode: "off",
|
nameDisplayMode: "off",
|
||||||
videoDisabledDisplayMode: "off"
|
videoDisabledDisplayMode: "off"
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -431,7 +431,8 @@ loop.conversationViews = (function(mozL10n) {
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||||
store: React.PropTypes.instanceOf(
|
store: React.PropTypes.instanceOf(
|
||||||
loop.store.ConversationStore).isRequired
|
loop.store.ConversationStore).isRequired,
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -462,22 +463,9 @@ loop.conversationViews = (function(mozL10n) {
|
|||||||
_renderFeedbackView: function() {
|
_renderFeedbackView: function() {
|
||||||
document.title = mozL10n.get("conversation_has_ended");
|
document.title = mozL10n.get("conversation_has_ended");
|
||||||
|
|
||||||
// XXX Bug 1076754 Feedback view should be redone in the Flux style.
|
|
||||||
var feebackAPIBaseUrl = navigator.mozLoop.getLoopPref(
|
|
||||||
"feedback.baseUrl");
|
|
||||||
|
|
||||||
var appVersionInfo = navigator.mozLoop.appVersionInfo;
|
|
||||||
|
|
||||||
var feedbackClient = new loop.FeedbackAPIClient(feebackAPIBaseUrl, {
|
|
||||||
product: navigator.mozLoop.getLoopPref("feedback.product"),
|
|
||||||
platform: appVersionInfo.OS,
|
|
||||||
channel: appVersionInfo.channel,
|
|
||||||
version: appVersionInfo.version
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<sharedViews.FeedbackView
|
<sharedViews.FeedbackView
|
||||||
feedbackApiClient={feedbackClient}
|
feedbackStore={this.props.feedbackStore}
|
||||||
onAfterFeedbackReceived={this._closeWindow.bind(this)}
|
onAfterFeedbackReceived={this._closeWindow.bind(this)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -281,6 +281,13 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleHelpEntry: function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var helloSupportUrl = navigator.mozLoop.getLoopPref('support_url');
|
||||||
|
window.open(helloSupportUrl);
|
||||||
|
window.close();
|
||||||
|
},
|
||||||
|
|
||||||
_isSignedIn: function() {
|
_isSignedIn: function() {
|
||||||
return !!navigator.mozLoop.userProfile;
|
return !!navigator.mozLoop.userProfile;
|
||||||
},
|
},
|
||||||
@ -318,7 +325,10 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
mozL10n.get("settings_menu_item_signin"),
|
mozL10n.get("settings_menu_item_signin"),
|
||||||
onClick: this.handleClickAuthEntry,
|
onClick: this.handleClickAuthEntry,
|
||||||
displayed: navigator.mozLoop.fxAEnabled,
|
displayed: navigator.mozLoop.fxAEnabled,
|
||||||
icon: this._isSignedIn() ? "signout" : "signin"})
|
icon: this._isSignedIn() ? "signout" : "signin"}),
|
||||||
|
SettingsDropdownEntry({label: mozL10n.get("help_label"),
|
||||||
|
onClick: this.handleHelpEntry,
|
||||||
|
icon: "help"})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -281,6 +281,13 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleHelpEntry: function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var helloSupportUrl = navigator.mozLoop.getLoopPref('support_url');
|
||||||
|
window.open(helloSupportUrl);
|
||||||
|
window.close();
|
||||||
|
},
|
||||||
|
|
||||||
_isSignedIn: function() {
|
_isSignedIn: function() {
|
||||||
return !!navigator.mozLoop.userProfile;
|
return !!navigator.mozLoop.userProfile;
|
||||||
},
|
},
|
||||||
@ -319,6 +326,9 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
onClick={this.handleClickAuthEntry}
|
onClick={this.handleClickAuthEntry}
|
||||||
displayed={navigator.mozLoop.fxAEnabled}
|
displayed={navigator.mozLoop.fxAEnabled}
|
||||||
icon={this._isSignedIn() ? "signout" : "signin"} />
|
icon={this._isSignedIn() ? "signout" : "signin"} />
|
||||||
|
<SettingsDropdownEntry label={mozL10n.get("help_label")}
|
||||||
|
onClick={this.handleHelpEntry}
|
||||||
|
icon="help" />
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -706,6 +706,7 @@ html, .fx-embedded, #main,
|
|||||||
background: #000;
|
background: #000;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
width: 75%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-conversation-wrapper header h1 {
|
.room-conversation-wrapper header h1 {
|
||||||
@ -717,6 +718,20 @@ html, .fx-embedded, #main,
|
|||||||
background-size: 30px;
|
background-size: 30px;
|
||||||
background-position: 10px;
|
background-position: 10px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-conversation-wrapper header a {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-conversation-wrapper header .icon-help {
|
||||||
|
display: inline-block;
|
||||||
|
background-size: contain;
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background: transparent url("../img/svg/glyph-help-16x16.svg") no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-conversation-wrapper footer {
|
.room-conversation-wrapper footer {
|
||||||
|
@ -659,6 +659,10 @@ body[dir=rtl] .generate-url-spinner {
|
|||||||
background: transparent url(../img/svg/glyph-signout-16x16.svg) no-repeat center center;
|
background: transparent url(../img/svg/glyph-signout-16x16.svg) no-repeat center center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-menu .icon-help {
|
||||||
|
background: transparent url(../img/svg/glyph-help-16x16.svg) no-repeat center center;
|
||||||
|
}
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||||
|
<circle fill="#5A5A5A" cx="8" cy="8" r="8"/>
|
||||||
|
<g>
|
||||||
|
<path fill="#FFFFFF" d="M10.716,5.643c0,1.943-2.158,1.812-2.158,3.154v0.3H6.831V8.726c0-2.075,1.907-1.932,1.907-2.915
|
||||||
|
c0-0.432-0.312-0.684-0.84-0.684c-0.491,0-0.972,0.24-1.403,0.731L5.284,4.923C5.967,4.121,6.855,3.64,8.09,3.64
|
||||||
|
C9.841,3.64,10.716,4.576,10.716,5.643z M8.797,11.268c0,0.6-0.479,1.092-1.079,1.092s-1.079-0.492-1.079-1.092
|
||||||
|
c0-0.588,0.479-1.079,1.079-1.079S8.797,10.68,8.797,11.268z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -325,6 +325,28 @@ loop.shared.actions = (function() {
|
|||||||
* Used to indicate the user wishes to leave the room.
|
* Used to indicate the user wishes to leave the room.
|
||||||
*/
|
*/
|
||||||
LeaveRoom: Action.define("leaveRoom", {
|
LeaveRoom: Action.define("leaveRoom", {
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires detailed information on sad feedback.
|
||||||
|
*/
|
||||||
|
RequireFeedbackDetails: Action.define("requireFeedbackDetails", {
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send feedback data.
|
||||||
|
*/
|
||||||
|
SendFeedback: Action.define("sendFeedback", {
|
||||||
|
happy: Boolean,
|
||||||
|
category: String,
|
||||||
|
description: String
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reacts on feedback submission error.
|
||||||
|
*/
|
||||||
|
SendFeedbackError: Action.define("sendFeedbackError", {
|
||||||
|
error: Error
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
@ -107,8 +107,8 @@ loop.FeedbackAPIClient = (function($, _) {
|
|||||||
req.fail(function(jqXHR, textStatus, errorThrown) {
|
req.fail(function(jqXHR, textStatus, errorThrown) {
|
||||||
var message = "Error posting user feedback data";
|
var message = "Error posting user feedback data";
|
||||||
var httpError = jqXHR.status + " " + errorThrown;
|
var httpError = jqXHR.status + " " + errorThrown;
|
||||||
console.error(message, httpError, JSON.stringify(jqXHR.responseJSON));
|
cb(new Error(message + ": " + httpError + "; " +
|
||||||
cb(new Error(message + ": " + httpError));
|
(jqXHR.responseJSON && jqXHR.responseJSON.detail || "")));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
98
browser/components/loop/content/shared/js/feedbackStore.js
Normal file
98
browser/components/loop/content/shared/js/feedbackStore.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/* global loop:true */
|
||||||
|
|
||||||
|
var loop = loop || {};
|
||||||
|
loop.store = loop.store || {};
|
||||||
|
|
||||||
|
loop.store.FeedbackStore = (function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var sharedActions = loop.shared.actions;
|
||||||
|
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES = {
|
||||||
|
// Initial state (mood selection)
|
||||||
|
INIT: "feedback-init",
|
||||||
|
// User detailed feedback form step
|
||||||
|
DETAILS: "feedback-details",
|
||||||
|
// Pending feedback data submission
|
||||||
|
PENDING: "feedback-pending",
|
||||||
|
// Feedback has been sent
|
||||||
|
SENT: "feedback-sent",
|
||||||
|
// There was an issue with the feedback API
|
||||||
|
FAILED: "feedback-failed"
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feedback store.
|
||||||
|
*
|
||||||
|
* @param {loop.Dispatcher} dispatcher The dispatcher for dispatching actions
|
||||||
|
* and registering to consume actions.
|
||||||
|
* @param {Object} options Options object:
|
||||||
|
* - {mozLoop} mozLoop The MozLoop API object.
|
||||||
|
* - {feedbackClient} loop.FeedbackAPIClient The feedback API client.
|
||||||
|
*/
|
||||||
|
var FeedbackStore = loop.store.createStore({
|
||||||
|
actions: [
|
||||||
|
"requireFeedbackDetails",
|
||||||
|
"sendFeedback",
|
||||||
|
"sendFeedbackError"
|
||||||
|
],
|
||||||
|
|
||||||
|
initialize: function(options) {
|
||||||
|
if (!options.feedbackClient) {
|
||||||
|
throw new Error("Missing option feedbackClient");
|
||||||
|
}
|
||||||
|
this._feedbackClient = options.feedbackClient;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns initial state data for this active room.
|
||||||
|
*/
|
||||||
|
getInitialStoreState: function() {
|
||||||
|
return {feedbackState: FEEDBACK_STATES.INIT};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires user detailed feedback.
|
||||||
|
*/
|
||||||
|
requireFeedbackDetails: function() {
|
||||||
|
this.setStoreState({feedbackState: FEEDBACK_STATES.DETAILS});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends feedback data to the feedback server.
|
||||||
|
*
|
||||||
|
* @param {sharedActions.SendFeedback} actionData The action data.
|
||||||
|
*/
|
||||||
|
sendFeedback: function(actionData) {
|
||||||
|
delete actionData.name;
|
||||||
|
this._feedbackClient.send(actionData, function(err) {
|
||||||
|
if (err) {
|
||||||
|
this.dispatchAction(new sharedActions.SendFeedbackError({
|
||||||
|
error: err
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setStoreState({feedbackState: FEEDBACK_STATES.SENT});
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.setStoreState({feedbackState: FEEDBACK_STATES.PENDING});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies a store from any error encountered while sending feedback data.
|
||||||
|
*
|
||||||
|
* @param {sharedActions.SendFeedback} actionData The action data.
|
||||||
|
*/
|
||||||
|
sendFeedbackError: function(actionData) {
|
||||||
|
this.setStoreState({
|
||||||
|
feedbackState: FEEDBACK_STATES.FAILED,
|
||||||
|
error: actionData.error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return FeedbackStore;
|
||||||
|
})();
|
326
browser/components/loop/content/shared/js/feedbackViews.js
Normal file
326
browser/components/loop/content/shared/js/feedbackViews.js
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
/** @jsx React.DOM */
|
||||||
|
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/* jshint newcap:false */
|
||||||
|
/* global loop:true, React */
|
||||||
|
var loop = loop || {};
|
||||||
|
loop.shared = loop.shared || {};
|
||||||
|
loop.shared.views = loop.shared.views || {};
|
||||||
|
loop.shared.views.FeedbackView = (function(l10n) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var sharedActions = loop.shared.actions;
|
||||||
|
var sharedMixins = loop.shared.mixins;
|
||||||
|
|
||||||
|
var WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS = 5;
|
||||||
|
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feedback outer layout.
|
||||||
|
*
|
||||||
|
* Props:
|
||||||
|
* -
|
||||||
|
*/
|
||||||
|
var FeedbackLayout = React.createClass({displayName: 'FeedbackLayout',
|
||||||
|
propTypes: {
|
||||||
|
children: React.PropTypes.component.isRequired,
|
||||||
|
title: React.PropTypes.string.isRequired,
|
||||||
|
reset: React.PropTypes.func // if not specified, no Back btn is shown
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var backButton = React.DOM.div(null);
|
||||||
|
if (this.props.reset) {
|
||||||
|
backButton = (
|
||||||
|
React.DOM.button({className: "fx-embedded-btn-back", type: "button",
|
||||||
|
onClick: this.props.reset},
|
||||||
|
"« ", l10n.get("feedback_back_button")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
React.DOM.div({className: "feedback"},
|
||||||
|
backButton,
|
||||||
|
React.DOM.h3(null, this.props.title),
|
||||||
|
this.props.children
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detailed feedback form.
|
||||||
|
*/
|
||||||
|
var FeedbackForm = React.createClass({displayName: 'FeedbackForm',
|
||||||
|
propTypes: {
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore),
|
||||||
|
pending: React.PropTypes.bool,
|
||||||
|
reset: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {category: "", description: ""};
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {pending: false};
|
||||||
|
},
|
||||||
|
|
||||||
|
_getCategories: function() {
|
||||||
|
return {
|
||||||
|
audio_quality: l10n.get("feedback_category_audio_quality"),
|
||||||
|
video_quality: l10n.get("feedback_category_video_quality"),
|
||||||
|
disconnected : l10n.get("feedback_category_was_disconnected"),
|
||||||
|
confusing: l10n.get("feedback_category_confusing"),
|
||||||
|
other: l10n.get("feedback_category_other")
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_getCategoryFields: function() {
|
||||||
|
var categories = this._getCategories();
|
||||||
|
return Object.keys(categories).map(function(category, key) {
|
||||||
|
return (
|
||||||
|
React.DOM.label({key: key, className: "feedback-category-label"},
|
||||||
|
React.DOM.input({type: "radio", ref: "category", name: "category",
|
||||||
|
className: "feedback-category-radio",
|
||||||
|
value: category,
|
||||||
|
onChange: this.handleCategoryChange,
|
||||||
|
checked: this.state.category === category}),
|
||||||
|
categories[category]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the form is ready for submission:
|
||||||
|
*
|
||||||
|
* - no feedback submission should be pending.
|
||||||
|
* - a category (reason) must be chosen;
|
||||||
|
* - if the "other" category is chosen, a custom description must have been
|
||||||
|
* entered by the end user;
|
||||||
|
*
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
_isFormReady: function() {
|
||||||
|
if (this.props.pending || !this.state.category) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.state.category === "other" && !this.state.description) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCategoryChange: function(event) {
|
||||||
|
var category = event.target.value;
|
||||||
|
this.setState({
|
||||||
|
category: category,
|
||||||
|
description: category == "other" ? "" : this._getCategories()[category]
|
||||||
|
});
|
||||||
|
if (category == "other") {
|
||||||
|
this.refs.description.getDOMNode().focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDescriptionFieldChange: function(event) {
|
||||||
|
this.setState({description: event.target.value});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDescriptionFieldFocus: function(event) {
|
||||||
|
this.setState({category: "other", description: ""});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleFormSubmit: function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
// XXX this feels ugly, we really want a feedbackActions object here.
|
||||||
|
this.props.feedbackStore.dispatchAction(new sharedActions.SendFeedback({
|
||||||
|
happy: false,
|
||||||
|
category: this.state.category,
|
||||||
|
description: this.state.description
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var descriptionDisplayValue = this.state.category === "other" ?
|
||||||
|
this.state.description : "";
|
||||||
|
return (
|
||||||
|
FeedbackLayout({title: l10n.get("feedback_what_makes_you_sad"),
|
||||||
|
reset: this.props.reset},
|
||||||
|
React.DOM.form({onSubmit: this.handleFormSubmit},
|
||||||
|
this._getCategoryFields(),
|
||||||
|
React.DOM.p(null,
|
||||||
|
React.DOM.input({type: "text", ref: "description", name: "description",
|
||||||
|
className: "feedback-description",
|
||||||
|
onChange: this.handleDescriptionFieldChange,
|
||||||
|
onFocus: this.handleDescriptionFieldFocus,
|
||||||
|
value: descriptionDisplayValue,
|
||||||
|
placeholder:
|
||||||
|
l10n.get("feedback_custom_category_text_placeholder")})
|
||||||
|
),
|
||||||
|
React.DOM.button({type: "submit", className: "btn btn-success",
|
||||||
|
disabled: !this._isFormReady()},
|
||||||
|
l10n.get("feedback_submit_button")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feedback received view.
|
||||||
|
*
|
||||||
|
* Props:
|
||||||
|
* - {Function} onAfterFeedbackReceived Function to execute after the
|
||||||
|
* WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS timeout has elapsed
|
||||||
|
*/
|
||||||
|
var FeedbackReceived = React.createClass({displayName: 'FeedbackReceived',
|
||||||
|
propTypes: {
|
||||||
|
onAfterFeedbackReceived: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {countdown: WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this._timer = setInterval(function() {
|
||||||
|
this.setState({countdown: this.state.countdown - 1});
|
||||||
|
}.bind(this), 1000);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
if (this._timer) {
|
||||||
|
clearInterval(this._timer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
if (this.state.countdown < 1) {
|
||||||
|
clearInterval(this._timer);
|
||||||
|
if (this.props.onAfterFeedbackReceived) {
|
||||||
|
this.props.onAfterFeedbackReceived();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
FeedbackLayout({title: l10n.get("feedback_thank_you_heading")},
|
||||||
|
React.DOM.p({className: "info thank-you"},
|
||||||
|
l10n.get("feedback_window_will_close_in2", {
|
||||||
|
countdown: this.state.countdown,
|
||||||
|
num: this.state.countdown
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feedback view.
|
||||||
|
*/
|
||||||
|
var FeedbackView = React.createClass({displayName: 'FeedbackView',
|
||||||
|
mixins: [Backbone.Events, sharedMixins.AudioMixin],
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore),
|
||||||
|
onAfterFeedbackReceived: React.PropTypes.func,
|
||||||
|
// Used by the UI showcase.
|
||||||
|
feedbackState: React.PropTypes.string
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
var storeState = this.props.feedbackStore.getStoreState();
|
||||||
|
return _.extend({}, storeState, {
|
||||||
|
feedbackState: this.props.feedbackState || storeState.feedbackState
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
this.listenTo(this.props.feedbackStore, "change", this._onStoreStateChanged);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this.play("terminated");
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
this.stopListening(this.props.feedbackStore);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onStoreStateChanged: function() {
|
||||||
|
this.setState(this.props.feedbackStore.getStoreState());
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: function() {
|
||||||
|
this.setState(this.props.feedbackStore.getInitialStoreState());
|
||||||
|
},
|
||||||
|
|
||||||
|
handleHappyClick: function() {
|
||||||
|
// XXX: If the user is happy, we directly send this information to the
|
||||||
|
// feedback API; this is a behavior we might want to revisit later.
|
||||||
|
this.props.feedbackStore.dispatchAction(new sharedActions.SendFeedback({
|
||||||
|
happy: true,
|
||||||
|
category: "",
|
||||||
|
description: ""
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSadClick: function() {
|
||||||
|
this.props.feedbackStore.dispatchAction(
|
||||||
|
new sharedActions.RequireFeedbackDetails());
|
||||||
|
},
|
||||||
|
|
||||||
|
_onFeedbackSent: function(err) {
|
||||||
|
if (err) {
|
||||||
|
// XXX better end user error reporting, see bug 1046738
|
||||||
|
console.error("Unable to send user feedback", err);
|
||||||
|
}
|
||||||
|
this.setState({pending: false, step: "finished"});
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
switch(this.state.feedbackState) {
|
||||||
|
default:
|
||||||
|
case FEEDBACK_STATES.INIT: {
|
||||||
|
return (
|
||||||
|
FeedbackLayout({title:
|
||||||
|
l10n.get("feedback_call_experience_heading2")},
|
||||||
|
React.DOM.div({className: "faces"},
|
||||||
|
React.DOM.button({className: "face face-happy",
|
||||||
|
onClick: this.handleHappyClick}),
|
||||||
|
React.DOM.button({className: "face face-sad",
|
||||||
|
onClick: this.handleSadClick})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case FEEDBACK_STATES.DETAILS: {
|
||||||
|
return (
|
||||||
|
FeedbackForm({
|
||||||
|
feedbackStore: this.props.feedbackStore,
|
||||||
|
reset: this.reset,
|
||||||
|
pending: this.state.feedbackState === FEEDBACK_STATES.PENDING})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case FEEDBACK_STATES.PENDING:
|
||||||
|
case FEEDBACK_STATES.SENT:
|
||||||
|
case FEEDBACK_STATES.FAILED: {
|
||||||
|
if (this.state.error) {
|
||||||
|
// XXX better end user error reporting, see bug 1046738
|
||||||
|
console.error("Error encountered while submitting feedback",
|
||||||
|
this.state.error);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
FeedbackReceived({
|
||||||
|
onAfterFeedbackReceived: this.props.onAfterFeedbackReceived})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return FeedbackView;
|
||||||
|
})(navigator.mozL10n || document.mozL10n);
|
326
browser/components/loop/content/shared/js/feedbackViews.jsx
Normal file
326
browser/components/loop/content/shared/js/feedbackViews.jsx
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
/** @jsx React.DOM */
|
||||||
|
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/* jshint newcap:false */
|
||||||
|
/* global loop:true, React */
|
||||||
|
var loop = loop || {};
|
||||||
|
loop.shared = loop.shared || {};
|
||||||
|
loop.shared.views = loop.shared.views || {};
|
||||||
|
loop.shared.views.FeedbackView = (function(l10n) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var sharedActions = loop.shared.actions;
|
||||||
|
var sharedMixins = loop.shared.mixins;
|
||||||
|
|
||||||
|
var WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS = 5;
|
||||||
|
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feedback outer layout.
|
||||||
|
*
|
||||||
|
* Props:
|
||||||
|
* -
|
||||||
|
*/
|
||||||
|
var FeedbackLayout = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
children: React.PropTypes.component.isRequired,
|
||||||
|
title: React.PropTypes.string.isRequired,
|
||||||
|
reset: React.PropTypes.func // if not specified, no Back btn is shown
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var backButton = <div />;
|
||||||
|
if (this.props.reset) {
|
||||||
|
backButton = (
|
||||||
|
<button className="fx-embedded-btn-back" type="button"
|
||||||
|
onClick={this.props.reset}>
|
||||||
|
« {l10n.get("feedback_back_button")}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="feedback">
|
||||||
|
{backButton}
|
||||||
|
<h3>{this.props.title}</h3>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detailed feedback form.
|
||||||
|
*/
|
||||||
|
var FeedbackForm = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore),
|
||||||
|
pending: React.PropTypes.bool,
|
||||||
|
reset: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {category: "", description: ""};
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {pending: false};
|
||||||
|
},
|
||||||
|
|
||||||
|
_getCategories: function() {
|
||||||
|
return {
|
||||||
|
audio_quality: l10n.get("feedback_category_audio_quality"),
|
||||||
|
video_quality: l10n.get("feedback_category_video_quality"),
|
||||||
|
disconnected : l10n.get("feedback_category_was_disconnected"),
|
||||||
|
confusing: l10n.get("feedback_category_confusing"),
|
||||||
|
other: l10n.get("feedback_category_other")
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_getCategoryFields: function() {
|
||||||
|
var categories = this._getCategories();
|
||||||
|
return Object.keys(categories).map(function(category, key) {
|
||||||
|
return (
|
||||||
|
<label key={key} className="feedback-category-label">
|
||||||
|
<input type="radio" ref="category" name="category"
|
||||||
|
className="feedback-category-radio"
|
||||||
|
value={category}
|
||||||
|
onChange={this.handleCategoryChange}
|
||||||
|
checked={this.state.category === category} />
|
||||||
|
{categories[category]}
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the form is ready for submission:
|
||||||
|
*
|
||||||
|
* - no feedback submission should be pending.
|
||||||
|
* - a category (reason) must be chosen;
|
||||||
|
* - if the "other" category is chosen, a custom description must have been
|
||||||
|
* entered by the end user;
|
||||||
|
*
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
_isFormReady: function() {
|
||||||
|
if (this.props.pending || !this.state.category) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.state.category === "other" && !this.state.description) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCategoryChange: function(event) {
|
||||||
|
var category = event.target.value;
|
||||||
|
this.setState({
|
||||||
|
category: category,
|
||||||
|
description: category == "other" ? "" : this._getCategories()[category]
|
||||||
|
});
|
||||||
|
if (category == "other") {
|
||||||
|
this.refs.description.getDOMNode().focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDescriptionFieldChange: function(event) {
|
||||||
|
this.setState({description: event.target.value});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDescriptionFieldFocus: function(event) {
|
||||||
|
this.setState({category: "other", description: ""});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleFormSubmit: function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
// XXX this feels ugly, we really want a feedbackActions object here.
|
||||||
|
this.props.feedbackStore.dispatchAction(new sharedActions.SendFeedback({
|
||||||
|
happy: false,
|
||||||
|
category: this.state.category,
|
||||||
|
description: this.state.description
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var descriptionDisplayValue = this.state.category === "other" ?
|
||||||
|
this.state.description : "";
|
||||||
|
return (
|
||||||
|
<FeedbackLayout title={l10n.get("feedback_what_makes_you_sad")}
|
||||||
|
reset={this.props.reset}>
|
||||||
|
<form onSubmit={this.handleFormSubmit}>
|
||||||
|
{this._getCategoryFields()}
|
||||||
|
<p>
|
||||||
|
<input type="text" ref="description" name="description"
|
||||||
|
className="feedback-description"
|
||||||
|
onChange={this.handleDescriptionFieldChange}
|
||||||
|
onFocus={this.handleDescriptionFieldFocus}
|
||||||
|
value={descriptionDisplayValue}
|
||||||
|
placeholder={
|
||||||
|
l10n.get("feedback_custom_category_text_placeholder")} />
|
||||||
|
</p>
|
||||||
|
<button type="submit" className="btn btn-success"
|
||||||
|
disabled={!this._isFormReady()}>
|
||||||
|
{l10n.get("feedback_submit_button")}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</FeedbackLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feedback received view.
|
||||||
|
*
|
||||||
|
* Props:
|
||||||
|
* - {Function} onAfterFeedbackReceived Function to execute after the
|
||||||
|
* WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS timeout has elapsed
|
||||||
|
*/
|
||||||
|
var FeedbackReceived = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
onAfterFeedbackReceived: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {countdown: WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this._timer = setInterval(function() {
|
||||||
|
this.setState({countdown: this.state.countdown - 1});
|
||||||
|
}.bind(this), 1000);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
if (this._timer) {
|
||||||
|
clearInterval(this._timer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
if (this.state.countdown < 1) {
|
||||||
|
clearInterval(this._timer);
|
||||||
|
if (this.props.onAfterFeedbackReceived) {
|
||||||
|
this.props.onAfterFeedbackReceived();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<FeedbackLayout title={l10n.get("feedback_thank_you_heading")}>
|
||||||
|
<p className="info thank-you">{
|
||||||
|
l10n.get("feedback_window_will_close_in2", {
|
||||||
|
countdown: this.state.countdown,
|
||||||
|
num: this.state.countdown
|
||||||
|
})}</p>
|
||||||
|
</FeedbackLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feedback view.
|
||||||
|
*/
|
||||||
|
var FeedbackView = React.createClass({
|
||||||
|
mixins: [Backbone.Events, sharedMixins.AudioMixin],
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore),
|
||||||
|
onAfterFeedbackReceived: React.PropTypes.func,
|
||||||
|
// Used by the UI showcase.
|
||||||
|
feedbackState: React.PropTypes.string
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
var storeState = this.props.feedbackStore.getStoreState();
|
||||||
|
return _.extend({}, storeState, {
|
||||||
|
feedbackState: this.props.feedbackState || storeState.feedbackState
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
this.listenTo(this.props.feedbackStore, "change", this._onStoreStateChanged);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this.play("terminated");
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
this.stopListening(this.props.feedbackStore);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onStoreStateChanged: function() {
|
||||||
|
this.setState(this.props.feedbackStore.getStoreState());
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: function() {
|
||||||
|
this.setState(this.props.feedbackStore.getInitialStoreState());
|
||||||
|
},
|
||||||
|
|
||||||
|
handleHappyClick: function() {
|
||||||
|
// XXX: If the user is happy, we directly send this information to the
|
||||||
|
// feedback API; this is a behavior we might want to revisit later.
|
||||||
|
this.props.feedbackStore.dispatchAction(new sharedActions.SendFeedback({
|
||||||
|
happy: true,
|
||||||
|
category: "",
|
||||||
|
description: ""
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSadClick: function() {
|
||||||
|
this.props.feedbackStore.dispatchAction(
|
||||||
|
new sharedActions.RequireFeedbackDetails());
|
||||||
|
},
|
||||||
|
|
||||||
|
_onFeedbackSent: function(err) {
|
||||||
|
if (err) {
|
||||||
|
// XXX better end user error reporting, see bug 1046738
|
||||||
|
console.error("Unable to send user feedback", err);
|
||||||
|
}
|
||||||
|
this.setState({pending: false, step: "finished"});
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
switch(this.state.feedbackState) {
|
||||||
|
default:
|
||||||
|
case FEEDBACK_STATES.INIT: {
|
||||||
|
return (
|
||||||
|
<FeedbackLayout title={
|
||||||
|
l10n.get("feedback_call_experience_heading2")}>
|
||||||
|
<div className="faces">
|
||||||
|
<button className="face face-happy"
|
||||||
|
onClick={this.handleHappyClick}></button>
|
||||||
|
<button className="face face-sad"
|
||||||
|
onClick={this.handleSadClick}></button>
|
||||||
|
</div>
|
||||||
|
</FeedbackLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case FEEDBACK_STATES.DETAILS: {
|
||||||
|
return (
|
||||||
|
<FeedbackForm
|
||||||
|
feedbackStore={this.props.feedbackStore}
|
||||||
|
reset={this.reset}
|
||||||
|
pending={this.state.feedbackState === FEEDBACK_STATES.PENDING} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case FEEDBACK_STATES.PENDING:
|
||||||
|
case FEEDBACK_STATES.SENT:
|
||||||
|
case FEEDBACK_STATES.FAILED: {
|
||||||
|
if (this.state.error) {
|
||||||
|
// XXX better end user error reporting, see bug 1046738
|
||||||
|
console.error("Error encountered while submitting feedback",
|
||||||
|
this.state.error);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<FeedbackReceived
|
||||||
|
onAfterFeedbackReceived={this.props.onAfterFeedbackReceived} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return FeedbackView;
|
||||||
|
})(navigator.mozL10n || document.mozL10n);
|
@ -14,8 +14,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
var sharedModels = loop.shared.models;
|
var sharedModels = loop.shared.models;
|
||||||
var sharedMixins = loop.shared.mixins;
|
var sharedMixins = loop.shared.mixins;
|
||||||
|
|
||||||
var WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS = 5;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Media control button.
|
* Media control button.
|
||||||
*
|
*
|
||||||
@ -345,287 +343,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Feedback outer layout.
|
|
||||||
*
|
|
||||||
* Props:
|
|
||||||
* -
|
|
||||||
*/
|
|
||||||
var FeedbackLayout = React.createClass({displayName: 'FeedbackLayout',
|
|
||||||
propTypes: {
|
|
||||||
children: React.PropTypes.component.isRequired,
|
|
||||||
title: React.PropTypes.string.isRequired,
|
|
||||||
reset: React.PropTypes.func // if not specified, no Back btn is shown
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var backButton = React.DOM.div(null);
|
|
||||||
if (this.props.reset) {
|
|
||||||
backButton = (
|
|
||||||
React.DOM.button({className: "fx-embedded-btn-back", type: "button",
|
|
||||||
onClick: this.props.reset},
|
|
||||||
"« ", l10n.get("feedback_back_button")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
React.DOM.div({className: "feedback"},
|
|
||||||
backButton,
|
|
||||||
React.DOM.h3(null, this.props.title),
|
|
||||||
this.props.children
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detailed feedback form.
|
|
||||||
*/
|
|
||||||
var FeedbackForm = React.createClass({displayName: 'FeedbackForm',
|
|
||||||
propTypes: {
|
|
||||||
pending: React.PropTypes.bool,
|
|
||||||
sendFeedback: React.PropTypes.func,
|
|
||||||
reset: React.PropTypes.func
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {category: "", description: ""};
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {pending: false};
|
|
||||||
},
|
|
||||||
|
|
||||||
_getCategories: function() {
|
|
||||||
return {
|
|
||||||
audio_quality: l10n.get("feedback_category_audio_quality"),
|
|
||||||
video_quality: l10n.get("feedback_category_video_quality"),
|
|
||||||
disconnected : l10n.get("feedback_category_was_disconnected"),
|
|
||||||
confusing: l10n.get("feedback_category_confusing"),
|
|
||||||
other: l10n.get("feedback_category_other")
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_getCategoryFields: function() {
|
|
||||||
var categories = this._getCategories();
|
|
||||||
return Object.keys(categories).map(function(category, key) {
|
|
||||||
return (
|
|
||||||
React.DOM.label({key: key, className: "feedback-category-label"},
|
|
||||||
React.DOM.input({type: "radio", ref: "category", name: "category",
|
|
||||||
className: "feedback-category-radio",
|
|
||||||
value: category,
|
|
||||||
onChange: this.handleCategoryChange,
|
|
||||||
checked: this.state.category === category}),
|
|
||||||
categories[category]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the form is ready for submission:
|
|
||||||
*
|
|
||||||
* - no feedback submission should be pending.
|
|
||||||
* - a category (reason) must be chosen;
|
|
||||||
* - if the "other" category is chosen, a custom description must have been
|
|
||||||
* entered by the end user;
|
|
||||||
*
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
_isFormReady: function() {
|
|
||||||
if (this.props.pending || !this.state.category) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (this.state.category === "other" && !this.state.description) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleCategoryChange: function(event) {
|
|
||||||
var category = event.target.value;
|
|
||||||
this.setState({
|
|
||||||
category: category,
|
|
||||||
description: category == "other" ? "" : this._getCategories()[category]
|
|
||||||
});
|
|
||||||
if (category == "other") {
|
|
||||||
this.refs.description.getDOMNode().focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDescriptionFieldChange: function(event) {
|
|
||||||
this.setState({description: event.target.value});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDescriptionFieldFocus: function(event) {
|
|
||||||
this.setState({category: "other", description: ""});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleFormSubmit: function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.props.sendFeedback({
|
|
||||||
happy: false,
|
|
||||||
category: this.state.category,
|
|
||||||
description: this.state.description
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var descriptionDisplayValue = this.state.category === "other" ?
|
|
||||||
this.state.description : "";
|
|
||||||
return (
|
|
||||||
FeedbackLayout({title: l10n.get("feedback_what_makes_you_sad"),
|
|
||||||
reset: this.props.reset},
|
|
||||||
React.DOM.form({onSubmit: this.handleFormSubmit},
|
|
||||||
this._getCategoryFields(),
|
|
||||||
React.DOM.p(null,
|
|
||||||
React.DOM.input({type: "text", ref: "description", name: "description",
|
|
||||||
className: "feedback-description",
|
|
||||||
onChange: this.handleDescriptionFieldChange,
|
|
||||||
onFocus: this.handleDescriptionFieldFocus,
|
|
||||||
value: descriptionDisplayValue,
|
|
||||||
placeholder:
|
|
||||||
l10n.get("feedback_custom_category_text_placeholder")})
|
|
||||||
),
|
|
||||||
React.DOM.button({type: "submit", className: "btn btn-success",
|
|
||||||
disabled: !this._isFormReady()},
|
|
||||||
l10n.get("feedback_submit_button")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Feedback received view.
|
|
||||||
*
|
|
||||||
* Props:
|
|
||||||
* - {Function} onAfterFeedbackReceived Function to execute after the
|
|
||||||
* WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS timeout has elapsed
|
|
||||||
*/
|
|
||||||
var FeedbackReceived = React.createClass({displayName: 'FeedbackReceived',
|
|
||||||
propTypes: {
|
|
||||||
onAfterFeedbackReceived: React.PropTypes.func
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {countdown: WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this._timer = setInterval(function() {
|
|
||||||
this.setState({countdown: this.state.countdown - 1});
|
|
||||||
}.bind(this), 1000);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
if (this._timer) {
|
|
||||||
clearInterval(this._timer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
if (this.state.countdown < 1) {
|
|
||||||
clearInterval(this._timer);
|
|
||||||
if (this.props.onAfterFeedbackReceived) {
|
|
||||||
this.props.onAfterFeedbackReceived();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
FeedbackLayout({title: l10n.get("feedback_thank_you_heading")},
|
|
||||||
React.DOM.p({className: "info thank-you"},
|
|
||||||
l10n.get("feedback_window_will_close_in2", {
|
|
||||||
countdown: this.state.countdown,
|
|
||||||
num: this.state.countdown
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Feedback view.
|
|
||||||
*/
|
|
||||||
var FeedbackView = React.createClass({displayName: 'FeedbackView',
|
|
||||||
mixins: [sharedMixins.AudioMixin],
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// A loop.FeedbackAPIClient instance
|
|
||||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
|
||||||
onAfterFeedbackReceived: React.PropTypes.func,
|
|
||||||
// The current feedback submission flow step name
|
|
||||||
step: React.PropTypes.oneOf(["start", "form", "finished"])
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {pending: false, step: this.props.step || "start"};
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {step: "start"};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.play("terminated");
|
|
||||||
},
|
|
||||||
|
|
||||||
reset: function() {
|
|
||||||
this.setState(this.getInitialState());
|
|
||||||
},
|
|
||||||
|
|
||||||
handleHappyClick: function() {
|
|
||||||
this.sendFeedback({happy: true}, this._onFeedbackSent);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleSadClick: function() {
|
|
||||||
this.setState({step: "form"});
|
|
||||||
},
|
|
||||||
|
|
||||||
sendFeedback: function(fields) {
|
|
||||||
// Setting state.pending to true will disable the submit button to avoid
|
|
||||||
// multiple submissions
|
|
||||||
this.setState({pending: true});
|
|
||||||
// Sends feedback data
|
|
||||||
this.props.feedbackApiClient.send(fields, this._onFeedbackSent);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onFeedbackSent: function(err) {
|
|
||||||
if (err) {
|
|
||||||
// XXX better end user error reporting, see bug 1046738
|
|
||||||
console.error("Unable to send user feedback", err);
|
|
||||||
}
|
|
||||||
this.setState({pending: false, step: "finished"});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
switch(this.state.step) {
|
|
||||||
case "finished":
|
|
||||||
return (
|
|
||||||
FeedbackReceived({
|
|
||||||
onAfterFeedbackReceived: this.props.onAfterFeedbackReceived})
|
|
||||||
);
|
|
||||||
case "form":
|
|
||||||
return FeedbackForm({feedbackApiClient: this.props.feedbackApiClient,
|
|
||||||
sendFeedback: this.sendFeedback,
|
|
||||||
reset: this.reset,
|
|
||||||
pending: this.state.pending});
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
FeedbackLayout({title:
|
|
||||||
l10n.get("feedback_call_experience_heading2")},
|
|
||||||
React.DOM.div({className: "faces"},
|
|
||||||
React.DOM.button({className: "face face-happy",
|
|
||||||
onClick: this.handleHappyClick}),
|
|
||||||
React.DOM.button({className: "face face-sad",
|
|
||||||
onClick: this.handleSadClick})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification view.
|
* Notification view.
|
||||||
*/
|
*/
|
||||||
@ -743,7 +460,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
React.DOM.span({className: "button-caption"}, this.props.caption),
|
React.DOM.span({className: "button-caption"}, this.props.caption),
|
||||||
this.props.children
|
this.props.children
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -768,7 +485,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
React.DOM.div({className: cx(classObject)},
|
React.DOM.div({className: cx(classObject)},
|
||||||
this.props.children
|
this.props.children
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -777,7 +494,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
ButtonGroup: ButtonGroup,
|
ButtonGroup: ButtonGroup,
|
||||||
ConversationView: ConversationView,
|
ConversationView: ConversationView,
|
||||||
ConversationToolbar: ConversationToolbar,
|
ConversationToolbar: ConversationToolbar,
|
||||||
FeedbackView: FeedbackView,
|
|
||||||
MediaControlButton: MediaControlButton,
|
MediaControlButton: MediaControlButton,
|
||||||
NotificationListView: NotificationListView
|
NotificationListView: NotificationListView
|
||||||
};
|
};
|
||||||
|
@ -14,8 +14,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
var sharedModels = loop.shared.models;
|
var sharedModels = loop.shared.models;
|
||||||
var sharedMixins = loop.shared.mixins;
|
var sharedMixins = loop.shared.mixins;
|
||||||
|
|
||||||
var WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS = 5;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Media control button.
|
* Media control button.
|
||||||
*
|
*
|
||||||
@ -345,287 +343,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Feedback outer layout.
|
|
||||||
*
|
|
||||||
* Props:
|
|
||||||
* -
|
|
||||||
*/
|
|
||||||
var FeedbackLayout = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
children: React.PropTypes.component.isRequired,
|
|
||||||
title: React.PropTypes.string.isRequired,
|
|
||||||
reset: React.PropTypes.func // if not specified, no Back btn is shown
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var backButton = <div />;
|
|
||||||
if (this.props.reset) {
|
|
||||||
backButton = (
|
|
||||||
<button className="fx-embedded-btn-back" type="button"
|
|
||||||
onClick={this.props.reset}>
|
|
||||||
« {l10n.get("feedback_back_button")}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="feedback">
|
|
||||||
{backButton}
|
|
||||||
<h3>{this.props.title}</h3>
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detailed feedback form.
|
|
||||||
*/
|
|
||||||
var FeedbackForm = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
pending: React.PropTypes.bool,
|
|
||||||
sendFeedback: React.PropTypes.func,
|
|
||||||
reset: React.PropTypes.func
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {category: "", description: ""};
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {pending: false};
|
|
||||||
},
|
|
||||||
|
|
||||||
_getCategories: function() {
|
|
||||||
return {
|
|
||||||
audio_quality: l10n.get("feedback_category_audio_quality"),
|
|
||||||
video_quality: l10n.get("feedback_category_video_quality"),
|
|
||||||
disconnected : l10n.get("feedback_category_was_disconnected"),
|
|
||||||
confusing: l10n.get("feedback_category_confusing"),
|
|
||||||
other: l10n.get("feedback_category_other")
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_getCategoryFields: function() {
|
|
||||||
var categories = this._getCategories();
|
|
||||||
return Object.keys(categories).map(function(category, key) {
|
|
||||||
return (
|
|
||||||
<label key={key} className="feedback-category-label">
|
|
||||||
<input type="radio" ref="category" name="category"
|
|
||||||
className="feedback-category-radio"
|
|
||||||
value={category}
|
|
||||||
onChange={this.handleCategoryChange}
|
|
||||||
checked={this.state.category === category} />
|
|
||||||
{categories[category]}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
}, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the form is ready for submission:
|
|
||||||
*
|
|
||||||
* - no feedback submission should be pending.
|
|
||||||
* - a category (reason) must be chosen;
|
|
||||||
* - if the "other" category is chosen, a custom description must have been
|
|
||||||
* entered by the end user;
|
|
||||||
*
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
_isFormReady: function() {
|
|
||||||
if (this.props.pending || !this.state.category) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (this.state.category === "other" && !this.state.description) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleCategoryChange: function(event) {
|
|
||||||
var category = event.target.value;
|
|
||||||
this.setState({
|
|
||||||
category: category,
|
|
||||||
description: category == "other" ? "" : this._getCategories()[category]
|
|
||||||
});
|
|
||||||
if (category == "other") {
|
|
||||||
this.refs.description.getDOMNode().focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDescriptionFieldChange: function(event) {
|
|
||||||
this.setState({description: event.target.value});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDescriptionFieldFocus: function(event) {
|
|
||||||
this.setState({category: "other", description: ""});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleFormSubmit: function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.props.sendFeedback({
|
|
||||||
happy: false,
|
|
||||||
category: this.state.category,
|
|
||||||
description: this.state.description
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var descriptionDisplayValue = this.state.category === "other" ?
|
|
||||||
this.state.description : "";
|
|
||||||
return (
|
|
||||||
<FeedbackLayout title={l10n.get("feedback_what_makes_you_sad")}
|
|
||||||
reset={this.props.reset}>
|
|
||||||
<form onSubmit={this.handleFormSubmit}>
|
|
||||||
{this._getCategoryFields()}
|
|
||||||
<p>
|
|
||||||
<input type="text" ref="description" name="description"
|
|
||||||
className="feedback-description"
|
|
||||||
onChange={this.handleDescriptionFieldChange}
|
|
||||||
onFocus={this.handleDescriptionFieldFocus}
|
|
||||||
value={descriptionDisplayValue}
|
|
||||||
placeholder={
|
|
||||||
l10n.get("feedback_custom_category_text_placeholder")} />
|
|
||||||
</p>
|
|
||||||
<button type="submit" className="btn btn-success"
|
|
||||||
disabled={!this._isFormReady()}>
|
|
||||||
{l10n.get("feedback_submit_button")}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</FeedbackLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Feedback received view.
|
|
||||||
*
|
|
||||||
* Props:
|
|
||||||
* - {Function} onAfterFeedbackReceived Function to execute after the
|
|
||||||
* WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS timeout has elapsed
|
|
||||||
*/
|
|
||||||
var FeedbackReceived = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
onAfterFeedbackReceived: React.PropTypes.func
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {countdown: WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this._timer = setInterval(function() {
|
|
||||||
this.setState({countdown: this.state.countdown - 1});
|
|
||||||
}.bind(this), 1000);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
if (this._timer) {
|
|
||||||
clearInterval(this._timer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
if (this.state.countdown < 1) {
|
|
||||||
clearInterval(this._timer);
|
|
||||||
if (this.props.onAfterFeedbackReceived) {
|
|
||||||
this.props.onAfterFeedbackReceived();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<FeedbackLayout title={l10n.get("feedback_thank_you_heading")}>
|
|
||||||
<p className="info thank-you">{
|
|
||||||
l10n.get("feedback_window_will_close_in2", {
|
|
||||||
countdown: this.state.countdown,
|
|
||||||
num: this.state.countdown
|
|
||||||
})}</p>
|
|
||||||
</FeedbackLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Feedback view.
|
|
||||||
*/
|
|
||||||
var FeedbackView = React.createClass({
|
|
||||||
mixins: [sharedMixins.AudioMixin],
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// A loop.FeedbackAPIClient instance
|
|
||||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
|
||||||
onAfterFeedbackReceived: React.PropTypes.func,
|
|
||||||
// The current feedback submission flow step name
|
|
||||||
step: React.PropTypes.oneOf(["start", "form", "finished"])
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {pending: false, step: this.props.step || "start"};
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {step: "start"};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.play("terminated");
|
|
||||||
},
|
|
||||||
|
|
||||||
reset: function() {
|
|
||||||
this.setState(this.getInitialState());
|
|
||||||
},
|
|
||||||
|
|
||||||
handleHappyClick: function() {
|
|
||||||
this.sendFeedback({happy: true}, this._onFeedbackSent);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleSadClick: function() {
|
|
||||||
this.setState({step: "form"});
|
|
||||||
},
|
|
||||||
|
|
||||||
sendFeedback: function(fields) {
|
|
||||||
// Setting state.pending to true will disable the submit button to avoid
|
|
||||||
// multiple submissions
|
|
||||||
this.setState({pending: true});
|
|
||||||
// Sends feedback data
|
|
||||||
this.props.feedbackApiClient.send(fields, this._onFeedbackSent);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onFeedbackSent: function(err) {
|
|
||||||
if (err) {
|
|
||||||
// XXX better end user error reporting, see bug 1046738
|
|
||||||
console.error("Unable to send user feedback", err);
|
|
||||||
}
|
|
||||||
this.setState({pending: false, step: "finished"});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
switch(this.state.step) {
|
|
||||||
case "finished":
|
|
||||||
return (
|
|
||||||
<FeedbackReceived
|
|
||||||
onAfterFeedbackReceived={this.props.onAfterFeedbackReceived} />
|
|
||||||
);
|
|
||||||
case "form":
|
|
||||||
return <FeedbackForm feedbackApiClient={this.props.feedbackApiClient}
|
|
||||||
sendFeedback={this.sendFeedback}
|
|
||||||
reset={this.reset}
|
|
||||||
pending={this.state.pending} />;
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<FeedbackLayout title={
|
|
||||||
l10n.get("feedback_call_experience_heading2")}>
|
|
||||||
<div className="faces">
|
|
||||||
<button className="face face-happy"
|
|
||||||
onClick={this.handleHappyClick}></button>
|
|
||||||
<button className="face face-sad"
|
|
||||||
onClick={this.handleSadClick}></button>
|
|
||||||
</div>
|
|
||||||
</FeedbackLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification view.
|
* Notification view.
|
||||||
*/
|
*/
|
||||||
@ -743,7 +460,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
<span className="button-caption">{this.props.caption}</span>
|
<span className="button-caption">{this.props.caption}</span>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</button>
|
</button>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -768,7 +485,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
<div className={cx(classObject)}>
|
<div className={cx(classObject)}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -777,7 +494,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
ButtonGroup: ButtonGroup,
|
ButtonGroup: ButtonGroup,
|
||||||
ConversationView: ConversationView,
|
ConversationView: ConversationView,
|
||||||
ConversationToolbar: ConversationToolbar,
|
ConversationToolbar: ConversationToolbar,
|
||||||
FeedbackView: FeedbackView,
|
|
||||||
MediaControlButton: MediaControlButton,
|
MediaControlButton: MediaControlButton,
|
||||||
NotificationListView: NotificationListView
|
NotificationListView: NotificationListView
|
||||||
};
|
};
|
||||||
|
@ -50,6 +50,7 @@ browser.jar:
|
|||||||
content/browser/loop/shared/img/svg/glyph-account-16x16.svg (content/shared/img/svg/glyph-account-16x16.svg)
|
content/browser/loop/shared/img/svg/glyph-account-16x16.svg (content/shared/img/svg/glyph-account-16x16.svg)
|
||||||
content/browser/loop/shared/img/svg/glyph-signin-16x16.svg (content/shared/img/svg/glyph-signin-16x16.svg)
|
content/browser/loop/shared/img/svg/glyph-signin-16x16.svg (content/shared/img/svg/glyph-signin-16x16.svg)
|
||||||
content/browser/loop/shared/img/svg/glyph-signout-16x16.svg (content/shared/img/svg/glyph-signout-16x16.svg)
|
content/browser/loop/shared/img/svg/glyph-signout-16x16.svg (content/shared/img/svg/glyph-signout-16x16.svg)
|
||||||
|
content/browser/loop/shared/img/svg/glyph-help-16x16.svg (content/shared/img/svg/glyph-help-16x16.svg)
|
||||||
content/browser/loop/shared/img/audio-call-avatar.svg (content/shared/img/audio-call-avatar.svg)
|
content/browser/loop/shared/img/audio-call-avatar.svg (content/shared/img/audio-call-avatar.svg)
|
||||||
content/browser/loop/shared/img/beta-ribbon.svg (content/shared/img/beta-ribbon.svg)
|
content/browser/loop/shared/img/beta-ribbon.svg (content/shared/img/beta-ribbon.svg)
|
||||||
content/browser/loop/shared/img/icons-10x10.svg (content/shared/img/icons-10x10.svg)
|
content/browser/loop/shared/img/icons-10x10.svg (content/shared/img/icons-10x10.svg)
|
||||||
@ -70,12 +71,14 @@ browser.jar:
|
|||||||
content/browser/loop/shared/js/store.js (content/shared/js/store.js)
|
content/browser/loop/shared/js/store.js (content/shared/js/store.js)
|
||||||
content/browser/loop/shared/js/roomStore.js (content/shared/js/roomStore.js)
|
content/browser/loop/shared/js/roomStore.js (content/shared/js/roomStore.js)
|
||||||
content/browser/loop/shared/js/activeRoomStore.js (content/shared/js/activeRoomStore.js)
|
content/browser/loop/shared/js/activeRoomStore.js (content/shared/js/activeRoomStore.js)
|
||||||
|
content/browser/loop/shared/js/feedbackStore.js (content/shared/js/feedbackStore.js)
|
||||||
content/browser/loop/shared/js/dispatcher.js (content/shared/js/dispatcher.js)
|
content/browser/loop/shared/js/dispatcher.js (content/shared/js/dispatcher.js)
|
||||||
content/browser/loop/shared/js/feedbackApiClient.js (content/shared/js/feedbackApiClient.js)
|
content/browser/loop/shared/js/feedbackApiClient.js (content/shared/js/feedbackApiClient.js)
|
||||||
content/browser/loop/shared/js/models.js (content/shared/js/models.js)
|
content/browser/loop/shared/js/models.js (content/shared/js/models.js)
|
||||||
content/browser/loop/shared/js/mixins.js (content/shared/js/mixins.js)
|
content/browser/loop/shared/js/mixins.js (content/shared/js/mixins.js)
|
||||||
content/browser/loop/shared/js/otSdkDriver.js (content/shared/js/otSdkDriver.js)
|
content/browser/loop/shared/js/otSdkDriver.js (content/shared/js/otSdkDriver.js)
|
||||||
content/browser/loop/shared/js/views.js (content/shared/js/views.js)
|
content/browser/loop/shared/js/views.js (content/shared/js/views.js)
|
||||||
|
content/browser/loop/shared/js/feedbackViews.js (content/shared/js/feedbackViews.js)
|
||||||
content/browser/loop/shared/js/utils.js (content/shared/js/utils.js)
|
content/browser/loop/shared/js/utils.js (content/shared/js/utils.js)
|
||||||
content/browser/loop/shared/js/validate.js (content/shared/js/validate.js)
|
content/browser/loop/shared/js/validate.js (content/shared/js/validate.js)
|
||||||
content/browser/loop/shared/js/websocket.js (content/shared/js/websocket.js)
|
content/browser/loop/shared/js/websocket.js (content/shared/js/websocket.js)
|
||||||
|
@ -84,3 +84,5 @@ config:
|
|||||||
@echo "loop.config.fxosApp = loop.config.fxosApp || {};" >> content/config.js
|
@echo "loop.config.fxosApp = loop.config.fxosApp || {};" >> content/config.js
|
||||||
@echo "loop.config.fxosApp.name = 'Loop';" >> content/config.js
|
@echo "loop.config.fxosApp.name = 'Loop';" >> content/config.js
|
||||||
@echo "loop.config.fxosApp.manifestUrl = 'http://fake-market.herokuapp.com/apps/packagedApp/manifest.webapp';" >> content/config.js
|
@echo "loop.config.fxosApp.manifestUrl = 'http://fake-market.herokuapp.com/apps/packagedApp/manifest.webapp';" >> content/config.js
|
||||||
|
@echo "loop.config.roomsSupportUrl = 'https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc';" >> content/config.js
|
||||||
|
@echo "loop.config.guestSupportUrl = 'https://support.mozilla.org/kb/respond-firefox-hello-invitation-guest-mode';" >> content/config.js
|
||||||
|
@ -87,15 +87,19 @@ body,
|
|||||||
color: #777;
|
color: #777;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-external-links a {
|
.footer-external-links {
|
||||||
padding: .2rem .7rem;
|
padding: .2rem .7rem;
|
||||||
margin: 0 .5rem;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-external-links a:hover {
|
.footer-external-links a {
|
||||||
color: #111;
|
margin: 0 .5rem;
|
||||||
}
|
text-decoration: none;
|
||||||
|
color: #adadad;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-external-links a:hover {
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
.footer-logo {
|
.footer-logo {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
|
@ -99,6 +99,8 @@
|
|||||||
<script type="text/javascript" src="shared/js/otSdkDriver.js"></script>
|
<script type="text/javascript" src="shared/js/otSdkDriver.js"></script>
|
||||||
<script type="text/javascript" src="shared/js/store.js"></script>
|
<script type="text/javascript" src="shared/js/store.js"></script>
|
||||||
<script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
|
<script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
|
||||||
|
<script type="text/javascript" src="shared/js/feedbackStore.js"></script>
|
||||||
|
<script type="text/javascript" src="shared/js/feedbackViews.js"></script>
|
||||||
<script type="text/javascript" src="js/standaloneAppStore.js"></script>
|
<script type="text/javascript" src="js/standaloneAppStore.js"></script>
|
||||||
<script type="text/javascript" src="js/standaloneClient.js"></script>
|
<script type="text/javascript" src="js/standaloneClient.js"></script>
|
||||||
<script type="text/javascript" src="js/standaloneMozLoop.js"></script>
|
<script type="text/javascript" src="js/standaloneMozLoop.js"></script>
|
||||||
|
@ -107,7 +107,10 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
React.DOM.header(null,
|
React.DOM.header(null,
|
||||||
React.DOM.h1(null, mozL10n.get("clientShortname2"))
|
React.DOM.h1(null, mozL10n.get("clientShortname2")),
|
||||||
|
React.DOM.a({target: "_blank", href: loop.config.roomsSupportUrl},
|
||||||
|
React.DOM.i({className: "icon icon-help"})
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,9 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
<h1>{mozL10n.get("clientShortname2")}</h1>
|
<h1>{mozL10n.get("clientShortname2")}</h1>
|
||||||
|
<a target="_blank" href={loop.config.roomsSupportUrl}>
|
||||||
|
<i className="icon icon-help"></i>
|
||||||
|
</a>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,12 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
React.DOM.div({className: "standalone-footer container-box"},
|
React.DOM.div({className: "standalone-footer container-box"},
|
||||||
React.DOM.div({title: mozL10n.get("vendor_alttext",
|
React.DOM.div({title: mozL10n.get("vendor_alttext",
|
||||||
{vendorShortname: mozL10n.get("vendorShortname")}),
|
{vendorShortname: mozL10n.get("vendorShortname")}),
|
||||||
className: "footer-logo"})
|
className: "footer-logo"}),
|
||||||
|
React.DOM.div({className: "footer-external-links"},
|
||||||
|
React.DOM.a({target: "_blank", href: loop.config.guestSupportUrl},
|
||||||
|
mozL10n.get("support_link")
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -538,7 +543,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||||
.isRequired,
|
.isRequired,
|
||||||
sdk: React.PropTypes.object.isRequired,
|
sdk: React.PropTypes.object.isRequired,
|
||||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore),
|
||||||
onAfterFeedbackReceived: React.PropTypes.func.isRequired
|
onAfterFeedbackReceived: React.PropTypes.func.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -549,7 +554,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
return (
|
return (
|
||||||
React.DOM.div({className: "ended-conversation"},
|
React.DOM.div({className: "ended-conversation"},
|
||||||
sharedViews.FeedbackView({
|
sharedViews.FeedbackView({
|
||||||
feedbackApiClient: this.props.feedbackApiClient,
|
feedbackStore: this.props.feedbackStore,
|
||||||
onAfterFeedbackReceived: this.props.onAfterFeedbackReceived}
|
onAfterFeedbackReceived: this.props.onAfterFeedbackReceived}
|
||||||
),
|
),
|
||||||
sharedViews.ConversationView({
|
sharedViews.ConversationView({
|
||||||
@ -611,7 +616,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||||
.isRequired,
|
.isRequired,
|
||||||
sdk: React.PropTypes.object.isRequired,
|
sdk: React.PropTypes.object.isRequired,
|
||||||
feedbackApiClient: React.PropTypes.object.isRequired
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -690,7 +695,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
EndedConversationView({
|
EndedConversationView({
|
||||||
sdk: this.props.sdk,
|
sdk: this.props.sdk,
|
||||||
conversation: this.props.conversation,
|
conversation: this.props.conversation,
|
||||||
feedbackApiClient: this.props.feedbackApiClient,
|
feedbackStore: this.props.feedbackStore,
|
||||||
onAfterFeedbackReceived: this.callStatusSwitcher("start")}
|
onAfterFeedbackReceived: this.callStatusSwitcher("start")}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -887,14 +892,14 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||||
.isRequired,
|
.isRequired,
|
||||||
sdk: React.PropTypes.object.isRequired,
|
sdk: React.PropTypes.object.isRequired,
|
||||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
|
||||||
|
|
||||||
// XXX New types for flux style
|
// XXX New types for flux style
|
||||||
standaloneAppStore: React.PropTypes.instanceOf(
|
standaloneAppStore: React.PropTypes.instanceOf(
|
||||||
loop.store.StandaloneAppStore).isRequired,
|
loop.store.StandaloneAppStore).isRequired,
|
||||||
activeRoomStore: React.PropTypes.instanceOf(
|
activeRoomStore: React.PropTypes.instanceOf(
|
||||||
loop.store.ActiveRoomStore).isRequired,
|
loop.store.ActiveRoomStore).isRequired,
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -931,7 +936,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
helper: this.props.helper,
|
helper: this.props.helper,
|
||||||
notifications: this.props.notifications,
|
notifications: this.props.notifications,
|
||||||
sdk: this.props.sdk,
|
sdk: this.props.sdk,
|
||||||
feedbackApiClient: this.props.feedbackApiClient}
|
feedbackStore: this.props.feedbackStore}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -992,7 +997,14 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
sdk: OT
|
sdk: OT
|
||||||
});
|
});
|
||||||
|
var feedbackClient = new loop.FeedbackAPIClient(
|
||||||
|
loop.config.feedbackApiUrl, {
|
||||||
|
product: loop.config.feedbackProductName,
|
||||||
|
user_agent: navigator.userAgent,
|
||||||
|
url: document.location.origin
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stores
|
||||||
var standaloneAppStore = new loop.store.StandaloneAppStore({
|
var standaloneAppStore = new loop.store.StandaloneAppStore({
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
@ -1003,6 +1015,9 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
mozLoop: standaloneMozLoop,
|
mozLoop: standaloneMozLoop,
|
||||||
sdkDriver: sdkDriver
|
sdkDriver: sdkDriver
|
||||||
});
|
});
|
||||||
|
var feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: feedbackClient
|
||||||
|
});
|
||||||
|
|
||||||
window.addEventListener("unload", function() {
|
window.addEventListener("unload", function() {
|
||||||
dispatcher.dispatch(new sharedActions.WindowUnload());
|
dispatcher.dispatch(new sharedActions.WindowUnload());
|
||||||
@ -1014,7 +1029,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
helper: helper,
|
helper: helper,
|
||||||
notifications: notifications,
|
notifications: notifications,
|
||||||
sdk: OT,
|
sdk: OT,
|
||||||
feedbackApiClient: feedbackApiClient,
|
feedbackStore: feedbackStore,
|
||||||
standaloneAppStore: standaloneAppStore,
|
standaloneAppStore: standaloneAppStore,
|
||||||
activeRoomStore: activeRoomStore,
|
activeRoomStore: activeRoomStore,
|
||||||
dispatcher: dispatcher}
|
dispatcher: dispatcher}
|
||||||
|
@ -260,6 +260,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
<div title={mozL10n.get("vendor_alttext",
|
<div title={mozL10n.get("vendor_alttext",
|
||||||
{vendorShortname: mozL10n.get("vendorShortname")})}
|
{vendorShortname: mozL10n.get("vendorShortname")})}
|
||||||
className="footer-logo"></div>
|
className="footer-logo"></div>
|
||||||
|
<div className="footer-external-links">
|
||||||
|
<a target="_blank" href={loop.config.guestSupportUrl}>
|
||||||
|
{mozL10n.get("support_link")}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -538,7 +543,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||||
.isRequired,
|
.isRequired,
|
||||||
sdk: React.PropTypes.object.isRequired,
|
sdk: React.PropTypes.object.isRequired,
|
||||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore),
|
||||||
onAfterFeedbackReceived: React.PropTypes.func.isRequired
|
onAfterFeedbackReceived: React.PropTypes.func.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -549,7 +554,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
return (
|
return (
|
||||||
<div className="ended-conversation">
|
<div className="ended-conversation">
|
||||||
<sharedViews.FeedbackView
|
<sharedViews.FeedbackView
|
||||||
feedbackApiClient={this.props.feedbackApiClient}
|
feedbackStore={this.props.feedbackStore}
|
||||||
onAfterFeedbackReceived={this.props.onAfterFeedbackReceived}
|
onAfterFeedbackReceived={this.props.onAfterFeedbackReceived}
|
||||||
/>
|
/>
|
||||||
<sharedViews.ConversationView
|
<sharedViews.ConversationView
|
||||||
@ -611,7 +616,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||||
.isRequired,
|
.isRequired,
|
||||||
sdk: React.PropTypes.object.isRequired,
|
sdk: React.PropTypes.object.isRequired,
|
||||||
feedbackApiClient: React.PropTypes.object.isRequired
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -690,7 +695,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
<EndedConversationView
|
<EndedConversationView
|
||||||
sdk={this.props.sdk}
|
sdk={this.props.sdk}
|
||||||
conversation={this.props.conversation}
|
conversation={this.props.conversation}
|
||||||
feedbackApiClient={this.props.feedbackApiClient}
|
feedbackStore={this.props.feedbackStore}
|
||||||
onAfterFeedbackReceived={this.callStatusSwitcher("start")}
|
onAfterFeedbackReceived={this.callStatusSwitcher("start")}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -887,14 +892,14 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||||
.isRequired,
|
.isRequired,
|
||||||
sdk: React.PropTypes.object.isRequired,
|
sdk: React.PropTypes.object.isRequired,
|
||||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
|
||||||
|
|
||||||
// XXX New types for flux style
|
// XXX New types for flux style
|
||||||
standaloneAppStore: React.PropTypes.instanceOf(
|
standaloneAppStore: React.PropTypes.instanceOf(
|
||||||
loop.store.StandaloneAppStore).isRequired,
|
loop.store.StandaloneAppStore).isRequired,
|
||||||
activeRoomStore: React.PropTypes.instanceOf(
|
activeRoomStore: React.PropTypes.instanceOf(
|
||||||
loop.store.ActiveRoomStore).isRequired,
|
loop.store.ActiveRoomStore).isRequired,
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||||
|
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -931,7 +936,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
helper={this.props.helper}
|
helper={this.props.helper}
|
||||||
notifications={this.props.notifications}
|
notifications={this.props.notifications}
|
||||||
sdk={this.props.sdk}
|
sdk={this.props.sdk}
|
||||||
feedbackApiClient={this.props.feedbackApiClient}
|
feedbackStore={this.props.feedbackStore}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -992,7 +997,14 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
sdk: OT
|
sdk: OT
|
||||||
});
|
});
|
||||||
|
var feedbackClient = new loop.FeedbackAPIClient(
|
||||||
|
loop.config.feedbackApiUrl, {
|
||||||
|
product: loop.config.feedbackProductName,
|
||||||
|
user_agent: navigator.userAgent,
|
||||||
|
url: document.location.origin
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stores
|
||||||
var standaloneAppStore = new loop.store.StandaloneAppStore({
|
var standaloneAppStore = new loop.store.StandaloneAppStore({
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
@ -1003,6 +1015,9 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
mozLoop: standaloneMozLoop,
|
mozLoop: standaloneMozLoop,
|
||||||
sdkDriver: sdkDriver
|
sdkDriver: sdkDriver
|
||||||
});
|
});
|
||||||
|
var feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: feedbackClient
|
||||||
|
});
|
||||||
|
|
||||||
window.addEventListener("unload", function() {
|
window.addEventListener("unload", function() {
|
||||||
dispatcher.dispatch(new sharedActions.WindowUnload());
|
dispatcher.dispatch(new sharedActions.WindowUnload());
|
||||||
@ -1014,7 +1029,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
helper={helper}
|
helper={helper}
|
||||||
notifications={notifications}
|
notifications={notifications}
|
||||||
sdk={OT}
|
sdk={OT}
|
||||||
feedbackApiClient={feedbackApiClient}
|
feedbackStore={feedbackStore}
|
||||||
standaloneAppStore={standaloneAppStore}
|
standaloneAppStore={standaloneAppStore}
|
||||||
activeRoomStore={activeRoomStore}
|
activeRoomStore={activeRoomStore}
|
||||||
dispatcher={dispatcher}
|
dispatcher={dispatcher}
|
||||||
|
@ -124,4 +124,4 @@ standalone_title_with_status={{clientShortname}} — {{currentStatus}}
|
|||||||
status_in_conversation=In conversation
|
status_in_conversation=In conversation
|
||||||
status_conversation_ended=Conversation ended
|
status_conversation_ended=Conversation ended
|
||||||
status_error=Something went wrong
|
status_error=Something went wrong
|
||||||
|
support_link=Get Help
|
||||||
|
@ -30,7 +30,9 @@ function getConfigFile(req, res) {
|
|||||||
"loop.config.legalWebsiteUrl = '/legal/terms';",
|
"loop.config.legalWebsiteUrl = '/legal/terms';",
|
||||||
"loop.config.fxosApp = loop.config.fxosApp || {};",
|
"loop.config.fxosApp = loop.config.fxosApp || {};",
|
||||||
"loop.config.fxosApp.name = 'Loop';",
|
"loop.config.fxosApp.name = 'Loop';",
|
||||||
"loop.config.fxosApp.manifestUrl = 'http://fake-market.herokuapp.com/apps/packagedApp/manifest.webapp';"
|
"loop.config.fxosApp.manifestUrl = 'http://fake-market.herokuapp.com/apps/packagedApp/manifest.webapp';",
|
||||||
|
"loop.config.roomsSupportUrl = 'https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc';",
|
||||||
|
"loop.config.guestSupportUrl = 'https://support.mozilla.org/kb/respond-firefox-hello-invitation-guest-mode';"
|
||||||
].join("\n"));
|
].join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,13 +445,14 @@ describe("loop.conversationViews", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("OutgoingConversationView", function() {
|
describe("OutgoingConversationView", function() {
|
||||||
var store;
|
var store, feedbackStore;
|
||||||
|
|
||||||
function mountTestComponent() {
|
function mountTestComponent() {
|
||||||
return TestUtils.renderIntoDocument(
|
return TestUtils.renderIntoDocument(
|
||||||
loop.conversationViews.OutgoingConversationView({
|
loop.conversationViews.OutgoingConversationView({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
store: store
|
store: store,
|
||||||
|
feedbackStore: feedbackStore
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,6 +462,9 @@ describe("loop.conversationViews", function () {
|
|||||||
client: {},
|
client: {},
|
||||||
sdkDriver: {}
|
sdkDriver: {}
|
||||||
});
|
});
|
||||||
|
feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: {}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render the CallFailedView when the call state is 'terminated'",
|
it("should render the CallFailedView when the call state is 'terminated'",
|
||||||
|
@ -233,7 +233,8 @@ describe("loop.conversation", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("IncomingConversationView", function() {
|
describe("IncomingConversationView", function() {
|
||||||
var conversationAppStore, conversation, client, icView, oldTitle;
|
var conversationAppStore, conversation, client, icView, oldTitle,
|
||||||
|
feedbackStore;
|
||||||
|
|
||||||
function mountTestComponent() {
|
function mountTestComponent() {
|
||||||
return TestUtils.renderIntoDocument(
|
return TestUtils.renderIntoDocument(
|
||||||
@ -241,7 +242,8 @@ describe("loop.conversation", function() {
|
|||||||
client: client,
|
client: client,
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
sdk: {},
|
sdk: {},
|
||||||
conversationAppStore: conversationAppStore
|
conversationAppStore: conversationAppStore,
|
||||||
|
feedbackStore: feedbackStore
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,6 +259,9 @@ describe("loop.conversation", function() {
|
|||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: navigator.mozLoop
|
mozLoop: navigator.mozLoop
|
||||||
});
|
});
|
||||||
|
feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: {}
|
||||||
|
});
|
||||||
sandbox.stub(conversation, "setOutgoingSessionData");
|
sandbox.stub(conversation, "setOutgoingSessionData");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -46,6 +46,8 @@
|
|||||||
<script src="../../content/shared/js/store.js"></script>
|
<script src="../../content/shared/js/store.js"></script>
|
||||||
<script src="../../content/shared/js/roomStore.js"></script>
|
<script src="../../content/shared/js/roomStore.js"></script>
|
||||||
<script src="../../content/shared/js/activeRoomStore.js"></script>
|
<script src="../../content/shared/js/activeRoomStore.js"></script>
|
||||||
|
<script src="../../content/shared/js/feedbackStore.js"></script>
|
||||||
|
<script src="../../content/shared/js/feedbackViews.js"></script>
|
||||||
<script src="../../content/js/client.js"></script>
|
<script src="../../content/js/client.js"></script>
|
||||||
<script src="../../content/js/conversationAppStore.js"></script>
|
<script src="../../content/js/conversationAppStore.js"></script>
|
||||||
<script src="../../content/js/roomViews.js"></script>
|
<script src="../../content/js/roomViews.js"></script>
|
||||||
|
@ -356,6 +356,31 @@ describe("loop.panel", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Help", function() {
|
||||||
|
var supportUrl = "https://example.com";
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
navigator.mozLoop.getLoopPref = function(pref) {
|
||||||
|
if (pref === "support_url")
|
||||||
|
return supportUrl;
|
||||||
|
return "unseen";
|
||||||
|
};
|
||||||
|
|
||||||
|
sandbox.stub(window, "open");
|
||||||
|
sandbox.stub(window, "close");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should open a tab to the support page", function() {
|
||||||
|
var view = TestUtils.renderIntoDocument(loop.panel.SettingsDropdown());
|
||||||
|
|
||||||
|
TestUtils.Simulate
|
||||||
|
.click(view.getDOMNode().querySelector(".icon-help"));
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(window.open);
|
||||||
|
sinon.assert.calledWithExactly(window.open, supportUrl);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("#render", function() {
|
describe("#render", function() {
|
||||||
it("should render a ToSView", function() {
|
it("should render a ToSView", function() {
|
||||||
var view = createTestPanelView();
|
var view = createTestPanelView();
|
||||||
|
108
browser/components/loop/test/shared/feedbackStore_test.js
Normal file
108
browser/components/loop/test/shared/feedbackStore_test.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/* global chai, loop */
|
||||||
|
|
||||||
|
var expect = chai.expect;
|
||||||
|
var sharedActions = loop.shared.actions;
|
||||||
|
|
||||||
|
describe("loop.store.FeedbackStore", function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
|
||||||
|
var sandbox, dispatcher, store, feedbackClient;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
|
||||||
|
dispatcher = new loop.Dispatcher();
|
||||||
|
|
||||||
|
feedbackClient = new loop.FeedbackAPIClient("http://invalid", {
|
||||||
|
product: "Loop"
|
||||||
|
});
|
||||||
|
|
||||||
|
store = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: feedbackClient
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#constructor", function() {
|
||||||
|
it("should throw an error if feedbackClient is missing", function() {
|
||||||
|
expect(function() {
|
||||||
|
new loop.store.FeedbackStore(dispatcher);
|
||||||
|
}).to.Throw(/feedbackClient/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the store to the INIT feedback state", function() {
|
||||||
|
var store = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: feedbackClient
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(store.getStoreState("feedbackState")).eql(FEEDBACK_STATES.INIT);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#requireFeedbackDetails", function() {
|
||||||
|
it("should transition to DETAILS state", function() {
|
||||||
|
store.requireFeedbackDetails(new sharedActions.RequireFeedbackDetails());
|
||||||
|
|
||||||
|
expect(store.getStoreState("feedbackState")).eql(FEEDBACK_STATES.DETAILS);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#sendFeedback", function() {
|
||||||
|
var sadFeedbackData = {
|
||||||
|
happy: false,
|
||||||
|
category: "fakeCategory",
|
||||||
|
description: "fakeDescription"
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
store.requireFeedbackDetails();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send feedback data over the feedback client", function() {
|
||||||
|
sandbox.stub(feedbackClient, "send");
|
||||||
|
|
||||||
|
store.sendFeedback(new sharedActions.SendFeedback(sadFeedbackData));
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(feedbackClient.send);
|
||||||
|
sinon.assert.calledWithMatch(feedbackClient.send, sadFeedbackData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should transition to PENDING state", function() {
|
||||||
|
sandbox.stub(feedbackClient, "send");
|
||||||
|
|
||||||
|
store.sendFeedback(new sharedActions.SendFeedback(sadFeedbackData));
|
||||||
|
|
||||||
|
expect(store.getStoreState("feedbackState")).eql(FEEDBACK_STATES.PENDING);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should transition to SENT state on successful submission", function(done) {
|
||||||
|
sandbox.stub(feedbackClient, "send", function(data, cb) {
|
||||||
|
cb(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
store.once("change:feedbackState", function() {
|
||||||
|
expect(store.getStoreState("feedbackState")).eql(FEEDBACK_STATES.SENT);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
store.sendFeedback(new sharedActions.SendFeedback(sadFeedbackData));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should transition to FAILED state on failed submission", function(done) {
|
||||||
|
sandbox.stub(feedbackClient, "send", function(data, cb) {
|
||||||
|
cb(new Error("failed"));
|
||||||
|
});
|
||||||
|
|
||||||
|
store.once("change:feedbackState", function() {
|
||||||
|
expect(store.getStoreState("feedbackState")).eql(FEEDBACK_STATES.FAILED);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
store.sendFeedback(new sharedActions.SendFeedback(sadFeedbackData));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
209
browser/components/loop/test/shared/feedbackViews_test.js
Normal file
209
browser/components/loop/test/shared/feedbackViews_test.js
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/*global loop, sinon, React */
|
||||||
|
/* jshint newcap:false */
|
||||||
|
|
||||||
|
var expect = chai.expect;
|
||||||
|
var l10n = navigator.mozL10n || document.mozL10n;
|
||||||
|
var TestUtils = React.addons.TestUtils;
|
||||||
|
var sharedActions = loop.shared.actions;
|
||||||
|
var sharedViews = loop.shared.views;
|
||||||
|
|
||||||
|
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
|
||||||
|
|
||||||
|
describe("loop.shared.views.FeedbackView", function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var sandbox, comp, dispatcher, feedbackStore, fakeAudioXHR, fakeFeedbackClient;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
fakeAudioXHR = {
|
||||||
|
open: sinon.spy(),
|
||||||
|
send: function() {},
|
||||||
|
abort: function() {},
|
||||||
|
getResponseHeader: function(header) {
|
||||||
|
if (header === "Content-Type")
|
||||||
|
return "audio/ogg";
|
||||||
|
},
|
||||||
|
responseType: null,
|
||||||
|
response: new ArrayBuffer(10),
|
||||||
|
onload: null
|
||||||
|
};
|
||||||
|
dispatcher = new loop.Dispatcher();
|
||||||
|
fakeFeedbackClient = {send: sandbox.stub()};
|
||||||
|
feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: fakeFeedbackClient
|
||||||
|
});
|
||||||
|
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
||||||
|
comp = TestUtils.renderIntoDocument(sharedViews.FeedbackView({
|
||||||
|
feedbackStore: feedbackStore
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
// local test helpers
|
||||||
|
function clickHappyFace(comp) {
|
||||||
|
var happyFace = comp.getDOMNode().querySelector(".face-happy");
|
||||||
|
TestUtils.Simulate.click(happyFace);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickSadFace(comp) {
|
||||||
|
var sadFace = comp.getDOMNode().querySelector(".face-sad");
|
||||||
|
TestUtils.Simulate.click(sadFace);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillSadFeedbackForm(comp, category, text) {
|
||||||
|
TestUtils.Simulate.change(
|
||||||
|
comp.getDOMNode().querySelector("[value='" + category + "']"));
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
TestUtils.Simulate.change(
|
||||||
|
comp.getDOMNode().querySelector("[name='description']"), {
|
||||||
|
target: {value: "fake reason"}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitSadFeedbackForm(comp, category, text) {
|
||||||
|
TestUtils.Simulate.submit(comp.getDOMNode().querySelector("form"));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Happy feedback", function() {
|
||||||
|
it("should dispatch a SendFeedback action", function() {
|
||||||
|
var dispatch = sandbox.stub(dispatcher, "dispatch");
|
||||||
|
|
||||||
|
clickHappyFace(comp);
|
||||||
|
|
||||||
|
sinon.assert.calledWithMatch(dispatch, new sharedActions.SendFeedback({
|
||||||
|
happy: true,
|
||||||
|
category: "",
|
||||||
|
description: ""
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should thank the user once feedback data is sent", function() {
|
||||||
|
feedbackStore.setStoreState({feedbackState: FEEDBACK_STATES.SENT});
|
||||||
|
|
||||||
|
expect(comp.getDOMNode().querySelectorAll(".thank-you")).not.eql(null);
|
||||||
|
expect(comp.getDOMNode().querySelector("button.fx-embedded-btn-back"))
|
||||||
|
.eql(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Sad feedback", function() {
|
||||||
|
it("should bring the user to feedback form when clicking on the sad face",
|
||||||
|
function() {
|
||||||
|
clickSadFace(comp);
|
||||||
|
|
||||||
|
expect(comp.getDOMNode().querySelectorAll("form")).not.eql(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render a back button", function() {
|
||||||
|
feedbackStore.setStoreState({feedbackState: FEEDBACK_STATES.DETAILS});
|
||||||
|
|
||||||
|
expect(comp.getDOMNode().querySelector("button.fx-embedded-btn-back"))
|
||||||
|
.not.eql(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reset the view when clicking the back button", function() {
|
||||||
|
feedbackStore.setStoreState({feedbackState: FEEDBACK_STATES.DETAILS});
|
||||||
|
|
||||||
|
TestUtils.Simulate.click(
|
||||||
|
comp.getDOMNode().querySelector("button.fx-embedded-btn-back"));
|
||||||
|
|
||||||
|
expect(comp.getDOMNode().querySelector(".faces")).not.eql(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should disable the form submit button when no category is chosen",
|
||||||
|
function() {
|
||||||
|
clickSadFace(comp);
|
||||||
|
|
||||||
|
expect(comp.getDOMNode().querySelector("form button").disabled).eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should disable the form submit button when the 'other' category is " +
|
||||||
|
"chosen but no description has been entered yet",
|
||||||
|
function() {
|
||||||
|
clickSadFace(comp);
|
||||||
|
fillSadFeedbackForm(comp, "other");
|
||||||
|
|
||||||
|
expect(comp.getDOMNode().querySelector("form button").disabled).eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should enable the form submit button when the 'other' category is " +
|
||||||
|
"chosen and a description is entered",
|
||||||
|
function() {
|
||||||
|
clickSadFace(comp);
|
||||||
|
fillSadFeedbackForm(comp, "other", "fake");
|
||||||
|
|
||||||
|
expect(comp.getDOMNode().querySelector("form button").disabled).eql(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should empty the description field when a predefined category is " +
|
||||||
|
"chosen",
|
||||||
|
function() {
|
||||||
|
clickSadFace(comp);
|
||||||
|
|
||||||
|
fillSadFeedbackForm(comp, "confusing");
|
||||||
|
|
||||||
|
expect(comp.getDOMNode().querySelector(".feedback-description").value).eql("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should enable the form submit button once a predefined category is " +
|
||||||
|
"chosen",
|
||||||
|
function() {
|
||||||
|
clickSadFace(comp);
|
||||||
|
|
||||||
|
fillSadFeedbackForm(comp, "confusing");
|
||||||
|
|
||||||
|
expect(comp.getDOMNode().querySelector("form button").disabled).eql(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send feedback data when the form is submitted", function() {
|
||||||
|
var dispatch = sandbox.stub(dispatcher, "dispatch");
|
||||||
|
feedbackStore.setStoreState({feedbackState: FEEDBACK_STATES.DETAILS});
|
||||||
|
fillSadFeedbackForm(comp, "confusing");
|
||||||
|
|
||||||
|
submitSadFeedbackForm(comp);
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(dispatch);
|
||||||
|
sinon.assert.calledWithMatch(dispatch, new sharedActions.SendFeedback({
|
||||||
|
happy: false,
|
||||||
|
category: "confusing",
|
||||||
|
description: ""
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send feedback data when user has entered a custom description",
|
||||||
|
function() {
|
||||||
|
clickSadFace(comp);
|
||||||
|
|
||||||
|
fillSadFeedbackForm(comp, "other", "fake reason");
|
||||||
|
submitSadFeedbackForm(comp);
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(fakeFeedbackClient.send);
|
||||||
|
sinon.assert.calledWith(fakeFeedbackClient.send, {
|
||||||
|
happy: false,
|
||||||
|
category: "other",
|
||||||
|
description: "fake reason"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should thank the user when feedback data has been sent", function() {
|
||||||
|
fakeFeedbackClient.send = function(data, cb) {
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
clickSadFace(comp);
|
||||||
|
fillSadFeedbackForm(comp, "confusing");
|
||||||
|
submitSadFeedbackForm(comp);
|
||||||
|
|
||||||
|
expect(comp.getDOMNode().querySelectorAll(".thank-you")).not.eql(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -47,6 +47,8 @@
|
|||||||
<script src="../../content/shared/js/activeRoomStore.js"></script>
|
<script src="../../content/shared/js/activeRoomStore.js"></script>
|
||||||
<script src="../../content/shared/js/roomStore.js"></script>
|
<script src="../../content/shared/js/roomStore.js"></script>
|
||||||
<script src="../../content/shared/js/conversationStore.js"></script>
|
<script src="../../content/shared/js/conversationStore.js"></script>
|
||||||
|
<script src="../../content/shared/js/feedbackStore.js"></script>
|
||||||
|
<script src="../../content/shared/js/feedbackViews.js"></script>
|
||||||
|
|
||||||
<!-- Test scripts -->
|
<!-- Test scripts -->
|
||||||
<script src="models_test.js"></script>
|
<script src="models_test.js"></script>
|
||||||
@ -55,10 +57,12 @@
|
|||||||
<script src="views_test.js"></script>
|
<script src="views_test.js"></script>
|
||||||
<script src="websocket_test.js"></script>
|
<script src="websocket_test.js"></script>
|
||||||
<script src="feedbackApiClient_test.js"></script>
|
<script src="feedbackApiClient_test.js"></script>
|
||||||
|
<script src="feedbackViews_test.js"></script>
|
||||||
<script src="validate_test.js"></script>
|
<script src="validate_test.js"></script>
|
||||||
<script src="dispatcher_test.js"></script>
|
<script src="dispatcher_test.js"></script>
|
||||||
<script src="activeRoomStore_test.js"></script>
|
<script src="activeRoomStore_test.js"></script>
|
||||||
<script src="conversationStore_test.js"></script>
|
<script src="conversationStore_test.js"></script>
|
||||||
|
<script src="feedbackStore_test.js"></script>
|
||||||
<script src="otSdkDriver_test.js"></script>
|
<script src="otSdkDriver_test.js"></script>
|
||||||
<script src="store_test.js"></script>
|
<script src="store_test.js"></script>
|
||||||
<script src="roomStore_test.js"></script>
|
<script src="roomStore_test.js"></script>
|
||||||
|
@ -526,177 +526,6 @@ describe("loop.shared.views", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("FeedbackView", function() {
|
|
||||||
var comp, fakeFeedbackApiClient;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
fakeFeedbackApiClient = {send: sandbox.stub()};
|
|
||||||
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
|
||||||
comp = TestUtils.renderIntoDocument(sharedViews.FeedbackView({
|
|
||||||
feedbackApiClient: fakeFeedbackApiClient
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
// local test helpers
|
|
||||||
function clickHappyFace(comp) {
|
|
||||||
var happyFace = comp.getDOMNode().querySelector(".face-happy");
|
|
||||||
TestUtils.Simulate.click(happyFace);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clickSadFace(comp) {
|
|
||||||
var sadFace = comp.getDOMNode().querySelector(".face-sad");
|
|
||||||
TestUtils.Simulate.click(sadFace);
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillSadFeedbackForm(comp, category, text) {
|
|
||||||
TestUtils.Simulate.change(
|
|
||||||
comp.getDOMNode().querySelector("[value='" + category + "']"));
|
|
||||||
|
|
||||||
if (text) {
|
|
||||||
TestUtils.Simulate.change(
|
|
||||||
comp.getDOMNode().querySelector("[name='description']"), {
|
|
||||||
target: {value: "fake reason"}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitSadFeedbackForm(comp, category, text) {
|
|
||||||
TestUtils.Simulate.submit(comp.getDOMNode().querySelector("form"));
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("Happy feedback", function() {
|
|
||||||
it("should send feedback data when clicking on the happy face",
|
|
||||||
function() {
|
|
||||||
clickHappyFace(comp);
|
|
||||||
|
|
||||||
sinon.assert.calledOnce(fakeFeedbackApiClient.send);
|
|
||||||
sinon.assert.calledWith(fakeFeedbackApiClient.send, {happy: true});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should thank the user once happy feedback data is sent", function() {
|
|
||||||
fakeFeedbackApiClient.send = function(data, cb) {
|
|
||||||
cb();
|
|
||||||
};
|
|
||||||
|
|
||||||
clickHappyFace(comp);
|
|
||||||
|
|
||||||
expect(comp.getDOMNode()
|
|
||||||
.querySelectorAll(".feedback .thank-you").length).eql(1);
|
|
||||||
expect(comp.getDOMNode().querySelector("button.back")).to.be.a("null");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Sad feedback", function() {
|
|
||||||
it("should bring the user to feedback form when clicking on the sad face",
|
|
||||||
function() {
|
|
||||||
clickSadFace(comp);
|
|
||||||
|
|
||||||
expect(comp.getDOMNode().querySelectorAll("form").length).eql(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should disable the form submit button when no category is chosen",
|
|
||||||
function() {
|
|
||||||
clickSadFace(comp);
|
|
||||||
|
|
||||||
expect(comp.getDOMNode()
|
|
||||||
.querySelector("form button").disabled).eql(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should disable the form submit button when the 'other' category is " +
|
|
||||||
"chosen but no description has been entered yet",
|
|
||||||
function() {
|
|
||||||
clickSadFace(comp);
|
|
||||||
fillSadFeedbackForm(comp, "other");
|
|
||||||
|
|
||||||
expect(comp.getDOMNode()
|
|
||||||
.querySelector("form button").disabled).eql(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should enable the form submit button when the 'other' category is " +
|
|
||||||
"chosen and a description is entered",
|
|
||||||
function() {
|
|
||||||
clickSadFace(comp);
|
|
||||||
fillSadFeedbackForm(comp, "other", "fake");
|
|
||||||
|
|
||||||
expect(comp.getDOMNode()
|
|
||||||
.querySelector("form button").disabled).eql(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should empty the description field when a predefined category is " +
|
|
||||||
"chosen",
|
|
||||||
function() {
|
|
||||||
clickSadFace(comp);
|
|
||||||
|
|
||||||
fillSadFeedbackForm(comp, "confusing");
|
|
||||||
|
|
||||||
expect(comp.getDOMNode()
|
|
||||||
.querySelector(".feedback-description").value).eql("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should enable the form submit button once a predefined category is " +
|
|
||||||
"chosen",
|
|
||||||
function() {
|
|
||||||
clickSadFace(comp);
|
|
||||||
|
|
||||||
fillSadFeedbackForm(comp, "confusing");
|
|
||||||
|
|
||||||
expect(comp.getDOMNode()
|
|
||||||
.querySelector("form button").disabled).eql(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should disable the form submit button once the form is submitted",
|
|
||||||
function() {
|
|
||||||
clickSadFace(comp);
|
|
||||||
fillSadFeedbackForm(comp, "confusing");
|
|
||||||
|
|
||||||
submitSadFeedbackForm(comp);
|
|
||||||
|
|
||||||
expect(comp.getDOMNode()
|
|
||||||
.querySelector("form button").disabled).eql(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should send feedback data when the form is submitted", function() {
|
|
||||||
clickSadFace(comp);
|
|
||||||
fillSadFeedbackForm(comp, "confusing");
|
|
||||||
|
|
||||||
submitSadFeedbackForm(comp);
|
|
||||||
|
|
||||||
sinon.assert.calledOnce(fakeFeedbackApiClient.send);
|
|
||||||
sinon.assert.calledWithMatch(fakeFeedbackApiClient.send, {
|
|
||||||
happy: false,
|
|
||||||
category: "confusing"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should send feedback data when user has entered a custom description",
|
|
||||||
function() {
|
|
||||||
clickSadFace(comp);
|
|
||||||
|
|
||||||
fillSadFeedbackForm(comp, "other", "fake reason");
|
|
||||||
submitSadFeedbackForm(comp);
|
|
||||||
|
|
||||||
sinon.assert.calledOnce(fakeFeedbackApiClient.send);
|
|
||||||
sinon.assert.calledWith(fakeFeedbackApiClient.send, {
|
|
||||||
happy: false,
|
|
||||||
category: "other",
|
|
||||||
description: "fake reason"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should thank the user when feedback data has been sent", function() {
|
|
||||||
fakeFeedbackApiClient.send = function(data, cb) {
|
|
||||||
cb();
|
|
||||||
};
|
|
||||||
clickSadFace(comp);
|
|
||||||
fillSadFeedbackForm(comp, "confusing");
|
|
||||||
submitSadFeedbackForm(comp);
|
|
||||||
|
|
||||||
expect(comp.getDOMNode()
|
|
||||||
.querySelectorAll(".feedback .thank-you").length).eql(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("NotificationListView", function() {
|
describe("NotificationListView", function() {
|
||||||
var coll, view, testNotif;
|
var coll, view, testNotif;
|
||||||
|
|
||||||
|
@ -42,6 +42,8 @@
|
|||||||
<script src="../../content/shared/js/dispatcher.js"></script>
|
<script src="../../content/shared/js/dispatcher.js"></script>
|
||||||
<script src="../../content/shared/js/store.js"></script>
|
<script src="../../content/shared/js/store.js"></script>
|
||||||
<script src="../../content/shared/js/activeRoomStore.js"></script>
|
<script src="../../content/shared/js/activeRoomStore.js"></script>
|
||||||
|
<script src="../../content/shared/js/feedbackStore.js"></script>
|
||||||
|
<script src="../../content/shared/js/feedbackViews.js"></script>
|
||||||
<script src="../../content/shared/js/otSdkDriver.js"></script>
|
<script src="../../content/shared/js/otSdkDriver.js"></script>
|
||||||
<script src="../../standalone/content/js/multiplexGum.js"></script>
|
<script src="../../standalone/content/js/multiplexGum.js"></script>
|
||||||
<script src="../../standalone/content/js/standaloneAppStore.js"></script>
|
<script src="../../standalone/content/js/standaloneAppStore.js"></script>
|
||||||
|
@ -19,14 +19,20 @@ describe("loop.webapp", function() {
|
|||||||
notifications,
|
notifications,
|
||||||
feedbackApiClient,
|
feedbackApiClient,
|
||||||
stubGetPermsAndCacheMedia,
|
stubGetPermsAndCacheMedia,
|
||||||
fakeAudioXHR;
|
fakeAudioXHR,
|
||||||
|
dispatcher,
|
||||||
|
feedbackStore;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
|
dispatcher = new loop.Dispatcher();
|
||||||
notifications = new sharedModels.NotificationCollection();
|
notifications = new sharedModels.NotificationCollection();
|
||||||
feedbackApiClient = new loop.FeedbackAPIClient("http://invalid", {
|
feedbackApiClient = new loop.FeedbackAPIClient("http://invalid", {
|
||||||
product: "Loop"
|
product: "Loop"
|
||||||
});
|
});
|
||||||
|
feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: {}
|
||||||
|
});
|
||||||
|
|
||||||
stubGetPermsAndCacheMedia = sandbox.stub(
|
stubGetPermsAndCacheMedia = sandbox.stub(
|
||||||
loop.standaloneMedia._MultiplexGum.prototype, "getPermsAndCacheMedia");
|
loop.standaloneMedia._MultiplexGum.prototype, "getPermsAndCacheMedia");
|
||||||
@ -123,7 +129,7 @@ describe("loop.webapp", function() {
|
|||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
notifications: notifications,
|
notifications: notifications,
|
||||||
sdk: {},
|
sdk: {},
|
||||||
feedbackApiClient: feedbackApiClient
|
feedbackStore: feedbackStore
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -582,7 +588,7 @@ describe("loop.webapp", function() {
|
|||||||
|
|
||||||
describe("WebappRootView", function() {
|
describe("WebappRootView", function() {
|
||||||
var helper, sdk, conversationModel, client, props, standaloneAppStore;
|
var helper, sdk, conversationModel, client, props, standaloneAppStore;
|
||||||
var dispatcher, activeRoomStore;
|
var activeRoomStore;
|
||||||
|
|
||||||
function mountTestComponent() {
|
function mountTestComponent() {
|
||||||
return TestUtils.renderIntoDocument(
|
return TestUtils.renderIntoDocument(
|
||||||
@ -609,7 +615,6 @@ describe("loop.webapp", function() {
|
|||||||
client = new loop.StandaloneClient({
|
client = new loop.StandaloneClient({
|
||||||
baseServerUrl: "fakeUrl"
|
baseServerUrl: "fakeUrl"
|
||||||
});
|
});
|
||||||
dispatcher = new loop.Dispatcher();
|
|
||||||
activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, {
|
activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, {
|
||||||
mozLoop: {},
|
mozLoop: {},
|
||||||
sdkDriver: {}
|
sdkDriver: {}
|
||||||
@ -1039,7 +1044,7 @@ describe("loop.webapp", function() {
|
|||||||
loop.webapp.EndedConversationView({
|
loop.webapp.EndedConversationView({
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
sdk: {},
|
sdk: {},
|
||||||
feedbackApiClient: feedbackApiClient,
|
feedbackStore: feedbackStore,
|
||||||
onAfterFeedbackReceived: function(){}
|
onAfterFeedbackReceived: function(){}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
<script src="../content/shared/js/roomStore.js"></script>
|
<script src="../content/shared/js/roomStore.js"></script>
|
||||||
<script src="../content/shared/js/conversationStore.js"></script>
|
<script src="../content/shared/js/conversationStore.js"></script>
|
||||||
<script src="../content/shared/js/activeRoomStore.js"></script>
|
<script src="../content/shared/js/activeRoomStore.js"></script>
|
||||||
|
<script src="../content/shared/js/feedbackStore.js"></script>
|
||||||
|
<script src="../content/shared/js/feedbackViews.js"></script>
|
||||||
<script src="../content/js/roomViews.js"></script>
|
<script src="../content/js/roomViews.js"></script>
|
||||||
<script src="../content/js/conversationViews.js"></script>
|
<script src="../content/js/conversationViews.js"></script>
|
||||||
<script src="../content/js/client.js"></script>
|
<script src="../content/js/client.js"></script>
|
||||||
|
@ -39,8 +39,9 @@
|
|||||||
var ConversationView = loop.shared.views.ConversationView;
|
var ConversationView = loop.shared.views.ConversationView;
|
||||||
var FeedbackView = loop.shared.views.FeedbackView;
|
var FeedbackView = loop.shared.views.FeedbackView;
|
||||||
|
|
||||||
// Room constants
|
// Store constants
|
||||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
|
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
|
||||||
|
|
||||||
// Local helpers
|
// Local helpers
|
||||||
function returnTrue() {
|
function returnTrue() {
|
||||||
@ -69,6 +70,9 @@
|
|||||||
var roomStore = new loop.store.RoomStore(dispatcher, {
|
var roomStore = new loop.store.RoomStore(dispatcher, {
|
||||||
mozLoop: navigator.mozLoop
|
mozLoop: navigator.mozLoop
|
||||||
});
|
});
|
||||||
|
var feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: stageFeedbackApiClient
|
||||||
|
});
|
||||||
|
|
||||||
// Local mocks
|
// Local mocks
|
||||||
|
|
||||||
@ -460,13 +464,13 @@
|
|||||||
React.DOM.a({href: "https://input.allizom.org/"}, "input.allizom.org"), "."
|
React.DOM.a({href: "https://input.allizom.org/"}, "input.allizom.org"), "."
|
||||||
),
|
),
|
||||||
Example({summary: "Default (useable demo)", dashed: "true", style: {width: "260px"}},
|
Example({summary: "Default (useable demo)", dashed: "true", style: {width: "260px"}},
|
||||||
FeedbackView({feedbackApiClient: stageFeedbackApiClient})
|
FeedbackView({feedbackStore: feedbackStore})
|
||||||
),
|
),
|
||||||
Example({summary: "Detailed form", dashed: "true", style: {width: "260px"}},
|
Example({summary: "Detailed form", dashed: "true", style: {width: "260px"}},
|
||||||
FeedbackView({feedbackApiClient: stageFeedbackApiClient, step: "form"})
|
FeedbackView({feedbackStore: feedbackStore, feedbackState: FEEDBACK_STATES.DETAILS})
|
||||||
),
|
),
|
||||||
Example({summary: "Thank you!", dashed: "true", style: {width: "260px"}},
|
Example({summary: "Thank you!", dashed: "true", style: {width: "260px"}},
|
||||||
FeedbackView({feedbackApiClient: stageFeedbackApiClient, step: "finished"})
|
FeedbackView({feedbackStore: feedbackStore, feedbackState: FEEDBACK_STATES.SENT})
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -486,7 +490,7 @@
|
|||||||
video: {enabled: true},
|
video: {enabled: true},
|
||||||
audio: {enabled: true},
|
audio: {enabled: true},
|
||||||
conversation: mockConversationModel,
|
conversation: mockConversationModel,
|
||||||
feedbackApiClient: stageFeedbackApiClient,
|
feedbackStore: feedbackStore,
|
||||||
onAfterFeedbackReceived: noop})
|
onAfterFeedbackReceived: noop})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -39,8 +39,9 @@
|
|||||||
var ConversationView = loop.shared.views.ConversationView;
|
var ConversationView = loop.shared.views.ConversationView;
|
||||||
var FeedbackView = loop.shared.views.FeedbackView;
|
var FeedbackView = loop.shared.views.FeedbackView;
|
||||||
|
|
||||||
// Room constants
|
// Store constants
|
||||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
|
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
|
||||||
|
|
||||||
// Local helpers
|
// Local helpers
|
||||||
function returnTrue() {
|
function returnTrue() {
|
||||||
@ -69,6 +70,9 @@
|
|||||||
var roomStore = new loop.store.RoomStore(dispatcher, {
|
var roomStore = new loop.store.RoomStore(dispatcher, {
|
||||||
mozLoop: navigator.mozLoop
|
mozLoop: navigator.mozLoop
|
||||||
});
|
});
|
||||||
|
var feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||||
|
feedbackClient: stageFeedbackApiClient
|
||||||
|
});
|
||||||
|
|
||||||
// Local mocks
|
// Local mocks
|
||||||
|
|
||||||
@ -460,13 +464,13 @@
|
|||||||
<a href="https://input.allizom.org/">input.allizom.org</a>.
|
<a href="https://input.allizom.org/">input.allizom.org</a>.
|
||||||
</p>
|
</p>
|
||||||
<Example summary="Default (useable demo)" dashed="true" style={{width: "260px"}}>
|
<Example summary="Default (useable demo)" dashed="true" style={{width: "260px"}}>
|
||||||
<FeedbackView feedbackApiClient={stageFeedbackApiClient} />
|
<FeedbackView feedbackStore={feedbackStore} />
|
||||||
</Example>
|
</Example>
|
||||||
<Example summary="Detailed form" dashed="true" style={{width: "260px"}}>
|
<Example summary="Detailed form" dashed="true" style={{width: "260px"}}>
|
||||||
<FeedbackView feedbackApiClient={stageFeedbackApiClient} step="form" />
|
<FeedbackView feedbackStore={feedbackStore} feedbackState={FEEDBACK_STATES.DETAILS} />
|
||||||
</Example>
|
</Example>
|
||||||
<Example summary="Thank you!" dashed="true" style={{width: "260px"}}>
|
<Example summary="Thank you!" dashed="true" style={{width: "260px"}}>
|
||||||
<FeedbackView feedbackApiClient={stageFeedbackApiClient} step="finished" />
|
<FeedbackView feedbackStore={feedbackStore} feedbackState={FEEDBACK_STATES.SENT} />
|
||||||
</Example>
|
</Example>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
@ -486,7 +490,7 @@
|
|||||||
video={{enabled: true}}
|
video={{enabled: true}}
|
||||||
audio={{enabled: true}}
|
audio={{enabled: true}}
|
||||||
conversation={mockConversationModel}
|
conversation={mockConversationModel}
|
||||||
feedbackApiClient={stageFeedbackApiClient}
|
feedbackStore={feedbackStore}
|
||||||
onAfterFeedbackReceived={noop} />
|
onAfterFeedbackReceived={noop} />
|
||||||
</div>
|
</div>
|
||||||
</Example>
|
</Example>
|
||||||
|
@ -16,14 +16,15 @@ const { indexedDB } = require("sdk/indexed-db");
|
|||||||
|
|
||||||
const IDB = {
|
const IDB = {
|
||||||
_db: null,
|
_db: null,
|
||||||
|
databaseName: "AppProjects",
|
||||||
|
|
||||||
open: function () {
|
open: function () {
|
||||||
let deferred = promise.defer();
|
let deferred = promise.defer();
|
||||||
|
|
||||||
let request = indexedDB.open("AppProjects", 5);
|
let request = indexedDB.open(IDB.databaseName, 5);
|
||||||
request.onerror = function(event) {
|
request.onerror = function(event) {
|
||||||
deferred.reject("Unable to open AppProjects indexedDB. " +
|
deferred.reject("Unable to open AppProjects indexedDB: " +
|
||||||
"Error code: " + event.target.errorCode);
|
this.error.name + " - " + this.error.message );
|
||||||
};
|
};
|
||||||
request.onupgradeneeded = function(event) {
|
request.onupgradeneeded = function(event) {
|
||||||
let db = event.target.result;
|
let db = event.target.result;
|
||||||
@ -147,11 +148,10 @@ const store = new ObservableObject({ projects:[] });
|
|||||||
|
|
||||||
let loadDeferred = promise.defer();
|
let loadDeferred = promise.defer();
|
||||||
|
|
||||||
IDB.open().then(function (projects) {
|
loadDeferred.resolve(IDB.open().then(function (projects) {
|
||||||
store.object.projects = projects;
|
store.object.projects = projects;
|
||||||
AppProjects.emit("ready", store.object.projects);
|
AppProjects.emit("ready", store.object.projects);
|
||||||
loadDeferred.resolve();
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
const AppProjects = {
|
const AppProjects = {
|
||||||
load: function() {
|
load: function() {
|
||||||
|
@ -72,6 +72,9 @@ let UI = {
|
|||||||
|
|
||||||
AppProjects.load().then(() => {
|
AppProjects.load().then(() => {
|
||||||
this.autoSelectProject();
|
this.autoSelectProject();
|
||||||
|
}, e => {
|
||||||
|
console.error(e);
|
||||||
|
this.reportError("error_appProjectsLoadFailed");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Auto install the ADB Addon Helper and Tools Adapters. Only once.
|
// Auto install the ADB Addon Helper and Tools Adapters. Only once.
|
||||||
@ -256,7 +259,7 @@ let UI = {
|
|||||||
this._busyTimeout = setTimeout(() => {
|
this._busyTimeout = setTimeout(() => {
|
||||||
this.unbusy();
|
this.unbusy();
|
||||||
UI.reportError("error_operationTimeout", this._busyOperationDescription);
|
UI.reportError("error_operationTimeout", this._busyOperationDescription);
|
||||||
}, 6000);
|
}, Services.prefs.getIntPref("devtools.webide.busyTimeout"));
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelBusyTimeout: function() {
|
cancelBusyTimeout: function() {
|
||||||
|
@ -32,3 +32,4 @@ pref("devtools.webide.widget.enabled", false);
|
|||||||
pref("devtools.webide.widget.inNavbarByDefault", false);
|
pref("devtools.webide.widget.inNavbarByDefault", false);
|
||||||
#endif
|
#endif
|
||||||
pref("devtools.webide.zoom", "1");
|
pref("devtools.webide.zoom", "1");
|
||||||
|
pref("devtools.webide.busyTimeout", 10000);
|
||||||
|
@ -20,7 +20,7 @@ importHostedApp_title=Open Hosted App
|
|||||||
importHostedApp_header=Enter Manifest URL
|
importHostedApp_header=Enter Manifest URL
|
||||||
|
|
||||||
notification_showTroubleShooting_label=Troubleshooting
|
notification_showTroubleShooting_label=Troubleshooting
|
||||||
notification_showTroubleShooting_accesskey=t
|
notification_showTroubleShooting_accesskey=T
|
||||||
|
|
||||||
# LOCALIZATION NOTE (project_tab_loading): This is shown as a temporary tab
|
# LOCALIZATION NOTE (project_tab_loading): This is shown as a temporary tab
|
||||||
# title for browser tab projects when the tab is still loading.
|
# title for browser tab projects when the tab is still loading.
|
||||||
@ -42,6 +42,8 @@ error_cantConnectToApp=Can't connect to app: %1$S
|
|||||||
# Variable: error message (in english)
|
# Variable: error message (in english)
|
||||||
error_cantFetchAddonsJSON=Can't fetch the add-on list: %S
|
error_cantFetchAddonsJSON=Can't fetch the add-on list: %S
|
||||||
|
|
||||||
|
error_appProjectsLoadFailed=Unable to load project list. This can occur if you've used this profile with a newer version of Firefox.
|
||||||
|
|
||||||
addons_stable=stable
|
addons_stable=stable
|
||||||
addons_unstable=unstable
|
addons_unstable=unstable
|
||||||
# LOCALIZATION NOTE (addons_simulator_label): This label is shown as the name of
|
# LOCALIZATION NOTE (addons_simulator_label): This label is shown as the name of
|
||||||
|
76
configure.in
76
configure.in
@ -2212,7 +2212,7 @@ ia64*-hpux*)
|
|||||||
if test "$CPU_ARCH" = "x86"; then
|
if test "$CPU_ARCH" = "x86"; then
|
||||||
WIN32_SUBSYSTEM_VERSION=5.01
|
WIN32_SUBSYSTEM_VERSION=5.01
|
||||||
else
|
else
|
||||||
WIN32_SUBSYSTEM_VERSION=5.02
|
WIN32_SUBSYSTEM_VERSION=6.01
|
||||||
fi
|
fi
|
||||||
WIN32_CONSOLE_EXE_LDFLAGS=-SUBSYSTEM:CONSOLE,$WIN32_SUBSYSTEM_VERSION
|
WIN32_CONSOLE_EXE_LDFLAGS=-SUBSYSTEM:CONSOLE,$WIN32_SUBSYSTEM_VERSION
|
||||||
WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
|
WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
|
||||||
@ -5935,53 +5935,57 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
|
|||||||
######################################
|
######################################
|
||||||
# Find _43 for use by XP.
|
# Find _43 for use by XP.
|
||||||
|
|
||||||
# Get the SDK path from the registry.
|
if test "$HAVE_64BIT_BUILD"; then
|
||||||
# First try to get the June 2010 SDK
|
AC_MSG_RESULT([We are building a 64-bit binary, skip checking d3dcompiler_43.])
|
||||||
MOZ_DIRECTX_SDK_REG_KEY=`reg query 'HKLM\Software\Microsoft\DirectX' //s | grep 'Microsoft DirectX SDK (June 2010)' | head -n 1`
|
|
||||||
if test -z "$MOZ_DIRECTX_SDK_REG_KEY" ; then
|
|
||||||
# Otherwise just take whatever comes first
|
|
||||||
MOZ_DIRECTX_SDK_REG_KEY=`reg query 'HKLM\Software\Microsoft\DirectX' //s | grep 'Microsoft DirectX SDK' | head -n 1`
|
|
||||||
fi
|
|
||||||
MOZ_DIRECTX_SDK_PATH=`reg query "$MOZ_DIRECTX_SDK_REG_KEY" //v InstallPath | grep REG_SZ | sed 's/.*\([[a-zA-Z]]\)\\:\\\\/\\1\\:\\\\/' | sed 's,\\\\,/,g'`
|
|
||||||
|
|
||||||
if test -n "$MOZ_DIRECTX_SDK_PATH" &&
|
|
||||||
test -f "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_D3D_CPU_SUFFIX/dxguid.lib ; then
|
|
||||||
AC_MSG_RESULT([Found DirectX SDK via registry, using $MOZ_DIRECTX_SDK_PATH])
|
|
||||||
else
|
else
|
||||||
AC_MSG_RESULT([DirectX SDK not found.])
|
# Get the SDK path from the registry.
|
||||||
MOZ_DIRECTX_SDK_PATH=
|
# First try to get the June 2010 SDK
|
||||||
fi
|
MOZ_DIRECTX_SDK_REG_KEY=`reg query 'HKLM\Software\Microsoft\DirectX' //s | grep 'Microsoft DirectX SDK (June 2010)' | head -n 1`
|
||||||
|
if test -z "$MOZ_DIRECTX_SDK_REG_KEY" ; then
|
||||||
|
# Otherwise just take whatever comes first
|
||||||
|
MOZ_DIRECTX_SDK_REG_KEY=`reg query 'HKLM\Software\Microsoft\DirectX' //s | grep 'Microsoft DirectX SDK' | head -n 1`
|
||||||
|
fi
|
||||||
|
MOZ_DIRECTX_SDK_PATH=`reg query "$MOZ_DIRECTX_SDK_REG_KEY" //v InstallPath | grep REG_SZ | sed 's/.*\([[a-zA-Z]]\)\\:\\\\/\\1\\:\\\\/' | sed 's,\\\\,/,g'`
|
||||||
|
|
||||||
# Check that our DirectX SDK is acceptable.
|
if test -n "$MOZ_DIRECTX_SDK_PATH" &&
|
||||||
if test -n "$MOZ_DIRECTX_SDK_PATH"; then
|
test -f "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_D3D_CPU_SUFFIX/dxguid.lib ; then
|
||||||
if test -n "`echo $MOZ_DIRECTX_SDK_REG_KEY | grep 'February 2010'`" ; then
|
AC_MSG_RESULT([Found DirectX SDK via registry, using $MOZ_DIRECTX_SDK_PATH])
|
||||||
AC_MSG_RESULT([Found the February 2010 DirectX SDK, which is unacceptable to ANGLE.])
|
else
|
||||||
|
AC_MSG_RESULT([DirectX SDK not found.])
|
||||||
MOZ_DIRECTX_SDK_PATH=
|
MOZ_DIRECTX_SDK_PATH=
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
if test -n "$MOZ_DIRECTX_SDK_PATH"; then
|
# Check that our DirectX SDK is acceptable.
|
||||||
# Find a D3D compiler DLL in the DirectX SDK, if we didn't find one already.
|
if test -n "$MOZ_DIRECTX_SDK_PATH"; then
|
||||||
# Get the SDK numeric version (e.g. 43) by looking at the dependencies of d3dx9.lib
|
if test -n "`echo $MOZ_DIRECTX_SDK_REG_KEY | grep 'February 2010'`" ; then
|
||||||
MOZ_D3DX9_VERSION=`dumpbin //headers "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_D3D_CPU_SUFFIX/d3dx9.lib | egrep d3dx9_[[0-9]][[0-9]]\.dll | head -n1 | sed 's/.*\([[0-9]][[0-9]]\).*/\\1/g'`
|
AC_MSG_RESULT([Found the February 2010 DirectX SDK, which is unacceptable to ANGLE.])
|
||||||
|
MOZ_DIRECTX_SDK_PATH=
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if test -n "$MOZ_D3DX9_VERSION" ; then
|
if test -n "$MOZ_DIRECTX_SDK_PATH"; then
|
||||||
MOZ_D3DCOMPILER_XP_CAB=`find "$MOZ_DIRECTX_SDK_PATH"/Redist -name *D3DCompiler_${MOZ_D3DX9_VERSION}_${MOZ_D3D_CPU_SUFFIX}.cab | head -n1`
|
# Find a D3D compiler DLL in the DirectX SDK, if we didn't find one already.
|
||||||
|
# Get the SDK numeric version (e.g. 43) by looking at the dependencies of d3dx9.lib
|
||||||
|
MOZ_D3DX9_VERSION=`dumpbin //headers "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_D3D_CPU_SUFFIX/d3dx9.lib | egrep d3dx9_[[0-9]][[0-9]]\.dll | head -n1 | sed 's/.*\([[0-9]][[0-9]]\).*/\\1/g'`
|
||||||
|
|
||||||
if test -n "$MOZ_D3DCOMPILER_XP_CAB"; then
|
if test -n "$MOZ_D3DX9_VERSION" ; then
|
||||||
MOZ_D3DCOMPILER_XP_DLL=D3DCompiler_$MOZ_D3DX9_VERSION.dll
|
MOZ_D3DCOMPILER_XP_CAB=`find "$MOZ_DIRECTX_SDK_PATH"/Redist -name *D3DCompiler_${MOZ_D3DX9_VERSION}_${MOZ_D3D_CPU_SUFFIX}.cab | head -n1`
|
||||||
|
|
||||||
|
if test -n "$MOZ_D3DCOMPILER_XP_CAB"; then
|
||||||
|
MOZ_D3DCOMPILER_XP_DLL=D3DCompiler_$MOZ_D3DX9_VERSION.dll
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT([Couldn't find a CAB containing the D3D compiler DLL.])
|
||||||
|
AC_MSG_ERROR([DirectX SDK at "$MOZ_DIRECTX_SDK_PATH" appears broken.])
|
||||||
|
MOZ_DIRECTX_SDK_PATH=
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
AC_MSG_RESULT([Couldn't find a CAB containing the D3D compiler DLL.])
|
AC_MSG_RESULT([Couldn't determine the D3DX9 version for the DirectX SDK.])
|
||||||
AC_MSG_ERROR([DirectX SDK at "$MOZ_DIRECTX_SDK_PATH" appears broken.])
|
|
||||||
MOZ_DIRECTX_SDK_PATH=
|
MOZ_DIRECTX_SDK_PATH=
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
AC_MSG_RESULT([Couldn't determine the D3DX9 version for the DirectX SDK.])
|
AC_MSG_RESULT([Couldn't find an acceptable DirectX SDK for ANGLE, needed for d3dcompiler_43.])
|
||||||
MOZ_DIRECTX_SDK_PATH=
|
AC_MSG_RESULT([ Either ignore, install DirectX SDK (June 2010 version or newer), or reconfigure with --disable-webgl.])
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
AC_MSG_RESULT([Couldn't find an acceptable DirectX SDK for ANGLE, needed for d3dcompiler_43.])
|
|
||||||
AC_MSG_RESULT([ Either ignore, install DirectX SDK (June 2010 version or newer), or reconfigure with --disable-webgl.])
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
######################################
|
######################################
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
<html>
|
|
||||||
<body>
|
|
||||||
<q>
|
|
||||||
<form>
|
|
||||||
<fieldset>
|
|
||||||
<input>
|
|
||||||
<input>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
<p>
|
|
||||||
<div><span>
|
|
||||||
<img>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><map><map><map><map><map><map><map><map><map>
|
|
||||||
<map><map><map><area><area><area><area><area><area></map>
|
|
||||||
<hgroup>
|
|
||||||
<object>
|
|
||||||
<form dir="auto">
|
|
||||||
<keygen dir="auto">
|
|
||||||
</keygen>
|
|
||||||
</form>
|
|
||||||
<input>
|
|
||||||
<input>
|
|
||||||
</form>
|
|
||||||
<script>
|
|
||||||
function boom(){
|
|
||||||
location.reload()
|
|
||||||
}
|
|
||||||
setInterval('boom()', 2000)
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -9593,7 +9593,22 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
|
|||||||
// the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
|
// the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
|
||||||
// unlink
|
// unlink
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
mPreloadingImages.AppendObject(request);
|
mPreloadingImages.Put(uri, request.forget());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsDocument::ForgetImagePreload(nsIURI* aURI)
|
||||||
|
{
|
||||||
|
// Checking count is faster than hashing the URI in the common
|
||||||
|
// case of empty table.
|
||||||
|
if (mPreloadingImages.Count() != 0) {
|
||||||
|
nsCOMPtr<imgIRequest> req;
|
||||||
|
mPreloadingImages.Remove(aURI, getter_AddRefs(req));
|
||||||
|
if (req) {
|
||||||
|
// Make sure to cancel the request so imagelib knows it's gone.
|
||||||
|
req->CancelAndForgetObserver(NS_BINDING_ABORTED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1094,6 +1094,7 @@ public:
|
|||||||
virtual void MaybePreLoadImage(nsIURI* uri,
|
virtual void MaybePreLoadImage(nsIURI* uri,
|
||||||
const nsAString &aCrossOriginAttr,
|
const nsAString &aCrossOriginAttr,
|
||||||
ReferrerPolicy aReferrerPolicy) MOZ_OVERRIDE;
|
ReferrerPolicy aReferrerPolicy) MOZ_OVERRIDE;
|
||||||
|
virtual void ForgetImagePreload(nsIURI* aURI) MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual void PreloadStyle(nsIURI* uri, const nsAString& charset,
|
virtual void PreloadStyle(nsIURI* uri, const nsAString& charset,
|
||||||
const nsAString& aCrossOriginAttr,
|
const nsAString& aCrossOriginAttr,
|
||||||
@ -1742,8 +1743,11 @@ private:
|
|||||||
|
|
||||||
nsExternalResourceMap mExternalResourceMap;
|
nsExternalResourceMap mExternalResourceMap;
|
||||||
|
|
||||||
// All images in process of being preloaded
|
// All images in process of being preloaded. This is a hashtable so
|
||||||
nsCOMArray<imgIRequest> mPreloadingImages;
|
// we can remove them as the real image loads start; that way we
|
||||||
|
// make sure to not keep the image load going when no one cares
|
||||||
|
// about it anymore.
|
||||||
|
nsRefPtrHashtable<nsURIHashKey, imgIRequest> mPreloadingImages;
|
||||||
|
|
||||||
nsRefPtr<mozilla::dom::DOMImplementation> mDOMImplementation;
|
nsRefPtr<mozilla::dom::DOMImplementation> mDOMImplementation;
|
||||||
|
|
||||||
|
@ -146,8 +146,8 @@ struct FullScreenOptions {
|
|||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
#define NS_IDOCUMENT_IID \
|
#define NS_IDOCUMENT_IID \
|
||||||
{ 0x1f343423, 0x957c, 0x4da3, \
|
{ 0xf63d2f6e, 0xd1c1, 0x49b9, \
|
||||||
{ 0xaa, 0xa3, 0x07, 0x37, 0x54, 0x3e, 0x79, 0x2a } }
|
{ 0x88, 0x26, 0xd5, 0x9e, 0x5d, 0x72, 0x2a, 0x42 } }
|
||||||
|
|
||||||
// Enum for requesting a particular type of document when creating a doc
|
// Enum for requesting a particular type of document when creating a doc
|
||||||
enum DocumentFlavor {
|
enum DocumentFlavor {
|
||||||
@ -1922,6 +1922,12 @@ public:
|
|||||||
const nsAString& aCrossOriginAttr,
|
const nsAString& aCrossOriginAttr,
|
||||||
ReferrerPolicy aReferrerPolicy) = 0;
|
ReferrerPolicy aReferrerPolicy) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by images to forget an image preload when they start doing
|
||||||
|
* the real load.
|
||||||
|
*/
|
||||||
|
virtual void ForgetImagePreload(nsIURI* aURI) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by nsParser to preload style sheets. Can also be merged into the
|
* Called by nsParser to preload style sheets. Can also be merged into the
|
||||||
* parser if and when the parser is merged with libgklayout. aCrossOriginAttr
|
* parser if and when the parser is merged with libgklayout. aCrossOriginAttr
|
||||||
|
@ -893,6 +893,11 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
|
|||||||
getter_AddRefs(req),
|
getter_AddRefs(req),
|
||||||
policyType);
|
policyType);
|
||||||
|
|
||||||
|
// Tell the document to forget about the image preload, if any, for
|
||||||
|
// this URI, now that we might have another imgRequestProxy for it.
|
||||||
|
// That way if we get canceled later the image load won't continue.
|
||||||
|
aDocument->ForgetImagePreload(aNewURI);
|
||||||
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
TrackImage(req);
|
TrackImage(req);
|
||||||
ResetAnimationIfNeeded();
|
ResetAnimationIfNeeded();
|
||||||
|
@ -459,7 +459,7 @@ CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
|
if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
|
||||||
JSPROP_READONLY | JSPROP_PERMANENT)) {
|
JSPROP_READONLY)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2089,7 +2089,9 @@ ConstructJSImplementation(JSContext* aCx, const char* aContractId,
|
|||||||
nsresult rv;
|
nsresult rv;
|
||||||
nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId, &rv);
|
nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId, &rv);
|
||||||
if (!implISupports) {
|
if (!implISupports) {
|
||||||
NS_WARNING("Failed to get JS implementation for contract");
|
nsPrintfCString msg("Failed to get JS implementation for contract \"%s\"",
|
||||||
|
aContractId);
|
||||||
|
NS_WARNING(msg.get());
|
||||||
aRv.Throw(rv);
|
aRv.Throw(rv);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -11882,6 +11882,7 @@ class CGResolveSystemBinding(CGAbstractMethod):
|
|||||||
def definition_body(self):
|
def definition_body(self):
|
||||||
descriptors = self.config.getDescriptors(hasInterfaceObject=True,
|
descriptors = self.config.getDescriptors(hasInterfaceObject=True,
|
||||||
isExposedInSystemGlobals=True,
|
isExposedInSystemGlobals=True,
|
||||||
|
workers=False,
|
||||||
register=True,
|
register=True,
|
||||||
skipGen=False)
|
skipGen=False)
|
||||||
|
|
||||||
|
@ -1150,15 +1150,15 @@ IdlInterface.prototype.test_self = function()
|
|||||||
|
|
||||||
if (!this.is_callback()) {
|
if (!this.is_callback()) {
|
||||||
test(function() {
|
test(function() {
|
||||||
// This function tests WebIDL as of 2013-08-25.
|
// This function tests WebIDL as of 2014-10-25.
|
||||||
// http://dev.w3.org/2006/webapi/WebIDL/#es-interface-call
|
// https://heycam.github.io/webidl/#es-interface-call
|
||||||
|
|
||||||
assert_own_property(window, this.name,
|
assert_own_property(window, this.name,
|
||||||
"window does not have own property " + format_value(this.name));
|
"window does not have own property " + format_value(this.name));
|
||||||
|
|
||||||
// "Interface objects for non-callback interfaces MUST have a
|
// "Interface objects for non-callback interfaces MUST have a
|
||||||
// property named “length” with attributes { [[Writable]]: false,
|
// property named “length” with attributes { [[Writable]]: false,
|
||||||
// [[Enumerable]]: false, [[Configurable]]: false } whose value is
|
// [[Enumerable]]: false, [[Configurable]]: true } whose value is
|
||||||
// a Number."
|
// a Number."
|
||||||
assert_own_property(window[this.name], "length");
|
assert_own_property(window[this.name], "length");
|
||||||
var desc = Object.getOwnPropertyDescriptor(window[this.name], "length");
|
var desc = Object.getOwnPropertyDescriptor(window[this.name], "length");
|
||||||
@ -1166,7 +1166,7 @@ IdlInterface.prototype.test_self = function()
|
|||||||
assert_false("set" in desc, this.name + ".length has setter");
|
assert_false("set" in desc, this.name + ".length has setter");
|
||||||
assert_false(desc.writable, this.name + ".length is writable");
|
assert_false(desc.writable, this.name + ".length is writable");
|
||||||
assert_false(desc.enumerable, this.name + ".length is enumerable");
|
assert_false(desc.enumerable, this.name + ".length is enumerable");
|
||||||
assert_false(desc.configurable, this.name + ".length is configurable");
|
assert_true(desc.configurable, this.name + ".length is not configurable");
|
||||||
|
|
||||||
var constructors = this.extAttrs
|
var constructors = this.extAttrs
|
||||||
.filter(function(attr) { return attr.name == "Constructor"; });
|
.filter(function(attr) { return attr.name == "Constructor"; });
|
||||||
|
@ -119,3 +119,38 @@ nsSVGPolyElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
|
|||||||
aMarks->LastElement().angle = prevAngle;
|
aMarks->LastElement().angle = prevAngle;
|
||||||
aMarks->LastElement().type = nsSVGMark::eEnd;
|
aMarks->LastElement().type = nsSVGMark::eEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsSVGPolyElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
|
||||||
|
const Matrix& aTransform)
|
||||||
|
{
|
||||||
|
const SVGPointList &points = mPoints.GetAnimValue();
|
||||||
|
|
||||||
|
if (!points.Length()) {
|
||||||
|
// Rendering of the element is disabled
|
||||||
|
aBounds->SetEmpty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aStrokeWidth > 0) {
|
||||||
|
// We don't handle stroke-miterlimit etc. yet
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aTransform.IsRectilinear()) {
|
||||||
|
// We can avoid transforming each point and just transform the result.
|
||||||
|
// Important for large point lists.
|
||||||
|
Rect bounds(points[0], Size());
|
||||||
|
for (uint32_t i = 1; i < points.Length(); ++i) {
|
||||||
|
bounds.ExpandToEnclose(points[i]);
|
||||||
|
}
|
||||||
|
*aBounds = aTransform.TransformBounds(bounds);
|
||||||
|
} else {
|
||||||
|
*aBounds = Rect(aTransform * points[0], Size());
|
||||||
|
for (uint32_t i = 1; i < points.Length(); ++i) {
|
||||||
|
aBounds->ExpandToEnclose(aTransform * points[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@ public:
|
|||||||
virtual bool AttributeDefinesGeometry(const nsIAtom *aName) MOZ_OVERRIDE;
|
virtual bool AttributeDefinesGeometry(const nsIAtom *aName) MOZ_OVERRIDE;
|
||||||
virtual bool IsMarkable() MOZ_OVERRIDE { return true; }
|
virtual bool IsMarkable() MOZ_OVERRIDE { return true; }
|
||||||
virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) MOZ_OVERRIDE;
|
virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) MOZ_OVERRIDE;
|
||||||
|
virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
|
||||||
|
const Matrix& aTransform) MOZ_OVERRIDE;
|
||||||
|
|
||||||
// WebIDL
|
// WebIDL
|
||||||
already_AddRefed<mozilla::DOMSVGPointList> Points();
|
already_AddRefed<mozilla::DOMSVGPointList> Points();
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
[Constructor(optional USVString init = ""),
|
[Constructor(optional USVString init = ""),
|
||||||
Constructor(URLSearchParams init),
|
Constructor(URLSearchParams init),
|
||||||
Exposed=(Window,Worker)]
|
Exposed=(Window,Worker,System)]
|
||||||
interface URLSearchParams {
|
interface URLSearchParams {
|
||||||
void append(USVString name, USVString value);
|
void append(USVString name, USVString value);
|
||||||
void delete(USVString name);
|
void delete(USVString name);
|
||||||
|
@ -34,7 +34,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1086996
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1086996">Mozilla Bug 1086996</a>
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1086996">Mozilla Bug 1086996</a>
|
||||||
<div id="boundContent" style="-moz-binding: url(#mainBinding)"></div>
|
|
||||||
<div id="content" style="display: none">
|
<div id="content" style="display: none">
|
||||||
</div>
|
</div>
|
||||||
<pre id="test">
|
<pre id="test">
|
||||||
@ -56,5 +55,8 @@ function gotEvent() {
|
|||||||
]]>
|
]]>
|
||||||
</script>
|
</script>
|
||||||
</pre>
|
</pre>
|
||||||
|
<!-- This div needs to come after the <script> so we don't run the binding ctor
|
||||||
|
before the <script> has been parsed -->
|
||||||
|
<div id="boundContent" style="-moz-binding: url(#mainBinding)"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -177,6 +177,23 @@ struct BaseRect {
|
|||||||
*static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
|
*static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expands the rect to include the point
|
||||||
|
void ExpandToEnclose(const Point& aPoint)
|
||||||
|
{
|
||||||
|
if (aPoint.x < x) {
|
||||||
|
width = XMost() - aPoint.x;
|
||||||
|
x = aPoint.x;
|
||||||
|
} else if (aPoint.x > XMost()) {
|
||||||
|
width = aPoint.x - x;
|
||||||
|
}
|
||||||
|
if (aPoint.y < y) {
|
||||||
|
height = YMost() - aPoint.y;
|
||||||
|
y = aPoint.y;
|
||||||
|
} else if (aPoint.y > YMost()) {
|
||||||
|
height = aPoint.y - y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SetRect(T aX, T aY, T aWidth, T aHeight)
|
void SetRect(T aX, T aY, T aWidth, T aHeight)
|
||||||
{
|
{
|
||||||
x = aX; y = aY; width = aWidth; height = aHeight;
|
x = aX; y = aY; width = aWidth; height = aHeight;
|
||||||
|
@ -396,7 +396,7 @@ DWriteGlyphRunFromGlyphs(const GlyphBuffer &aGlyphs, ScaledFontDWrite *aFont, Au
|
|||||||
run->isSideways = FALSE;
|
run->isSideways = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TemporaryRef<ID2D1Geometry>
|
static inline TemporaryRef<ID2D1Geometry>
|
||||||
ConvertRectToGeometry(const D2D1_RECT_F& aRect)
|
ConvertRectToGeometry(const D2D1_RECT_F& aRect)
|
||||||
{
|
{
|
||||||
RefPtr<ID2D1RectangleGeometry> rectGeom;
|
RefPtr<ID2D1RectangleGeometry> rectGeom;
|
||||||
@ -404,7 +404,7 @@ ConvertRectToGeometry(const D2D1_RECT_F& aRect)
|
|||||||
return rectGeom.forget();
|
return rectGeom.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
static TemporaryRef<ID2D1Geometry>
|
static inline TemporaryRef<ID2D1Geometry>
|
||||||
GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
|
GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
|
||||||
{
|
{
|
||||||
RefPtr<ID2D1PathGeometry> tmpGeometry;
|
RefPtr<ID2D1PathGeometry> tmpGeometry;
|
||||||
@ -417,7 +417,7 @@ GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTrans
|
|||||||
return tmpGeometry.forget();
|
return tmpGeometry.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
static TemporaryRef<ID2D1Geometry>
|
static inline TemporaryRef<ID2D1Geometry>
|
||||||
IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB)
|
IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB)
|
||||||
{
|
{
|
||||||
RefPtr<ID2D1PathGeometry> pathGeom;
|
RefPtr<ID2D1PathGeometry> pathGeom;
|
||||||
@ -430,7 +430,7 @@ IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB)
|
|||||||
return pathGeom.forget();
|
return pathGeom.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
static TemporaryRef<ID2D1StrokeStyle>
|
static inline TemporaryRef<ID2D1StrokeStyle>
|
||||||
CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
|
CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
|
||||||
{
|
{
|
||||||
RefPtr<ID2D1StrokeStyle> style;
|
RefPtr<ID2D1StrokeStyle> style;
|
||||||
@ -510,7 +510,7 @@ CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
|
|||||||
// This creates a (partially) uploaded bitmap for a DataSourceSurface. It
|
// This creates a (partially) uploaded bitmap for a DataSourceSurface. It
|
||||||
// uploads the minimum requirement and possibly downscales. It adjusts the
|
// uploads the minimum requirement and possibly downscales. It adjusts the
|
||||||
// input Matrix to compensate.
|
// input Matrix to compensate.
|
||||||
static TemporaryRef<ID2D1Bitmap>
|
static inline TemporaryRef<ID2D1Bitmap>
|
||||||
CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform,
|
CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform,
|
||||||
const IntSize &aDestinationSize, ExtendMode aExtendMode,
|
const IntSize &aDestinationSize, ExtendMode aExtendMode,
|
||||||
Matrix &aSourceTransform, ID2D1RenderTarget *aRT,
|
Matrix &aSourceTransform, ID2D1RenderTarget *aRT,
|
||||||
|
@ -42,10 +42,9 @@ class APZTestData {
|
|||||||
friend struct APZTestDataToJSConverter;
|
friend struct APZTestDataToJSConverter;
|
||||||
public:
|
public:
|
||||||
void StartNewPaint(SequenceNumber aSequenceNumber) {
|
void StartNewPaint(SequenceNumber aSequenceNumber) {
|
||||||
|
// We should never get more than one paint with the same sequence number.
|
||||||
|
MOZ_ASSERT(mPaints.find(aSequenceNumber) == mPaints.end());
|
||||||
mPaints.insert(DataStore::value_type(aSequenceNumber, Bucket()));
|
mPaints.insert(DataStore::value_type(aSequenceNumber, Bucket()));
|
||||||
// TODO(botond): MOZ_ASSERT() that we didn't already have a paint with this
|
|
||||||
// sequence number once we get rid ofAPZCTreeManager::UpdatePanZoomControllerTree()
|
|
||||||
// calls for repeat transactions (bug 1007728).
|
|
||||||
}
|
}
|
||||||
void LogTestDataForPaint(SequenceNumber aSequenceNumber,
|
void LogTestDataForPaint(SequenceNumber aSequenceNumber,
|
||||||
ViewID aScrollId,
|
ViewID aScrollId,
|
||||||
@ -93,10 +92,9 @@ private:
|
|||||||
}
|
}
|
||||||
Bucket& bucket = bucketIterator->second;
|
Bucket& bucket = bucketIterator->second;
|
||||||
ScrollFrameData& scrollFrameData = bucket[aScrollId]; // create if doesn't exist
|
ScrollFrameData& scrollFrameData = bucket[aScrollId]; // create if doesn't exist
|
||||||
|
MOZ_ASSERT(scrollFrameData.find(aKey) == scrollFrameData.end()
|
||||||
|
|| scrollFrameData[aKey] == aValue);
|
||||||
scrollFrameData.insert(ScrollFrameData::value_type(aKey, aValue));
|
scrollFrameData.insert(ScrollFrameData::value_type(aKey, aValue));
|
||||||
// TODO(botond): MOZ_ASSERT() that we don't already have this key once we
|
|
||||||
// get rid of APZCTreeManager::UpdatePanZoomControllerTree() calls for
|
|
||||||
// repeat transactions (bug 1007728).
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <dwrite.h>
|
#include <dwrite.h>
|
||||||
|
|
||||||
static DWRITE_FONT_STRETCH
|
static inline DWRITE_FONT_STRETCH
|
||||||
DWriteFontStretchFromStretch(int16_t aStretch)
|
DWriteFontStretchFromStretch(int16_t aStretch)
|
||||||
{
|
{
|
||||||
switch (aStretch) {
|
switch (aStretch) {
|
||||||
@ -47,7 +47,7 @@ DWriteFontStretchFromStretch(int16_t aStretch)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int16_t
|
static inline int16_t
|
||||||
FontStretchFromDWriteStretch(DWRITE_FONT_STRETCH aStretch)
|
FontStretchFromDWriteStretch(DWRITE_FONT_STRETCH aStretch)
|
||||||
{
|
{
|
||||||
switch (aStretch) {
|
switch (aStretch) {
|
||||||
|
@ -1266,7 +1266,6 @@ CallTraceCallbackOnNonHeap(T *v, const TraceCallbacks &aCallbacks, const char *a
|
|||||||
MOZ_ASSERT(!IsInsideNursery(cell));
|
MOZ_ASSERT(!IsInsideNursery(cell));
|
||||||
JS::Heap<T> *asHeapT = reinterpret_cast<JS::Heap<T>*>(v);
|
JS::Heap<T> *asHeapT = reinterpret_cast<JS::Heap<T>*>(v);
|
||||||
aCallbacks.Trace(asHeapT, aName, aClosure);
|
aCallbacks.Trace(asHeapT, aName, aClosure);
|
||||||
MOZ_ASSERT(GCMethods<T>::asGCThingOrNull(*v) == cell);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace gc */
|
} /* namespace gc */
|
||||||
|
@ -559,10 +559,6 @@ struct Or {
|
|||||||
static inline T apply(T l, T r) { return l | r; }
|
static inline T apply(T l, T r) { return l | r; }
|
||||||
};
|
};
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct Scale {
|
|
||||||
static inline T apply(int32_t lane, T scalar, T x) { return scalar * x; }
|
|
||||||
};
|
|
||||||
template<typename T>
|
|
||||||
struct WithX {
|
struct WithX {
|
||||||
static inline T apply(int32_t lane, T scalar, T x) { return lane == 0 ? scalar : x; }
|
static inline T apply(int32_t lane, T scalar, T x) { return lane == 0 ? scalar : x; }
|
||||||
};
|
};
|
||||||
@ -578,22 +574,6 @@ template<typename T>
|
|||||||
struct WithW {
|
struct WithW {
|
||||||
static inline T apply(int32_t lane, T scalar, T x) { return lane == 3 ? scalar : x; }
|
static inline T apply(int32_t lane, T scalar, T x) { return lane == 3 ? scalar : x; }
|
||||||
};
|
};
|
||||||
template<typename T>
|
|
||||||
struct WithFlagX {
|
|
||||||
static inline T apply(T l, T f, T x) { return l == 0 ? (f ? 0xFFFFFFFF : 0x0) : x; }
|
|
||||||
};
|
|
||||||
template<typename T>
|
|
||||||
struct WithFlagY {
|
|
||||||
static inline T apply(T l, T f, T x) { return l == 1 ? (f ? 0xFFFFFFFF : 0x0) : x; }
|
|
||||||
};
|
|
||||||
template<typename T>
|
|
||||||
struct WithFlagZ {
|
|
||||||
static inline T apply(T l, T f, T x) { return l == 2 ? (f ? 0xFFFFFFFF : 0x0) : x; }
|
|
||||||
};
|
|
||||||
template<typename T>
|
|
||||||
struct WithFlagW {
|
|
||||||
static inline T apply(T l, T f, T x) { return l == 3 ? (f ? 0xFFFFFFFF : 0x0) : x; }
|
|
||||||
};
|
|
||||||
struct ShiftLeft {
|
struct ShiftLeft {
|
||||||
static inline int32_t apply(int32_t v, int32_t bits) { return v << bits; }
|
static inline int32_t apply(int32_t v, int32_t bits) { return v << bits; }
|
||||||
};
|
};
|
||||||
|
@ -18,9 +18,6 @@
|
|||||||
* https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js
|
* https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define FLOAT32X4_NULLARY_FUNCTION_LIST(V) \
|
|
||||||
V(zero, (FuncZero<Float32x4>), 0, 0)
|
|
||||||
|
|
||||||
#define FLOAT32X4_UNARY_FUNCTION_LIST(V) \
|
#define FLOAT32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
V(abs, (UnaryFunc<Float32x4, Abs, Float32x4>), 1, 0) \
|
V(abs, (UnaryFunc<Float32x4, Abs, Float32x4>), 1, 0) \
|
||||||
V(fromInt32x4, (FuncConvert<Int32x4, Float32x4> ), 1, 0) \
|
V(fromInt32x4, (FuncConvert<Int32x4, Float32x4> ), 1, 0) \
|
||||||
@ -52,7 +49,6 @@
|
|||||||
V(mul, (BinaryFunc<Float32x4, Mul, Float32x4>), 2, 0) \
|
V(mul, (BinaryFunc<Float32x4, Mul, Float32x4>), 2, 0) \
|
||||||
V(notEqual, (CompareFunc<Float32x4, NotEqual>), 2, 0) \
|
V(notEqual, (CompareFunc<Float32x4, NotEqual>), 2, 0) \
|
||||||
V(or, (CoercedBinaryFunc<Float32x4, Int32x4, Or, Float32x4>), 2, 0) \
|
V(or, (CoercedBinaryFunc<Float32x4, Int32x4, Or, Float32x4>), 2, 0) \
|
||||||
V(scale, (FuncWith<Float32x4, Scale>), 2, 0) \
|
|
||||||
V(store, (Store<Float32x4, 4>), 3, 0) \
|
V(store, (Store<Float32x4, 4>), 3, 0) \
|
||||||
V(storeXYZ, (Store<Float32x4, 3>), 3, 0) \
|
V(storeXYZ, (Store<Float32x4, 3>), 3, 0) \
|
||||||
V(storeXY, (Store<Float32x4, 2>), 3, 0) \
|
V(storeXY, (Store<Float32x4, 2>), 3, 0) \
|
||||||
@ -73,15 +69,11 @@
|
|||||||
V(shuffle, Shuffle<Float32x4>, 3, 0)
|
V(shuffle, Shuffle<Float32x4>, 3, 0)
|
||||||
|
|
||||||
#define FLOAT32X4_FUNCTION_LIST(V) \
|
#define FLOAT32X4_FUNCTION_LIST(V) \
|
||||||
FLOAT32X4_NULLARY_FUNCTION_LIST(V) \
|
|
||||||
FLOAT32X4_UNARY_FUNCTION_LIST(V) \
|
FLOAT32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
FLOAT32X4_BINARY_FUNCTION_LIST(V) \
|
FLOAT32X4_BINARY_FUNCTION_LIST(V) \
|
||||||
FLOAT32X4_TERNARY_FUNCTION_LIST(V) \
|
FLOAT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||||
FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)
|
FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)
|
||||||
|
|
||||||
#define INT32X4_NULLARY_FUNCTION_LIST(V) \
|
|
||||||
V(zero, (FuncZero<Int32x4>), 0, 0)
|
|
||||||
|
|
||||||
#define INT32X4_UNARY_FUNCTION_LIST(V) \
|
#define INT32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
V(fromFloat32x4, (FuncConvert<Float32x4, Int32x4>), 1, 0) \
|
V(fromFloat32x4, (FuncConvert<Float32x4, Int32x4>), 1, 0) \
|
||||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1, 0) \
|
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1, 0) \
|
||||||
@ -94,12 +86,15 @@
|
|||||||
V(and, (BinaryFunc<Int32x4, And, Int32x4>), 2, 0) \
|
V(and, (BinaryFunc<Int32x4, And, Int32x4>), 2, 0) \
|
||||||
V(equal, (CompareFunc<Int32x4, Equal>), 2, 0) \
|
V(equal, (CompareFunc<Int32x4, Equal>), 2, 0) \
|
||||||
V(greaterThan, (CompareFunc<Int32x4, GreaterThan>), 2, 0) \
|
V(greaterThan, (CompareFunc<Int32x4, GreaterThan>), 2, 0) \
|
||||||
|
V(greaterThanOrEqual, (CompareFunc<Int32x4, GreaterThanOrEqual>), 2, 0) \
|
||||||
V(lessThan, (CompareFunc<Int32x4, LessThan>), 2, 0) \
|
V(lessThan, (CompareFunc<Int32x4, LessThan>), 2, 0) \
|
||||||
|
V(lessThanOrEqual, (CompareFunc<Int32x4, LessThanOrEqual>), 2, 0) \
|
||||||
V(load, (Load<Int32x4, 4>), 2, 0) \
|
V(load, (Load<Int32x4, 4>), 2, 0) \
|
||||||
V(loadXYZ, (Load<Int32x4, 3>), 2, 0) \
|
V(loadXYZ, (Load<Int32x4, 3>), 2, 0) \
|
||||||
V(loadXY, (Load<Int32x4, 2>), 2, 0) \
|
V(loadXY, (Load<Int32x4, 2>), 2, 0) \
|
||||||
V(loadX, (Load<Int32x4, 1>), 2, 0) \
|
V(loadX, (Load<Int32x4, 1>), 2, 0) \
|
||||||
V(mul, (BinaryFunc<Int32x4, Mul, Int32x4>), 2, 0) \
|
V(mul, (BinaryFunc<Int32x4, Mul, Int32x4>), 2, 0) \
|
||||||
|
V(notEqual, (CompareFunc<Int32x4, NotEqual>), 2, 0) \
|
||||||
V(or, (BinaryFunc<Int32x4, Or, Int32x4>), 2, 0) \
|
V(or, (BinaryFunc<Int32x4, Or, Int32x4>), 2, 0) \
|
||||||
V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2, 0) \
|
V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2, 0) \
|
||||||
V(shiftLeft, (Int32x4BinaryScalar<ShiftLeft>), 2, 0) \
|
V(shiftLeft, (Int32x4BinaryScalar<ShiftLeft>), 2, 0) \
|
||||||
@ -109,10 +104,6 @@
|
|||||||
V(storeXYZ, (Store<Int32x4, 3>), 3, 0) \
|
V(storeXYZ, (Store<Int32x4, 3>), 3, 0) \
|
||||||
V(storeXY, (Store<Int32x4, 2>), 3, 0) \
|
V(storeXY, (Store<Int32x4, 2>), 3, 0) \
|
||||||
V(storeX, (Store<Int32x4, 1>), 3, 0) \
|
V(storeX, (Store<Int32x4, 1>), 3, 0) \
|
||||||
V(withFlagX, (FuncWith<Int32x4, WithFlagX>), 2, 0) \
|
|
||||||
V(withFlagY, (FuncWith<Int32x4, WithFlagY>), 2, 0) \
|
|
||||||
V(withFlagZ, (FuncWith<Int32x4, WithFlagZ>), 2, 0) \
|
|
||||||
V(withFlagW, (FuncWith<Int32x4, WithFlagW>), 2, 0) \
|
|
||||||
V(withX, (FuncWith<Int32x4, WithX>), 2, 0) \
|
V(withX, (FuncWith<Int32x4, WithX>), 2, 0) \
|
||||||
V(withY, (FuncWith<Int32x4, WithY>), 2, 0) \
|
V(withY, (FuncWith<Int32x4, WithY>), 2, 0) \
|
||||||
V(withZ, (FuncWith<Int32x4, WithZ>), 2, 0) \
|
V(withZ, (FuncWith<Int32x4, WithZ>), 2, 0) \
|
||||||
@ -130,7 +121,6 @@
|
|||||||
V(shuffle, Shuffle<Int32x4>, 3, 0)
|
V(shuffle, Shuffle<Int32x4>, 3, 0)
|
||||||
|
|
||||||
#define INT32X4_FUNCTION_LIST(V) \
|
#define INT32X4_FUNCTION_LIST(V) \
|
||||||
INT32X4_NULLARY_FUNCTION_LIST(V) \
|
|
||||||
INT32X4_UNARY_FUNCTION_LIST(V) \
|
INT32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
INT32X4_BINARY_FUNCTION_LIST(V) \
|
INT32X4_BINARY_FUNCTION_LIST(V) \
|
||||||
INT32X4_TERNARY_FUNCTION_LIST(V) \
|
INT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||||
@ -155,16 +145,16 @@
|
|||||||
_(max) \
|
_(max) \
|
||||||
_(min) \
|
_(min) \
|
||||||
_(maxNum) \
|
_(maxNum) \
|
||||||
_(minNum) \
|
_(minNum)
|
||||||
_(lessThanOrEqual) \
|
|
||||||
_(notEqual) \
|
|
||||||
_(greaterThanOrEqual)
|
|
||||||
#define FOREACH_COMMONX4_SIMD_OP(_) \
|
#define FOREACH_COMMONX4_SIMD_OP(_) \
|
||||||
_(add) \
|
_(add) \
|
||||||
_(sub) \
|
_(sub) \
|
||||||
_(lessThan) \
|
_(lessThan) \
|
||||||
|
_(lessThanOrEqual) \
|
||||||
_(equal) \
|
_(equal) \
|
||||||
|
_(notEqual) \
|
||||||
_(greaterThan) \
|
_(greaterThan) \
|
||||||
|
_(greaterThanOrEqual) \
|
||||||
_(and) \
|
_(and) \
|
||||||
_(or) \
|
_(or) \
|
||||||
_(xor) \
|
_(xor) \
|
||||||
|
@ -1770,7 +1770,7 @@ ia64*-hpux*)
|
|||||||
if test "$CPU_ARCH" = "x86"; then
|
if test "$CPU_ARCH" = "x86"; then
|
||||||
WIN32_SUBSYSTEM_VERSION=5.01
|
WIN32_SUBSYSTEM_VERSION=5.01
|
||||||
else
|
else
|
||||||
WIN32_SUBSYSTEM_VERSION=5.02
|
WIN32_SUBSYSTEM_VERSION=6.01
|
||||||
fi
|
fi
|
||||||
WIN32_CONSOLE_EXE_LDFLAGS=-SUBSYSTEM:CONSOLE,$WIN32_SUBSYSTEM_VERSION
|
WIN32_CONSOLE_EXE_LDFLAGS=-SUBSYSTEM:CONSOLE,$WIN32_SUBSYSTEM_VERSION
|
||||||
WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
|
WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
|
||||||
|
@ -611,6 +611,7 @@ class GCRuntime
|
|||||||
void updateAllCellPointersSerial(MovingTracer *trc, ArenasToUpdate &source);
|
void updateAllCellPointersSerial(MovingTracer *trc, ArenasToUpdate &source);
|
||||||
void updatePointersToRelocatedCells();
|
void updatePointersToRelocatedCells();
|
||||||
void releaseRelocatedArenas(ArenaHeader *relocatedList);
|
void releaseRelocatedArenas(ArenaHeader *relocatedList);
|
||||||
|
void releaseRelocatedArenasWithoutUnlocking(ArenaHeader *relocatedList, const AutoLockGC& lock);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void protectRelocatedArenas(ArenaHeader *relocatedList);
|
void protectRelocatedArenas(ArenaHeader *relocatedList);
|
||||||
void unprotectRelocatedArenas(ArenaHeader *relocatedList);
|
void unprotectRelocatedArenas(ArenaHeader *relocatedList);
|
||||||
|
@ -590,26 +590,38 @@ CheckI4(WWI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 2, 3, 42]);
|
|||||||
// yields all bits set to 0 (i.e 0).
|
// yields all bits set to 0 (i.e 0).
|
||||||
const T = -1;
|
const T = -1;
|
||||||
const F = 0;
|
const F = 0;
|
||||||
assertAsmTypeFail('glob', USE_ASM + I32 + "var lt=i4.lessThanOrEqual; function f() {} return f");
|
|
||||||
assertAsmTypeFail('glob', USE_ASM + I32 + "var ge=i4.greaterThanOrEqual; function f() {} return f");
|
|
||||||
assertAsmTypeFail('glob', USE_ASM + I32 + "var ne=i4.notEqual; function f() {} return f");
|
|
||||||
|
|
||||||
|
const EQI32 = 'var eq = i4.equal';
|
||||||
|
const NEI32 = 'var ne = i4.notEqual';
|
||||||
const LTI32 = 'var lt = i4.lessThan;';
|
const LTI32 = 'var lt = i4.lessThan;';
|
||||||
|
const LEI32 = 'var le = i4.lessThanOrEqual';
|
||||||
const GTI32 = 'var gt = i4.greaterThan;';
|
const GTI32 = 'var gt = i4.greaterThan;';
|
||||||
const EQI32 = 'var eq = i4.equal;';
|
const GEI32 = 'var ge = i4.greaterThanOrEqual';
|
||||||
|
|
||||||
CheckI4(LTI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=lt(x,y)', [F, F, F, F]);
|
|
||||||
CheckI4(LTI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=lt(x,y)', [T, T, T, T]);
|
|
||||||
CheckI4(LTI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=lt(x,y)', [F, T, T, F]);
|
|
||||||
|
|
||||||
CheckI4(EQI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=eq(x,y)', [F, F, F, F]);
|
CheckI4(EQI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=eq(x,y)', [F, F, F, F]);
|
||||||
CheckI4(EQI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=eq(x,y)', [F, F, F, F]);
|
CheckI4(EQI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=eq(x,y)', [F, F, F, F]);
|
||||||
CheckI4(EQI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=eq(x,y)', [T, F, F, F]);
|
CheckI4(EQI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=eq(x,y)', [T, F, F, F]);
|
||||||
|
|
||||||
|
CheckI4(NEI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=ne(x,y)', [T, T, T, T]);
|
||||||
|
CheckI4(NEI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=ne(x,y)', [T, T, T, T]);
|
||||||
|
CheckI4(NEI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=ne(x,y)', [F, T, T, T]);
|
||||||
|
|
||||||
|
CheckI4(LTI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=lt(x,y)', [F, F, F, F]);
|
||||||
|
CheckI4(LTI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=lt(x,y)', [T, T, T, T]);
|
||||||
|
CheckI4(LTI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=lt(x,y)', [F, T, T, F]);
|
||||||
|
|
||||||
|
CheckI4(LEI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=le(x,y)', [F, F, F, F]);
|
||||||
|
CheckI4(LEI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=le(x,y)', [T, T, T, T]);
|
||||||
|
CheckI4(LEI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=le(x,y)', [T, T, T, F]);
|
||||||
|
|
||||||
CheckI4(GTI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=gt(x,y)', [T, T, T, T]);
|
CheckI4(GTI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=gt(x,y)', [T, T, T, T]);
|
||||||
CheckI4(GTI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=gt(x,y)', [F, F, F, F]);
|
CheckI4(GTI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=gt(x,y)', [F, F, F, F]);
|
||||||
CheckI4(GTI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=gt(x,y)', [F, F, F, T]);
|
CheckI4(GTI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=gt(x,y)', [F, F, F, T]);
|
||||||
|
|
||||||
|
CheckI4(GEI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=ge(x,y)', [T, T, T, T]);
|
||||||
|
CheckI4(GEI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=ge(x,y)', [F, F, F, F]);
|
||||||
|
CheckI4(GEI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=ge(x,y)', [T, F, F, T]);
|
||||||
|
|
||||||
const LTF32 = 'var lt=f4.lessThan;';
|
const LTF32 = 'var lt=f4.lessThan;';
|
||||||
const LEF32 = 'var le=f4.lessThanOrEqual;';
|
const LEF32 = 'var le=f4.lessThanOrEqual;';
|
||||||
const GTF32 = 'var gt=f4.greaterThan;';
|
const GTF32 = 'var gt=f4.greaterThan;';
|
||||||
|
19
js/src/jit-test/tests/ion/sink-in-recovered-object.js
Normal file
19
js/src/jit-test/tests/ion/sink-in-recovered-object.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
setJitCompilerOption("ion.warmup.trigger", 30);
|
||||||
|
|
||||||
|
var arr = [];
|
||||||
|
function f (cond, a) {
|
||||||
|
var obj = { a: 0 };
|
||||||
|
var x = 2 * a + 1;
|
||||||
|
if (cond) {
|
||||||
|
obj.a = x;
|
||||||
|
arr.push(obj.a);
|
||||||
|
obj.a = 1;
|
||||||
|
} else {
|
||||||
|
obj.a = 1;
|
||||||
|
}
|
||||||
|
return obj.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < 100; i++) {
|
||||||
|
assertEq(f(i % 2, i), 1);
|
||||||
|
}
|
@ -309,11 +309,15 @@ BaselineCompiler::emitInitializeLocals(size_t n, const Value &v)
|
|||||||
bool
|
bool
|
||||||
BaselineCompiler::emitPrologue()
|
BaselineCompiler::emitPrologue()
|
||||||
{
|
{
|
||||||
|
#ifdef JS_USE_LINK_REGISTER
|
||||||
|
// Push link register from generateEnterJIT()'s BLR.
|
||||||
|
masm.pushReturnAddress();
|
||||||
|
masm.checkStackAlignment();
|
||||||
|
#endif
|
||||||
masm.push(BaselineFrameReg);
|
masm.push(BaselineFrameReg);
|
||||||
masm.mov(BaselineStackReg, BaselineFrameReg);
|
masm.mov(BaselineStackReg, BaselineFrameReg);
|
||||||
|
|
||||||
masm.subPtr(Imm32(BaselineFrame::Size()), BaselineStackReg);
|
masm.subPtr(Imm32(BaselineFrame::Size()), BaselineStackReg);
|
||||||
masm.checkStackAlignment();
|
|
||||||
|
|
||||||
// Initialize BaselineFrame. For eval scripts, the scope chain
|
// Initialize BaselineFrame. For eval scripts, the scope chain
|
||||||
// is passed in R1, so we have to be careful not to clobber
|
// is passed in R1, so we have to be careful not to clobber
|
||||||
@ -3450,7 +3454,7 @@ typedef bool (*InterpretResumeFn)(JSContext *, HandleObject, HandleValue, Handle
|
|||||||
static const VMFunction InterpretResumeInfo = FunctionInfo<InterpretResumeFn>(jit::InterpretResume);
|
static const VMFunction InterpretResumeInfo = FunctionInfo<InterpretResumeFn>(jit::InterpretResume);
|
||||||
|
|
||||||
typedef bool (*GeneratorThrowFn)(JSContext *, BaselineFrame *, HandleObject, HandleValue, uint32_t);
|
typedef bool (*GeneratorThrowFn)(JSContext *, BaselineFrame *, HandleObject, HandleValue, uint32_t);
|
||||||
static const VMFunction GeneratorThrowInfo = FunctionInfo<GeneratorThrowFn>(jit::GeneratorThrowOrClose);
|
static const VMFunction GeneratorThrowInfo = FunctionInfo<GeneratorThrowFn>(jit::GeneratorThrowOrClose, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BaselineCompiler::emit_JSOP_RESUME()
|
BaselineCompiler::emit_JSOP_RESUME()
|
||||||
|
@ -660,6 +660,7 @@ ICStubCompiler::tailCallVM(const VMFunction &fun, MacroAssembler &masm)
|
|||||||
if (!code)
|
if (!code)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
MOZ_ASSERT(fun.expectTailCall == TailCall);
|
||||||
uint32_t argSize = fun.explicitStackSlots() * sizeof(void *);
|
uint32_t argSize = fun.explicitStackSlots() * sizeof(void *);
|
||||||
EmitTailCallVM(code, masm, argSize);
|
EmitTailCallVM(code, masm, argSize);
|
||||||
return true;
|
return true;
|
||||||
@ -672,6 +673,7 @@ ICStubCompiler::callVM(const VMFunction &fun, MacroAssembler &masm)
|
|||||||
if (!code)
|
if (!code)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
MOZ_ASSERT(fun.expectTailCall == NonTailCall);
|
||||||
EmitCallVM(code, masm);
|
EmitCallVM(code, masm);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1120,7 +1122,7 @@ DoProfilerFallback(JSContext *cx, BaselineFrame *frame, ICProfiler_Fallback *stu
|
|||||||
|
|
||||||
typedef bool (*DoProfilerFallbackFn)(JSContext *, BaselineFrame *frame, ICProfiler_Fallback *);
|
typedef bool (*DoProfilerFallbackFn)(JSContext *, BaselineFrame *frame, ICProfiler_Fallback *);
|
||||||
static const VMFunction DoProfilerFallbackInfo =
|
static const VMFunction DoProfilerFallbackInfo =
|
||||||
FunctionInfo<DoProfilerFallbackFn>(DoProfilerFallback);
|
FunctionInfo<DoProfilerFallbackFn>(DoProfilerFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICProfiler_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICProfiler_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -1322,7 +1324,7 @@ DoTypeMonitorFallback(JSContext *cx, BaselineFrame *frame, ICTypeMonitor_Fallbac
|
|||||||
typedef bool (*DoTypeMonitorFallbackFn)(JSContext *, BaselineFrame *, ICTypeMonitor_Fallback *,
|
typedef bool (*DoTypeMonitorFallbackFn)(JSContext *, BaselineFrame *, ICTypeMonitor_Fallback *,
|
||||||
HandleValue, MutableHandleValue);
|
HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoTypeMonitorFallbackInfo =
|
static const VMFunction DoTypeMonitorFallbackInfo =
|
||||||
FunctionInfo<DoTypeMonitorFallbackFn>(DoTypeMonitorFallback);
|
FunctionInfo<DoTypeMonitorFallbackFn>(DoTypeMonitorFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICTypeMonitor_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICTypeMonitor_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -1553,7 +1555,7 @@ DoTypeUpdateFallback(JSContext *cx, BaselineFrame *frame, ICUpdatedStub *stub, H
|
|||||||
typedef bool (*DoTypeUpdateFallbackFn)(JSContext *, BaselineFrame *, ICUpdatedStub *, HandleValue,
|
typedef bool (*DoTypeUpdateFallbackFn)(JSContext *, BaselineFrame *, ICUpdatedStub *, HandleValue,
|
||||||
HandleValue);
|
HandleValue);
|
||||||
const VMFunction DoTypeUpdateFallbackInfo =
|
const VMFunction DoTypeUpdateFallbackInfo =
|
||||||
FunctionInfo<DoTypeUpdateFallbackFn>(DoTypeUpdateFallback);
|
FunctionInfo<DoTypeUpdateFallbackFn>(DoTypeUpdateFallback, NonTailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICTypeUpdate_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICTypeUpdate_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -1696,7 +1698,7 @@ DoThisFallback(JSContext *cx, ICThis_Fallback *stub, HandleValue thisv, MutableH
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef bool (*DoThisFallbackFn)(JSContext *, ICThis_Fallback *, HandleValue, MutableHandleValue);
|
typedef bool (*DoThisFallbackFn)(JSContext *, ICThis_Fallback *, HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoThisFallbackInfo = FunctionInfo<DoThisFallbackFn>(DoThisFallback);
|
static const VMFunction DoThisFallbackInfo = FunctionInfo<DoThisFallbackFn>(DoThisFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICThis_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICThis_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -1732,7 +1734,7 @@ DoNewArray(JSContext *cx, ICNewArray_Fallback *stub, uint32_t length,
|
|||||||
|
|
||||||
typedef bool(*DoNewArrayFn)(JSContext *, ICNewArray_Fallback *, uint32_t, HandleTypeObject,
|
typedef bool(*DoNewArrayFn)(JSContext *, ICNewArray_Fallback *, uint32_t, HandleTypeObject,
|
||||||
MutableHandleValue);
|
MutableHandleValue);
|
||||||
static const VMFunction DoNewArrayInfo = FunctionInfo<DoNewArrayFn>(DoNewArray);
|
static const VMFunction DoNewArrayInfo = FunctionInfo<DoNewArrayFn>(DoNewArray, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICNewArray_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICNewArray_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -1765,7 +1767,7 @@ DoNewObject(JSContext *cx, ICNewObject_Fallback *stub, MutableHandleValue res)
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef bool(*DoNewObjectFn)(JSContext *, ICNewObject_Fallback *, MutableHandleValue);
|
typedef bool(*DoNewObjectFn)(JSContext *, ICNewObject_Fallback *, MutableHandleValue);
|
||||||
static const VMFunction DoNewObjectInfo = FunctionInfo<DoNewObjectFn>(DoNewObject);
|
static const VMFunction DoNewObjectInfo = FunctionInfo<DoNewObjectFn>(DoNewObject, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICNewObject_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICNewObject_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -1977,7 +1979,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub_
|
|||||||
typedef bool (*DoCompareFallbackFn)(JSContext *, BaselineFrame *, ICCompare_Fallback *,
|
typedef bool (*DoCompareFallbackFn)(JSContext *, BaselineFrame *, ICCompare_Fallback *,
|
||||||
HandleValue, HandleValue, MutableHandleValue);
|
HandleValue, HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoCompareFallbackInfo =
|
static const VMFunction DoCompareFallbackInfo =
|
||||||
FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, PopValues(2));
|
FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, TailCall, PopValues(2));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICCompare_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -2312,7 +2314,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H
|
|||||||
|
|
||||||
typedef bool (*pf)(JSContext *, BaselineFrame *, ICToBool_Fallback *, HandleValue,
|
typedef bool (*pf)(JSContext *, BaselineFrame *, ICToBool_Fallback *, HandleValue,
|
||||||
MutableHandleValue);
|
MutableHandleValue);
|
||||||
static const VMFunction fun = FunctionInfo<pf>(DoToBoolFallback);
|
static const VMFunction fun = FunctionInfo<pf>(DoToBoolFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICToBool_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICToBool_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -2479,7 +2481,7 @@ DoToNumberFallback(JSContext *cx, ICToNumber_Fallback *stub, HandleValue arg, Mu
|
|||||||
|
|
||||||
typedef bool (*DoToNumberFallbackFn)(JSContext *, ICToNumber_Fallback *, HandleValue, MutableHandleValue);
|
typedef bool (*DoToNumberFallbackFn)(JSContext *, ICToNumber_Fallback *, HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoToNumberFallbackInfo =
|
static const VMFunction DoToNumberFallbackInfo =
|
||||||
FunctionInfo<DoToNumberFallbackFn>(DoToNumberFallback, PopValues(1));
|
FunctionInfo<DoToNumberFallbackFn>(DoToNumberFallback, TailCall, PopValues(1));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -2722,7 +2724,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac
|
|||||||
typedef bool (*DoBinaryArithFallbackFn)(JSContext *, BaselineFrame *, ICBinaryArith_Fallback *,
|
typedef bool (*DoBinaryArithFallbackFn)(JSContext *, BaselineFrame *, ICBinaryArith_Fallback *,
|
||||||
HandleValue, HandleValue, MutableHandleValue);
|
HandleValue, HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoBinaryArithFallbackInfo =
|
static const VMFunction DoBinaryArithFallbackInfo =
|
||||||
FunctionInfo<DoBinaryArithFallbackFn>(DoBinaryArithFallback, PopValues(2));
|
FunctionInfo<DoBinaryArithFallbackFn>(DoBinaryArithFallback, TailCall, PopValues(2));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICBinaryArith_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICBinaryArith_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -2768,7 +2770,7 @@ DoConcatStrings(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleVa
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef bool (*DoConcatStringsFn)(JSContext *, HandleValue, HandleValue, MutableHandleValue);
|
typedef bool (*DoConcatStringsFn)(JSContext *, HandleValue, HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoConcatStringsInfo = FunctionInfo<DoConcatStringsFn>(DoConcatStrings);
|
static const VMFunction DoConcatStringsInfo = FunctionInfo<DoConcatStringsFn>(DoConcatStrings, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICBinaryArith_StringConcat::Compiler::generateStubCode(MacroAssembler &masm)
|
ICBinaryArith_StringConcat::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -2845,7 +2847,7 @@ DoConcatStringObject(JSContext *cx, bool lhsIsString, HandleValue lhs, HandleVal
|
|||||||
typedef bool (*DoConcatStringObjectFn)(JSContext *, bool lhsIsString, HandleValue, HandleValue,
|
typedef bool (*DoConcatStringObjectFn)(JSContext *, bool lhsIsString, HandleValue, HandleValue,
|
||||||
MutableHandleValue);
|
MutableHandleValue);
|
||||||
static const VMFunction DoConcatStringObjectInfo =
|
static const VMFunction DoConcatStringObjectInfo =
|
||||||
FunctionInfo<DoConcatStringObjectFn>(DoConcatStringObject, PopValues(2));
|
FunctionInfo<DoConcatStringObjectFn>(DoConcatStringObject, TailCall, PopValues(2));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICBinaryArith_StringObjectConcat::Compiler::generateStubCode(MacroAssembler &masm)
|
ICBinaryArith_StringObjectConcat::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -3132,7 +3134,7 @@ DoUnaryArithFallback(JSContext *cx, BaselineFrame *frame, ICUnaryArith_Fallback
|
|||||||
typedef bool (*DoUnaryArithFallbackFn)(JSContext *, BaselineFrame *, ICUnaryArith_Fallback *,
|
typedef bool (*DoUnaryArithFallbackFn)(JSContext *, BaselineFrame *, ICUnaryArith_Fallback *,
|
||||||
HandleValue, MutableHandleValue);
|
HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoUnaryArithFallbackInfo =
|
static const VMFunction DoUnaryArithFallbackInfo =
|
||||||
FunctionInfo<DoUnaryArithFallbackFn>(DoUnaryArithFallback, PopValues(1));
|
FunctionInfo<DoUnaryArithFallbackFn>(DoUnaryArithFallback, TailCall, PopValues(1));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -4087,7 +4089,7 @@ DoGetElemFallback(JSContext *cx, BaselineFrame *frame, ICGetElem_Fallback *stub_
|
|||||||
typedef bool (*DoGetElemFallbackFn)(JSContext *, BaselineFrame *, ICGetElem_Fallback *,
|
typedef bool (*DoGetElemFallbackFn)(JSContext *, BaselineFrame *, ICGetElem_Fallback *,
|
||||||
HandleValue, HandleValue, MutableHandleValue);
|
HandleValue, HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoGetElemFallbackInfo =
|
static const VMFunction DoGetElemFallbackInfo =
|
||||||
FunctionInfo<DoGetElemFallbackFn>(DoGetElemFallback, PopValues(2));
|
FunctionInfo<DoGetElemFallbackFn>(DoGetElemFallback, TailCall, PopValues(2));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICGetElem_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICGetElem_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -5150,7 +5152,7 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub_
|
|||||||
typedef bool (*DoSetElemFallbackFn)(JSContext *, BaselineFrame *, ICSetElem_Fallback *, Value *,
|
typedef bool (*DoSetElemFallbackFn)(JSContext *, BaselineFrame *, ICSetElem_Fallback *, Value *,
|
||||||
HandleValue, HandleValue, HandleValue);
|
HandleValue, HandleValue, HandleValue);
|
||||||
static const VMFunction DoSetElemFallbackInfo =
|
static const VMFunction DoSetElemFallbackInfo =
|
||||||
FunctionInfo<DoSetElemFallbackFn>(DoSetElemFallback, PopValues(2));
|
FunctionInfo<DoSetElemFallbackFn>(DoSetElemFallback, TailCall, PopValues(2));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -5674,7 +5676,7 @@ DoInFallback(JSContext *cx, ICIn_Fallback *stub, HandleValue key, HandleValue ob
|
|||||||
typedef bool (*DoInFallbackFn)(JSContext *, ICIn_Fallback *, HandleValue, HandleValue,
|
typedef bool (*DoInFallbackFn)(JSContext *, ICIn_Fallback *, HandleValue, HandleValue,
|
||||||
MutableHandleValue);
|
MutableHandleValue);
|
||||||
static const VMFunction DoInFallbackInfo =
|
static const VMFunction DoInFallbackInfo =
|
||||||
FunctionInfo<DoInFallbackFn>(DoInFallback, PopValues(2));
|
FunctionInfo<DoInFallbackFn>(DoInFallback, TailCall, PopValues(2));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICIn_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICIn_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -6038,7 +6040,7 @@ DoGetNameFallback(JSContext *cx, BaselineFrame *frame, ICGetName_Fallback *stub_
|
|||||||
|
|
||||||
typedef bool (*DoGetNameFallbackFn)(JSContext *, BaselineFrame *, ICGetName_Fallback *,
|
typedef bool (*DoGetNameFallbackFn)(JSContext *, BaselineFrame *, ICGetName_Fallback *,
|
||||||
HandleObject, MutableHandleValue);
|
HandleObject, MutableHandleValue);
|
||||||
static const VMFunction DoGetNameFallbackInfo = FunctionInfo<DoGetNameFallbackFn>(DoGetNameFallback);
|
static const VMFunction DoGetNameFallbackInfo = FunctionInfo<DoGetNameFallbackFn>(DoGetNameFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICGetName_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICGetName_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -6149,7 +6151,7 @@ DoBindNameFallback(JSContext *cx, BaselineFrame *frame, ICBindName_Fallback *stu
|
|||||||
typedef bool (*DoBindNameFallbackFn)(JSContext *, BaselineFrame *, ICBindName_Fallback *,
|
typedef bool (*DoBindNameFallbackFn)(JSContext *, BaselineFrame *, ICBindName_Fallback *,
|
||||||
HandleObject, MutableHandleValue);
|
HandleObject, MutableHandleValue);
|
||||||
static const VMFunction DoBindNameFallbackInfo =
|
static const VMFunction DoBindNameFallbackInfo =
|
||||||
FunctionInfo<DoBindNameFallbackFn>(DoBindNameFallback);
|
FunctionInfo<DoBindNameFallbackFn>(DoBindNameFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICBindName_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICBindName_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -6209,7 +6211,7 @@ DoGetIntrinsicFallback(JSContext *cx, BaselineFrame *frame, ICGetIntrinsic_Fallb
|
|||||||
typedef bool (*DoGetIntrinsicFallbackFn)(JSContext *, BaselineFrame *, ICGetIntrinsic_Fallback *,
|
typedef bool (*DoGetIntrinsicFallbackFn)(JSContext *, BaselineFrame *, ICGetIntrinsic_Fallback *,
|
||||||
MutableHandleValue);
|
MutableHandleValue);
|
||||||
static const VMFunction DoGetIntrinsicFallbackInfo =
|
static const VMFunction DoGetIntrinsicFallbackInfo =
|
||||||
FunctionInfo<DoGetIntrinsicFallbackFn>(DoGetIntrinsicFallback);
|
FunctionInfo<DoGetIntrinsicFallbackFn>(DoGetIntrinsicFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICGetIntrinsic_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICGetIntrinsic_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -6833,7 +6835,7 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_
|
|||||||
typedef bool (*DoGetPropFallbackFn)(JSContext *, BaselineFrame *, ICGetProp_Fallback *,
|
typedef bool (*DoGetPropFallbackFn)(JSContext *, BaselineFrame *, ICGetProp_Fallback *,
|
||||||
MutableHandleValue, MutableHandleValue);
|
MutableHandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoGetPropFallbackInfo =
|
static const VMFunction DoGetPropFallbackInfo =
|
||||||
FunctionInfo<DoGetPropFallbackFn>(DoGetPropFallback, PopValues(1));
|
FunctionInfo<DoGetPropFallbackFn>(DoGetPropFallback, TailCall, PopValues(1));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -7985,7 +7987,7 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
|
|||||||
typedef bool (*DoSetPropFallbackFn)(JSContext *, BaselineFrame *, ICSetProp_Fallback *,
|
typedef bool (*DoSetPropFallbackFn)(JSContext *, BaselineFrame *, ICSetProp_Fallback *,
|
||||||
HandleValue, HandleValue, MutableHandleValue);
|
HandleValue, HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoSetPropFallbackInfo =
|
static const VMFunction DoSetPropFallbackInfo =
|
||||||
FunctionInfo<DoSetPropFallbackFn>(DoSetPropFallback, PopValues(2));
|
FunctionInfo<DoSetPropFallbackFn>(DoSetPropFallback, TailCall, PopValues(2));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -10484,7 +10486,7 @@ DoIteratorNewFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNew_Fallbac
|
|||||||
typedef bool (*DoIteratorNewFallbackFn)(JSContext *, BaselineFrame *, ICIteratorNew_Fallback *,
|
typedef bool (*DoIteratorNewFallbackFn)(JSContext *, BaselineFrame *, ICIteratorNew_Fallback *,
|
||||||
HandleValue, MutableHandleValue);
|
HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoIteratorNewFallbackInfo =
|
static const VMFunction DoIteratorNewFallbackInfo =
|
||||||
FunctionInfo<DoIteratorNewFallbackFn>(DoIteratorNewFallback, PopValues(1));
|
FunctionInfo<DoIteratorNewFallbackFn>(DoIteratorNewFallback, TailCall, PopValues(1));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICIteratorNew_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICIteratorNew_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -10540,7 +10542,7 @@ DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallb
|
|||||||
typedef bool (*DoIteratorMoreFallbackFn)(JSContext *, BaselineFrame *, ICIteratorMore_Fallback *,
|
typedef bool (*DoIteratorMoreFallbackFn)(JSContext *, BaselineFrame *, ICIteratorMore_Fallback *,
|
||||||
HandleObject, MutableHandleValue);
|
HandleObject, MutableHandleValue);
|
||||||
static const VMFunction DoIteratorMoreFallbackInfo =
|
static const VMFunction DoIteratorMoreFallbackInfo =
|
||||||
FunctionInfo<DoIteratorMoreFallbackFn>(DoIteratorMoreFallback);
|
FunctionInfo<DoIteratorMoreFallbackFn>(DoIteratorMoreFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICIteratorMore_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICIteratorMore_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -10619,7 +10621,7 @@ DoIteratorCloseFallback(JSContext *cx, ICIteratorClose_Fallback *stub, HandleVal
|
|||||||
|
|
||||||
typedef bool (*DoIteratorCloseFallbackFn)(JSContext *, ICIteratorClose_Fallback *, HandleValue);
|
typedef bool (*DoIteratorCloseFallbackFn)(JSContext *, ICIteratorClose_Fallback *, HandleValue);
|
||||||
static const VMFunction DoIteratorCloseFallbackInfo =
|
static const VMFunction DoIteratorCloseFallbackInfo =
|
||||||
FunctionInfo<DoIteratorCloseFallbackFn>(DoIteratorCloseFallback);
|
FunctionInfo<DoIteratorCloseFallbackFn>(DoIteratorCloseFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICIteratorClose_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICIteratorClose_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -10666,7 +10668,7 @@ DoInstanceOfFallback(JSContext *cx, ICInstanceOf_Fallback *stub,
|
|||||||
typedef bool (*DoInstanceOfFallbackFn)(JSContext *, ICInstanceOf_Fallback *, HandleValue, HandleValue,
|
typedef bool (*DoInstanceOfFallbackFn)(JSContext *, ICInstanceOf_Fallback *, HandleValue, HandleValue,
|
||||||
MutableHandleValue);
|
MutableHandleValue);
|
||||||
static const VMFunction DoInstanceOfFallbackInfo =
|
static const VMFunction DoInstanceOfFallbackInfo =
|
||||||
FunctionInfo<DoInstanceOfFallbackFn>(DoInstanceOfFallback, PopValues(2));
|
FunctionInfo<DoInstanceOfFallbackFn>(DoInstanceOfFallback, TailCall, PopValues(2));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICInstanceOf_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICInstanceOf_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -10715,7 +10717,7 @@ DoTypeOfFallback(JSContext *cx, BaselineFrame *frame, ICTypeOf_Fallback *stub, H
|
|||||||
typedef bool (*DoTypeOfFallbackFn)(JSContext *, BaselineFrame *frame, ICTypeOf_Fallback *,
|
typedef bool (*DoTypeOfFallbackFn)(JSContext *, BaselineFrame *frame, ICTypeOf_Fallback *,
|
||||||
HandleValue, MutableHandleValue);
|
HandleValue, MutableHandleValue);
|
||||||
static const VMFunction DoTypeOfFallbackInfo =
|
static const VMFunction DoTypeOfFallbackInfo =
|
||||||
FunctionInfo<DoTypeOfFallbackFn>(DoTypeOfFallback);
|
FunctionInfo<DoTypeOfFallbackFn>(DoTypeOfFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICTypeOf_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICTypeOf_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -10806,7 +10808,7 @@ typedef bool(*DoRetSubFallbackFn)(JSContext *cx, BaselineFrame *, ICRetSub_Fallb
|
|||||||
static const VMFunction DoRetSubFallbackInfo = FunctionInfo<DoRetSubFallbackFn>(DoRetSubFallback);
|
static const VMFunction DoRetSubFallbackInfo = FunctionInfo<DoRetSubFallbackFn>(DoRetSubFallback);
|
||||||
|
|
||||||
typedef bool (*ThrowFn)(JSContext *, HandleValue);
|
typedef bool (*ThrowFn)(JSContext *, HandleValue);
|
||||||
static const VMFunction ThrowInfoBaseline = FunctionInfo<ThrowFn>(js::Throw);
|
static const VMFunction ThrowInfoBaseline = FunctionInfo<ThrowFn>(js::Throw, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICRetSub_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICRetSub_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
@ -11529,7 +11531,7 @@ static bool DoRestFallback(JSContext *cx, ICRest_Fallback *stub,
|
|||||||
typedef bool (*DoRestFallbackFn)(JSContext *, ICRest_Fallback *, BaselineFrame *,
|
typedef bool (*DoRestFallbackFn)(JSContext *, ICRest_Fallback *, BaselineFrame *,
|
||||||
MutableHandleValue);
|
MutableHandleValue);
|
||||||
static const VMFunction DoRestFallbackInfo =
|
static const VMFunction DoRestFallbackInfo =
|
||||||
FunctionInfo<DoRestFallbackFn>(DoRestFallback);
|
FunctionInfo<DoRestFallbackFn>(DoRestFallback, TailCall);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICRest_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
ICRest_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||||
|
@ -1095,7 +1095,10 @@ PrepareAndExecuteRegExp(JSContext *cx, MacroAssembler &masm, Register regexp, Re
|
|||||||
RegExpStatics *res = cx->global()->getRegExpStatics(cx);
|
RegExpStatics *res = cx->global()->getRegExpStatics(cx);
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
|
#ifdef JS_USE_LINK_REGISTER
|
||||||
|
if (mode != RegExpShared::MatchOnly)
|
||||||
|
masm.pushReturnAddress();
|
||||||
|
#endif
|
||||||
if (mode == RegExpShared::Normal) {
|
if (mode == RegExpShared::Normal) {
|
||||||
// First, fill in a skeletal MatchPairs instance on the stack. This will be
|
// First, fill in a skeletal MatchPairs instance on the stack. This will be
|
||||||
// passed to the OOL stub in the caller if we aren't able to execute the
|
// passed to the OOL stub in the caller if we aren't able to execute the
|
||||||
@ -1573,6 +1576,10 @@ JitCompartment::generateRegExpTestStub(JSContext *cx)
|
|||||||
|
|
||||||
MacroAssembler masm(cx);
|
MacroAssembler masm(cx);
|
||||||
|
|
||||||
|
#ifdef JS_USE_LINK_REGISTER
|
||||||
|
masm.pushReturnAddress();
|
||||||
|
#endif
|
||||||
|
|
||||||
masm.reserveStack(sizeof(irregexp::InputOutputData));
|
masm.reserveStack(sizeof(irregexp::InputOutputData));
|
||||||
|
|
||||||
Label notFound, oolEntry;
|
Label notFound, oolEntry;
|
||||||
@ -6173,7 +6180,9 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
|
|||||||
Register forkJoinContext = CallTempReg4;
|
Register forkJoinContext = CallTempReg4;
|
||||||
|
|
||||||
Label failure, failurePopTemps;
|
Label failure, failurePopTemps;
|
||||||
|
#ifdef JS_USE_LINK_REGISTER
|
||||||
|
masm.pushReturnAddress();
|
||||||
|
#endif
|
||||||
// If lhs is empty, return rhs.
|
// If lhs is empty, return rhs.
|
||||||
Label leftEmpty;
|
Label leftEmpty;
|
||||||
masm.loadStringLength(lhs, temp1);
|
masm.loadStringLength(lhs, temp1);
|
||||||
@ -6284,6 +6293,9 @@ JitRuntime::generateMallocStub(JSContext *cx)
|
|||||||
MacroAssembler masm(cx);
|
MacroAssembler masm(cx);
|
||||||
|
|
||||||
RegisterSet regs = RegisterSet::Volatile();
|
RegisterSet regs = RegisterSet::Volatile();
|
||||||
|
#ifdef JS_USE_LINK_REGISTER
|
||||||
|
masm.pushReturnAddress();
|
||||||
|
#endif
|
||||||
regs.takeUnchecked(regNBytes);
|
regs.takeUnchecked(regNBytes);
|
||||||
masm.PushRegsInMask(regs);
|
masm.PushRegsInMask(regs);
|
||||||
|
|
||||||
@ -6319,7 +6331,9 @@ JitRuntime::generateFreeStub(JSContext *cx)
|
|||||||
const Register regSlots = CallTempReg0;
|
const Register regSlots = CallTempReg0;
|
||||||
|
|
||||||
MacroAssembler masm(cx);
|
MacroAssembler masm(cx);
|
||||||
|
#ifdef JS_USE_LINK_REGISTER
|
||||||
|
masm.pushReturnAddress();
|
||||||
|
#endif
|
||||||
RegisterSet regs = RegisterSet::Volatile();
|
RegisterSet regs = RegisterSet::Volatile();
|
||||||
regs.takeUnchecked(regSlots);
|
regs.takeUnchecked(regSlots);
|
||||||
masm.PushRegsInMask(regs);
|
masm.PushRegsInMask(regs);
|
||||||
@ -6352,15 +6366,26 @@ JitCode *
|
|||||||
JitRuntime::generateLazyLinkStub(JSContext *cx)
|
JitRuntime::generateLazyLinkStub(JSContext *cx)
|
||||||
{
|
{
|
||||||
MacroAssembler masm(cx);
|
MacroAssembler masm(cx);
|
||||||
|
#ifdef JS_USE_LINK_REGISTER
|
||||||
|
masm.push(lr);
|
||||||
|
#endif
|
||||||
|
|
||||||
Label call;
|
Label call;
|
||||||
GeneralRegisterSet regs = GeneralRegisterSet::Volatile();
|
GeneralRegisterSet regs = GeneralRegisterSet::Volatile();
|
||||||
Register temp0 = regs.takeAny();
|
Register temp0 = regs.takeAny();
|
||||||
|
|
||||||
masm.callWithExitFrame(&call);
|
masm.callWithExitFrame(&call);
|
||||||
|
#ifdef JS_USE_LINK_REGISTER
|
||||||
|
// sigh, this should probably attempt to bypass the push lr that starts off the block
|
||||||
|
// but oh well.
|
||||||
|
masm.pop(lr);
|
||||||
|
#endif
|
||||||
masm.jump(ReturnReg);
|
masm.jump(ReturnReg);
|
||||||
|
|
||||||
masm.bind(&call);
|
masm.bind(&call);
|
||||||
|
#ifdef JS_USE_LINK_REGISTER
|
||||||
|
masm.push(lr);
|
||||||
|
#endif
|
||||||
masm.enterExitFrame();
|
masm.enterExitFrame();
|
||||||
masm.setupUnalignedABICall(1, temp0);
|
masm.setupUnalignedABICall(1, temp0);
|
||||||
masm.loadJSContext(temp0);
|
masm.loadJSContext(temp0);
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "jit/PerfSpewer.h"
|
#include "jit/PerfSpewer.h"
|
||||||
#include "jit/RangeAnalysis.h"
|
#include "jit/RangeAnalysis.h"
|
||||||
#include "jit/ScalarReplacement.h"
|
#include "jit/ScalarReplacement.h"
|
||||||
|
#include "jit/Sink.h"
|
||||||
#include "jit/StupidAllocator.h"
|
#include "jit/StupidAllocator.h"
|
||||||
#include "jit/ValueNumbering.h"
|
#include "jit/ValueNumbering.h"
|
||||||
#include "vm/ForkJoin.h"
|
#include "vm/ForkJoin.h"
|
||||||
@ -1562,6 +1563,17 @@ OptimizeMIR(MIRGenerator *mir)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mir->optimizationInfo().sinkEnabled()) {
|
||||||
|
AutoTraceLog log(logger, TraceLogger::EliminateDeadCode);
|
||||||
|
if (!Sink(mir, graph))
|
||||||
|
return false;
|
||||||
|
IonSpewPass("Sink");
|
||||||
|
AssertExtendedGraphCoherency(graph);
|
||||||
|
|
||||||
|
if (mir->shouldCancel("Sink"))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Make loops contiguous. We do this after GVN/UCE and range analysis,
|
// Make loops contiguous. We do this after GVN/UCE and range analysis,
|
||||||
// which can remove CFG edges, exposing more blocks that can be moved.
|
// which can remove CFG edges, exposing more blocks that can be moved.
|
||||||
{
|
{
|
||||||
|
@ -569,10 +569,6 @@ jit::EliminateDeadCode(MIRGenerator *mir, MIRGraph &graph)
|
|||||||
if (js::jit::IsDiscardable(inst))
|
if (js::jit::IsDiscardable(inst))
|
||||||
{
|
{
|
||||||
block->discard(inst);
|
block->discard(inst);
|
||||||
} else if (!inst->isRecoveredOnBailout() && !inst->isGuard() &&
|
|
||||||
!inst->hasLiveDefUses() && inst->canRecoverOnBailout())
|
|
||||||
{
|
|
||||||
inst->setRecoveredOnBailout();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8204,8 +8204,12 @@ IonBuilder::jsop_setelem()
|
|||||||
if (!setElemTryArguments(&emitted, object, index, value) || emitted)
|
if (!setElemTryArguments(&emitted, object, index, value) || emitted)
|
||||||
return emitted;
|
return emitted;
|
||||||
|
|
||||||
if (script()->argumentsHasVarBinding() && object->mightBeType(MIRType_MagicOptimizedArguments))
|
if (script()->argumentsHasVarBinding() &&
|
||||||
|
object->mightBeType(MIRType_MagicOptimizedArguments) &&
|
||||||
|
info().executionMode() != ArgumentsUsageAnalysis)
|
||||||
|
{
|
||||||
return abort("Type is not definitely lazy arguments.");
|
return abort("Type is not definitely lazy arguments.");
|
||||||
|
}
|
||||||
|
|
||||||
if (!setElemTryCache(&emitted, object, index, value) || emitted)
|
if (!setElemTryCache(&emitted, object, index, value) || emitted)
|
||||||
return emitted;
|
return emitted;
|
||||||
|
@ -33,6 +33,7 @@ OptimizationInfo::initNormalOptimizationInfo()
|
|||||||
rangeAnalysis_ = true;
|
rangeAnalysis_ = true;
|
||||||
loopUnrolling_ = true;
|
loopUnrolling_ = true;
|
||||||
autoTruncate_ = true;
|
autoTruncate_ = true;
|
||||||
|
sink_ = true;
|
||||||
registerAllocator_ = RegisterAllocator_LSRA;
|
registerAllocator_ = RegisterAllocator_LSRA;
|
||||||
|
|
||||||
inlineMaxTotalBytecodeLength_ = 1000;
|
inlineMaxTotalBytecodeLength_ = 1000;
|
||||||
@ -58,6 +59,7 @@ OptimizationInfo::initAsmjsOptimizationInfo()
|
|||||||
edgeCaseAnalysis_ = false;
|
edgeCaseAnalysis_ = false;
|
||||||
eliminateRedundantChecks_ = false;
|
eliminateRedundantChecks_ = false;
|
||||||
autoTruncate_ = false;
|
autoTruncate_ = false;
|
||||||
|
sink_ = false;
|
||||||
registerAllocator_ = RegisterAllocator_Backtracking;
|
registerAllocator_ = RegisterAllocator_Backtracking;
|
||||||
scalarReplacement_ = false; // AsmJS has no objects.
|
scalarReplacement_ = false; // AsmJS has no objects.
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,9 @@ class OptimizationInfo
|
|||||||
// Toggles whether Truncation based on Range Analysis is used.
|
// Toggles whether Truncation based on Range Analysis is used.
|
||||||
bool autoTruncate_;
|
bool autoTruncate_;
|
||||||
|
|
||||||
|
// Toggles whether sink is used.
|
||||||
|
bool sink_;
|
||||||
|
|
||||||
// Describes which register allocator to use.
|
// Describes which register allocator to use.
|
||||||
IonRegisterAllocator registerAllocator_;
|
IonRegisterAllocator registerAllocator_;
|
||||||
|
|
||||||
@ -153,6 +156,10 @@ class OptimizationInfo
|
|||||||
return autoTruncate_ && rangeAnalysisEnabled();
|
return autoTruncate_ && rangeAnalysisEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sinkEnabled() const {
|
||||||
|
return sink_ && !js_JitOptions.disableSink;
|
||||||
|
}
|
||||||
|
|
||||||
bool eaaEnabled() const {
|
bool eaaEnabled() const {
|
||||||
return eaa_ && !js_JitOptions.disableEaa;
|
return eaa_ && !js_JitOptions.disableEaa;
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,9 @@ JitOptions::JitOptions()
|
|||||||
// Toggles whether Range Analysis is globally disabled.
|
// Toggles whether Range Analysis is globally disabled.
|
||||||
SET_DEFAULT(disableRangeAnalysis, false);
|
SET_DEFAULT(disableRangeAnalysis, false);
|
||||||
|
|
||||||
|
// Toggles whether sink code motion is globally disabled.
|
||||||
|
SET_DEFAULT(disableSink, false);
|
||||||
|
|
||||||
// Toggles whether Loop Unrolling is globally disabled.
|
// Toggles whether Loop Unrolling is globally disabled.
|
||||||
SET_DEFAULT(disableLoopUnrolling, true);
|
SET_DEFAULT(disableLoopUnrolling, true);
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ struct JitOptions
|
|||||||
bool disableInlining;
|
bool disableInlining;
|
||||||
bool disableEdgeCaseAnalysis;
|
bool disableEdgeCaseAnalysis;
|
||||||
bool disableRangeAnalysis;
|
bool disableRangeAnalysis;
|
||||||
|
bool disableSink;
|
||||||
bool disableLoopUnrolling;
|
bool disableLoopUnrolling;
|
||||||
bool disableEaa;
|
bool disableEaa;
|
||||||
bool eagerCompilation;
|
bool eagerCompilation;
|
||||||
|
@ -240,6 +240,7 @@ jit::CheckLogging()
|
|||||||
" alias Alias analysis\n"
|
" alias Alias analysis\n"
|
||||||
" gvn Global Value Numbering\n"
|
" gvn Global Value Numbering\n"
|
||||||
" licm Loop invariant code motion\n"
|
" licm Loop invariant code motion\n"
|
||||||
|
" sink Sink transformation\n"
|
||||||
" regalloc Register allocation\n"
|
" regalloc Register allocation\n"
|
||||||
" inline Inlining\n"
|
" inline Inlining\n"
|
||||||
" snapshots Snapshot information\n"
|
" snapshots Snapshot information\n"
|
||||||
@ -288,6 +289,8 @@ jit::CheckLogging()
|
|||||||
EnableChannel(JitSpew_Unrolling);
|
EnableChannel(JitSpew_Unrolling);
|
||||||
if (ContainsFlag(env, "licm"))
|
if (ContainsFlag(env, "licm"))
|
||||||
EnableChannel(JitSpew_LICM);
|
EnableChannel(JitSpew_LICM);
|
||||||
|
if (ContainsFlag(env, "sink"))
|
||||||
|
EnableChannel(JitSpew_Sink);
|
||||||
if (ContainsFlag(env, "regalloc"))
|
if (ContainsFlag(env, "regalloc"))
|
||||||
EnableChannel(JitSpew_RegAlloc);
|
EnableChannel(JitSpew_RegAlloc);
|
||||||
if (ContainsFlag(env, "inline"))
|
if (ContainsFlag(env, "inline"))
|
||||||
|
@ -26,6 +26,8 @@ namespace jit {
|
|||||||
_(Alias) \
|
_(Alias) \
|
||||||
/* Information during GVN */ \
|
/* Information during GVN */ \
|
||||||
_(GVN) \
|
_(GVN) \
|
||||||
|
/* Information during sinking */ \
|
||||||
|
_(Sink) \
|
||||||
/* Information during Range analysis */ \
|
/* Information during Range analysis */ \
|
||||||
_(Range) \
|
_(Range) \
|
||||||
/* Information during loop unrolling */ \
|
/* Information during loop unrolling */ \
|
||||||
|
@ -795,6 +795,27 @@ MBasicBlock::moveBefore(MInstruction *at, MInstruction *ins)
|
|||||||
ins->setTrackedSite(at->trackedSite());
|
ins->setTrackedSite(at->trackedSite());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MInstruction *
|
||||||
|
MBasicBlock::safeInsertTop(MDefinition *ins, IgnoreTop ignore)
|
||||||
|
{
|
||||||
|
// Beta nodes and interrupt checks are required to be located at the
|
||||||
|
// beginnings of basic blocks, so we must insert new instructions after any
|
||||||
|
// such instructions.
|
||||||
|
MInstructionIterator insertIter = !ins || ins->isPhi()
|
||||||
|
? begin()
|
||||||
|
: begin(ins->toInstruction());
|
||||||
|
while (insertIter->isBeta() ||
|
||||||
|
insertIter->isInterruptCheck() ||
|
||||||
|
insertIter->isInterruptCheckPar() ||
|
||||||
|
insertIter->isConstant() ||
|
||||||
|
(!(ignore & IgnoreRecover) && insertIter->isRecoveredOnBailout()))
|
||||||
|
{
|
||||||
|
insertIter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *insertIter;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MBasicBlock::discardResumePoint(MResumePoint *rp, ReferencesType refType /* = RefType_Default */)
|
MBasicBlock::discardResumePoint(MResumePoint *rp, ReferencesType refType /* = RefType_Default */)
|
||||||
{
|
{
|
||||||
|
@ -286,6 +286,15 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
|||||||
// Move an instruction. Movement may cross block boundaries.
|
// Move an instruction. Movement may cross block boundaries.
|
||||||
void moveBefore(MInstruction *at, MInstruction *ins);
|
void moveBefore(MInstruction *at, MInstruction *ins);
|
||||||
|
|
||||||
|
enum IgnoreTop {
|
||||||
|
IgnoreNone = 0,
|
||||||
|
IgnoreRecover = 1 << 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Locate the top of the |block|, where it is safe to insert a new
|
||||||
|
// instruction.
|
||||||
|
MInstruction *safeInsertTop(MDefinition *ins = nullptr, IgnoreTop ignore = IgnoreNone);
|
||||||
|
|
||||||
// Removes an instruction with the intention to discard it.
|
// Removes an instruction with the intention to discard it.
|
||||||
void discard(MInstruction *ins);
|
void discard(MInstruction *ins);
|
||||||
void discardLastIns();
|
void discardLastIns();
|
||||||
|
@ -2263,22 +2263,12 @@ RangeAnalysis::addRangeAssertions()
|
|||||||
// Beta nodes and interrupt checks are required to be located at the
|
// Beta nodes and interrupt checks are required to be located at the
|
||||||
// beginnings of basic blocks, so we must insert range assertions
|
// beginnings of basic blocks, so we must insert range assertions
|
||||||
// after any such instructions.
|
// after any such instructions.
|
||||||
MInstructionIterator insertIter = ins->isPhi()
|
MInstruction *insertAt = block->safeInsertTop(ins);
|
||||||
? block->begin()
|
|
||||||
: block->begin(ins->toInstruction());
|
|
||||||
while (insertIter->isBeta() ||
|
|
||||||
insertIter->isInterruptCheck() ||
|
|
||||||
insertIter->isInterruptCheckPar() ||
|
|
||||||
insertIter->isConstant() ||
|
|
||||||
insertIter->isRecoveredOnBailout())
|
|
||||||
{
|
|
||||||
insertIter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*insertIter == *iter)
|
if (insertAt == *iter)
|
||||||
block->insertAfter(*insertIter, guard);
|
block->insertAfter(insertAt, guard);
|
||||||
else
|
else
|
||||||
block->insertBefore(*insertIter, guard);
|
block->insertBefore(insertAt, guard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
202
js/src/jit/Sink.cpp
Normal file
202
js/src/jit/Sink.cpp
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "jit/Sink.h"
|
||||||
|
|
||||||
|
#include "mozilla/Vector.h"
|
||||||
|
|
||||||
|
#include "jit/IonAnalysis.h"
|
||||||
|
#include "jit/JitSpewer.h"
|
||||||
|
#include "jit/MIR.h"
|
||||||
|
#include "jit/MIRGenerator.h"
|
||||||
|
#include "jit/MIRGraph.h"
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
namespace jit {
|
||||||
|
|
||||||
|
// Given the last found common dominator and a new definition to dominate, the
|
||||||
|
// CommonDominator function returns the basic block which dominate the last
|
||||||
|
// common dominator and the definition. If no such block exists, then this
|
||||||
|
// functions return null.
|
||||||
|
static MBasicBlock *
|
||||||
|
CommonDominator(MBasicBlock *commonDominator, MBasicBlock *defBlock)
|
||||||
|
{
|
||||||
|
// This is the first instruction visited, record its basic block as being
|
||||||
|
// the only interesting one.
|
||||||
|
if (!commonDominator)
|
||||||
|
return defBlock;
|
||||||
|
|
||||||
|
// Iterate on immediate dominators of the known common dominator to find a
|
||||||
|
// block which dominates all previous uses as well as this instruction.
|
||||||
|
while (!commonDominator->dominates(defBlock)) {
|
||||||
|
MBasicBlock *nextBlock = commonDominator->immediateDominator();
|
||||||
|
// All uses are dominated, so, this cannot happen unless the graph
|
||||||
|
// coherency is not respected.
|
||||||
|
MOZ_ASSERT(commonDominator != nextBlock);
|
||||||
|
commonDominator = nextBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
return commonDominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Sink(MIRGenerator *mir, MIRGraph &graph)
|
||||||
|
{
|
||||||
|
TempAllocator &alloc = graph.alloc();
|
||||||
|
|
||||||
|
for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
|
||||||
|
if (mir->shouldCancel("Sink"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (MInstructionReverseIterator iter = block->rbegin(); iter != block->rend(); ) {
|
||||||
|
MInstruction *ins = *iter++;
|
||||||
|
|
||||||
|
// Only instructions which can be recovered on bailout can be moved
|
||||||
|
// into the bailout paths.
|
||||||
|
if (ins->isGuard() || ins->isRecoveredOnBailout() || !ins->canRecoverOnBailout())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Compute a common dominator for all uses of the current
|
||||||
|
// instruction.
|
||||||
|
bool hasLiveUses = false;
|
||||||
|
bool hasUses = false;
|
||||||
|
MBasicBlock *usesDominator = nullptr;
|
||||||
|
for (MUseIterator i(ins->usesBegin()), e(ins->usesEnd()); i != e; i++) {
|
||||||
|
hasUses = true;
|
||||||
|
MNode *consumerNode = (*i)->consumer();
|
||||||
|
if (consumerNode->isResumePoint())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MDefinition *consumer = consumerNode->toDefinition();
|
||||||
|
if (consumer->isRecoveredOnBailout())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hasLiveUses = true;
|
||||||
|
|
||||||
|
// If the instruction is a Phi, then we should dominate the
|
||||||
|
// predecessor from which the value is coming from.
|
||||||
|
MBasicBlock *consumerBlock = consumer->block();
|
||||||
|
if (consumer->isPhi())
|
||||||
|
consumerBlock = consumerBlock->getPredecessor(consumer->indexOf(*i));
|
||||||
|
|
||||||
|
usesDominator = CommonDominator(usesDominator, consumerBlock);
|
||||||
|
if (usesDominator == *block)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leave this instruction for DCE.
|
||||||
|
if (!hasUses)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// We have no uses, so sink this instruction in all the bailout
|
||||||
|
// paths.
|
||||||
|
if (!hasLiveUses) {
|
||||||
|
MOZ_ASSERT(!usesDominator);
|
||||||
|
ins->setRecoveredOnBailout();
|
||||||
|
JitSpewDef(JitSpew_Sink, " No live uses, recover the instruction on bailout\n", ins);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all the uses are under a loop, we might not want to work
|
||||||
|
// against LICM by moving everything back into the loop, but if the
|
||||||
|
// loop is it-self inside an if, then we still want to move the
|
||||||
|
// computation under this if statement.
|
||||||
|
while (block->loopDepth() < usesDominator->loopDepth()) {
|
||||||
|
MOZ_ASSERT(usesDominator != usesDominator->immediateDominator());
|
||||||
|
usesDominator = usesDominator->immediateDominator();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only move instructions if there is a branch between the dominator
|
||||||
|
// of the uses and the original instruction. This prevent moving the
|
||||||
|
// computation of the arguments into an inline function if there is
|
||||||
|
// no major win.
|
||||||
|
MBasicBlock *lastJoin = usesDominator;
|
||||||
|
while (*block != lastJoin && lastJoin->numPredecessors() == 1) {
|
||||||
|
MOZ_ASSERT(lastJoin != lastJoin->immediateDominator());
|
||||||
|
MBasicBlock *next = lastJoin->immediateDominator();
|
||||||
|
if (next->numSuccessors() > 1)
|
||||||
|
break;
|
||||||
|
lastJoin = next;
|
||||||
|
}
|
||||||
|
if (*block == lastJoin)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Skip to the next instruction if we cannot find a common dominator
|
||||||
|
// for all the uses of this instruction, or if the common dominator
|
||||||
|
// correspond to the block of the current instruction.
|
||||||
|
if (!usesDominator || usesDominator == *block)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Only instruction which can be recovered on bailout and which are
|
||||||
|
// sinkable can be moved into blocks which are below while filling
|
||||||
|
// the resume points with a clone which is recovered on bailout.
|
||||||
|
|
||||||
|
// If the instruction has live uses and if it is clonable, then we
|
||||||
|
// can clone the instruction for all non-dominated uses and move the
|
||||||
|
// instruction into the block which is dominating all live uses.
|
||||||
|
if (!ins->canClone())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
JitSpewDef(JitSpew_Sink, " Can Clone & Recover, sink instruction\n", ins);
|
||||||
|
JitSpew(JitSpew_Sink, " into Block %u", usesDominator->id());
|
||||||
|
|
||||||
|
// Copy the arguments and clone the instruction.
|
||||||
|
MDefinitionVector operands(alloc);
|
||||||
|
for (size_t i = 0, end = ins->numOperands(); i < end; i++) {
|
||||||
|
if (!operands.append(ins->getOperand(i)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MInstruction *clone = ins->clone(alloc, operands);
|
||||||
|
ins->block()->insertBefore(ins, clone);
|
||||||
|
clone->setRecoveredOnBailout();
|
||||||
|
|
||||||
|
// We should not update the producer of the entry resume point, as
|
||||||
|
// it cannot refer to any instruction within the basic block excepts
|
||||||
|
// for Phi nodes.
|
||||||
|
MResumePoint *entry = usesDominator->entryResumePoint();
|
||||||
|
|
||||||
|
// Replace the instruction by its clone in all the resume points /
|
||||||
|
// recovered-on-bailout instructions which are not in blocks which
|
||||||
|
// are dominated by the usesDominator block.
|
||||||
|
for (MUseIterator i(ins->usesBegin()), e(ins->usesEnd()); i != e; ) {
|
||||||
|
MUse *use = *i++;
|
||||||
|
MNode *consumer = use->consumer();
|
||||||
|
|
||||||
|
// If the consumer is a Phi, then we look for the index of the
|
||||||
|
// use to find the corresponding predecessor block, which is
|
||||||
|
// then used as the consumer block.
|
||||||
|
MBasicBlock *consumerBlock = consumer->block();
|
||||||
|
if (consumer->isDefinition() && consumer->toDefinition()->isPhi()) {
|
||||||
|
consumerBlock = consumerBlock->getPredecessor(
|
||||||
|
consumer->toDefinition()->toPhi()->indexOf(use));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the current instruction for all dominated uses, except
|
||||||
|
// for the entry resume point of the block in which the
|
||||||
|
// instruction would be moved into.
|
||||||
|
if (usesDominator->dominates(consumerBlock) &&
|
||||||
|
(!consumer->isResumePoint() || consumer->toResumePoint() != entry))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
use->replaceProducer(clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, that all uses which are not dominated by usesDominator are
|
||||||
|
// using the cloned instruction, we can safely move the instruction
|
||||||
|
// into the usesDominator block.
|
||||||
|
MInstruction *at = usesDominator->safeInsertTop(nullptr, MBasicBlock::IgnoreRecover);
|
||||||
|
block->moveBefore(at, ins);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
23
js/src/jit/Sink.h
Normal file
23
js/src/jit/Sink.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// This file declares sink transformation.
|
||||||
|
#ifndef jit_Sink_h
|
||||||
|
#define jit_Sink_h
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
namespace jit {
|
||||||
|
|
||||||
|
class MIRGenerator;
|
||||||
|
class MIRGraph;
|
||||||
|
|
||||||
|
bool
|
||||||
|
Sink(MIRGenerator *mir, MIRGraph &graph);
|
||||||
|
|
||||||
|
} // namespace jit
|
||||||
|
} // namespace js
|
||||||
|
|
||||||
|
#endif /* jit_Sink_h */
|
@ -41,6 +41,11 @@ struct PopValues
|
|||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum MaybeTailCall {
|
||||||
|
TailCall,
|
||||||
|
NonTailCall
|
||||||
|
};
|
||||||
|
|
||||||
// Contains information about a virtual machine function that can be called
|
// Contains information about a virtual machine function that can be called
|
||||||
// from JIT code. Functions described in this manner must conform to a simple
|
// from JIT code. Functions described in this manner must conform to a simple
|
||||||
// protocol: the return type must have a special "failure" value (for example,
|
// protocol: the return type must have a special "failure" value (for example,
|
||||||
@ -123,6 +128,11 @@ struct VMFunction
|
|||||||
// wrapper.
|
// wrapper.
|
||||||
uint32_t extraValuesToPop;
|
uint32_t extraValuesToPop;
|
||||||
|
|
||||||
|
// On some architectures, called functions need to explicitly push their
|
||||||
|
// return address, for a tail call, there is nothing to push, so tail-callness
|
||||||
|
// needs to be known at compile time.
|
||||||
|
MaybeTailCall expectTailCall;
|
||||||
|
|
||||||
uint32_t argc() const {
|
uint32_t argc() const {
|
||||||
// JSContext * + args + (OutParam? *)
|
// JSContext * + args + (OutParam? *)
|
||||||
return 1 + explicitArgc() + ((outParam == Type_Void) ? 0 : 1);
|
return 1 + explicitArgc() + ((outParam == Type_Void) ? 0 : 1);
|
||||||
@ -227,7 +237,8 @@ struct VMFunction
|
|||||||
VMFunction(void *wrapped, uint32_t explicitArgs, uint32_t argumentProperties,
|
VMFunction(void *wrapped, uint32_t explicitArgs, uint32_t argumentProperties,
|
||||||
uint32_t argumentPassedInFloatRegs, uint64_t argRootTypes,
|
uint32_t argumentPassedInFloatRegs, uint64_t argRootTypes,
|
||||||
DataType outParam, RootType outParamRootType, DataType returnType,
|
DataType outParam, RootType outParamRootType, DataType returnType,
|
||||||
ExecutionMode executionMode, uint32_t extraValuesToPop = 0)
|
ExecutionMode executionMode, uint32_t extraValuesToPop = 0,
|
||||||
|
MaybeTailCall expectTailCall = NonTailCall)
|
||||||
: wrapped(wrapped),
|
: wrapped(wrapped),
|
||||||
explicitArgs(explicitArgs),
|
explicitArgs(explicitArgs),
|
||||||
argumentProperties(argumentProperties),
|
argumentProperties(argumentProperties),
|
||||||
@ -237,7 +248,8 @@ struct VMFunction
|
|||||||
argumentRootTypes(argRootTypes),
|
argumentRootTypes(argRootTypes),
|
||||||
outParamRootType(outParamRootType),
|
outParamRootType(outParamRootType),
|
||||||
executionMode(executionMode),
|
executionMode(executionMode),
|
||||||
extraValuesToPop(extraValuesToPop)
|
extraValuesToPop(extraValuesToPop),
|
||||||
|
expectTailCall(expectTailCall)
|
||||||
{
|
{
|
||||||
// Check for valid failure/return type.
|
// Check for valid failure/return type.
|
||||||
MOZ_ASSERT_IF(outParam != Type_Void && executionMode == SequentialExecution,
|
MOZ_ASSERT_IF(outParam != Type_Void && executionMode == SequentialExecution,
|
||||||
@ -503,12 +515,20 @@ template <> struct MatchContext<ThreadSafeContext *> {
|
|||||||
static inline uint64_t argumentRootTypes() { \
|
static inline uint64_t argumentRootTypes() { \
|
||||||
return ForEachNb(COMPUTE_ARG_ROOT, SEP_OR, NOTHING); \
|
return ForEachNb(COMPUTE_ARG_ROOT, SEP_OR, NOTHING); \
|
||||||
} \
|
} \
|
||||||
|
explicit FunctionInfo(pf fun, MaybeTailCall expectTailCall, \
|
||||||
|
PopValues extraValuesToPop = PopValues(0)) \
|
||||||
|
: VMFunction(JS_FUNC_TO_DATA_PTR(void *, fun), explicitArgs(), \
|
||||||
|
argumentProperties(), argumentPassedInFloatRegs(), \
|
||||||
|
argumentRootTypes(), outParam(), outParamRootType(), \
|
||||||
|
returnType(), executionMode(), \
|
||||||
|
extraValuesToPop.numValues, expectTailCall) \
|
||||||
|
{ } \
|
||||||
explicit FunctionInfo(pf fun, PopValues extraValuesToPop = PopValues(0)) \
|
explicit FunctionInfo(pf fun, PopValues extraValuesToPop = PopValues(0)) \
|
||||||
: VMFunction(JS_FUNC_TO_DATA_PTR(void *, fun), explicitArgs(), \
|
: VMFunction(JS_FUNC_TO_DATA_PTR(void *, fun), explicitArgs(), \
|
||||||
argumentProperties(), argumentPassedInFloatRegs(), \
|
argumentProperties(), argumentPassedInFloatRegs(), \
|
||||||
argumentRootTypes(), outParam(), outParamRootType(), \
|
argumentRootTypes(), outParam(), outParamRootType(), \
|
||||||
returnType(), executionMode(), \
|
returnType(), executionMode(), \
|
||||||
extraValuesToPop.numValues) \
|
extraValuesToPop.numValues, NonTailCall) \
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
template <typename Fun>
|
template <typename Fun>
|
||||||
@ -548,7 +568,13 @@ struct FunctionInfo<R (*)(Context)> : public VMFunction {
|
|||||||
: VMFunction(JS_FUNC_TO_DATA_PTR(void *, fun), explicitArgs(),
|
: VMFunction(JS_FUNC_TO_DATA_PTR(void *, fun), explicitArgs(),
|
||||||
argumentProperties(), argumentPassedInFloatRegs(),
|
argumentProperties(), argumentPassedInFloatRegs(),
|
||||||
argumentRootTypes(), outParam(), outParamRootType(),
|
argumentRootTypes(), outParam(), outParamRootType(),
|
||||||
returnType(), executionMode())
|
returnType(), executionMode(), 0, NonTailCall)
|
||||||
|
{ }
|
||||||
|
explicit FunctionInfo(pf fun, MaybeTailCall expectTailCall)
|
||||||
|
: VMFunction(JS_FUNC_TO_DATA_PTR(void *, fun), explicitArgs(),
|
||||||
|
argumentProperties(), argumentPassedInFloatRegs(),
|
||||||
|
argumentRootTypes(), outParam(), outParamRootType(),
|
||||||
|
returnType(), executionMode(), 0, expectTailCall)
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,7 +42,9 @@ CodeGeneratorARM::generatePrologue()
|
|||||||
{
|
{
|
||||||
MOZ_ASSERT(masm.framePushed() == 0);
|
MOZ_ASSERT(masm.framePushed() == 0);
|
||||||
MOZ_ASSERT(!gen->compilingAsmJS());
|
MOZ_ASSERT(!gen->compilingAsmJS());
|
||||||
|
#ifdef JS_USE_LINK_REGISTER
|
||||||
|
masm.pushReturnAddress();
|
||||||
|
#endif
|
||||||
// Note that this automatically sets MacroAssembler::framePushed().
|
// Note that this automatically sets MacroAssembler::framePushed().
|
||||||
masm.reserveStack(frameSize());
|
masm.reserveStack(frameSize());
|
||||||
masm.checkStackAlignment();
|
masm.checkStackAlignment();
|
||||||
|
@ -3715,8 +3715,7 @@ MacroAssemblerARM::ma_callIon(const Register r)
|
|||||||
// When the stack is 8 byte aligned, we want to decrement sp by 8, and write
|
// When the stack is 8 byte aligned, we want to decrement sp by 8, and write
|
||||||
// pc + 8 into the new sp. When we return from this call, sp will be its
|
// pc + 8 into the new sp. When we return from this call, sp will be its
|
||||||
// present value minus 4.
|
// present value minus 4.
|
||||||
AutoForbidPools afp(this, 2);
|
as_sub(sp, sp, Imm8(4));
|
||||||
as_dtr(IsStore, 32, PreIndex, pc, DTRAddr(sp, DtrOffImm(-8)));
|
|
||||||
as_blx(r);
|
as_blx(r);
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
@ -3724,8 +3723,9 @@ MacroAssemblerARM::ma_callIonNoPush(const Register r)
|
|||||||
{
|
{
|
||||||
// Since we just write the return address into the stack, which is popped on
|
// Since we just write the return address into the stack, which is popped on
|
||||||
// return, the net effect is removing 4 bytes from the stack.
|
// return, the net effect is removing 4 bytes from the stack.
|
||||||
AutoForbidPools afp(this, 2);
|
|
||||||
as_dtr(IsStore, 32, Offset, pc, DTRAddr(sp, DtrOffImm(0)));
|
// Bug 1103108: remove this function, and refactor all uses.
|
||||||
|
as_add(sp, sp, Imm8(4));
|
||||||
as_blx(r);
|
as_blx(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3735,19 +3735,18 @@ MacroAssemblerARM::ma_callIonHalfPush(const Register r)
|
|||||||
// The stack is unaligned by 4 bytes. We push the pc to the stack to align
|
// The stack is unaligned by 4 bytes. We push the pc to the stack to align
|
||||||
// the stack before the call, when we return the pc is poped and the stack
|
// the stack before the call, when we return the pc is poped and the stack
|
||||||
// is restored to its unaligned state.
|
// is restored to its unaligned state.
|
||||||
AutoForbidPools afp(this, 2);
|
|
||||||
ma_push(pc);
|
|
||||||
as_blx(r);
|
as_blx(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssemblerARM::ma_callIonHalfPush(Label *label)
|
MacroAssemblerARM::ma_callIonHalfPush(Label *label)
|
||||||
{
|
{
|
||||||
// The stack is unaligned by 4 bytes. We push the pc to the stack to align
|
// The stack is unaligned by 4 bytes. The callee will push the lr to the stack to align
|
||||||
// the stack before the call, when we return the pc is poped and the stack
|
// the stack after the call, when we return the pc is poped and the stack
|
||||||
// is restored to its unaligned state.
|
// is restored to its unaligned state.
|
||||||
AutoForbidPools afp(this, 2);
|
|
||||||
ma_push(pc);
|
// leave the stack as-is so the callee-side can push when necessary.
|
||||||
|
|
||||||
as_bl(label, Always);
|
as_bl(label, Always);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1833,6 +1833,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||||||
void loadAsmJSHeapRegisterFromGlobalData() {
|
void loadAsmJSHeapRegisterFromGlobalData() {
|
||||||
loadPtr(Address(GlobalReg, AsmJSHeapGlobalDataOffset - AsmJSGlobalRegBias), HeapReg);
|
loadPtr(Address(GlobalReg, AsmJSHeapGlobalDataOffset - AsmJSGlobalRegBias), HeapReg);
|
||||||
}
|
}
|
||||||
|
void pushReturnAddress() {
|
||||||
|
push(lr);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef MacroAssemblerARMCompat MacroAssemblerSpecific;
|
typedef MacroAssemblerARMCompat MacroAssemblerSpecific;
|
||||||
|
@ -419,6 +419,8 @@ JitCode *
|
|||||||
JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut)
|
JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut)
|
||||||
{
|
{
|
||||||
MacroAssembler masm(cx);
|
MacroAssembler masm(cx);
|
||||||
|
masm.pushReturnAddress();
|
||||||
|
|
||||||
// ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
|
// ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
|
||||||
// Including |this|, there are (|nargs| + 1) arguments to copy.
|
// Including |this|, there are (|nargs| + 1) arguments to copy.
|
||||||
MOZ_ASSERT(ArgumentsRectifierReg == r8);
|
MOZ_ASSERT(ArgumentsRectifierReg == r8);
|
||||||
@ -747,6 +749,10 @@ JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
|||||||
// +0 ExitFrame
|
// +0 ExitFrame
|
||||||
//
|
//
|
||||||
// We're aligned to an exit frame, so link it up.
|
// We're aligned to an exit frame, so link it up.
|
||||||
|
// If it isn't a tail call, then the return address needs to be saved
|
||||||
|
if (f.expectTailCall == NonTailCall)
|
||||||
|
masm.pushReturnAddress();
|
||||||
|
|
||||||
masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode);
|
masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode);
|
||||||
|
|
||||||
// Save the base of the argument set stored on the stack.
|
// Save the base of the argument set stored on the stack.
|
||||||
@ -914,6 +920,7 @@ JitRuntime::generatePreBarrier(JSContext *cx, MIRType type)
|
|||||||
save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
|
save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
|
||||||
FloatRegisterSet());
|
FloatRegisterSet());
|
||||||
}
|
}
|
||||||
|
save.add(lr);
|
||||||
masm.PushRegsInMask(save);
|
masm.PushRegsInMask(save);
|
||||||
|
|
||||||
MOZ_ASSERT(PreBarrierReg == r1);
|
MOZ_ASSERT(PreBarrierReg == r1);
|
||||||
@ -923,9 +930,9 @@ JitRuntime::generatePreBarrier(JSContext *cx, MIRType type)
|
|||||||
masm.passABIArg(r0);
|
masm.passABIArg(r0);
|
||||||
masm.passABIArg(r1);
|
masm.passABIArg(r1);
|
||||||
masm.callWithABI(IonMarkFunction(type));
|
masm.callWithABI(IonMarkFunction(type));
|
||||||
|
save.take(AnyRegister(lr));
|
||||||
|
save.add(pc);
|
||||||
masm.PopRegsInMask(save);
|
masm.PopRegsInMask(save);
|
||||||
masm.ret();
|
|
||||||
|
|
||||||
Linker linker(masm);
|
Linker linker(masm);
|
||||||
AutoFlushICache afc("PreBarrier");
|
AutoFlushICache afc("PreBarrier");
|
||||||
|
@ -18,6 +18,10 @@
|
|||||||
#include "jit/RegisterSets.h"
|
#include "jit/RegisterSets.h"
|
||||||
#include "vm/HelperThreads.h"
|
#include "vm/HelperThreads.h"
|
||||||
|
|
||||||
|
#if defined(JS_CODEGEN_ARM)
|
||||||
|
#define JS_USE_LINK_REGISTER
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
|
#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
|
||||||
// JS_SMALL_BRANCH means the range on a branch instruction
|
// JS_SMALL_BRANCH means the range on a branch instruction
|
||||||
// is smaller than the whole address space
|
// is smaller than the whole address space
|
||||||
|
@ -90,7 +90,7 @@ BaselineCompilerShared::callVM(const VMFunction &fun, CallVMPhase phase)
|
|||||||
masm.makeFrameDescriptor(BaselineTailCallReg, JitFrame_BaselineJS);
|
masm.makeFrameDescriptor(BaselineTailCallReg, JitFrame_BaselineJS);
|
||||||
masm.push(BaselineTailCallReg);
|
masm.push(BaselineTailCallReg);
|
||||||
}
|
}
|
||||||
|
MOZ_ASSERT(fun.expectTailCall == NonTailCall);
|
||||||
// Perform the call.
|
// Perform the call.
|
||||||
masm.call(code);
|
masm.call(code);
|
||||||
uint32_t callOffset = masm.currentOffset();
|
uint32_t callOffset = masm.currentOffset();
|
||||||
|
@ -2677,6 +2677,8 @@ CodeGeneratorX86Shared::visitSimdShuffle(LSimdShuffle *ins)
|
|||||||
bool
|
bool
|
||||||
CodeGeneratorX86Shared::visitSimdBinaryCompIx4(LSimdBinaryCompIx4 *ins)
|
CodeGeneratorX86Shared::visitSimdBinaryCompIx4(LSimdBinaryCompIx4 *ins)
|
||||||
{
|
{
|
||||||
|
static const SimdConstant allOnes = SimdConstant::SplatX4(-1);
|
||||||
|
|
||||||
FloatRegister lhs = ToFloatRegister(ins->lhs());
|
FloatRegister lhs = ToFloatRegister(ins->lhs());
|
||||||
Operand rhs = ToOperand(ins->rhs());
|
Operand rhs = ToOperand(ins->rhs());
|
||||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == lhs);
|
MOZ_ASSERT(ToFloatRegister(ins->output()) == lhs);
|
||||||
@ -2690,22 +2692,41 @@ CodeGeneratorX86Shared::visitSimdBinaryCompIx4(LSimdBinaryCompIx4 *ins)
|
|||||||
masm.packedEqualInt32x4(rhs, lhs);
|
masm.packedEqualInt32x4(rhs, lhs);
|
||||||
return true;
|
return true;
|
||||||
case MSimdBinaryComp::lessThan:
|
case MSimdBinaryComp::lessThan:
|
||||||
// scr := rhs
|
// src := rhs
|
||||||
if (rhs.kind() == Operand::FPREG)
|
if (rhs.kind() == Operand::FPREG)
|
||||||
masm.moveAlignedInt32x4(ToFloatRegister(ins->rhs()), ScratchSimdReg);
|
masm.moveAlignedInt32x4(ToFloatRegister(ins->rhs()), ScratchSimdReg);
|
||||||
else
|
else
|
||||||
masm.loadAlignedInt32x4(rhs, ScratchSimdReg);
|
masm.loadAlignedInt32x4(rhs, ScratchSimdReg);
|
||||||
|
|
||||||
// scr := scr > lhs (i.e. lhs < rhs)
|
// src := src > lhs (i.e. lhs < rhs)
|
||||||
// Improve by doing custom lowering (rhs is tied to the output register)
|
// Improve by doing custom lowering (rhs is tied to the output register)
|
||||||
masm.packedGreaterThanInt32x4(ToOperand(ins->lhs()), ScratchSimdReg);
|
masm.packedGreaterThanInt32x4(ToOperand(ins->lhs()), ScratchSimdReg);
|
||||||
masm.moveAlignedInt32x4(ScratchSimdReg, lhs);
|
masm.moveAlignedInt32x4(ScratchSimdReg, lhs);
|
||||||
return true;
|
return true;
|
||||||
case MSimdBinaryComp::notEqual:
|
case MSimdBinaryComp::notEqual:
|
||||||
|
// Ideally for notEqual, greaterThanOrEqual, and lessThanOrEqual, we
|
||||||
|
// should invert the comparison by, e.g. swapping the arms of a select
|
||||||
|
// if that's what it's used in.
|
||||||
|
masm.loadConstantInt32x4(allOnes, ScratchSimdReg);
|
||||||
|
masm.packedEqualInt32x4(rhs, lhs);
|
||||||
|
masm.bitwiseXorX4(Operand(ScratchSimdReg), lhs);
|
||||||
|
return true;
|
||||||
case MSimdBinaryComp::greaterThanOrEqual:
|
case MSimdBinaryComp::greaterThanOrEqual:
|
||||||
|
// src := rhs
|
||||||
|
if (rhs.kind() == Operand::FPREG)
|
||||||
|
masm.moveAlignedInt32x4(ToFloatRegister(ins->rhs()), ScratchSimdReg);
|
||||||
|
else
|
||||||
|
masm.loadAlignedInt32x4(rhs, ScratchSimdReg);
|
||||||
|
masm.packedGreaterThanInt32x4(ToOperand(ins->lhs()), ScratchSimdReg);
|
||||||
|
masm.loadConstantInt32x4(allOnes, lhs);
|
||||||
|
masm.bitwiseXorX4(Operand(ScratchSimdReg), lhs);
|
||||||
|
return true;
|
||||||
case MSimdBinaryComp::lessThanOrEqual:
|
case MSimdBinaryComp::lessThanOrEqual:
|
||||||
// These operations are not part of the spec. so are not implemented.
|
// lhs <= rhs is equivalent to !(rhs < lhs), which we compute here.
|
||||||
break;
|
masm.loadConstantInt32x4(allOnes, ScratchSimdReg);
|
||||||
|
masm.packedGreaterThanInt32x4(rhs, lhs);
|
||||||
|
masm.bitwiseXorX4(Operand(ScratchSimdReg), lhs);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
MOZ_CRASH("unexpected SIMD op");
|
MOZ_CRASH("unexpected SIMD op");
|
||||||
}
|
}
|
||||||
|
@ -2732,9 +2732,15 @@ GCRuntime::unprotectRelocatedArenas(ArenaHeader *relocatedList)
|
|||||||
void
|
void
|
||||||
GCRuntime::releaseRelocatedArenas(ArenaHeader *relocatedList)
|
GCRuntime::releaseRelocatedArenas(ArenaHeader *relocatedList)
|
||||||
{
|
{
|
||||||
// Release the relocated arenas, now containing only forwarding pointers
|
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
|
releaseRelocatedArenasWithoutUnlocking(relocatedList, lock);
|
||||||
|
expireChunksAndArenas(true, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCRuntime::releaseRelocatedArenasWithoutUnlocking(ArenaHeader *relocatedList, const AutoLockGC &lock)
|
||||||
|
{
|
||||||
|
// Release the relocated arenas, now containing only forwarding pointers
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
while (relocatedList) {
|
while (relocatedList) {
|
||||||
ArenaHeader *aheader = relocatedList;
|
ArenaHeader *aheader = relocatedList;
|
||||||
@ -2759,8 +2765,6 @@ GCRuntime::releaseRelocatedArenas(ArenaHeader *relocatedList)
|
|||||||
releaseArena(aheader, lock);
|
releaseArena(aheader, lock);
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
|
|
||||||
expireChunksAndArenas(true, lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // JSGC_COMPACTING
|
#endif // JSGC_COMPACTING
|
||||||
@ -6406,6 +6410,13 @@ GCRuntime::onOutOfMallocMemory(const AutoLockGC &lock)
|
|||||||
// Throw away any excess chunks we have lying around.
|
// Throw away any excess chunks we have lying around.
|
||||||
freeEmptyChunks(rt, lock);
|
freeEmptyChunks(rt, lock);
|
||||||
|
|
||||||
|
// Release any relocated areans we may be holding on to.
|
||||||
|
#if defined(JSGC_COMPACTING) && defined(DEBUG)
|
||||||
|
unprotectRelocatedArenas(relocatedArenasToRelease);
|
||||||
|
releaseRelocatedArenasWithoutUnlocking(relocatedArenasToRelease, lock);
|
||||||
|
relocatedArenasToRelease = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Immediately decommit as many arenas as possible in the hopes that this
|
// Immediately decommit as many arenas as possible in the hopes that this
|
||||||
// might let the OS scrape together enough pages to satisfy the failing
|
// might let the OS scrape together enough pages to satisfy the failing
|
||||||
// malloc request.
|
// malloc request.
|
||||||
|
@ -190,6 +190,7 @@ UNIFIED_SOURCES += [
|
|||||||
'jit/shared/BaselineCompiler-shared.cpp',
|
'jit/shared/BaselineCompiler-shared.cpp',
|
||||||
'jit/shared/CodeGenerator-shared.cpp',
|
'jit/shared/CodeGenerator-shared.cpp',
|
||||||
'jit/shared/Lowering-shared.cpp',
|
'jit/shared/Lowering-shared.cpp',
|
||||||
|
'jit/Sink.cpp',
|
||||||
'jit/Snapshots.cpp',
|
'jit/Snapshots.cpp',
|
||||||
'jit/StupidAllocator.cpp',
|
'jit/StupidAllocator.cpp',
|
||||||
'jit/TypedObjectPrediction.cpp',
|
'jit/TypedObjectPrediction.cpp',
|
||||||
|
@ -5500,6 +5500,15 @@ SetRuntimeOptions(JSRuntime *rt, const OptionParser &op)
|
|||||||
return OptionFailure("ion-range-analysis", str);
|
return OptionFailure("ion-range-analysis", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const char *str = op.getStringOption("ion-sink")) {
|
||||||
|
if (strcmp(str, "on") == 0)
|
||||||
|
jit::js_JitOptions.disableSink = false;
|
||||||
|
else if (strcmp(str, "off") == 0)
|
||||||
|
jit::js_JitOptions.disableSink = true;
|
||||||
|
else
|
||||||
|
return OptionFailure("ion-sink", str);
|
||||||
|
}
|
||||||
|
|
||||||
if (const char *str = op.getStringOption("ion-loop-unrolling")) {
|
if (const char *str = op.getStringOption("ion-loop-unrolling")) {
|
||||||
if (strcmp(str, "on") == 0)
|
if (strcmp(str, "on") == 0)
|
||||||
jit::js_JitOptions.disableLoopUnrolling = false;
|
jit::js_JitOptions.disableLoopUnrolling = false;
|
||||||
@ -5797,6 +5806,8 @@ main(int argc, char **argv, char **envp)
|
|||||||
"Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
|
"Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
|
||||||
|| !op.addStringOption('\0', "ion-range-analysis", "on/off",
|
|| !op.addStringOption('\0', "ion-range-analysis", "on/off",
|
||||||
"Range analysis (default: on, off to disable)")
|
"Range analysis (default: on, off to disable)")
|
||||||
|
|| !op.addStringOption('\0', "ion-sink", "on/off",
|
||||||
|
"Sink code motion (default: on, off to disable)")
|
||||||
|| !op.addStringOption('\0', "ion-loop-unrolling", "on/off",
|
|| !op.addStringOption('\0', "ion-loop-unrolling", "on/off",
|
||||||
"Loop unrolling (default: off, on to enable)")
|
"Loop unrolling (default: off, on to enable)")
|
||||||
|| !op.addBoolOption('\0', "ion-check-range-analysis",
|
|| !op.addBoolOption('\0', "ion-check-range-analysis",
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user