Merge m-c to inbound. a=merge

CLOSED TREE

--HG--
extra : amend_source : e59af3fa8b325da192204b3a227c24de690055c7
This commit is contained in:
Ryan VanderMeulen 2014-08-27 15:54:44 -04:00
commit d51fed3906
164 changed files with 2809 additions and 2061 deletions

View File

@ -1302,6 +1302,7 @@ pref("devtools.toolbox.splitconsoleHeight", 100);
// Toolbox Button preferences
pref("devtools.command-button-pick.enabled", true);
pref("devtools.command-button-frames.enabled", false);
pref("devtools.command-button-splitconsole.enabled", true);
pref("devtools.command-button-paintflashing.enabled", false);
pref("devtools.command-button-tilt.enabled", false);
@ -1531,10 +1532,10 @@ pref("browser.newtabpage.rows", 3);
pref("browser.newtabpage.columns", 5);
// directory tiles download URL
pref("browser.newtabpage.directory.source", "https://tiles.up.mozillalabs.com/v2/links/fetch");
pref("browser.newtabpage.directory.source", "https://tiles.services.mozilla.com/v2/links/fetch");
// endpoint to send newtab click and view pings
pref("browser.newtabpage.directory.ping", "https://tiles.up.mozillalabs.com/v2/links/");
pref("browser.newtabpage.directory.ping", "https://tiles.services.mozilla.com/v2/links/");
// Enable the DOM fullscreen API.
pref("full-screen-api.enabled", true);

View File

@ -110,62 +110,53 @@ SocialUI = {
},
observe: function SocialUI_observe(subject, topic, data) {
// Exceptions here sometimes don't get reported properly, report them
// manually :(
try {
switch (topic) {
case "social:provider-enabled":
SocialMarks.populateToolbarPalette();
SocialStatus.populateToolbarPalette();
break;
case "social:provider-disabled":
SocialMarks.removeProvider(data);
SocialStatus.removeProvider(data);
SocialSidebar.disableProvider(data);
break;
case "social:provider-reload":
SocialStatus.reloadProvider(data);
// if the reloaded provider is our current provider, fall through
// to social:providers-changed so the ui will be reset
if (!SocialSidebar.provider || SocialSidebar.provider.origin != data)
return;
// currently only the sidebar and flyout have a selected provider.
// sidebar provider has changed (possibly to null), ensure the content
// is unloaded and the frames are reset, they will be loaded in
// providers-changed below if necessary.
SocialSidebar.unloadSidebar();
SocialFlyout.unload();
// fall through to providers-changed to ensure the reloaded provider
// is correctly reflected in any UI and the multi-provider menu
case "social:providers-changed":
this._providersChanged();
break;
// Provider-specific notifications
case "social:ambient-notification-changed":
SocialStatus.updateButton(data);
break;
case "social:profile-changed":
// make sure anything that happens here only affects the provider for
// which the profile is changing, and that anything we call actually
// needs to change based on profile data.
SocialStatus.updateButton(data);
break;
case "social:frameworker-error":
if (this.enabled && SocialSidebar.provider && SocialSidebar.provider.origin == data) {
SocialSidebar.setSidebarErrorMessage();
}
break;
case "nsPref:changed":
if (data == "social.toast-notifications.enabled") {
SocialSidebar.updateToggleNotifications();
}
break;
}
} catch (e) {
Components.utils.reportError(e + "\n" + e.stack);
throw e;
switch (topic) {
case "social:provider-enabled":
SocialMarks.populateToolbarPalette();
SocialStatus.populateToolbarPalette();
break;
case "social:provider-disabled":
SocialMarks.removeProvider(data);
SocialStatus.removeProvider(data);
SocialSidebar.disableProvider(data);
break;
case "social:provider-reload":
SocialStatus.reloadProvider(data);
// if the reloaded provider is our current provider, fall through
// to social:providers-changed so the ui will be reset
if (!SocialSidebar.provider || SocialSidebar.provider.origin != data)
return;
// currently only the sidebar and flyout have a selected provider.
// sidebar provider has changed (possibly to null), ensure the content
// is unloaded and the frames are reset, they will be loaded in
// providers-changed below if necessary.
SocialSidebar.unloadSidebar();
SocialFlyout.unload();
// fall through to providers-changed to ensure the reloaded provider
// is correctly reflected in any UI and the multi-provider menu
case "social:providers-changed":
this._providersChanged();
break;
// Provider-specific notifications
case "social:ambient-notification-changed":
SocialStatus.updateButton(data);
break;
case "social:profile-changed":
// make sure anything that happens here only affects the provider for
// which the profile is changing, and that anything we call actually
// needs to change based on profile data.
SocialStatus.updateButton(data);
break;
case "social:frameworker-error":
if (this.enabled && SocialSidebar.provider && SocialSidebar.provider.origin == data) {
SocialSidebar.setSidebarErrorMessage();
}
break;
case "nsPref:changed":
if (data == "social.toast-notifications.enabled") {
SocialSidebar.updateToggleNotifications();
}
break;
}
},

View File

@ -7,10 +7,11 @@
<meta charset="utf-8">
<!-- Title is set in conversation.js -->
<title></title>
<link rel="stylesheet" type="text/css" href="loop/shared/css/reset.css">
<link rel="stylesheet" type="text/css" href="loop/shared/css/common.css">
<link rel="stylesheet" type="text/css" href="loop/shared/css/conversation.css">
</head>
<body onload="loop.conversation.init();">
<body class="conversation-window" onload="loop.conversation.init();">
<div id="messages"></div>

View File

@ -77,13 +77,10 @@ loop.conversation = (function(OT, mozL10n) {
render: function() {
/* jshint ignore:start */
var btnClassAccept = "btn btn-success btn-accept call-audio-video";
var btnClassBlock = "btn btn-error btn-block";
var btnClassAccept = "btn btn-accept";
var btnClassDecline = "btn btn-error btn-decline";
var conversationPanelClass = "incoming-call " +
loop.shared.utils.getTargetPlatform();
var cx = React.addons.classSet;
var dropdownMenuClassesDecline = cx({
var conversationPanelClass = "incoming-call";
var dropdownMenuClassesDecline = React.addons.classSet({
"native-dropdown-menu": true,
"conversation-window-dropdown": true,
"visually-hidden": !this.state.showDeclineMenu
@ -91,10 +88,13 @@ loop.conversation = (function(OT, mozL10n) {
return (
React.DOM.div({className: conversationPanelClass},
React.DOM.h2(null, __("incoming_call")),
React.DOM.div({className: "button-group incoming-call-action-group"},
React.DOM.div({className: "button-chevron-menu-group"},
React.DOM.div({className: "button-group-chevron"},
React.DOM.div({className: "button-group"},
React.DOM.div({className: "btn-group incoming-call-action-group"},
React.DOM.div({className: "fx-embedded-incoming-call-button-spacer"}),
React.DOM.div({className: "btn-chevron-menu-group"},
React.DOM.div({className: "btn-group-chevron"},
React.DOM.div({className: "btn-group"},
React.DOM.button({className: btnClassDecline,
onClick: this._handleDecline},
@ -114,18 +114,27 @@ loop.conversation = (function(OT, mozL10n) {
)
),
React.DOM.div({className: "button-chevron-menu-group"},
React.DOM.div({className: "button-group"},
React.DOM.div({className: "fx-embedded-incoming-call-button-spacer"}),
React.DOM.div({className: "btn-chevron-menu-group"},
React.DOM.div({className: "btn-group"},
React.DOM.button({className: btnClassAccept,
onClick: this._handleAccept("audio-video")},
__("incoming_call_answer_button")
React.DOM.span({className: "fx-embedded-answer-btn-text"},
__("incoming_call_answer_button")
),
React.DOM.span({className: "fx-embedded-btn-icon-video"}
)
),
React.DOM.div({className: "call-audio-only",
onClick: this._handleAccept("audio"),
title: __("incoming_call_answer_audio_only_tooltip")}
)
)
)
),
React.DOM.div({className: "fx-embedded-incoming-call-button-spacer"})
)
)
);
@ -339,6 +348,8 @@ loop.conversation = (function(OT, mozL10n) {
document.title = mozL10n.get("incoming_call_title");
document.body.classList.add(loop.shared.utils.getTargetPlatform());
var client = new loop.Client();
router = new ConversationRouter({
client: client,

View File

@ -77,13 +77,10 @@ loop.conversation = (function(OT, mozL10n) {
render: function() {
/* jshint ignore:start */
var btnClassAccept = "btn btn-success btn-accept call-audio-video";
var btnClassBlock = "btn btn-error btn-block";
var btnClassAccept = "btn btn-accept";
var btnClassDecline = "btn btn-error btn-decline";
var conversationPanelClass = "incoming-call " +
loop.shared.utils.getTargetPlatform();
var cx = React.addons.classSet;
var dropdownMenuClassesDecline = cx({
var conversationPanelClass = "incoming-call";
var dropdownMenuClassesDecline = React.addons.classSet({
"native-dropdown-menu": true,
"conversation-window-dropdown": true,
"visually-hidden": !this.state.showDeclineMenu
@ -91,10 +88,13 @@ loop.conversation = (function(OT, mozL10n) {
return (
<div className={conversationPanelClass}>
<h2>{__("incoming_call")}</h2>
<div className="button-group incoming-call-action-group">
<div className="button-chevron-menu-group">
<div className="button-group-chevron">
<div className="button-group">
<div className="btn-group incoming-call-action-group">
<div className="fx-embedded-incoming-call-button-spacer"></div>
<div className="btn-chevron-menu-group">
<div className="btn-group-chevron">
<div className="btn-group">
<button className={btnClassDecline}
onClick={this._handleDecline}>
@ -114,11 +114,17 @@ loop.conversation = (function(OT, mozL10n) {
</div>
</div>
<div className="button-chevron-menu-group">
<div className="button-group">
<div className="fx-embedded-incoming-call-button-spacer"></div>
<div className="btn-chevron-menu-group">
<div className="btn-group">
<button className={btnClassAccept}
onClick={this._handleAccept("audio-video")}>
{__("incoming_call_answer_button")}
<span className="fx-embedded-answer-btn-text">
{__("incoming_call_answer_button")}
</span>
<span className="fx-embedded-btn-icon-video">
</span>
</button>
<div className="call-audio-only"
onClick={this._handleAccept("audio")}
@ -126,6 +132,9 @@ loop.conversation = (function(OT, mozL10n) {
</div>
</div>
</div>
<div className="fx-embedded-incoming-call-button-spacer"></div>
</div>
</div>
);
@ -339,6 +348,8 @@ loop.conversation = (function(OT, mozL10n) {
document.title = mozL10n.get("incoming_call_title");
document.body.classList.add(loop.shared.utils.getTargetPlatform());
var client = new loop.Client();
router = new ConversationRouter({
client: client,

View File

@ -77,9 +77,9 @@ loop.panel = (function(_, mozL10n) {
__("display_name_available_status");
return (
React.DOM.div({className: "footer component-spacer"},
React.DOM.div({className: "footer"},
React.DOM.div({className: "do-not-disturb"},
React.DOM.p({className: "dnd-status", onClick: this.showDropdownMenu},
React.DOM.div({className: "dnd-status", onClick: this.showDropdownMenu},
React.DOM.span(null, availabilityText),
React.DOM.i({className: availabilityStatus})
),
@ -138,10 +138,8 @@ loop.panel = (function(_, mozL10n) {
render: function() {
return (
React.DOM.div({className: "component-spacer share generate-url"},
React.DOM.div({className: "description"},
React.DOM.p({className: "description-content"}, this.props.summary)
),
React.DOM.div({className: "share generate-url"},
React.DOM.div({className: "description"}, this.props.summary),
React.DOM.div({className: "action"},
this.props.children
)
@ -175,6 +173,12 @@ loop.panel = (function(_, mozL10n) {
},
componentDidMount: function() {
// If we've already got a callURL, don't bother requesting a new one.
// As of this writing, only used for visual testing in the UI showcase.
if (this.state.callUrl.length) {
return;
}
this.setState({pending: true});
this.props.client.requestCallUrl(this.conversationIdentifier(),
this._onCallUrlReceived);
@ -239,7 +243,7 @@ loop.panel = (function(_, mozL10n) {
React.DOM.div({className: "invite"},
React.DOM.input({type: "url", value: this.state.callUrl, readOnly: "true",
className: inputCSSClass}),
React.DOM.p({className: "button-group url-actions"},
React.DOM.p({className: "btn-group url-actions"},
React.DOM.button({className: "btn btn-email", disabled: !this.state.callUrl,
onClick: this.handleEmailButtonClick,
'data-mailto': this._generateMailTo()},
@ -361,6 +365,8 @@ loop.panel = (function(_, mozL10n) {
});
Backbone.history.start();
document.body.classList.add(loop.shared.utils.getTargetPlatform());
// Notify the window that we've finished initalization and initial layout
var evtObject = document.createEvent('Event');
evtObject.initEvent('loopPanelInitialized', true, false);

View File

@ -77,12 +77,12 @@ loop.panel = (function(_, mozL10n) {
__("display_name_available_status");
return (
<div className="footer component-spacer">
<div className="footer">
<div className="do-not-disturb">
<p className="dnd-status" onClick={this.showDropdownMenu}>
<div className="dnd-status" onClick={this.showDropdownMenu}>
<span>{availabilityText}</span>
<i className={availabilityStatus}></i>
</p>
</div>
<ul className={availabilityDropdown}
onMouseLeave={this.hideDropdownMenu}>
<li onClick={this.changeAvailability("available")}
@ -138,10 +138,8 @@ loop.panel = (function(_, mozL10n) {
render: function() {
return (
<div className="component-spacer share generate-url">
<div className="description">
<p className="description-content">{this.props.summary}</p>
</div>
<div className="share generate-url">
<div className="description">{this.props.summary}</div>
<div className="action">
{this.props.children}
</div>
@ -175,6 +173,12 @@ loop.panel = (function(_, mozL10n) {
},
componentDidMount: function() {
// If we've already got a callURL, don't bother requesting a new one.
// As of this writing, only used for visual testing in the UI showcase.
if (this.state.callUrl.length) {
return;
}
this.setState({pending: true});
this.props.client.requestCallUrl(this.conversationIdentifier(),
this._onCallUrlReceived);
@ -239,7 +243,7 @@ loop.panel = (function(_, mozL10n) {
<div className="invite">
<input type="url" value={this.state.callUrl} readOnly="true"
className={inputCSSClass} />
<p className="button-group url-actions">
<p className="btn-group url-actions">
<button className="btn btn-email" disabled={!this.state.callUrl}
onClick={this.handleEmailButtonClick}
data-mailto={this._generateMailTo()}>
@ -361,6 +365,8 @@ loop.panel = (function(_, mozL10n) {
});
Backbone.history.start();
document.body.classList.add(loop.shared.utils.getTargetPlatform());
// Notify the window that we've finished initalization and initial layout
var evtObject = document.createEvent('Event');
evtObject.initEvent('loopPanelInitialized', true, false);

View File

@ -6,6 +6,7 @@
<head>
<meta charset="utf-8">
<title>Loop Panel</title>
<link rel="stylesheet" type="text/css" href="loop/shared/css/reset.css">
<link rel="stylesheet" type="text/css" href="loop/shared/css/common.css">
<link rel="stylesheet" type="text/css" href="loop/shared/css/panel.css">
</head>
@ -21,6 +22,7 @@
<script type="text/javascript" src="loop/shared/libs/lodash-2.4.1.js"></script>
<script type="text/javascript" src="loop/shared/libs/backbone-1.1.2.js"></script>
<script type="text/javascript" src="loop/shared/js/utils.js"></script>
<script type="text/javascript" src="loop/shared/js/models.js"></script>
<script type="text/javascript" src="loop/shared/js/router.js"></script>
<script type="text/javascript" src="loop/shared/js/views.js"></script>

View File

@ -13,8 +13,6 @@
}
body {
margin: 0;
padding: 0;
font-family: "Lucida Grande", sans-serif;
font-size: 12px;
background: #fbfbfb;
@ -36,6 +34,11 @@ h1, h2, h3 {
color: #666;
}
/* choose a sane default for paragraphs, since reset.css' 0px is not what we want */
p {
margin: 1em 0;
}
/* Helpers */
/**
@ -49,8 +52,8 @@ h1, h2, h3 {
*/
.cf:before,
.cf:after {
content: " "; /* 1 */
display: table; /* 2 */
display: table; /* 1 */
content: " "; /* 2 */
}
.cf:after {
@ -79,48 +82,25 @@ h1, h2, h3 {
width: 100%;
}
/* Buttons */
/* A reset for all button-appearing elements, with the lowest-common
* denominator of the needed rules. Intended to be used as a base class
* together with .btn-*
*/
.btn {
display: inline-block;
background: #a5a;
overflow: hidden;
margin: 0;
padding: 0;
border: none;
background: #a5a;
color: #fff;
text-decoration: none;
padding: .3em .6em;
border: 1px solid transparent;
border-radius: 2px;
cursor: pointer;
font-size: .9em;
text-align: center;
}
button.btn {
/* for some reason, buttons respond differently to setting padding than
regular links */
padding: .3em .3em .3em .5em;
}
.btn-large {
/* Dimensions from spec
* https://people.mozilla.org/~dhenein/labs/loop-link-spec/#call-start */
padding: .5em;
font-size: 18px;
height: auto;
}
.btn-large + .btn-chevron {
padding: 1rem;
height: 100%; /* match full height of button */
}
/*
* Left / Right padding elements
* used to center components
* */
.flex-padding-1 {
display: flex;
flex: 1;
text-decoration: none;
text-overflow: ellipsis;
white-space: nowrap;
font-size: .9em;
cursor: pointer;
}
.btn-info {
@ -138,20 +118,23 @@ h1, h2, h3 {
border: 1px solid #006b9d;
}
.btn-accept,
.btn-success,
.btn-success + .btn-chevron {
.btn-accept + .btn-chevron {
background-color: #74bf43;
border: 1px solid #74bf43;
}
.btn-accept:hover,
.btn-success:hover,
.btn-success + .btn-chevron:hover {
.btn-accept + .btn-chevron:hover {
background-color: #6cb23e;
border: 1px solid #6cb23e;
}
.btn-accept:active,
.btn-success:active,
.btn-success + .btn-chevron:active {
.btn-accept + .btn-chevron:active {
background-color: #64a43a;
border: 1px solid #64a43a;
}
@ -161,18 +144,21 @@ h1, h2, h3 {
}
.btn-error,
.btn-hangup,
.btn-error + .btn-chevron {
background-color: #d74345;
border: 1px solid #d74345;
}
.btn-error:hover,
.btn-hangup:hover,
.btn-error + .btn-chevron:hover {
background-color: #c53436;
border: 1px solid #c53436;
}
.btn-error:active,
.btn-hangup:active,
.btn-error + .btn-chevron:active {
background-color: #ae2325;
border: 1px solid #ae2325;
@ -186,7 +172,7 @@ h1, h2, h3 {
}
/* Groups together a button and a chevron */
.button-group-chevron {
.btn-group-chevron {
display: flex;
flex-direction: column;
flex: 1;
@ -194,15 +180,13 @@ h1, h2, h3 {
/* Groups together a button-group-chevron
* and the dropdown menu */
.button-chevron-menu-group {
.btn-chevron-menu-group {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
flex: 1;
flex: 8;
}
.button-group-chevron .btn {
.btn-group-chevron .btn {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
flex: 2;
@ -221,9 +205,6 @@ h1, h2, h3 {
@media (min-resolution: 2dppx) {
.btn-chevron {
background-image: url(../img/dropdown-inverse@2x.png);
background-position: center;
background-size: 10px;
background-repeat: no-repeat;
}
}
@ -233,14 +214,13 @@ h1, h2, h3 {
opacity: 0.65;
}
.button-group {
.btn-group {
display: flex;
width: 100%;
align-content: space-between;
justify-content: center;
}
.button-group .btn {
.btn-group .btn {
flex: 1;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
@ -276,6 +256,17 @@ h1, h2, h3 {
/* Misc */
.call-url,
.overflow-text-ellipsis,
.standalone-call-btn-text,
.fx-embedded-answer-btn-text {
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.close {
float: right;
font-size: 20px;
@ -359,39 +350,15 @@ h1, h2, h3 {
font-family: 'Segoe';
}
.windows p {
font-size: 12px;
}
.windows h1 {
font-family: 'Segoe Bold';
}
.mac {
font-family: 'Lucida Grande';
}
.mac p {
font-size: 11.5px;
}
.mac h1 {
font-family: 'Lucida Grande Bold';
}
.linux {
/* XXX requires fallbacks */
font-family: 'Ubuntu', sans-serif;
}
.linux p {
font-size: 12px;
}
.linux h1 {
font-family: 'Ubuntu Bold';
}
/* Web panel */
.info-panel {
@ -408,7 +375,6 @@ h1, h2, h3 {
font-weight: 700;
padding: 20px 0;
text-align: center;
margin: 0;
}
.info-panel h4 {
@ -421,9 +387,9 @@ h1, h2, h3 {
/* Logos */
.firefox-logo {
background: transparent url(../img/firefox-logo.png) no-repeat center center;
background-size: contain;
margin: 0 auto; /* horizontal block centering */
width: 100px;
height: 100px;
margin: 0px auto; /* horizontal block centering */
background: transparent url(../img/firefox-logo.png) no-repeat center center;
background-size: contain;
}

View File

@ -2,56 +2,120 @@
* 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/. */
/* Conversation window styles */
/* Shared conversation window styles */
.standalone .video-layout-wrapper {
background-color: #444;
}
.conversation {
position: relative;
}
.standalone .conversation {
margin: 0 auto;
max-height: 100vh;
}
.conversation-toolbar {
position: absolute;
z-index: 999; /* required to have it superimposed to the video element */
border: 1px solid #5a5a5a;
border-left: 0;
border-right: 0;
}
.conversation .conversation-toolbar {
position: absolute;
left: 0;
right: 0;
background: rgba(0, 0, 0, .70);
border: 1px solid #5a5a5a;
list-style-type: none;
margin: 0;
padding: 0;
}
/* desktop version */
.conversation-window .conversation-toolbar {
height: 26px;
background: rgba(0,0,0,.70);
}
/* standalone version */
.standalone .conversation-toolbar {
bottom: 0;
}
.standalone .conversation-toolbar {
background: rgba(0,0,0,.50);
padding: 20px;
height: 64px;
}
.conversation-toolbar li {
float: left;
font-size: 0px; /* prevents vertical bottom padding added to buttons in google
chrome */
font-size: 0; /* prevents vertical bottom padding added to buttons in google
chrome */
}
.conversation-toolbar .btn {
width: 40px;
height: 30px;
background: transparent;
background-repeat: no-repeat;
background-position: 12px 8px;
background-size: 14px 14px;
.standalone .conversation-toolbar li {
/* XXX Might make sense to use relative units here.
*/
margin-right: 16px;
}
.conversation-toolbar-btn-box {
border-right: 1px solid #5a5a5a;
border-radius: 0;
}
.standalone .conversation-toolbar-btn-box {
/* overwrite the style for standalone
* because we reuse the same component */
border: none;
}
.conversation-toolbar .btn {
/* dimensions according to spec
* https://people.mozilla.org/~dhenein/labs/loop-link-spec/#video-call */
width: 32px;
height: 24px;
background-position: center;
background-size: 40%;
background-repeat: no-repeat;
}
.standalone .media-control {
width: 36px;
background-position: center;
border-radius: 28px;
}
.standalone-conversation-toolbar-media-btn:hover {
background-color: rgba(255,255,255,.35);
}
.fx-embedded-answer-btn-text {
vertical-align: bottom;
/* don't stretch the button if the localized text is too big */
max-width: 80%;
}
.fx-embedded-btn-icon-video {
display: inline-block;
vertical-align: top;
width: .8rem;
height: .8rem;
background-image: url("../img/video-inverse-14x14.png");
background-repeat: no-repeat;
cursor: pointer;
}
.conversation-toolbar .btn:hover {
background-color: rgba(255, 255, 255, .35);
.standalone .btn-hangup {
width: auto;
font-size: 12px;
border-radius: 2px;
padding: 0 20px;
}
/* Hangup button */
.conversation-toolbar .btn-hangup {
background-color: #D74345;
.conversation-window .conversation-toolbar .btn-hangup {
background-image: url(../img/hangup-inverse-14x14.png);
}
.conversation-toolbar .btn-hangup:hover {
background-color: #C53436;
}
@media (min-resolution: 2dppx) {
.conversation-toolbar .btn-hangup {
.conversation-window .conversation-toolbar .btn-hangup {
background-image: url(../img/hangup-inverse-14x14@2x.png);
}
}
@ -71,33 +135,33 @@
}
/* Audio mute button */
.conversation-toolbar .local-media.btn-mute-audio {
.btn-mute-audio {
background-image: url(../img/audio-inverse-14x14.png);
}
.conversation-toolbar .local-media.btn-mute-audio.muted {
.btn-mute-audio.muted {
background-image: url(../img/mute-inverse-14x14.png);
}
@media (min-resolution: 2dppx) {
.conversation-toolbar .local-media.btn-mute-audio {
.btn-mute-audio {
background-image: url(../img/audio-inverse-14x14@2x.png);
}
.conversation-toolbar .local-media.btn-mute-audio.muted {
.btn-mute-audio.muted {
background-image: url(../img/mute-inverse-14x14@2x.png);
}
}
/* Video mute button */
.conversation-toolbar .local-media.btn-mute-video {
.btn-mute-video {
background-image: url(../img/video-inverse-14x14.png);
}
.conversation-toolbar .local-media.btn-mute-video.muted {
.btn-mute-video.muted {
background-image: url(../img/facemute-14x14.png);
}
@media (min-resolution: 2dppx) {
.conversation-toolbar .local-media.btn-mute-video {
.btn-mute-video {
background-image: url(../img/video-inverse-14x14@2x.png);
}
.conversation-toolbar .local-media.btn-mute-video.muted {
.btn-mute-video.muted {
background-image: url(../img/facemute-14x14@2x.png);
}
}
@ -119,7 +183,6 @@
.conversation .media.nested .remote_wrapper {
display: inline-block;
position: relative;
background: #0f0;
width: 100%;
padding-bottom: 75%; /* XXX forced 4:3 ratio, see bug 1020445 */
}
@ -146,6 +209,14 @@
max-height: 105px;
}
.standalone .conversation .media.nested .local {
/* required to have it superimposed on the control toolbar */
z-index: 1001;
bottom: 10px;
right: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
/* Side by side video elements */
.conversation .media.side-by-side .remote {
@ -178,20 +249,37 @@
}
.incoming-call-action-group {
padding: 2.5em 1em;
}
.incoming-call-action-group .button-chevron-menu-group {
margin-right: .5em;
display: flex;
padding: 2.5em 0;
width: 100%;
justify-content: space-around;
}
.incoming-call-action-group > .btn {
margin-left: .5em;
}
.incoming-call-action-group .btn-group-chevron,
.incoming-call-action-group .btn-group {
width: 100%;
max-width: 120px; /* required by the UI Showcase, but the not real code */
}
/* XXX Once we get the incoming call avatar, bug 1047435, the H2 should
* disappear from our markup, and we should remove this rule entirely.
*/
.incoming-call h2 {
font-size: 1.5em;
font-weight: normal;
/* compensate for reset.css overriding this; values borrowed from
Firefox Mac html.css */
margin: 0.83em 0;
}
.fx-embedded-incoming-call-button-spacer {
display: flex;
flex: 1;
}
.call-audio-only {
@ -212,21 +300,10 @@
background-color: #6cb23e;
}
.call-audio-video {
background-image: url("../img/video-inverse-14x14.png");
background-position: 96% center;
background-repeat: no-repeat;
background-size: 1rem;
}
@media (min-resolution: 2dppx) {
.call-audio-only {
background-image: url("../img/audio-inverse-14x14@2x.png");
}
.call-audio-video {
background-image: url("../img/video-inverse-14x14@2x.png");
}
}
/* Expired call url page */
@ -293,7 +370,7 @@
}
.conversation-window-dropdown li {
padding: 0 10px 0 5px;
padding: 2px;
font-size: .9em;
}
@ -318,7 +395,7 @@
/* Feedback form */
.feedback {
padding: 1em;
padding: 14px;
}
.feedback h3 {
@ -326,8 +403,8 @@
font-size: 12px;
font-weight: 700;
text-align: center;
margin-bottom: 10px;
margin-top: 15px;
margin-bottom: 14px;
margin-top: 14px;
}
.feedback .faces {
@ -340,10 +417,10 @@
.feedback .face {
border: 1px solid transparent;
box-shadow: 0px 1px 2px #CCC;
box-shadow: 0 1px 2px #CCC;
cursor: pointer;
border-radius: 4px;
margin: 0px 10px;
margin: 0 10px;
width: 80px;
height: 80px;
background-color: #fbfbfb;
@ -373,7 +450,7 @@
cursor: pointer;
padding: 3px 10px;
display: inline;
margin-bottom: 1em;
margin-bottom: 14px;
}
.feedback label {
@ -385,12 +462,10 @@
margin-right: .5em;
}
.feedback form button[type="submit"] {
width: 100%;
}
.feedback form button[type="submit"],
.feedback form input[type="text"] {
width: 100%;
margin-top: 14px;
}
.feedback .info {

View File

@ -4,13 +4,14 @@
/* Panel styles */
.panel {
/* XXX the Social API panel behaves weirdly on inner element size changes,
adding unwanted scrollbars; quickfix is to hide these for now. */
overflow: hidden;
}
/* XXX force proper content positioning by adding extra margin space
* taken away by reset.css
*/
margin-top: 7px;
margin-bottom: 7px;
.component-spacer {
padding: 5px 10px 10px 10px;
/* hide the extra margin space that the panel resizer now wants to show */
overflow: hidden;
}
.spacer {
@ -21,45 +22,26 @@
background: #fbfbfb;
}
.share .description .field {
padding-bottom: 1em;
border-bottom: 1px dotted #ddd;
.share .description,
.share .action input,
.share > .action > .invite > .url-actions {
margin: 14px 14px 0 14px;
}
.share .description select {
float: right;
}
.share .description .preview video {
background: #ccc;
float: right;
width: 180px;
}
.description-content {
margin: .5em 0;
font-size: 1em;
.share .description {
font-weight: 700;
font-family: Open Sans,sans-serif;
color: #666;
}
.share .action {
clear: right;
}
.share .action input[type="text"],
.share .action input[type="url"] {
.share .action input {
border: 1px solid #ccc; /* Overriding background style for a text input (see
below) resets its borders to a weird beveled style;
defining a default 1px border solves the issue. */
font-size: 1em;
width: 100%;
padding: 0 10px;
margin: 5px 0;
border-radius: 2px;
outline: 0;
height: 24px;
height: 26px;
width: calc(100% - 28px);
}
.share .action input.pending {
@ -68,13 +50,6 @@
background-position: right;
}
/* For some reason, buttons have a bigger default font size in FF; we're
reducing a bit for graphical consistency here. */
.share .action button {
font-size: .9em;
padding-top: 6px;
}
.share .action .btn {
background-color: #0096DD;
border: 1px solid #0095DD;
@ -82,8 +57,6 @@
width: 50%;
height: 26px;
text-align: center;
font-family: 'Lucida Grande', sans-serif;
margin-top: 10px;
}
.share > .action .btn:hover {
@ -91,10 +64,6 @@
border: 1px solid #008ACB;
}
.share > .action > .invite > .url-actions {
margin: 0 0 5px;
}
.share > .action > .invite > .url-actions > .btn:first-child {
-moz-margin-end: 1em;
}
@ -126,8 +95,8 @@
.dnd-menu {
position: absolute;
top: -35px;
left: 10px;
top: -28px;
left: 0;
background: #fdfdfd;
box-shadow: 0 1px 3px rgba(0,0,0,.3);
list-style: none;
@ -194,7 +163,8 @@
color: #7F7F7F;
display: flex;
align-items: center;
margin-top: auto;
margin-top: 14px;
flex-direction: row;
padding: 14px;
}

View File

@ -0,0 +1,53 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
:focus {
/* from https://people.mozilla.org/~dhenein/labs/loop-link-spec/app.min.css */
/* remove blue outline in chrome */
outline: 0;
}

View File

@ -179,18 +179,24 @@ loop.shared.views = (function(_, OT, l10n) {
render: function() {
/* jshint ignore:start */
var hangupButtonClasses = "btn btn-hangup";
return (
React.DOM.ul({className: "conversation-toolbar"},
React.DOM.li(null, React.DOM.button({className: hangupButtonClasses,
onClick: this.handleClickHangup,
title: l10n.get("hangup_button_title")})),
React.DOM.li(null, MediaControlButton({action: this.handleToggleVideo,
enabled: this.props.video.enabled,
scope: "local", type: "video"})),
React.DOM.li(null, MediaControlButton({action: this.handleToggleAudio,
enabled: this.props.audio.enabled,
scope: "local", type: "audio"}))
React.DOM.li({className: "conversation-toolbar-btn-box"},
React.DOM.button({className: "btn btn-hangup", onClick: this.handleClickHangup,
title: l10n.get("hangup_button_title")},
l10n.get("hangup_button_caption")
)
),
React.DOM.li({className: "conversation-toolbar-btn-box"},
MediaControlButton({action: this.handleToggleVideo,
enabled: this.props.video.enabled,
scope: "local", type: "video"})
),
React.DOM.li({className: "conversation-toolbar-btn-box"},
MediaControlButton({action: this.handleToggleAudio,
enabled: this.props.audio.enabled,
scope: "local", type: "audio"})
)
)
);
/* jshint ignore:end */
@ -347,16 +353,18 @@ loop.shared.views = (function(_, OT, l10n) {
render: function() {
/* jshint ignore:start */
return (
React.DOM.div({className: "conversation"},
ConversationToolbar({video: this.state.video,
audio: this.state.audio,
publishStream: this.publishStream,
hangup: this.hangup}),
React.DOM.div({className: "media nested"},
React.DOM.div({className: "video_wrapper remote_wrapper"},
React.DOM.div({className: "video_inner remote"})
),
React.DOM.div({className: "local"})
React.DOM.div({className: "video-layout-wrapper"},
React.DOM.div({className: "conversation"},
ConversationToolbar({video: this.state.video,
audio: this.state.audio,
publishStream: this.publishStream,
hangup: this.hangup}),
React.DOM.div({className: "media nested"},
React.DOM.div({className: "video_wrapper remote_wrapper"},
React.DOM.div({className: "video_inner remote"})
),
React.DOM.div({className: "local"})
)
)
)
);

View File

@ -179,18 +179,24 @@ loop.shared.views = (function(_, OT, l10n) {
render: function() {
/* jshint ignore:start */
var hangupButtonClasses = "btn btn-hangup";
return (
<ul className="conversation-toolbar">
<li><button className={hangupButtonClasses}
onClick={this.handleClickHangup}
title={l10n.get("hangup_button_title")}></button></li>
<li><MediaControlButton action={this.handleToggleVideo}
enabled={this.props.video.enabled}
scope="local" type="video" /></li>
<li><MediaControlButton action={this.handleToggleAudio}
enabled={this.props.audio.enabled}
scope="local" type="audio" /></li>
<li className="conversation-toolbar-btn-box">
<button className="btn btn-hangup" onClick={this.handleClickHangup}
title={l10n.get("hangup_button_title")}>
{l10n.get("hangup_button_caption")}
</button>
</li>
<li className="conversation-toolbar-btn-box">
<MediaControlButton action={this.handleToggleVideo}
enabled={this.props.video.enabled}
scope="local" type="video" />
</li>
<li className="conversation-toolbar-btn-box">
<MediaControlButton action={this.handleToggleAudio}
enabled={this.props.audio.enabled}
scope="local" type="audio" />
</li>
</ul>
);
/* jshint ignore:end */
@ -347,16 +353,18 @@ loop.shared.views = (function(_, OT, l10n) {
render: function() {
/* jshint ignore:start */
return (
<div className="conversation">
<ConversationToolbar video={this.state.video}
audio={this.state.audio}
publishStream={this.publishStream}
hangup={this.hangup} />
<div className="media nested">
<div className="video_wrapper remote_wrapper">
<div className="video_inner remote"></div>
<div className="video-layout-wrapper">
<div className="conversation">
<ConversationToolbar video={this.state.video}
audio={this.state.audio}
publishStream={this.publishStream}
hangup={this.hangup} />
<div className="media nested">
<div className="video_wrapper remote_wrapper">
<div className="video_inner remote"></div>
</div>
<div className="local"></div>
</div>
<div className="local"></div>
</div>
</div>
);

View File

@ -17,6 +17,7 @@ browser.jar:
content/browser/loop/js/panel.js (content/js/panel.js)
# Shared styles
content/browser/loop/shared/css/reset.css (content/shared/css/reset.css)
content/browser/loop/shared/css/common.css (content/shared/css/common.css)
content/browser/loop/shared/css/panel.css (content/shared/css/panel.css)
content/browser/loop/shared/css/conversation.css (content/shared/css/conversation.css)

View File

@ -10,50 +10,49 @@ body,
height: 100%;
}
body {
.standalone {
width: 100%;
/* prevent the video convsersation elements to occupy the whole available
width hence the height while keeping aspect ratio */
max-width: 730px;
margin: 0 auto;
background: #fbfbfb;
color: #666;
text-align: center;
font-family: Open Sans,sans-serif;
}
header {
.standalone-header {
border-radius: 4px;
background: #fff;
padding: 1rem 5rem;
border: 1px solid #E7E7E7;
box-shadow: 0px 2px 0px rgba(0, 0, 0, 0.03);
margin-top: 2rem;
}
/*
* Top/Bottom spacing
**/
header {
margin-top: 2rem;
}
.footer {
.standalone-footer {
margin-bottom: 2rem;
}
.container {
display: flex;
align-items: center;
flex-direction: column;
justify-content: space-between;
margin: 0 auto;
/* prevent the video conversation elements to occupy the whole available
width hence the height while keeping aspect ratio */
width: 30%;
min-width: 400px;
height: 100%;
align-items: center;
justify-content: space-between;
}
.container-box {
display: flex;
flex-direction: column;
align-content: center;
width: 100%;
align-content: center;
}
.footer,
@ -96,12 +95,6 @@ header {
background-repeat: no-repeat;
}
.call-url {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.loop-logo {
width: 100px;
height: 100px;
@ -111,12 +104,17 @@ header {
background-repeat: no-repeat;
}
.large-font {
.standalone-header-title,
.standalone-call-btn-label {
font-weight: lighter;
}
.standalone-header-title {
font-size: 1.8rem;
}
.light-weight-font {
font-weight: lighter;
.standalone-call-btn-label {
font-size: 1.2rem;
}
.light-color-font {
@ -124,28 +122,49 @@ header {
font-weight: normal;
}
.standalone-btn-chevron-menu-group {
display: flex;
justify-content: space-between;
flex: 2;
}
.start-audio-only-call,
.start-audio-video-call {
background-color: none;
background-image: url("../shared/img/audio-default-16x16@1.5x.png");
background-position: 80% center;
background-size: 10px;
.standalone-call-btn-video-icon {
width: 1.2rem;
height: 1.2rem;
background-repeat: no-repeat;
cursor: pointer;
}
.start-audio-only-call {
border: none;
width: 100%;
border: none;
background-size: 10px;
background-color: #F0F0F0;
background-image: url("../shared/img/audio-default-16x16@1.5x.png");
background-position: 90% center;
}
.start-audio-only-call:hover {
background-image: url("../shared/img/audio-inverse-14x14.png");
}
.start-audio-video-call {
background-size: 20px;
.standalone-call-btn-video-icon {
background-image: url("../shared/img/video-inverse-14x14.png");
display: inline-block;
background-size: 1.2rem;
margin-left: .5rem;
}
/* Ellipsed text content of audio-video call btn */
.standalone-call-btn-text {
/* make some room for the video icon */
max-width: 85%;
vertical-align: bottom;
}
.standalone-call-btn-video-icon {
vertical-align: top;
}
@media (min-resolution: 2dppx) {
@ -155,8 +174,29 @@ header {
.start-audio-only-call:hover {
background-image: url("../shared/img/audio-inverse-14x14@2x.png");
}
.start-audio-video-call {
.standalone-call-btn-video-icon {
background-image: url("../shared/img/video-inverse-14x14@2x.png");
}
}
.btn-large {
/* Dimensions from spec
* https://people.mozilla.org/~dhenein/labs/loop-link-spec/#call-start */
font-size: 1rem;
padding: .3em .5rem;
}
.btn-large + .btn-chevron {
padding: 1rem;
height: 100%; /* match full height of button */
}
/*
* Left / Right padding elements
* used to center components
* */
.flex-padding-1 {
display: flex;
flex: 1;
}

View File

@ -6,12 +6,13 @@
<head>
<meta charset="utf-8">
<title>Loop</title>
<link rel="stylesheet" type="text/css" href="shared/css/reset.css">
<link rel="stylesheet" type="text/css" href="shared/css/common.css">
<link rel="stylesheet" type="text/css" href="shared/css/conversation.css">
<link rel="stylesheet" type="text/css" href="css/webapp.css">
<link type="application/l10n" href="l10n/data.ini">
</head>
<body>
<body class="standalone">
<div id="main"></div>

View File

@ -48,7 +48,7 @@ loop.webapp = (function($, _, OT, webL10n) {
React.DOM.div({className: "promote-firefox"},
React.DOM.h3(null, __("promote_firefox_hello_heading")),
React.DOM.p(null,
React.DOM.a({className: "btn btn-large btn-success",
React.DOM.a({className: "btn btn-large btn-accept",
href: "https://www.mozilla.org/firefox/"},
__("get_firefox_button")
)
@ -100,8 +100,8 @@ loop.webapp = (function($, _, OT, webL10n) {
return (
/* jshint ignore:start */
React.DOM.header({className: "container-box"},
React.DOM.h1({className: "light-weight-font"},
React.DOM.header({className: "standalone-header container-box"},
React.DOM.h1({className: "standalone-header-title"},
React.DOM.strong(null, __("brandShortname")), " ", __("clientShortname")
),
React.DOM.div({className: "loop-logo", title: "Firefox WebRTC! logo"}),
@ -120,7 +120,7 @@ loop.webapp = (function($, _, OT, webL10n) {
var ConversationFooter = React.createClass({displayName: 'ConversationFooter',
render: function() {
return (
React.DOM.div({className: "footer container-box"},
React.DOM.div({className: "standalone-footer container-box"},
React.DOM.div({title: "Mozilla Logo", className: "footer-logo"})
)
);
@ -229,8 +229,7 @@ loop.webapp = (function($, _, OT, webL10n) {
"https://www.mozilla.org/privacy/'>" + privacy_notice_name + "</a>"
});
var btnClassStartCall = "btn btn-large btn-success " +
"start-audio-video-call " +
var btnClassStartCall = "btn btn-large btn-accept " +
loop.shared.utils.getTargetPlatform();
var dropdownMenuClasses = React.addons.classSet({
"native-dropdown-large-parent": true,
@ -250,23 +249,26 @@ loop.webapp = (function($, _, OT, webL10n) {
ConversationHeader({
urlCreationDateString: this.state.urlCreationDateString}),
React.DOM.p({className: "large-font light-weight-font"},
React.DOM.p({className: "standalone-call-btn-label"},
__("initiate_call_button_label")
),
React.DOM.div({id: "messages"}),
React.DOM.div({className: "button-group"},
React.DOM.div({className: "btn-group"},
React.DOM.div({className: "flex-padding-1"}),
React.DOM.div({className: "button-chevron-menu-group"},
React.DOM.div({className: "button-group-chevron"},
React.DOM.div({className: "button-group"},
React.DOM.div({className: "standalone-btn-chevron-menu-group"},
React.DOM.div({className: "btn-group-chevron"},
React.DOM.div({className: "btn-group"},
React.DOM.button({className: btnClassStartCall,
onClick: this._initiateOutgoingCall("audio-video"),
disabled: this.state.disableCallButton,
title: __("initiate_audio_video_call_tooltip")},
__("initiate_audio_video_call_button")
React.DOM.span({className: "standalone-call-btn-text"},
__("initiate_audio_video_call_button")
),
React.DOM.span({className: "standalone-call-btn-video-icon"})
),
React.DOM.div({className: "btn-chevron",
@ -537,8 +539,8 @@ loop.webapp = (function($, _, OT, webL10n) {
var helper = new WebappHelper();
var client = new loop.StandaloneClient({
baseServerUrl: baseServerUrl
}),
router = new WebappRouter({
});
var router = new WebappRouter({
helper: helper,
notifier: new sharedViews.NotificationListView({el: "#messages"}),
client: client,
@ -547,12 +549,16 @@ loop.webapp = (function($, _, OT, webL10n) {
pendingCallTimeout: loop.config.pendingCallTimeout
})
});
Backbone.history.start();
if (helper.isIOS(navigator.platform)) {
router.navigate("unsupportedDevice", {trigger: true});
} else if (!OT.checkSystemRequirements()) {
router.navigate("unsupportedBrowser", {trigger: true});
}
document.body.classList.add(loop.shared.utils.getTargetPlatform());
// Set the 'lang' and 'dir' attributes to <html> when the page is translated
document.documentElement.lang = document.webL10n.getLanguage();
document.documentElement.dir = document.webL10n.getDirection();

View File

@ -48,7 +48,7 @@ loop.webapp = (function($, _, OT, webL10n) {
<div className="promote-firefox">
<h3>{__("promote_firefox_hello_heading")}</h3>
<p>
<a className="btn btn-large btn-success"
<a className="btn btn-large btn-accept"
href="https://www.mozilla.org/firefox/">
{__("get_firefox_button")}
</a>
@ -100,8 +100,8 @@ loop.webapp = (function($, _, OT, webL10n) {
return (
/* jshint ignore:start */
<header className="container-box">
<h1 className="light-weight-font">
<header className="standalone-header container-box">
<h1 className="standalone-header-title">
<strong>{__("brandShortname")}</strong> {__("clientShortname")}
</h1>
<div className="loop-logo" title="Firefox WebRTC! logo"></div>
@ -120,7 +120,7 @@ loop.webapp = (function($, _, OT, webL10n) {
var ConversationFooter = React.createClass({
render: function() {
return (
<div className="footer container-box">
<div className="standalone-footer container-box">
<div title="Mozilla Logo" className="footer-logo"></div>
</div>
);
@ -229,8 +229,7 @@ loop.webapp = (function($, _, OT, webL10n) {
"https://www.mozilla.org/privacy/'>" + privacy_notice_name + "</a>"
});
var btnClassStartCall = "btn btn-large btn-success " +
"start-audio-video-call " +
var btnClassStartCall = "btn btn-large btn-accept " +
loop.shared.utils.getTargetPlatform();
var dropdownMenuClasses = React.addons.classSet({
"native-dropdown-large-parent": true,
@ -250,23 +249,26 @@ loop.webapp = (function($, _, OT, webL10n) {
<ConversationHeader
urlCreationDateString={this.state.urlCreationDateString} />
<p className="large-font light-weight-font">
<p className="standalone-call-btn-label">
{__("initiate_call_button_label")}
</p>
<div id="messages"></div>
<div className="button-group">
<div className="btn-group">
<div className="flex-padding-1"></div>
<div className="button-chevron-menu-group">
<div className="button-group-chevron">
<div className="button-group">
<div className="standalone-btn-chevron-menu-group">
<div className="btn-group-chevron">
<div className="btn-group">
<button className={btnClassStartCall}
onClick={this._initiateOutgoingCall("audio-video")}
disabled={this.state.disableCallButton}
title={__("initiate_audio_video_call_tooltip")} >
{__("initiate_audio_video_call_button")}
<span className="standalone-call-btn-text">
{__("initiate_audio_video_call_button")}
</span>
<span className="standalone-call-btn-video-icon"></span>
</button>
<div className="btn-chevron"
@ -537,8 +539,8 @@ loop.webapp = (function($, _, OT, webL10n) {
var helper = new WebappHelper();
var client = new loop.StandaloneClient({
baseServerUrl: baseServerUrl
}),
router = new WebappRouter({
});
var router = new WebappRouter({
helper: helper,
notifier: new sharedViews.NotificationListView({el: "#messages"}),
client: client,
@ -547,12 +549,16 @@ loop.webapp = (function($, _, OT, webL10n) {
pendingCallTimeout: loop.config.pendingCallTimeout
})
});
Backbone.history.start();
if (helper.isIOS(navigator.platform)) {
router.navigate("unsupportedDevice", {trigger: true});
} else if (!OT.checkSystemRequirements()) {
router.navigate("unsupportedBrowser", {trigger: true});
}
document.body.classList.add(loop.shared.utils.getTargetPlatform());
// Set the 'lang' and 'dir' attributes to <html> when the page is translated
document.documentElement.lang = document.webL10n.getLanguage();
document.documentElement.dir = document.webL10n.getDirection();

View File

@ -7,6 +7,7 @@ network_disconnected=The network connection terminated abruptly.
peer_ended_conversation2=The person you were calling has ended the conversation.
unable_retrieve_call_info=Unable to retrieve conversation information.
hangup_button_title=Hang up
hangup_button_caption=End Call
mute_local_audio_button_title=Mute your audio
unmute_local_audio_button_title=Unmute your audio
mute_local_video_button_title=Mute your video
@ -44,6 +45,7 @@ missing_conversation_info=Informations de communication manquantes.
network_disconnected=La connexion réseau semble avoir été interrompue.
unable_retrieve_call_info=Impossible de récupérer les informations liées à cet appel.
hangup_button_title=Terminer l'appel
hangup_button_caption=Raccrocher
mute_local_audio_button_title=Couper la diffusion audio
unmute_local_audio_button_title=Reprendre la diffusion audio
mute_local_video_button_title=Couper la diffusion vidéo

View File

@ -25,6 +25,8 @@ app.get('/content/config.js', function (req, res) {
app.use('/', express.static(__dirname + '/../'));
// This lets /content/ be mapped right for the static contents.
app.use('/', express.static(__dirname + '/'));
// This lets standalone components load images into the UI showcase
app.use('/standalone/content', express.static(__dirname + '/../content'));
var server = app.listen(port);

View File

@ -547,27 +547,38 @@ describe("loop.conversation", function() {
});
describe("click event on .btn-accept", function() {
it("should trigger an 'accept' conversation model event", function() {
it("should trigger an 'accept' conversation model event", function () {
var buttonAccept = view.getDOMNode().querySelector(".btn-accept");
model.trigger.withArgs("accept");
TestUtils.Simulate.click(buttonAccept);
/* Setting a model property triggers 2 events */
sinon.assert.calledThrice(model.trigger);
sinon.assert.calledWith(model.trigger, "accept");
sinon.assert.calledWith(model.trigger, "change:selectedCallType");
sinon.assert.calledWith(model.trigger, "change");
sinon.assert.calledOnce(model.trigger.withArgs("accept"));
});
it("should set selectedCallType to audio-video", function() {
var buttonAccept = view.getDOMNode().querySelector(".call-audio-video");
it("should set selectedCallType to audio-video", function () {
var buttonAccept = view.getDOMNode().querySelector(".btn-accept");
sandbox.stub(model, "set");
TestUtils.Simulate.click(buttonAccept);
sinon.assert.calledOnce(model.set);
sinon.assert.calledWithExactly(model.set, "selectedCallType", "audio-video");
sinon.assert.calledWithExactly(model.set, "selectedCallType",
"audio-video");
});
});
describe("click event on .call-audio-only", function() {
it("should trigger an 'accept' conversation model event", function () {
var buttonAccept = view.getDOMNode().querySelector(".call-audio-only");
model.trigger.withArgs("accept");
TestUtils.Simulate.click(buttonAccept);
/* Setting a model property triggers 2 events */
sinon.assert.calledOnce(model.trigger.withArgs("accept"));
});
it("should set selectedCallType to audio", function() {
var buttonAccept = view.getDOMNode().querySelector(".call-audio-only");

View File

@ -39,7 +39,7 @@ class Test1BrowserCall(MarionetteTestCase):
.until(lambda m: m.find_element(by, locator).is_displayed())
return self.marionette.find_element(by, locator)
# XXX workaround for Marionette bug YYY
# XXX workaround for Marionette bug 1055309
def wait_for_element_exists(self, by, locator, timeout=None):
Wait(self.marionette, timeout,
ignored_exceptions=[NoSuchElementException, StaleElementException]) \
@ -95,7 +95,8 @@ class Test1BrowserCall(MarionetteTestCase):
def start_and_verify_outgoing_call(self):
# make the call!
call_button = self.marionette.find_element(By.CLASS_NAME, "btn-success")
call_button = self.marionette.find_element(By.CLASS_NAME,
"btn-accept")
call_button.click()
# expect a video container on standalone side
@ -116,7 +117,7 @@ class Test1BrowserCall(MarionetteTestCase):
# Accept the incoming call
call_button = self.marionette.find_element(By.CLASS_NAME,
"btn-success")
"btn-accept")
# accept call from the desktop side
call_button.click()

View File

@ -528,27 +528,29 @@ describe("loop.webapp", function() {
);
});
it("should start the conversation establishment process", function() {
var button = view.getDOMNode().querySelector(".start-audio-video-call");
React.addons.TestUtils.Simulate.click(button);
it("should start the audio-video conversation establishment process",
function() {
var button = view.getDOMNode().querySelector(".btn-accept");
React.addons.TestUtils.Simulate.click(button);
sinon.assert.calledOnce(setupOutgoingCall);
sinon.assert.calledWithExactly(setupOutgoingCall);
sinon.assert.calledOnce(setupOutgoingCall);
sinon.assert.calledWithExactly(setupOutgoingCall);
});
it("should start the conversation establishment process", function() {
var button = view.getDOMNode().querySelector(".start-audio-only-call");
React.addons.TestUtils.Simulate.click(button);
it("should start the audio-only conversation establishment process",
function() {
var button = view.getDOMNode().querySelector(".start-audio-only-call");
React.addons.TestUtils.Simulate.click(button);
sinon.assert.calledOnce(setupOutgoingCall);
sinon.assert.calledWithExactly(setupOutgoingCall);
});
sinon.assert.calledOnce(setupOutgoingCall);
sinon.assert.calledWithExactly(setupOutgoingCall);
});
it("should disable audio-video button once session is initiated",
function() {
conversation.set("loopToken", "fake");
var button = view.getDOMNode().querySelector(".start-audio-video-call");
var button = view.getDOMNode().querySelector(".btn-accept");
React.addons.TestUtils.Simulate.click(button);
expect(button.disabled).to.eql(true);
@ -576,7 +578,7 @@ describe("loop.webapp", function() {
it("should set selectedCallType to audio-video", function() {
conversation.set("loopToken", "fake");
var button = view.getDOMNode().querySelector(".start-audio-video-call");
var button = view.getDOMNode().querySelector(".standalone-call-btn-video-icon");
React.addons.TestUtils.Simulate.click(button);
expect(conversation.get("selectedCallType")).to.eql("audio-video");

View File

@ -9,7 +9,14 @@
* @type {Object}
*/
document.webL10n = document.mozL10n = {
get: function(sringId, vars) {
return "" + sringId + (vars ? ";" + JSON.stringify(vars) : "");
get: function(stringId, vars) {
// upcase the first letter
var readableStringId = stringId.replace(/^./, function(match) {
"use strict";
return match.toUpperCase();
}).replace(/_/g, " "); // and convert _ chars to spaces
return "" + readableStringId + (vars ? ";" + JSON.stringify(vars) : "");
}
};

View File

@ -6,9 +6,11 @@
<head>
<meta charset="utf-8">
<title>Loop UI Components Showcase</title>
<link rel="stylesheet" type="text/css" href="../content/shared/css/reset.css">
<link rel="stylesheet" type="text/css" href="../content/shared/css/common.css">
<link rel="stylesheet" type="text/css" href="../content/shared/css/conversation.css">
<link rel="stylesheet" type="text/css" href="../content/shared/css/panel.css">
<link rel="stylesheet" type="text/css" href="../standalone/content/css/webapp.css">
<link rel="stylesheet" type="text/css" href="ui-showcase.css">
</head>
<body>

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@ -16,8 +16,16 @@
padding-bottom: 1em;
}
.showcase .menu > a {
.showcase > header > h1,
.showcase > section > h1 {
font-size: 2em;
font-weight: bold;
margin: .5em 0;
}
.showcase-menu > a {
margin-right: .5em;
padding: .4rem;
}
.showcase > section {
@ -43,7 +51,10 @@
}
.showcase > section .example > h3 {
font-size: 1.2em;
font-weight: bold;
border-bottom: 1px dashed #aaa;
margin: .5em 0;
}
.showcase p.note {
@ -52,3 +63,22 @@
color: #666;
font-style: italic;
}
.showcase p.note > strong {
font-weight: bold;
}
/* Images as fake videos for conversation view */
.conversation .media.nested .remote,
.conversation .media.nested .local {
background-size: contain;
}
.conversation .media.nested .remote {
background-image: url("sample-img/video-screen-remote.png");
background-repeat: no-repeat;
}
.conversation .media.nested .local {
background-image: url("sample-img/video-screen-local.png");
}

View File

@ -17,7 +17,8 @@
var IncomingCallView = loop.conversation.IncomingCallView;
// 2. Standalone webapp
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
var StartConversationView = loop.webapp.StartConversationView;
// 3. Shared components
var ConversationToolbar = loop.shared.views.ConversationToolbar;
@ -38,7 +39,14 @@
var stageFeedbackApiClient = new loop.FeedbackAPIClient(
"https://input.allizom.org/api/v1/feedback", {
product: "Loop"
});
}
);
var mockClient = {
requestCallUrl: function() {}
};
var mockConversationModel = new loop.shared.models.ConversationModel({}, {sdk: {}});
var Example = React.createClass({displayName: 'Example',
render: function() {
@ -72,7 +80,7 @@
React.DOM.div({className: "showcase"},
React.DOM.header(null,
React.DOM.h1(null, "Loop UI Components Showcase"),
React.DOM.nav({className: "menu"},
React.DOM.nav({className: "showcase-menu"},
React.Children.map(this.props.children, function(section) {
return (
React.DOM.a({className: "btn btn-info", href: "#" + section.props.name},
@ -96,11 +104,11 @@
React.DOM.p({className: "note"},
React.DOM.strong(null, "Note:"), " 332px wide."
),
Example({summary: "Pending call url retrieval", dashed: "true", style: {width: "332px"}},
PanelView(null)
),
Example({summary: "Call URL retrieved", dashed: "true", style: {width: "332px"}},
PanelView({callUrl: "http://invalid.example.url/"})
PanelView({callUrl: "http://invalid.example.url/", client: mockClient})
),
Example({summary: "Pending call url retrieval", dashed: "true", style: {width: "332px"}},
PanelView({client: mockClient})
)
),
@ -111,20 +119,58 @@
),
Section({name: "ConversationToolbar"},
Example({summary: "Default"},
ConversationToolbar({video: {enabled: true}, audio: {enabled: true}})
React.DOM.h3(null, "Desktop Conversation Window"),
React.DOM.div({className: "conversation-window"},
Example({summary: "Default (260x265)", dashed: "true"},
ConversationToolbar({video: {enabled: true}, audio: {enabled: true}})
),
Example({summary: "Video muted"},
ConversationToolbar({video: {enabled: false}, audio: {enabled: true}})
),
Example({summary: "Audio muted"},
ConversationToolbar({video: {enabled: true}, audio: {enabled: false}})
)
),
Example({summary: "Video muted"},
ConversationToolbar({video: {enabled: false}, audio: {enabled: true}})
),
Example({summary: "Audio muted"},
ConversationToolbar({video: {enabled: true}, audio: {enabled: false}})
React.DOM.h3(null, "Standalone"),
React.DOM.div({className: "standalone"},
Example({summary: "Default"},
ConversationToolbar({video: {enabled: true}, audio: {enabled: true}})
),
Example({summary: "Video muted"},
ConversationToolbar({video: {enabled: false}, audio: {enabled: true}})
),
Example({summary: "Audio muted"},
ConversationToolbar({video: {enabled: true}, audio: {enabled: false}})
)
)
),
Section({name: "StartConversationView"},
Example({summary: "Start conversation view", dashed: "true"},
React.DOM.div({className: "standalone"},
StartConversationView({model: mockConversationModel,
client: mockClient})
)
)
),
Section({name: "ConversationView"},
Example({summary: "Default"},
ConversationView({video: {enabled: true}, audio: {enabled: true}})
Example({summary: "Desktop conversation window", dashed: "true",
style: {width: "260px", height: "265px"}},
React.DOM.div({className: "conversation-window"},
ConversationView({video: {enabled: true}, audio: {enabled: true},
model: mockConversationModel})
)
),
Example({summary: "Standalone version"},
React.DOM.div({className: "standalone"},
ConversationView({video: {enabled: true}, audio: {enabled: true},
model: mockConversationModel})
)
)
),
@ -158,6 +204,8 @@
});
window.addEventListener("DOMContentLoaded", function() {
var body = document.body;
body.className = loop.shared.utils.getTargetPlatform();
React.renderComponent(App(null), document.body);
});
})();

View File

@ -17,7 +17,8 @@
var IncomingCallView = loop.conversation.IncomingCallView;
// 2. Standalone webapp
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
var StartConversationView = loop.webapp.StartConversationView;
// 3. Shared components
var ConversationToolbar = loop.shared.views.ConversationToolbar;
@ -38,7 +39,14 @@
var stageFeedbackApiClient = new loop.FeedbackAPIClient(
"https://input.allizom.org/api/v1/feedback", {
product: "Loop"
});
}
);
var mockClient = {
requestCallUrl: function() {}
};
var mockConversationModel = new loop.shared.models.ConversationModel({}, {sdk: {}});
var Example = React.createClass({
render: function() {
@ -72,7 +80,7 @@
<div className="showcase">
<header>
<h1>Loop UI Components Showcase</h1>
<nav className="menu">{
<nav className="showcase-menu">{
React.Children.map(this.props.children, function(section) {
return (
<a className="btn btn-info" href={"#" + section.props.name}>
@ -96,11 +104,11 @@
<p className="note">
<strong>Note:</strong> 332px wide.
</p>
<Example summary="Pending call url retrieval" dashed="true" style={{width: "332px"}}>
<PanelView />
</Example>
<Example summary="Call URL retrieved" dashed="true" style={{width: "332px"}}>
<PanelView callUrl="http://invalid.example.url/" />
<PanelView callUrl="http://invalid.example.url/" client={mockClient} />
</Example>
<Example summary="Pending call url retrieval" dashed="true" style={{width: "332px"}}>
<PanelView client={mockClient} />
</Example>
</Section>
@ -111,20 +119,58 @@
</Section>
<Section name="ConversationToolbar">
<Example summary="Default">
<ConversationToolbar video={{enabled: true}} audio={{enabled: true}} />
</Example>
<Example summary="Video muted">
<ConversationToolbar video={{enabled: false}} audio={{enabled: true}} />
</Example>
<Example summary="Audio muted">
<ConversationToolbar video={{enabled: true}} audio={{enabled: false}} />
<h3>Desktop Conversation Window</h3>
<div className="conversation-window">
<Example summary="Default (260x265)" dashed="true">
<ConversationToolbar video={{enabled: true}} audio={{enabled: true}} />
</Example>
<Example summary="Video muted">
<ConversationToolbar video={{enabled: false}} audio={{enabled: true}} />
</Example>
<Example summary="Audio muted">
<ConversationToolbar video={{enabled: true}} audio={{enabled: false}} />
</Example>
</div>
<h3>Standalone</h3>
<div className="standalone">
<Example summary="Default">
<ConversationToolbar video={{enabled: true}} audio={{enabled: true}} />
</Example>
<Example summary="Video muted">
<ConversationToolbar video={{enabled: false}} audio={{enabled: true}} />
</Example>
<Example summary="Audio muted">
<ConversationToolbar video={{enabled: true}} audio={{enabled: false}} />
</Example>
</div>
</Section>
<Section name="StartConversationView">
<Example summary="Start conversation view" dashed="true">
<div className="standalone">
<StartConversationView model={mockConversationModel}
client={mockClient} />
</div>
</Example>
</Section>
<Section name="ConversationView">
<Example summary="Default">
<ConversationView video={{enabled: true}} audio={{enabled: true}} />
<Example summary="Desktop conversation window" dashed="true"
style={{width: "260px", height: "265px"}}>
<div className="conversation-window">
<ConversationView video={{enabled: true}} audio={{enabled: true}}
model={mockConversationModel} />
</div>
</Example>
<Example summary="Standalone version">
<div className="standalone">
<ConversationView video={{enabled: true}} audio={{enabled: true}}
model={mockConversationModel} />
</div>
</Example>
</Section>
@ -158,6 +204,8 @@
});
window.addEventListener("DOMContentLoaded", function() {
var body = document.body;
body.className = loop.shared.utils.getTargetPlatform();
React.renderComponent(<App />, document.body);
});
})();

View File

@ -45,6 +45,10 @@ var gAdvancedPane = {
this.updateActualCacheSize();
this.updateActualAppCacheSize();
let bundlePrefs = document.getElementById("bundlePreferences");
document.getElementById("offlineAppsList")
.style.height = bundlePrefs.getString("offlineAppsList.height");
// Notify observers that the UI is now ready
Services.obs.notifyObservers(window, "advanced-pane-loaded", null);
},

View File

@ -288,7 +288,6 @@
<vbox flex="1">
<label id="offlineAppsListLabel">&offlineAppsList2.label;</label>
<listbox id="offlineAppsList"
style="height: &offlineAppsList.height;;"
flex="1"
aria-labelledby="offlineAppsListLabel"
onselect="gAdvancedPane.offlineAppSelected(event);">

View File

@ -15,6 +15,12 @@ var gAdvancedPane = {
*/
init: function ()
{
function setEventListener(aId, aEventType, aCallback)
{
document.getElementById(aId)
.addEventListener(aEventType, aCallback.bind(gAdvancedPane));
}
this._inited = true;
var advancedPrefs = document.getElementById("advancedPrefs");
@ -35,6 +41,44 @@ var gAdvancedPane = {
#endif
this.updateActualCacheSize();
this.updateActualAppCacheSize();
setEventListener("layers.acceleration.disabled", "change",
gAdvancedPane.updateHardwareAcceleration);
setEventListener("advancedPrefs", "select",
gAdvancedPane.tabSelectionChanged);
#ifdef MOZ_SERVICES_HEALTHREPORT
setEventListener("submitHealthReportBox", "command",
gAdvancedPane.updateSubmitHealthReport);
#endif
#ifdef MOZ_CRASHREPORTER
setEventListener("submitCrashesBox", "command",
gAdvancedPane.updateSubmitCrashes);
#endif
setEventListener("connectionSettings", "command",
gAdvancedPane.showConnections);
setEventListener("clearCacheButton", "command",
gAdvancedPane.clearCache);
setEventListener("clearOfflineAppCacheButton", "command",
gAdvancedPane.clearOfflineAppCache);
setEventListener("offlineNotifyExceptions", "command",
gAdvancedPane.showOfflineExceptions);
setEventListener("offlineNotifyExceptions", "command", function (event) {
gAdvancedPane.offlineAppSelected(event); })
let bundlePrefs = document.getElementById("bundlePreferences");
document.getElementById("offlineAppsList")
.style.height = bundlePrefs.getString("offlineAppsList.height");
setEventListener("offlineAppsListRemove", "command",
gAdvancedPane.removeOfflineApp);
#ifdef MOZ_UPDATER
setEventListener("updateRadioGroup", "command",
gAdvancedPane.updateWritePrefs);
#endif
setEventListener("showUpdateHistory", "command",
gAdvancedPane.showUpdates);
setEventListener("viewCertificatesButton", "command",
gAdvancedPane.showCertificates);
setEventListener("viewSecurityDevicesButton", "command",
gAdvancedPane.showSecurityDevices);
},
/**

View File

@ -32,8 +32,7 @@
<preference id="layers.acceleration.disabled"
name="layers.acceleration.disabled"
type="bool"
inverted="true"
onchange="gAdvancedPane.updateHardwareAcceleration()"/>
inverted="true"/>
#ifdef XP_WIN
<preference id="gfx.direct2d.disabled"
name="gfx.direct2d.disabled"
@ -133,8 +132,7 @@
handleCtrlPageUpDown="false"
flex="1"
data-category="paneAdvanced"
hidden="true"
onselect="gAdvancedPane.tabSelectionChanged();">
hidden="true">
<tabs id="tabsElement">
<tab id="generalTab" label="&generalTab.label;"/>
@ -215,7 +213,6 @@
<description>&healthReportDesc.label;</description>
<hbox>
<checkbox id="submitHealthReportBox"
oncommand="gAdvancedPane.updateSubmitHealthReport();"
label="&enableHealthReport.label;"
accesskey="&enableHealthReport.accesskey;"/>
<spacer flex="1"/>
@ -230,7 +227,6 @@
<description>&crashReporterDesc.label;</description>
<hbox>
<checkbox id="submitCrashesBox"
oncommand="gAdvancedPane.updateSubmitCrashes();"
label="&enableCrashReporter.label;"
accesskey="&enableCrashReporter.accesskey;"/>
@ -253,8 +249,7 @@
<hbox align="center">
<description flex="1" control="connectionSettings">&connectionDesc.label;</description>
<button id="connectionSettings" icon="network" label="&connectionSettings.label;"
accesskey="&connectionSettings.accesskey;"
oncommand="gAdvancedPane.showConnections();"/>
accesskey="&connectionSettings.accesskey;"/>
</hbox>
</groupbox>
@ -265,8 +260,7 @@
<hbox align="center">
<label id="actualDiskCacheSize" flex="1"/>
<button id="clearCacheButton" icon="clear"
label="&clearCacheNow.label;" accesskey="&clearCacheNow.accesskey;"
oncommand="gAdvancedPane.clearCache();"/>
label="&clearCacheNow.label;" accesskey="&clearCacheNow.accesskey;"/>
</hbox>
<hbox>
<checkbox preference="browser.cache.disk.smart_size.enabled"
@ -296,8 +290,7 @@
<hbox align="center">
<label id="actualAppCacheSize" flex="1"/>
<button id="clearOfflineAppCacheButton" icon="clear"
label="&clearOfflineAppCacheNow.label;" accesskey="&clearOfflineAppCacheNow.accesskey;"
oncommand="gAdvancedPane.clearOfflineAppCache();"/>
label="&clearOfflineAppCacheNow.label;" accesskey="&clearOfflineAppCacheNow.accesskey;"/>
</hbox>
<hbox align="center">
<checkbox id="offlineNotify"
@ -307,25 +300,21 @@
<spacer flex="1"/>
<button id="offlineNotifyExceptions"
label="&offlineNotifyExceptions.label;"
accesskey="&offlineNotifyExceptions.accesskey;"
oncommand="gAdvancedPane.showOfflineExceptions();"/>
accesskey="&offlineNotifyExceptions.accesskey;"/>
</hbox>
<hbox>
<vbox flex="1">
<label id="offlineAppsListLabel">&offlineAppsList2.label;</label>
<listbox id="offlineAppsList"
style="height: &offlineAppsList.height;;"
flex="1"
aria-labelledby="offlineAppsListLabel"
onselect="gAdvancedPane.offlineAppSelected(event);">
aria-labelledby="offlineAppsListLabel">
</listbox>
</vbox>
<vbox pack="end">
<button id="offlineAppsListRemove"
disabled="true"
label="&offlineAppsListRemove.label;"
accesskey="&offlineAppsListRemove.accesskey;"
oncommand="gAdvancedPane.removeOfflineApp();"/>
accesskey="&offlineAppsListRemove.accesskey;"/>
</vbox>
</hbox>
</groupbox>
@ -336,8 +325,7 @@
#ifdef MOZ_UPDATER
<groupbox id="updateApp" align="start">
<caption><label>&updateApp.label;</label></caption>
<radiogroup id="updateRadioGroup" align="start"
oncommand="gAdvancedPane.updateWritePrefs();">
<radiogroup id="updateRadioGroup" align="start">
#ifdef XP_WIN
#ifdef MOZ_METRO
<radio id="autoMetro"
@ -374,8 +362,7 @@
<button id="showUpdateHistory"
label="&updateHistory.label;"
accesskey="&updateHistory.accesskey;"
preference="app.update.disable_button.showUpdateHistory"
oncommand="gAdvancedPane.showUpdates();"/>
preference="app.update.disable_button.showUpdateHistory"/>
</hbox>
#ifdef MOZ_MAINTENANCE_SERVICE
@ -428,11 +415,9 @@
<hbox>
<button id="viewCertificatesButton"
label="&viewCerts.label;" accesskey="&viewCerts.accesskey;"
oncommand="gAdvancedPane.showCertificates();"
preference="security.disable_button.openCertManager"/>
<button id="viewSecurityDevicesButton"
label="&viewSecurityDevices.label;" accesskey="&viewSecurityDevices.accesskey;"
oncommand="gAdvancedPane.showSecurityDevices();"
preference="security.disable_button.openDeviceManager"/>
</hbox>
</tabpanel>

View File

@ -904,6 +904,12 @@ var gApplicationsPane = {
// Initialization & Destruction
init: function() {
function setEventListener(aId, aEventType, aCallback)
{
document.getElementById(aId)
.addEventListener(aEventType, aCallback.bind(gApplicationsPane));
}
// Initialize shortcuts to some commonly accessed elements & values.
this._brandShortName =
document.getElementById("bundleBrand").getString("brandShortName");
@ -931,6 +937,14 @@ var gApplicationsPane = {
this._prefSvc.addObserver(PREF_AUDIO_FEED_SELECTED_READER, this, false);
setEventListener("focusSearch1", "command", gApplicationsPane.focusFilterBox);
setEventListener("focusSearch2", "command", gApplicationsPane.focusFilterBox);
setEventListener("filter", "command", gApplicationsPane.filter);
setEventListener("handlersView", "select",
gApplicationsPane.onSelectionChanged);
setEventListener("typeColumn", "click", gApplicationsPane.sort);
setEventListener("actionColumn", "click", gApplicationsPane.sort);
// Listen for window unload so we can remove our preference observers.
window.addEventListener("unload", this, false);

View File

@ -53,8 +53,8 @@
</preferences>
<keyset>
<key key="&focusSearch1.key;" modifiers="accel" oncommand="gApplicationsPane.focusFilterBox();"/>
<key key="&focusSearch2.key;" modifiers="accel" oncommand="gApplicationsPane.focusFilterBox();"/>
<key key="&focusSearch1.key;" modifiers="accel" id="focusSearch1"/>
<key key="&focusSearch2.key;" modifiers="accel" id="focusSearch2"/>
</keyset>
<hbox id="header-application"
@ -72,24 +72,21 @@
<textbox id="filter" flex="1"
type="search"
placeholder="&filter.emptytext;"
aria-controls="handlersView"
oncommand="gApplicationsPane.filter();"/>
aria-controls="handlersView"/>
</hbox>
<separator class="thin"/>
<richlistbox id="handlersView" orient="vertical" persist="lastSelectedType"
preference="pref.downloads.disable_button.edit_actions"
flex="1"
onselect="gApplicationsPane.onSelectionChanged();">
<listheader equalsize="always" style="border: 0; padding: 0; -moz-appearance: none;">
flex="1">
<listheader equalsize="always">
<treecol id="typeColumn" label="&typeColumn.label;" value="type"
accesskey="&typeColumn.accesskey;" persist="sortDirection"
flex="1" onclick="gApplicationsPane.sort(event);"
sortDirection="ascending"/>
flex="1" sortDirection="ascending"/>
<treecol id="actionColumn" label="&actionColumn2.label;" value="action"
accesskey="&actionColumn2.accesskey;" persist="sortDirection"
flex="1" onclick="gApplicationsPane.sort(event);"/>
flex="1"/>
</listheader>
</richlistbox>
</vbox>

View File

@ -9,6 +9,12 @@ var gContentPane = {
*/
init: function ()
{
function setEventListener(aId, aEventType, aCallback)
{
document.getElementById(aId)
.addEventListener(aEventType, aCallback.bind(gContentPane));
}
this._rebuildFonts();
var menulist = document.getElementById("defaultFont");
if (menulist.selectedIndex == -1) {
@ -22,6 +28,21 @@ var gContentPane = {
let row = document.getElementById("translationBox");
row.removeAttribute("hidden");
}
setEventListener("font.language.group", "change",
gContentPane._rebuildFonts);
setEventListener("popupPolicyButton", "command",
gContentPane.showPopupExceptions);
setEventListener("advancedFonts", "command",
gContentPane.configureFonts);
setEventListener("colors", "command",
gContentPane.configureColors);
setEventListener("chooseLanguage", "command",
gContentPane.showLanguages);
setEventListener("translationAttributionImage", "click",
gContentPane.openTranslationProviderAttribution);
setEventListener("translateButton", "command",
gContentPane.showTranslationExceptions);
},
// UTILITY FUNCTIONS

View File

@ -14,8 +14,7 @@
<!-- Fonts -->
<preference id="font.language.group"
name="font.language.group"
type="wstring"
onchange="gContentPane._rebuildFonts();"/>
type="wstring"/>
<!-- Languages -->
<preference id="browser.translation.detectLanguage"
@ -52,7 +51,6 @@
'dom.disable_open_during_load');"/>
</vbox>
<button id="popupPolicyButton" label="&popupExceptions.label;"
oncommand="gContentPane.showPopupExceptions();"
accesskey="&popupExceptions.accesskey;"/>
</row>
</rows>
@ -106,15 +104,13 @@
</hbox>
<button id="advancedFonts" icon="select-font"
label="&advancedFonts.label;"
accesskey="&advancedFonts.accesskey;"
oncommand="gContentPane.configureFonts();"/>
accesskey="&advancedFonts.accesskey;"/>
</row>
<row id="colorsRow">
<hbox/>
<button id="colors" icon="select-color"
label="&colors.label;"
accesskey="&colors.accesskey;"
oncommand="gContentPane.configureColors();"/>
accesskey="&colors.accesskey;"/>
</row>
</rows>
</grid>
@ -128,8 +124,7 @@
<description flex="1" control="chooseLanguage">&chooseLanguage.label;</description>
<button id="chooseLanguage"
label="&chooseButton.label;"
accesskey="&chooseButton.accesskey;"
oncommand="gContentPane.showLanguages();"/>
accesskey="&chooseButton.accesskey;"/>
</hbox>
<hbox id="translationBox" hidden="true">
@ -141,13 +136,11 @@
<label>&translation.options.attribution.beforeLogo;</label>
<separator orient="vertical" class="thin"/>
<image id="translationAttributionImage" aria-label="Microsoft Translator"
onclick="gContentPane.openTranslationProviderAttribution()"
src="chrome://browser/content/microsoft-translator-attribution.png"/>
<separator orient="vertical" class="thin"/>
<label>&translation.options.attribution.afterLogo;</label>
</hbox>
<button id="translateButton" label="&translateExceptions.label;"
oncommand="gContentPane.showTranslationExceptions();"
accesskey="&translateExceptions.accesskey;"/>
</hbox>
</groupbox>

View File

@ -11,6 +11,12 @@ var gMainPane = {
*/
init: function ()
{
function setEventListener(aId, aEventType, aCallback)
{
document.getElementById(aId)
.addEventListener(aEventType, aCallback.bind(gMainPane));
}
#ifdef HAVE_SHELL_SERVICE
this.updateSetDefaultBrowser();
#ifdef XP_WIN
@ -72,6 +78,22 @@ var gMainPane = {
#endif
setEventListener("browser.privatebrowsing.autostart", "change",
gMainPane.updateBrowserStartupLastSession);
setEventListener("browser.download.dir", "change",
gMainPane.displayDownloadDirPref);
#ifdef HAVE_SHELL_SERVICE
setEventListener("setDefaultButton", "command",
gMainPane.setDefaultBrowser);
#endif
setEventListener("useCurrent", "command",
gMainPane.setHomePageToCurrent);
setEventListener("useBookmark", "command",
gMainPane.setHomePageToBookmark);
setEventListener("restoreDefaultHomePage", "command",
gMainPane.restoreDefaultHomePage);
setEventListener("chooseFolder", "command",
gMainPane.chooseFolder);
},
// HOME PAGE

View File

@ -39,8 +39,7 @@
<preference id="browser.privatebrowsing.autostart"
name="browser.privatebrowsing.autostart"
type="bool"
onchange="gMainPane.updateBrowserStartupLastSession();"/>
type="bool"/>
<!-- Downloads -->
<preference id="browser.download.useDownloadDir"
@ -52,8 +51,7 @@
type="int"/>
<preference id="browser.download.dir"
name="browser.download.dir"
type="file"
onchange="gMainPane.displayDownloadDirPref();"/>
type="file"/>
<!-- Tab preferences
Preferences:
@ -121,7 +119,6 @@
<label id="isNotDefaultLabel" flex="1">&isNotDefault.label;</label>
<button id="setDefaultButton"
label="&setAsMyDefaultBrowser.label;" accesskey="&setAsMyDefaultBrowser.accesskey;"
oncommand="gMainPane.setDefaultBrowser();"
preference="pref.general.disable_button.default_browser"/>
</hbox>
<hbox align="center" class="indent">
@ -168,17 +165,14 @@
accesskey="&useCurrentPage.accesskey;"
label1="&useCurrentPage.label;"
label2="&useMultiple.label;"
oncommand="gMainPane.setHomePageToCurrent();"
id="useCurrent"
preference="pref.browser.homepage.disable_button.current_page"/>
<button label="&chooseBookmark.label;"
accesskey="&chooseBookmark.accesskey;"
oncommand="gMainPane.setHomePageToBookmark();"
id="useBookmark"
preference="pref.browser.homepage.disable_button.bookmark_page"/>
<button label="&restoreDefault.label;"
accesskey="&restoreDefault.accesskey;"
oncommand="gMainPane.restoreDefaultHomePage();"
id="restoreDefaultHomePage"
preference="pref.browser.homepage.disable_button.restore_default"/>
</hbox>
@ -207,7 +201,6 @@
onsyncfrompreference="return gMainPane.displayDownloadDirPref();"
onsynctopreference="return gMainPane.getFolderListPref()"/>
<button id="chooseFolder"
oncommand="gMainPane.chooseFolder();"
#ifdef XP_MACOSX
accesskey="&chooseFolderMac.accesskey;"
label="&chooseFolderMac.label;"

View File

@ -52,6 +52,9 @@ function init_all() {
window.addEventListener("hashchange", onHashChange);
gotoPref();
let helpCmd = document.getElementById("help-button");
helpCmd.addEventListener("command", helpButtonCommand);
// Wait until initialization of all preferences are complete before
// notifying observers that the UI is now ready.
Services.obs.notifyObservers(window, "advanced-pane-loaded", null);

View File

@ -172,9 +172,9 @@
#endif
</prefpane>
<hbox pack="end">
<button class="help-button"
aria-label="&helpButton.label;"
oncommand="helpButtonCommand();"/>
<button id="help-button"
class="help-button"
aria-label="&helpButton.label;"/>
</hbox>
</vbox>

View File

@ -20,11 +20,52 @@ var gPrivacyPane = {
*/
init: function ()
{
function setEventListener(aId, aEventType, aCallback)
{
document.getElementById(aId)
.addEventListener(aEventType, aCallback.bind(gPrivacyPane));
}
this._updateSanitizeSettingsButton();
this.initializeHistoryMode();
this.updateHistoryModePane();
this.updatePrivacyMicroControls();
this.initAutoStartPrivateBrowsingReverter();
setEventListener("browser.urlbar.default.behavior", "change",
document.getElementById('browser.urlbar.autocomplete.enabled')
.updateElements
);
setEventListener("privacy.sanitize.sanitizeOnShutdown", "change",
gPrivacyPane._updateSanitizeSettingsButton);
setEventListener("browser.privatebrowsing.autostart", "change",
gPrivacyPane.updatePrivacyMicroControls);
setEventListener("historyMode", "command", function () {
gPrivacyPane.updateHistoryModePane();
gPrivacyPane.updateHistoryModePrefs();
gPrivacyPane.updatePrivacyMicroControls();
gPrivacyPane.updateAutostart();
});
setEventListener("historyRememberClear", "click", function () {
gPrivacyPane.clearPrivateDataNow(false);
return false;
});
setEventListener("historyRememberCookies", "click", function () {
gPrivacyPane.showCookies();
return false;
});
setEventListener("historyDontRememberClear", "click", function () {
gPrivacyPane.clearPrivateDataNow(true);
return false;
});
setEventListener("privateBrowsingAutoStart", "command",
gPrivacyPane.updateAutostart);
setEventListener("cookieExceptions", "command",
gPrivacyPane.showCookieExceptions);
setEventListener("showCookiesButton", "command",
gPrivacyPane.showCookies);
setEventListener("clearDataSettings", "command",
gPrivacyPane.showClearPrivateDataSettings);
},
// HISTORY MODE

View File

@ -31,8 +31,7 @@
type="bool"/>
<preference id="browser.urlbar.default.behavior"
name="browser.urlbar.default.behavior"
type="int"
onchange="document.getElementById('browser.urlbar.autocomplete.enabled').updateElements();"/>
type="int"/>
<!-- History -->
<preference id="places.history.enabled"
@ -54,7 +53,6 @@
<!-- Clear Private Data -->
<preference id="privacy.sanitize.sanitizeOnShutdown"
name="privacy.sanitize.sanitizeOnShutdown"
onchange="gPrivacyPane._updateSanitizeSettingsButton();"
type="bool"/>
<preference id="privacy.sanitize.timeSpan"
name="privacy.sanitize.timeSpan"
@ -62,7 +60,6 @@
<!-- Private Browsing -->
<preference id="browser.privatebrowsing.autostart"
name="browser.privatebrowsing.autostart"
onchange="gPrivacyPane.updatePrivacyMicroControls();"
type="bool"/>
</preferences>
@ -102,11 +99,7 @@
control="historyMode"
accesskey="&historyHeader.pre.accesskey;">&historyHeader.pre.label;
</label>
<menulist id="historyMode"
oncommand="gPrivacyPane.updateHistoryModePane();
gPrivacyPane.updateHistoryModePrefs();
gPrivacyPane.updatePrivacyMicroControls();
gPrivacyPane.updateAutostart();">
<menulist id="historyMode">
<menupopup>
<menuitem label="&historyHeader.remember.label;" value="remember"/>
<menuitem label="&historyHeader.dontremember.label;" value="dontremember"/>
@ -123,11 +116,9 @@
<description>&rememberDescription.label;</description>
<separator/>
<description>&rememberActions.pre.label;<html:a
class="inline-link" href="#"
onclick="gPrivacyPane.clearPrivateDataNow(false); return false;"
class="inline-link" id="historyRememberClear" href="#"
>&rememberActions.clearHistory.label;</html:a>&rememberActions.middle.label;<html:a
class="inline-link" href="#"
onclick="gPrivacyPane.showCookies(); return false;"
class="inline-link" id="historyRememberCookies" href="#"
>&rememberActions.removeCookies.label;</html:a>&rememberActions.post.label;</description>
</vbox>
<spacer flex="1" class="indent"/>
@ -140,8 +131,7 @@
<description>&dontrememberDescription.label;</description>
<separator/>
<description>&dontrememberActions.pre.label;<html:a
class="inline-link" href="#"
onclick="gPrivacyPane.clearPrivateDataNow(true); return false;"
class="inline-link" id="historyDontRememberClear" href="#"
>&dontrememberActions.clearHistory.label;</html:a>&dontrememberActions.post.label;</description>
</vbox>
<spacer flex="1" class="indent"/>
@ -154,8 +144,7 @@
<checkbox id="privateBrowsingAutoStart"
label="&privateBrowsingPermanent2.label;"
accesskey="&privateBrowsingPermanent2.accesskey;"
preference="browser.privatebrowsing.autostart"
oncommand="gPrivacyPane.updateAutostart()"/>
preference="browser.privatebrowsing.autostart"/>
</vbox>
<vbox class="indent">
<vbox align="start">
@ -175,7 +164,7 @@
onsyncfrompreference="return gPrivacyPane.readAcceptCookies();"
onsynctopreference="return gPrivacyPane.writeAcceptCookies();"/>
<spacer flex="1" />
<button id="cookieExceptions" oncommand="gPrivacyPane.showCookieExceptions();"
<button id="cookieExceptions"
label="&cookieExceptions.label;" accesskey="&cookieExceptions.accesskey;"
preference="pref.privacy.disable_button.cookie_exceptions"/>
</hbox>
@ -211,7 +200,6 @@
<spacer flex="1"/>
<button id="showCookiesButton"
label="&showCookies.label;" accesskey="&showCookies.accesskey;"
oncommand="gPrivacyPane.showCookies();"
preference="pref.privacy.disable_button.view_cookies"/>
</hbox>
<hbox id="clearDataBox"
@ -222,8 +210,7 @@
accesskey="&clearOnClose.accesskey;"/>
<spacer flex="1"/>
<button id="clearDataSettings" label="&clearOnCloseSettings.label;"
accesskey="&clearOnCloseSettings.accesskey;"
oncommand="gPrivacyPane.showClearPrivateDataSettings();"/>
accesskey="&clearOnCloseSettings.accesskey;"/>
</hbox>
</vbox>
</vbox>

View File

@ -12,8 +12,25 @@ var gSecurityPane = {
*/
init: function ()
{
function setEventListener(aId, aEventType, aCallback)
{
document.getElementById(aId)
.addEventListener(aEventType, aCallback.bind(gSecurityPane));
}
this._pane = document.getElementById("paneSecurity");
this._initMasterPasswordUI();
setEventListener("addonExceptions", "command",
gSecurityPane.showAddonExceptions);
setEventListener("passwordExceptions", "command",
gSecurityPane.showPasswordExceptions);
setEventListener("useMasterPassword", "command",
gSecurityPane.updateMasterPasswordButton);
setEventListener("changeMasterPassword", "command",
gSecurityPane.changeMasterPassword);
setEventListener("showPasswords", "command",
gSecurityPane.showPasswords);
},
// ADD-ONS

View File

@ -52,8 +52,7 @@
<spacer flex="1"/>
<button id="addonExceptions"
label="&addonExceptions.label;"
accesskey="&addonExceptions.accesskey;"
oncommand="gSecurityPane.showAddonExceptions();"/>
accesskey="&addonExceptions.accesskey;"/>
</hbox>
<separator class="thin"/>
@ -82,26 +81,22 @@
<button id="passwordExceptions"
label="&passwordExceptions.label;"
accesskey="&passwordExceptions.accesskey;"
oncommand="gSecurityPane.showPasswordExceptions();"
preference="pref.privacy.disable_button.view_passwords_exceptions"/>
</hbox>
<hbox id="masterPasswordBox">
<checkbox id="useMasterPassword"
oncommand="gSecurityPane.updateMasterPasswordButton();"
label="&useMasterPassword.label;"
accesskey="&useMasterPassword.accesskey;"/>
<spacer flex="1"/>
<button id="changeMasterPassword"
label="&changeMasterPassword.label;"
accesskey="&changeMasterPassword.accesskey;"
oncommand="gSecurityPane.changeMasterPassword();"/>
accesskey="&changeMasterPassword.accesskey;"/>
</hbox>
<hbox id="showPasswordsBox">
<spacer flex="1"/>
<button id="showPasswords"
label="&savedPasswords.label;" accesskey="&savedPasswords.accesskey;"
oncommand="gSecurityPane.showPasswords();"
preference="pref.privacy.disable_button.view_passwords"/>
</hbox>
</groupbox>

View File

@ -49,6 +49,8 @@ let gSyncPane = {
},
init: function () {
this._setupEventListeners();
// If the Service hasn't finished initializing, wait for it.
let xps = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
@ -107,6 +109,91 @@ let gSyncPane = {
this.updateWeavePrefs();
},
_setupEventListeners: function() {
function setEventListener(aId, aEventType, aCallback)
{
document.getElementById(aId)
.addEventListener(aEventType, aCallback.bind(gSyncPane));
}
setEventListener("noAccountSetup", "click", function (aEvent) {
aEvent.stopPropagation();
gSyncPane.openSetup(null);
});
setEventListener("noAccountPair", "click", function (aEvent) {
aEvent.stopPropagation();
gSyncPane.openSetup('pair');
});
setEventListener("syncViewQuota", "command", gSyncPane.openQuotaDialog);
setEventListener("syncChangePassword", "command",
gSyncUtils.changePassword);
setEventListener("syncResetPassphrase", "command",
gSyncUtils.resetPassphrase);
setEventListener("syncReset", "command", gSyncPane.resetSync);
setEventListener("syncAddDeviceLabel", "click", function () {
gSyncPane.openAddDevice();
return false;
});
setEventListener("syncEnginesList", "select", function () {
if (this.selectedCount)
this.clearSelection();
});
setEventListener("syncComputerName", "change", function () {
gSyncUtils.changeName(this);
});
setEventListener("unlinkDevice", "click", function () {
gSyncPane.startOver(true);
return false;
});
setEventListener("tosPP-normal-ToS", "click", gSyncPane.openToS);
setEventListener("tosPP-normal-PP", "click", gSyncPane.openPrivacyPolicy);
setEventListener("loginErrorUpdatePass", "click", function () {
gSyncPane.updatePass();
return false;
});
setEventListener("loginErrorResetPass", "click", function () {
gSyncPane.resetPass();
return false;
});
setEventListener("loginErrorStartOver", "click", function () {
gSyncPane.startOver(true);
return false;
});
setEventListener("noFxaSignUp", "click", function () {
gSyncPane.signUp();
return false;
});
setEventListener("noFxaSignIn", "click", function () {
gSyncPane.signIn();
return false;
});
setEventListener("noFxaUseOldSync", "click", function () {
gSyncPane.openOldSyncSupportPage();
return false;
});
setEventListener("verifiedManage", "command",
gSyncPane.manageFirefoxAccount);
setEventListener("fxaUnlinkButton", "click", function () {
gSyncPane.unlinkFirefoxAccount(true);
});
setEventListener("verifyFxaAccount", "command",
gSyncPane.verifyFirefoxAccount);
setEventListener("unverifiedUnlinkFxaAccount", "click", function () {
/* no warning as account can't have previously synced */
gSyncPane.unlinkFirefoxAccount(false);
});
setEventListener("rejectReSignIn", "command",
gSyncPane.reSignIn);
setEventListener("rejectUnlinkFxaAccount", "click", function () {
gSyncPane.unlinkFirefoxAccount(true);
});
setEventListener("fxaSyncComputerName", "change", function () {
gSyncUtils.changeName(this);
});
setEventListener("tosPP-small-ToS", "click", gSyncPane.openToS);
setEventListener("tosPP-small-PP", "click", gSyncPane.openPrivacyPolicy);
},
updateWeavePrefs: function () {
let service = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
@ -171,7 +258,7 @@ let gSyncPane = {
this.page = PAGE_HAS_ACCOUNT;
document.getElementById("accountName").textContent = Weave.Service.identity.account;
document.getElementById("syncComputerName").value = Weave.Service.clientsEngine.localName;
document.getElementById("tosPP").hidden = this._usingCustomServer;
document.getElementById("tosPP-normal").hidden = this._usingCustomServer;
}
},
@ -251,6 +338,17 @@ let gSyncPane = {
win.openUILinkIn(url, "tab");
},
openPrivacyPolicy: function(aEvent) {
aEvent.stopPropagation();
gSyncUtils.openPrivacyPolicy();
},
openToS: function(aEvent) {
aEvent.stopPropagation();
gSyncUtils.openToS();
},
signUp: function() {
this.openContentInBrowser("about:accounts?action=signup");
},

View File

@ -45,14 +45,12 @@
&weaveDesc.label;
</description>
<separator/>
<label class="text-link"
onclick="event.stopPropagation(); gSyncPane.openSetup(null);">
<label id="noAccountSetup" class="text-link">
&setupButton.label;
</label>
<vbox id="pairDevice">
<separator/>
<label class="text-link"
onclick="event.stopPropagation(); gSyncPane.openSetup('pair');">
<label id="noAccountPair" class="text-link">
&pairDevice.label;
</label>
</vbox>
@ -72,24 +70,19 @@
label="&manageAccount.label;"
accesskey="&manageAccount.accesskey;">
<menupopup>
<menuitem label="&viewQuota.label;"
oncommand="gSyncPane.openQuotaDialog();"/>
<menuitem id="syncViewQuota" label="&viewQuota.label;"/>
<menuseparator/>
<menuitem label="&changePassword2.label;"
oncommand="gSyncUtils.changePassword();"/>
<menuitem label="&myRecoveryKey.label;"
oncommand="gSyncUtils.resetPassphrase();"/>
<menuitem id="syncChangePassword" label="&changePassword2.label;"/>
<menuitem id="syncResetPassphrase" label="&myRecoveryKey.label;"/>
<menuseparator/>
<menuitem label="&resetSync2.label;"
oncommand="gSyncPane.resetSync();"/>
<menuitem id="syncReset" label="&resetSync2.label;"/>
</menupopup>
</button>
</hbox>
<hbox>
<label id="syncAddDeviceLabel"
class="text-link"
onclick="gSyncPane.openAddDevice(); return false;">
class="text-link">
&pairDevice.label;
</label>
</hbox>
@ -97,8 +90,7 @@
<vbox>
<label>&syncMy.label;</label>
<richlistbox id="syncEnginesList"
orient="vertical"
onselect="if (this.selectedCount) this.clearSelection();">
orient="vertical">
<richlistitem>
<checkbox label="&engine.addons.label;"
accesskey="&engine.addons.accesskey;"
@ -145,25 +137,21 @@
control="syncComputerName">
&syncDeviceName.label;
</label>
<textbox id="syncComputerName"
onchange="gSyncUtils.changeName(this)"/>
<textbox id="syncComputerName"/>
</row>
</rows>
</grid>
<hbox>
<label class="text-link"
onclick="gSyncPane.startOver(true); return false;">
<label id="unlinkDevice" class="text-link">
&unlinkDevice.label;
</label>
</hbox>
</groupbox>
<hbox id="tosPP" pack="center">
<label class="text-link"
onclick="event.stopPropagation();gSyncUtils.openToS();">
<hbox id="tosPP-normal" pack="center">
<label id="tosPP-normal-ToS" class="text-link">
&prefs.tosLink.label;
</label>
<label class="text-link"
onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();">
<label id="tosPP-normal-PP" class="text-link">
&prefs.ppLink.label;
</label>
</hbox>
@ -172,17 +160,14 @@
<vbox id="needsUpdate" align="center" pack="center">
<hbox>
<label id="loginError"/>
<label class="text-link"
onclick="gSyncPane.updatePass(); return false;">
<label id="loginErrorUpdatePass" class="text-link">
&updatePass.label;
</label>
<label class="text-link"
onclick="gSyncPane.resetPass(); return false;">
<label id="loginErrorResetPass" class="text-link">
&resetPass.label;
</label>
</hbox>
<label class="text-link"
onclick="gSyncPane.startOver(true); return false;">
<label id="loginErrorStartOver" class="text-link">
&unlinkDevice.label;
</label>
</vbox>
@ -196,17 +181,14 @@
<vbox id="noFxaAccount" align="start">
<label>&welcome.description;</label>
<label class="text-link"
onclick="gSyncPane.signUp(); return false;">
<label id="noFxaSignUp" class="text-link">
&welcome.createAccount.label;
</label>
<label class="text-link"
onclick="gSyncPane.signIn(); return false;">
<label id="noFxaSignIn" class="text-link">
&welcome.signIn.label;
</label>
<separator/>
<label class="text-link"
onclick="gSyncPane.openOldSyncSupportPage(); return false;">
<label id="noFxaUseOldSync" class="text-link">
&welcome.useOldSync.label;
</label>
</vbox>
@ -221,10 +203,9 @@
<hbox align="center">
<label id="fxaEmailAddress1"/>
<spacer flex="1"/>
<button oncommand="gSyncPane.manageFirefoxAccount();"
<button id="verifiedManage"
label="&manage.label;"/>
<button id="fxaUnlinkButton"
oncommand="gSyncPane.unlinkFirefoxAccount(true);"
label="&disconnect.label;"/>
</hbox>
@ -237,10 +218,9 @@
</description>
<spacer flex="1"/>
<vbox align="end">
<button oncommand="gSyncPane.verifyFirefoxAccount();"
<button id="verifyFxaAccount"
label="&verify.label;"/>
<label class="text-link"
onclick="/* no warning as account can't have previously synced */ gSyncPane.unlinkFirefoxAccount(false);">
<label id="unverifiedUnlinkFxaAccount" class="text-link">
&forget.label;
</label>
</vbox>
@ -255,10 +235,9 @@
</description>
<spacer flex="1"/>
<vbox align="end">
<button oncommand="gSyncPane.reSignIn();"
<button id="rejectReSignIn"
label="&signIn.label;"/>
<label class="text-link"
onclick="gSyncPane.unlinkFirefoxAccount(true);">
<label id="rejectUnlinkFxaAccount" class="text-link">
&forget.label;
</label>
</vbox>
@ -298,17 +277,14 @@
&syncDeviceName.label;
</label>
<textbox id="fxaSyncComputerName"
flex="1"
onchange="gSyncUtils.changeName(this)"/>
flex="1"/>
</hbox>
<spacer flex="1"/>
<hbox id="tosPP" pack="center">
<label class="text-link small"
onclick="event.stopPropagation();gSyncUtils.openToS();">
<hbox id="tosPP-small" pack="center">
<label id="tosPP-small-ToS" class="text-link small">
&prefs.tosLink.label;
</label>
<label class="text-link small"
onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();">
<label id="tosPP-small-PP" class="text-link small">
&fxaPrivacyNotice.link.label;
</label>
</hbox>

View File

@ -171,7 +171,7 @@ let gSyncPane = {
this.page = PAGE_HAS_ACCOUNT;
document.getElementById("accountName").value = Weave.Service.identity.account;
document.getElementById("syncComputerName").value = Weave.Service.clientsEngine.localName;
document.getElementById("tosPP").hidden = this._usingCustomServer;
document.getElementById("tosPP-normal").hidden = this._usingCustomServer;
}
},

View File

@ -153,7 +153,7 @@
value="&unlinkDevice.label;"/>
</hbox>
</groupbox>
<hbox id="tosPP" pack="center">
<hbox id="tosPP-normal" pack="center">
<label class="text-link"
onclick="event.stopPropagation();gSyncUtils.openToS();"
value="&prefs.tosLink.label;"/>
@ -290,7 +290,7 @@
onchange="gSyncUtils.changeName(this)"/>
</hbox>
<spacer flex="1"/>
<hbox id="tosPP" pack="center">
<hbox id="tosPP-small" pack="center">
<label class="text-link small"
onclick="event.stopPropagation();gSyncUtils.openToS();"
value="&prefs.tosLink.label;"/>

View File

@ -1,4 +1,5 @@
[DEFAULT]
skip-if = e10s # Bug 1058879 - canvas debugger tests disabled with e10s
subsuite = devtools
support-files =
doc_simple-canvas.html

View File

@ -37,7 +37,9 @@ function test() {
// Check no toolbox buttons are shown
let buttons = addonDebugger.frame.contentDocument.getElementById("toolbox-buttons").children;
is(buttons.length, 0, "no toolbox buttons for the addon debugger");
Array.forEach(buttons, (btn, i) => {
is(btn.hidden, true, "no toolbox buttons for the addon debugger -- " + btn.className);
});
yield addonDebugger.destroy();
yield removeAddon(addon);

View File

@ -374,6 +374,7 @@ TabTarget.prototype = {
event.url = aPacket.url;
event.title = aPacket.title;
event.nativeConsoleAPI = aPacket.nativeConsoleAPI;
event.isFrameSwitching = aPacket.isFrameSwitching;
// Send any stored event payload (DOMWindow or nsIRequest) for backwards
// compatibility with non-remotable tools.
if (aPacket.state == "start") {
@ -387,6 +388,11 @@ TabTarget.prototype = {
}
};
this.client.addListener("tabNavigated", this._onTabNavigated);
this._onFrameUpdate = (aType, aPacket) => {
this.emit("frame-update", aPacket);
};
this.client.addListener("frameUpdate", this._onFrameUpdate);
},
/**
@ -396,6 +402,7 @@ TabTarget.prototype = {
this.client.removeListener("closed", this.destroy);
this.client.removeListener("tabNavigated", this._onTabNavigated);
this.client.removeListener("tabDetached", this._onTabDetached);
this.client.removeListener("frameUpdate", this._onFrameUpdate);
},
/**

View File

@ -13,6 +13,7 @@ support-files =
[browser_new_activation_workflow.js]
[browser_target_events.js]
[browser_target_remote.js]
[browser_two_tabs.js]
[browser_toolbox_dynamic_registration.js]
[browser_toolbox_highlight.js]
[browser_toolbox_hosts.js]

View File

@ -57,12 +57,13 @@ function testSelectTool(aToolbox) {
function testPreferenceAndUIStateIsConsistent() {
let checkNodes = [...panelWin.document.querySelectorAll("#enabled-toolbox-buttons-box > checkbox")];
let toolboxButtonNodes = [...doc.querySelectorAll(".command-button")];
toolboxButtonNodes.push(doc.getElementById("command-button-frames"));
let toggleableTools = toolbox.toolboxButtons;
for (let tool of toggleableTools) {
let isVisible = getBoolPref(tool.visibilityswitch);
let button = toolboxButtonNodes.filter(button=>button.id === tool.id)[0];
let button = toolboxButtonNodes.filter(button => button.id === tool.id)[0];
is (!button.hasAttribute("hidden"), isVisible, "Button visibility matches pref for " + tool.id);
let check = checkNodes.filter(node=>node.id === tool.id)[0];

View File

@ -0,0 +1,101 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Check regression when opening two tabs
*/
let { DebuggerServer } =
Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
let { DebuggerClient } =
Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
let { devtools } =
Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const TAB_URL_1 = "data:text/html;charset=utf-8,foo";
const TAB_URL_2 = "data:text/html;charset=utf-8,bar";
let gClient;
let gTab1, gTab2;
let gTabActor1, gTabActor2;
function test() {
waitForExplicitFinish();
if (!DebuggerServer.initialized) {
DebuggerServer.init(() => true);
DebuggerServer.addBrowserActors();
}
openTabs();
}
function openTabs() {
// Open two tabs, select the second
gTab1 = gBrowser.addTab(TAB_URL_1);
gTab1.linkedBrowser.addEventListener("load", function onLoad1(evt) {
gTab1.linkedBrowser.removeEventListener("load", onLoad1);
gTab2 = gBrowser.selectedTab = gBrowser.addTab(TAB_URL_2);
gTab2.linkedBrowser.addEventListener("load", function onLoad2(evt) {
gTab2.linkedBrowser.removeEventListener("load", onLoad2);
connect();
}, true);
}, true);
}
function connect() {
// Connect to debugger server to fetch the two tab actors
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(() => {
gClient.listTabs(response => {
// Fetch the tab actors for each tab
gTabActor1 = response.tabs.filter(a => a.url === TAB_URL_1)[0];
gTabActor2 = response.tabs.filter(a => a.url === TAB_URL_2)[0];
checkSelectedTabActor();
});
});
}
function checkSelectedTabActor() {
// Send a naive request to the second tab actor
// to check if it works
gClient.request({ to: gTabActor2.consoleActor, type: "startListeners", listeners: [] }, aResponse => {
ok("startedListeners" in aResponse, "Actor from the selected tab should respond to the request.");
closeSecondTab();
});
}
function closeSecondTab() {
// Close the second tab, currently selected
let container = gBrowser.tabContainer;
container.addEventListener("TabClose", function onTabClose() {
container.removeEventListener("TabClose", onTabClose);
checkFirstTabActor();
});
gBrowser.removeTab(gTab2);
}
function checkFirstTabActor() {
// then send a request to the first tab actor
// to check if it still works
gClient.request({ to: gTabActor1.consoleActor, type: "startListeners", listeners: [] }, aResponse => {
ok("startedListeners" in aResponse, "Actor from the first tab should still respond.");
cleanup();
});
}
function cleanup() {
let container = gBrowser.tabContainer;
container.addEventListener("TabClose", function onTabClose() {
container.removeEventListener("TabClose", onTabClose);
gClient.close(finish);
});
gBrowser.removeTab(gTab1);
}

View File

@ -47,6 +47,26 @@ loader.lazyGetter(this, "toolboxStrings", () => {
loader.lazyGetter(this, "Selection", () => require("devtools/framework/selection").Selection);
loader.lazyGetter(this, "InspectorFront", () => require("devtools/server/actors/inspector").InspectorFront);
// White-list buttons that can be toggled to prevent adding prefs for
// addons that have manually inserted toolbarbuttons into DOM.
// (By default, supported target is only local tab)
const ToolboxButtons = [
{ id: "command-button-pick",
isTargetSupported: target => !target.isAddon },
{ id: "command-button-frames",
isTargetSupported: target => (
!target.isAddon && target.activeTab && target.activeTab.traits.frames
)
},
{ id: "command-button-splitconsole" },
{ id: "command-button-responsive" },
{ id: "command-button-paintflashing" },
{ id: "command-button-tilt" },
{ id: "command-button-scratchpad" },
{ id: "command-button-eyedropper" },
{ id: "command-button-screenshot" }
];
/**
* A "Toolbox" is the component that holds all the tools for one specific
* target. Visually, it's a document that includes the tools tabs and all
@ -69,6 +89,8 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
this._toolRegistered = this._toolRegistered.bind(this);
this._toolUnregistered = this._toolUnregistered.bind(this);
this._refreshHostTitle = this._refreshHostTitle.bind(this);
this.selectFrame = this.selectFrame.bind(this);
this._updateFrames = this._updateFrames.bind(this);
this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this);
this.destroy = this.destroy.bind(this);
this.highlighterUtils = getHighlighterUtils(this);
@ -96,6 +118,8 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
EventEmitter.decorate(this);
this._target.on("navigate", this._refreshHostTitle);
this._target.on("frame-update", this._updateFrames);
this.on("host-changed", this._refreshHostTitle);
this.on("select", this._refreshHostTitle);
@ -243,11 +267,16 @@ Toolbox.prototype = {
let domReady = () => {
this.isReady = true;
this._listFrames();
this.closeButton = this.doc.getElementById("toolbox-close");
this.closeButton.addEventListener("command", this.destroy, true);
gDevTools.on("pref-changed", this._prefChanged);
let framesMenu = this.doc.getElementById("command-button-frames");
framesMenu.addEventListener("command", this.selectFrame, true);
this._buildDockButtons();
this._buildOptions();
this._buildTabs();
@ -591,6 +620,7 @@ Toolbox.prototype = {
}
if (!this.target.isLocalTab) {
this.setToolboxButtonsVisibility();
return Promise.resolve();
}
@ -616,6 +646,7 @@ Toolbox.prototype = {
this._pickerButton.id = "command-button-pick";
this._pickerButton.className = "command-button command-button-invertable";
this._pickerButton.setAttribute("tooltiptext", toolboxStrings("pickButton.tooltip"));
this._pickerButton.setAttribute("hidden", "true");
let container = this.doc.querySelector("#toolbox-picker-container");
container.appendChild(this._pickerButton);
@ -654,28 +685,19 @@ Toolbox.prototype = {
* added manually).
*/
get toolboxButtons() {
// White-list buttons that can be toggled to prevent adding prefs for
// addons that have manually inserted toolbarbuttons into DOM.
return [
"command-button-pick",
"command-button-splitconsole",
"command-button-responsive",
"command-button-paintflashing",
"command-button-tilt",
"command-button-scratchpad",
"command-button-eyedropper",
"command-button-screenshot"
].map(id => {
let button = this.doc.getElementById(id);
return ToolboxButtons.map(options => {
let button = this.doc.getElementById(options.id);
// Some buttons may not exist inside of Browser Toolbox
if (!button) {
return false;
}
return {
id: id,
id: options.id,
button: button,
label: button.getAttribute("tooltiptext"),
visibilityswitch: "devtools." + id + ".enabled"
visibilityswitch: "devtools." + options.id + ".enabled",
isTargetSupported: options.isTargetSupported ? options.isTargetSupported
: target => target.isLocalTab
}
}).filter(button=>button);
},
@ -686,12 +708,14 @@ Toolbox.prototype = {
*/
setToolboxButtonsVisibility: function() {
this.toolboxButtons.forEach(buttonSpec => {
let {visibilityswitch, id, button}=buttonSpec;
let { visibilityswitch, id, button, isTargetSupported } = buttonSpec;
let on = true;
try {
on = Services.prefs.getBoolPref(visibilityswitch);
} catch (ex) { }
on = on && isTargetSupported(this.target);
if (button) {
if (on) {
button.removeAttribute("hidden");
@ -1134,6 +1158,96 @@ Toolbox.prototype = {
this._host.setTitle(title);
},
_listFrames: function (event) {
if (!this._target.form || !this._target.form.actor) {
// We are not targetting a regular TabActor
// it can be either an addon or browser toolbox actor
return;
}
let packet = {
to: this._target.form.actor,
type: "listFrames"
};
this._target.client.request(packet, resp => {
this._updateFrames(null, { frames: resp.frames });
});
},
selectFrame: function (event) {
let windowId = event.target.getAttribute("data-window-id");
let packet = {
to: this._target.form.actor,
type: "switchToFrame",
windowId: windowId
};
this._target.client.request(packet);
// Wait for frameUpdate event to update the UI
},
_updateFrames: function (event, data) {
if (!Services.prefs.getBoolPref("devtools.command-button-frames.enabled")) {
return;
}
// We may receive this event before the toolbox is ready.
if (!this.isReady) {
return;
}
let menu = this.doc.getElementById("command-button-frames");
if (data.destroyAll) {
let menupopup = menu.firstChild;
while (menupopup.firstChild) {
menupopup.firstChild.remove();
}
return;
} else if (data.selected) {
let item = menu.querySelector("menuitem[data-window-id=\"" + data.selected + "\"]");
if (!item) {
return;
}
// Toggle the toolbarbutton if we selected a non top-level frame
if (item.hasAttribute("data-parent-id")) {
menu.setAttribute("checked", "true");
} else {
menu.removeAttribute("checked");
}
// Uncheck the previously selected frame
let selected = menu.querySelector("menuitem[checked=true]")
if (selected) {
selected.removeAttribute("checked");
}
// Check the new one
item.setAttribute("checked", "true");
} else if (data.frames) {
data.frames.forEach(win => {
let item = menu.querySelector("menuitem[data-window-id=\"" + win.id + "\"]");
if (win.destroy) {
if (item) {
item.remove();
}
return;
}
if (!item) {
item = this.doc.createElement("menuitem");
item.setAttribute("data-window-id", win.id);
if (win.parentID) {
item.setAttribute("data-parent-id", win.parentID);
}
// If we register a root docshell and we don't have any selected,
// consider it as the currently targeted one.
if (!win.parentID && !menu.querySelector("menuitem[checked=true]")) {
item.setAttribute("checked", "true");
menu.removeAttribute("checked");
}
menu.firstChild.appendChild(item);
}
item.setAttribute("label", win.url);
});
}
},
/**
* Create a host object based on the given host type.
*
@ -1355,6 +1469,7 @@ Toolbox.prototype = {
}
this._target.off("navigate", this._refreshHostTitle);
this._target.off("frame-update", this._updateFrames);
this.off("select", this._refreshHostTitle);
this.off("host-changed", this._refreshHostTitle);
@ -1364,9 +1479,11 @@ Toolbox.prototype = {
gDevTools.off("pref-changed", this._prefChanged);
this._lastFocusedElement = null;
this._saveSplitConsoleHeight();
this.webconsolePanel.removeEventListener("resize",
this._saveSplitConsoleHeight);
if (this.webconsolePanel) {
this._saveSplitConsoleHeight();
this.webconsolePanel.removeEventListener("resize",
this._saveSplitConsoleHeight);
}
this.closeButton.removeEventListener("command", this.destroy, true);
let outstanding = [];

View File

@ -69,7 +69,15 @@
<toolbar class="devtools-tabbar">
<hbox id="toolbox-picker-container" />
<hbox id="toolbox-tabs" flex="1" role="tablist" />
<hbox id="toolbox-buttons" pack="end"/>
<hbox id="toolbox-buttons" pack="end">
<toolbarbutton id="command-button-frames"
class="command-button command-button-invertable devtools-toolbarbutton"
tooltiptext="&toolboxFramesTooltip;"
type="menu"
hidden="true">
<menupopup position="bottomright topright"></menupopup>
</toolbarbutton>
</hbox>
<vbox id="toolbox-controls-separator" class="devtools-separator"/>
<hbox id="toolbox-option-container"/>
<hbox id="toolbox-controls">

View File

@ -219,7 +219,9 @@ InspectorPanel.prototype = {
}
rootNode = aRootNode;
return walker.querySelector(rootNode, this.selectionCssSelector);
if (this.selectionCssSelector) {
return walker.querySelector(rootNode, this.selectionCssSelector);
}
}).then(front => {
if (hasNavigated()) {
return promise.reject("navigated; resolution of _defaultNode aborted");

View File

@ -51,8 +51,10 @@ support-files =
[browser_inspector_search-01.js]
[browser_inspector_search-02.js]
[browser_inspector_search-03.js]
[browser_inspector_select-docshell.js]
[browser_inspector_select-last-selected.js]
[browser_inspector_search-navigation.js]
[browser_inspector_sidebarstate.js]
[browser_inspector_switch-to-inspector-on-pick.js]
[browser_inspector_update-on-navigation.js]

View File

@ -0,0 +1,81 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Test frame selection switching at toolbox level
// when using the inspector
let test = asyncTest(function*() {
const FrameURL = "data:text/html;charset=UTF-8," + encodeURI("<div id=\"frame\">frame</div>");
const URL = "data:text/html;charset=UTF-8," + encodeURI("<iframe src=\"" + FrameURL + "\"></iframe><div id=\"top\">top</div>");
Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
let {toolbox, inspector} = yield openInspectorForURL(URL);
// Verify we are on the top level document
let testNode = content.document.querySelector("#top");
ok(testNode, "We have the test node on the top level document");
assertMarkupViewIsLoaded();
// Verify that the frame list button is visible and populated
let btn = toolbox.doc.getElementById("command-button-frames");
ok(!btn.firstChild.getAttribute("hidden"), "The frame list button is visible");
let frameBtns = Array.slice(btn.firstChild.querySelectorAll("[data-window-id]"));
is(frameBtns.length, 2, "We have both frames in the list");
frameBtns.sort(function (a, b) {
return a.getAttribute("label").localeCompare(b.getAttribute("label"));
});
is(frameBtns[0].getAttribute("label"), FrameURL, "Got top level document in the list");
is(frameBtns[1].getAttribute("label"), URL, "Got iframe document in the list");
// Listen to will-navigate to check if the view is empty
let willNavigate = toolbox.target.on("will-navigate", () => {
info("Navigation to the iframe has started, the inspector should be empty");
assertMarkupViewIsEmpty();
});
let newRoot = inspector.once("new-root", () => {
info("Navigation to the iframe is done, the inspector should be back up");
// Verify we are on page one
//let testNode = content.frames[0].document.querySelector("#frame");
let testNode = getNode("#frame", { document: content.frames[0].document});
ok(testNode, "We have the test node on the iframe");
// On page 2 load, verify we have the right content
assertMarkupViewIsLoaded();
inspector.once("inspector-updated", () => {
deferred.resolve();
});
selectNode(testNode, inspector);
});
// select the iframe once we were able to select an element from the
// top level document
selectNode("#top", inspector);
inspector.once("inspector-updated", () => {
// Select the iframe
frameBtns[0].click();
});
yield willNavigate;
yield newRoot;
// gBrowser.removeCurrentTab();
Services.prefs.clearUserPref("devtools.command-button-frames.enabled");
function assertMarkupViewIsLoaded() {
let markupViewBox = inspector.panelDoc.getElementById("markup-box");
is(markupViewBox.childNodes.length, 1, "The markup-view is loaded");
}
function assertMarkupViewIsEmpty() {
let markupViewBox = inspector.panelDoc.getElementById("markup-box");
is(markupViewBox.childNodes.length, 0, "The markup-view is unloaded");
}
});

View File

@ -3,6 +3,7 @@ subsuite = devtools
support-files =
doc_markup_edit.html
doc_markup_events.html
doc_markup_events-overflow.html
doc_markup_flashing.html
doc_markup_mutation.html
doc_markup_navigation.html
@ -20,6 +21,8 @@ support-files =
[browser_markupview_css_completion_style_attribute.js]
[browser_markupview_events.js]
skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
[browser_markupview_events-overflow.js]
skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
[browser_markupview_highlight_hover_01.js]
skip-if = e10s # Bug 985597 - The XUL-based highlighter isn't e10s compatible
[browser_markupview_highlight_hover_02.js]

View File

@ -0,0 +1,87 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_URL = TEST_URL_ROOT + "doc_markup_events-overflow.html";
const TEST_DATA = [
{
desc: "editor overflows container",
initialScrollTop: -1, // scroll to bottom
headerToClick: 49, // last header
alignBottom: true,
alignTop: false,
},
{
desc: "header overflows the container",
initialScrollTop: 2,
headerToClick: 0,
alignBottom: false,
alignTop: true,
},
{
desc: "neither header nor editor overflows the container",
initialScrollTop: 2,
headerToClick: 5,
alignBottom: false,
alignTop: false,
},
];
let test = asyncTest(function*() {
let { inspector } = yield addTab(TEST_URL).then(openInspector);
let markupContainer = yield getContainerForSelector("#events", inspector);
let evHolder = markupContainer.elt.querySelector(".markupview-events");
let tooltip = inspector.markup.tooltip;
info("Clicking to open event tooltip.");
EventUtils.synthesizeMouseAtCenter(evHolder, {}, inspector.markup.doc.defaultView);
yield tooltip.once("shown");
info("EventTooltip visible.");
let container = tooltip.content;
let containerRect = container.getBoundingClientRect();
let headers = container.querySelectorAll(".event-header");
for (let data of TEST_DATA) {
info("Testing scrolling when " + data.desc);
if (data.initialScrollTop < 0) {
info("Scrolling container to the bottom.");
let newScrollTop = container.scrollHeight - container.clientHeight;
data.initialScrollTop = container.scrollTop = newScrollTop;
} else {
info("Scrolling container by " + data.initialScrollTop + "px");
container.scrollTop = data.initialScrollTop;
}
is(container.scrollTop, data.initialScrollTop, "Container scrolled.");
info("Clicking on header #" + data.headerToClick);
let header = headers[data.headerToClick];
let ready = tooltip.once("event-tooltip-ready");
EventUtils.synthesizeMouseAtCenter(header, {}, header.ownerGlobal);
yield ready;
info("Event handler expanded.");
if (data.alignTop) {
let headerRect = header.getBoundingClientRect();
is(headerRect.top, containerRect.top,
"Clicked header is aligned with the container top.");
} else if (data.alignBottom) {
let editorRect = header.nextElementSibling.getBoundingClientRect();
is(editorRect.bottom, containerRect.bottom,
"Clicked event handler code is aligned with the container bottom.");
} else {
is(container.scrollTop, data.initialScrollTop,
"Container did not scroll, as expected.");
}
}
});

View File

@ -0,0 +1,18 @@
<html>
<head>
<meta charset="UTF-8">
<title>doc_markup_events-overflow.html</title>
</head>
<body>
<h1>doc_markup_events-overflow.html</h1>
<span id="events">Inspect me!</span>
<script>
var el = document.getElementById("events");
for (var i = 50; i > 0; i--) {
el.addEventListener("click", function onClick() {
alert("click");
});
}
</script>
</body>
</html>

View File

@ -1,4 +1,5 @@
[DEFAULT]
skip-if = e10s # Bug 1030357 - projecteditor tests disabled with e10s
subsuite = devtools
support-files =
head.js

View File

@ -4,8 +4,6 @@
"use strict";
let { utils: Cu, interfaces: Ci } = Components;
addMessageListener("devtools:test:history", function ({ data }) {
content.history[data.direction]();
});
@ -18,11 +16,3 @@ addMessageListener("devtools:test:reload", function ({ data }) {
data = data || {};
content.location.reload(data.forceget);
});
addMessageListener("devtools:test:forceCC", function () {
let DOMWindowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
DOMWindowUtils.cycleCollect();
DOMWindowUtils.garbageCollect();
DOMWindowUtils.garbageCollect();
});

View File

@ -108,11 +108,15 @@ function populateTable() {
col3: "value27",
col4: "value34"
});
let span = doc.createElement("span");
span.textContent = "domnode";
table.push({
col1: "id9",
col2: "value11",
col3: "value23",
col4: "value38"
col4: span
});
}
@ -135,7 +139,7 @@ function testTreeItemInsertedCorrectly() {
}
/**
* Tests if the API exposed by TreeWidget works properly
* Tests if the API exposed by TableWidget works properly
*/
function testAPI() {
info("Testing TableWidget API");
@ -300,22 +304,48 @@ function testAPI() {
// Calling it on an unsorted column should sort by it in ascending manner
table.sortBy("col2");
let cell = table.tbody.children[2].firstChild.children[2];
while(cell) {
ok(cell.value >= cell.previousSibling.value, "Sorting is in ascending order");
cell = cell.nextSibling;
}
checkAscendingOrder(cell);
// Calling it again should sort by it in descending manner
table.sortBy("col2");
let cell = table.tbody.children[2].firstChild.lastChild.previousSibling;
while(cell != cell.parentNode.firstChild) {
ok(cell.value >= cell.nextSibling.value, "Sorting is in descending order");
cell = cell.previousSibling;
}
checkDescendingOrder(cell);
// Calling it again should sort by it in ascending manner
table.sortBy("col2");
let cell = table.tbody.children[2].firstChild.children[2];
checkAscendingOrder(cell);
table.clear();
populateTable();
// testing if sorting works should sort by ascending manner
table.sortBy("col4");
let cell = table.tbody.children[6].firstChild.children[1];
is(cell.textContent, "domnode", "DOMNode sorted correctly");
checkAscendingOrder(cell.nextSibling);
// Calling it again should sort it in descending order
table.sortBy("col4");
let cell = table.tbody.children[6].firstChild.children[9];
is(cell.textContent, "domnode", "DOMNode sorted correctly");
checkDescendingOrder(cell.previousSibling);
}
function checkAscendingOrder(cell) {
while(cell) {
ok(cell.value >= cell.previousSibling.value, "Sorting is in ascending order");
let currentCell = cell.value || cell.textContent;
let prevCell = cell.previousSibling.value || cell.previousSibling.textContent;
ok(currentCell >= prevCell, "Sorting is in ascending order");
cell = cell.nextSibling;
}
}
function checkDescendingOrder(cell) {
while(cell != cell.parentNode.firstChild) {
let currentCell = cell.value || cell.textContent;
let nextCell = cell.nextSibling.value || cell.nextSibling.textContent;
ok(currentCell >= nextCell, "Sorting is in descending order");
cell = cell.previousSibling;
}
}

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Cu } = require("chrome");
const {Cc, Ci, Cu} = require("chrome");
const EventEmitter = require("devtools/toolkit/event-emitter");
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@ -754,15 +754,22 @@ Column.prototype = {
* by this column.
*/
sort: function(items) {
// Only sort the array if we are sorting based on this column
if (this.sorted == 1) {
items.sort((a, b) => {
return a[this.id] > b[this.id]
let val1 = (a[this.id] instanceof Ci.nsIDOMNode) ?
a[this.id].textContent : a[this.id];
let val2 = (b[this.id] instanceof Ci.nsIDOMNode) ?
b[this.id].textContent : b[this.id];
return val1 > val2;
});
} else if (this.sorted > 1) {
items.sort((a, b) => {
return b[this.id] > a[this.id]
let val1 = (a[this.id] instanceof Ci.nsIDOMNode) ?
a[this.id].textContent : a[this.id];
let val2 = (b[this.id] instanceof Ci.nsIDOMNode) ?
b[this.id].textContent : b[this.id];
return val2 > val1;
});
}
@ -806,8 +813,18 @@ Column.prototype = {
return;
}
if (event.button == 0) {
this.table.emit(EVENTS.ROW_SELECTED,
event.originalTarget.getAttribute("data-id"));
let target = event.originalTarget;
let dataid = null;
while (target) {
dataid = target.getAttribute("data-id");
if (dataid) {
break;
}
target = target.parentNode;
}
this.table.emit(EVENTS.ROW_SELECTED, dataid);
}
},
@ -854,7 +871,9 @@ Column.prototype = {
* @param {Column} column
* The column object to which the cell belongs.
* @param {object} item
* The object representing the row.
* The object representing the row. It contains a key value pair
* representing the column id and its associated value. The value
* can be a DOMNode that is appended or a string value.
* @param {Cell} nextCell
* The cell object which is next to this cell. null if this cell is last
* cell of the column
@ -892,10 +911,23 @@ Cell.prototype = {
this.label.setAttribute("value", "");
return;
}
if (value.length > MAX_VISIBLE_STRING_SIZE) {
if (!(value instanceof Ci.nsIDOMNode) &&
value.length > MAX_VISIBLE_STRING_SIZE) {
value = value .substr(0, MAX_VISIBLE_STRING_SIZE) + "\u2026"; // …
}
this.label.setAttribute("value", value + "");
if (value instanceof Ci.nsIDOMNode) {
this.label.removeAttribute("value");
while (this.label.firstChild) {
this.label.removeChild(this.label.firstChild);
}
this.label.appendChild(value);
} else {
this.label.setAttribute("value", value + "");
}
},
get value() {

View File

@ -1265,6 +1265,14 @@ EventTooltip.prototype = {
editor.setText(tidied);
eventEditors.appended = true;
let container = header.parentElement.getBoundingClientRect();
if (header.getBoundingClientRect().top < container.top) {
header.scrollIntoView(true);
} else if (content.getBoundingClientRect().bottom > container.bottom) {
content.scrollIntoView(false);
}
this._tooltip.emit("event-tooltip-ready");
});
}

View File

@ -4,85 +4,74 @@
const TESTCASE_URI = TEST_BASE + "simple.html";
let TRANSITION_CLASS = "moz-styleeditor-transitioning";
let TESTCASE_CSS_SOURCE = "body{background-color:red;";
let gOriginalHref;
let gUI;
function test()
{
waitForExplicitFinish();
waitForExplicitFinish();
addTabAndCheckOnStyleEditorAdded(panel => gUI = panel.UI, testEditorAdded);
let test = asyncTest(function*() {
let panel = yield addTabAndOpenStyleEditors(2, null, TESTCASE_URI);
gUI = panel.UI;
content.location = TESTCASE_URI;
let editor = yield createNew();
testInitialState(editor);
let waitForPropertyChange = onPropertyChange(editor);
yield typeInEditor(editor);
yield waitForPropertyChange;
testUpdated(editor);
gUI = null;
});
function createNew() {
info("Creating a new stylesheet now");
let deferred = promise.defer();
gUI.once("editor-added", (ev, editor) => {
editor.getSourceEditor().then(deferred.resolve);
});
waitForFocus(function () {// create a new style sheet
let newButton = gPanelWindow.document.querySelector(".style-editor-newButton");
ok(newButton, "'new' button exists");
EventUtils.synthesizeMouseAtCenter(newButton, {}, gPanelWindow);
}, gPanelWindow);
return deferred.promise;
}
let gAddedCount = 0; // to add new stylesheet after the 2 initial stylesheets
let gNewEditor; // to make sure only one new stylesheet got created
let gOriginalHref;
function onPropertyChange(aEditor) {
let deferred = promise.defer();
let checksCompleted = 0;
function testEditorAdded(aEditor)
{
info("added " + gAddedCount + " editors");
if (++gAddedCount == 2) {
waitForFocus(function () {// create a new style sheet
let newButton = gPanelWindow.document.querySelector(".style-editor-newButton");
ok(newButton, "'new' button exists");
EventUtils.synthesizeMouseAtCenter(newButton, {}, gPanelWindow);
}, gPanelWindow);
}
if (gAddedCount < 3) {
return;
}
ok(!gNewEditor, "creating a new stylesheet triggers one EditorAdded event");
gNewEditor = aEditor; // above test will fail if we get a duplicate event
is(gUI.editors.length, 3,
"creating a new stylesheet added a new StyleEditor instance");
aEditor.styleSheet.once("style-applied", function() {
// when changes have been completely applied to live stylesheet after transisiton
ok(!content.document.documentElement.classList.contains(TRANSITION_CLASS),
"StyleEditor's transition class has been removed from content");
if (++checksCompleted == 3) {
cleanup();
aEditor.styleSheet.on("property-change", function onProp(property, value) {
// wait for text to be entered fully
let text = aEditor.sourceEditor.getText();
if (property == "ruleCount" && text == TESTCASE_CSS_SOURCE + "}") {
aEditor.styleSheet.off("property-change", onProp);
deferred.resolve();
}
});
aEditor.styleSheet.on("property-change", function(property) {
if (property == "ruleCount") {
let ruleCount = aEditor.summary.querySelector(".stylesheet-rule-count").textContent;
is(parseInt(ruleCount), 1,
"new editor shows 1 rule after modification");
if (++checksCompleted == 3) {
cleanup();
}
}
});
aEditor.getSourceEditor().then(testEditor);
return deferred.promise;
}
function testEditor(aEditor) {
waitForFocus(function () {
function testInitialState(aEditor) {
info("Testing the initial state of the new editor");
gOriginalHref = aEditor.styleSheet.href;
let summary = aEditor.summary;
ok(aEditor.sourceLoaded,
"new editor is loaded when attached");
ok(aEditor.isNew,
"new editor has isNew flag");
ok(aEditor.sourceLoaded, "new editor is loaded when attached");
ok(aEditor.isNew, "new editor has isNew flag");
ok(aEditor.sourceEditor.hasFocus(),
"new editor has focus");
ok(aEditor.sourceEditor.hasFocus(), "new editor has focus");
let summary = aEditor.summary;
let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
@ -92,41 +81,33 @@ function testEditor(aEditor) {
let computedStyle = content.getComputedStyle(content.document.body, null);
is(computedStyle.backgroundColor, "rgb(255, 255, 255)",
"content's background color is initially white");
for each (let c in TESTCASE_CSS_SOURCE) {
EventUtils.synthesizeKey(c, {}, gPanelWindow);
}
ok(aEditor.unsaved,
"new editor has unsaved flag");
// we know that the testcase above will start a CSS transition
content.addEventListener("transitionend", onTransitionEnd, false);
}, gPanelWindow) ;
}
function onTransitionEnd() {
content.removeEventListener("transitionend", onTransitionEnd, false);
function typeInEditor(aEditor) {
let deferred = promise.defer();
is(gNewEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}",
waitForFocus(function () {
for each (let c in TESTCASE_CSS_SOURCE) {
EventUtils.synthesizeKey(c, {}, gPanelWindow);
}
ok(aEditor.unsaved, "new editor has unsaved flag");
deferred.resolve();
}, gPanelWindow);
return deferred.promise;
}
function testUpdated(aEditor) {
info("Testing the state of the new editor after editing it");
is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}",
"rule bracket has been auto-closed");
let computedStyle = content.getComputedStyle(content.document.body, null);
is(computedStyle.backgroundColor, "rgb(255, 0, 0)",
"content's background color has been updated to red");
let ruleCount = aEditor.summary.querySelector(".stylesheet-rule-count").textContent;
is(parseInt(ruleCount), 1,
"new editor shows 1 rule after modification");
if (gNewEditor) {
is(gNewEditor.styleSheet.href, gOriginalHref,
"style sheet href did not change");
}
if (++checksCompleted == 3) {
cleanup();
}
}
function cleanup() {
gNewEditor = null;
gUI = null;
finish();
is(aEditor.styleSheet.href, gOriginalHref,
"style sheet href did not change");
}

View File

@ -10,7 +10,6 @@ support-files =
doc_connect-toggle.html
doc_connect-param.html
doc_connect-multi-param.html
doc_change-param.html
440hz_sine.ogg
head.js
@ -20,10 +19,6 @@ support-files =
[browser_audionode-actor-get-set-param.js]
[browser_audionode-actor-get-type.js]
[browser_audionode-actor-is-source.js]
[browser_webaudio-actor-change-params-01.js]
[browser_webaudio-actor-change-params-02.js]
[browser_webaudio-actor-change-params-03.js]
[browser_webaudio-actor-connect-param.js]
[browser_webaudio-actor-destroy-node.js]
[browser_webaudio-actor-simple.js]
@ -36,11 +31,11 @@ support-files =
[browser_wa_reset-03.js]
[browser_wa_graph-click.js]
[browser_wa_graph-markers.js]
[browser_wa_graph-render-01.js]
[browser_wa_graph-render-02.js]
[browser_wa_graph-render-03.js]
[browser_wa_graph-render-04.js]
[browser_wa_graph-markers.js]
[browser_wa_graph-selected.js]
[browser_wa_graph-zoom.js]
@ -48,10 +43,10 @@ support-files =
[browser_wa_inspector-toggle.js]
[browser_wa_properties-view.js]
[browser_wa_properties-view-edit-01.js]
skip-if = true # bug 1010423
[browser_wa_properties-view-edit-02.js]
skip-if = true # bug 1010423
[browser_wa_properties-view-media-nodes.js]
# [browser_wa_properties-view-edit-01.js]
# [browser_wa_properties-view-edit-02.js]
# Disabled for too many intermittents bug 1010423
[browser_wa_properties-view-params.js]
[browser_wa_properties-view-change-params.js]
[browser_wa_properties-view-params-objects.js]

View File

@ -1,46 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that params view correctly updates changed parameters
* when source code updates them, as well as CHANGE_PARAM events.
*/
function spawnTest() {
let [target, debuggee, panel] = yield initWebAudioEditor(CHANGE_PARAM_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, WebAudioInspectorView } = panelWin;
let gVars = WebAudioInspectorView._propsView;
// Set parameter polling to 20ms for tests
panelWin.PARAM_POLLING_FREQUENCY = 20;
let started = once(gFront, "start-context");
reload(target);
let [actors] = yield Promise.all([
getN(gFront, "create-node", 3),
waitForGraphRendered(panelWin, 3, 0)
]);
let oscId = actors[1].actorID;
click(panelWin, findGraphNode(panelWin, oscId));
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
// Yield twice so we get a diff
yield once(panelWin, EVENTS.CHANGE_PARAM);
let [[_, args]] = yield getSpread(panelWin, EVENTS.CHANGE_PARAM);
is(args.actorID, oscId, "EVENTS.CHANGE_PARAM has correct `actorID`");
ok(args.oldValue < args.newValue, "EVENTS.CHANGE_PARAM has correct `newValue` and `oldValue`");
is(args.param, "detune", "EVENTS.CHANGE_PARAM has correct `param`");
let [[_, args]] = yield getSpread(panelWin, EVENTS.CHANGE_PARAM);
checkVariableView(gVars, 0, { "detune": args.newValue }, "`detune` parameter updated.");
let [[_, args]] = yield getSpread(panelWin, EVENTS.CHANGE_PARAM);
checkVariableView(gVars, 0, { "detune": args.newValue }, "`detune` parameter updated.");
yield teardown(panel);
finish();
}

View File

@ -1,46 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test WebAudioActor `change-param` events and front.[en|dis]ableChangeParamEvents
*/
function spawnTest () {
let [target, debuggee, front] = yield initBackend(CHANGE_PARAM_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
getN(front, "create-node", 3)
]);
let osc = nodes[1];
let eventCount = 0;
yield front.enableChangeParamEvents(osc, 20);
front.on("change-param", onChangeParam);
yield getN(front, "change-param", 3);
yield front.disableChangeParamEvents();
let currEventCount = eventCount;
// Be flexible here incase we get an extra counter before the listener is turned off
ok(eventCount >= 3, "Calling `enableChangeParamEvents` should allow front to emit `change-param`.");
yield wait(100);
ok((eventCount - currEventCount) <= 2, "Calling `disableChangeParamEvents` should turn off the listener.");
front.off("change-param", onChangeParam);
yield removeTab(target.tab);
finish();
function onChangeParam ({ newValue, oldValue, param, actorID }) {
is(actorID, osc.actorID, "correct `actorID` in `change-param`.");
is(param, "detune", "correct `param` property in `change-param`.");
ok(newValue > oldValue,
"correct `newValue` (" + newValue + ") and `oldValue` (" + oldValue + ") in `change-param`");
eventCount++;
}
}

View File

@ -1,36 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that listening to param change polling does not break when the AudioNode is collected.
*/
function spawnTest () {
let [target, debuggee, front] = yield initBackend(DESTROY_NODES_URL);
let waitUntilDestroyed = getN(front, "destroy-node", 10);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
getN(front, "create-node", 13)
]);
let bufferNode = nodes[6];
yield front.enableChangeParamEvents(bufferNode, 20);
front.on("change-param", onChangeParam);
forceCC();
yield waitUntilDestroyed;
yield wait(50);
front.off("change-param", onChangeParam);
ok(true, "listening to `change-param` on a dead node doesn't throw.");
yield removeTab(target.tab);
finish();
function onChangeParam (args) {
ok(false, "`change-param` should not be emitted on a node that hasn't changed params or is dead.");
}
}

View File

@ -1,32 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test WebAudioActor `change-param` events on special types.
*/
function spawnTest () {
let [target, debuggee, front] = yield initBackend(CHANGE_PARAM_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
getN(front, "create-node", 3)
]);
let shaper = nodes[2];
let eventCount = 0;
yield front.enableChangeParamEvents(shaper, 20);
let onChange = once(front, "change-param");
shaper.setParam("curve", null);
let { newValue, oldValue } = yield onChange;
is(oldValue.type, "object", "`oldValue` should be an object.");
is(oldValue.class, "Float32Array", "`oldValue` should be of class Float32Array.");
is(newValue.type, "null", "`newValue` should be null.");
yield removeTab(target.tab);
finish();
}

View File

@ -1,25 +0,0 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Web Audio Editor test page</title>
</head>
<body>
<script type="text/javascript;version=1.8">
"use strict";
let ctx = new AudioContext();
let osc = ctx.createOscillator();
let shaperNode = ctx.createWaveShaper();
let detuneVal = 0;
shaperNode.curve = new Float32Array(65536);
setInterval(() => osc.detune.value = ++detuneVal, 10);
</script>
</body>
</html>

View File

@ -19,9 +19,7 @@ let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.j
let { WebAudioFront } = devtools.require("devtools/server/actors/webaudio");
let TargetFactory = devtools.TargetFactory;
let mm = null;
const FRAME_SCRIPT_UTILS_URL = "chrome://browser/content/devtools/frame-script-utils.js";
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/webaudioeditor/test/";
const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html";
const COMPLEX_CONTEXT_URL = EXAMPLE_URL + "doc_complex-context.html";
@ -32,7 +30,6 @@ const DESTROY_NODES_URL = EXAMPLE_URL + "doc_destroy-nodes.html";
const CONNECT_TOGGLE_URL = EXAMPLE_URL + "doc_connect-toggle.html";
const CONNECT_PARAM_URL = EXAMPLE_URL + "doc_connect-param.html";
const CONNECT_MULTI_PARAM_URL = EXAMPLE_URL + "doc_connect-multi-param.html";
const CHANGE_PARAM_URL = EXAMPLE_URL + "doc_change-param.html";
// All tests are asynchronous.
waitForExplicitFinish();
@ -136,8 +133,6 @@ function initBackend(aUrl) {
yield target.makeRemote();
let front = new WebAudioFront(target.client, target.form);
loadFrameScripts();
return [target, debuggee, front];
});
}
@ -155,8 +150,6 @@ function initWebAudioEditor(aUrl) {
Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
let toolbox = yield gDevTools.showToolbox(target, "webaudioeditor");
let panel = toolbox.getCurrentPanel();
loadFrameScripts();
return [target, debuggee, panel];
});
}
@ -394,12 +387,9 @@ function countGraphObjects (win) {
* Forces cycle collection and GC, used in AudioNode destruction tests.
*/
function forceCC () {
mm.sendAsyncMessage("devtools:test:forceCC");
}
function loadFrameScripts () {
mm = gBrowser.selectedBrowser.messageManager;
mm.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
SpecialPowers.DOMWindowUtils.cycleCollect();
SpecialPowers.DOMWindowUtils.garbageCollect();
SpecialPowers.DOMWindowUtils.garbageCollect();
}
/**

View File

@ -20,9 +20,8 @@ const STRINGS_URI = "chrome://browser/locale/devtools/webaudioeditor.properties"
const L10N = new ViewHelpers.L10N(STRINGS_URI);
const Telemetry = require("devtools/shared/telemetry");
const telemetry = new Telemetry();
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
let PARAM_POLLING_FREQUENCY = 1000;
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
@ -174,8 +173,6 @@ let WebAudioEditorController = {
telemetry.toolOpened("webaudioeditor");
this._onTabNavigated = this._onTabNavigated.bind(this);
this._onThemeChange = this._onThemeChange.bind(this);
this._onSelectNode = this._onSelectNode.bind(this);
this._onChangeParam = this._onChangeParam.bind(this);
gTarget.on("will-navigate", this._onTabNavigated);
gTarget.on("navigate", this._onTabNavigated);
gFront.on("start-context", this._onStartContext);
@ -197,15 +194,12 @@ let WebAudioEditorController = {
window.on(EVENTS.DISCONNECT_NODE, this._onUpdatedContext);
window.on(EVENTS.DESTROY_NODE, this._onUpdatedContext);
window.on(EVENTS.CONNECT_PARAM, this._onUpdatedContext);
// Set up a controller for managing parameter changes per audio node
window.on(EVENTS.UI_SELECT_NODE, this._onSelectNode);
},
/**
* Remove events emitted by the current tab target.
*/
destroy: Task.async(function* () {
destroy: function() {
telemetry.toolClosed("webaudioeditor");
gTarget.off("will-navigate", this._onTabNavigated);
gTarget.off("navigate", this._onTabNavigated);
@ -221,11 +215,8 @@ let WebAudioEditorController = {
window.off(EVENTS.DISCONNECT_NODE, this._onUpdatedContext);
window.off(EVENTS.DESTROY_NODE, this._onUpdatedContext);
window.off(EVENTS.CONNECT_PARAM, this._onUpdatedContext);
window.off(EVENTS.UI_SELECT_NODE, this._onSelectNode);
gDevTools.off("pref-changed", this._onThemeChange);
yield gFront.disableChangeParamEvents();
}),
},
/**
* Called when page is reloaded to show the reload notice and waiting
@ -354,21 +345,9 @@ let WebAudioEditorController = {
/**
* Called when a node param is changed.
*/
_onChangeParam: function (args) {
window.emit(EVENTS.CHANGE_PARAM, args);
},
/**
* Called on UI_SELECT_NODE, used to manage
* `change-param` events on that node.
*/
_onSelectNode: function (_, id) {
let node = getViewNodeById(id);
if (node && node.actor) {
gFront.enableChangeParamEvents(node.actor, PARAM_POLLING_FREQUENCY);
}
},
_onChangeParam: function({ actor, param, value }) {
window.emit(EVENTS.CHANGE_PARAM, getViewNodeByActor(actor), param, value);
}
};
/**

View File

@ -377,7 +377,6 @@ let WebAudioInspectorView = {
this._onNodeSelect = this._onNodeSelect.bind(this);
this._onTogglePaneClick = this._onTogglePaneClick.bind(this);
this._onDestroyNode = this._onDestroyNode.bind(this);
this._onChangeParam = this._onChangeParam.bind(this);
this._inspectorPaneToggleButton.addEventListener("mousedown", this._onTogglePaneClick, false);
this._propsView = new VariablesView($("#properties-tabpanel-content"), GENERIC_VARIABLES_VIEW_SETTINGS);
@ -385,7 +384,6 @@ let WebAudioInspectorView = {
window.on(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
window.on(EVENTS.DESTROY_NODE, this._onDestroyNode);
window.on(EVENTS.CHANGE_PARAM, this._onChangeParam);
},
/**
@ -395,7 +393,6 @@ let WebAudioInspectorView = {
this._inspectorPaneToggleButton.removeEventListener("mousedown", this._onTogglePaneClick);
window.off(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
window.off(EVENTS.DESTROY_NODE, this._onDestroyNode);
window.off(EVENTS.CHANGE_PARAM, this._onChangeParam);
this._inspectorPane = null;
this._inspectorPaneToggleButton = null;
@ -615,22 +612,7 @@ let WebAudioInspectorView = {
if (this._currentNode && this._currentNode.id === id) {
this.setCurrentAudioNode(null);
}
},
/**
* Called when `CHANGE_PARAM` is fired. We should ensure that this event is
* for the same node that is currently selected. We check the existence
* of each part of the scope to make sure that if this event was fired
* during a VariablesView rebuild, then we just ignore it.
*/
_onChangeParam: function (_, { param, newValue, oldValue, actorID }) {
if (!this._currentNode || this._currentNode.actor.actorID !== actorID) return;
let scope = this._getAudioPropertiesScope();
if (!scope) return;
let property = scope.get(param);
if (!property) return;
property.setGrip(newValue);
},
}
};
/**

View File

@ -18,6 +18,10 @@
<!ENTITY toolboxZoomReset.key "0">
<!ENTITY toolboxReload.key "r">
<!-- LOCALIZATION NOTE (toolboxFramesButton): This is the label for
- the iframes menu list that appears only when the document has some.
- It allows you to switch the context of the whole toolbox. -->
<!ENTITY toolboxFramesTooltip "Select an iframe as the currently targeted document">
<!-- LOCALIZATION NOTE (options.context.advancedSettings): This is the label for
- the heading of the advanced settings group in the options panel. -->

View File

@ -22,6 +22,7 @@ incoming_call_decline_button=Decline
incoming_call_decline_and_block_button=Decline and Block
incoming_call_block_button=Block
hangup_button_title=Hang up
hangup_button_caption=End Call
mute_local_audio_button_title=Mute your audio
unmute_local_audio_button_title=Unmute your audio
mute_local_video_button_title=Mute your video

View File

@ -94,6 +94,7 @@ cookiesAll=The following cookies are stored on your computer:
cookiesFiltered=The following cookies match your search:
#### Offline apps
offlineAppsList.height=7em
offlineAppRemoveTitle=Remove offline website data
offlineAppRemovePrompt=After removing this data, %S will not be available offline. Are you sure you want to remove this offline website?
offlineAppRemoveConfirm=Remove offline data

View File

@ -217,6 +217,8 @@ browser.jar:
skin/classic/browser/devtools/command-tilt@2x.png (../shared/devtools/images/command-tilt@2x.png)
skin/classic/browser/devtools/command-pick.png (../shared/devtools/images/command-pick.png)
skin/classic/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
skin/classic/browser/devtools/command-frames.png (../shared/devtools/images/command-frames.png)
skin/classic/browser/devtools/command-frames@2x.png (../shared/devtools/images/command-frames@2x.png)
skin/classic/browser/devtools/command-console.png (../shared/devtools/images/command-console.png)
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
skin/classic/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)

View File

@ -340,6 +340,8 @@ browser.jar:
skin/classic/browser/devtools/command-tilt@2x.png (../shared/devtools/images/command-tilt@2x.png)
skin/classic/browser/devtools/command-pick.png (../shared/devtools/images/command-pick.png)
skin/classic/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
skin/classic/browser/devtools/command-frames.png (../shared/devtools/images/command-frames.png)
skin/classic/browser/devtools/command-frames@2x.png (../shared/devtools/images/command-frames@2x.png)
skin/classic/browser/devtools/command-console.png (../shared/devtools/images/command-console.png)
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
skin/classic/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 B

View File

@ -44,6 +44,7 @@
#devtools-tooltip-events-container {
margin: -4px; /* Compensate for the .panel-arrowcontent padding. */
max-width: 390px;
overflow: auto;
}
.event-header {

View File

@ -380,87 +380,84 @@
margin: 0;
min-width: 78px;
text-align: center;
background-color: transparent;
color: inherit;
-moz-box-flex: 1;
border-width: 0;
-moz-border-start-width: 1px;
border-style: solid;
border-radius: 0;
position: static;
-moz-margin-start: -1px;
text-shadow: none;
}
.devtools-sidebar-tabs > tabs > tab {
background-size: calc(100% - 1px) 100%, 1px 100%;
background-repeat: no-repeat;
background-position: 1px, 0;
}
.devtools-sidebar-tabs > tabs > tab:not(:last-of-type) {
background-size: calc(100% - 2px) 100%, 1px 100%;
}
.devtools-sidebar-tabs:-moz-locale-dir(rtl) > tabs > tab {
background-position: calc(100% - 1px), 100%;
}
.devtools-sidebar-tabs > tabs > tab {
background-color: transparent;
.devtools-sidebar-tabs > tabs > tab:first-child {
-moz-border-start-width: 0;
}
.theme-dark .devtools-sidebar-tabs > tabs > tab {
background-image: linear-gradient(transparent, transparent), @smallSeparatorDark@;
border-image: @smallSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs > tabs > tab:hover {
background-image: linear-gradient(hsla(206,37%,4%,.2), hsla(206,37%,4%,.2)), @smallSeparatorDark@;
background: hsla(206,37%,4%,.2);
border-image: @smallSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs > tabs > tab:hover:active {
background-image: linear-gradient(hsla(206,37%,4%,.4), hsla(206,37%,4%,.4)), @smallSeparatorDark@;
background: hsla(206,37%,4%,.4);
border-image: @smallSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs > tabs > tab[selected] + tab {
background-image: linear-gradient(transparent, transparent), @solidSeparatorDark@;
border-image: @solidSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs > tabs > tab[selected] + tab:hover {
background-image: linear-gradient(hsla(206,37%,4%,.2), hsla(206,37%,4%,.2)), @solidSeparatorDark@;
background: hsla(206,37%,4%,.2);
border-image: @solidSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs > tabs > tab[selected] + tab:hover:active {
background-image: linear-gradient(hsla(206,37%,4%,.4), hsla(206,37%,4%,.4)), @solidSeparatorDark@;
background: hsla(206,37%,4%,.4);
border-image: @solidSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs > tabs > tab[selected],
.theme-dark .devtools-sidebar-tabs > tabs > tab[selected]:hover:active {
color: #f5f7fa;
background-image: linear-gradient(#1d4f73, #1d4f73), @solidSeparatorDark@;
background: #1d4f73;
border-image: @solidSeparatorDark@ 1 1;
}
.theme-light .devtools-sidebar-tabs > tabs > tab {
background-image: linear-gradient(transparent, transparent), @smallSeparatorLight@;
border-image: @smallSeparatorLight@ 1 1;
}
.theme-light .devtools-sidebar-tabs > tabs > tab:hover {
background-image: linear-gradient(#ddd, #ddd), @smallSeparatorLight@;
background: #ddd;
border-image: @smallSeparatorLight@ 1 1;
}
.theme-light .devtools-sidebar-tabs > tabs > tab:hover:active {
background-image: linear-gradient(#ddd, #ddd), @smallSeparatorLight@;
background: #ddd;
border-image: @smallSeparatorLight@ 1 1;
}
.theme-light .devtools-sidebar-tabs > tabs > tab[selected] + tab {
background-image: linear-gradient(transparent, transparent), @solidSeparatorLight@;
border-image: @solidSeparatorLight@;
}
.theme-light .devtools-sidebar-tabs > tabs > tab[selected] + tab:hover {
background-image: linear-gradient(#ddd, #ddd), @solidSeparatorLight@;
background: #ddd;
border-image: @solidSeparatorLight@;
}
.theme-light .devtools-sidebar-tabs > tabs > tab[selected],
.theme-light .devtools-sidebar-tabs > tabs > tab[selected]:hover:active {
color: #f5f7fa;
background-image: linear-gradient(#4c9ed9, #4c9ed9), @solidSeparatorLight@;
background: #4c9ed9;
border-image: @solidSeparatorLight@;
}
/* Toolbox - moved from toolbox.css.
@ -596,6 +593,10 @@
background-image: url("chrome://browser/skin/devtools/command-pick.png");
}
#command-button-frames > image {
background-image: url("chrome://browser/skin/devtools/command-frames.png");
}
#command-button-splitconsole > image {
background-image: url("chrome://browser/skin/devtools/command-console.png");
}
@ -629,6 +630,10 @@
background-image: url("chrome://browser/skin/devtools/command-pick@2x.png");
}
#command-button-frames > image {
background-image: url("chrome://browser/skin/devtools/command-frames@2x.png");
}
#command-button-splitconsole > image {
background-image: url("chrome://browser/skin/devtools/command-console@2x.png");
}

View File

@ -131,6 +131,12 @@ prefpane {
height: 500px;
}
#handlersView > listheader {
-moz-appearance: none;
border: 0;
padding: 0;
}
#typeColumn,
#actionColumn {
-moz-appearance: none;
@ -244,7 +250,8 @@ description > html|a {
#weavePrefsDeck > vbox > description,
#weavePrefsDeck > vbox > #pairDevice > label,
#weavePrefsDeck > #needsUpdate > hbox > #loginError,
#weavePrefsDeck > #hasFxaAccount > hbox:not(#tosPP) > label {
#weavePrefsDeck > #hasFxaAccount > hbox:not(#tosPP-normal) > label,
#weavePrefsDeck > #hasFxaAccount > hbox:not(#tosPP-small) > label {
/* no margin-start for elements at the begin of a line */
-moz-margin-start: 0;
}

View File

@ -257,6 +257,8 @@ browser.jar:
skin/classic/browser/devtools/command-tilt@2x.png (../shared/devtools/images/command-tilt@2x.png)
skin/classic/browser/devtools/command-pick.png (../shared/devtools/images/command-pick.png)
skin/classic/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
skin/classic/browser/devtools/command-frames.png (../shared/devtools/images/command-frames.png)
skin/classic/browser/devtools/command-frames@2x.png (../shared/devtools/images/command-frames@2x.png)
skin/classic/browser/devtools/command-console.png (../shared/devtools/images/command-console.png)
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
skin/classic/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)
@ -666,6 +668,8 @@ browser.jar:
skin/classic/aero/browser/devtools/command-tilt@2x.png (../shared/devtools/images/command-tilt@2x.png)
skin/classic/aero/browser/devtools/command-pick.png (../shared/devtools/images/command-pick.png)
skin/classic/aero/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
skin/classic/aero/browser/devtools/command-frames.png (../shared/devtools/images/command-frames.png)
skin/classic/aero/browser/devtools/command-frames@2x.png (../shared/devtools/images/command-frames@2x.png)
skin/classic/aero/browser/devtools/command-console.png (../shared/devtools/images/command-console.png)
skin/classic/aero/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
skin/classic/aero/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)

View File

@ -3905,6 +3905,11 @@ else
AC_MSG_RESULT([none])
AC_MSG_ERROR([--enable-application=mobile is no longer supported.])
fi
# Support comm-central.
if test -n "$EXTERNAL_SOURCE_DIR" ; then
MOZ_BUILD_APP="$EXTERNAL_SOURCE_DIR/$MOZ_BUILD_APP"
MOZ_BUILD_APP=`${PYTHON} -c "import os.path; print(os.path.relpath(\"${MOZ_BUILD_APP}\", \"${srcdir}\"))"`
fi
# We have a valid application only if it has a build.mk file in its top
# directory.
if test ! -f "${srcdir}/${MOZ_BUILD_APP}/build.mk" ; then
@ -9046,7 +9051,7 @@ if test -n "$_subconfigure_subdir"; then
srcdir="$srcdir/.."
_save_ac_configure_args="$ac_configure_args"
ac_configure_args="$_subconfigure_config_args"
AC_OUTPUT_SUBDIRS("$_subconfigure_subdir")
AC_OUTPUT_SUBDIRS("$_subconfigure_subdir",$cache_file)
ac_configure_args="$_save_ac_configure_args"
srcdir="$_save_srcdir"
fi

View File

@ -78,11 +78,7 @@ class ChromeCast implements GeckoMediaPlayer {
}
@Override
public void onMetadataUpdated() {
MediaInfo mediaInfo = remoteMediaPlayer.getMediaInfo();
MediaMetadata metadata = mediaInfo.getMetadata();
debug("metadata updated " + metadata);
}
public void onMetadataUpdated() { }
@Override
public void onResult(ApplicationConnectionResult result) {

View File

@ -4,7 +4,10 @@
package org.mozilla.gecko.background.fxa;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.concurrent.Executor;
import org.json.simple.JSONObject;
@ -94,13 +97,35 @@ public class FxAccountClient20 extends FxAccountClient10 implements FxAccountCli
post(resource, body, delegate);
}
public void createAccount(final byte[] emailUTF8, final byte[] quickStretchedPW, final boolean getKeys, final boolean preVerified,
/**
* Create account/create URI, encoding query parameters carefully.
* <p>
* This is equivalent to <code>android.net.Uri.Builder</code>, which is not
* present in our JUnit 4 tests.
*/
protected URI getCreateAccountURI(final boolean getKeys, final String service) throws UnsupportedEncodingException, URISyntaxException {
if (service == null) {
throw new IllegalArgumentException("service must not be null");
}
final StringBuilder sb = new StringBuilder(serverURI); // serverURI always has a trailing slash.
sb.append("account/create?service=");
// Be very careful that query parameters are encoded correctly!
sb.append(URLEncoder.encode(service, "UTF-8"));
if (getKeys) {
sb.append("&keys=true");
}
return new URI(sb.toString());
}
public void createAccount(final byte[] emailUTF8, final byte[] quickStretchedPW,
final boolean getKeys,
final boolean preVerified,
final String service,
final RequestDelegate<LoginResponse> delegate) {
BaseResource resource;
JSONObject body;
final String path = getKeys ? "account/create?keys=true" : "account/create";
final BaseResource resource;
final JSONObject body;
try {
resource = new BaseResource(new URI(serverURI + path));
resource = new BaseResource(getCreateAccountURI(getKeys, service));
body = new FxAccount20CreateDelegate(emailUTF8, quickStretchedPW, preVerified).getCreateBody();
} catch (Exception e) {
invokeHandleError(delegate, e);
@ -144,7 +169,7 @@ public class FxAccountClient20 extends FxAccountClient10 implements FxAccountCli
public void createAccountAndGetKeys(byte[] emailUTF8, PasswordStretcher passwordStretcher, RequestDelegate<LoginResponse> delegate) {
try {
byte[] quickStretchedPW = passwordStretcher.getQuickStretchedPW(emailUTF8);
createAccount(emailUTF8, quickStretchedPW, true, false, delegate);
createAccount(emailUTF8, quickStretchedPW, true, false, "sync", delegate);
} catch (Exception e) {
invokeHandleError(delegate, e);
return;

View File

@ -37,7 +37,7 @@ public class SearchEnginePreference extends CustomListPreference {
private FaviconView mFaviconView;
// Search engine identifier specified by the gecko search service. This will be null
// Search engine identifier specified by the gecko search service. This will be "other"
// for engines that are not shipped with the app.
private String mIdentifier;
@ -123,7 +123,7 @@ public class SearchEnginePreference extends CustomListPreference {
* @return Identifier of built-in search engine, or "other" if engine is not built-in.
*/
public String getIdentifier() {
return (mIdentifier == null) ? "other" : mIdentifier;
return mIdentifier;
}
/**
@ -134,6 +134,11 @@ public class SearchEnginePreference extends CustomListPreference {
public void setSearchEngineFromJSON(JSONObject geckoEngineJSON) throws JSONException {
mIdentifier = geckoEngineJSON.getString("identifier");
// A null JS value gets converted into a string.
if (mIdentifier.equals("null")) {
mIdentifier = "other";
}
final String engineName = geckoEngineJSON.getString("name");
final SpannableString titleSpannable = new SpannableString(engineName);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

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