mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to m-i
This commit is contained in:
commit
ac27299b59
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="75071a1117605a7ac79c51025caf6ffcfc4dfa95"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="75071a1117605a7ac79c51025caf6ffcfc4dfa95"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="75071a1117605a7ac79c51025caf6ffcfc4dfa95"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="75071a1117605a7ac79c51025caf6ffcfc4dfa95"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="75071a1117605a7ac79c51025caf6ffcfc4dfa95"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="75071a1117605a7ac79c51025caf6ffcfc4dfa95"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="75071a1117605a7ac79c51025caf6ffcfc4dfa95"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="75071a1117605a7ac79c51025caf6ffcfc4dfa95"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"git": {
|
||||
"git_revision": "c2438f746a3236398735202c0d79fab28cd019ae",
|
||||
"git_revision": "75071a1117605a7ac79c51025caf6ffcfc4dfa95",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "0312e33fddf5c92fad3933fd16f39075d6e6190f",
|
||||
"revision": "22c58f4fec82075c7110bbc59ca7f6aad0a232a8",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="75071a1117605a7ac79c51025caf6ffcfc4dfa95"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="75071a1117605a7ac79c51025caf6ffcfc4dfa95"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -313,7 +313,7 @@ let gFxAccounts = {
|
||||
fxAccounts.getSignedInUser().then(userData => {
|
||||
// userData may be null here when the user is not signed-in, but that's expected
|
||||
updateWithUserData(userData);
|
||||
return fxAccounts.getSignedInUserProfile();
|
||||
return userData ? fxAccounts.getSignedInUserProfile() : null;
|
||||
}).then(profile => {
|
||||
if (!profile) {
|
||||
return;
|
||||
|
@ -318,50 +318,6 @@ let AboutNetErrorListener = {
|
||||
|
||||
AboutNetErrorListener.init(this);
|
||||
|
||||
// An event listener for custom "WebChannelMessageToChrome" events on pages
|
||||
addEventListener("WebChannelMessageToChrome", function (e) {
|
||||
// if target is window then we want the document principal, otherwise fallback to target itself.
|
||||
let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
|
||||
|
||||
if (e.detail) {
|
||||
sendAsyncMessage("WebChannelMessageToChrome", e.detail, { eventTarget: e.target }, principal);
|
||||
} else {
|
||||
Cu.reportError("WebChannel message failed. No message detail.");
|
||||
}
|
||||
}, true, true);
|
||||
|
||||
// Add message listener for "WebChannelMessageToContent" messages from chrome scripts
|
||||
addMessageListener("WebChannelMessageToContent", function (e) {
|
||||
if (e.data) {
|
||||
// e.objects.eventTarget will be defined if sending a response to
|
||||
// a WebChannelMessageToChrome event. An unsolicited send
|
||||
// may not have an eventTarget defined, in this case send to the
|
||||
// main content window.
|
||||
let eventTarget = e.objects.eventTarget || content;
|
||||
|
||||
// if eventTarget is window then we want the document principal,
|
||||
// otherwise use target itself.
|
||||
let targetPrincipal = eventTarget instanceof Ci.nsIDOMWindow ? eventTarget.document.nodePrincipal : eventTarget.nodePrincipal;
|
||||
|
||||
if (e.principal.subsumes(targetPrincipal)) {
|
||||
// if eventTarget is a window, use it as the targetWindow, otherwise
|
||||
// find the window that owns the eventTarget.
|
||||
let targetWindow = eventTarget instanceof Ci.nsIDOMWindow ? eventTarget : eventTarget.ownerDocument.defaultView;
|
||||
|
||||
eventTarget.dispatchEvent(new targetWindow.CustomEvent("WebChannelMessageToContent", {
|
||||
detail: Cu.cloneInto({
|
||||
id: e.data.id,
|
||||
message: e.data.message,
|
||||
}, targetWindow),
|
||||
}));
|
||||
} else {
|
||||
Cu.reportError("WebChannel message failed. Principal mismatch.");
|
||||
}
|
||||
} else {
|
||||
Cu.reportError("WebChannel message failed. No message data.");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let ClickEventHandler = {
|
||||
init: function init() {
|
||||
|
@ -11,6 +11,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebChannel",
|
||||
const HTTP_PATH = "http://example.com";
|
||||
const HTTP_ENDPOINT = "/browser/browser/base/content/test/general/browser_web_channel.html";
|
||||
|
||||
// Keep this synced with /mobile/android/tests/browser/robocop/testWebChannel.js
|
||||
// as much as possible. (We only have that since we can't run browser chrome
|
||||
// tests on Android. Yet?)
|
||||
let gTests = [
|
||||
{
|
||||
desc: "WebChannel generic message",
|
||||
|
@ -1631,8 +1631,12 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete"))
|
||||
return;
|
||||
|
||||
if (this._matchCount > 0 && this.selectedIndex == -1)
|
||||
if (this._matchCount > 0 && this.selectedIndex == -1) {
|
||||
// Don't handle this as a user-initiated action.
|
||||
this._ignoreNextSelect = true;
|
||||
this.selectedIndex = 0;
|
||||
this._ignoreNextSelect = false;
|
||||
}
|
||||
|
||||
this.input.gotResultForCurrentQuery = true;
|
||||
if (this.input.handleEnterWhenGotResult) {
|
||||
@ -1644,6 +1648,18 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
</method>
|
||||
|
||||
</implementation>
|
||||
<handlers>
|
||||
|
||||
<handler event="select"><![CDATA[
|
||||
// When the user selects one of matches, stop the search to avoid
|
||||
// changing the underlying result unexpectedly.
|
||||
if (!this._ignoreNextSelect && this.selectedIndex >= 0) {
|
||||
let controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
|
||||
controller.stopSearch();
|
||||
}
|
||||
]]></handler>
|
||||
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
<binding id="addon-progress-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
// Eslint built-in rules are documented at <http://eslint.org/docs/rules/>
|
||||
"camelcase": 0, // TODO: Remove (use default)
|
||||
"computed-property-spacing": [2, "never"],
|
||||
"consistent-return": 0, // TODO: Remove (use default)
|
||||
dot-location: 0, // [2, property],
|
||||
"eqeqeq": 0, // TBD. Might need to be separate for content & chrome
|
||||
@ -52,11 +53,14 @@
|
||||
"no-redeclare": 0, // TODO: Remove (use default)
|
||||
"no-return-assign": 0, // TODO: Remove (use default)
|
||||
"no-underscore-dangle": 0, // Leave as 0. Commonly used for private variables.
|
||||
"no-unexpected-multiline": 2,
|
||||
"no-unneeded-ternary": 2,
|
||||
"no-unused-expressions": 0, // TODO: Remove (use default)
|
||||
"no-unused-vars": 0, // TODO: Remove (use default)
|
||||
"no-use-before-define": 0, // TODO: Remove (use default)
|
||||
"object-curly-spacing": 0, // [2, "always"],
|
||||
"quotes": [2, "double", "avoid-escape"],
|
||||
"spaced-comment": [2, "always"],
|
||||
"strict": 0, // [2, "function"],
|
||||
// eslint-plugin-react rules. These are documented at
|
||||
// <https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules>
|
||||
|
@ -129,7 +129,9 @@ loop.contacts = (function(_, mozL10n) {
|
||||
});
|
||||
return (
|
||||
React.createElement("div", {className: "contacts-gravatar-promo"},
|
||||
React.createElement(Button, {additionalClass: "button-close", onClick: this.handleCloseButtonClick}),
|
||||
React.createElement(Button, {additionalClass: "button-close",
|
||||
caption: "",
|
||||
onClick: this.handleCloseButtonClick}),
|
||||
React.createElement("p", {dangerouslySetInnerHTML: {__html: message},
|
||||
onClick: this.handleLinkClick}),
|
||||
React.createElement(ButtonGroup, null,
|
||||
|
@ -129,7 +129,9 @@ loop.contacts = (function(_, mozL10n) {
|
||||
});
|
||||
return (
|
||||
<div className="contacts-gravatar-promo">
|
||||
<Button additionalClass="button-close" onClick={this.handleCloseButtonClick}/>
|
||||
<Button additionalClass="button-close"
|
||||
caption=""
|
||||
onClick={this.handleCloseButtonClick} />
|
||||
<p dangerouslySetInnerHTML={{__html: message}}
|
||||
onClick={this.handleLinkClick}></p>
|
||||
<ButtonGroup>
|
||||
|
@ -659,6 +659,7 @@ loop.conversationViews = (function(mozL10n) {
|
||||
React.createElement("div", {className: "video_wrapper remote_wrapper"},
|
||||
React.createElement("div", {className: "video_inner remote focus-stream"},
|
||||
React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(),
|
||||
isLoading: false,
|
||||
mediaType: "remote",
|
||||
posterUrl: this.props.remotePosterUrl,
|
||||
srcVideoObject: this.state.remoteSrcVideoObject})
|
||||
@ -666,6 +667,7 @@ loop.conversationViews = (function(mozL10n) {
|
||||
),
|
||||
React.createElement("div", {className: localStreamClasses},
|
||||
React.createElement(sharedViews.MediaView, {displayAvatar: !this.props.video.enabled,
|
||||
isLoading: false,
|
||||
mediaType: "local",
|
||||
posterUrl: this.props.localPosterUrl,
|
||||
srcVideoObject: this.state.localSrcVideoObject})
|
||||
@ -674,6 +676,7 @@ loop.conversationViews = (function(mozL10n) {
|
||||
React.createElement(loop.shared.views.ConversationToolbar, {
|
||||
audio: this.props.audio,
|
||||
dispatcher: this.props.dispatcher,
|
||||
edit: { visible: false, enabled: false},
|
||||
hangup: this.hangup,
|
||||
publishStream: this.publishStream,
|
||||
video: this.props.video})
|
||||
|
@ -659,6 +659,7 @@ loop.conversationViews = (function(mozL10n) {
|
||||
<div className="video_wrapper remote_wrapper">
|
||||
<div className="video_inner remote focus-stream">
|
||||
<sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
|
||||
isLoading={false}
|
||||
mediaType="remote"
|
||||
posterUrl={this.props.remotePosterUrl}
|
||||
srcVideoObject={this.state.remoteSrcVideoObject} />
|
||||
@ -666,6 +667,7 @@ loop.conversationViews = (function(mozL10n) {
|
||||
</div>
|
||||
<div className={localStreamClasses}>
|
||||
<sharedViews.MediaView displayAvatar={!this.props.video.enabled}
|
||||
isLoading={false}
|
||||
mediaType="local"
|
||||
posterUrl={this.props.localPosterUrl}
|
||||
srcVideoObject={this.state.localSrcVideoObject} />
|
||||
@ -674,6 +676,7 @@ loop.conversationViews = (function(mozL10n) {
|
||||
<loop.shared.views.ConversationToolbar
|
||||
audio={this.props.audio}
|
||||
dispatcher={this.props.dispatcher}
|
||||
edit={{ visible: false, enabled: false }}
|
||||
hangup={this.hangup}
|
||||
publishStream={this.publishStream}
|
||||
video={this.props.video} />
|
||||
|
@ -153,18 +153,19 @@ loop.roomViews = (function(mozL10n) {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
error: React.PropTypes.object,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
onAddContextClick: React.PropTypes.func,
|
||||
onEditContextClose: React.PropTypes.func,
|
||||
// This data is supplied by the activeRoomStore.
|
||||
roomData: React.PropTypes.object.isRequired,
|
||||
savingContext: React.PropTypes.bool,
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
showContext: React.PropTypes.bool.isRequired,
|
||||
showEditContext: React.PropTypes.bool.isRequired,
|
||||
socialShareProviders: React.PropTypes.array
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
copiedUrl: false,
|
||||
editMode: false,
|
||||
newRoomName: ""
|
||||
};
|
||||
},
|
||||
@ -207,11 +208,15 @@ loop.roomViews = (function(mozL10n) {
|
||||
handleAddContextClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.handleEditModeChange(true);
|
||||
if (this.props.onAddContextClick) {
|
||||
this.props.onAddContextClick();
|
||||
}
|
||||
},
|
||||
|
||||
handleEditModeChange: function(newEditMode) {
|
||||
this.setState({ editMode: newEditMode });
|
||||
handleEditContextClose: function() {
|
||||
if (this.props.onEditContextClose) {
|
||||
this.props.onEditContextClose();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -220,13 +225,16 @@ loop.roomViews = (function(mozL10n) {
|
||||
}
|
||||
|
||||
var canAddContext = this.props.mozLoop.getLoopPref("contextInConversations.enabled") &&
|
||||
!this.props.showContext && !this.state.editMode;
|
||||
// Don't show the link when we're showing the edit form already:
|
||||
!this.props.showEditContext &&
|
||||
// Don't show the link when there's already context data available:
|
||||
!(this.props.roomData.roomContextUrls || this.props.roomData.roomDescription);
|
||||
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
React.createElement("div", {className: "room-invitation-overlay"},
|
||||
React.createElement("div", {className: "room-invitation-content"},
|
||||
React.createElement("p", {className: cx({hide: this.state.editMode})},
|
||||
React.createElement("p", {className: cx({hide: this.props.showEditContext})},
|
||||
mozL10n.get("invite_header_text")
|
||||
),
|
||||
React.createElement("a", {className: cx({hide: !canAddContext, "room-invitation-addcontext": true}),
|
||||
@ -237,7 +245,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
React.createElement("div", {className: cx({
|
||||
"btn-group": true,
|
||||
"call-action-group": true,
|
||||
hide: this.state.editMode
|
||||
hide: this.props.showEditContext
|
||||
})},
|
||||
React.createElement("button", {className: "btn btn-info btn-email",
|
||||
onClick: this.handleEmailButtonClick},
|
||||
@ -260,62 +268,45 @@ loop.roomViews = (function(mozL10n) {
|
||||
roomUrl: this.props.roomData.roomUrl,
|
||||
show: this.state.showMenu,
|
||||
socialShareProviders: this.props.socialShareProviders}),
|
||||
React.createElement(DesktopRoomContextView, {
|
||||
React.createElement(DesktopRoomEditContextView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
editMode: this.state.editMode,
|
||||
error: this.props.error,
|
||||
mozLoop: this.props.mozLoop,
|
||||
onEditModeChange: this.handleEditModeChange,
|
||||
onClose: this.handleEditContextClose,
|
||||
roomData: this.props.roomData,
|
||||
savingContext: this.props.savingContext,
|
||||
show: this.props.showContext || this.state.editMode})
|
||||
show: this.props.showEditContext})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var DesktopRoomContextView = React.createClass({displayName: "DesktopRoomContextView",
|
||||
var DesktopRoomEditContextView = React.createClass({displayName: "DesktopRoomEditContextView",
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
propTypes: {
|
||||
// Only used for tests.
|
||||
availableContext: React.PropTypes.object,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
editMode: React.PropTypes.bool,
|
||||
error: React.PropTypes.object,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
onEditModeChange: React.PropTypes.func,
|
||||
onClose: React.PropTypes.func,
|
||||
// This data is supplied by the activeRoomStore.
|
||||
roomData: React.PropTypes.object.isRequired,
|
||||
savingContext: React.PropTypes.bool.isRequired,
|
||||
show: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._fetchMetadata();
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
var newState = {};
|
||||
// When the 'show' prop is changed from outside this component, we do need
|
||||
// to update the state.
|
||||
if (("show" in nextProps) && nextProps.show !== this.props.show) {
|
||||
newState.show = nextProps.show;
|
||||
}
|
||||
if (("editMode" in nextProps && nextProps.editMode !== this.props.editMode)) {
|
||||
newState.editMode = nextProps.editMode;
|
||||
// If we're switching to edit mode, fetch the metadata of the current tab.
|
||||
// But _only_ if there's no context currently attached to the room; the
|
||||
// checkbox will be disabled in that case.
|
||||
if (nextProps.editMode) {
|
||||
this.props.mozLoop.getSelectedTabMetadata(function(metadata) {
|
||||
var previewImage = metadata.favicon || "";
|
||||
var description = metadata.title || metadata.description;
|
||||
var metaUrl = metadata.url;
|
||||
this.setState({
|
||||
availableContext: {
|
||||
previewImage: previewImage,
|
||||
description: description,
|
||||
url: metaUrl
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
if (nextProps.show) {
|
||||
this._fetchMetadata();
|
||||
}
|
||||
}
|
||||
// When we receive an update for the `roomData` property, make sure that
|
||||
@ -341,11 +332,15 @@ loop.roomViews = (function(mozL10n) {
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we do not show the edit-mode when we just successfully saved
|
||||
// context.
|
||||
if (this.props.savingContext && nextProps.savingContext !== this.props.savingContext &&
|
||||
!nextProps.error && this.state.editMode) {
|
||||
newState.editMode = false;
|
||||
// Feature support: when a context save completed without error, we can
|
||||
// close the context edit form.
|
||||
if (("savingContext" in nextProps) && this.props.savingContext &&
|
||||
this.props.savingContext !== nextProps.savingContext && this.state.show
|
||||
&& !this.props.error && !nextProps.error) {
|
||||
newState.show = false;
|
||||
if (this.props.onClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.getOwnPropertyNames(newState).length) {
|
||||
@ -353,16 +348,11 @@ loop.roomViews = (function(mozL10n) {
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return { editMode: false };
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var url = this._getURL();
|
||||
return {
|
||||
// `availableContext` prop only used in tests.
|
||||
availableContext: this.props.availableContext,
|
||||
editMode: this.props.editMode,
|
||||
availableContext: null,
|
||||
show: this.props.show,
|
||||
newRoomName: this.props.roomData.roomName || "",
|
||||
newRoomURL: url && url.location || "",
|
||||
@ -371,17 +361,29 @@ loop.roomViews = (function(mozL10n) {
|
||||
};
|
||||
},
|
||||
|
||||
_fetchMetadata: function() {
|
||||
this.props.mozLoop.getSelectedTabMetadata(function(metadata) {
|
||||
var previewImage = metadata.favicon || "";
|
||||
var description = metadata.title || metadata.description;
|
||||
var metaUrl = metadata.url;
|
||||
this.setState({
|
||||
availableContext: {
|
||||
previewImage: previewImage,
|
||||
description: description,
|
||||
url: metaUrl
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
handleCloseClick: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (this.state.editMode) {
|
||||
this.setState({ editMode: false });
|
||||
if (this.props.onEditModeChange) {
|
||||
this.props.onEditModeChange(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.setState({ show: false });
|
||||
if (this.props.onClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
},
|
||||
|
||||
handleContextClick: function(event) {
|
||||
@ -396,15 +398,6 @@ loop.roomViews = (function(mozL10n) {
|
||||
this.props.mozLoop.openURL(url.location);
|
||||
},
|
||||
|
||||
handleEditClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.setState({ editMode: true });
|
||||
if (this.props.onEditModeChange) {
|
||||
this.props.onEditModeChange(true);
|
||||
}
|
||||
},
|
||||
|
||||
handleCheckboxChange: function(state) {
|
||||
if (state.checked) {
|
||||
// The checkbox was checked, prefill the fields with the values available
|
||||
@ -478,7 +471,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.show && !this.state.editMode) {
|
||||
if (!this.state.show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -486,93 +479,55 @@ loop.roomViews = (function(mozL10n) {
|
||||
var thumbnail = url && url.thumbnail || "loop/shared/img/icons-16x16.svg#globe";
|
||||
var urlDescription = url && url.description || "";
|
||||
var location = url && url.location || "";
|
||||
var locationData = null;
|
||||
if (location) {
|
||||
locationData = checkboxLabel = sharedUtils.formatURL(location);
|
||||
}
|
||||
if (!checkboxLabel) {
|
||||
try {
|
||||
checkboxLabel = sharedUtils.formatURL((this.state.availableContext ?
|
||||
this.state.availableContext.url : ""));
|
||||
} catch (ex) {}
|
||||
}
|
||||
|
||||
var cx = React.addons.classSet;
|
||||
if (this.state.editMode) {
|
||||
var availableContext = this.state.availableContext;
|
||||
// The checkbox shows as checked when there's already context data
|
||||
// attached to this room.
|
||||
var checked = !!urlDescription;
|
||||
var checkboxLabel = urlDescription || (availableContext && availableContext.url ?
|
||||
availableContext.description : "");
|
||||
|
||||
return (
|
||||
React.createElement("div", {className: "room-context editMode"},
|
||||
React.createElement("p", {className: cx({"error": !!this.props.error,
|
||||
"error-display-area": true})},
|
||||
mozL10n.get("rooms_change_failed_label")
|
||||
),
|
||||
React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")),
|
||||
React.createElement(sharedViews.Checkbox, {
|
||||
additionalClass: cx({ hide: !checkboxLabel }),
|
||||
checked: checked,
|
||||
disabled: checked,
|
||||
label: checkboxLabel,
|
||||
onChange: this.handleCheckboxChange,
|
||||
value: location}),
|
||||
React.createElement("form", {onSubmit: this.handleFormSubmit},
|
||||
React.createElement("input", {className: "room-context-name",
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: mozL10n.get("context_edit_name_placeholder"),
|
||||
type: "text",
|
||||
valueLink: this.linkState("newRoomName")}),
|
||||
React.createElement("input", {className: "room-context-url",
|
||||
disabled: availableContext && availableContext.url === this.state.newRoomURL,
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: "https://",
|
||||
type: "text",
|
||||
valueLink: this.linkState("newRoomURL")}),
|
||||
React.createElement("textarea", {className: "room-context-comments",
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: mozL10n.get("context_edit_comments_placeholder"),
|
||||
rows: "3", type: "text",
|
||||
valueLink: this.linkState("newRoomDescription")})
|
||||
),
|
||||
React.createElement("button", {className: "btn btn-info",
|
||||
disabled: this.props.savingContext,
|
||||
onClick: this.handleFormSubmit},
|
||||
mozL10n.get("context_save_label2")
|
||||
),
|
||||
React.createElement("button", {className: "room-context-btn-close",
|
||||
onClick: this.handleCloseClick,
|
||||
title: mozL10n.get("cancel_button")})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!locationData) {
|
||||
return null;
|
||||
}
|
||||
var availableContext = this.state.availableContext;
|
||||
// The checkbox shows as checked when there's already context data
|
||||
// attached to this room.
|
||||
var checked = !!urlDescription;
|
||||
var checkboxLabel = urlDescription || (availableContext && availableContext.url ?
|
||||
availableContext.description : "");
|
||||
|
||||
return (
|
||||
React.createElement("div", {className: "room-context"},
|
||||
React.createElement("p", {className: cx({"error": !!this.props.error,
|
||||
"error-display-area": true})},
|
||||
mozL10n.get("rooms_change_failed_label")
|
||||
),
|
||||
React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")),
|
||||
React.createElement("div", {className: "room-context-content",
|
||||
onClick: this.handleContextClick},
|
||||
React.createElement("img", {className: "room-context-thumbnail", src: thumbnail}),
|
||||
React.createElement("div", {className: "room-context-description",
|
||||
title: urlDescription},
|
||||
this._truncate(urlDescription),
|
||||
React.createElement("a", {className: "room-context-url",
|
||||
title: locationData.location}, locationData.hostname)
|
||||
)
|
||||
React.createElement(sharedViews.Checkbox, {
|
||||
additionalClass: cx({ hide: !checkboxLabel }),
|
||||
checked: checked,
|
||||
disabled: checked,
|
||||
label: checkboxLabel,
|
||||
onChange: this.handleCheckboxChange,
|
||||
value: location}),
|
||||
React.createElement("form", {onSubmit: this.handleFormSubmit},
|
||||
React.createElement("input", {className: "room-context-name",
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: mozL10n.get("context_edit_name_placeholder"),
|
||||
type: "text",
|
||||
valueLink: this.linkState("newRoomName")}),
|
||||
React.createElement("input", {className: "room-context-url",
|
||||
disabled: availableContext && availableContext.url === this.state.newRoomURL,
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: "https://",
|
||||
type: "text",
|
||||
valueLink: this.linkState("newRoomURL")}),
|
||||
React.createElement("textarea", {className: "room-context-comments",
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: mozL10n.get("context_edit_comments_placeholder"),
|
||||
rows: "3", type: "text",
|
||||
valueLink: this.linkState("newRoomDescription")})
|
||||
),
|
||||
React.createElement("button", {className: "btn btn-info",
|
||||
disabled: this.props.savingContext,
|
||||
onClick: this.handleFormSubmit},
|
||||
mozL10n.get("context_save_label2")
|
||||
),
|
||||
React.createElement("button", {className: "room-context-btn-close",
|
||||
onClick: this.handleCloseClick,
|
||||
title: mozL10n.get("context_hide_tooltip")}),
|
||||
React.createElement("button", {className: "room-context-btn-edit",
|
||||
onClick: this.handleEditClick,
|
||||
title: mozL10n.get("context_edit_tooltip")})
|
||||
title: mozL10n.get("cancel_button")})
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -595,7 +550,15 @@ loop.roomViews = (function(mozL10n) {
|
||||
// The poster URLs are for UI-showcase testing and development.
|
||||
localPosterUrl: React.PropTypes.string,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
remotePosterUrl: React.PropTypes.string
|
||||
remotePosterUrl: React.PropTypes.string,
|
||||
roomStore: React.PropTypes.instanceOf(loop.store.RoomStore).isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
contextEnabled: this.props.mozLoop.getLoopPref("contextInConversations.enabled"),
|
||||
showEditContext: false
|
||||
};
|
||||
},
|
||||
|
||||
componentWillUpdate: function(nextProps, nextState) {
|
||||
@ -641,13 +604,6 @@ loop.roomViews = (function(mozL10n) {
|
||||
return (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS);
|
||||
},
|
||||
|
||||
_shouldRenderContextView: function() {
|
||||
return !!(
|
||||
this.props.mozLoop.getLoopPref("contextInConversations.enabled") &&
|
||||
(this.state.roomContextUrls || this.state.roomDescription)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Works out if remote video should be rended or not, depending on the
|
||||
* room state and other flags.
|
||||
@ -712,9 +668,21 @@ loop.roomViews = (function(mozL10n) {
|
||||
* @private
|
||||
*/
|
||||
_shouldRenderRemoteLoading: function() {
|
||||
return this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
|
||||
!this.state.remoteSrcVideoObject &&
|
||||
!this.state.mediaConnected;
|
||||
return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
|
||||
!this.state.remoteSrcVideoObject &&
|
||||
!this.state.mediaConnected);
|
||||
},
|
||||
|
||||
handleAddContextClick: function() {
|
||||
this.setState({ showEditContext: true });
|
||||
},
|
||||
|
||||
handleEditContextClick: function() {
|
||||
this.setState({ showEditContext: !this.state.showEditContext });
|
||||
},
|
||||
|
||||
handleEditContextClose: function() {
|
||||
this.setState({ showEditContext: false });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -730,12 +698,12 @@ loop.roomViews = (function(mozL10n) {
|
||||
});
|
||||
|
||||
var screenShareData = {
|
||||
state: this.state.screenSharingState,
|
||||
state: this.state.screenSharingState || SCREEN_SHARE_STATES.INACTIVE,
|
||||
visible: true
|
||||
};
|
||||
|
||||
var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay();
|
||||
var shouldRenderContextView = this._shouldRenderContextView();
|
||||
var shouldRenderEditContextView = this.state.contextEnabled && this.state.showEditContext;
|
||||
var roomData = this.props.roomStore.getStoreState("activeRoom");
|
||||
|
||||
switch(this.state.roomState) {
|
||||
@ -759,18 +727,20 @@ loop.roomViews = (function(mozL10n) {
|
||||
|
||||
return (
|
||||
React.createElement("div", {className: "room-conversation-wrapper"},
|
||||
React.createElement(DesktopRoomInvitationView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
error: this.state.error,
|
||||
mozLoop: this.props.mozLoop,
|
||||
roomData: roomData,
|
||||
savingContext: this.state.savingContext,
|
||||
show: shouldRenderInvitationOverlay,
|
||||
showContext: shouldRenderContextView,
|
||||
socialShareProviders: this.state.socialShareProviders}),
|
||||
React.createElement("div", {className: "video-layout-wrapper"},
|
||||
React.createElement("div", {className: "conversation room-conversation"},
|
||||
React.createElement("div", {className: "media nested"},
|
||||
React.createElement(DesktopRoomInvitationView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
error: this.state.error,
|
||||
mozLoop: this.props.mozLoop,
|
||||
onAddContextClick: this.handleAddContextClick,
|
||||
onEditContextClose: this.handleEditContextClose,
|
||||
roomData: roomData,
|
||||
savingContext: this.state.savingContext,
|
||||
show: shouldRenderInvitationOverlay,
|
||||
showEditContext: shouldRenderInvitationOverlay && shouldRenderEditContextView,
|
||||
socialShareProviders: this.state.socialShareProviders}),
|
||||
React.createElement("div", {className: "video_wrapper remote_wrapper"},
|
||||
React.createElement("div", {className: "video_inner remote focus-stream"},
|
||||
React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(),
|
||||
@ -791,23 +761,27 @@ loop.roomViews = (function(mozL10n) {
|
||||
React.createElement(sharedViews.ConversationToolbar, {
|
||||
audio: {enabled: !this.state.audioMuted, visible: true},
|
||||
dispatcher: this.props.dispatcher,
|
||||
edit: { visible: this.state.contextEnabled, enabled: !this.state.showEditContext},
|
||||
hangup: this.leaveRoom,
|
||||
onEditClick: this.handleEditContextClick,
|
||||
publishStream: this.publishStream,
|
||||
screenShare: screenShareData,
|
||||
video: {enabled: !this.state.videoMuted, visible: true}})
|
||||
)
|
||||
),
|
||||
React.createElement(DesktopRoomContextView, {
|
||||
React.createElement(DesktopRoomEditContextView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
error: this.state.error,
|
||||
mozLoop: this.props.mozLoop,
|
||||
onClose: this.handleEditContextClose,
|
||||
roomData: roomData,
|
||||
savingContext: this.state.savingContext,
|
||||
show: !shouldRenderInvitationOverlay && shouldRenderContextView}),
|
||||
show: !shouldRenderInvitationOverlay && shouldRenderEditContextView}),
|
||||
React.createElement(sharedViews.chat.TextChatView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
showAlways: false,
|
||||
showRoomName: false})
|
||||
showRoomName: false,
|
||||
useDesktopPaths: true})
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -818,7 +792,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
return {
|
||||
ActiveRoomStoreMixin: ActiveRoomStoreMixin,
|
||||
SocialShareDropdown: SocialShareDropdown,
|
||||
DesktopRoomContextView: DesktopRoomContextView,
|
||||
DesktopRoomEditContextView: DesktopRoomEditContextView,
|
||||
DesktopRoomConversationView: DesktopRoomConversationView,
|
||||
DesktopRoomInvitationView: DesktopRoomInvitationView
|
||||
};
|
||||
|
@ -153,18 +153,19 @@ loop.roomViews = (function(mozL10n) {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
error: React.PropTypes.object,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
onAddContextClick: React.PropTypes.func,
|
||||
onEditContextClose: React.PropTypes.func,
|
||||
// This data is supplied by the activeRoomStore.
|
||||
roomData: React.PropTypes.object.isRequired,
|
||||
savingContext: React.PropTypes.bool,
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
showContext: React.PropTypes.bool.isRequired,
|
||||
showEditContext: React.PropTypes.bool.isRequired,
|
||||
socialShareProviders: React.PropTypes.array
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
copiedUrl: false,
|
||||
editMode: false,
|
||||
newRoomName: ""
|
||||
};
|
||||
},
|
||||
@ -207,11 +208,15 @@ loop.roomViews = (function(mozL10n) {
|
||||
handleAddContextClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.handleEditModeChange(true);
|
||||
if (this.props.onAddContextClick) {
|
||||
this.props.onAddContextClick();
|
||||
}
|
||||
},
|
||||
|
||||
handleEditModeChange: function(newEditMode) {
|
||||
this.setState({ editMode: newEditMode });
|
||||
handleEditContextClose: function() {
|
||||
if (this.props.onEditContextClose) {
|
||||
this.props.onEditContextClose();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -220,13 +225,16 @@ loop.roomViews = (function(mozL10n) {
|
||||
}
|
||||
|
||||
var canAddContext = this.props.mozLoop.getLoopPref("contextInConversations.enabled") &&
|
||||
!this.props.showContext && !this.state.editMode;
|
||||
// Don't show the link when we're showing the edit form already:
|
||||
!this.props.showEditContext &&
|
||||
// Don't show the link when there's already context data available:
|
||||
!(this.props.roomData.roomContextUrls || this.props.roomData.roomDescription);
|
||||
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
<div className="room-invitation-overlay">
|
||||
<div className="room-invitation-content">
|
||||
<p className={cx({hide: this.state.editMode})}>
|
||||
<p className={cx({hide: this.props.showEditContext})}>
|
||||
{mozL10n.get("invite_header_text")}
|
||||
</p>
|
||||
<a className={cx({hide: !canAddContext, "room-invitation-addcontext": true})}
|
||||
@ -237,7 +245,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
<div className={cx({
|
||||
"btn-group": true,
|
||||
"call-action-group": true,
|
||||
hide: this.state.editMode
|
||||
hide: this.props.showEditContext
|
||||
})}>
|
||||
<button className="btn btn-info btn-email"
|
||||
onClick={this.handleEmailButtonClick}>
|
||||
@ -260,62 +268,45 @@ loop.roomViews = (function(mozL10n) {
|
||||
roomUrl={this.props.roomData.roomUrl}
|
||||
show={this.state.showMenu}
|
||||
socialShareProviders={this.props.socialShareProviders} />
|
||||
<DesktopRoomContextView
|
||||
<DesktopRoomEditContextView
|
||||
dispatcher={this.props.dispatcher}
|
||||
editMode={this.state.editMode}
|
||||
error={this.props.error}
|
||||
mozLoop={this.props.mozLoop}
|
||||
onEditModeChange={this.handleEditModeChange}
|
||||
onClose={this.handleEditContextClose}
|
||||
roomData={this.props.roomData}
|
||||
savingContext={this.props.savingContext}
|
||||
show={this.props.showContext || this.state.editMode} />
|
||||
show={this.props.showEditContext} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var DesktopRoomContextView = React.createClass({
|
||||
var DesktopRoomEditContextView = React.createClass({
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
propTypes: {
|
||||
// Only used for tests.
|
||||
availableContext: React.PropTypes.object,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
editMode: React.PropTypes.bool,
|
||||
error: React.PropTypes.object,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
onEditModeChange: React.PropTypes.func,
|
||||
onClose: React.PropTypes.func,
|
||||
// This data is supplied by the activeRoomStore.
|
||||
roomData: React.PropTypes.object.isRequired,
|
||||
savingContext: React.PropTypes.bool.isRequired,
|
||||
show: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._fetchMetadata();
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
var newState = {};
|
||||
// When the 'show' prop is changed from outside this component, we do need
|
||||
// to update the state.
|
||||
if (("show" in nextProps) && nextProps.show !== this.props.show) {
|
||||
newState.show = nextProps.show;
|
||||
}
|
||||
if (("editMode" in nextProps && nextProps.editMode !== this.props.editMode)) {
|
||||
newState.editMode = nextProps.editMode;
|
||||
// If we're switching to edit mode, fetch the metadata of the current tab.
|
||||
// But _only_ if there's no context currently attached to the room; the
|
||||
// checkbox will be disabled in that case.
|
||||
if (nextProps.editMode) {
|
||||
this.props.mozLoop.getSelectedTabMetadata(function(metadata) {
|
||||
var previewImage = metadata.favicon || "";
|
||||
var description = metadata.title || metadata.description;
|
||||
var metaUrl = metadata.url;
|
||||
this.setState({
|
||||
availableContext: {
|
||||
previewImage: previewImage,
|
||||
description: description,
|
||||
url: metaUrl
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
if (nextProps.show) {
|
||||
this._fetchMetadata();
|
||||
}
|
||||
}
|
||||
// When we receive an update for the `roomData` property, make sure that
|
||||
@ -341,11 +332,15 @@ loop.roomViews = (function(mozL10n) {
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we do not show the edit-mode when we just successfully saved
|
||||
// context.
|
||||
if (this.props.savingContext && nextProps.savingContext !== this.props.savingContext &&
|
||||
!nextProps.error && this.state.editMode) {
|
||||
newState.editMode = false;
|
||||
// Feature support: when a context save completed without error, we can
|
||||
// close the context edit form.
|
||||
if (("savingContext" in nextProps) && this.props.savingContext &&
|
||||
this.props.savingContext !== nextProps.savingContext && this.state.show
|
||||
&& !this.props.error && !nextProps.error) {
|
||||
newState.show = false;
|
||||
if (this.props.onClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.getOwnPropertyNames(newState).length) {
|
||||
@ -353,16 +348,11 @@ loop.roomViews = (function(mozL10n) {
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return { editMode: false };
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var url = this._getURL();
|
||||
return {
|
||||
// `availableContext` prop only used in tests.
|
||||
availableContext: this.props.availableContext,
|
||||
editMode: this.props.editMode,
|
||||
availableContext: null,
|
||||
show: this.props.show,
|
||||
newRoomName: this.props.roomData.roomName || "",
|
||||
newRoomURL: url && url.location || "",
|
||||
@ -371,17 +361,29 @@ loop.roomViews = (function(mozL10n) {
|
||||
};
|
||||
},
|
||||
|
||||
_fetchMetadata: function() {
|
||||
this.props.mozLoop.getSelectedTabMetadata(function(metadata) {
|
||||
var previewImage = metadata.favicon || "";
|
||||
var description = metadata.title || metadata.description;
|
||||
var metaUrl = metadata.url;
|
||||
this.setState({
|
||||
availableContext: {
|
||||
previewImage: previewImage,
|
||||
description: description,
|
||||
url: metaUrl
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
handleCloseClick: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (this.state.editMode) {
|
||||
this.setState({ editMode: false });
|
||||
if (this.props.onEditModeChange) {
|
||||
this.props.onEditModeChange(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.setState({ show: false });
|
||||
if (this.props.onClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
},
|
||||
|
||||
handleContextClick: function(event) {
|
||||
@ -396,15 +398,6 @@ loop.roomViews = (function(mozL10n) {
|
||||
this.props.mozLoop.openURL(url.location);
|
||||
},
|
||||
|
||||
handleEditClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.setState({ editMode: true });
|
||||
if (this.props.onEditModeChange) {
|
||||
this.props.onEditModeChange(true);
|
||||
}
|
||||
},
|
||||
|
||||
handleCheckboxChange: function(state) {
|
||||
if (state.checked) {
|
||||
// The checkbox was checked, prefill the fields with the values available
|
||||
@ -478,7 +471,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.show && !this.state.editMode) {
|
||||
if (!this.state.show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -486,93 +479,55 @@ loop.roomViews = (function(mozL10n) {
|
||||
var thumbnail = url && url.thumbnail || "loop/shared/img/icons-16x16.svg#globe";
|
||||
var urlDescription = url && url.description || "";
|
||||
var location = url && url.location || "";
|
||||
var locationData = null;
|
||||
if (location) {
|
||||
locationData = checkboxLabel = sharedUtils.formatURL(location);
|
||||
}
|
||||
if (!checkboxLabel) {
|
||||
try {
|
||||
checkboxLabel = sharedUtils.formatURL((this.state.availableContext ?
|
||||
this.state.availableContext.url : ""));
|
||||
} catch (ex) {}
|
||||
}
|
||||
|
||||
var cx = React.addons.classSet;
|
||||
if (this.state.editMode) {
|
||||
var availableContext = this.state.availableContext;
|
||||
// The checkbox shows as checked when there's already context data
|
||||
// attached to this room.
|
||||
var checked = !!urlDescription;
|
||||
var checkboxLabel = urlDescription || (availableContext && availableContext.url ?
|
||||
availableContext.description : "");
|
||||
|
||||
return (
|
||||
<div className="room-context editMode">
|
||||
<p className={cx({"error": !!this.props.error,
|
||||
"error-display-area": true})}>
|
||||
{mozL10n.get("rooms_change_failed_label")}
|
||||
</p>
|
||||
<div className="room-context-label">{mozL10n.get("context_inroom_label")}</div>
|
||||
<sharedViews.Checkbox
|
||||
additionalClass={cx({ hide: !checkboxLabel })}
|
||||
checked={checked}
|
||||
disabled={checked}
|
||||
label={checkboxLabel}
|
||||
onChange={this.handleCheckboxChange}
|
||||
value={location} />
|
||||
<form onSubmit={this.handleFormSubmit}>
|
||||
<input className="room-context-name"
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder={mozL10n.get("context_edit_name_placeholder")}
|
||||
type="text"
|
||||
valueLink={this.linkState("newRoomName")} />
|
||||
<input className="room-context-url"
|
||||
disabled={availableContext && availableContext.url === this.state.newRoomURL}
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder="https://"
|
||||
type="text"
|
||||
valueLink={this.linkState("newRoomURL")} />
|
||||
<textarea className="room-context-comments"
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder={mozL10n.get("context_edit_comments_placeholder")}
|
||||
rows="3" type="text"
|
||||
valueLink={this.linkState("newRoomDescription")} />
|
||||
</form>
|
||||
<button className="btn btn-info"
|
||||
disabled={this.props.savingContext}
|
||||
onClick={this.handleFormSubmit}>
|
||||
{mozL10n.get("context_save_label2")}
|
||||
</button>
|
||||
<button className="room-context-btn-close"
|
||||
onClick={this.handleCloseClick}
|
||||
title={mozL10n.get("cancel_button")}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!locationData) {
|
||||
return null;
|
||||
}
|
||||
var availableContext = this.state.availableContext;
|
||||
// The checkbox shows as checked when there's already context data
|
||||
// attached to this room.
|
||||
var checked = !!urlDescription;
|
||||
var checkboxLabel = urlDescription || (availableContext && availableContext.url ?
|
||||
availableContext.description : "");
|
||||
|
||||
return (
|
||||
<div className="room-context">
|
||||
<p className={cx({"error": !!this.props.error,
|
||||
"error-display-area": true})}>
|
||||
{mozL10n.get("rooms_change_failed_label")}
|
||||
</p>
|
||||
<div className="room-context-label">{mozL10n.get("context_inroom_label")}</div>
|
||||
<div className="room-context-content"
|
||||
onClick={this.handleContextClick}>
|
||||
<img className="room-context-thumbnail" src={thumbnail} />
|
||||
<div className="room-context-description"
|
||||
title={urlDescription}>
|
||||
{this._truncate(urlDescription)}
|
||||
<a className="room-context-url"
|
||||
title={locationData.location}>{locationData.hostname}</a>
|
||||
</div>
|
||||
</div>
|
||||
<sharedViews.Checkbox
|
||||
additionalClass={cx({ hide: !checkboxLabel })}
|
||||
checked={checked}
|
||||
disabled={checked}
|
||||
label={checkboxLabel}
|
||||
onChange={this.handleCheckboxChange}
|
||||
value={location} />
|
||||
<form onSubmit={this.handleFormSubmit}>
|
||||
<input className="room-context-name"
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder={mozL10n.get("context_edit_name_placeholder")}
|
||||
type="text"
|
||||
valueLink={this.linkState("newRoomName")} />
|
||||
<input className="room-context-url"
|
||||
disabled={availableContext && availableContext.url === this.state.newRoomURL}
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder="https://"
|
||||
type="text"
|
||||
valueLink={this.linkState("newRoomURL")} />
|
||||
<textarea className="room-context-comments"
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder={mozL10n.get("context_edit_comments_placeholder")}
|
||||
rows="3" type="text"
|
||||
valueLink={this.linkState("newRoomDescription")} />
|
||||
</form>
|
||||
<button className="btn btn-info"
|
||||
disabled={this.props.savingContext}
|
||||
onClick={this.handleFormSubmit}>
|
||||
{mozL10n.get("context_save_label2")}
|
||||
</button>
|
||||
<button className="room-context-btn-close"
|
||||
onClick={this.handleCloseClick}
|
||||
title={mozL10n.get("context_hide_tooltip")}/>
|
||||
<button className="room-context-btn-edit"
|
||||
onClick={this.handleEditClick}
|
||||
title={mozL10n.get("context_edit_tooltip")}/>
|
||||
title={mozL10n.get("cancel_button")}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -595,7 +550,15 @@ loop.roomViews = (function(mozL10n) {
|
||||
// The poster URLs are for UI-showcase testing and development.
|
||||
localPosterUrl: React.PropTypes.string,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
remotePosterUrl: React.PropTypes.string
|
||||
remotePosterUrl: React.PropTypes.string,
|
||||
roomStore: React.PropTypes.instanceOf(loop.store.RoomStore).isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
contextEnabled: this.props.mozLoop.getLoopPref("contextInConversations.enabled"),
|
||||
showEditContext: false
|
||||
};
|
||||
},
|
||||
|
||||
componentWillUpdate: function(nextProps, nextState) {
|
||||
@ -641,13 +604,6 @@ loop.roomViews = (function(mozL10n) {
|
||||
return (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS);
|
||||
},
|
||||
|
||||
_shouldRenderContextView: function() {
|
||||
return !!(
|
||||
this.props.mozLoop.getLoopPref("contextInConversations.enabled") &&
|
||||
(this.state.roomContextUrls || this.state.roomDescription)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Works out if remote video should be rended or not, depending on the
|
||||
* room state and other flags.
|
||||
@ -712,9 +668,21 @@ loop.roomViews = (function(mozL10n) {
|
||||
* @private
|
||||
*/
|
||||
_shouldRenderRemoteLoading: function() {
|
||||
return this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
|
||||
!this.state.remoteSrcVideoObject &&
|
||||
!this.state.mediaConnected;
|
||||
return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
|
||||
!this.state.remoteSrcVideoObject &&
|
||||
!this.state.mediaConnected);
|
||||
},
|
||||
|
||||
handleAddContextClick: function() {
|
||||
this.setState({ showEditContext: true });
|
||||
},
|
||||
|
||||
handleEditContextClick: function() {
|
||||
this.setState({ showEditContext: !this.state.showEditContext });
|
||||
},
|
||||
|
||||
handleEditContextClose: function() {
|
||||
this.setState({ showEditContext: false });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -730,12 +698,12 @@ loop.roomViews = (function(mozL10n) {
|
||||
});
|
||||
|
||||
var screenShareData = {
|
||||
state: this.state.screenSharingState,
|
||||
state: this.state.screenSharingState || SCREEN_SHARE_STATES.INACTIVE,
|
||||
visible: true
|
||||
};
|
||||
|
||||
var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay();
|
||||
var shouldRenderContextView = this._shouldRenderContextView();
|
||||
var shouldRenderEditContextView = this.state.contextEnabled && this.state.showEditContext;
|
||||
var roomData = this.props.roomStore.getStoreState("activeRoom");
|
||||
|
||||
switch(this.state.roomState) {
|
||||
@ -759,18 +727,20 @@ loop.roomViews = (function(mozL10n) {
|
||||
|
||||
return (
|
||||
<div className="room-conversation-wrapper">
|
||||
<DesktopRoomInvitationView
|
||||
dispatcher={this.props.dispatcher}
|
||||
error={this.state.error}
|
||||
mozLoop={this.props.mozLoop}
|
||||
roomData={roomData}
|
||||
savingContext={this.state.savingContext}
|
||||
show={shouldRenderInvitationOverlay}
|
||||
showContext={shouldRenderContextView}
|
||||
socialShareProviders={this.state.socialShareProviders} />
|
||||
<div className="video-layout-wrapper">
|
||||
<div className="conversation room-conversation">
|
||||
<div className="media nested">
|
||||
<DesktopRoomInvitationView
|
||||
dispatcher={this.props.dispatcher}
|
||||
error={this.state.error}
|
||||
mozLoop={this.props.mozLoop}
|
||||
onAddContextClick={this.handleAddContextClick}
|
||||
onEditContextClose={this.handleEditContextClose}
|
||||
roomData={roomData}
|
||||
savingContext={this.state.savingContext}
|
||||
show={shouldRenderInvitationOverlay}
|
||||
showEditContext={shouldRenderInvitationOverlay && shouldRenderEditContextView}
|
||||
socialShareProviders={this.state.socialShareProviders} />
|
||||
<div className="video_wrapper remote_wrapper">
|
||||
<div className="video_inner remote focus-stream">
|
||||
<sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
|
||||
@ -791,23 +761,27 @@ loop.roomViews = (function(mozL10n) {
|
||||
<sharedViews.ConversationToolbar
|
||||
audio={{enabled: !this.state.audioMuted, visible: true}}
|
||||
dispatcher={this.props.dispatcher}
|
||||
edit={{ visible: this.state.contextEnabled, enabled: !this.state.showEditContext }}
|
||||
hangup={this.leaveRoom}
|
||||
onEditClick={this.handleEditContextClick}
|
||||
publishStream={this.publishStream}
|
||||
screenShare={screenShareData}
|
||||
video={{enabled: !this.state.videoMuted, visible: true}} />
|
||||
</div>
|
||||
</div>
|
||||
<DesktopRoomContextView
|
||||
<DesktopRoomEditContextView
|
||||
dispatcher={this.props.dispatcher}
|
||||
error={this.state.error}
|
||||
mozLoop={this.props.mozLoop}
|
||||
onClose={this.handleEditContextClose}
|
||||
roomData={roomData}
|
||||
savingContext={this.state.savingContext}
|
||||
show={!shouldRenderInvitationOverlay && shouldRenderContextView} />
|
||||
show={!shouldRenderInvitationOverlay && shouldRenderEditContextView} />
|
||||
<sharedViews.chat.TextChatView
|
||||
dispatcher={this.props.dispatcher}
|
||||
showAlways={false}
|
||||
showRoomName={false} />
|
||||
showRoomName={false}
|
||||
useDesktopPaths={true} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -818,7 +792,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
return {
|
||||
ActiveRoomStoreMixin: ActiveRoomStoreMixin,
|
||||
SocialShareDropdown: SocialShareDropdown,
|
||||
DesktopRoomContextView: DesktopRoomContextView,
|
||||
DesktopRoomEditContextView: DesktopRoomEditContextView,
|
||||
DesktopRoomConversationView: DesktopRoomConversationView,
|
||||
DesktopRoomInvitationView: DesktopRoomInvitationView
|
||||
};
|
||||
|
@ -54,7 +54,7 @@
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.btn-screen-share-entry {
|
||||
.btn-edit-entry {
|
||||
float: right !important;
|
||||
border-left: 1px solid #5a5a5a;
|
||||
}
|
||||
@ -210,10 +210,15 @@
|
||||
}
|
||||
|
||||
/* Screen share button */
|
||||
.btn-mute-edit,
|
||||
.btn-screen-share {
|
||||
position: relative;
|
||||
background-image: url(../img/icons-16x16.svg#screen-white);
|
||||
background-image: url(../img/icons-10x10.svg#edit-white);
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.btn-screen-share {
|
||||
background-image: url(../img/icons-16x16.svg#screen-white);
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
@ -225,6 +230,10 @@
|
||||
.btn-screen-share.active {
|
||||
background-image: url(../img/icons-16x16.svg#screenmute-white);
|
||||
background-color: #6CB23E;
|
||||
}
|
||||
|
||||
.btn-mute-edit.muted,
|
||||
.btn-screen-share.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@ -930,7 +939,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
||||
}
|
||||
|
||||
.room-context {
|
||||
background: rgba(0,0,0,.6);
|
||||
background: rgba(0,0,0,.8);
|
||||
border-top: 2px solid #444;
|
||||
border-bottom: 2px solid #444;
|
||||
padding: .5rem;
|
||||
@ -939,6 +948,9 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
/* Stretch to the maximum available space whilst not covering the conversation
|
||||
toolbar (26px). */
|
||||
height: calc(100% - 26px);
|
||||
font-size: .9em;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
@ -950,42 +962,14 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.room-context.editMode {
|
||||
/* Stretch to the maximum available space whilst not covering the conversation
|
||||
toolbar (26px). */
|
||||
height: calc(100% - 26px);
|
||||
}
|
||||
|
||||
.room-invitation-overlay .room-context {
|
||||
position: relative;
|
||||
left: auto;
|
||||
bottom: auto;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.room-invitation-overlay .room-context.editMode {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.room-context-content {
|
||||
flex: 1 1 auto;
|
||||
text-align: start;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
.room-context-thumbnail {
|
||||
/* 16px icon size + 3px border width. */
|
||||
width: 19px;
|
||||
max-height: 19px;
|
||||
border: 3px solid #fff;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
-moz-margin-end: 1ch;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.room-context > .error-display-area.error {
|
||||
display: block;
|
||||
background-color: rgba(215,67,69,.8);
|
||||
@ -1011,7 +995,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.room-context-description,
|
||||
.room-context-label,
|
||||
.room-context > .checkbox-wrapper > label {
|
||||
color: #fff;
|
||||
}
|
||||
@ -1020,7 +1004,6 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
||||
color: #707070;
|
||||
}
|
||||
|
||||
.room-context-description,
|
||||
.room-context-comment {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
@ -1079,8 +1062,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.room-context-btn-close,
|
||||
.room-context-btn-edit {
|
||||
.room-context-btn-close {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
/* 8px offset + 2px border-top */
|
||||
@ -1096,31 +1078,16 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.room-context-btn-edit {
|
||||
right: 20px;
|
||||
background-image: url("../img/icons-10x10.svg#edit-darkergrey");
|
||||
}
|
||||
|
||||
.room-context-btn-edit:hover,
|
||||
.room-context-btn-edit:hover:active {
|
||||
background-image: url("../img/icons-10x10.svg#edit-active");
|
||||
}
|
||||
|
||||
.room-context-btn-close:hover,
|
||||
.room-context-btn-close:hover:active {
|
||||
background-image: url("../img/icons-10x10.svg#close-active");
|
||||
}
|
||||
|
||||
html[dir="rtl"] .room-context-btn-close,
|
||||
html[dir="rtl"] .room-context-btn-edit {
|
||||
html[dir="rtl"] .room-context-btn-close {
|
||||
right: auto;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .room-context-btn-edit {
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
.media-layout {
|
||||
/* 50px is the header, 3em is the footer. */
|
||||
height: calc(100% - 50px - 3em);
|
||||
|
@ -17,14 +17,11 @@
|
||||
fill: #0095dd;
|
||||
}
|
||||
use[id$="-white"] {
|
||||
fill: rgba(255,255,255,0.8);
|
||||
fill: #fff;
|
||||
}
|
||||
use[id$="-disabled"] {
|
||||
fill: rgba(255,255,255,0.4);
|
||||
}
|
||||
use[id$="-darkergrey"] {
|
||||
fill: #999;
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<polygon id="close-shape" points="10,1.717 8.336,0.049 5.024,3.369 1.663,0 0,1.668 3.36,5.037 0.098,8.307 1.762,9.975 5.025,6.705 8.311,10 9.975,8.332 6.688,5.037"/>
|
||||
@ -44,7 +41,7 @@
|
||||
<use id="edit" xlink:href="#edit-shape"/>
|
||||
<use id="edit-active" xlink:href="#edit-shape"/>
|
||||
<use id="edit-disabled" xlink:href="#edit-shape"/>
|
||||
<use id="edit-darkergrey" xlink:href="#edit-shape"/>
|
||||
<use id="edit-white" xlink:href="#edit-shape"/>
|
||||
<use id="expand" xlink:href="#expand-shape"/>
|
||||
<use id="expand-active" xlink:href="#expand-shape"/>
|
||||
<use id="expand-disabled" xlink:href="#expand-shape"/>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.6 KiB |
@ -100,12 +100,29 @@ loop.store.TextChatStore = (function() {
|
||||
sentTimestamp: messageData.sentTimestamp,
|
||||
receivedTimestamp: messageData.receivedTimestamp
|
||||
};
|
||||
var newList = this._storeState.messageList.concat(message);
|
||||
var newList = [].concat(this._storeState.messageList);
|
||||
var isContext = message.contentType === CHAT_CONTENT_TYPES.CONTEXT;
|
||||
if (isContext) {
|
||||
var contextUpdated = false;
|
||||
for (var i = 0, l = newList.length; i < l; ++i) {
|
||||
// Replace the current context message with the provided update.
|
||||
if (newList[i].contentType === CHAT_CONTENT_TYPES.CONTEXT) {
|
||||
newList[i] = message;
|
||||
contextUpdated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contextUpdated) {
|
||||
newList.push(message);
|
||||
}
|
||||
} else {
|
||||
newList.push(message);
|
||||
}
|
||||
this.setStoreState({ messageList: newList });
|
||||
|
||||
// Notify MozLoopService if appropriate that a message has been appended
|
||||
// and it should therefore check if we need a different sized window or not.
|
||||
if (type != CHAT_MESSAGE_TYPES.SPECIAL) {
|
||||
if (message.contentType != CHAT_CONTENT_TYPES.ROOM_NAME) {
|
||||
window.dispatchEvent(new CustomEvent("LoopChatMessageAppended"));
|
||||
}
|
||||
},
|
||||
|
@ -68,7 +68,8 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
mixins: [React.addons.PureRenderMixin],
|
||||
|
||||
propTypes: {
|
||||
message: React.PropTypes.string.isRequired
|
||||
message: React.PropTypes.string.isRequired,
|
||||
useDesktopPaths: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -97,7 +98,8 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
messageList: React.PropTypes.array.isRequired
|
||||
messageList: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
|
||||
useDesktopPaths: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -157,7 +159,12 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
|
||||
switch (entry.contentType) {
|
||||
case CHAT_CONTENT_TYPES.ROOM_NAME:
|
||||
return React.createElement(TextChatRoomName, {key: i, message: entry.message});
|
||||
return (
|
||||
React.createElement(TextChatRoomName, {
|
||||
key: i,
|
||||
message: entry.message,
|
||||
useDesktopPaths: this.props.useDesktopPaths})
|
||||
);
|
||||
case CHAT_CONTENT_TYPES.CONTEXT:
|
||||
return (
|
||||
React.createElement("div", {className: "context-url-view-wrapper", key: i},
|
||||
@ -168,7 +175,7 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
showContextTitle: true,
|
||||
thumbnail: entry.extraData.thumbnail,
|
||||
url: entry.extraData.location,
|
||||
useDesktopPaths: false})
|
||||
useDesktopPaths: this.props.useDesktopPaths})
|
||||
)
|
||||
);
|
||||
default:
|
||||
@ -334,11 +341,8 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
* as a field for entering new messages.
|
||||
*
|
||||
* @property {loop.Dispatcher} dispatcher
|
||||
* @property {Boolean} showAlways If false, the view will not be rendered
|
||||
* if text chat is not enabled and the
|
||||
* message list is empty.
|
||||
* @property {Boolean} showRoomName Set to true to show the room name special
|
||||
* list item.
|
||||
* @property {Boolean} showRoomName Set to true to show the room name
|
||||
* special list item.
|
||||
*/
|
||||
var TextChatView = React.createClass({displayName: "TextChatView",
|
||||
mixins: [
|
||||
@ -348,8 +352,8 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
showAlways: React.PropTypes.bool.isRequired,
|
||||
showRoomName: React.PropTypes.bool.isRequired
|
||||
showRoomName: React.PropTypes.bool.isRequired,
|
||||
useDesktopPaths: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -366,14 +370,14 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
return item.type !== CHAT_MESSAGE_TYPES.SPECIAL;
|
||||
});
|
||||
} else {
|
||||
// XXX Desktop should be showing the initial context here (bug 1171940).
|
||||
messageList = this.state.messageList.filter(function(item) {
|
||||
return item.type !== CHAT_MESSAGE_TYPES.SPECIAL;
|
||||
return item.type !== CHAT_MESSAGE_TYPES.SPECIAL ||
|
||||
item.contentType !== CHAT_CONTENT_TYPES.ROOM_NAME;
|
||||
});
|
||||
hasNonSpecialMessages = !!messageList.length;
|
||||
}
|
||||
|
||||
if (!this.props.showAlways && !this.state.textChatEnabled && !messageList.length) {
|
||||
if (!this.state.textChatEnabled && !messageList.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -386,7 +390,8 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
React.createElement("div", {className: textChatViewClasses},
|
||||
React.createElement(TextChatEntriesView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
messageList: messageList}),
|
||||
messageList: messageList,
|
||||
useDesktopPaths: this.props.useDesktopPaths}),
|
||||
React.createElement(TextChatInputView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
showPlaceholder: !hasNonSpecialMessages,
|
||||
|
@ -68,7 +68,8 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
mixins: [React.addons.PureRenderMixin],
|
||||
|
||||
propTypes: {
|
||||
message: React.PropTypes.string.isRequired
|
||||
message: React.PropTypes.string.isRequired,
|
||||
useDesktopPaths: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -97,7 +98,8 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
messageList: React.PropTypes.array.isRequired
|
||||
messageList: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
|
||||
useDesktopPaths: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -157,7 +159,12 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
|
||||
switch (entry.contentType) {
|
||||
case CHAT_CONTENT_TYPES.ROOM_NAME:
|
||||
return <TextChatRoomName key={i} message={entry.message}/>;
|
||||
return (
|
||||
<TextChatRoomName
|
||||
key={i}
|
||||
message={entry.message}
|
||||
useDesktopPaths={this.props.useDesktopPaths} />
|
||||
);
|
||||
case CHAT_CONTENT_TYPES.CONTEXT:
|
||||
return (
|
||||
<div className="context-url-view-wrapper" key={i}>
|
||||
@ -168,7 +175,7 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
showContextTitle={true}
|
||||
thumbnail={entry.extraData.thumbnail}
|
||||
url={entry.extraData.location}
|
||||
useDesktopPaths={false} />
|
||||
useDesktopPaths={this.props.useDesktopPaths} />
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
@ -334,11 +341,8 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
* as a field for entering new messages.
|
||||
*
|
||||
* @property {loop.Dispatcher} dispatcher
|
||||
* @property {Boolean} showAlways If false, the view will not be rendered
|
||||
* if text chat is not enabled and the
|
||||
* message list is empty.
|
||||
* @property {Boolean} showRoomName Set to true to show the room name special
|
||||
* list item.
|
||||
* @property {Boolean} showRoomName Set to true to show the room name
|
||||
* special list item.
|
||||
*/
|
||||
var TextChatView = React.createClass({
|
||||
mixins: [
|
||||
@ -348,8 +352,8 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
showAlways: React.PropTypes.bool.isRequired,
|
||||
showRoomName: React.PropTypes.bool.isRequired
|
||||
showRoomName: React.PropTypes.bool.isRequired,
|
||||
useDesktopPaths: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -366,14 +370,14 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
return item.type !== CHAT_MESSAGE_TYPES.SPECIAL;
|
||||
});
|
||||
} else {
|
||||
// XXX Desktop should be showing the initial context here (bug 1171940).
|
||||
messageList = this.state.messageList.filter(function(item) {
|
||||
return item.type !== CHAT_MESSAGE_TYPES.SPECIAL;
|
||||
return item.type !== CHAT_MESSAGE_TYPES.SPECIAL ||
|
||||
item.contentType !== CHAT_CONTENT_TYPES.ROOM_NAME;
|
||||
});
|
||||
hasNonSpecialMessages = !!messageList.length;
|
||||
}
|
||||
|
||||
if (!this.props.showAlways && !this.state.textChatEnabled && !messageList.length) {
|
||||
if (!this.state.textChatEnabled && !messageList.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -386,7 +390,8 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
<div className={textChatViewClasses}>
|
||||
<TextChatEntriesView
|
||||
dispatcher={this.props.dispatcher}
|
||||
messageList={messageList} />
|
||||
messageList={messageList}
|
||||
useDesktopPaths={this.props.useDesktopPaths} />
|
||||
<TextChatInputView
|
||||
dispatcher={this.props.dispatcher}
|
||||
showPlaceholder={!hasNonSpecialMessages}
|
||||
|
@ -112,7 +112,6 @@ loop.validate = (function() {
|
||||
*/
|
||||
_dependencyMatchTypes: function(value, types) {
|
||||
return types.some(function(Type) {
|
||||
/*jshint eqeqeq:false*/
|
||||
try {
|
||||
return typeof Type === "undefined" || // skip checking
|
||||
Type === null && value === null || // null type
|
||||
|
@ -26,6 +26,7 @@ loop.shared.views = (function(_, l10n) {
|
||||
action: React.PropTypes.func.isRequired,
|
||||
enabled: React.PropTypes.bool.isRequired,
|
||||
scope: React.PropTypes.string.isRequired,
|
||||
title: React.PropTypes.string,
|
||||
type: React.PropTypes.string.isRequired,
|
||||
visible: React.PropTypes.bool.isRequired
|
||||
},
|
||||
@ -54,6 +55,10 @@ loop.shared.views = (function(_, l10n) {
|
||||
},
|
||||
|
||||
_getTitle: function(enabled) {
|
||||
if (this.props.title) {
|
||||
return this.props.title;
|
||||
}
|
||||
|
||||
var prefix = this.props.enabled ? "mute" : "unmute";
|
||||
var suffix = "button_title";
|
||||
var msgId = [prefix, this.props.scope, this.props.type, suffix].join("_");
|
||||
@ -183,6 +188,7 @@ loop.shared.views = (function(_, l10n) {
|
||||
return {
|
||||
video: {enabled: true, visible: true},
|
||||
audio: {enabled: true, visible: true},
|
||||
edit: {enabled: false, visible: false},
|
||||
screenShare: {state: SCREEN_SHARE_STATES.INACTIVE, visible: false},
|
||||
enableHangup: true
|
||||
};
|
||||
@ -191,9 +197,11 @@ loop.shared.views = (function(_, l10n) {
|
||||
propTypes: {
|
||||
audio: React.PropTypes.object.isRequired,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
edit: React.PropTypes.object.isRequired,
|
||||
enableHangup: React.PropTypes.bool,
|
||||
hangup: React.PropTypes.func.isRequired,
|
||||
hangupButtonLabel: React.PropTypes.string,
|
||||
onEditClick: React.PropTypes.func,
|
||||
publishStream: React.PropTypes.func.isRequired,
|
||||
screenShare: React.PropTypes.object,
|
||||
video: React.PropTypes.object.isRequired
|
||||
@ -211,6 +219,12 @@ loop.shared.views = (function(_, l10n) {
|
||||
this.props.publishStream("audio", !this.props.audio.enabled);
|
||||
},
|
||||
|
||||
handleToggleEdit: function() {
|
||||
if (this.props.onEditClick) {
|
||||
this.props.onEditClick(!this.props.edit.enabled);
|
||||
}
|
||||
},
|
||||
|
||||
_getHangupButtonLabel: function() {
|
||||
return this.props.hangupButtonLabel || l10n.get("hangup_button_caption2");
|
||||
},
|
||||
@ -238,10 +252,19 @@ loop.shared.views = (function(_, l10n) {
|
||||
scope: "local", type: "audio",
|
||||
visible: this.props.audio.visible})
|
||||
),
|
||||
React.createElement("li", {className: "conversation-toolbar-btn-box btn-screen-share-entry"},
|
||||
React.createElement("li", {className: "conversation-toolbar-btn-box"},
|
||||
React.createElement(ScreenShareControlButton, {dispatcher: this.props.dispatcher,
|
||||
state: this.props.screenShare.state,
|
||||
visible: this.props.screenShare.visible})
|
||||
),
|
||||
React.createElement("li", {className: "conversation-toolbar-btn-box btn-edit-entry"},
|
||||
React.createElement(MediaControlButton, {action: this.handleToggleEdit,
|
||||
enabled: this.props.edit.enabled,
|
||||
scope: "local",
|
||||
title: l10n.get(this.props.edit.enabled ?
|
||||
"context_edit_tooltip" : "context_hide_tooltip"),
|
||||
type: "edit",
|
||||
visible: this.props.edit.visible})
|
||||
)
|
||||
)
|
||||
);
|
||||
@ -300,7 +323,7 @@ loop.shared.views = (function(_, l10n) {
|
||||
};
|
||||
}
|
||||
|
||||
this.listenTo(this.props.sdk, "exception", this._handleSdkException.bind(this));
|
||||
this.listenTo(this.props.sdk, "exception", this._handleSdkException);
|
||||
|
||||
this.listenTo(this.props.model, "session:connected",
|
||||
this._onSessionConnected);
|
||||
@ -402,14 +425,14 @@ loop.shared.views = (function(_, l10n) {
|
||||
audio: {enabled: ev.stream.hasAudio},
|
||||
video: {enabled: ev.stream.hasVideo}
|
||||
});
|
||||
}.bind(this));
|
||||
});
|
||||
|
||||
this.listenTo(this.publisher, "streamDestroyed", function() {
|
||||
this.setState({
|
||||
audio: {enabled: false},
|
||||
video: {enabled: false}
|
||||
});
|
||||
}.bind(this));
|
||||
});
|
||||
|
||||
this.props.model.publish(this.publisher);
|
||||
},
|
||||
@ -519,7 +542,7 @@ loop.shared.views = (function(_, l10n) {
|
||||
componentDidMount: function() {
|
||||
this.listenTo(this.props.notifications, "reset add remove", function() {
|
||||
this.forceUpdate();
|
||||
}.bind(this));
|
||||
});
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
|
@ -26,6 +26,7 @@ loop.shared.views = (function(_, l10n) {
|
||||
action: React.PropTypes.func.isRequired,
|
||||
enabled: React.PropTypes.bool.isRequired,
|
||||
scope: React.PropTypes.string.isRequired,
|
||||
title: React.PropTypes.string,
|
||||
type: React.PropTypes.string.isRequired,
|
||||
visible: React.PropTypes.bool.isRequired
|
||||
},
|
||||
@ -54,6 +55,10 @@ loop.shared.views = (function(_, l10n) {
|
||||
},
|
||||
|
||||
_getTitle: function(enabled) {
|
||||
if (this.props.title) {
|
||||
return this.props.title;
|
||||
}
|
||||
|
||||
var prefix = this.props.enabled ? "mute" : "unmute";
|
||||
var suffix = "button_title";
|
||||
var msgId = [prefix, this.props.scope, this.props.type, suffix].join("_");
|
||||
@ -183,6 +188,7 @@ loop.shared.views = (function(_, l10n) {
|
||||
return {
|
||||
video: {enabled: true, visible: true},
|
||||
audio: {enabled: true, visible: true},
|
||||
edit: {enabled: false, visible: false},
|
||||
screenShare: {state: SCREEN_SHARE_STATES.INACTIVE, visible: false},
|
||||
enableHangup: true
|
||||
};
|
||||
@ -191,9 +197,11 @@ loop.shared.views = (function(_, l10n) {
|
||||
propTypes: {
|
||||
audio: React.PropTypes.object.isRequired,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
edit: React.PropTypes.object.isRequired,
|
||||
enableHangup: React.PropTypes.bool,
|
||||
hangup: React.PropTypes.func.isRequired,
|
||||
hangupButtonLabel: React.PropTypes.string,
|
||||
onEditClick: React.PropTypes.func,
|
||||
publishStream: React.PropTypes.func.isRequired,
|
||||
screenShare: React.PropTypes.object,
|
||||
video: React.PropTypes.object.isRequired
|
||||
@ -211,6 +219,12 @@ loop.shared.views = (function(_, l10n) {
|
||||
this.props.publishStream("audio", !this.props.audio.enabled);
|
||||
},
|
||||
|
||||
handleToggleEdit: function() {
|
||||
if (this.props.onEditClick) {
|
||||
this.props.onEditClick(!this.props.edit.enabled);
|
||||
}
|
||||
},
|
||||
|
||||
_getHangupButtonLabel: function() {
|
||||
return this.props.hangupButtonLabel || l10n.get("hangup_button_caption2");
|
||||
},
|
||||
@ -238,11 +252,20 @@ loop.shared.views = (function(_, l10n) {
|
||||
scope="local" type="audio"
|
||||
visible={this.props.audio.visible} />
|
||||
</li>
|
||||
<li className="conversation-toolbar-btn-box btn-screen-share-entry">
|
||||
<li className="conversation-toolbar-btn-box">
|
||||
<ScreenShareControlButton dispatcher={this.props.dispatcher}
|
||||
state={this.props.screenShare.state}
|
||||
visible={this.props.screenShare.visible} />
|
||||
</li>
|
||||
<li className="conversation-toolbar-btn-box btn-edit-entry">
|
||||
<MediaControlButton action={this.handleToggleEdit}
|
||||
enabled={this.props.edit.enabled}
|
||||
scope="local"
|
||||
title={l10n.get(this.props.edit.enabled ?
|
||||
"context_edit_tooltip" : "context_hide_tooltip")}
|
||||
type="edit"
|
||||
visible={this.props.edit.visible} />
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
@ -300,7 +323,7 @@ loop.shared.views = (function(_, l10n) {
|
||||
};
|
||||
}
|
||||
|
||||
this.listenTo(this.props.sdk, "exception", this._handleSdkException.bind(this));
|
||||
this.listenTo(this.props.sdk, "exception", this._handleSdkException);
|
||||
|
||||
this.listenTo(this.props.model, "session:connected",
|
||||
this._onSessionConnected);
|
||||
@ -402,14 +425,14 @@ loop.shared.views = (function(_, l10n) {
|
||||
audio: {enabled: ev.stream.hasAudio},
|
||||
video: {enabled: ev.stream.hasVideo}
|
||||
});
|
||||
}.bind(this));
|
||||
});
|
||||
|
||||
this.listenTo(this.publisher, "streamDestroyed", function() {
|
||||
this.setState({
|
||||
audio: {enabled: false},
|
||||
video: {enabled: false}
|
||||
});
|
||||
}.bind(this));
|
||||
});
|
||||
|
||||
this.props.model.publish(this.publisher);
|
||||
},
|
||||
@ -519,7 +542,7 @@ loop.shared.views = (function(_, l10n) {
|
||||
componentDidMount: function() {
|
||||
this.listenTo(this.props.notifications, "reset add remove", function() {
|
||||
this.forceUpdate();
|
||||
}.bind(this));
|
||||
});
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
|
@ -15,7 +15,7 @@ const LOOP_SESSION_TYPE = {
|
||||
FXA: 2
|
||||
};
|
||||
|
||||
/***
|
||||
/**
|
||||
* Values that we segment 2-way media connection length telemetry probes
|
||||
* into.
|
||||
*
|
||||
@ -614,7 +614,8 @@ let MozLoopServiceInternal = {
|
||||
return this.hawkRequestInternal(sessionType, path, method, payloadObj, false);
|
||||
},
|
||||
() => {
|
||||
return handle401Error(error); //Process the original error that triggered the retry.
|
||||
// Process the original error that triggered the retry.
|
||||
return handle401Error(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -889,7 +890,10 @@ let MozLoopServiceInternal = {
|
||||
// When the chat box or messages are shown, resize the panel or window
|
||||
// to be slightly higher to accomodate them.
|
||||
let customSize = kSizeMap[ev.type];
|
||||
if (customSize) {
|
||||
let currSize = chatbox.getAttribute("customSize");
|
||||
// If the size is already at the requested one or at the maximum size
|
||||
// already, don't do anything. Especially don't make it shrink.
|
||||
if (customSize && currSize != customSize && currSize != "loopChatMessageAppended") {
|
||||
chatbox.setAttribute("customSize", customSize);
|
||||
chatbox.parentNode.setAttribute("customSize", customSize);
|
||||
}
|
||||
@ -916,10 +920,10 @@ let MozLoopServiceInternal = {
|
||||
// Not ideal but insert our data amidst existing data like this:
|
||||
// - 000 (id=00 url=http)
|
||||
// + 000 (session=000 call=000 id=00 url=http)
|
||||
var pair = pc.id.split("("); //)
|
||||
var pair = pc.id.split("(");
|
||||
if (pair.length == 2) {
|
||||
pc.id = pair[0] + "(session=" + context.sessionId +
|
||||
(context.callId ? " call=" + context.callId : "") + " " + pair[1]; //)
|
||||
(context.callId ? " call=" + context.callId : "") + " " + pair[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,9 +394,9 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
* @private
|
||||
*/
|
||||
_shouldRenderRemoteLoading: function() {
|
||||
return this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
|
||||
!this.state.remoteSrcVideoObject &&
|
||||
!this.state.mediaConnected;
|
||||
return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
|
||||
!this.state.remoteSrcVideoObject &&
|
||||
!this.state.mediaConnected);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -464,7 +464,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
React.createElement(sharedViews.chat.TextChatView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
showAlways: true,
|
||||
showRoomName: true}),
|
||||
showRoomName: true,
|
||||
useDesktopPaths: false}),
|
||||
React.createElement("div", {className: "local"},
|
||||
React.createElement(sharedViews.MediaView, {displayAvatar: this.state.videoMuted,
|
||||
isLoading: this._shouldRenderLocalLoading(),
|
||||
@ -477,6 +478,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
audio: {enabled: !this.state.audioMuted,
|
||||
visible: this._roomIsActive()},
|
||||
dispatcher: this.props.dispatcher,
|
||||
edit: { visible: false, enabled: false},
|
||||
enableHangup: this._roomIsActive(),
|
||||
hangup: this.leaveRoom,
|
||||
hangupButtonLabel: mozL10n.get("rooms_leave_button_label"),
|
||||
|
@ -394,9 +394,9 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
* @private
|
||||
*/
|
||||
_shouldRenderRemoteLoading: function() {
|
||||
return this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
|
||||
!this.state.remoteSrcVideoObject &&
|
||||
!this.state.mediaConnected;
|
||||
return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
|
||||
!this.state.remoteSrcVideoObject &&
|
||||
!this.state.mediaConnected);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -464,7 +464,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
<sharedViews.chat.TextChatView
|
||||
dispatcher={this.props.dispatcher}
|
||||
showAlways={true}
|
||||
showRoomName={true} />
|
||||
showRoomName={true}
|
||||
useDesktopPaths={false} />
|
||||
<div className="local">
|
||||
<sharedViews.MediaView displayAvatar={this.state.videoMuted}
|
||||
isLoading={this._shouldRenderLocalLoading()}
|
||||
@ -477,6 +478,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
audio={{enabled: !this.state.audioMuted,
|
||||
visible: this._roomIsActive()}}
|
||||
dispatcher={this.props.dispatcher}
|
||||
edit={{ visible: false, enabled: false }}
|
||||
enableHangup={this._roomIsActive()}
|
||||
hangup={this.leaveRoom}
|
||||
hangupButtonLabel={mozL10n.get("rooms_leave_button_label")}
|
||||
|
@ -223,7 +223,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var urlCreationDateClasses = cx({
|
||||
"light-color-font": true,
|
||||
"call-url-date": true, /* Used as a handler in the tests */
|
||||
/*hidden until date is available*/
|
||||
// Hidden until date is available.
|
||||
"hide": !this.props.urlCreationDateString.length
|
||||
});
|
||||
|
||||
|
@ -223,7 +223,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var urlCreationDateClasses = cx({
|
||||
"light-color-font": true,
|
||||
"call-url-date": true, /* Used as a handler in the tests */
|
||||
/*hidden until date is available*/
|
||||
// Hidden until date is available.
|
||||
"hide": !this.props.urlCreationDateString.length
|
||||
});
|
||||
|
||||
|
@ -12,8 +12,8 @@
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"eslint": "0.21.x",
|
||||
"eslint-plugin-react": "2.3.x",
|
||||
"eslint": "0.24.x",
|
||||
"eslint-plugin-react": "2.6.x",
|
||||
"express": "4.x"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -146,7 +146,8 @@ describe("loop.contacts", function() {
|
||||
it("should show the gravatars promo box", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
notifications: notifications,
|
||||
startForm: function() {}
|
||||
}));
|
||||
|
||||
var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
|
||||
@ -166,7 +167,8 @@ describe("loop.contacts", function() {
|
||||
};
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
notifications: notifications,
|
||||
startForm: function() {}
|
||||
}));
|
||||
|
||||
var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
|
||||
@ -178,7 +180,8 @@ describe("loop.contacts", function() {
|
||||
it("should hide the gravatars promo box when the 'use' button is clicked", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
notifications: notifications,
|
||||
startForm: function() {}
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
@ -193,7 +196,8 @@ describe("loop.contacts", function() {
|
||||
it("should should set the prefs correctly when the 'use' button is clicked", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
notifications: notifications,
|
||||
startForm: function() {}
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
@ -207,7 +211,8 @@ describe("loop.contacts", function() {
|
||||
it("should hide the gravatars promo box when the 'close' button is clicked", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
notifications: notifications,
|
||||
startForm: function() {}
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
@ -220,7 +225,8 @@ describe("loop.contacts", function() {
|
||||
it("should set prefs correctly when the 'close' button is clicked", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
notifications: notifications,
|
||||
startForm: function() {}
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
@ -242,7 +248,8 @@ describe("loop.contacts", function() {
|
||||
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
notifications: notifications,
|
||||
startForm: function() {}
|
||||
}));
|
||||
});
|
||||
|
||||
@ -299,8 +306,10 @@ describe("loop.contacts", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
view = TestUtils.renderIntoDocument(
|
||||
React.createElement(
|
||||
loop.contacts.ContactDetailsForm, {mode: "add"}));
|
||||
React.createElement(loop.contacts.ContactDetailsForm, {
|
||||
mode: "add",
|
||||
selectTab: function() {}
|
||||
}));
|
||||
});
|
||||
|
||||
it("should render 'add' header", function() {
|
||||
@ -409,8 +418,10 @@ describe("loop.contacts", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
view = TestUtils.renderIntoDocument(
|
||||
React.createElement(
|
||||
loop.contacts.ContactDetailsForm, {mode: "edit"}));
|
||||
React.createElement(loop.contacts.ContactDetailsForm, {
|
||||
mode: "edit",
|
||||
selectTab: function() {}
|
||||
}));
|
||||
});
|
||||
|
||||
it("should render 'edit' header", function() {
|
||||
|
@ -18,19 +18,6 @@ describe("loop.conversationViews", function () {
|
||||
var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
|
||||
var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
|
||||
|
||||
// XXX refactor to Just Work with "sandbox.stubComponent" or else
|
||||
// just pass in the sandbox and put somewhere generally usable
|
||||
|
||||
function stubComponent(obj, component, mockTagName){
|
||||
var reactClass = React.createClass({
|
||||
render: function() {
|
||||
var tagName = mockTagName || "div";
|
||||
return React.DOM[tagName](null, this.props.children);
|
||||
}
|
||||
});
|
||||
return sandbox.stub(obj, component, reactClass);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.useFakeTimers();
|
||||
|
@ -46,7 +46,10 @@ describe("loop.conversation", function() {
|
||||
},
|
||||
getAudioBlob: sinon.spy(function(name, callback) {
|
||||
callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
|
||||
})
|
||||
}),
|
||||
getSelectedTabMetadata: function(callback) {
|
||||
callback({});
|
||||
}
|
||||
};
|
||||
|
||||
fakeWindow = {
|
||||
|
@ -97,7 +97,7 @@
|
||||
|
||||
describe("Unexpected Warnings Check", function() {
|
||||
it("should long only the warnings we expect", function() {
|
||||
chai.expect(caughtWarnings.length).to.eql(128);
|
||||
chai.expect(caughtWarnings.length).to.eql(30);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -778,7 +778,7 @@ describe("loop.panel", function() {
|
||||
"conversation button",
|
||||
function() {
|
||||
navigator.mozLoop.userProfile = {email: fakeEmail};
|
||||
var view = createTestComponent();
|
||||
var view = createTestComponent(false);
|
||||
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".new-room-button"));
|
||||
|
||||
@ -801,7 +801,7 @@ describe("loop.panel", function() {
|
||||
});
|
||||
};
|
||||
|
||||
var view = createTestComponent();
|
||||
var view = createTestComponent(false);
|
||||
|
||||
// Simulate being visible
|
||||
view.onDocumentVisible();
|
||||
@ -841,7 +841,7 @@ describe("loop.panel", function() {
|
||||
});
|
||||
};
|
||||
|
||||
var view = createTestComponent();
|
||||
var view = createTestComponent(false);
|
||||
|
||||
// Simulate being visible
|
||||
view.onDocumentVisible();
|
||||
@ -859,7 +859,7 @@ describe("loop.panel", function() {
|
||||
});
|
||||
};
|
||||
|
||||
var view = createTestComponent();
|
||||
var view = createTestComponent(false);
|
||||
|
||||
view.setState({ checked: true });
|
||||
|
||||
@ -878,7 +878,7 @@ describe("loop.panel", function() {
|
||||
});
|
||||
};
|
||||
|
||||
var view = createTestComponent();
|
||||
var view = createTestComponent(false);
|
||||
|
||||
// Simulate being visible
|
||||
view.onDocumentVisible();
|
||||
@ -896,7 +896,7 @@ describe("loop.panel", function() {
|
||||
});
|
||||
};
|
||||
|
||||
var view = createTestComponent();
|
||||
var view = createTestComponent(false);
|
||||
|
||||
view.onDocumentVisible();
|
||||
|
||||
@ -913,7 +913,7 @@ describe("loop.panel", function() {
|
||||
});
|
||||
};
|
||||
|
||||
var view = createTestComponent();
|
||||
var view = createTestComponent(false);
|
||||
|
||||
// Simulate being visible
|
||||
view.onDocumentVisible();
|
||||
@ -933,7 +933,7 @@ describe("loop.panel", function() {
|
||||
});
|
||||
};
|
||||
|
||||
var view = createTestComponent();
|
||||
var view = createTestComponent(false);
|
||||
|
||||
// Simulate being visible.
|
||||
view.onDocumentVisible();
|
||||
|
@ -141,8 +141,9 @@ describe("loop.roomViews", function () {
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: fakeMozLoop,
|
||||
roomData: {},
|
||||
savingContext: false,
|
||||
show: true,
|
||||
showContext: false
|
||||
showEditContext: false
|
||||
}, props);
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.roomViews.DesktopRoomInvitationView, props));
|
||||
@ -248,76 +249,37 @@ describe("loop.roomViews", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Context", function() {
|
||||
it("should not render the context data when told not to", function() {
|
||||
describe("Edit Context", function() {
|
||||
it("should show the 'Add some context' link", function() {
|
||||
view = mountTestComponent();
|
||||
|
||||
expect(view.getDOMNode().querySelector(".room-context")).to.eql(null);
|
||||
expect(view.getDOMNode().querySelector(".room-invitation-addcontext")).
|
||||
to.not.eql(null);
|
||||
});
|
||||
|
||||
it("should render context when data is available", function() {
|
||||
it("should call a callback when the link is clicked", function() {
|
||||
var onAddContextClick = sinon.stub();
|
||||
view = mountTestComponent({
|
||||
showContext: true,
|
||||
roomData: {
|
||||
roomContextUrls: [fakeContextURL]
|
||||
}
|
||||
onAddContextClick: onAddContextClick
|
||||
});
|
||||
|
||||
var node = view.getDOMNode();
|
||||
expect(node.querySelector(".room-context")).to.eql(null);
|
||||
|
||||
var addLink = node.querySelector(".room-invitation-addcontext");
|
||||
|
||||
React.addons.TestUtils.Simulate.click(addLink);
|
||||
|
||||
sinon.assert.calledOnce(onAddContextClick);
|
||||
});
|
||||
|
||||
it("should show the edit context view", function() {
|
||||
view = mountTestComponent({
|
||||
showEditContext: true
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".room-context")).to.not.eql(null);
|
||||
});
|
||||
|
||||
it("should render the context in editMode when the pencil is clicked", function() {
|
||||
view = mountTestComponent({
|
||||
showContext: true,
|
||||
roomData: {
|
||||
roomContextUrls: [fakeContextURL]
|
||||
}
|
||||
});
|
||||
|
||||
var pencil = view.getDOMNode().querySelector(".room-context-btn-edit");
|
||||
expect(pencil).to.not.eql(null);
|
||||
|
||||
React.addons.TestUtils.Simulate.click(pencil);
|
||||
|
||||
expect(view.state.editMode).to.eql(true);
|
||||
var node = view.getDOMNode();
|
||||
expect(node.querySelector("form")).to.not.eql(null);
|
||||
// No text paragraphs should be visible in editMode.
|
||||
var visiblePs = Array.slice(node.querySelector("p")).filter(function(p) {
|
||||
return p.classList.contains("hide") || p.classList.contains("error");
|
||||
});
|
||||
expect(visiblePs.length).to.eql(0);
|
||||
});
|
||||
|
||||
it("should format the context url for display", function() {
|
||||
sandbox.stub(sharedUtils, "formatURL").returns({
|
||||
location: "location",
|
||||
hostname: "hostname"
|
||||
});
|
||||
|
||||
view = mountTestComponent({
|
||||
showContext: true,
|
||||
roomData: {
|
||||
roomContextUrls: [fakeContextURL]
|
||||
}
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".room-context-url").textContent)
|
||||
.eql("hostname");
|
||||
});
|
||||
|
||||
it("should show a default favicon when none is available", function() {
|
||||
fakeContextURL.thumbnail = null;
|
||||
view = mountTestComponent({
|
||||
showContext: true,
|
||||
roomData: {
|
||||
roomContextUrls: [fakeContextURL]
|
||||
}
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".room-context-thumbnail").src)
|
||||
.to.match(/loop\/shared\/img\/icons-16x16.svg#globe$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -331,6 +293,12 @@ describe("loop.roomViews", function () {
|
||||
})
|
||||
});
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
fakeMozLoop.getLoopPref = function(prefName) {
|
||||
if (prefName == "contextInConversations.enabled") {
|
||||
return true;
|
||||
}
|
||||
return "test";
|
||||
};
|
||||
});
|
||||
|
||||
function mountTestComponent() {
|
||||
@ -643,6 +611,32 @@ describe("loop.roomViews", function () {
|
||||
.not.eql(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Edit Context", function() {
|
||||
it("should show the form when the edit button is clicked", function() {
|
||||
view = mountTestComponent();
|
||||
var node = view.getDOMNode();
|
||||
|
||||
expect(node.querySelector(".room-context")).to.eql(null);
|
||||
|
||||
var editButton = node.querySelector(".btn-mute-edit");
|
||||
React.addons.TestUtils.Simulate.click(editButton);
|
||||
|
||||
expect(view.getDOMNode().querySelector(".room-context")).to.not.eql(null);
|
||||
});
|
||||
|
||||
it("should hide the form when the edit button is clicked again", function() {
|
||||
view = mountTestComponent();
|
||||
|
||||
var editButton = view.getDOMNode().querySelector(".btn-mute-edit");
|
||||
React.addons.TestUtils.Simulate.click(editButton);
|
||||
|
||||
// Click again.
|
||||
React.addons.TestUtils.Simulate.click(editButton);
|
||||
|
||||
expect(view.getDOMNode().querySelector(".room-context")).to.eql(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("SocialShareDropdown", function() {
|
||||
@ -741,7 +735,7 @@ describe("loop.roomViews", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("DesktopRoomContextView", function() {
|
||||
describe("DesktopRoomEditContextView", function() {
|
||||
var view;
|
||||
|
||||
afterEach(function() {
|
||||
@ -759,35 +753,10 @@ describe("loop.roomViews", function () {
|
||||
}
|
||||
}, props);
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.roomViews.DesktopRoomContextView, props));
|
||||
React.createElement(loop.roomViews.DesktopRoomEditContextView, props));
|
||||
}
|
||||
|
||||
describe("#render", function() {
|
||||
it("should show the context information properly when available", function() {
|
||||
view = mountTestComponent({
|
||||
roomData: {
|
||||
roomDescription: "Hello, is it me you're looking for?",
|
||||
roomContextUrls: [fakeContextURL]
|
||||
}
|
||||
});
|
||||
|
||||
var node = view.getDOMNode();
|
||||
expect(node).to.not.eql(null);
|
||||
expect(node.querySelector(".room-context-thumbnail").src).to.
|
||||
eql(fakeContextURL.thumbnail);
|
||||
expect(node.querySelector(".room-context-description").firstChild.textContent).
|
||||
to.eql(fakeContextURL.description);
|
||||
});
|
||||
|
||||
it("should not render optional data", function() {
|
||||
view = mountTestComponent({
|
||||
roomData: { roomContextUrls: [fakeContextURL] }
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".room-context-comment")).to.
|
||||
eql(null);
|
||||
});
|
||||
|
||||
it("should not render the component when 'show' is false", function() {
|
||||
view = mountTestComponent({
|
||||
show: false
|
||||
@ -806,10 +775,9 @@ describe("loop.roomViews", function () {
|
||||
expect(view.getDOMNode()).to.eql(null);
|
||||
});
|
||||
|
||||
it("should render the view in editMode when appropriate", function() {
|
||||
it("should render the view correctly", function() {
|
||||
var roomName = "Hello, is it me you're looking for?";
|
||||
view = mountTestComponent({
|
||||
editMode: true,
|
||||
roomData: {
|
||||
roomName: roomName,
|
||||
roomContextUrls: [fakeContextURL]
|
||||
@ -826,7 +794,6 @@ describe("loop.roomViews", function () {
|
||||
|
||||
it("should show the checkbox as disabled when context is already set", function() {
|
||||
view = mountTestComponent({
|
||||
editMode: true,
|
||||
roomData: {
|
||||
roomToken: "fakeToken",
|
||||
roomName: "fakeName",
|
||||
@ -838,34 +805,7 @@ describe("loop.roomViews", function () {
|
||||
expect(checkbox.classList.contains("disabled")).to.eql(true);
|
||||
});
|
||||
|
||||
it("should render the editMode view when the edit button is clicked", function(done) {
|
||||
var roomName = "Hello, is it me you're looking for?";
|
||||
view = mountTestComponent({
|
||||
roomData: {
|
||||
roomToken: "fakeToken",
|
||||
roomName: roomName,
|
||||
roomContextUrls: [fakeContextURL]
|
||||
}
|
||||
});
|
||||
|
||||
// Switch to editMode via setting the prop, since we can control that
|
||||
// better.
|
||||
view.setProps({ editMode: true }, function() {
|
||||
// First check if availableContext is set correctly.
|
||||
expect(view.state.availableContext).to.not.eql(null);
|
||||
expect(view.state.availableContext.previewImage).to.eql(favicon);
|
||||
|
||||
var node = view.getDOMNode();
|
||||
expect(node.querySelector(".checkbox-wrapper").classList.contains("disabled")).to.eql(true);
|
||||
expect(node.querySelector(".room-context-name").value).to.eql(roomName);
|
||||
expect(node.querySelector(".room-context-url").value).to.eql(fakeContextURL.location);
|
||||
expect(node.querySelector(".room-context-comments").value).to.eql(fakeContextURL.description);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should hide the checkbox when no context data is stored or available", function(done) {
|
||||
it("should hide the checkbox when no context data is stored or available", function() {
|
||||
view = mountTestComponent({
|
||||
roomData: {
|
||||
roomToken: "fakeToken",
|
||||
@ -873,18 +813,12 @@ describe("loop.roomViews", function () {
|
||||
}
|
||||
});
|
||||
|
||||
// Switch to editMode via setting the prop, since we can control that
|
||||
// better.
|
||||
view.setProps({ editMode: true }, function() {
|
||||
// First check if availableContext is set correctly.
|
||||
expect(view.state.availableContext).to.not.eql(null);
|
||||
expect(view.state.availableContext.previewImage).to.eql(favicon);
|
||||
// First check if availableContext is set correctly.
|
||||
expect(view.state.availableContext).to.not.eql(null);
|
||||
expect(view.state.availableContext.previewImage).to.eql(favicon);
|
||||
|
||||
var node = view.getDOMNode();
|
||||
expect(node.querySelector(".checkbox-wrapper").classList.contains("hide")).to.eql(true);
|
||||
|
||||
done();
|
||||
});
|
||||
var node = view.getDOMNode();
|
||||
expect(node.querySelector(".checkbox-wrapper").classList.contains("hide")).to.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
@ -952,8 +886,8 @@ describe("loop.roomViews", function () {
|
||||
|
||||
// Now simulate a successful save.
|
||||
view.setProps({ savingContext: false }, function() {
|
||||
// The editMode flag should be updated.
|
||||
expect(view.state.editMode).to.eql(false);
|
||||
// The 'show flag should be updated.
|
||||
expect(view.state.show).to.eql(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -964,13 +898,12 @@ describe("loop.roomViews", function () {
|
||||
var node, checkbox;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeMozLoop.getSelectedTabMetadata = sinon.stub().callsArgWith(0, {
|
||||
favicon: fakeContextURL.thumbnail,
|
||||
title: fakeContextURL.description,
|
||||
url: fakeContextURL.location
|
||||
});
|
||||
view = mountTestComponent({
|
||||
availableContext: {
|
||||
description: fakeContextURL.description,
|
||||
previewImage: fakeContextURL.thumbnail,
|
||||
url: fakeContextURL.location
|
||||
},
|
||||
editMode: true,
|
||||
roomData: {
|
||||
roomToken: "fakeToken",
|
||||
roomName: "fakeName"
|
||||
|
@ -96,7 +96,7 @@
|
||||
|
||||
describe("Unexpected Warnings Check", function() {
|
||||
it("should long only the warnings we expect", function() {
|
||||
chai.expect(caughtWarnings.length).to.eql(180);
|
||||
chai.expect(caughtWarnings.length).to.eql(73);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -204,6 +204,52 @@ describe("loop.store.TextChatStore", function () {
|
||||
]);
|
||||
});
|
||||
|
||||
it("should not add more than one context message", function() {
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomOwner: "Mark",
|
||||
roomUrl: "fake",
|
||||
urls: [{
|
||||
description: "A wonderful event",
|
||||
location: "http://wonderful.invalid",
|
||||
thumbnail: "fake"
|
||||
}]
|
||||
}));
|
||||
|
||||
expect(store.getStoreState("messageList")).eql([{
|
||||
type: CHAT_MESSAGE_TYPES.SPECIAL,
|
||||
contentType: CHAT_CONTENT_TYPES.CONTEXT,
|
||||
message: "A wonderful event",
|
||||
sentTimestamp: undefined,
|
||||
receivedTimestamp: undefined,
|
||||
extraData: {
|
||||
location: "http://wonderful.invalid",
|
||||
thumbnail: "fake"
|
||||
}
|
||||
}]);
|
||||
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomOwner: "Mark",
|
||||
roomUrl: "fake",
|
||||
urls: [{
|
||||
description: "A wonderful event2",
|
||||
location: "http://wonderful.invalid2",
|
||||
thumbnail: "fake2"
|
||||
}]
|
||||
}));
|
||||
|
||||
expect(store.getStoreState("messageList")).eql([{
|
||||
type: CHAT_MESSAGE_TYPES.SPECIAL,
|
||||
contentType: CHAT_CONTENT_TYPES.CONTEXT,
|
||||
message: "A wonderful event2",
|
||||
sentTimestamp: undefined,
|
||||
receivedTimestamp: undefined,
|
||||
extraData: {
|
||||
location: "http://wonderful.invalid2",
|
||||
thumbnail: "fake2"
|
||||
}
|
||||
}]);
|
||||
});
|
||||
|
||||
it("should not dispatch a LoopChatMessageAppended event", function() {
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "Let's share!",
|
||||
|
@ -43,7 +43,8 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
function mountTestComponent(extraProps) {
|
||||
var basicProps = {
|
||||
dispatcher: dispatcher,
|
||||
messageList: []
|
||||
messageList: [],
|
||||
useDesktopPaths: false
|
||||
};
|
||||
|
||||
return TestUtils.renderIntoDocument(
|
||||
@ -56,11 +57,13 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
messageList: [{
|
||||
type: CHAT_MESSAGE_TYPES.RECEIVED,
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello!"
|
||||
message: "Hello!",
|
||||
receivedTimestamp: "2015-06-25T17:53:55.357Z"
|
||||
}, {
|
||||
type: CHAT_MESSAGE_TYPES.SENT,
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Is it me you're looking for?"
|
||||
message: "Is it me you're looking for?",
|
||||
sentTimestamp: "2015-06-25T17:53:55.357Z"
|
||||
}]
|
||||
});
|
||||
|
||||
@ -81,7 +84,8 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
messageList: [{
|
||||
type: CHAT_MESSAGE_TYPES.RECEIVED,
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello!"
|
||||
message: "Hello!",
|
||||
receivedTimestamp: "2015-06-25T17:53:55.357Z"
|
||||
}]
|
||||
});
|
||||
|
||||
@ -97,7 +101,8 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
messageList: [{
|
||||
type: CHAT_MESSAGE_TYPES.SPECIAL,
|
||||
contentType: CHAT_CONTENT_TYPES.ROOM_NAME,
|
||||
message: "Hello!"
|
||||
message: "Hello!",
|
||||
receivedTimestamp: "2015-06-25T17:53:55.357Z"
|
||||
}]
|
||||
});
|
||||
|
||||
@ -112,7 +117,8 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
messageList: [{
|
||||
type: CHAT_MESSAGE_TYPES.SENT,
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello!"
|
||||
message: "Hello!",
|
||||
sentTimestamp: "2015-06-25T17:53:55.357Z"
|
||||
}]
|
||||
});
|
||||
|
||||
@ -125,7 +131,11 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
|
||||
function mountTestComponent(extraProps) {
|
||||
var props = _.extend({
|
||||
dispatcher: dispatcher
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
dispatcher: dispatcher,
|
||||
message: "test",
|
||||
type: CHAT_MESSAGE_TYPES.RECEIVED,
|
||||
timestamp: "2015-06-23T22:48:39.738Z"
|
||||
}, extraProps);
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.shared.views.chat.TextChatEntry, props));
|
||||
@ -133,8 +143,7 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
|
||||
it("should not render a timestamp", function() {
|
||||
view = mountTestComponent({
|
||||
showTimestamp: false,
|
||||
timestamp: "2015-06-23T22:48:39.738Z"
|
||||
showTimestamp: false
|
||||
});
|
||||
var node = view.getDOMNode();
|
||||
|
||||
@ -143,8 +152,7 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
|
||||
it("should render a timestamp", function() {
|
||||
view = mountTestComponent({
|
||||
showTimestamp: true,
|
||||
timestamp: "2015-06-23T22:48:39.738Z"
|
||||
showTimestamp: true
|
||||
});
|
||||
var node = view.getDOMNode();
|
||||
|
||||
@ -157,7 +165,9 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
|
||||
function mountTestComponent(extraProps) {
|
||||
var props = _.extend({
|
||||
dispatcher: dispatcher
|
||||
dispatcher: dispatcher,
|
||||
messageList: [],
|
||||
useDesktopPaths: false
|
||||
}, extraProps);
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.shared.views.chat.TextChatEntriesView, props));
|
||||
@ -172,11 +182,13 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
messageList: [{
|
||||
type: CHAT_MESSAGE_TYPES.RECEIVED,
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello!"
|
||||
message: "Hello!",
|
||||
receivedTimestamp: "2015-06-25T17:53:55.357Z"
|
||||
}, {
|
||||
type: CHAT_MESSAGE_TYPES.SENT,
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Is it me you're looking for?"
|
||||
message: "Is it me you're looking for?",
|
||||
sentTimestamp: "2015-06-25T17:53:55.357Z"
|
||||
}]
|
||||
});
|
||||
node = view.getDOMNode();
|
||||
@ -230,11 +242,13 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
messageList: [{
|
||||
type: CHAT_MESSAGE_TYPES.RECEIVED,
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello!"
|
||||
message: "Hello!",
|
||||
receivedTimestamp: "2015-06-25T17:53:55.357Z"
|
||||
}, {
|
||||
type: CHAT_MESSAGE_TYPES.RECEIVED,
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Is it me you're looking for?"
|
||||
message: "Is it me you're looking for?",
|
||||
sentTimestamp: "2015-06-25T17:53:55.357Z"
|
||||
}]
|
||||
});
|
||||
node = view.getDOMNode();
|
||||
@ -249,7 +263,9 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
|
||||
function mountTestComponent(extraProps) {
|
||||
var props = _.extend({
|
||||
dispatcher: dispatcher
|
||||
dispatcher: dispatcher,
|
||||
showRoomName: false,
|
||||
useDesktopPaths: false
|
||||
}, extraProps);
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.shared.views.chat.TextChatView, props));
|
||||
@ -288,30 +304,16 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
.to.eql(2);
|
||||
});
|
||||
|
||||
it("should not display anything if no messages and text chat not enabled and showAlways is false", function() {
|
||||
it("should not display the view if no messages and text chat not enabled", function() {
|
||||
store.setStoreState({ textChatEnabled: false });
|
||||
|
||||
view = mountTestComponent({
|
||||
showAlways: false
|
||||
});
|
||||
view = mountTestComponent();
|
||||
|
||||
expect(view.getDOMNode()).eql(null);
|
||||
});
|
||||
|
||||
it("should display the view if no messages and text chat not enabled and showAlways is true", function() {
|
||||
store.setStoreState({ textChatEnabled: false });
|
||||
|
||||
view = mountTestComponent({
|
||||
showAlways: true
|
||||
});
|
||||
|
||||
expect(view.getDOMNode()).not.eql(null);
|
||||
});
|
||||
|
||||
it("should display the view if text chat is enabled", function() {
|
||||
view = mountTestComponent({
|
||||
showAlways: true
|
||||
});
|
||||
it("should display the view if no messages and text chat is enabled", function() {
|
||||
view = mountTestComponent();
|
||||
|
||||
expect(view.getDOMNode()).not.eql(null);
|
||||
});
|
||||
@ -330,11 +332,15 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
|
||||
store.receivedTextChatMessage({
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello!"
|
||||
message: "Hello!",
|
||||
sentTimestamp: "1970-01-01T00:03:00.000Z",
|
||||
receivedTimestamp: "1970-01-01T00:03:00.000Z"
|
||||
});
|
||||
store.sendTextChatMessage({
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Is it me you're looking for?"
|
||||
message: "Is it me you're looking for?",
|
||||
sentTimestamp: "1970-01-01T00:03:00.000Z",
|
||||
receivedTimestamp: "1970-01-01T00:03:00.000Z"
|
||||
});
|
||||
|
||||
var node = view.getDOMNode();
|
||||
@ -359,17 +365,18 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
});
|
||||
|
||||
it("should add `received` CSS class selector to msg of type RECEIVED",
|
||||
function() {
|
||||
var node = mountTestComponent().getDOMNode();
|
||||
function() {
|
||||
var node = mountTestComponent().getDOMNode();
|
||||
|
||||
store.receivedTextChatMessage({
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Foo",
|
||||
timestamp: 0
|
||||
});
|
||||
store.receivedTextChatMessage({
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Foo",
|
||||
sentTimestamp: "1970-01-01T00:03:00.000Z",
|
||||
receivedTimestamp: "1970-01-01T00:03:00.000Z"
|
||||
});
|
||||
|
||||
expect(node.querySelector(".received")).to.not.eql(null);
|
||||
});
|
||||
expect(node.querySelector(".received")).to.not.eql(null);
|
||||
});
|
||||
|
||||
it("should render a room name special entry", function() {
|
||||
view = mountTestComponent({
|
||||
@ -392,9 +399,7 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
});
|
||||
|
||||
it("should render a special entry for the context url", function() {
|
||||
view = mountTestComponent({
|
||||
showRoomName: true
|
||||
});
|
||||
view = mountTestComponent();
|
||||
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "A Very Long Conversation Name",
|
||||
|
@ -256,6 +256,9 @@ describe("loop.shared.views", function() {
|
||||
var hangup, publishStream;
|
||||
|
||||
function mountTestComponent(props) {
|
||||
props = _.extend({
|
||||
dispatcher: dispatcher
|
||||
}, props || {});
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ConversationToolbar, props));
|
||||
}
|
||||
@ -362,6 +365,9 @@ describe("loop.shared.views", function() {
|
||||
var fakeSDK, fakeSessionData, fakeSession, fakePublisher, model, fakeAudio;
|
||||
|
||||
function mountTestComponent(props) {
|
||||
props = _.extend({
|
||||
dispatcher: dispatcher
|
||||
}, props || {});
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ConversationView, props));
|
||||
}
|
||||
@ -676,6 +682,9 @@ describe("loop.shared.views", function() {
|
||||
var coll, view, testNotif;
|
||||
|
||||
function mountTestComponent(props) {
|
||||
props = _.extend({
|
||||
key: 0
|
||||
}, props || {});
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.NotificationListView, props));
|
||||
}
|
||||
@ -831,7 +840,11 @@ describe("loop.shared.views", function() {
|
||||
|
||||
function mountTestComponent(extraProps) {
|
||||
var props = _.extend({
|
||||
dispatcher: dispatcher
|
||||
allowClick: false,
|
||||
description: "test",
|
||||
dispatcher: dispatcher,
|
||||
showContextTitle: false,
|
||||
useDesktopPaths: false
|
||||
}, extraProps);
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ContextUrlView, props));
|
||||
@ -913,6 +926,9 @@ describe("loop.shared.views", function() {
|
||||
var view;
|
||||
|
||||
function mountTestComponent(props) {
|
||||
props = _.extend({
|
||||
isLoading: false
|
||||
}, props || {});
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.MediaView, props));
|
||||
}
|
||||
|
@ -88,7 +88,7 @@
|
||||
|
||||
describe("Unexpected Warnings Check", function() {
|
||||
it("should long only the warnings we expect", function() {
|
||||
chai.expect(caughtWarnings.length).to.eql(37);
|
||||
chai.expect(caughtWarnings.length).to.eql(36);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -93,7 +93,8 @@ add_test(function test_reconnect_websocket() {
|
||||
// The uaID is cleared to force re-regsitration of all notification channels.
|
||||
add_test(function test_reopen_websocket() {
|
||||
MozLoopPushHandler.uaID = undefined;
|
||||
MozLoopPushHandler.registeredChannels = {}; //Do this to force a new registration callback.
|
||||
// Do this to force a new registration callback.
|
||||
MozLoopPushHandler.registeredChannels = {};
|
||||
mockWebSocket.serverClose();
|
||||
// Previously registered onRegistration callbacks will fire and be checked (see above).
|
||||
});
|
||||
|
@ -25,6 +25,10 @@ window.queuedFrames = [];
|
||||
*/
|
||||
window.Frame = React.createClass({
|
||||
propTypes: {
|
||||
children: React.PropTypes.oneOfType([
|
||||
React.PropTypes.element,
|
||||
React.PropTypes.arrayOf(React.PropTypes.element)
|
||||
]).isRequired,
|
||||
className: React.PropTypes.string,
|
||||
/* By default, <link rel="stylesheet> nodes from the containing frame's
|
||||
head will be cloned into this iframe. However, if the link also has
|
||||
|
@ -1243,8 +1243,8 @@
|
||||
width: 298},
|
||||
React.createElement("div", {className: "fx-embedded"},
|
||||
React.createElement(TextChatView, {dispatcher: dispatcher,
|
||||
showAlways: false,
|
||||
showRoomName: false})
|
||||
showRoomName: false,
|
||||
useDesktopPaths: false})
|
||||
)
|
||||
),
|
||||
|
||||
@ -1257,8 +1257,8 @@
|
||||
React.createElement("div", {className: "media-wrapper"},
|
||||
React.createElement(TextChatView, {
|
||||
dispatcher: dispatcher,
|
||||
showAlways: true,
|
||||
showRoomName: true})
|
||||
showRoomName: true,
|
||||
useDesktopPaths: false})
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -1313,7 +1313,7 @@
|
||||
|
||||
// This simulates the mocha layout for errors which means we can run
|
||||
// this alongside our other unit tests but use the same harness.
|
||||
var expectedWarningsCount = 53;
|
||||
var expectedWarningsCount = 29;
|
||||
var warningsMismatch = caughtWarnings.length !== expectedWarningsCount;
|
||||
if (uncaughtError || warningsMismatch) {
|
||||
$("#results").append("<div class='failures'><em>" +
|
||||
|
@ -1243,8 +1243,8 @@
|
||||
width={298}>
|
||||
<div className="fx-embedded">
|
||||
<TextChatView dispatcher={dispatcher}
|
||||
showAlways={false}
|
||||
showRoomName={false} />
|
||||
showRoomName={false}
|
||||
useDesktopPaths={false} />
|
||||
</div>
|
||||
</FramedExample>
|
||||
|
||||
@ -1257,8 +1257,8 @@
|
||||
<div className="media-wrapper">
|
||||
<TextChatView
|
||||
dispatcher={dispatcher}
|
||||
showAlways={true}
|
||||
showRoomName={true} />
|
||||
showRoomName={true}
|
||||
useDesktopPaths={false} />
|
||||
</div>
|
||||
</div>
|
||||
</FramedExample>
|
||||
@ -1313,7 +1313,7 @@
|
||||
|
||||
// This simulates the mocha layout for errors which means we can run
|
||||
// this alongside our other unit tests but use the same harness.
|
||||
var expectedWarningsCount = 53;
|
||||
var expectedWarningsCount = 29;
|
||||
var warningsMismatch = caughtWarnings.length !== expectedWarningsCount;
|
||||
if (uncaughtError || warningsMismatch) {
|
||||
$("#results").append("<div class='failures'><em>" +
|
||||
|
@ -37,10 +37,13 @@ XPIDL_MODULE = 'browsercompsbase'
|
||||
|
||||
EXTRA_PP_COMPONENTS += [
|
||||
'BrowserComponents.manifest',
|
||||
'nsBrowserContentHandler.js',
|
||||
'nsBrowserGlue.js',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'nsBrowserContentHandler.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'distribution.js',
|
||||
]
|
||||
|
@ -1,14 +1,17 @@
|
||||
# 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 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
||||
"resource:///modules/RecentWindow.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
|
||||
"@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
|
||||
|
||||
const nsISupports = Components.interfaces.nsISupports;
|
||||
|
||||
@ -40,9 +43,6 @@ const NS_BINDING_ABORTED = Components.results.NS_BINDING_ABORTED;
|
||||
const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
|
||||
const NS_ERROR_ABORT = Components.results.NS_ERROR_ABORT;
|
||||
|
||||
const URI_INHERITS_SECURITY_CONTEXT = Components.interfaces.nsIHttpProtocolHandler
|
||||
.URI_INHERITS_SECURITY_CONTEXT;
|
||||
|
||||
function shouldLoadURI(aURI) {
|
||||
if (aURI && !aURI.schemeIs("chrome"))
|
||||
return true;
|
||||
@ -390,14 +390,22 @@ nsBrowserContentHandler.prototype = {
|
||||
openPreferences();
|
||||
cmdLine.preventDefault = true;
|
||||
} else try {
|
||||
// only load URIs which do not inherit chrome privs
|
||||
var features = "chrome,dialog=no,all" + this.getFeatures(cmdLine);
|
||||
var uri = resolveURIInternal(cmdLine, chromeParam);
|
||||
var netutil = Components.classes["@mozilla.org/network/util;1"]
|
||||
.getService(nsINetUtil);
|
||||
if (!netutil.URIChainHasFlags(uri, URI_INHERITS_SECURITY_CONTEXT)) {
|
||||
let isLocal = (uri) => {
|
||||
let localSchemes = new Set(["chrome", "file", "resource"]);
|
||||
if (uri instanceof Components.interfaces.nsINestedURI) {
|
||||
uri = uri.QueryInterface(Components.interfaces.nsINestedURI).innerMostURI;
|
||||
}
|
||||
return localSchemes.has(uri.scheme);
|
||||
};
|
||||
if (isLocal(uri)) {
|
||||
// If the URI is local, we are sure it won't wrongly inherit chrome privs
|
||||
var features = "chrome,dialog=no,all" + this.getFeatures(cmdLine);
|
||||
openWindow(null, uri.spec, "_blank", features);
|
||||
cmdLine.preventDefault = true;
|
||||
} else {
|
||||
dump("*** Preventing load of web URI as chrome\n");
|
||||
dump(" If you're trying to load a webpage, do not pass --chrome.\n");
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
@ -452,31 +460,35 @@ nsBrowserContentHandler.prototype = {
|
||||
cmdLine.preventDefault = true;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Handle "? searchterm" for Windows Vista start menu integration
|
||||
for (var i = cmdLine.length - 1; i >= 0; --i) {
|
||||
var param = cmdLine.getArgument(i);
|
||||
if (param.match(/^\? /)) {
|
||||
cmdLine.removeArguments(i, i);
|
||||
cmdLine.preventDefault = true;
|
||||
if (AppConstants.platform == "win") {
|
||||
// Handle "? searchterm" for Windows Vista start menu integration
|
||||
for (var i = cmdLine.length - 1; i >= 0; --i) {
|
||||
var param = cmdLine.getArgument(i);
|
||||
if (param.match(/^\? /)) {
|
||||
cmdLine.removeArguments(i, i);
|
||||
cmdLine.preventDefault = true;
|
||||
|
||||
searchParam = param.substr(2);
|
||||
doSearch(searchParam, cmdLine);
|
||||
searchParam = param.substr(2);
|
||||
doSearch(searchParam, cmdLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
},
|
||||
|
||||
helpInfo : " --browser Open a browser window.\n" +
|
||||
" --new-window <url> Open <url> in a new window.\n" +
|
||||
" --new-tab <url> Open <url> in a new tab.\n" +
|
||||
" --private-window <url> Open <url> in a new private window.\n" +
|
||||
#ifdef XP_WIN
|
||||
" --preferences Open Options dialog.\n" +
|
||||
#else
|
||||
" --preferences Open Preferences dialog.\n" +
|
||||
#endif
|
||||
" --search <term> Search <term> with your default search engine.\n",
|
||||
get helpInfo() {
|
||||
let info =
|
||||
" --browser Open a browser window.\n" +
|
||||
" --new-window <url> Open <url> in a new window.\n" +
|
||||
" --new-tab <url> Open <url> in a new tab.\n" +
|
||||
" --private-window <url> Open <url> in a new private window.\n";
|
||||
if (AppConstants.platform == "win") {
|
||||
info += " --preferences Open Options dialog.\n";
|
||||
} else {
|
||||
info += " --preferences Open Preferences dialog.\n";
|
||||
}
|
||||
info += " --search <term> Search <term> with your default search engine.\n";
|
||||
return info;
|
||||
},
|
||||
|
||||
/* nsIBrowserHandler */
|
||||
|
||||
@ -687,35 +699,33 @@ nsDefaultCommandLineHandler.prototype = {
|
||||
return this;
|
||||
},
|
||||
|
||||
#ifdef XP_WIN
|
||||
_haveProfile: false,
|
||||
#endif
|
||||
|
||||
/* nsICommandLineHandler */
|
||||
handle : function dch_handle(cmdLine) {
|
||||
var urilist = [];
|
||||
|
||||
#ifdef XP_WIN
|
||||
// If we don't have a profile selected yet (e.g. the Profile Manager is
|
||||
// displayed) we will crash if we open an url and then select a profile. To
|
||||
// prevent this handle all url command line flags and set the command line's
|
||||
// preventDefault to true to prevent the display of the ui. The initial
|
||||
// command line will be retained when nsAppRunner calls LaunchChild though
|
||||
// urls launched after the initial launch will be lost.
|
||||
if (!this._haveProfile) {
|
||||
try {
|
||||
// This will throw when a profile has not been selected.
|
||||
var fl = Components.classes["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Components.interfaces.nsIProperties);
|
||||
var dir = fl.get("ProfD", Components.interfaces.nsILocalFile);
|
||||
this._haveProfile = true;
|
||||
}
|
||||
catch (e) {
|
||||
while ((ar = cmdLine.handleFlagWithParam("url", false))) { }
|
||||
cmdLine.preventDefault = true;
|
||||
if (AppConstants.platform == "win") {
|
||||
// If we don't have a profile selected yet (e.g. the Profile Manager is
|
||||
// displayed) we will crash if we open an url and then select a profile. To
|
||||
// prevent this handle all url command line flags and set the command line's
|
||||
// preventDefault to true to prevent the display of the ui. The initial
|
||||
// command line will be retained when nsAppRunner calls LaunchChild though
|
||||
// urls launched after the initial launch will be lost.
|
||||
if (!this._haveProfile) {
|
||||
try {
|
||||
// This will throw when a profile has not been selected.
|
||||
var fl = Components.classes["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Components.interfaces.nsIProperties);
|
||||
var dir = fl.get("ProfD", Components.interfaces.nsILocalFile);
|
||||
this._haveProfile = true;
|
||||
}
|
||||
catch (e) {
|
||||
while ((ar = cmdLine.handleFlagWithParam("url", false))) { }
|
||||
cmdLine.preventDefault = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
try {
|
||||
var ar;
|
||||
@ -767,6 +777,16 @@ nsDefaultCommandLineHandler.prototype = {
|
||||
|
||||
}
|
||||
else if (!cmdLine.preventDefault) {
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("win", "10") &&
|
||||
cmdLine.state != nsICommandLine.STATE_INITIAL_LAUNCH &&
|
||||
WindowsUIUtils.inTabletMode) {
|
||||
// In windows 10 tablet mode, do not create a new window, but reuse the existing one.
|
||||
let win = RecentWindow.getMostRecentBrowserWindow();
|
||||
if (win) {
|
||||
win.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Passing defaultArgs, so use NO_EXTERNAL_URIS
|
||||
openWindow(null, gBrowserContentHandler.chromeURL, "_blank",
|
||||
"chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine),
|
||||
|
@ -127,11 +127,20 @@ Factory.prototype = {
|
||||
var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(proto.classID, proto.classDescription,
|
||||
proto.contractID, factory);
|
||||
|
||||
if (proto.classID2) {
|
||||
this._classID2 = proto.classID2;
|
||||
registrar.registerFactory(proto.classID2, proto.classDescription,
|
||||
proto.contractID2, factory);
|
||||
}
|
||||
},
|
||||
|
||||
unregister: function unregister() {
|
||||
var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.unregisterFactory(this._classID, this._factory);
|
||||
if (this._classID2) {
|
||||
registrar.unregisterFactory(this._classID2, this._factory);
|
||||
}
|
||||
this._factory = null;
|
||||
}
|
||||
};
|
||||
@ -320,13 +329,6 @@ let PdfJs = {
|
||||
Cu.import('resource://pdf.js/PdfStreamConverter.jsm');
|
||||
this._pdfStreamConverterFactory.register(PdfStreamConverter);
|
||||
|
||||
this._pdfRedirectorFactory = new Factory();
|
||||
Cu.import('resource://pdf.js/PdfRedirector.jsm');
|
||||
this._pdfRedirectorFactory.register(PdfRedirector);
|
||||
|
||||
Svc.pluginHost.registerPlayPreviewMimeType(PDF_CONTENT_TYPE, true,
|
||||
'data:application/x-moz-playpreview-pdfjs;,');
|
||||
|
||||
this._registered = true;
|
||||
},
|
||||
|
||||
@ -338,12 +340,6 @@ let PdfJs = {
|
||||
Cu.unload('resource://pdf.js/PdfStreamConverter.jsm');
|
||||
delete this._pdfStreamConverterFactory;
|
||||
|
||||
this._pdfRedirectorFactory.unregister();
|
||||
Cu.unload('resource://pdf.js/PdfRedirector.jsm');
|
||||
delete this._pdfRedirectorFactory;
|
||||
|
||||
Svc.pluginHost.unregisterPlayPreviewMimeType(PDF_CONTENT_TYPE);
|
||||
|
||||
this._registered = false;
|
||||
}
|
||||
};
|
||||
|
@ -813,6 +813,9 @@ PdfStreamConverter.prototype = {
|
||||
classDescription: 'pdf.js Component',
|
||||
contractID: '@mozilla.org/streamconv;1?from=application/pdf&to=*/*',
|
||||
|
||||
classID2: Components.ID('{d0c5195d-e798-49d4-b1d3-9324328b2292}'),
|
||||
contractID2: '@mozilla.org/streamconv;1?from=application/pdf&to=text/html',
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsISupports,
|
||||
Ci.nsIStreamConverter,
|
||||
|
@ -260,16 +260,6 @@ if test -n "$MOZ_NATIVE_DEVICES" ; then
|
||||
AC_SUBST(ANDROID_APPCOMPAT_LIB)
|
||||
AC_SUBST(ANDROID_APPCOMPAT_RES)
|
||||
|
||||
ANDROID_RECYCLERVIEW_LIB="$ANDROID_COMPAT_DIR_BASE/v7/recyclerview/libs/android-support-v7-recyclerview.jar"
|
||||
ANDROID_RECYCLERVIEW_RES="$ANDROID_COMPAT_DIR_BASE/v7/recyclerview/res"
|
||||
AC_MSG_CHECKING([for v7 recyclerview library])
|
||||
if ! test -e $ANDROID_RECYCLERVIEW_LIB ; then
|
||||
AC_MSG_ERROR([You must download the v7 recyclerview Android support library. Run the Android SDK tool and install Android Support Library under Extras. See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_RECYCLERVIEW_LIB)])
|
||||
fi
|
||||
AC_MSG_RESULT([$ANDROID_RECYCLERVIEW_LIB])
|
||||
AC_SUBST(ANDROID_RECYCLERVIEW_LIB)
|
||||
AC_SUBST(ANDROID_RECYCLERVIEW_RES)
|
||||
|
||||
ANDROID_MEDIAROUTER_LIB="$ANDROID_COMPAT_DIR_BASE/v7/mediarouter/libs/android-support-v7-mediarouter.jar"
|
||||
ANDROID_MEDIAROUTER_RES="$ANDROID_COMPAT_DIR_BASE/v7/mediarouter/res"
|
||||
AC_MSG_CHECKING([for v7 mediarouter library])
|
||||
@ -390,6 +380,16 @@ case "$target" in
|
||||
fi
|
||||
AC_MSG_RESULT([$ANDROID_COMPAT_LIB])
|
||||
|
||||
ANDROID_RECYCLERVIEW_LIB="$ANDROID_COMPAT_DIR_BASE/v7/recyclerview/libs/android-support-v7-recyclerview.jar"
|
||||
ANDROID_RECYCLERVIEW_RES="$ANDROID_COMPAT_DIR_BASE/v7/recyclerview/res"
|
||||
AC_MSG_CHECKING([for v7 recyclerview library])
|
||||
if ! test -e $ANDROID_RECYCLERVIEW_LIB ; then
|
||||
AC_MSG_ERROR([You must download the v7 recyclerview Android support library. Run the Android SDK tool and install Android Support Library under Extras. See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_RECYCLERVIEW_LIB)])
|
||||
fi
|
||||
AC_MSG_RESULT([$ANDROID_RECYCLERVIEW_LIB])
|
||||
AC_SUBST(ANDROID_RECYCLERVIEW_LIB)
|
||||
AC_SUBST(ANDROID_RECYCLERVIEW_RES)
|
||||
|
||||
dnl Google has a history of moving the Android tools around. We don't
|
||||
dnl care where they are, so let's try to find them anywhere we can.
|
||||
ALL_ANDROID_TOOLS_PATHS="$ANDROID_TOOLS$all_android_build_tools:$ANDROID_PLATFORM_TOOLS"
|
||||
|
@ -44,6 +44,13 @@ nsWebNavigationInfo::IsTypeSupported(const nsACString& aType,
|
||||
// to say for itself.
|
||||
*aIsTypeSupported = nsIWebNavigationInfo::UNSUPPORTED;
|
||||
|
||||
// We want to claim that the type for PDF documents is unsupported,
|
||||
// so that the internal PDF viewer's stream converted will get used.
|
||||
if (aType.LowerCaseEqualsLiteral("application/pdf") &&
|
||||
nsContentUtils::IsPDFJSEnabled()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCString& flatType = PromiseFlatCString(aType);
|
||||
nsresult rv = IsTypeSupportedInternal(flatType, aIsTypeSupported);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -152,6 +152,7 @@
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIStreamConverterService.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIURL.h"
|
||||
@ -6500,6 +6501,19 @@ nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal)
|
||||
IsSitePermAllow(aPrincipal, "allowXULXBL"));
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentUtils::IsPDFJSEnabled()
|
||||
{
|
||||
nsCOMPtr<nsIStreamConverterService> convServ =
|
||||
do_GetService("@mozilla.org/streamConverters;1");
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
bool canConvert = false;
|
||||
if (convServ) {
|
||||
rv = convServ->CanConvert("application/pdf", "text/html", &canConvert);
|
||||
}
|
||||
return NS_SUCCEEDED(rv) && canConvert;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDocumentLoaderFactory>
|
||||
nsContentUtils::FindInternalContentViewer(const nsACString& aType,
|
||||
ContentViewerType* aLoaderType)
|
||||
@ -7922,4 +7936,4 @@ nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
|
||||
|
||||
net::ReferrerPolicy referrerPolicy = aDoc->GetReferrerPolicy();
|
||||
return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
|
||||
}
|
||||
}
|
||||
|
@ -2070,6 +2070,11 @@ public:
|
||||
*/
|
||||
static void XPCOMShutdown();
|
||||
|
||||
/**
|
||||
* Checks if internal PDF viewer is enabled.
|
||||
*/
|
||||
static bool IsPDFJSEnabled();
|
||||
|
||||
enum ContentViewerType
|
||||
{
|
||||
TYPE_UNSUPPORTED,
|
||||
|
@ -552,6 +552,11 @@ IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disables any native PDF plugins, when internal PDF viewer is enabled.
|
||||
if (ext.EqualsIgnoreCase("pdf") && nsContentUtils::IsPDFJSEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
|
||||
|
||||
if (!pluginHost) {
|
||||
@ -2672,6 +2677,13 @@ nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
|
||||
return eType_Image;
|
||||
}
|
||||
|
||||
// Faking support of the PDF content as a document for EMBED tags
|
||||
// when internal PDF viewer is enabled.
|
||||
if (aMIMEType.LowerCaseEqualsLiteral("application/pdf") &&
|
||||
nsContentUtils::IsPDFJSEnabled()) {
|
||||
return eType_Document;
|
||||
}
|
||||
|
||||
// SVGs load as documents, but are their own capability
|
||||
bool isSVG = aMIMEType.LowerCaseEqualsLiteral("image/svg+xml");
|
||||
Capabilities supportType = isSVG ? eSupportSVG : eSupportDocuments;
|
||||
|
@ -68,8 +68,7 @@ class StartDiscoveryTask final : public BluetoothReplyRunnable
|
||||
{
|
||||
public:
|
||||
StartDiscoveryTask(BluetoothAdapter* aAdapter, Promise* aPromise)
|
||||
: BluetoothReplyRunnable(nullptr, aPromise,
|
||||
NS_LITERAL_STRING("StartDiscovery"))
|
||||
: BluetoothReplyRunnable(nullptr, aPromise)
|
||||
, mAdapter(aAdapter)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
@ -117,8 +116,7 @@ class StartLeScanTask final : public BluetoothReplyRunnable
|
||||
public:
|
||||
StartLeScanTask(BluetoothAdapter* aAdapter, Promise* aPromise,
|
||||
const nsTArray<nsString>& aServiceUuids)
|
||||
: BluetoothReplyRunnable(nullptr, aPromise,
|
||||
NS_LITERAL_STRING("StartLeScan"))
|
||||
: BluetoothReplyRunnable(nullptr, aPromise)
|
||||
, mAdapter(aAdapter)
|
||||
, mServiceUuids(aServiceUuids)
|
||||
{
|
||||
@ -175,8 +173,7 @@ public:
|
||||
StopLeScanTask(BluetoothAdapter* aAdapter,
|
||||
Promise* aPromise,
|
||||
const nsAString& aScanUuid)
|
||||
: BluetoothReplyRunnable(nullptr, aPromise,
|
||||
NS_LITERAL_STRING("StopLeScan"))
|
||||
: BluetoothReplyRunnable(nullptr, aPromise)
|
||||
, mAdapter(aAdapter)
|
||||
, mScanUuid(aScanUuid)
|
||||
{
|
||||
@ -574,9 +571,7 @@ BluetoothAdapter::StartDiscovery(ErrorResult& aRv)
|
||||
}
|
||||
|
||||
// Return BluetoothDiscoveryHandle in StartDiscoveryTask
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new StartDiscoveryTask(this, promise);
|
||||
bs->StartDiscoveryInternal(result);
|
||||
bs->StartDiscoveryInternal(new StartDiscoveryTask(this, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -606,11 +601,7 @@ BluetoothAdapter::StopDiscovery(ErrorResult& aRv)
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("StopDiscovery"));
|
||||
bs->StopDiscoveryInternal(result);
|
||||
bs->StopDiscoveryInternal(new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -707,13 +698,10 @@ BluetoothAdapter::SetName(const nsAString& aName, ErrorResult& aRv)
|
||||
nsString name(aName);
|
||||
BluetoothNamedValue property(NS_LITERAL_STRING("Name"),
|
||||
BluetoothValue(name));
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("SetName"));
|
||||
BT_ENSURE_TRUE_REJECT(
|
||||
NS_SUCCEEDED(bs->SetProperty(BluetoothObjectType::TYPE_ADAPTER,
|
||||
property, result)),
|
||||
NS_SUCCEEDED(
|
||||
bs->SetProperty(BluetoothObjectType::TYPE_ADAPTER, property,
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise))),
|
||||
promise,
|
||||
NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
||||
@ -750,13 +738,10 @@ BluetoothAdapter::SetDiscoverable(bool aDiscoverable, ErrorResult& aRv)
|
||||
// Wrap property to set and runnable to handle result
|
||||
BluetoothNamedValue property(NS_LITERAL_STRING("Discoverable"),
|
||||
BluetoothValue(aDiscoverable));
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("SetDiscoverable"));
|
||||
BT_ENSURE_TRUE_REJECT(
|
||||
NS_SUCCEEDED(bs->SetProperty(BluetoothObjectType::TYPE_ADAPTER,
|
||||
property, result)),
|
||||
NS_SUCCEEDED(
|
||||
bs->SetProperty(BluetoothObjectType::TYPE_ADAPTER, property,
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise))),
|
||||
promise,
|
||||
NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
||||
@ -832,19 +817,12 @@ BluetoothAdapter::PairUnpair(bool aPair, const nsAString& aDeviceAddress,
|
||||
|
||||
nsresult rv;
|
||||
if (aPair) {
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("Pair"));
|
||||
rv = bs->CreatePairedDeviceInternal(aDeviceAddress,
|
||||
kCreatePairedDeviceTimeout,
|
||||
result);
|
||||
rv = bs->CreatePairedDeviceInternal(
|
||||
aDeviceAddress, kCreatePairedDeviceTimeout,
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
} else {
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("Unpair"));
|
||||
rv = bs->RemoveDeviceInternal(aDeviceAddress, result);
|
||||
rv = bs->RemoveDeviceInternal(aDeviceAddress,
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
}
|
||||
BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(rv), promise, NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
||||
@ -891,9 +869,7 @@ BluetoothAdapter::Enable(ErrorResult& aRv)
|
||||
|
||||
// Wrap runnable to handle result
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr, /* DOMRequest */
|
||||
promise,
|
||||
NS_LITERAL_STRING("Enable"));
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise);
|
||||
|
||||
if (NS_FAILED(bs->EnableDisable(true, result))) {
|
||||
// Restore adapter state and reject promise
|
||||
@ -932,9 +908,7 @@ BluetoothAdapter::Disable(ErrorResult& aRv)
|
||||
|
||||
// Wrap runnable to handle result
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr, /* DOMRequest */
|
||||
promise,
|
||||
NS_LITERAL_STRING("Disable"));
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise);
|
||||
|
||||
if (NS_FAILED(bs->EnableDisable(false, result))) {
|
||||
// Restore adapter state and reject promise
|
||||
|
@ -54,9 +54,8 @@ class FetchUuidsTask final : public BluetoothReplyRunnable
|
||||
{
|
||||
public:
|
||||
FetchUuidsTask(Promise* aPromise,
|
||||
const nsAString& aName,
|
||||
BluetoothDevice* aDevice)
|
||||
: BluetoothReplyRunnable(nullptr /* DOMRequest */, aPromise, aName)
|
||||
: BluetoothReplyRunnable(nullptr, aPromise)
|
||||
, mDevice(aDevice)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
@ -185,16 +184,14 @@ BluetoothDevice::FetchUuids(ErrorResult& aRv)
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
// Ensure BluetoothService is available
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new FetchUuidsTask(promise,
|
||||
NS_LITERAL_STRING("FetchUuids"),
|
||||
this);
|
||||
|
||||
nsresult rv = bs->FetchUuidsInternal(mAddress, result);
|
||||
BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(rv), promise, NS_ERROR_DOM_OPERATION_ERR);
|
||||
BT_ENSURE_TRUE_REJECT(
|
||||
NS_SUCCEEDED(
|
||||
bs->FetchUuidsInternal(mAddress, new FetchUuidsTask(promise, this))),
|
||||
promise, NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -382,7 +379,7 @@ BluetoothDevice::UpdatePropertiesFromAdvData(const nsTArray<uint8_t>& aAdvData)
|
||||
dataLength -= 2;
|
||||
}
|
||||
|
||||
char uuidStr[36];
|
||||
char uuidStr[37]; // one more char to be null-terminated
|
||||
if (type == GAP_INCOMPLETE_UUID16 || type == GAP_COMPLETE_UUID16) {
|
||||
// Convert 16-bits UUID into string.
|
||||
snprintf(uuidStr, sizeof(uuidStr),
|
||||
|
@ -121,13 +121,8 @@ BluetoothGatt::Connect(ErrorResult& aRv)
|
||||
}
|
||||
|
||||
UpdateConnectionState(BluetoothConnectionState::Connecting);
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("ConnectGattClient"));
|
||||
bs->ConnectGattClientInternal(mAppUuid,
|
||||
mDeviceAddr,
|
||||
result);
|
||||
bs->ConnectGattClientInternal(
|
||||
mAppUuid, mDeviceAddr, new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -153,11 +148,8 @@ BluetoothGatt::Disconnect(ErrorResult& aRv)
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
UpdateConnectionState(BluetoothConnectionState::Disconnecting);
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("DisconnectGattClient"));
|
||||
bs->DisconnectGattClientInternal(mAppUuid, mDeviceAddr, result);
|
||||
bs->DisconnectGattClientInternal(
|
||||
mAppUuid, mDeviceAddr, new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -166,8 +158,7 @@ class ReadRemoteRssiTask final : public BluetoothReplyRunnable
|
||||
{
|
||||
public:
|
||||
ReadRemoteRssiTask(Promise* aPromise)
|
||||
: BluetoothReplyRunnable(nullptr, aPromise,
|
||||
NS_LITERAL_STRING("GattClientReadRemoteRssi"))
|
||||
: BluetoothReplyRunnable(nullptr, aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
}
|
||||
@ -205,9 +196,8 @@ BluetoothGatt::ReadRemoteRssi(ErrorResult& aRv)
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new ReadRemoteRssiTask(promise);
|
||||
bs->GattClientReadRemoteRssiInternal(mClientIf, mDeviceAddr, result);
|
||||
bs->GattClientReadRemoteRssiInternal(
|
||||
mClientIf, mDeviceAddr, new ReadRemoteRssiTask(promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -236,11 +226,9 @@ BluetoothGatt::DiscoverServices(ErrorResult& aRv)
|
||||
mDiscoveringServices = true;
|
||||
mServices.Clear();
|
||||
BluetoothGattBinding::ClearCachedServicesValue(this);
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("DiscoverGattServices"));
|
||||
bs->DiscoverGattServicesInternal(mAppUuid, result);
|
||||
|
||||
bs->DiscoverGattServicesInternal(
|
||||
mAppUuid, new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
@ -99,15 +99,9 @@ BluetoothGattCharacteristic::StartNotifications(ErrorResult& aRv)
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
BT_ENSURE_TRUE_REJECT(mService, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(
|
||||
nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("GattClientStartNotifications"));
|
||||
bs->GattClientStartNotificationsInternal(mService->GetAppUuid(),
|
||||
mService->GetServiceId(),
|
||||
mCharId,
|
||||
result);
|
||||
bs->GattClientStartNotificationsInternal(
|
||||
mService->GetAppUuid(), mService->GetServiceId(), mCharId,
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -128,15 +122,9 @@ BluetoothGattCharacteristic::StopNotifications(ErrorResult& aRv)
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
BT_ENSURE_TRUE_REJECT(mService, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(
|
||||
nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("GattClientStopNotifications"));
|
||||
bs->GattClientStopNotificationsInternal(mService->GetAppUuid(),
|
||||
mService->GetServiceId(),
|
||||
mCharId,
|
||||
result);
|
||||
bs->GattClientStopNotificationsInternal(
|
||||
mService->GetAppUuid(), mService->GetServiceId(), mCharId,
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -212,9 +200,7 @@ class ReadValueTask final : public BluetoothReplyRunnable
|
||||
{
|
||||
public:
|
||||
ReadValueTask(BluetoothGattCharacteristic* aCharacteristic, Promise* aPromise)
|
||||
: BluetoothReplyRunnable(
|
||||
nullptr, aPromise,
|
||||
NS_LITERAL_STRING("GattClientReadCharacteristicValue"))
|
||||
: BluetoothReplyRunnable(nullptr, aPromise)
|
||||
, mCharacteristic(aCharacteristic)
|
||||
{
|
||||
MOZ_ASSERT(aCharacteristic);
|
||||
@ -271,11 +257,9 @@ BluetoothGattCharacteristic::ReadValue(ErrorResult& aRv)
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result = new ReadValueTask(this, promise);
|
||||
bs->GattClientReadCharacteristicValueInternal(mService->GetAppUuid(),
|
||||
mService->GetServiceId(),
|
||||
mCharId,
|
||||
result);
|
||||
bs->GattClientReadCharacteristicValueInternal(
|
||||
mService->GetAppUuid(), mService->GetServiceId(), mCharId,
|
||||
new ReadValueTask(this, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -308,14 +292,10 @@ BluetoothGattCharacteristic::WriteValue(const ArrayBuffer& aValue,
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result = new BluetoothVoidReplyRunnable(
|
||||
nullptr, promise, NS_LITERAL_STRING("GattClientWriteCharacteristicValue"));
|
||||
bs->GattClientWriteCharacteristicValueInternal(mService->GetAppUuid(),
|
||||
mService->GetServiceId(),
|
||||
mCharId,
|
||||
mWriteType,
|
||||
value,
|
||||
result);
|
||||
bs->GattClientWriteCharacteristicValueInternal(
|
||||
mService->GetAppUuid(), mService->GetServiceId(),
|
||||
mCharId, mWriteType, value,
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
@ -122,9 +122,7 @@ class ReadValueTask final : public BluetoothReplyRunnable
|
||||
{
|
||||
public:
|
||||
ReadValueTask(BluetoothGattDescriptor* aDescriptor, Promise* aPromise)
|
||||
: BluetoothReplyRunnable(
|
||||
nullptr, aPromise,
|
||||
NS_LITERAL_STRING("GattClientReadDescriptorValue"))
|
||||
: BluetoothReplyRunnable(nullptr, aPromise)
|
||||
, mDescriptor(aDescriptor)
|
||||
{
|
||||
MOZ_ASSERT(aDescriptor);
|
||||
@ -177,13 +175,12 @@ BluetoothGattDescriptor::ReadValue(ErrorResult& aRv)
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result = new ReadValueTask(this, promise);
|
||||
bs->GattClientReadDescriptorValueInternal(
|
||||
mCharacteristic->Service()->GetAppUuid(),
|
||||
mCharacteristic->Service()->GetServiceId(),
|
||||
mCharacteristic->GetCharacteristicId(),
|
||||
mDescriptorId,
|
||||
result);
|
||||
new ReadValueTask(this, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -209,15 +206,13 @@ BluetoothGattDescriptor::WriteValue(
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result = new BluetoothVoidReplyRunnable(
|
||||
nullptr, promise, NS_LITERAL_STRING("GattClientWriteDescriptorValue"));
|
||||
bs->GattClientWriteDescriptorValueInternal(
|
||||
mCharacteristic->Service()->GetAppUuid(),
|
||||
mCharacteristic->Service()->GetServiceId(),
|
||||
mCharacteristic->GetCharacteristicId(),
|
||||
mDescriptorId,
|
||||
value,
|
||||
result);
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
@ -82,11 +82,8 @@ BluetoothPairingHandle::SetPinCode(const nsAString& aPinCode, ErrorResult& aRv)
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("SetPinCode"));
|
||||
bs->PinReplyInternal(mDeviceAddress, true /* accept */, aPinCode, result);
|
||||
bs->PinReplyInternal(mDeviceAddress, true /* accept */, aPinCode,
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -116,12 +113,8 @@ BluetoothPairingHandle::Accept(ErrorResult& aRv)
|
||||
promise,
|
||||
NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("Accept"));
|
||||
bs->SspReplyInternal(
|
||||
mDeviceAddress, variant, true /* aAccept */, result);
|
||||
bs->SspReplyInternal(mDeviceAddress, variant, true /* aAccept */,
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -141,22 +134,17 @@ BluetoothPairingHandle::Reject(ErrorResult& aRv)
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("Reject"));
|
||||
|
||||
if (mType.EqualsLiteral(PAIRING_REQ_TYPE_ENTERPINCODE)) { // Pin request
|
||||
bs->PinReplyInternal(
|
||||
mDeviceAddress, false /* aAccept */, EmptyString(), result);
|
||||
bs->PinReplyInternal(mDeviceAddress, false /* aAccept */, EmptyString(),
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
} else { // Ssp request
|
||||
BluetoothSspVariant variant;
|
||||
BT_ENSURE_TRUE_REJECT(GetSspVariant(variant),
|
||||
promise,
|
||||
NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
||||
bs->SspReplyInternal(
|
||||
mDeviceAddress, variant, false /* aAccept */, result);
|
||||
bs->SspReplyInternal(mDeviceAddress, variant, false /* aAccept */,
|
||||
new BluetoothVoidReplyRunnable(nullptr, promise));
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
|
@ -17,12 +17,10 @@ using namespace mozilla::dom;
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
BluetoothReplyRunnable::BluetoothReplyRunnable(nsIDOMDOMRequest* aReq,
|
||||
Promise* aPromise,
|
||||
const nsAString& aName)
|
||||
Promise* aPromise)
|
||||
: mDOMRequest(aReq)
|
||||
, mPromise(aPromise)
|
||||
, mErrorStatus(STATUS_FAIL)
|
||||
, mName(aName)
|
||||
{}
|
||||
|
||||
void
|
||||
@ -121,9 +119,8 @@ BluetoothReplyRunnable::Run()
|
||||
}
|
||||
|
||||
BluetoothVoidReplyRunnable::BluetoothVoidReplyRunnable(nsIDOMDOMRequest* aReq,
|
||||
Promise* aPromise,
|
||||
const nsAString& aName)
|
||||
: BluetoothReplyRunnable(aReq, aPromise, aName)
|
||||
Promise* aPromise)
|
||||
: BluetoothReplyRunnable(aReq, aPromise)
|
||||
{}
|
||||
|
||||
BluetoothVoidReplyRunnable::~BluetoothVoidReplyRunnable()
|
||||
|
@ -30,8 +30,7 @@ public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
BluetoothReplyRunnable(nsIDOMDOMRequest* aReq,
|
||||
Promise* aPromise = nullptr,
|
||||
const nsAString& aName = EmptyString());
|
||||
Promise* aPromise = nullptr);
|
||||
|
||||
void SetReply(BluetoothReply* aReply);
|
||||
|
||||
@ -71,15 +70,13 @@ private:
|
||||
|
||||
BluetoothStatus mErrorStatus;
|
||||
nsString mErrorString;
|
||||
nsString mName; // for debugging
|
||||
};
|
||||
|
||||
class BluetoothVoidReplyRunnable : public BluetoothReplyRunnable
|
||||
{
|
||||
public:
|
||||
BluetoothVoidReplyRunnable(nsIDOMDOMRequest* aReq,
|
||||
Promise* aPromise = nullptr,
|
||||
const nsAString& aName = EmptyString());
|
||||
Promise* aPromise = nullptr);
|
||||
~BluetoothVoidReplyRunnable();
|
||||
|
||||
protected:
|
||||
|
@ -173,7 +173,7 @@ HasTouchListener(nsIContent* aContent)
|
||||
}
|
||||
|
||||
static bool
|
||||
IsElementClickable(nsIFrame* aFrame, nsIAtom* stopAt = nullptr)
|
||||
IsElementClickable(nsIFrame* aFrame, nsIAtom* stopAt = nullptr, nsAutoString* aLabelTargetId = nullptr)
|
||||
{
|
||||
// Input events propagate up the content tree so we'll follow the content
|
||||
// ancestors to look for elements accepting the click.
|
||||
@ -188,8 +188,13 @@ IsElementClickable(nsIFrame* aFrame, nsIAtom* stopAt = nullptr)
|
||||
if (content->IsAnyOfHTMLElements(nsGkAtoms::button,
|
||||
nsGkAtoms::input,
|
||||
nsGkAtoms::select,
|
||||
nsGkAtoms::textarea,
|
||||
nsGkAtoms::label)) {
|
||||
nsGkAtoms::textarea)) {
|
||||
return true;
|
||||
}
|
||||
if (content->IsHTMLElement(nsGkAtoms::label)) {
|
||||
if (aLabelTargetId) {
|
||||
content->GetAttr(kNameSpaceID_None, nsGkAtoms::_for, *aLabelTargetId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -320,6 +325,22 @@ SubtractFromExposedRegion(nsRegion* aExposedRegion, const nsRegion& aRegion)
|
||||
}
|
||||
}
|
||||
|
||||
// Search in the list of frames aCandidates if the element with the id "aLabelTargetId"
|
||||
// is present.
|
||||
static bool IsElementPresent(nsTArray<nsIFrame*>& aCandidates, const nsAutoString& aLabelTargetId)
|
||||
{
|
||||
for (uint32_t i = 0; i < aCandidates.Length(); ++i) {
|
||||
nsIFrame* f = aCandidates[i];
|
||||
nsIContent* aContent = f->GetContent();
|
||||
if (aContent && aContent->IsElement()) {
|
||||
if (aContent->GetID() && aLabelTargetId == nsAtomString(aContent->GetID())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static nsIFrame*
|
||||
GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame,
|
||||
const nsRect& aTargetRect, const EventRadiusPrefs* aPrefs,
|
||||
@ -351,7 +372,8 @@ GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame,
|
||||
SubtractFromExposedRegion(&exposedRegion, region);
|
||||
}
|
||||
|
||||
if (!IsElementClickable(f, nsGkAtoms::body)) {
|
||||
nsAutoString labelTargetId;
|
||||
if (!IsElementClickable(f, nsGkAtoms::body, &labelTargetId)) {
|
||||
PET_LOG(" candidate %p was not clickable\n", f);
|
||||
continue;
|
||||
}
|
||||
@ -366,7 +388,13 @@ GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame,
|
||||
continue;
|
||||
}
|
||||
|
||||
(*aElementsInCluster)++;
|
||||
// If the first clickable ancestor of f is a label element
|
||||
// and "for" attribute is present in label element, search the frame list for the "for" element
|
||||
// If this element is present in the current list, do not count the frame in
|
||||
// the cluster elements counter
|
||||
if (labelTargetId.IsEmpty() || !IsElementPresent(aCandidates, labelTargetId)) {
|
||||
(*aElementsInCluster)++;
|
||||
}
|
||||
|
||||
// distance is in appunits
|
||||
float distance = ComputeDistanceFromRegion(aPointRelativeToRootFrame, region);
|
||||
|
@ -1503,7 +1503,7 @@ skip-if(B2G||Mulet) fuzzy-if(Android&&AndroidVersion>=15,12,300) == 551463-1.htm
|
||||
random != 553571-1.html 553571-1-notref.html # expect dotted circle in test, not in ref: "fails" under harfbuzz, which doesn't consider the sequence invalid
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,128,91) random-if(d2d) skip-if(azureSkiaGL) == 555388-1.html 555388-1-ref.html
|
||||
== 556661-1.html 556661-1-ref.html
|
||||
skip-if(B2G||Mulet) fails-if(Android) == 557087-1.html 557087-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) == 557087-1.html 557087-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fails-if(Android) == 557087-2.html 557087-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
== 557736-1.html 557736-1-ref.html
|
||||
skip-if((B2G&&browserIsRemote)||Mulet) != 558011-1.xul 558011-1-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
|
@ -1,9 +1,9 @@
|
||||
== button-fieldset-1.html button-fieldset-ref.html
|
||||
fails-if(Android||B2G||Mulet) == button-fieldset-2.html button-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(Android||B2G||Mulet) == button-fieldset-3.html button-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(B2G||Mulet) == button-fieldset-2.html button-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(B2G||Mulet) == button-fieldset-3.html button-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
== button-fieldset-4.html button-fieldset-ref.html
|
||||
== button-fieldset-legend-1.html button-fieldset-legend-ref-1.html
|
||||
fails-if(Android||B2G||Mulet) == button-fieldset-legend-2.html button-fieldset-legend-ref-2.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(Android||B2G||Mulet) == button-fieldset-legend-3.html button-fieldset-legend-ref-3.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(B2G||Mulet) == button-fieldset-legend-2.html button-fieldset-legend-ref-2.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(B2G||Mulet) == button-fieldset-legend-3.html button-fieldset-legend-ref-3.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
== button-fieldset-legend-4.html button-fieldset-legend-ref-4.html
|
||||
== button-fieldset-legend-5.html button-fieldset-legend-ref-5.html
|
||||
|
@ -1,7 +1,7 @@
|
||||
== select-fieldset-1.html select-fieldset-ref.html
|
||||
fails-if(Android||B2G||Mulet) == select-fieldset-2.html select-fieldset-ref-disabled.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(Android||B2G||Mulet) == select-fieldset-3.html select-fieldset-ref-disabled.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
== select-fieldset-4.html select-fieldset-ref.html
|
||||
fails-if(Android) == select-fieldset-4.html select-fieldset-ref.html
|
||||
== select-fieldset-legend-1.html select-fieldset-legend-ref-1.html
|
||||
fails-if(Android||B2G||Mulet) == select-fieldset-legend-2.html select-fieldset-legend-ref-2.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(Android||B2G||Mulet) == select-fieldset-legend-3.html select-fieldset-legend-ref-3.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
|
@ -1,9 +1,9 @@
|
||||
== button-fieldset-1.html button-fieldset-ref.html
|
||||
fails-if(Android||B2G||Mulet) == button-fieldset-2.html button-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(Android||B2G||Mulet) == button-fieldset-3.html button-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(B2G||Mulet) == button-fieldset-2.html button-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(B2G||Mulet) == button-fieldset-3.html button-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
== button-fieldset-4.html button-fieldset-ref.html
|
||||
== button-fieldset-legend-1.html button-fieldset-legend-ref-1.html
|
||||
fails-if(Android||B2G||Mulet) == button-fieldset-legend-2.html button-fieldset-legend-ref-2.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(Android||B2G||Mulet) == button-fieldset-legend-3.html button-fieldset-legend-ref-3.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(B2G||Mulet) == button-fieldset-legend-2.html button-fieldset-legend-ref-2.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(B2G||Mulet) == button-fieldset-legend-3.html button-fieldset-legend-ref-3.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
== button-fieldset-legend-4.html button-fieldset-legend-ref-4.html
|
||||
== button-fieldset-legend-5.html button-fieldset-legend-ref-5.html
|
||||
|
@ -275,6 +275,9 @@ pref("browser.search.order.US.3", "chrome://browser/locale/region.properties");
|
||||
// disable updating
|
||||
pref("browser.search.update", false);
|
||||
|
||||
// enable tracking protection for private browsing
|
||||
pref("privacy.trackingprotection.pbmode.enabled", true);
|
||||
|
||||
// disable search suggestions by default
|
||||
pref("browser.search.suggest.enabled", false);
|
||||
pref("browser.search.suggest.prompted", false);
|
||||
|
@ -2101,6 +2101,9 @@ public class BrowserApp extends GeckoApp
|
||||
mDoorHangerPopup.disable();
|
||||
}
|
||||
mTabsPanel.show(panel);
|
||||
|
||||
// Hide potentially visible "find in page" bar (Bug 1177338)
|
||||
mFindInPageBar.hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.Locales.LocaleAwareFragmentActivity;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountUtils;
|
||||
import org.mozilla.gecko.fxa.FirefoxAccounts;
|
||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
@ -180,6 +181,16 @@ public class FxAccountStatusActivity extends LocaleAwareFragmentActivity {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.enable_debug_mode) {
|
||||
FxAccountUtils.LOG_PERSONAL_INFORMATION = !FxAccountUtils.LOG_PERSONAL_INFORMATION;
|
||||
Toast.makeText(this, (FxAccountUtils.LOG_PERSONAL_INFORMATION ? "Enabled" : "Disabled") +
|
||||
" Firefox Account personal information!", Toast.LENGTH_LONG).show();
|
||||
item.setChecked(!item.isChecked());
|
||||
// Display or hide debug options.
|
||||
statusFragment.hardRefresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@ -187,6 +198,11 @@ public class FxAccountStatusActivity extends LocaleAwareFragmentActivity {
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
final MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.fxaccount_status_menu, menu);
|
||||
// !defined(MOZILLA_OFFICIAL) || defined(NIGHTLY_BUILD) || defined(MOZ_DEBUG)
|
||||
boolean enabled = !AppConstants.MOZILLA_OFFICIAL || AppConstants.NIGHTLY_BUILD || AppConstants.DEBUG_BUILD;
|
||||
if (!enabled) {
|
||||
menu.removeItem(R.id.enable_debug_mode);
|
||||
}
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
};
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ import android.preference.PreferenceScreen;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.Target;
|
||||
@ -93,15 +92,6 @@ public class FxAccountStatusFragment
|
||||
// configured to use a custom Sync server. In debug mode, this is set.
|
||||
private static boolean ALWAYS_SHOW_SYNC_SERVER = false;
|
||||
|
||||
// If the user clicks the email field this many times, the debug / personal
|
||||
// information logging setting will toggle. The setting is not permanent: it
|
||||
// lasts until this process is killed. We don't want to dump PII to the log
|
||||
// for a long time!
|
||||
private final int NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG =
|
||||
// !defined(MOZILLA_OFFICIAL) || defined(NIGHTLY_BUILD) || defined(MOZ_DEBUG)
|
||||
(!AppConstants.MOZILLA_OFFICIAL || AppConstants.NIGHTLY_BUILD || AppConstants.DEBUG_BUILD) ? 5 : -1 /* infinite */;
|
||||
private int debugClickCount = 0;
|
||||
|
||||
protected PreferenceCategory accountCategory;
|
||||
protected Preference profilePreference;
|
||||
protected Preference emailPreference;
|
||||
@ -245,18 +235,6 @@ public class FxAccountStatusFragment
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
final Preference personalInformationPreference = AppConstants.MOZ_ANDROID_FIREFOX_ACCOUNT_PROFILES ? profilePreference : emailPreference;
|
||||
if (preference == personalInformationPreference) {
|
||||
debugClickCount += 1;
|
||||
if (NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG > 0 && debugClickCount >= NUMBER_OF_CLICKS_TO_TOGGLE_DEBUG) {
|
||||
debugClickCount = 0;
|
||||
FxAccountUtils.LOG_PERSONAL_INFORMATION = !FxAccountUtils.LOG_PERSONAL_INFORMATION;
|
||||
Toast.makeText(getActivity(), "Toggled logging Firefox Account personal information!", Toast.LENGTH_LONG).show();
|
||||
hardRefresh(); // Display or hide debug options.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (preference == needsPasswordPreference) {
|
||||
Intent intent = new Intent(getActivity(), FxAccountUpdateCredentialsActivity.class);
|
||||
final Bundle extras = getExtrasForAccount();
|
||||
|
@ -192,7 +192,7 @@
|
||||
<!ENTITY pref_cookies_disabled "Disabled">
|
||||
|
||||
<!ENTITY pref_tracking_protection_title "Tracking protection">
|
||||
<!ENTITY pref_tracking_protection_summary "&brandShortName; will prevent sites from tracking you">
|
||||
<!ENTITY pref_tracking_protection_summary2 "Actively block tracking elements in Private Browsing">
|
||||
<!ENTITY pref_donottrack_title "Do not track">
|
||||
<!ENTITY pref_donottrack_summary "&brandShortName; will tell sites that you do not want to be tracked">
|
||||
|
||||
|
@ -230,6 +230,7 @@
|
||||
<!ENTITY fxaccount_remove_account_toast 'Firefox Account &formatS; removed.'>
|
||||
|
||||
<!ENTITY fxaccount_remove_account_menu_item 'Remove Account'>
|
||||
<!ENTITY fxaccount_enable_debug_mode 'Enable Debug Mode'>
|
||||
|
||||
<!-- Localization note: this is the name shown by the Android system
|
||||
itself for a Firefox Account. Don't localize this. -->
|
||||
|
@ -124,8 +124,6 @@ OnSharedPreferenceChangeListener
|
||||
private static final String PREFS_DISPLAY_REFLOW_ON_ZOOM = "browser.zoom.reflowOnZoom";
|
||||
private static final String PREFS_DISPLAY_TITLEBAR_MODE = "browser.chrome.titlebarMode";
|
||||
private static final String PREFS_SYNC = NON_PREF_PREFIX + "sync";
|
||||
private static final String PREFS_TRACKING_PROTECTION = "privacy.trackingprotection.enabled";
|
||||
private static final String PREFS_TRACKING_PROTECTION_LEARN_MORE = NON_PREF_PREFIX + "trackingprotection.learn_more";
|
||||
public static final String PREFS_OPEN_URLS_IN_PRIVATE = NON_PREF_PREFIX + "openExternalURLsPrivately";
|
||||
public static final String PREFS_VOICE_INPUT_ENABLED = NON_PREF_PREFIX + "voice_input_enabled";
|
||||
public static final String PREFS_QRCODE_ENABLED = NON_PREF_PREFIX + "qrcode_enabled";
|
||||
@ -723,14 +721,6 @@ OnSharedPreferenceChangeListener
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
} else if (PREFS_TRACKING_PROTECTION.equals(key) ||
|
||||
PREFS_TRACKING_PROTECTION_LEARN_MORE.equals(key)) {
|
||||
// Remove UI for tracking protection preference on non-Nightly builds.
|
||||
if (!AppConstants.NIGHTLY_BUILD) {
|
||||
preferences.removePreference(pref);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
} else if (PREFS_TELEMETRY_ENABLED.equals(key)) {
|
||||
if (!AppConstants.MOZ_TELEMETRY_REPORTING) {
|
||||
preferences.removePreference(pref);
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 543 B |
Binary file not shown.
After Width: | Height: | Size: 716 B |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -3,4 +3,9 @@
|
||||
<item
|
||||
android:id="@+id/remove_account"
|
||||
android:title="@string/fxaccount_remove_account_menu_item" />
|
||||
<item
|
||||
android:id="@+id/enable_debug_mode"
|
||||
android:checkable="true"
|
||||
android:checked="false"
|
||||
android:title="@string/fxaccount_enable_debug_mode" />
|
||||
</menu>
|
||||
|
@ -8,7 +8,7 @@
|
||||
android:title="@string/pref_category_privacy_short"
|
||||
android:enabled="false">
|
||||
|
||||
<CheckBoxPreference android:key="privacy.trackingprotection.enabled"
|
||||
<CheckBoxPreference android:key="privacy.trackingprotection.pbmode.enabled"
|
||||
android:title="@string/pref_tracking_protection_title"
|
||||
android:summary="@string/pref_tracking_protection_summary"
|
||||
android:persistent="false" />
|
||||
|
@ -190,7 +190,7 @@
|
||||
<string name="pref_cookies_disabled">&pref_cookies_disabled;</string>
|
||||
|
||||
<string name="pref_tracking_protection_title">&pref_tracking_protection_title;</string>
|
||||
<string name="pref_tracking_protection_summary">&pref_tracking_protection_summary;</string>
|
||||
<string name="pref_tracking_protection_summary">&pref_tracking_protection_summary2;</string>
|
||||
<string name="pref_donottrack_title">&pref_donottrack_title;</string>
|
||||
<string name="pref_donottrack_summary">&pref_donottrack_summary;</string>
|
||||
|
||||
|
@ -54,7 +54,7 @@ public class ExtendedJSONObject {
|
||||
return getJSONParser().parse(in);
|
||||
} catch (Error e) {
|
||||
// Don't be stupid, org.json.simple. Bug 1042929.
|
||||
throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION);
|
||||
throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ public class ExtendedJSONObject {
|
||||
return getJSONParser().parse(input);
|
||||
} catch (Error e) {
|
||||
// Don't be stupid, org.json.simple. Bug 1042929.
|
||||
throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION);
|
||||
throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,6 +314,7 @@ public class ExtendedJSONObject {
|
||||
return this.object.toJSONString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.object.toString();
|
||||
}
|
||||
|
@ -124,6 +124,9 @@ class TabsGridLayout extends GridView
|
||||
for (int x = 1, i = (removedPosition - firstPosition) + 1; i < childCount; i++, x++) {
|
||||
final View child = getChildAt(i);
|
||||
if (child != null) {
|
||||
// Reset the transformations here in case the user is swiping tabs away fast and they swipe a tab
|
||||
// before the last animation has finished (bug 1179195).
|
||||
resetTransforms(child);
|
||||
mTabLocations.append(x, new PointF(child.getX(), child.getY()));
|
||||
}
|
||||
}
|
||||
@ -316,7 +319,7 @@ class TabsGridLayout extends GridView
|
||||
TabsLayoutItemView itemView = (TabsLayoutItemView) v.getTag();
|
||||
Tab tab = Tabs.getInstance().getTab(itemView.getTabId());
|
||||
|
||||
Tabs.getInstance().closeTab(tab);
|
||||
Tabs.getInstance().closeTab(tab, true);
|
||||
}
|
||||
|
||||
private void animateRemoveTab(final Tab removedTab) {
|
||||
|
@ -251,7 +251,7 @@ function removeSnippet(snippetId) {
|
||||
function writeStat(snippetId, timestamp) {
|
||||
let data = gEncoder.encode(snippetId + "," + timestamp + ";");
|
||||
|
||||
Task.spawn(function* () {
|
||||
Task.spawn(function() {
|
||||
try {
|
||||
let file = yield OS.File.open(gStatsPath, { append: true, write: true });
|
||||
try {
|
||||
|
@ -213,7 +213,7 @@ var gDatabaseEnsured = false;
|
||||
* Creates the database schema.
|
||||
*/
|
||||
function createDatabase(db) {
|
||||
return Task.spawn(function* create_database_task() {
|
||||
return Task.spawn(function create_database_task() {
|
||||
yield db.execute(SQL.createItemsTable);
|
||||
});
|
||||
}
|
||||
@ -222,7 +222,7 @@ function createDatabase(db) {
|
||||
* Migrates the database schema to a new version.
|
||||
*/
|
||||
function upgradeDatabase(db, oldVersion, newVersion) {
|
||||
return Task.spawn(function* upgrade_database_task() {
|
||||
return Task.spawn(function upgrade_database_task() {
|
||||
switch (oldVersion) {
|
||||
case 1:
|
||||
// Migration from v1 to latest:
|
||||
@ -251,7 +251,7 @@ function upgradeDatabase(db, oldVersion, newVersion) {
|
||||
* @resolves Handle on an opened SQLite database.
|
||||
*/
|
||||
function getDatabaseConnection() {
|
||||
return Task.spawn(function* get_database_connection_task() {
|
||||
return Task.spawn(function get_database_connection_task() {
|
||||
let db = yield Sqlite.openConnection({ path: DB_PATH });
|
||||
if (gDatabaseEnsured) {
|
||||
throw new Task.Result(db);
|
||||
@ -350,10 +350,10 @@ HomeStorage.prototype = {
|
||||
": you cannot save more than " + MAX_SAVE_COUNT + " items at once";
|
||||
}
|
||||
|
||||
return Task.spawn(function* save_task() {
|
||||
return Task.spawn(function save_task() {
|
||||
let db = yield getDatabaseConnection();
|
||||
try {
|
||||
yield db.executeTransaction(function* save_transaction() {
|
||||
yield db.executeTransaction(function save_transaction() {
|
||||
if (options && options.replace) {
|
||||
yield db.executeCached(SQL.deleteFromDataset, { dataset_id: this.datasetId });
|
||||
}
|
||||
@ -392,7 +392,7 @@ HomeStorage.prototype = {
|
||||
* @resolves When the operation has completed.
|
||||
*/
|
||||
deleteAll: function() {
|
||||
return Task.spawn(function* delete_all_task() {
|
||||
return Task.spawn(function delete_all_task() {
|
||||
let db = yield getDatabaseConnection();
|
||||
try {
|
||||
let params = { dataset_id: this.datasetId };
|
||||
|
@ -238,3 +238,6 @@
|
||||
|
||||
<string name="fxaccount_sync_finish_migrating_notification_title">&fxaccount_sync_finish_migrating_notification_title;</string>
|
||||
<string name="fxaccount_sync_finish_migrating_notification_text">&fxaccount_sync_finish_migrating_notification_text;</string>
|
||||
|
||||
<!-- Log Personal information -->
|
||||
<string name="fxaccount_enable_debug_mode">&fxaccount_enable_debug_mode;</string>
|
@ -149,6 +149,7 @@ skip-if = android_version == "18"
|
||||
# disabled on Android 2.3 due to video playback issues, bug 1088038; on 4.3, bug 1098532
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testVideoDiscovery.java]
|
||||
[testWebChannel.java]
|
||||
|
||||
# Used for Talos, please don't use in mochitest
|
||||
#[testCheck2.java]
|
||||
|
@ -32,7 +32,6 @@ public class testSettingsMenuItems extends PixelTest {
|
||||
|
||||
// Privacy menu items.
|
||||
String[] PATH_PRIVACY;
|
||||
String[] TRACKING_PROTECTION_LABEL_ARR;
|
||||
String[] MANAGE_LOGINS_ARR;
|
||||
String[][] OPTIONS_PRIVACY;
|
||||
|
||||
@ -96,10 +95,9 @@ public class testSettingsMenuItems extends PixelTest {
|
||||
};
|
||||
|
||||
PATH_PRIVACY = new String[] { mStringHelper.PRIVACY_SECTION_LABEL };
|
||||
TRACKING_PROTECTION_LABEL_ARR = new String[] { mStringHelper.TRACKING_PROTECTION_LABEL };
|
||||
MANAGE_LOGINS_ARR = new String[] { mStringHelper.MANAGE_LOGINS_LABEL };
|
||||
OPTIONS_PRIVACY = new String[][] {
|
||||
TRACKING_PROTECTION_LABEL_ARR,
|
||||
{ mStringHelper.TRACKING_PROTECTION_LABEL },
|
||||
{ mStringHelper.DNT_LABEL },
|
||||
{ mStringHelper.COOKIES_LABEL, "Enabled", "Enabled, excluding 3rd party", "Disabled" },
|
||||
{ mStringHelper.REMEMBER_LOGINS_LABEL },
|
||||
@ -196,7 +194,6 @@ public class testSettingsMenuItems extends PixelTest {
|
||||
|
||||
if (!AppConstants.NIGHTLY_BUILD) {
|
||||
final List<String[]> privacy = settingsMap.get(PATH_PRIVACY);
|
||||
privacy.remove(TRACKING_PROTECTION_LABEL_ARR);
|
||||
privacy.remove(MANAGE_LOGINS_ARR);
|
||||
}
|
||||
|
||||
|
89
mobile/android/tests/browser/robocop/testWebChannel.html
Normal file
89
mobile/android/tests/browser/robocop/testWebChannel.html
Normal file
@ -0,0 +1,89 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>web_channel_test</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
var testName = window.location.search.replace(/^\?/, "");
|
||||
|
||||
switch(testName) {
|
||||
case "generic":
|
||||
test_generic();
|
||||
break;
|
||||
case "twoway":
|
||||
test_twoWay();
|
||||
break;
|
||||
case "multichannel":
|
||||
test_multichannel();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
function test_generic() {
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
id: "generic",
|
||||
message: {
|
||||
something: {
|
||||
nested: "hello",
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function test_twoWay() {
|
||||
var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
id: "twoway",
|
||||
message: {
|
||||
command: "one",
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("WebChannelMessageToContent", function(e) {
|
||||
var secondMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
id: "twoway",
|
||||
message: {
|
||||
command: "two",
|
||||
detail: e.detail.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!e.detail.message.error) {
|
||||
window.dispatchEvent(secondMessage);
|
||||
}
|
||||
}, true);
|
||||
|
||||
window.dispatchEvent(firstMessage);
|
||||
}
|
||||
|
||||
function test_multichannel() {
|
||||
var event1 = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
id: "wrongchannel",
|
||||
message: {},
|
||||
}
|
||||
});
|
||||
|
||||
var event2 = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
id: "multichannel",
|
||||
message: {},
|
||||
}
|
||||
});
|
||||
|
||||
window.dispatchEvent(event1);
|
||||
window.dispatchEvent(event2);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
13
mobile/android/tests/browser/robocop/testWebChannel.java
Normal file
13
mobile/android/tests/browser/robocop/testWebChannel.java
Normal file
@ -0,0 +1,13 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
|
||||
|
||||
public class testWebChannel extends JavascriptTest {
|
||||
public testWebChannel() {
|
||||
super("testWebChannel.js");
|
||||
}
|
||||
}
|
107
mobile/android/tests/browser/robocop/testWebChannel.js
Normal file
107
mobile/android/tests/browser/robocop/testWebChannel.js
Normal file
@ -0,0 +1,107 @@
|
||||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
/* 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/. */
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components; /*global Components */
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm"); /*global Promise */
|
||||
Cu.import("resource://gre/modules/Services.jsm"); /*global Services */
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /*global XPCOMUtils */
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebChannel",
|
||||
"resource://gre/modules/WebChannel.jsm"); /*global WebChannel */
|
||||
|
||||
const HTTP_PATH = "http://mochi.test:8888";
|
||||
const HTTP_ENDPOINT = "/tests/robocop/testWebChannel.html";
|
||||
|
||||
const gChromeWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let BrowserApp = gChromeWin.BrowserApp;
|
||||
|
||||
/**
|
||||
* Robocop test helpers.
|
||||
*/
|
||||
function ok(passed, text) {
|
||||
do_report_result(passed, text, Components.stack.caller, false);
|
||||
}
|
||||
|
||||
function is(lhs, rhs, text) {
|
||||
do_report_result(lhs === rhs, "[ " + lhs + " === " + rhs + " ] " + text,
|
||||
Components.stack.caller, false);
|
||||
}
|
||||
|
||||
// Keep this synced with /browser/base/content/test/general/browser_web_channel.js
|
||||
// as much as possible. (We only have this since we can't run browser chrome
|
||||
// tests on Android. Yet?)
|
||||
let gTests = [
|
||||
{
|
||||
desc: "WebChannel generic message",
|
||||
run: function* () {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let tab;
|
||||
let channel = new WebChannel("generic", Services.io.newURI(HTTP_PATH, null, null));
|
||||
channel.listen(function (id, message, target) {
|
||||
is(id, "generic");
|
||||
is(message.something.nested, "hello");
|
||||
channel.stopListening();
|
||||
BrowserApp.closeTab(tab);
|
||||
resolve();
|
||||
});
|
||||
|
||||
tab = BrowserApp.addTab(HTTP_PATH + HTTP_ENDPOINT + "?generic");
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "WebChannel two way communication",
|
||||
run: function* () {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let tab;
|
||||
let channel = new WebChannel("twoway", Services.io.newURI(HTTP_PATH, null, null));
|
||||
|
||||
channel.listen(function (id, message, sender) {
|
||||
is(id, "twoway");
|
||||
ok(message.command);
|
||||
|
||||
if (message.command === "one") {
|
||||
channel.send({ data: { nested: true } }, sender);
|
||||
}
|
||||
|
||||
if (message.command === "two") {
|
||||
is(message.detail.data.nested, true);
|
||||
channel.stopListening();
|
||||
BrowserApp.closeTab(tab);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
tab = BrowserApp.addTab(HTTP_PATH + HTTP_ENDPOINT + "?twoway");
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "WebChannel multichannel",
|
||||
run: function* () {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let tab;
|
||||
let channel = new WebChannel("multichannel", Services.io.newURI(HTTP_PATH, null, null));
|
||||
|
||||
channel.listen(function (id, message, sender) {
|
||||
is(id, "multichannel");
|
||||
BrowserApp.closeTab(tab);
|
||||
resolve();
|
||||
});
|
||||
|
||||
tab = BrowserApp.addTab(HTTP_PATH + HTTP_ENDPOINT + "?multichannel");
|
||||
});
|
||||
}
|
||||
}
|
||||
]; // gTests
|
||||
|
||||
add_task(function test() {
|
||||
for (let test of gTests) {
|
||||
do_print("Running: " + test.desc);
|
||||
yield test.run();
|
||||
}
|
||||
});
|
||||
|
||||
run_next_test();
|
@ -241,17 +241,17 @@ input[type="radio"]:focus {
|
||||
}
|
||||
|
||||
/* we need to be specific for selects because the above rules are specific too */
|
||||
textarea[disabled],
|
||||
select[size][disabled],
|
||||
select[multiple][disabled],
|
||||
select[size][multiple][disabled],
|
||||
select:not([size]):not([multiple])[disabled],
|
||||
select[size="0"][disabled],
|
||||
select[size="1"][disabled],
|
||||
button[disabled],
|
||||
button[disabled]:active,
|
||||
* > input:not([type="image"])[disabled],
|
||||
* > input:not([type="image"])[disabled]:active {
|
||||
textarea:disabled,
|
||||
select[size]:disabled,
|
||||
select[multiple]:disabled,
|
||||
select[size][multiple]:disabled,
|
||||
select:not([size]):not([multiple]):disabled,
|
||||
select[size="0"]:disabled,
|
||||
select[size="1"]:disabled,
|
||||
button:disabled,
|
||||
button:disabled:active,
|
||||
* > input:not([type="image"]):disabled,
|
||||
* > input:not([type="image"]):disabled:active {
|
||||
color: @form_text_disabled@;
|
||||
border-color: @form_border@;
|
||||
border-style: solid;
|
||||
@ -259,20 +259,20 @@ button[disabled]:active,
|
||||
background: @form_background_disabled@;
|
||||
}
|
||||
|
||||
select:not([size]):not([multiple])[disabled],
|
||||
select[size="0"][disabled],
|
||||
select[size="1"][disabled] {
|
||||
select:not([size]):not([multiple]):disabled,
|
||||
select[size="0"]:disabled,
|
||||
select[size="1"]:disabled {
|
||||
background: @form_background_disabled@;
|
||||
}
|
||||
|
||||
input[type="button"][disabled],
|
||||
input[type="button"][disabled]:active,
|
||||
input[type="submit"][disabled],
|
||||
input[type="submit"][disabled]:active,
|
||||
input[type="reset"][disabled],
|
||||
input[type="reset"][disabled]:active,
|
||||
button[disabled],
|
||||
button[disabled]:active {
|
||||
input[type="button"]:disabled,
|
||||
input[type="button"]:disabled:active,
|
||||
input[type="submit"]:disabled,
|
||||
input[type="submit"]:disabled:active,
|
||||
input[type="reset"]:disabled,
|
||||
input[type="reset"]:disabled:active,
|
||||
button:disabled,
|
||||
button:disabled:active {
|
||||
-moz-padding-start: 7px;
|
||||
-moz-padding-end: 7px;
|
||||
padding-block-start: 0;
|
||||
@ -280,18 +280,18 @@ button[disabled]:active {
|
||||
background: @form_background_disabled@;
|
||||
}
|
||||
|
||||
input[type="radio"][disabled],
|
||||
input[type="radio"][disabled]:active,
|
||||
input[type="radio"][disabled]:hover,
|
||||
input[type="radio"][disabled]:hover:active,
|
||||
input[type="checkbox"][disabled],
|
||||
input[type="checkbox"][disabled]:active,
|
||||
input[type="checkbox"][disabled]:hover,
|
||||
input[type="checkbox"][disabled]:hover:active {
|
||||
input[type="radio"]:disabled,
|
||||
input[type="radio"]:disabled:active,
|
||||
input[type="radio"]:disabled:hover,
|
||||
input[type="radio"]:disabled:hover:active,
|
||||
input[type="checkbox"]:disabled,
|
||||
input[type="checkbox"]:disabled:active,
|
||||
input[type="checkbox"]:disabled:hover,
|
||||
input[type="checkbox"]:disabled:hover:active {
|
||||
border:1px solid @form_border@ !important;
|
||||
}
|
||||
|
||||
select[disabled] > button {
|
||||
select:disabled > button {
|
||||
opacity: 0.6;
|
||||
-moz-padding-start: 7px;
|
||||
-moz-padding-end: 7px;
|
||||
@ -306,10 +306,10 @@ select[disabled] > button {
|
||||
|
||||
*:-moz-any-link:active,
|
||||
*[role=button]:active,
|
||||
button:not([disabled]):active,
|
||||
input:not(:focus):not([disabled]):active,
|
||||
select:not([disabled]):active,
|
||||
textarea:not(:focus):not([disabled]):active,
|
||||
button:not(:disabled):active,
|
||||
input:not(:focus):not(:disabled):active,
|
||||
select:not(:disabled):active,
|
||||
textarea:not(:focus):not(:disabled):active,
|
||||
option:active,
|
||||
label:active,
|
||||
xul|menulist:active {
|
||||
|
@ -2,13 +2,13 @@ package org.json.simple.parser;
|
||||
|
||||
/**
|
||||
* ParseException explains why and where the error occurs in source JSON text.
|
||||
*
|
||||
*
|
||||
* @author FangYidong<fangyidong@yahoo.com.cn>
|
||||
*
|
||||
*/
|
||||
public class ParseException extends Exception {
|
||||
private static final long serialVersionUID = -7880698968187728548L;
|
||||
|
||||
|
||||
public static final int ERROR_UNEXPECTED_CHAR = 0;
|
||||
public static final int ERROR_UNEXPECTED_TOKEN = 1;
|
||||
public static final int ERROR_UNEXPECTED_EXCEPTION = 2;
|
||||
@ -16,45 +16,50 @@ public class ParseException extends Exception {
|
||||
private int errorType;
|
||||
private Object unexpectedObject;
|
||||
private int position;
|
||||
|
||||
public ParseException(int errorType){
|
||||
this(-1, errorType, null);
|
||||
|
||||
public ParseException(int errorType, Throwable throwable) {
|
||||
this(-1, errorType, null, throwable);
|
||||
}
|
||||
|
||||
public ParseException(int errorType, Object unexpectedObject){
|
||||
|
||||
public ParseException(int errorType, Object unexpectedObject) {
|
||||
this(-1, errorType, unexpectedObject);
|
||||
}
|
||||
|
||||
public ParseException(int position, int errorType, Object unexpectedObject){
|
||||
this.position = position;
|
||||
this.errorType = errorType;
|
||||
this.unexpectedObject = unexpectedObject;
|
||||
|
||||
public ParseException(int position, int errorType, Object unexpectedObject) {
|
||||
this(-1, errorType, unexpectedObject, null);
|
||||
}
|
||||
|
||||
|
||||
public ParseException(int position, int errorType, Object unexpectedObject, Throwable throwable) {
|
||||
super(throwable);
|
||||
this.position = position;
|
||||
this.errorType = errorType;
|
||||
this.unexpectedObject = unexpectedObject;
|
||||
}
|
||||
|
||||
public int getErrorType() {
|
||||
return errorType;
|
||||
}
|
||||
|
||||
|
||||
public void setErrorType(int errorType) {
|
||||
this.errorType = errorType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see org.json.simple.parser.JSONParser#getPosition()
|
||||
*
|
||||
*
|
||||
* @return The character position (starting with 0) of the input where the error occurs.
|
||||
*/
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
public void setPosition(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see org.json.simple.parser.Yytoken
|
||||
*
|
||||
*
|
||||
* @return One of the following base on the value of errorType:
|
||||
* ERROR_UNEXPECTED_CHAR java.lang.Character
|
||||
* ERROR_UNEXPECTED_TOKEN org.json.simple.parser.Yytoken
|
||||
@ -63,14 +68,15 @@ public class ParseException extends Exception {
|
||||
public Object getUnexpectedObject() {
|
||||
return unexpectedObject;
|
||||
}
|
||||
|
||||
|
||||
public void setUnexpectedObject(Object unexpectedObject) {
|
||||
this.unexpectedObject = unexpectedObject;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
|
||||
switch(errorType){
|
||||
case ERROR_UNEXPECTED_CHAR:
|
||||
sb.append("Unexpected character (").append(unexpectedObject).append(") at position ").append(position).append(".");
|
||||
|
@ -4,10 +4,40 @@
|
||||
|
||||
#include "nsAutoCompleteSimpleResult.h"
|
||||
|
||||
#define CHECK_MATCH_INDEX(_index, _insert) \
|
||||
if (_index < 0 || \
|
||||
static_cast<MatchesArray::size_type>(_index) > mMatches.Length() || \
|
||||
(!_insert && static_cast<MatchesArray::size_type>(_index) == mMatches.Length())) { \
|
||||
MOZ_ASSERT(false, "Trying to use an invalid index on mMatches"); \
|
||||
return NS_ERROR_ILLEGAL_VALUE; \
|
||||
} \
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsAutoCompleteSimpleResult,
|
||||
nsIAutoCompleteResult,
|
||||
nsIAutoCompleteSimpleResult)
|
||||
|
||||
struct AutoCompleteSimpleResultMatch
|
||||
{
|
||||
AutoCompleteSimpleResultMatch(const nsAString& aValue,
|
||||
const nsAString& aComment,
|
||||
const nsAString& aImage,
|
||||
const nsAString& aStyle,
|
||||
const nsAString& aFinalCompleteValue)
|
||||
: mValue(aValue)
|
||||
, mComment(aComment)
|
||||
, mImage(aImage)
|
||||
, mStyle(aStyle)
|
||||
, mFinalCompleteValue(aFinalCompleteValue)
|
||||
{
|
||||
}
|
||||
|
||||
nsString mValue;
|
||||
nsString mComment;
|
||||
nsString mImage;
|
||||
nsString mStyle;
|
||||
nsString mFinalCompleteValue;
|
||||
};
|
||||
|
||||
nsAutoCompleteSimpleResult::nsAutoCompleteSimpleResult() :
|
||||
mDefaultIndex(-1),
|
||||
mSearchResult(RESULT_NOMATCH),
|
||||
@ -86,6 +116,25 @@ nsAutoCompleteSimpleResult::SetTypeAheadResult(bool aTypeAheadResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAutoCompleteSimpleResult::InsertMatchAt(int32_t aIndex,
|
||||
const nsAString& aValue,
|
||||
const nsAString& aComment,
|
||||
const nsAString& aImage,
|
||||
const nsAString& aStyle,
|
||||
const nsAString& aFinalCompleteValue)
|
||||
{
|
||||
CHECK_MATCH_INDEX(aIndex, true);
|
||||
|
||||
AutoCompleteSimpleResultMatch match(aValue, aComment, aImage, aStyle, aFinalCompleteValue);
|
||||
|
||||
if (!mMatches.InsertElementAt(aIndex, match)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAutoCompleteSimpleResult::AppendMatch(const nsAString& aValue,
|
||||
const nsAString& aComment,
|
||||
@ -93,52 +142,22 @@ nsAutoCompleteSimpleResult::AppendMatch(const nsAString& aValue,
|
||||
const nsAString& aStyle,
|
||||
const nsAString& aFinalCompleteValue)
|
||||
{
|
||||
CheckInvariants();
|
||||
|
||||
if (! mValues.AppendElement(aValue))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
if (! mComments.AppendElement(aComment)) {
|
||||
mValues.RemoveElementAt(mValues.Length() - 1);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (! mImages.AppendElement(aImage)) {
|
||||
mValues.RemoveElementAt(mValues.Length() - 1);
|
||||
mComments.RemoveElementAt(mComments.Length() - 1);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (! mStyles.AppendElement(aStyle)) {
|
||||
mValues.RemoveElementAt(mValues.Length() - 1);
|
||||
mComments.RemoveElementAt(mComments.Length() - 1);
|
||||
mImages.RemoveElementAt(mImages.Length() - 1);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (!mFinalCompleteValues.AppendElement(aFinalCompleteValue)) {
|
||||
mValues.RemoveElementAt(mValues.Length() - 1);
|
||||
mComments.RemoveElementAt(mComments.Length() - 1);
|
||||
mImages.RemoveElementAt(mImages.Length() - 1);
|
||||
mStyles.RemoveElementAt(mStyles.Length() - 1);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
return InsertMatchAt(mMatches.Length(), aValue, aComment, aImage, aStyle,
|
||||
aFinalCompleteValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAutoCompleteSimpleResult::GetMatchCount(uint32_t *aMatchCount)
|
||||
{
|
||||
CheckInvariants();
|
||||
|
||||
*aMatchCount = mValues.Length();
|
||||
*aMatchCount = mMatches.Length();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAutoCompleteSimpleResult::GetValueAt(int32_t aIndex, nsAString& _retval)
|
||||
{
|
||||
NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mValues.Length()),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
CheckInvariants();
|
||||
|
||||
_retval = mValues[aIndex];
|
||||
CHECK_MATCH_INDEX(aIndex, false);
|
||||
_retval = mMatches[aIndex].mValue;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -151,30 +170,24 @@ nsAutoCompleteSimpleResult::GetLabelAt(int32_t aIndex, nsAString& _retval)
|
||||
NS_IMETHODIMP
|
||||
nsAutoCompleteSimpleResult::GetCommentAt(int32_t aIndex, nsAString& _retval)
|
||||
{
|
||||
NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mComments.Length()),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
CheckInvariants();
|
||||
_retval = mComments[aIndex];
|
||||
CHECK_MATCH_INDEX(aIndex, false);
|
||||
_retval = mMatches[aIndex].mComment;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAutoCompleteSimpleResult::GetImageAt(int32_t aIndex, nsAString& _retval)
|
||||
{
|
||||
NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mImages.Length()),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
CheckInvariants();
|
||||
_retval = mImages[aIndex];
|
||||
CHECK_MATCH_INDEX(aIndex, false);
|
||||
_retval = mMatches[aIndex].mImage;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAutoCompleteSimpleResult::GetStyleAt(int32_t aIndex, nsAString& _retval)
|
||||
{
|
||||
NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mStyles.Length()),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
CheckInvariants();
|
||||
_retval = mStyles[aIndex];
|
||||
CHECK_MATCH_INDEX(aIndex, false);
|
||||
_retval = mMatches[aIndex].mStyle;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -182,12 +195,10 @@ NS_IMETHODIMP
|
||||
nsAutoCompleteSimpleResult::GetFinalCompleteValueAt(int32_t aIndex,
|
||||
nsAString& _retval)
|
||||
{
|
||||
NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mFinalCompleteValues.Length()),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
CheckInvariants();
|
||||
_retval = mFinalCompleteValues[aIndex];
|
||||
CHECK_MATCH_INDEX(aIndex, false);
|
||||
_retval = mMatches[aIndex].mFinalCompleteValue;
|
||||
if (_retval.Length() == 0)
|
||||
_retval = mValues[aIndex];
|
||||
_retval = mMatches[aIndex].mValue;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -202,18 +213,13 @@ NS_IMETHODIMP
|
||||
nsAutoCompleteSimpleResult::RemoveValueAt(int32_t aRowIndex,
|
||||
bool aRemoveFromDb)
|
||||
{
|
||||
NS_ENSURE_TRUE(aRowIndex >= 0 && aRowIndex < int32_t(mValues.Length()),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
CHECK_MATCH_INDEX(aRowIndex, false);
|
||||
|
||||
nsAutoString removedValue(mValues[aRowIndex]);
|
||||
mValues.RemoveElementAt(aRowIndex);
|
||||
mComments.RemoveElementAt(aRowIndex);
|
||||
mImages.RemoveElementAt(aRowIndex);
|
||||
mStyles.RemoveElementAt(aRowIndex);
|
||||
mFinalCompleteValues.RemoveElementAt(aRowIndex);
|
||||
nsString value = mMatches[aRowIndex].mValue;
|
||||
mMatches.RemoveElementAt(aRowIndex);
|
||||
|
||||
if (mListener)
|
||||
mListener->OnValueRemoved(this, removedValue, aRemoveFromDb);
|
||||
mListener->OnValueRemoved(this, value, aRemoveFromDb);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -13,16 +13,12 @@
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
struct AutoCompleteSimpleResultMatch;
|
||||
|
||||
class nsAutoCompleteSimpleResult final : public nsIAutoCompleteSimpleResult
|
||||
{
|
||||
public:
|
||||
nsAutoCompleteSimpleResult();
|
||||
inline void CheckInvariants() {
|
||||
NS_ASSERTION(mValues.Length() == mComments.Length(), "Arrays out of sync");
|
||||
NS_ASSERTION(mValues.Length() == mImages.Length(), "Arrays out of sync");
|
||||
NS_ASSERTION(mValues.Length() == mStyles.Length(), "Arrays out of sync");
|
||||
NS_ASSERTION(mValues.Length() == mFinalCompleteValues.Length(), "Arrays out of sync");
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIAUTOCOMPLETERESULT
|
||||
@ -32,15 +28,8 @@ private:
|
||||
~nsAutoCompleteSimpleResult() {}
|
||||
|
||||
protected:
|
||||
|
||||
// What we really want is an array of structs with value/comment/image/style contents.
|
||||
// But then we'd either have to use COM or manage object lifetimes ourselves.
|
||||
// Having four arrays of string simplifies this, but is stupid.
|
||||
nsTArray<nsString> mValues;
|
||||
nsTArray<nsString> mComments;
|
||||
nsTArray<nsString> mImages;
|
||||
nsTArray<nsString> mStyles;
|
||||
nsTArray<nsString> mFinalCompleteValues;
|
||||
typedef nsTArray<AutoCompleteSimpleResultMatch> MatchesArray;
|
||||
MatchesArray mMatches;
|
||||
|
||||
nsString mSearchString;
|
||||
nsString mErrorDescription;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user