Merge fx-team to m-c. a=merge
@ -144,6 +144,7 @@
|
||||
@BINPATH@/components/content_html.xpt
|
||||
@BINPATH@/components/content_xslt.xpt
|
||||
@BINPATH@/components/cookie.xpt
|
||||
@BINPATH@/components/devtools_security.xpt
|
||||
@BINPATH@/components/directory.xpt
|
||||
@BINPATH@/components/diskspacewatcher.xpt
|
||||
@BINPATH@/components/docshell.xpt
|
||||
|
@ -60,14 +60,6 @@ ul {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* common.css overrides */
|
||||
|
||||
button {
|
||||
font-size: 1em;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
/* Pressing the retry button will cause the cursor to flicker from a pointer to
|
||||
* not-allowed. Override the disabled cursor behaviour since we will never show
|
||||
* the button disabled as the initial state. */
|
||||
|
@ -3,16 +3,30 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
let notificationObserver;
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("browser.fixup.domainwhitelist.localhost");
|
||||
if (notificationObserver) {
|
||||
notificationObserver.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
function promiseNotificationForTab(value, expected, tab=gBrowser.selectedTab) {
|
||||
let deferred = Promise.defer();
|
||||
let notificationBox = gBrowser.getNotificationBox(tab.linkedBrowser);
|
||||
if (expected) {
|
||||
waitForCondition(() => notificationBox.getNotificationWithValue(value) !== null,
|
||||
deferred.resolve, "Were expecting to get a notification");
|
||||
let checkForNotification = function() {
|
||||
if (notificationBox.getNotificationWithValue(value)) {
|
||||
notificationObserver.disconnect();
|
||||
notificationObserver = null;
|
||||
deferred.resolve();
|
||||
}
|
||||
}
|
||||
if (notificationObserver) {
|
||||
notificationObserver.disconnect();
|
||||
}
|
||||
notificationObserver = new MutationObserver(checkForNotification);
|
||||
notificationObserver.observe(notificationBox, {childList: true});
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
is(notificationBox.getNotificationWithValue(value), null, "We are expecting to not get a notification");
|
||||
|
@ -1819,11 +1819,11 @@
|
||||
this._setupLink(linkLabel, action.detailsLink);
|
||||
|
||||
this._primaryButton.label = gNavigatorBundle.getString(button1.label);
|
||||
this._primaryButton.accesskey = gNavigatorBundle.getString(button1.accesskey);
|
||||
this._primaryButton.accessKey = gNavigatorBundle.getString(button1.accesskey);
|
||||
this._primaryButton.setAttribute("action", button1.action);
|
||||
|
||||
this._secondaryButton.label = gNavigatorBundle.getString(button2.label);
|
||||
this._secondaryButton.accesskey = gNavigatorBundle.getString(button2.accesskey);
|
||||
this._secondaryButton.accessKey = gNavigatorBundle.getString(button2.accesskey);
|
||||
this._secondaryButton.setAttribute("action", button2.action);
|
||||
if (button1.default) {
|
||||
this._primaryButton.setAttribute("default", "true");
|
||||
|
@ -11,8 +11,10 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource:///modules/loop/MozLoopService.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "hookWindowCloseForPanelClose",
|
||||
"resource://gre/modules/MozSocialAPI.jsm");
|
||||
|
||||
"resource://gre/modules/MozSocialAPI.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
|
||||
"@mozilla.org/widget/clipboardhelper;1",
|
||||
"nsIClipboardHelper");
|
||||
this.EXPORTED_SYMBOLS = ["injectLoopAPI"];
|
||||
|
||||
/**
|
||||
@ -225,6 +227,19 @@ function injectLoopAPI(targetWindow) {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copies passed string onto the system clipboard.
|
||||
*
|
||||
* @param {String} str The string to copy
|
||||
*/
|
||||
copyString: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(str) {
|
||||
clipboardHelper.copyString(str);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let contentObj = Cu.createObjectIn(targetWindow);
|
||||
|
@ -152,11 +152,17 @@ loop.panel = (function(_, mozL10n) {
|
||||
});
|
||||
|
||||
var CallUrlResult = React.createClass({displayName: 'CallUrlResult',
|
||||
propTypes: {
|
||||
callUrl: React.PropTypes.string,
|
||||
notifier: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
pending: false,
|
||||
callUrl: ''
|
||||
copied: false,
|
||||
callUrl: this.props.callUrl || ""
|
||||
};
|
||||
},
|
||||
|
||||
@ -184,7 +190,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
|
||||
if (err) {
|
||||
this.props.notifier.errorL10n("unable_retrieve_url");
|
||||
this.setState({pending: false});
|
||||
this.setState(this.getInitialState());
|
||||
} else {
|
||||
try {
|
||||
var callUrl = new window.URL(callUrlData.callUrl);
|
||||
@ -194,43 +200,57 @@ loop.panel = (function(_, mozL10n) {
|
||||
callUrl.pathname.split('/').pop();
|
||||
|
||||
navigator.mozLoop.setLoopCharPref('loopToken', token);
|
||||
this.setState({pending: false, callUrl: callUrl.href});
|
||||
this.setState({pending: false, copied: false, callUrl: callUrl.href});
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
this.props.notifier.errorL10n("unable_retrieve_url");
|
||||
this.setState({pending: false});
|
||||
this.setState(this.getInitialState());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_generateMailto: function() {
|
||||
_generateMailTo: function() {
|
||||
return encodeURI([
|
||||
"mailto:?subject=" + __("share_email_subject") + "&",
|
||||
"body=" + __("share_email_body", {callUrl: this.state.callUrl})
|
||||
].join(""));
|
||||
},
|
||||
|
||||
handleEmailButtonClick: function(event) {
|
||||
// Note: side effect
|
||||
document.location = event.target.dataset.mailto;
|
||||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
// XXX the mozLoop object should be passed as a prop, to ease testing and
|
||||
// using a fake implementation in UI components showcase.
|
||||
navigator.mozLoop.copyString(this.state.callUrl);
|
||||
this.setState({copied: true});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX setting elem value from a state (in the callUrl input)
|
||||
// makes it immutable ie read only but that is fine in our case.
|
||||
// readOnly attr will suppress a warning regarding this issue
|
||||
// from the react lib.
|
||||
var cx = React.addons.classSet;
|
||||
var inputCSSClass = {
|
||||
"pending": this.state.pending,
|
||||
"callUrl": !this.state.pending
|
||||
};
|
||||
return (
|
||||
PanelLayout({summary: __("share_link_header_text")},
|
||||
React.DOM.div({className: "invite"},
|
||||
React.DOM.input({type: "url", value: this.state.callUrl, readOnly: "true",
|
||||
className: cx(inputCSSClass)}),
|
||||
React.DOM.a({className: cx({btn: true, hide: !this.state.callUrl}),
|
||||
href: this._generateMailto()},
|
||||
React.DOM.span(null,
|
||||
__("share_button")
|
||||
)
|
||||
)
|
||||
className: cx({pending: this.state.pending})}),
|
||||
React.DOM.p({className: "button-group url-actions"},
|
||||
React.DOM.button({className: "btn btn-email", disabled: !this.state.callUrl,
|
||||
onClick: this.handleEmailButtonClick,
|
||||
'data-mailto': this._generateMailTo()},
|
||||
__("share_button")
|
||||
),
|
||||
React.DOM.button({className: "btn btn-copy", disabled: !this.state.callUrl,
|
||||
onClick: this.handleCopyButtonClick},
|
||||
this.state.copied ? __("copied_url_button") :
|
||||
__("copy_url_button")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
@ -243,14 +263,17 @@ loop.panel = (function(_, mozL10n) {
|
||||
var PanelView = React.createClass({displayName: 'PanelView',
|
||||
propTypes: {
|
||||
notifier: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
client: React.PropTypes.object.isRequired,
|
||||
// Mostly used for UI components showcase and unit tests
|
||||
callUrl: React.PropTypes.string
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
React.DOM.div(null,
|
||||
CallUrlResult({client: this.props.client,
|
||||
notifier: this.props.notifier}),
|
||||
notifier: this.props.notifier,
|
||||
callUrl: this.props.callUrl}),
|
||||
ToSView(null),
|
||||
AvailabilityDropdown(null)
|
||||
)
|
||||
|
@ -152,11 +152,17 @@ loop.panel = (function(_, mozL10n) {
|
||||
});
|
||||
|
||||
var CallUrlResult = React.createClass({
|
||||
propTypes: {
|
||||
callUrl: React.PropTypes.string,
|
||||
notifier: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
pending: false,
|
||||
callUrl: ''
|
||||
copied: false,
|
||||
callUrl: this.props.callUrl || ""
|
||||
};
|
||||
},
|
||||
|
||||
@ -184,7 +190,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
|
||||
if (err) {
|
||||
this.props.notifier.errorL10n("unable_retrieve_url");
|
||||
this.setState({pending: false});
|
||||
this.setState(this.getInitialState());
|
||||
} else {
|
||||
try {
|
||||
var callUrl = new window.URL(callUrlData.callUrl);
|
||||
@ -194,43 +200,57 @@ loop.panel = (function(_, mozL10n) {
|
||||
callUrl.pathname.split('/').pop();
|
||||
|
||||
navigator.mozLoop.setLoopCharPref('loopToken', token);
|
||||
this.setState({pending: false, callUrl: callUrl.href});
|
||||
this.setState({pending: false, copied: false, callUrl: callUrl.href});
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
this.props.notifier.errorL10n("unable_retrieve_url");
|
||||
this.setState({pending: false});
|
||||
this.setState(this.getInitialState());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_generateMailto: function() {
|
||||
_generateMailTo: function() {
|
||||
return encodeURI([
|
||||
"mailto:?subject=" + __("share_email_subject") + "&",
|
||||
"body=" + __("share_email_body", {callUrl: this.state.callUrl})
|
||||
].join(""));
|
||||
},
|
||||
|
||||
handleEmailButtonClick: function(event) {
|
||||
// Note: side effect
|
||||
document.location = event.target.dataset.mailto;
|
||||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
// XXX the mozLoop object should be passed as a prop, to ease testing and
|
||||
// using a fake implementation in UI components showcase.
|
||||
navigator.mozLoop.copyString(this.state.callUrl);
|
||||
this.setState({copied: true});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX setting elem value from a state (in the callUrl input)
|
||||
// makes it immutable ie read only but that is fine in our case.
|
||||
// readOnly attr will suppress a warning regarding this issue
|
||||
// from the react lib.
|
||||
var cx = React.addons.classSet;
|
||||
var inputCSSClass = {
|
||||
"pending": this.state.pending,
|
||||
"callUrl": !this.state.pending
|
||||
};
|
||||
return (
|
||||
<PanelLayout summary={__("share_link_header_text")}>
|
||||
<div className="invite">
|
||||
<input type="url" value={this.state.callUrl} readOnly="true"
|
||||
className={cx(inputCSSClass)} />
|
||||
<a className={cx({btn: true, hide: !this.state.callUrl})}
|
||||
href={this._generateMailto()}>
|
||||
<span>
|
||||
{__("share_button")}
|
||||
</span>
|
||||
</a>
|
||||
className={cx({pending: this.state.pending})} />
|
||||
<p className="button-group url-actions">
|
||||
<button className="btn btn-email" disabled={!this.state.callUrl}
|
||||
onClick={this.handleEmailButtonClick}
|
||||
data-mailto={this._generateMailTo()}>
|
||||
{__("share_button")}
|
||||
</button>
|
||||
<button className="btn btn-copy" disabled={!this.state.callUrl}
|
||||
onClick={this.handleCopyButtonClick}>
|
||||
{this.state.copied ? __("copied_url_button") :
|
||||
__("copy_url_button")}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</PanelLayout>
|
||||
);
|
||||
@ -243,14 +263,17 @@ loop.panel = (function(_, mozL10n) {
|
||||
var PanelView = React.createClass({
|
||||
propTypes: {
|
||||
notifier: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
client: React.PropTypes.object.isRequired,
|
||||
// Mostly used for UI components showcase and unit tests
|
||||
callUrl: React.PropTypes.string
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<CallUrlResult client={this.props.client}
|
||||
notifier={this.props.notifier} />
|
||||
notifier={this.props.notifier}
|
||||
callUrl={this.props.callUrl} />
|
||||
<ToSView />
|
||||
<AvailabilityDropdown />
|
||||
</div>
|
||||
|
@ -320,6 +320,7 @@
|
||||
|
||||
.feedback label {
|
||||
display: block;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.feedback form input[type="radio"] {
|
||||
|
@ -86,15 +86,17 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.share .action .btn:hover {
|
||||
.share > .action .btn:hover {
|
||||
background-color: #008ACB;
|
||||
border: 1px solid #008ACB;
|
||||
}
|
||||
|
||||
.share .action .btn span {
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
.share > .action > .invite > .url-actions {
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
|
||||
.share > .action > .invite > .url-actions > .btn:first-child {
|
||||
-moz-margin-end: 1em;
|
||||
}
|
||||
|
||||
/* Specific cases */
|
||||
|
@ -12,7 +12,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
"use strict";
|
||||
|
||||
var sharedModels = loop.shared.models;
|
||||
var __ = l10n.get;
|
||||
var WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS = 5;
|
||||
|
||||
/**
|
||||
@ -134,7 +133,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
var prefix = this.props.enabled ? "mute" : "unmute";
|
||||
var suffix = "button_title";
|
||||
var msgId = [prefix, this.props.scope, this.props.type, suffix].join("_");
|
||||
return __(msgId);
|
||||
return l10n.get(msgId);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -184,7 +183,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
React.DOM.ul({className: "conversation-toolbar"},
|
||||
React.DOM.li(null, React.DOM.button({className: "btn btn-hangup",
|
||||
onClick: this.handleClickHangup,
|
||||
title: __("hangup_button_title")})),
|
||||
title: l10n.get("hangup_button_title")})),
|
||||
React.DOM.li(null, MediaControlButton({action: this.handleToggleVideo,
|
||||
enabled: this.props.video.enabled,
|
||||
scope: "local", type: "video"})),
|
||||
@ -371,7 +370,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
if (this.props.reset) {
|
||||
backButton = (
|
||||
React.DOM.button({className: "back", type: "button", onClick: this.props.reset},
|
||||
"« ", __("feedback_back_button")
|
||||
"« ", l10n.get("feedback_back_button")
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -405,11 +404,11 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
|
||||
_getCategories: function() {
|
||||
return {
|
||||
audio_quality: __("feedback_category_audio_quality"),
|
||||
video_quality: __("feedback_category_video_quality"),
|
||||
disconnected : __("feedback_category_was_disconnected"),
|
||||
confusing: __("feedback_category_confusing"),
|
||||
other: __("feedback_category_other")
|
||||
audio_quality: l10n.get("feedback_category_audio_quality"),
|
||||
video_quality: l10n.get("feedback_category_video_quality"),
|
||||
disconnected : l10n.get("feedback_category_was_disconnected"),
|
||||
confusing: l10n.get("feedback_category_confusing"),
|
||||
other: l10n.get("feedback_category_other")
|
||||
};
|
||||
},
|
||||
|
||||
@ -420,7 +419,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
React.DOM.label({key: key},
|
||||
React.DOM.input({type: "radio", ref: "category", name: "category",
|
||||
value: category,
|
||||
onChange: this.handleCategoryChange}),
|
||||
onChange: this.handleCategoryChange,
|
||||
checked: this.state.category === category}),
|
||||
categories[category]
|
||||
)
|
||||
);
|
||||
@ -429,28 +429,43 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
|
||||
/**
|
||||
* Checks if the form is ready for submission:
|
||||
* - a category (reason) must be chosen
|
||||
* - no feedback submission should be pending
|
||||
*
|
||||
* - no feedback submission should be pending.
|
||||
* - a category (reason) must be chosen;
|
||||
* - if the "other" category is chosen, a custom description must have been
|
||||
* entered by the end user;
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
_isFormReady: function() {
|
||||
return this.state.category !== "" && !this.props.pending;
|
||||
if (this.props.pending || !this.state.category) {
|
||||
return false;
|
||||
}
|
||||
if (this.state.category === "other" && !this.state.description) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
handleCategoryChange: function(event) {
|
||||
var category = event.target.value;
|
||||
if (category !== "other") {
|
||||
// resets description text field
|
||||
this.setState({description: ""});
|
||||
this.setState({
|
||||
category: category,
|
||||
description: category == "other" ? "" : this._getCategories()[category]
|
||||
});
|
||||
if (category == "other") {
|
||||
this.refs.description.getDOMNode().focus();
|
||||
}
|
||||
this.setState({category: category});
|
||||
},
|
||||
|
||||
handleCustomTextChange: function(event) {
|
||||
handleDescriptionFieldChange: function(event) {
|
||||
this.setState({description: event.target.value});
|
||||
},
|
||||
|
||||
handleDescriptionFieldFocus: function(event) {
|
||||
this.setState({category: "other", description: ""});
|
||||
},
|
||||
|
||||
handleFormSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
this.props.sendFeedback({
|
||||
@ -461,18 +476,24 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var descriptionDisplayValue = this.state.category === "other" ?
|
||||
this.state.description : "";
|
||||
return (
|
||||
FeedbackLayout({title: __("feedback_what_makes_you_sad"),
|
||||
FeedbackLayout({title: l10n.get("feedback_what_makes_you_sad"),
|
||||
reset: this.props.reset},
|
||||
React.DOM.form({onSubmit: this.handleFormSubmit},
|
||||
this._getCategoryFields(),
|
||||
React.DOM.p(null, React.DOM.input({type: "text", ref: "description", name: "description",
|
||||
disabled: this.state.category !== "other",
|
||||
onChange: this.handleCustomTextChange,
|
||||
value: this.state.description})),
|
||||
React.DOM.p(null,
|
||||
React.DOM.input({type: "text", ref: "description", name: "description",
|
||||
onChange: this.handleDescriptionFieldChange,
|
||||
onFocus: this.handleDescriptionFieldFocus,
|
||||
value: descriptionDisplayValue,
|
||||
placeholder:
|
||||
l10n.get("feedback_custom_category_text_placeholder")})
|
||||
),
|
||||
React.DOM.button({type: "submit", className: "btn btn-success",
|
||||
disabled: !this._isFormReady()},
|
||||
__("feedback_submit_button")
|
||||
l10n.get("feedback_submit_button")
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -506,10 +527,11 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
window.close();
|
||||
}
|
||||
return (
|
||||
FeedbackLayout({title: __("feedback_thank_you_heading")},
|
||||
React.DOM.p({className: "info thank-you"}, __("feedback_window_will_close_in", {
|
||||
countdown: this.state.countdown
|
||||
}))
|
||||
FeedbackLayout({title: l10n.get("feedback_thank_you_heading")},
|
||||
React.DOM.p({className: "info thank-you"},
|
||||
l10n.get("feedback_window_will_close_in", {
|
||||
countdown: this.state.countdown
|
||||
}))
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -573,7 +595,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
pending: this.state.pending});
|
||||
default:
|
||||
return (
|
||||
FeedbackLayout({title: __("feedback_call_experience_heading")},
|
||||
FeedbackLayout({title:
|
||||
l10n.get("feedback_call_experience_heading")},
|
||||
React.DOM.div({className: "faces"},
|
||||
React.DOM.button({className: "face face-happy",
|
||||
onClick: this.handleHappyClick}),
|
||||
|
@ -12,7 +12,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
"use strict";
|
||||
|
||||
var sharedModels = loop.shared.models;
|
||||
var __ = l10n.get;
|
||||
var WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS = 5;
|
||||
|
||||
/**
|
||||
@ -134,7 +133,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
var prefix = this.props.enabled ? "mute" : "unmute";
|
||||
var suffix = "button_title";
|
||||
var msgId = [prefix, this.props.scope, this.props.type, suffix].join("_");
|
||||
return __(msgId);
|
||||
return l10n.get(msgId);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -184,7 +183,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
<ul className="conversation-toolbar">
|
||||
<li><button className="btn btn-hangup"
|
||||
onClick={this.handleClickHangup}
|
||||
title={__("hangup_button_title")}></button></li>
|
||||
title={l10n.get("hangup_button_title")}></button></li>
|
||||
<li><MediaControlButton action={this.handleToggleVideo}
|
||||
enabled={this.props.video.enabled}
|
||||
scope="local" type="video" /></li>
|
||||
@ -371,7 +370,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
if (this.props.reset) {
|
||||
backButton = (
|
||||
<button className="back" type="button" onClick={this.props.reset}>
|
||||
« {__("feedback_back_button")}
|
||||
« {l10n.get("feedback_back_button")}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@ -405,11 +404,11 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
|
||||
_getCategories: function() {
|
||||
return {
|
||||
audio_quality: __("feedback_category_audio_quality"),
|
||||
video_quality: __("feedback_category_video_quality"),
|
||||
disconnected : __("feedback_category_was_disconnected"),
|
||||
confusing: __("feedback_category_confusing"),
|
||||
other: __("feedback_category_other")
|
||||
audio_quality: l10n.get("feedback_category_audio_quality"),
|
||||
video_quality: l10n.get("feedback_category_video_quality"),
|
||||
disconnected : l10n.get("feedback_category_was_disconnected"),
|
||||
confusing: l10n.get("feedback_category_confusing"),
|
||||
other: l10n.get("feedback_category_other")
|
||||
};
|
||||
},
|
||||
|
||||
@ -420,7 +419,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
<label key={key}>
|
||||
<input type="radio" ref="category" name="category"
|
||||
value={category}
|
||||
onChange={this.handleCategoryChange} />
|
||||
onChange={this.handleCategoryChange}
|
||||
checked={this.state.category === category} />
|
||||
{categories[category]}
|
||||
</label>
|
||||
);
|
||||
@ -429,28 +429,43 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
|
||||
/**
|
||||
* Checks if the form is ready for submission:
|
||||
* - a category (reason) must be chosen
|
||||
* - no feedback submission should be pending
|
||||
*
|
||||
* - no feedback submission should be pending.
|
||||
* - a category (reason) must be chosen;
|
||||
* - if the "other" category is chosen, a custom description must have been
|
||||
* entered by the end user;
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
_isFormReady: function() {
|
||||
return this.state.category !== "" && !this.props.pending;
|
||||
if (this.props.pending || !this.state.category) {
|
||||
return false;
|
||||
}
|
||||
if (this.state.category === "other" && !this.state.description) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
handleCategoryChange: function(event) {
|
||||
var category = event.target.value;
|
||||
if (category !== "other") {
|
||||
// resets description text field
|
||||
this.setState({description: ""});
|
||||
this.setState({
|
||||
category: category,
|
||||
description: category == "other" ? "" : this._getCategories()[category]
|
||||
});
|
||||
if (category == "other") {
|
||||
this.refs.description.getDOMNode().focus();
|
||||
}
|
||||
this.setState({category: category});
|
||||
},
|
||||
|
||||
handleCustomTextChange: function(event) {
|
||||
handleDescriptionFieldChange: function(event) {
|
||||
this.setState({description: event.target.value});
|
||||
},
|
||||
|
||||
handleDescriptionFieldFocus: function(event) {
|
||||
this.setState({category: "other", description: ""});
|
||||
},
|
||||
|
||||
handleFormSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
this.props.sendFeedback({
|
||||
@ -461,18 +476,24 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var descriptionDisplayValue = this.state.category === "other" ?
|
||||
this.state.description : "";
|
||||
return (
|
||||
<FeedbackLayout title={__("feedback_what_makes_you_sad")}
|
||||
<FeedbackLayout title={l10n.get("feedback_what_makes_you_sad")}
|
||||
reset={this.props.reset}>
|
||||
<form onSubmit={this.handleFormSubmit}>
|
||||
{this._getCategoryFields()}
|
||||
<p><input type="text" ref="description" name="description"
|
||||
disabled={this.state.category !== "other"}
|
||||
onChange={this.handleCustomTextChange}
|
||||
value={this.state.description} /></p>
|
||||
<p>
|
||||
<input type="text" ref="description" name="description"
|
||||
onChange={this.handleDescriptionFieldChange}
|
||||
onFocus={this.handleDescriptionFieldFocus}
|
||||
value={descriptionDisplayValue}
|
||||
placeholder={
|
||||
l10n.get("feedback_custom_category_text_placeholder")} />
|
||||
</p>
|
||||
<button type="submit" className="btn btn-success"
|
||||
disabled={!this._isFormReady()}>
|
||||
{__("feedback_submit_button")}
|
||||
{l10n.get("feedback_submit_button")}
|
||||
</button>
|
||||
</form>
|
||||
</FeedbackLayout>
|
||||
@ -506,10 +527,11 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
window.close();
|
||||
}
|
||||
return (
|
||||
<FeedbackLayout title={__("feedback_thank_you_heading")}>
|
||||
<p className="info thank-you">{__("feedback_window_will_close_in", {
|
||||
countdown: this.state.countdown
|
||||
})}</p>
|
||||
<FeedbackLayout title={l10n.get("feedback_thank_you_heading")}>
|
||||
<p className="info thank-you">{
|
||||
l10n.get("feedback_window_will_close_in", {
|
||||
countdown: this.state.countdown
|
||||
})}</p>
|
||||
</FeedbackLayout>
|
||||
);
|
||||
}
|
||||
@ -573,7 +595,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
pending={this.state.pending} />;
|
||||
default:
|
||||
return (
|
||||
<FeedbackLayout title={__("feedback_call_experience_heading")}>
|
||||
<FeedbackLayout title={
|
||||
l10n.get("feedback_call_experience_heading")}>
|
||||
<div className="faces">
|
||||
<button className="face face-happy"
|
||||
onClick={this.handleHappyClick}></button>
|
||||
|
@ -48,7 +48,8 @@ describe("loop.panel", function() {
|
||||
return "en-US";
|
||||
},
|
||||
setLoopCharPref: sandbox.stub(),
|
||||
getLoopCharPref: sandbox.stub().returns("unseen")
|
||||
getLoopCharPref: sandbox.stub().returns("unseen"),
|
||||
copyString: sandbox.stub()
|
||||
};
|
||||
|
||||
document.mozL10n.initialize(navigator.mozLoop);
|
||||
@ -314,9 +315,28 @@ describe("loop.panel", function() {
|
||||
}));
|
||||
view.setState({pending: false, callUrl: "http://example.com"});
|
||||
|
||||
TestUtils.findRenderedDOMComponentWithTag(view, "a");
|
||||
var shareButton = view.getDOMNode().querySelector("a.btn");
|
||||
expect(shareButton.href).to.equal(encodeURI(mailto));
|
||||
TestUtils.findRenderedDOMComponentWithClass(view, "btn-email");
|
||||
expect(view.getDOMNode().querySelector(".btn-email").dataset.mailto)
|
||||
.to.equal(encodeURI(mailto));
|
||||
});
|
||||
|
||||
it("should feature a copy button capable of copying the call url when clicked", function() {
|
||||
fakeClient.requestCallUrl = sandbox.stub();
|
||||
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||
notifier: notifier,
|
||||
client: fakeClient
|
||||
}));
|
||||
view.setState({
|
||||
pending: false,
|
||||
copied: false,
|
||||
callUrl: "http://example.com"
|
||||
});
|
||||
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".btn-copy"));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.copyString);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.copyString,
|
||||
view.state.callUrl);
|
||||
});
|
||||
|
||||
it("should notify the user when the operation failed", function() {
|
||||
|
@ -405,6 +405,9 @@ describe("loop.shared.views", function() {
|
||||
var comp, fakeFeedbackApiClient;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(l10n, "get", function(x) {
|
||||
return x;
|
||||
});
|
||||
fakeFeedbackApiClient = {send: sandbox.stub()};
|
||||
comp = TestUtils.renderIntoDocument(sharedViews.FeedbackView({
|
||||
feedbackApiClient: fakeFeedbackApiClient
|
||||
@ -476,7 +479,39 @@ describe("loop.shared.views", function() {
|
||||
.querySelector("form button").disabled).eql(true);
|
||||
});
|
||||
|
||||
it("should enable the form submit button once a choice is made",
|
||||
it("should disable the form submit button when the 'other' category is " +
|
||||
"chosen but no description has been entered yet",
|
||||
function() {
|
||||
clickSadFace(comp);
|
||||
fillSadFeedbackForm(comp, "other");
|
||||
|
||||
expect(comp.getDOMNode()
|
||||
.querySelector("form button").disabled).eql(true);
|
||||
});
|
||||
|
||||
it("should enable the form submit button when the 'other' category is " +
|
||||
"chosen and a description is entered",
|
||||
function() {
|
||||
clickSadFace(comp);
|
||||
fillSadFeedbackForm(comp, "other", "fake");
|
||||
|
||||
expect(comp.getDOMNode()
|
||||
.querySelector("form button").disabled).eql(false);
|
||||
});
|
||||
|
||||
it("should empty the description field when a predefined category is " +
|
||||
"chosen",
|
||||
function() {
|
||||
clickSadFace(comp);
|
||||
|
||||
fillSadFeedbackForm(comp, "confusing");
|
||||
|
||||
expect(comp.getDOMNode()
|
||||
.querySelector("form input[type='text']").value).eql("");
|
||||
});
|
||||
|
||||
it("should enable the form submit button once a predefined category is " +
|
||||
"chosen",
|
||||
function() {
|
||||
clickSadFace(comp);
|
||||
|
||||
|
@ -4,11 +4,12 @@
|
||||
|
||||
/**
|
||||
* /!\ FIXME: THIS IS A HORRID HACK which fakes both the mozL10n and webL10n
|
||||
* objects and makes them returning "fake string" for any requested string id.
|
||||
* objects and makes them returning the string id and serialized vars if any,
|
||||
* for any requested string id.
|
||||
* @type {Object}
|
||||
*/
|
||||
document.webL10n = document.mozL10n = {
|
||||
get: function() {
|
||||
return "fake text";
|
||||
get: function(sringId, vars) {
|
||||
return "" + sringId + (vars ? ";" + JSON.stringify(vars) : "");
|
||||
}
|
||||
};
|
||||
|
@ -93,8 +93,14 @@
|
||||
return (
|
||||
ShowCase(null,
|
||||
Section({name: "PanelView"},
|
||||
Example({summary: "332px wide", dashed: "true", style: {width: "332px"}},
|
||||
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/"})
|
||||
)
|
||||
),
|
||||
|
||||
|
@ -93,9 +93,15 @@
|
||||
return (
|
||||
<ShowCase>
|
||||
<Section name="PanelView">
|
||||
<Example summary="332px wide" dashed="true" style={{width: "332px"}}>
|
||||
<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/" />
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
<Section name="IncomingCallView">
|
||||
|
@ -8,30 +8,31 @@
|
||||
<?xml-stylesheet href="chrome://mozapps/content/preferences/preferences.css"?>
|
||||
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
|
||||
<?xml-stylesheet
|
||||
<?xml-stylesheet href="chrome://browser/skin/in-content/common.css"?>
|
||||
<?xml-stylesheet
|
||||
href="chrome://browser/skin/preferences/in-content/preferences.css"?>
|
||||
<?xml-stylesheet
|
||||
<?xml-stylesheet
|
||||
href="chrome://browser/content/preferences/handlers.css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
|
||||
|
||||
<!DOCTYPE page [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
<!ENTITY % globalPreferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
|
||||
<!ENTITY % preferencesDTD SYSTEM
|
||||
<!ENTITY % preferencesDTD SYSTEM
|
||||
"chrome://browser/locale/preferences/preferences.dtd">
|
||||
<!ENTITY % privacyDTD SYSTEM "chrome://browser/locale/preferences/privacy.dtd">
|
||||
<!ENTITY % tabsDTD SYSTEM "chrome://browser/locale/preferences/tabs.dtd">
|
||||
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
|
||||
<!ENTITY % syncDTD SYSTEM "chrome://browser/locale/preferences/sync.dtd">
|
||||
<!ENTITY % securityDTD SYSTEM
|
||||
<!ENTITY % securityDTD SYSTEM
|
||||
"chrome://browser/locale/preferences/security.dtd">
|
||||
<!ENTITY % sanitizeDTD SYSTEM "chrome://browser/locale/sanitize.dtd">
|
||||
<!ENTITY % mainDTD SYSTEM "chrome://browser/locale/preferences/main.dtd">
|
||||
<!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
|
||||
<!ENTITY % contentDTD SYSTEM "chrome://browser/locale/preferences/content.dtd">
|
||||
<!ENTITY % applicationsDTD SYSTEM
|
||||
<!ENTITY % applicationsDTD SYSTEM
|
||||
"chrome://browser/locale/preferences/applications.dtd">
|
||||
<!ENTITY % advancedDTD SYSTEM
|
||||
<!ENTITY % advancedDTD SYSTEM
|
||||
"chrome://browser/locale/preferences/advanced.dtd">
|
||||
%brandDTD;
|
||||
%globalPreferencesDTD;
|
||||
@ -70,7 +71,7 @@
|
||||
src="chrome://browser/content/preferences/in-content/preferences.js"/>
|
||||
<script src="chrome://browser/content/preferences/in-content/subdialogs.js"/>
|
||||
|
||||
<stringbundle id="bundleBrand"
|
||||
<stringbundle id="bundleBrand"
|
||||
src="chrome://branding/locale/brand.properties"/>
|
||||
<stringbundle id="bundlePreferences"
|
||||
src="chrome://browser/locale/preferences/preferences.properties"/>
|
||||
|
@ -11,6 +11,7 @@ let gSubDialog = {
|
||||
_box: null,
|
||||
_injectedStyleSheets: ["chrome://mozapps/content/preferences/preferences.css",
|
||||
"chrome://browser/skin/preferences/preferences.css",
|
||||
"chrome://browser/skin/in-content/common.css",
|
||||
"chrome://browser/skin/preferences/in-content/preferences.css"],
|
||||
|
||||
init: function() {
|
||||
|
@ -9,7 +9,6 @@ support-files =
|
||||
[browser_devtools_api.js]
|
||||
[browser_dynamic_tool_enabling.js]
|
||||
[browser_keybindings.js]
|
||||
skip-if = e10s # Bug 1030318
|
||||
[browser_new_activation_workflow.js]
|
||||
[browser_target_events.js]
|
||||
[browser_target_remote.js]
|
||||
@ -19,6 +18,7 @@ skip-if = e10s # Bug 1030318
|
||||
[browser_toolbox_options.js]
|
||||
[browser_toolbox_options_disable_buttons.js]
|
||||
[browser_toolbox_options_disable_cache.js]
|
||||
skip-if = e10s # Bug 1030318
|
||||
[browser_toolbox_options_disable_js.js]
|
||||
# [browser_toolbox_raise.js] # Bug 962258
|
||||
# skip-if = os == "win"
|
||||
|
@ -18,7 +18,7 @@ function test()
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
doc = content.document;
|
||||
node = doc.querySelector("h1");
|
||||
waitForFocus(setupKeyBindingsTest, content);
|
||||
waitForFocus(setupKeyBindingsTest);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,<html><head><title>Test for the " +
|
||||
|
@ -32,7 +32,8 @@ function onHidden() {
|
||||
function onVisible() {
|
||||
ok(true, "Visible event received");
|
||||
target.once("will-navigate", onWillNavigate);
|
||||
gBrowser.contentWindow.location = "data:text/html,test navigation";
|
||||
let mm = getFrameScript();
|
||||
mm.sendAsyncMessage("devtools:test:navigate", { location: "data:text/html,<meta charset='utf8'/>test navigation" });
|
||||
}
|
||||
|
||||
function onWillNavigate(event, request) {
|
||||
|
@ -131,7 +131,8 @@ function reloadTab(tabX) {
|
||||
}, true);
|
||||
|
||||
info("Reloading tab " + tabX.title);
|
||||
content.document.location.reload(false);
|
||||
let mm = getFrameScript();
|
||||
mm.sendAsyncMessage("devtools:test:reload");
|
||||
|
||||
return def.promise;
|
||||
}
|
||||
@ -139,15 +140,17 @@ function reloadTab(tabX) {
|
||||
function* destroyTab(tabX) {
|
||||
let toolbox = gDevTools.getToolbox(tabX.target);
|
||||
|
||||
let onceDestroyed = promise.resolve();
|
||||
if (toolbox) {
|
||||
onceDestroyed = gDevTools.once("toolbox-destroyed");
|
||||
}
|
||||
|
||||
info("Removing tab " + tabX.title);
|
||||
gBrowser.removeTab(tabX.tab);
|
||||
info("Removed tab " + tabX.title);
|
||||
|
||||
if (toolbox) {
|
||||
info("Waiting for toolbox-destroyed");
|
||||
yield gDevTools.once("toolbox-destroyed");
|
||||
info("toolbox-destroyed event received for " + tabX.title);
|
||||
}
|
||||
info("Waiting for toolbox-destroyed");
|
||||
yield onceDestroyed;
|
||||
}
|
||||
|
||||
function* finishUp() {
|
||||
|
@ -32,13 +32,11 @@ function testJSEnabled(event, tool, secondPass) {
|
||||
ok(true, "Toolbox selected via selectTool method");
|
||||
info("Testing that JS is enabled");
|
||||
|
||||
let logJSEnabled = doc.getElementById("logJSEnabled");
|
||||
let output = doc.getElementById("output");
|
||||
|
||||
// We use executeSoon here because switching docSehll.allowJavascript to true
|
||||
// takes a while to become live.
|
||||
executeSoon(function() {
|
||||
EventUtils.synthesizeMouseAtCenter(logJSEnabled, {}, doc.defaultView);
|
||||
let output = doc.getElementById("output");
|
||||
doc.querySelector("#logJSEnabled").click();
|
||||
is(output.textContent, "JavaScript Enabled", 'Output is "JavaScript Enabled"');
|
||||
testJSEnabledIframe(secondPass);
|
||||
});
|
||||
@ -49,12 +47,10 @@ function testJSEnabledIframe(secondPass) {
|
||||
|
||||
let iframe = doc.querySelector("iframe");
|
||||
let iframeDoc = iframe.contentDocument;
|
||||
let logJSEnabled = iframeDoc.getElementById("logJSEnabled");
|
||||
let output = iframeDoc.getElementById("output");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(logJSEnabled, {}, iframe.contentWindow);
|
||||
iframeDoc.querySelector("#logJSEnabled").click();
|
||||
is(output.textContent, "JavaScript Enabled",
|
||||
'Output is "JavaScript Enabled" in iframe');
|
||||
'Output is "JavaScript Enabled" in iframe');
|
||||
if (secondPass) {
|
||||
finishUp();
|
||||
} else {
|
||||
@ -75,18 +71,13 @@ function toggleJS() {
|
||||
info("Checking checkbox to disable JS");
|
||||
}
|
||||
|
||||
// After uising scrollIntoView() we need to use executeSoon() to wait for the
|
||||
// browser to scroll.
|
||||
executeSoon(function() {
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
||||
doc = content.document;
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
||||
doc = content.document;
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(cbx, {}, panel.panelWin);
|
||||
});
|
||||
cbx.click();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
@ -94,13 +85,11 @@ function toggleJS() {
|
||||
function testJSDisabled() {
|
||||
info("Testing that JS is disabled");
|
||||
|
||||
let logJSDisabled = doc.getElementById("logJSDisabled");
|
||||
let output = doc.getElementById("output");
|
||||
doc.querySelector("#logJSDisabled").click();
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(logJSDisabled, {}, doc.defaultView);
|
||||
ok(output.textContent !== "JavaScript Disabled",
|
||||
'output is not "JavaScript Disabled"');
|
||||
|
||||
testJSDisabledIframe();
|
||||
}
|
||||
|
||||
@ -109,10 +98,8 @@ function testJSDisabledIframe() {
|
||||
|
||||
let iframe = doc.querySelector("iframe");
|
||||
let iframeDoc = iframe.contentDocument;
|
||||
let logJSDisabled = iframeDoc.getElementById("logJSDisabled");
|
||||
let output = iframeDoc.getElementById("output");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(logJSDisabled, {}, iframe.contentWindow);
|
||||
iframeDoc.querySelector("#logJSDisabled").click();
|
||||
ok(output.textContent !== "JavaScript Disabled",
|
||||
'output is not "JavaScript Disabled" in iframe');
|
||||
toggleJS().then(function() {
|
||||
|
@ -16,6 +16,16 @@ waitForExplicitFinish();
|
||||
// Uncomment this pref to dump all devtools emitted events to the console.
|
||||
// Services.prefs.setBoolPref("devtools.dump.emit", true);
|
||||
|
||||
function getFrameScript() {
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
let frameURL = "chrome://browser/content/devtools/frame-script-utils.js";
|
||||
mm.loadFrameScript(frameURL, false);
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
mm = null;
|
||||
});
|
||||
return mm;
|
||||
}
|
||||
|
||||
gDevTools.testing = true;
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
gDevTools.testing = false;
|
||||
|
@ -1356,16 +1356,12 @@ Toolbox.prototype = {
|
||||
}
|
||||
|
||||
// Destroying the walker and inspector fronts
|
||||
outstanding.push(this.destroyInspector());
|
||||
// Removing buttons
|
||||
outstanding.push(() => {
|
||||
outstanding.push(this.destroyInspector().then(() => {
|
||||
// Removing buttons
|
||||
this._pickerButton.removeEventListener("command", this._togglePicker, false);
|
||||
this._pickerButton = null;
|
||||
let container = this.doc.getElementById("toolbox-buttons");
|
||||
while (container.firstChild) {
|
||||
container.removeChild(container.firstChild);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
// Remove the host UI
|
||||
outstanding.push(this.destroyHost());
|
||||
|
||||
|
@ -7,3 +7,12 @@
|
||||
addMessageListener("devtools:test:history", function ({ data }) {
|
||||
content.history[data.direction]();
|
||||
});
|
||||
|
||||
addMessageListener("devtools:test:navigate", function ({ data }) {
|
||||
content.location = data.location;
|
||||
});
|
||||
|
||||
addMessageListener("devtools:test:reload", function ({ data }) {
|
||||
data = data || {};
|
||||
content.location.reload(data.forceget);
|
||||
});
|
||||
|
@ -1483,6 +1483,10 @@ CssRuleView.prototype = {
|
||||
|
||||
// Remove context menu
|
||||
if (this._contextmenu) {
|
||||
// Destroy the Add Rule menuitem.
|
||||
this.menuitemAddRule.removeEventListener("command", this._onAddRule);
|
||||
this.menuitemAddRule = null;
|
||||
|
||||
// Destroy the Select All menuitem.
|
||||
this.menuitemSelectAll.removeEventListener("command", this._onSelectAll);
|
||||
this.menuitemSelectAll = null;
|
||||
|
@ -6,6 +6,7 @@ const Cu = Components.utils;
|
||||
Cu.import('resource:///modules/devtools/gDevTools.jsm');
|
||||
const {require} = Cu.import('resource://gre/modules/devtools/Loader.jsm', {}).devtools;
|
||||
const {Services} = Cu.import('resource://gre/modules/Services.jsm');
|
||||
const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
|
||||
const {AppManager} = require('devtools/webide/app-manager');
|
||||
const {AppActorFront} = require('devtools/app-actor-front');
|
||||
const {Connection} = require('devtools/client/connection-manager');
|
||||
@ -45,7 +46,8 @@ let Monitor = {
|
||||
front: null,
|
||||
socket: null,
|
||||
wstimeout: null,
|
||||
loaded: false,
|
||||
b2ginfo: false,
|
||||
b2gtimeout: null,
|
||||
|
||||
/**
|
||||
* Add new data to the graphs, create a new graph if necessary.
|
||||
@ -56,6 +58,12 @@ let Monitor = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Monitor.b2ginfo && data.graph === 'USS') {
|
||||
// If we're polling b2g-info, ignore USS updates from the device's
|
||||
// USSAgents (see Monitor.pollB2GInfo()).
|
||||
return;
|
||||
}
|
||||
|
||||
if (fallback) {
|
||||
for (let key in fallback) {
|
||||
if (!data[key]) {
|
||||
@ -84,7 +92,6 @@ let Monitor = {
|
||||
AppManager.on('app-manager-update', Monitor.onAppManagerUpdate);
|
||||
Monitor.connectToRuntime();
|
||||
Monitor.connectToWebSocket();
|
||||
Monitor.loaded = true;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -125,6 +132,7 @@ let Monitor = {
|
||||
* Use an AppActorFront on a runtime to watch track its apps.
|
||||
*/
|
||||
connectToRuntime: function() {
|
||||
Monitor.pollB2GInfo();
|
||||
let client = AppManager.connection && AppManager.connection.client;
|
||||
let resp = AppManager._listTabsResponse;
|
||||
if (client && resp && !Monitor.front) {
|
||||
@ -137,6 +145,7 @@ let Monitor = {
|
||||
* Destroy our AppActorFront.
|
||||
*/
|
||||
disconnectFromRuntime: function() {
|
||||
Monitor.unpollB2GInfo();
|
||||
if (Monitor.front) {
|
||||
Monitor.front.unwatchApps(Monitor.onRuntimeAppEvent);
|
||||
Monitor.front = null;
|
||||
@ -206,6 +215,66 @@ let Monitor = {
|
||||
fallback.curve = app.manifest.name
|
||||
}
|
||||
Monitor.update(packet.data, fallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Bug 1047355: If possible, parsing the output of `b2g-info` has several
|
||||
* benefits over bug 1037465's multi-process USSAgent approach, notably:
|
||||
* - Works for older Firefox OS devices (pre-2.1),
|
||||
* - Doesn't need certified-apps debugging,
|
||||
* - Polling time is synchronized for all processes.
|
||||
* TODO: After bug 1043324 lands, consider removing this hack.
|
||||
*/
|
||||
pollB2GInfo: function() {
|
||||
if (AppManager.selectedRuntime) {
|
||||
let id = AppManager.selectedRuntime.id;
|
||||
let device = Devices.getByName(id);
|
||||
if (device && device.shell) {
|
||||
device.shell('b2g-info').then(s => {
|
||||
let lines = s.split('\n');
|
||||
let line = '';
|
||||
|
||||
// Find the header row to locate NAME and USS, looks like:
|
||||
// ' NAME PID NICE USS PSS RSS VSIZE OOM_ADJ USER '.
|
||||
while (line.indexOf('NAME') < 0) {
|
||||
if (lines.length < 1) {
|
||||
// Something is wrong with this output, don't trust b2g-info.
|
||||
Monitor.unpollB2GInfo();
|
||||
return;
|
||||
}
|
||||
line = lines.shift();
|
||||
}
|
||||
let namelength = line.indexOf('NAME') + 'NAME'.length;
|
||||
let ussindex = line.slice(namelength).split(/\s+/).indexOf('USS');
|
||||
|
||||
// Get the NAME and USS in each following line, looks like:
|
||||
// 'Homescreen 375 18 12.6 16.3 27.1 67.8 4 app_375'.
|
||||
while (lines.length > 0 && lines[0].length > namelength) {
|
||||
line = lines.shift();
|
||||
let name = line.slice(0, namelength);
|
||||
let uss = line.slice(namelength).split(/\s+/)[ussindex];
|
||||
Monitor.update({
|
||||
curve: name.trim(),
|
||||
value: 1024 * 1024 * parseFloat(uss) // Convert MB to bytes.
|
||||
}, {
|
||||
// Note: We use the fallback object to set the graph name to 'USS'
|
||||
// so that Monitor.update() can ignore USSAgent updates.
|
||||
graph: 'USS'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Monitor.b2ginfo = true;
|
||||
Monitor.b2gtimeout = setTimeout(Monitor.pollB2GInfo, 350);
|
||||
},
|
||||
|
||||
/**
|
||||
* Polling b2g-info doesn't work or is no longer needed.
|
||||
*/
|
||||
unpollB2GInfo: function() {
|
||||
clearTimeout(Monitor.b2gtimeout);
|
||||
Monitor.b2ginfo = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -208,10 +208,8 @@ let UI = {
|
||||
setupBusyTimeout: function() {
|
||||
this.cancelBusyTimeout();
|
||||
this._busyTimeout = setTimeout(() => {
|
||||
let busyPromise = this._busyPromise;
|
||||
this.unbusy();
|
||||
UI.reportError("error_operationTimeout", this._busyOperationDescription);
|
||||
busyPromise.reject("promise timeout: " + this._busyOperationDescription);
|
||||
}, 30000);
|
||||
},
|
||||
|
||||
|
@ -173,6 +173,7 @@
|
||||
#endif
|
||||
@BINPATH@/components/content_xslt.xpt
|
||||
@BINPATH@/components/cookie.xpt
|
||||
@BINPATH@/components/devtools_security.xpt
|
||||
@BINPATH@/components/directory.xpt
|
||||
@BINPATH@/components/docshell.xpt
|
||||
@BINPATH@/components/dom.xpt
|
||||
|
@ -50,6 +50,7 @@ feedback_category_video_quality=Video quality
|
||||
feedback_category_was_disconnected=Was disconnected
|
||||
feedback_category_confusing=Confusing
|
||||
feedback_category_other=Other:
|
||||
feedback_custom_category_text_placeholder=What went wrong?
|
||||
feedback_submit_button=Submit
|
||||
feedback_back_button=Back
|
||||
## LOCALIZATION NOTE (feedback_window_will_close_in): In this item, don't
|
||||
@ -61,4 +62,5 @@ feedback_window_will_close_in=This window will close in {{countdown}} seconds
|
||||
share_email_subject=Loop invitation to chat
|
||||
share_email_body=Please click that link to call me back:\r\n\r\n{{callUrl}}
|
||||
share_button=Email
|
||||
|
||||
copy_url_button=Copy
|
||||
copied_url_button=Copied!
|
||||
|
62
browser/themes/linux/in-content/common.css
Normal file
@ -0,0 +1,62 @@
|
||||
/* - This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
- You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
%include ../../shared/in-content/common.inc.css
|
||||
|
||||
xul|tab[selected] {
|
||||
/* Override styles for tab[selected] from
|
||||
toolkit/themes/linux/global/tabbox.css */
|
||||
margin-bottom: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
xul|button,
|
||||
xul|colorpicker[type="button"],
|
||||
xul|menulist {
|
||||
margin: 2px 4px;
|
||||
}
|
||||
|
||||
xul|button > xul|*.button-box,
|
||||
xul|menulist > xul|*.menulist-label-box {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker {
|
||||
-moz-appearance: none !important;
|
||||
}
|
||||
|
||||
xul|*.help-button > xul|*.button-box > xul|*.button-icon {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
xul|menulist {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker {
|
||||
display: -moz-box;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
xul|checkbox {
|
||||
-moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox");
|
||||
-moz-box-align: center;
|
||||
}
|
||||
|
||||
xul|radio {
|
||||
-moz-binding: url("chrome://global/content/bindings/radio.xml#radio");
|
||||
-moz-box-align: center;
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
xul|*.radio-label-box {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
xul|*.numberbox-input-box {
|
||||
-moz-appearance: none;
|
||||
border-width: 0;
|
||||
}
|
@ -35,7 +35,17 @@ browser.jar:
|
||||
skin/classic/browser/identity-icons-https-mixed-active.png
|
||||
skin/classic/browser/identity-icons-https-mixed-display.png
|
||||
skin/classic/browser/Info.png
|
||||
skin/classic/browser/in-content/common.css (../shared/in-content/common.css)
|
||||
* skin/classic/browser/in-content/common.css (in-content/common.css)
|
||||
skin/classic/browser/in-content/check.png (../shared/in-content/check.png)
|
||||
skin/classic/browser/in-content/check@2x.png (../shared/in-content/check@2x.png)
|
||||
skin/classic/browser/in-content/dropdown.png (../shared/in-content/dropdown.png)
|
||||
skin/classic/browser/in-content/dropdown@2x.png (../shared/in-content/dropdown@2x.png)
|
||||
skin/classic/browser/in-content/dropdown-disabled.png (../shared/in-content/dropdown-disabled.png)
|
||||
skin/classic/browser/in-content/dropdown-disabled@2x.png (../shared/in-content/dropdown-disabled@2x.png)
|
||||
skin/classic/browser/in-content/help-glyph.png (../shared/in-content/help-glyph.png)
|
||||
skin/classic/browser/in-content/help-glyph@2x.png (../shared/in-content/help-glyph@2x.png)
|
||||
skin/classic/browser/in-content/sorter.png (../shared/in-content/sorter.png)
|
||||
skin/classic/browser/in-content/sorter@2x.png (../shared/in-content/sorter@2x.png)
|
||||
skin/classic/browser/menuPanel.png
|
||||
skin/classic/browser/menuPanel-customize.png
|
||||
skin/classic/browser/menuPanel-exit.png
|
||||
@ -150,18 +160,8 @@ browser.jar:
|
||||
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
|
||||
skin/classic/browser/preferences/in-content/check.png (../shared/incontentprefs/check.png)
|
||||
skin/classic/browser/preferences/in-content/check@2x.png (../shared/incontentprefs/check@2x.png)
|
||||
skin/classic/browser/preferences/in-content/icons.png (../shared/incontentprefs/icons.png)
|
||||
skin/classic/browser/preferences/in-content/icons@2x.png (../shared/incontentprefs/icons@2x.png)
|
||||
skin/classic/browser/preferences/in-content/help-glyph.png (../shared/incontentprefs/help-glyph.png)
|
||||
skin/classic/browser/preferences/in-content/help-glyph@2x.png (../shared/incontentprefs/help-glyph@2x.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown.png (../shared/incontentprefs/dropdown.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown@2x.png (../shared/incontentprefs/dropdown@2x.png)
|
||||
skin/classic/browser/preferences/in-content/sorter.png (../shared/incontentprefs/sorter.png)
|
||||
skin/classic/browser/preferences/in-content/sorter@2x.png (../shared/incontentprefs/sorter@2x.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown-disabled.png (../shared/incontentprefs/dropdown-disabled.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown-disabled@2x.png (../shared/incontentprefs/dropdown-disabled@2x.png)
|
||||
skin/classic/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/browser/social/services-16.png (social/services-16.png)
|
||||
|
@ -4,63 +4,6 @@
|
||||
|
||||
%include ../../../shared/incontentprefs/preferences.css
|
||||
|
||||
tab[selected] {
|
||||
/* Override styles for tab[selected] from
|
||||
toolkit/themes/linux/global/tabbox.css */
|
||||
margin-bottom: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
button,
|
||||
colorpicker[type="button"],
|
||||
menulist {
|
||||
margin: 2px 4px;
|
||||
}
|
||||
|
||||
button > .button-box,
|
||||
menulist > .menulist-label-box {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
button[type="menu"] > .button-box > .button-menu-dropmarker {
|
||||
-moz-appearance: none !important;
|
||||
}
|
||||
|
||||
.help-button > .button-box > .button-icon {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
menulist {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
menulist:not([editable="true"]) > .menulist-dropmarker {
|
||||
display: -moz-box;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
checkbox {
|
||||
-moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox");
|
||||
-moz-box-align: center;
|
||||
}
|
||||
|
||||
radio {
|
||||
-moz-binding: url("chrome://global/content/bindings/radio.xml#radio");
|
||||
-moz-box-align: center;
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
.radio-label-box {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
.numberbox-input-box {
|
||||
-moz-appearance: none;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
spinbuttons {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
60
browser/themes/osx/in-content/common.css
Normal file
@ -0,0 +1,60 @@
|
||||
/* - This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
- You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
%include ../../shared/in-content/common.inc.css
|
||||
|
||||
xul|tabs {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
xul|tab[selected] {
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
xul|button,
|
||||
xul|colorpicker[type="button"],
|
||||
xul|menulist {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
xul|menulist:not([editable="true"]) > xul|menupopup > xul|menuitem[checked="true"]::before,
|
||||
xul|menulist:not([editable="true"]) > xul|menupopup > xul|menuitem[selected="true"]::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker {
|
||||
display: -moz-box;
|
||||
margin-top: 1px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
xul|menulist > xul|menupopup xul|menu,
|
||||
xul|menulist > xul|menupopup xul|menuitem,
|
||||
xul|button[type="menu"] > xul|menupopup xul|menu,
|
||||
xul|button[type="menu"] > xul|menupopup xul|menuitem {
|
||||
-moz-padding-end: 34px;
|
||||
}
|
||||
|
||||
xul|*.help-button > xul|*.button-box > xul|*.button-icon {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
xul|*.checkbox-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
xul|*.radio-icon {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
xul|*.numberbox-input-box {
|
||||
-moz-appearance: none;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
xul|description {
|
||||
font-size: 1.25rem;
|
||||
line-height: 22px;
|
||||
}
|
@ -43,7 +43,17 @@ browser.jar:
|
||||
skin/classic/browser/identity-icons-https-mixed-display.png
|
||||
skin/classic/browser/identity-icons-https-mixed-display@2x.png
|
||||
skin/classic/browser/Info.png
|
||||
skin/classic/browser/in-content/common.css (../shared/in-content/common.css)
|
||||
* skin/classic/browser/in-content/common.css (in-content/common.css)
|
||||
skin/classic/browser/in-content/check.png (../shared/in-content/check.png)
|
||||
skin/classic/browser/in-content/check@2x.png (../shared/in-content/check@2x.png)
|
||||
skin/classic/browser/in-content/dropdown.png (../shared/in-content/dropdown.png)
|
||||
skin/classic/browser/in-content/dropdown@2x.png (../shared/in-content/dropdown@2x.png)
|
||||
skin/classic/browser/in-content/dropdown-disabled.png (../shared/in-content/dropdown-disabled.png)
|
||||
skin/classic/browser/in-content/dropdown-disabled@2x.png (../shared/in-content/dropdown-disabled@2x.png)
|
||||
skin/classic/browser/in-content/help-glyph.png (../shared/in-content/help-glyph.png)
|
||||
skin/classic/browser/in-content/help-glyph@2x.png (../shared/in-content/help-glyph@2x.png)
|
||||
skin/classic/browser/in-content/sorter.png (../shared/in-content/sorter.png)
|
||||
skin/classic/browser/in-content/sorter@2x.png (../shared/in-content/sorter@2x.png)
|
||||
skin/classic/browser/keyhole-circle.png
|
||||
skin/classic/browser/keyhole-circle@2x.png
|
||||
skin/classic/browser/KUI-background.png
|
||||
@ -248,18 +258,8 @@ browser.jar:
|
||||
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
|
||||
skin/classic/browser/preferences/in-content/check.png (../shared/incontentprefs/check.png)
|
||||
skin/classic/browser/preferences/in-content/check@2x.png (../shared/incontentprefs/check@2x.png)
|
||||
skin/classic/browser/preferences/in-content/icons.png (../shared/incontentprefs/icons.png)
|
||||
skin/classic/browser/preferences/in-content/icons@2x.png (../shared/incontentprefs/icons@2x.png)
|
||||
skin/classic/browser/preferences/in-content/help-glyph.png (../shared/incontentprefs/help-glyph.png)
|
||||
skin/classic/browser/preferences/in-content/help-glyph@2x.png (../shared/incontentprefs/help-glyph@2x.png)
|
||||
skin/classic/browser/preferences/in-content/sorter.png (../shared/incontentprefs/sorter.png)
|
||||
skin/classic/browser/preferences/in-content/sorter@2x.png (../shared/incontentprefs/sorter@2x.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown.png (../shared/incontentprefs/dropdown.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown@2x.png (../shared/incontentprefs/dropdown@2x.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown-disabled.png (../shared/incontentprefs/dropdown-disabled.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown-disabled@2x.png (../shared/incontentprefs/dropdown-disabled@2x.png)
|
||||
skin/classic/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/browser/social/services-16.png (social/services-16.png)
|
||||
|
@ -9,56 +9,6 @@ prefpane .groupbox-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
tabs {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
tab[selected] {
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
button,
|
||||
colorpicker[type="button"],
|
||||
menulist {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
menulist:not([editable="true"]) > menupopup > menuitem[checked="true"]::before,
|
||||
menulist:not([editable="true"]) > menupopup > menuitem[selected="true"]::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
menulist:not([editable="true"]) > .menulist-dropmarker {
|
||||
display: -moz-box;
|
||||
margin-top: 1px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
menulist > menupopup menu,
|
||||
menulist > menupopup menuitem,
|
||||
button[type="menu"] > menupopup menu,
|
||||
button[type="menu"] > menupopup menuitem {
|
||||
-moz-padding-end: 34px;
|
||||
}
|
||||
|
||||
.help-button > .button-box > .button-icon {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
.checkbox-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.radio-icon {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
.numberbox-input-box {
|
||||
-moz-appearance: none;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
spinbuttons {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
@ -88,11 +38,6 @@ spinbuttons {
|
||||
-moz-margin-end: 8px !important;
|
||||
}
|
||||
|
||||
description {
|
||||
font-size: 1.25rem;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
#downloadFolder > .fileFieldContentBox {
|
||||
-moz-padding-start: 3px;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 288 B After Width: | Height: | Size: 288 B |
Before Width: | Height: | Size: 471 B After Width: | Height: | Size: 471 B |
@ -1,55 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
body {
|
||||
font: message-box;
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
color: #424e5a;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5em;
|
||||
font-weight: lighter;
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
button {
|
||||
line-height: 20px;
|
||||
height: 30px;
|
||||
max-height: 30px;
|
||||
padding: 3px;
|
||||
color: #333333;
|
||||
border: 1px solid rgba(23,50,77,.4);
|
||||
border-radius: 5px;
|
||||
background-color: #f1f1f1;
|
||||
background-image: linear-gradient(#fff, rgba(255,255,255,.1));
|
||||
box-shadow: 0 1px 1px 0 #fff, inset 0 2px 2px 0 #fff;
|
||||
text-shadow: 0 1px 1px #fefffe;
|
||||
-moz-appearance: none;
|
||||
-moz-border-top-colors: none !important;
|
||||
-moz-border-right-colors: none !important;
|
||||
-moz-border-bottom-colors: none !important;
|
||||
-moz-border-left-colors: none !important;
|
||||
}
|
||||
|
||||
button:enabled:hover {
|
||||
background-image: linear-gradient(#fff, rgba(255,255,255,.6));
|
||||
}
|
||||
|
||||
button:enabled:hover:active {
|
||||
background-image: linear-gradient(rgba(255,255,255,.1), rgba(255,255,255,.6));
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
cursor: not-allowed;
|
||||
color: rgba(115,121,128,.5);
|
||||
border-color: rgba(23,50,77,.25);
|
||||
background-image: linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,.1));
|
||||
text-shadow: 0 1px 1px #fff;
|
||||
}
|
593
browser/themes/shared/in-content/common.inc.css
Normal file
@ -0,0 +1,593 @@
|
||||
%if 0
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
%endif
|
||||
@namespace html "http://www.w3.org/1999/xhtml";
|
||||
@namespace xul "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
html|body,
|
||||
xul|page {
|
||||
font: message-box;
|
||||
-moz-appearance: none;
|
||||
background-color: #f1f1f1;
|
||||
color: #424e5a;
|
||||
}
|
||||
|
||||
*|* {
|
||||
-moz-user-select: text;
|
||||
}
|
||||
|
||||
xul|treecol {
|
||||
/* override the * rule to let the treecol be sortable */
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
html|body {
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html|h1 {
|
||||
font-size: 2.5em;
|
||||
font-weight: lighter;
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
xul|caption {
|
||||
-moz-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
xul|caption > xul|label {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
line-height: 22px;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
*|*.main-content {
|
||||
padding: 40px 48px 48px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
xul|prefpane > xul|*.content-box {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* groupboxes */
|
||||
|
||||
xul|groupbox {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
-moz-margin-end: 0;
|
||||
-moz-padding-start: 0;
|
||||
-moz-padding-end: 0;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
xul|groupbox xul|label {
|
||||
/* !important needed to override toolkit !important rule */
|
||||
-moz-margin-start: 0 !important;
|
||||
-moz-margin-end: 0 !important;
|
||||
}
|
||||
|
||||
/* tabpanels and tabs */
|
||||
|
||||
xul|tabpanels {
|
||||
-moz-appearance: none;
|
||||
font-size: 1.25rem;
|
||||
line-height: 22px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
xul|tabs {
|
||||
margin-bottom: 15px;
|
||||
border-top: 1px solid #c1c1c1;
|
||||
border-bottom: 1px solid #c1c1c1;
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
|
||||
xul|*.tabs-left,
|
||||
xul|*.tabs-right {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
xul|tab {
|
||||
-moz-appearance: none;
|
||||
margin-top: 0;
|
||||
padding: 0 20px;
|
||||
min-height: 44px;
|
||||
color: #424f5a;
|
||||
background-color: #fbfbfb;
|
||||
border-width: 0;
|
||||
transition: background-color 50ms ease 0s;
|
||||
}
|
||||
|
||||
xul|tab:hover {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
|
||||
xul|tab[selected] {
|
||||
background-color: #ebebeb;
|
||||
box-shadow: inset 0 -4px 0 0 #ff9500;
|
||||
}
|
||||
|
||||
xul|*.tab-text {
|
||||
font-size: 1.3rem;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
/* html buttons */
|
||||
|
||||
html|button {
|
||||
padding: 3px;
|
||||
/* override forms.css */
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
/* xul buttons and menulists */
|
||||
|
||||
*|button,
|
||||
xul|colorpicker[type="button"],
|
||||
xul|menulist {
|
||||
-moz-appearance: none;
|
||||
height: 30px;
|
||||
color: #333;
|
||||
line-height: 20px;
|
||||
text-shadow: 0 1px 1px #fefffe;
|
||||
border: 1px solid #c1c1c1;
|
||||
-moz-border-top-colors: none !important;
|
||||
-moz-border-right-colors: none !important;
|
||||
-moz-border-bottom-colors: none !important;
|
||||
-moz-border-left-colors: none !important;
|
||||
border-radius: 2px;
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
|
||||
html|button:enabled:hover,
|
||||
xul|button:not([disabled="true"]):hover,
|
||||
xul|colorpicker[type="button"]:not([disabled="true"]):hover,
|
||||
xul|menulist:not([disabled="true"]):hover {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
|
||||
html|button:enabled:hover:active,
|
||||
xul|button:not([disabled="true"]):hover:active,
|
||||
xul|colorpicker[type="button"]:not([disabled="true"]):hover:active,
|
||||
xul|menulist[open="true"]:not([disabled="true"]) {
|
||||
background-color: #dadada;
|
||||
}
|
||||
|
||||
html|button:disabled,
|
||||
xul|button[disabled="true"],
|
||||
xul|colorpicker[type="button"][disabled="true"],
|
||||
xul|menulist[disabled="true"] {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
xul|colorpicker[type="button"] {
|
||||
padding: 6px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
xul|button > xul|*.button-box,
|
||||
xul|menulist > xul|*.menulist-label-box {
|
||||
padding-right: 10px !important;
|
||||
padding-left: 10px !important;
|
||||
}
|
||||
|
||||
xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker {
|
||||
-moz-appearance: none;
|
||||
margin: 1px 0;
|
||||
-moz-margin-start: 10px;
|
||||
padding: 0;
|
||||
width: 10px;
|
||||
height: 16px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
list-style-image: url("chrome://browser/skin/in-content/dropdown.png");
|
||||
}
|
||||
|
||||
xul|*.help-button {
|
||||
min-width: 30px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #c1c1c1;
|
||||
background-color: #ffcb00;
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
xul|*.help-button:not([disabled="true"]):hover {
|
||||
background-color: #f4c200;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
xul|*.help-button:not([disabled="true"]):hover:active {
|
||||
background-color: #eaba00;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
xul|*.close-icon > xul|*.button-box,
|
||||
xul|*.help-button > xul|*.button-box {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
padding-right: 0 !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
xul|*.help-button > xul|*.button-box > xul|*.button-icon {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
background-image: url("chrome://browser/skin/in-content/help-glyph.png");
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
xul|*.help-button > xul|*.button-box > xul|*.button-icon {
|
||||
background-size: 26px 26px;
|
||||
background-image: url("chrome://browser/skin/in-content/help-glyph@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
xul|*.help-button > xul|*.button-box > xul|*.button-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
xul|*.spinbuttons-button {
|
||||
-moz-margin-start: 10px !important;
|
||||
-moz-margin-end: 2px !important;
|
||||
}
|
||||
|
||||
xul|*.spinbuttons-up {
|
||||
margin-top: 2px !important;
|
||||
border-radius: 1px 1px 0 0;
|
||||
}
|
||||
|
||||
xul|*.spinbuttons-down {
|
||||
margin-bottom: 2px !important;
|
||||
border-radius: 0 0 1px 1px;
|
||||
}
|
||||
|
||||
xul|*.spinbuttons-button > xul|*.button-box {
|
||||
padding: 1px 5px 2px !important;
|
||||
}
|
||||
|
||||
xul|*.spinbuttons-up > xul|*.button-box > xul|*.button-icon {
|
||||
list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
|
||||
}
|
||||
|
||||
xul|*.spinbuttons-up[disabled="true"] > xul|*.button-box > xul|*.button-icon {
|
||||
list-style-image: url("chrome://global/skin/arrow/arrow-up-dis.gif");
|
||||
}
|
||||
|
||||
xul|*.spinbuttons-down > xul|*.button-box > xul|*.button-icon {
|
||||
list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
|
||||
}
|
||||
|
||||
xul|*.spinbuttons-down[disabled="true"] > xul|*.button-box > xul|*.button-icon {
|
||||
list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif");
|
||||
}
|
||||
|
||||
xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker {
|
||||
-moz-appearance: none;
|
||||
-moz-margin-end: 10px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
list-style-image: url("chrome://browser/skin/in-content/dropdown.png");
|
||||
}
|
||||
|
||||
xul|menulist[disabled="true"]:not([editable="true"]) > xul|*.menulist-dropmarker {
|
||||
list-style-image: url("chrome://browser/skin/in-content/dropdown-disabled.png")
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker,
|
||||
xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker {
|
||||
list-style-image: url("chrome://browser/skin/in-content/dropdown@2x.png");
|
||||
}
|
||||
|
||||
xul|menulist[disabled="true"]:not([editable="true"]) > xul|*.menulist-dropmarker {
|
||||
list-style-image: url("chrome://browser/skin/in-content/dropdown-disabled@2x.png")
|
||||
}
|
||||
|
||||
xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker > xul|*.dropmarker-icon,
|
||||
xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker > xul|*.dropmarker-icon {
|
||||
width: 10px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
xul|menulist > xul|menupopup,
|
||||
xul|button[type="menu"] > xul|menupopup {
|
||||
-moz-appearance: none;
|
||||
border: 1px solid rgba(23,50,77,0.4);
|
||||
border-radius: 2px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
xul|menulist > xul|menupopup xul|menu,
|
||||
xul|menulist > xul|menupopup xul|menuitem,
|
||||
xul|button[type="menu"] > xul|menupopup xul|menu,
|
||||
xul|button[type="menu"] > xul|menupopup xul|menuitem {
|
||||
-moz-appearance: none;
|
||||
font-size: 1.25rem;
|
||||
line-height: 22px;
|
||||
height: 40px;
|
||||
color: #333;
|
||||
-moz-padding-start: 10px;
|
||||
-moz-padding-end: 30px;
|
||||
}
|
||||
|
||||
xul|menulist > xul|menupopup > xul|menu[_moz-menuactive="true"],
|
||||
xul|menulist > xul|menupopup > xul|menuitem[_moz-menuactive="true"],
|
||||
xul|button[type="menu"] > xul|menupopup > xul|menu[_moz-menuactive="true"],
|
||||
xul|button[type="menu"] > xul|menupopup > xul|menuitem[_moz-menuactive="true"] {
|
||||
color: #333;
|
||||
background-color: transparent;
|
||||
background-image: linear-gradient(rgba(76,177,255,0.25), rgba(23,146,229,0.25));
|
||||
}
|
||||
|
||||
xul|menulist > xul|menupopup > xul|menu[selected="true"],
|
||||
xul|menulist > xul|menupopup > xul|menuitem[selected="true"],
|
||||
xul|button[type="menu"] > xul|menupopup > xul|menu[selected="true"],
|
||||
xul|button[type="menu"] > xul|menupopup > xul|menuitem[selected="true"] {
|
||||
color: #fff;
|
||||
background-image: linear-gradient(#4cb1ff, #1792e5);
|
||||
}
|
||||
|
||||
xul|menulist > xul|menupopup xul|menuseparator,
|
||||
xul|button[type="menu"] > xul|menupopup xul|menuseparator {
|
||||
-moz-appearance: none;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
padding: 0;
|
||||
border-top: 1px solid rgba(23,50,77,0.4);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* textboxes */
|
||||
|
||||
*|textbox {
|
||||
-moz-appearance: none;
|
||||
height: 30px;
|
||||
color: #333;
|
||||
line-height: 20px;
|
||||
text-shadow: 0 1px 1px #fefffe;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
border: 1px solid #c1c1c1;
|
||||
-moz-border-top-colors: none !important;
|
||||
-moz-border-right-colors: none !important;
|
||||
-moz-border-bottom-colors: none !important;
|
||||
-moz-border-left-colors: none !important;
|
||||
border-radius: 2px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
html|textbox:focus,
|
||||
xul|textbox[focused] {
|
||||
border-color: #0095dd;
|
||||
}
|
||||
|
||||
html|textbox:disabled,
|
||||
xul|textbox[disabled="true"] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
|
||||
xul|*.text-link,
|
||||
xul|*.inline-link,
|
||||
html|a.inline-link {
|
||||
font-size: 1.25rem;
|
||||
line-height: 22px;
|
||||
color: #0095dd;
|
||||
}
|
||||
|
||||
xul|*.text-link:hover,
|
||||
xul|*.inline-link:hover {
|
||||
color: #4cb1ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
xul|*.text-link:hover:active,
|
||||
xul|*.inline-link:hover:active {
|
||||
color: #ff9500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Checkboxes and radio buttons */
|
||||
|
||||
xul|checkbox {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
xul|*.checkbox-check {
|
||||
-moz-appearance: none;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #c1c1c1;
|
||||
-moz-margin-end: 10px;
|
||||
background-color: #f1f1f1;
|
||||
background-image: linear-gradient(#fff, rgba(255,255,255,0.8));
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
box-shadow: 0 1px 1px 0 #fff, inset 0 2px 0 0 rgba(0,0,0,0.03);
|
||||
}
|
||||
|
||||
xul|checkbox:not([disabled="true"]):hover > xul|*.checkbox-check {
|
||||
border-color: #0095dd;
|
||||
}
|
||||
|
||||
xul|*.checkbox-check[checked] {
|
||||
background-image: url("chrome://browser/skin/in-content/check.png"),
|
||||
/* !important needed to override toolkit !important rule */
|
||||
linear-gradient(#fff, rgba(255,255,255,0.8)) !important;
|
||||
}
|
||||
|
||||
xul|checkbox[disabled="true"] > xul|*.checkbox-check {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
xul|*.checkbox-label-box {
|
||||
-moz-margin-start: -1px; /* negative margin for the transparent border */
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
xul|*.checkbox-check[checked] {
|
||||
background-size: 12px 12px, auto;
|
||||
background-image: url("chrome://browser/skin/in-content/check@2x.png"),
|
||||
linear-gradient(#fff, rgba(255,255,255,0.8)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
xul|*.radio-check {
|
||||
-moz-appearance: none;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
border: 1px solid #c1c1c1;
|
||||
border-radius: 50%;
|
||||
-moz-margin-end: 10px;
|
||||
background-color: #f1f1f1;
|
||||
background-image: linear-gradient(#fff, rgba(255,255,255,0.80));
|
||||
box-shadow: 0 1px 1px 0 #fff, inset 0 2px 0 0 rgba(0,0,0,0.03);
|
||||
}
|
||||
|
||||
xul|radio:not([disabled="true"]):hover > xul|*.radio-check {
|
||||
border-color: #0095dd;
|
||||
}
|
||||
|
||||
xul|*.radio-check[selected] {
|
||||
background-image: radial-gradient(circle, rgb(23,146,229),
|
||||
rgb(76,177,255) 5.5px, rgba(76,177,255,0.2) 6px,
|
||||
transparent 6px),
|
||||
linear-gradient(rgb(255,255,255), rgba(255,255,255,0.8));
|
||||
}
|
||||
|
||||
xul|radio[disabled="true"] > xul|*.radio-check {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
xul|*.radio-label-box {
|
||||
-moz-margin-start: -1px; /* negative margin for the transparent border */
|
||||
-moz-margin-end: 10px;
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
/* Category List */
|
||||
|
||||
xul|*#categories {
|
||||
-moz-appearance: none;
|
||||
background-color: #424f5a;
|
||||
padding-top: 39px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
xul|*.category {
|
||||
-moz-appearance: none;
|
||||
color: #c1c1c1;
|
||||
-moz-border-end-width: 0;
|
||||
-moz-padding-start: 15px;
|
||||
-moz-padding-end: 21px;
|
||||
min-height: 40px;
|
||||
transition: background-color 150ms;
|
||||
}
|
||||
|
||||
xul|*.category:hover {
|
||||
background-color: #5e6972;
|
||||
}
|
||||
|
||||
xul|*.category[selected] {
|
||||
background-color: #343f48;
|
||||
color: #f2f2f2;
|
||||
box-shadow: inset 4px 0 0 0 #ff9500;
|
||||
}
|
||||
|
||||
xul|*#categories[keyboard-navigation="true"]:-moz-focusring > xul|*.category[current] {
|
||||
border-top: 1px #ffffff dotted;
|
||||
border-bottom: 1px #ffffff dotted;
|
||||
}
|
||||
|
||||
*|*.category-name {
|
||||
line-height: 22px;
|
||||
font-size: 1.25rem;
|
||||
padding-bottom: 2px;
|
||||
-moz-padding-start: 9px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
*|*.category-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
/* header */
|
||||
|
||||
*|*.header {
|
||||
border-bottom: 1px solid #c8c8c8;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
*|*.header-name {
|
||||
font-size: 2.5rem;
|
||||
font-weight: normal;
|
||||
line-height: 40px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* File fields */
|
||||
|
||||
xul|filefield {
|
||||
-moz-appearance: none;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
xul|*.fileFieldContentBox {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
xul|*.fileFieldIcon {
|
||||
-moz-margin-start: 10px;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
xul|*.fileFieldLabel {
|
||||
-moz-margin-start: -26px;
|
||||
-moz-padding-start: 36px;
|
||||
}
|
||||
|
||||
xul|textbox:-moz-locale-dir(rtl),
|
||||
xul|*.fileFieldLabel:-moz-locale-dir(rtl),
|
||||
xul|textbox + xul|button:-moz-locale-dir(ltr),
|
||||
xul|filefield + xul|button:-moz-locale-dir(ltr) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
xul|textbox:-moz-locale-dir(ltr),
|
||||
xul|*.fileFieldLabel:-moz-locale-dir(ltr),
|
||||
xul|textbox + xul|button:-moz-locale-dir(rtl),
|
||||
xul|filefield + xul|button:-moz-locale-dir(rtl) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
xul|textbox + xul|button,
|
||||
xul|filefield + xul|button {
|
||||
-moz-border-start: none;
|
||||
}
|
Before Width: | Height: | Size: 412 B After Width: | Height: | Size: 412 B |
Before Width: | Height: | Size: 865 B After Width: | Height: | Size: 865 B |
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 421 B |
Before Width: | Height: | Size: 885 B After Width: | Height: | Size: 885 B |
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 280 B |
Before Width: | Height: | Size: 500 B After Width: | Height: | Size: 500 B |
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 523 B After Width: | Height: | Size: 523 B |
@ -9,39 +9,12 @@
|
||||
dialog,
|
||||
window,
|
||||
prefwindow,
|
||||
.windowDialog,
|
||||
page {
|
||||
.windowDialog {
|
||||
-moz-appearance: none;
|
||||
background-color: #f1f1f1;
|
||||
color: #424E5A;
|
||||
}
|
||||
|
||||
* {
|
||||
-moz-user-select: text;
|
||||
}
|
||||
|
||||
treecol {
|
||||
/* override the * rule to let the treecol be sortable */
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
caption {
|
||||
-moz-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
caption > label {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
line-height: 22px;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
padding: 40px 48px 48px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
prefpane {
|
||||
max-width: 800px;
|
||||
padding: 0;
|
||||
@ -50,469 +23,9 @@ prefpane {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
prefpane > .content-box {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* groupboxes */
|
||||
|
||||
groupbox {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
-moz-margin-end: 0;
|
||||
-moz-padding-start: 0;
|
||||
-moz-padding-end: 0;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
groupbox label {
|
||||
/* !important needed to override toolkit !important rule */
|
||||
-moz-margin-start: 0 !important;
|
||||
-moz-margin-end: 0 !important;
|
||||
}
|
||||
|
||||
/* tabpanels and tabs */
|
||||
|
||||
tabpanels {
|
||||
-moz-appearance: none;
|
||||
font-size: 1.25rem;
|
||||
line-height: 22px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
tabs {
|
||||
margin-bottom: 15px;
|
||||
border-top: 1px solid #c1c1c1;
|
||||
border-bottom: 1px solid #c1c1c1;
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
|
||||
.tabs-left,
|
||||
.tabs-right {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tab {
|
||||
-moz-appearance: none;
|
||||
margin-top: 0;
|
||||
padding: 0 20px;
|
||||
min-height: 44px;
|
||||
color: #424f5a;
|
||||
background-color: #fbfbfb;
|
||||
border-width: 0;
|
||||
transition: background-color 50ms ease 0s;
|
||||
}
|
||||
|
||||
tab:hover {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
|
||||
tab[selected] {
|
||||
background-color: #ebebeb;
|
||||
box-shadow: inset 0 -4px 0 0 #ff9500;
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 1.3rem;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
/* buttons and menulists */
|
||||
|
||||
button,
|
||||
colorpicker[type="button"],
|
||||
menulist {
|
||||
-moz-appearance: none;
|
||||
height: 30px;
|
||||
color: #333333;
|
||||
line-height: 20px;
|
||||
text-shadow: 0 1px 1px #FEFFFE;
|
||||
border: 1px solid #C1C1C1;
|
||||
-moz-border-top-colors: none !important;
|
||||
-moz-border-right-colors: none !important;
|
||||
-moz-border-bottom-colors: none !important;
|
||||
-moz-border-left-colors: none !important;
|
||||
border-radius: 2px;
|
||||
background-color: #FBFBFB;
|
||||
}
|
||||
|
||||
button:not([disabled="true"]):hover,
|
||||
colorpicker[type="button"]:not([disabled="true"]):hover,
|
||||
menulist:not([disabled="true"]):hover {
|
||||
background-color: #EBEBEB;
|
||||
}
|
||||
|
||||
button:not([disabled="true"]):hover:active,
|
||||
colorpicker[type="button"]:not([disabled="true"]):hover:active,
|
||||
menulist[open="true"]:not([disabled="true"]) {
|
||||
background-color: #DADADA;
|
||||
}
|
||||
|
||||
button[disabled="true"],
|
||||
colorpicker[type="button"][disabled="true"],
|
||||
menulist[disabled="true"] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
colorpicker[type="button"] {
|
||||
padding: 6px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
button > .button-box,
|
||||
menulist > .menulist-label-box {
|
||||
padding-right: 10px !important;
|
||||
padding-left: 10px !important;
|
||||
}
|
||||
|
||||
button[type="menu"] > .button-box > .button-menu-dropmarker {
|
||||
-moz-appearance: none;
|
||||
margin: 1px 0;
|
||||
-moz-margin-start: 10px;
|
||||
padding: 0;
|
||||
width: 10px;
|
||||
height: 16px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
list-style-image: url("chrome://browser/skin/preferences/in-content/dropdown.png");
|
||||
}
|
||||
|
||||
.help-button {
|
||||
min-width: 30px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #C1C1C1;
|
||||
background-color: #FFCB00;
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.help-button:not([disabled="true"]):hover {
|
||||
background-color: #F4C200;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.help-button:not([disabled="true"]):hover:active {
|
||||
background-color: #EABA00;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.close-icon > .button-box,
|
||||
.help-button > .button-box {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
padding-right: 0 !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.help-button > .button-box > .button-icon {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
background-image: url("chrome://browser/skin/preferences/in-content/help-glyph.png");
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
.help-button > .button-box > .button-icon {
|
||||
background-size: 26px 26px;
|
||||
background-image: url("chrome://browser/skin/preferences/in-content/help-glyph@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
.help-button > .button-box > .button-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.spinbuttons-button {
|
||||
-moz-margin-start: 10px !important;
|
||||
-moz-margin-end: 2px !important;
|
||||
}
|
||||
|
||||
.spinbuttons-up {
|
||||
margin-top: 2px !important;
|
||||
border-radius: 1px 1px 0 0;
|
||||
}
|
||||
|
||||
.spinbuttons-down {
|
||||
margin-bottom: 2px !important;
|
||||
border-radius: 0 0 1px 1px;
|
||||
}
|
||||
|
||||
.spinbuttons-button > .button-box {
|
||||
padding: 1px 5px 2px !important;
|
||||
}
|
||||
|
||||
.spinbuttons-up > .button-box > .button-icon {
|
||||
list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
|
||||
}
|
||||
|
||||
.spinbuttons-up[disabled="true"] > .button-box > .button-icon {
|
||||
list-style-image: url("chrome://global/skin/arrow/arrow-up-dis.gif");
|
||||
}
|
||||
|
||||
.spinbuttons-down > .button-box > .button-icon {
|
||||
list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
|
||||
}
|
||||
|
||||
.spinbuttons-down[disabled="true"] > .button-box > .button-icon {
|
||||
list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif");
|
||||
}
|
||||
|
||||
menulist:not([editable="true"]) > .menulist-dropmarker {
|
||||
-moz-appearance: none;
|
||||
-moz-margin-end: 10px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
list-style-image: url("chrome://browser/skin/preferences/in-content/dropdown.png")
|
||||
}
|
||||
|
||||
menulist[disabled="true"]:not([editable="true"]) > .menulist-dropmarker {
|
||||
list-style-image: url("chrome://browser/skin/preferences/in-content/dropdown-disabled.png")
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
menulist:not([editable="true"]) > .menulist-dropmarker,
|
||||
button[type="menu"] > .button-box > .button-menu-dropmarker {
|
||||
list-style-image: url("chrome://browser/skin/preferences/in-content/dropdown@2x.png");
|
||||
}
|
||||
|
||||
menulist[disabled="true"]:not([editable="true"]) > .menulist-dropmarker {
|
||||
list-style-image: url("chrome://browser/skin/preferences/in-content/dropdown-disabled@2x.png")
|
||||
}
|
||||
|
||||
menulist:not([editable="true"]) > .menulist-dropmarker > .dropmarker-icon,
|
||||
button[type="menu"] > .button-box > .button-menu-dropmarker > .dropmarker-icon {
|
||||
width: 10px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
menulist > menupopup,
|
||||
button[type="menu"] > menupopup {
|
||||
-moz-appearance: none;
|
||||
border: 1px solid rgba(23,50,77,0.4);
|
||||
border-radius: 2px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
menulist > menupopup menu,
|
||||
menulist > menupopup menuitem,
|
||||
button[type="menu"] > menupopup menu,
|
||||
button[type="menu"] > menupopup menuitem {
|
||||
-moz-appearance: none;
|
||||
font-size: 1.25rem;
|
||||
line-height: 22px;
|
||||
height: 40px;
|
||||
color: #333333;
|
||||
-moz-padding-start: 10px;
|
||||
-moz-padding-end: 30px;
|
||||
}
|
||||
|
||||
menulist > menupopup > menu[_moz-menuactive="true"],
|
||||
menulist > menupopup > menuitem[_moz-menuactive="true"],
|
||||
button[type="menu"] > menupopup > menu[_moz-menuactive="true"],
|
||||
button[type="menu"] > menupopup > menuitem[_moz-menuactive="true"] {
|
||||
color: #333333;
|
||||
background-color: transparent;
|
||||
background-image: linear-gradient(rgba(76,177,255,0.25), rgba(23,146,229,0.25));
|
||||
}
|
||||
|
||||
menulist > menupopup > menu[selected="true"],
|
||||
menulist > menupopup > menuitem[selected="true"],
|
||||
button[type="menu"] > menupopup > menu[selected="true"],
|
||||
button[type="menu"] > menupopup > menuitem[selected="true"] {
|
||||
color: #fff;
|
||||
background-image: linear-gradient(#4CB1FF, #1792E5);
|
||||
}
|
||||
|
||||
menulist > menupopup menuseparator,
|
||||
button[type="menu"] > menupopup menuseparator {
|
||||
-moz-appearance: none;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
padding: 0;
|
||||
border-top: 1px solid rgba(23,50,77,0.4);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* textboxes */
|
||||
|
||||
textbox {
|
||||
-moz-appearance: none;
|
||||
height: 30px;
|
||||
color: #333333;
|
||||
line-height: 20px;
|
||||
text-shadow: 0 1px 1px #FEFFFE;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
border: 1px solid #C1C1C1;
|
||||
-moz-border-top-colors: none !important;
|
||||
-moz-border-right-colors: none !important;
|
||||
-moz-border-bottom-colors: none !important;
|
||||
-moz-border-left-colors: none !important;
|
||||
border-radius: 2px;
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
textbox[focused] {
|
||||
border-color: #0095DD;
|
||||
}
|
||||
|
||||
textbox[disabled="true"] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
|
||||
.text-link,
|
||||
.inline-link,
|
||||
html|a.inline-link {
|
||||
font-size: 1.25rem;
|
||||
line-height: 22px;
|
||||
color: #0095DD;
|
||||
}
|
||||
|
||||
.text-link:hover,
|
||||
.inline-link:hover {
|
||||
color: #4CB1FF;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.text-link:hover:active,
|
||||
.inline-link:hover:active {
|
||||
color: #FF9500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Checkboxes and radio buttons */
|
||||
|
||||
checkbox {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
.checkbox-check {
|
||||
-moz-appearance: none;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #C1C1C1;
|
||||
-moz-margin-end: 10px;
|
||||
background-color: #f1f1f1;
|
||||
background-image: linear-gradient(#ffffff, rgba(255,255,255,0.8));
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
box-shadow: 0 1px 1px 0 #ffffff, inset 0 2px 0 0 rgba(0,0,0,0.03);
|
||||
}
|
||||
|
||||
checkbox:not([disabled="true"]):hover > .checkbox-check {
|
||||
border-color: #0095DD;
|
||||
}
|
||||
|
||||
.checkbox-check[checked] {
|
||||
background-image: url("chrome://browser/skin/preferences/in-content/check.png"),
|
||||
/* !important needed to override toolkit !important rule */
|
||||
linear-gradient(#ffffff, rgba(255,255,255,0.8)) !important;
|
||||
}
|
||||
|
||||
checkbox[disabled="true"] > .checkbox-check {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.checkbox-label-box {
|
||||
-moz-margin-start: -1px; /* negative margin for the transparent border */
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
.checkbox-check[checked] {
|
||||
background-size: 12px 12px, auto;
|
||||
background-image: url("chrome://browser/skin/preferences/in-content/check@2x.png"),
|
||||
linear-gradient(#ffffff, rgba(255,255,255,0.8)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-check {
|
||||
-moz-appearance: none;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
border: 1px solid #C1C1C1;
|
||||
border-radius: 50%;
|
||||
-moz-margin-end: 10px;
|
||||
background-color: #f1f1f1;
|
||||
background-image: linear-gradient(#ffffff, rgba(255,255,255,0.80));
|
||||
box-shadow: 0 1px 1px 0 #ffffff, inset 0 2px 0 0 rgba(0,0,0,0.03);
|
||||
}
|
||||
|
||||
radio:not([disabled="true"]):hover > .radio-check {
|
||||
border-color: #0095DD;
|
||||
}
|
||||
|
||||
.radio-check[selected] {
|
||||
background-image: radial-gradient(circle, rgb(23,146,229),
|
||||
rgb(76,177,255) 5.5px, rgba(76,177,255,0.2) 6px,
|
||||
transparent 6px),
|
||||
linear-gradient(rgb(255,255,255), rgba(255,255,255,0.8));
|
||||
}
|
||||
|
||||
radio[disabled="true"] > .radio-check {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.radio-label-box {
|
||||
-moz-margin-start: -1px; /* negative margin for the transparent border */
|
||||
-moz-margin-end: 10px;
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
/* Category List */
|
||||
|
||||
#categories {
|
||||
-moz-appearance: none;
|
||||
background-color: #424f5a;
|
||||
padding-top: 39px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.category {
|
||||
-moz-appearance: none;
|
||||
color: #c1c1c1;
|
||||
-moz-border-end-width: 0;
|
||||
-moz-padding-start: 15px;
|
||||
-moz-padding-end: 21px;
|
||||
min-height: 40px;
|
||||
transition: background-color 150ms;
|
||||
}
|
||||
|
||||
.category:hover {
|
||||
background-color: #5e6972;
|
||||
}
|
||||
|
||||
.category[selected] {
|
||||
background-color: #343f48;
|
||||
color: #f2f2f2;
|
||||
box-shadow: inset 4px 0 0 0 #FF9500;
|
||||
}
|
||||
|
||||
#categories[keyboard-navigation="true"]:-moz-focusring > .category[current] {
|
||||
border-top: 1px #ffffff dotted;
|
||||
border-bottom: 1px #ffffff dotted;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
line-height: 22px;
|
||||
font-size: 1.25rem;
|
||||
padding-bottom: 2px;
|
||||
-moz-padding-start: 9px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
list-style-image: url("chrome://browser/skin/preferences/in-content/icons.png");
|
||||
}
|
||||
|
||||
@ -580,68 +93,13 @@ radio[disabled="true"] > .radio-check {
|
||||
|
||||
/* header */
|
||||
|
||||
.header {
|
||||
border-bottom: 1px solid #c8c8c8;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
#header-advanced {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.header-name {
|
||||
font-size: 2.5rem;
|
||||
font-weight: normal;
|
||||
line-height: 40px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* General Pane */
|
||||
|
||||
filefield {
|
||||
-moz-appearance: none;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.fileFieldContentBox {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.fileFieldIcon {
|
||||
-moz-margin-start: 10px;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
.fileFieldLabel {
|
||||
-moz-margin-start: -26px;
|
||||
-moz-padding-start: 36px;
|
||||
}
|
||||
|
||||
textbox:-moz-locale-dir(rtl),
|
||||
.fileFieldLabel:-moz-locale-dir(rtl),
|
||||
textbox + button:-moz-locale-dir(ltr),
|
||||
filefield + button:-moz-locale-dir(ltr) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
textbox:-moz-locale-dir(ltr),
|
||||
.fileFieldLabel:-moz-locale-dir(ltr),
|
||||
textbox + button:-moz-locale-dir(rtl),
|
||||
filefield + button:-moz-locale-dir(rtl) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
textbox + button,
|
||||
filefield + button {
|
||||
-moz-border-start: none;
|
||||
}
|
||||
|
||||
#downloadFolder {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
35
browser/themes/windows/in-content/common.css
Normal file
@ -0,0 +1,35 @@
|
||||
/* - This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
- You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
%include ../../shared/in-content/common.inc.css
|
||||
|
||||
xul|caption {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
xul|button,
|
||||
xul|colorpicker[type="button"],
|
||||
xul|menulist {
|
||||
margin: 2px 4px;
|
||||
}
|
||||
|
||||
xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker {
|
||||
margin-top: 1px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
xul|checkbox {
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
xul|radio {
|
||||
-moz-binding: url("chrome://global/content/bindings/radio.xml#radio");
|
||||
-moz-margin-start: 0;
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
xul|*.radio-icon,
|
||||
xul|*.checkbox-icon {
|
||||
-moz-margin-end: 0;
|
||||
}
|
@ -37,7 +37,17 @@ browser.jar:
|
||||
skin/classic/browser/identity-icons-https-ev.png
|
||||
skin/classic/browser/identity-icons-https-mixed-active.png
|
||||
skin/classic/browser/identity-icons-https-mixed-display.png
|
||||
skin/classic/browser/in-content/common.css (../shared/in-content/common.css)
|
||||
* skin/classic/browser/in-content/common.css (in-content/common.css)
|
||||
skin/classic/browser/in-content/check.png (../shared/in-content/check.png)
|
||||
skin/classic/browser/in-content/check@2x.png (../shared/in-content/check@2x.png)
|
||||
skin/classic/browser/in-content/dropdown.png (../shared/in-content/dropdown.png)
|
||||
skin/classic/browser/in-content/dropdown@2x.png (../shared/in-content/dropdown@2x.png)
|
||||
skin/classic/browser/in-content/dropdown-disabled.png (../shared/in-content/dropdown-disabled.png)
|
||||
skin/classic/browser/in-content/dropdown-disabled@2x.png (../shared/in-content/dropdown-disabled@2x.png)
|
||||
skin/classic/browser/in-content/help-glyph.png (../shared/in-content/help-glyph.png)
|
||||
skin/classic/browser/in-content/help-glyph@2x.png (../shared/in-content/help-glyph@2x.png)
|
||||
skin/classic/browser/in-content/sorter.png (../shared/in-content/sorter.png)
|
||||
skin/classic/browser/in-content/sorter@2x.png (../shared/in-content/sorter@2x.png)
|
||||
skin/classic/browser/keyhole-forward-mask.svg
|
||||
skin/classic/browser/KUI-background.png
|
||||
skin/classic/browser/livemark-folder.png
|
||||
@ -177,18 +187,8 @@ browser.jar:
|
||||
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
|
||||
skin/classic/browser/preferences/in-content/check.png (../shared/incontentprefs/check.png)
|
||||
skin/classic/browser/preferences/in-content/check@2x.png (../shared/incontentprefs/check@2x.png)
|
||||
skin/classic/browser/preferences/in-content/icons.png (../shared/incontentprefs/icons.png)
|
||||
skin/classic/browser/preferences/in-content/icons@2x.png (../shared/incontentprefs/icons@2x.png)
|
||||
skin/classic/browser/preferences/in-content/help-glyph.png (../shared/incontentprefs/help-glyph.png)
|
||||
skin/classic/browser/preferences/in-content/help-glyph@2x.png (../shared/incontentprefs/help-glyph@2x.png)
|
||||
skin/classic/browser/preferences/in-content/sorter.png (../shared/incontentprefs/sorter.png)
|
||||
skin/classic/browser/preferences/in-content/sorter@2x.png (../shared/incontentprefs/sorter@2x.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown.png (../shared/incontentprefs/dropdown.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown@2x.png (../shared/incontentprefs/dropdown@2x.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown-disabled.png (../shared/incontentprefs/dropdown-disabled.png)
|
||||
skin/classic/browser/preferences/in-content/dropdown-disabled@2x.png (../shared/incontentprefs/dropdown-disabled@2x.png)
|
||||
skin/classic/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/browser/social/services-16.png (social/services-16.png)
|
||||
@ -456,7 +456,17 @@ browser.jar:
|
||||
skin/classic/aero/browser/identity-icons-https-ev.png
|
||||
skin/classic/aero/browser/identity-icons-https-mixed-active.png
|
||||
skin/classic/aero/browser/identity-icons-https-mixed-display.png
|
||||
skin/classic/aero/browser/in-content/common.css (../shared/in-content/common.css)
|
||||
* skin/classic/aero/browser/in-content/common.css (in-content/common.css)
|
||||
skin/classic/aero/browser/in-content/check.png (../shared/in-content/check.png)
|
||||
skin/classic/aero/browser/in-content/check@2x.png (../shared/in-content/check@2x.png)
|
||||
skin/classic/aero/browser/in-content/dropdown.png (../shared/in-content/dropdown.png)
|
||||
skin/classic/aero/browser/in-content/dropdown@2x.png (../shared/in-content/dropdown@2x.png)
|
||||
skin/classic/aero/browser/in-content/dropdown-disabled.png (../shared/in-content/dropdown-disabled.png)
|
||||
skin/classic/aero/browser/in-content/dropdown-disabled@2x.png (../shared/in-content/dropdown-disabled@2x.png)
|
||||
skin/classic/aero/browser/in-content/help-glyph.png (../shared/in-content/help-glyph.png)
|
||||
skin/classic/aero/browser/in-content/help-glyph@2x.png (../shared/in-content/help-glyph@2x.png)
|
||||
skin/classic/aero/browser/in-content/sorter.png (../shared/in-content/sorter.png)
|
||||
skin/classic/aero/browser/in-content/sorter@2x.png (../shared/in-content/sorter@2x.png)
|
||||
skin/classic/aero/browser/keyhole-forward-mask.svg
|
||||
skin/classic/aero/browser/KUI-background.png
|
||||
skin/classic/aero/browser/livemark-folder.png (livemark-folder-aero.png)
|
||||
@ -602,18 +612,8 @@ browser.jar:
|
||||
* skin/classic/aero/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
* skin/classic/aero/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/aero/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
|
||||
skin/classic/aero/browser/preferences/in-content/check.png (../shared/incontentprefs/check.png)
|
||||
skin/classic/aero/browser/preferences/in-content/check@2x.png (../shared/incontentprefs/check@2x.png)
|
||||
skin/classic/aero/browser/preferences/in-content/icons.png (../shared/incontentprefs/icons.png)
|
||||
skin/classic/aero/browser/preferences/in-content/icons@2x.png (../shared/incontentprefs/icons@2x.png)
|
||||
skin/classic/aero/browser/preferences/in-content/help-glyph.png (../shared/incontentprefs/help-glyph.png)
|
||||
skin/classic/aero/browser/preferences/in-content/help-glyph@2x.png (../shared/incontentprefs/help-glyph@2x.png)
|
||||
skin/classic/aero/browser/preferences/in-content/sorter.png (../shared/incontentprefs/sorter.png)
|
||||
skin/classic/aero/browser/preferences/in-content/sorter@2x.png (../shared/incontentprefs/sorter@2x.png)
|
||||
skin/classic/aero/browser/preferences/in-content/dropdown.png (../shared/incontentprefs/dropdown.png)
|
||||
skin/classic/aero/browser/preferences/in-content/dropdown@2x.png (../shared/incontentprefs/dropdown@2x.png)
|
||||
skin/classic/aero/browser/preferences/in-content/dropdown-disabled.png (../shared/incontentprefs/dropdown-disabled.png)
|
||||
skin/classic/aero/browser/preferences/in-content/dropdown-disabled@2x.png (../shared/incontentprefs/dropdown-disabled@2x.png)
|
||||
skin/classic/aero/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/aero/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/aero/browser/social/services-16.png (social/services-16.png)
|
||||
|
@ -4,36 +4,6 @@
|
||||
|
||||
%include ../../../shared/incontentprefs/preferences.css
|
||||
|
||||
caption {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
button,
|
||||
colorpicker[type="button"],
|
||||
menulist {
|
||||
margin: 2px 4px;
|
||||
}
|
||||
|
||||
menulist:not([editable="true"]) > .menulist-dropmarker {
|
||||
margin-top: 1px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
checkbox {
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
radio {
|
||||
-moz-binding: url("chrome://global/content/bindings/radio.xml#radio");
|
||||
-moz-margin-start: 0;
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
.radio-icon,
|
||||
.checkbox-icon {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
.actionsMenu > .menulist-label-box > .menulist-icon {
|
||||
-moz-margin-end: 9px;
|
||||
}
|
||||
|
@ -26,8 +26,6 @@
|
||||
|
||||
<script>
|
||||
<![CDATA[
|
||||
SimpleTest.expectAssertions(1, 2);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
copyToProfile('animals.sqlite');
|
||||
|
@ -17,7 +17,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=495648
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
SimpleTest.expectAssertions(18, 24);
|
||||
SimpleTest.expectAssertions(15, 24);
|
||||
|
||||
/** Test for Bug 495648 **/
|
||||
var uri = window.location.href.replace(/test_bug495648.xul/, "bug495648.rdf");
|
||||
|
@ -24,9 +24,18 @@ import android.database.Cursor;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.Color;
|
||||
|
||||
/**
|
||||
* A utility wrapper for accessing a static {@link LocalBrowserDB},
|
||||
* manually initialized with a particular profile, and similarly
|
||||
* managing a static instance of {@link SuggestedSites}.
|
||||
*
|
||||
* Be careful using this class: if you're not BrowserApp, you probably
|
||||
* want to manually instantiate and use LocalBrowserDB itself.
|
||||
*
|
||||
* Also manages some mapping column names and flags.
|
||||
* The column name mapping will be removed in Bug 1050034.
|
||||
*/
|
||||
public class BrowserDB {
|
||||
private static boolean sAreContentProvidersEnabled = true;
|
||||
|
||||
public static interface URLColumns {
|
||||
public static String URL = "url";
|
||||
public static String TITLE = "title";
|
||||
@ -41,121 +50,9 @@ public class BrowserDB {
|
||||
EXCLUDE_PINNED_SITES
|
||||
}
|
||||
|
||||
private static BrowserDBIface sDb;
|
||||
private static SuggestedSites sSuggestedSites;
|
||||
|
||||
public interface BrowserDBIface {
|
||||
public void invalidateCachedState();
|
||||
|
||||
@RobocopTarget
|
||||
public Cursor filter(ContentResolver cr, CharSequence constraint, int limit,
|
||||
EnumSet<FilterFlags> flags);
|
||||
|
||||
// This should only return frecent sites. BrowserDB.getTopSites will do the
|
||||
// work to combine that list with the pinned sites list.
|
||||
public Cursor getTopSites(ContentResolver cr, int limit);
|
||||
|
||||
public void updateVisitedHistory(ContentResolver cr, String uri);
|
||||
|
||||
public void updateHistoryTitle(ContentResolver cr, String uri, String title);
|
||||
|
||||
public void updateHistoryEntry(ContentResolver cr, String uri, String title,
|
||||
long date, int visits);
|
||||
|
||||
@RobocopTarget
|
||||
public Cursor getAllVisitedHistory(ContentResolver cr);
|
||||
|
||||
public Cursor getRecentHistory(ContentResolver cr, int limit);
|
||||
|
||||
public void expireHistory(ContentResolver cr, ExpirePriority priority);
|
||||
|
||||
public void removeHistoryEntry(ContentResolver cr, int id);
|
||||
|
||||
@RobocopTarget
|
||||
public void removeHistoryEntry(ContentResolver cr, String url);
|
||||
|
||||
public void clearHistory(ContentResolver cr);
|
||||
|
||||
@RobocopTarget
|
||||
public Cursor getBookmarksInFolder(ContentResolver cr, long folderId);
|
||||
|
||||
public Cursor getReadingList(ContentResolver cr);
|
||||
|
||||
public boolean isVisited(ContentResolver cr, String uri);
|
||||
|
||||
public int getReadingListCount(ContentResolver cr);
|
||||
|
||||
@RobocopTarget
|
||||
public boolean isBookmark(ContentResolver cr, String uri);
|
||||
|
||||
public boolean isReadingListItem(ContentResolver cr, String uri);
|
||||
|
||||
/**
|
||||
* Return a combination of fields about the provided URI
|
||||
* in a single hit on the DB.
|
||||
*/
|
||||
public int getItemFlags(ContentResolver cr, String uri);
|
||||
|
||||
public String getUrlForKeyword(ContentResolver cr, String keyword);
|
||||
|
||||
@RobocopTarget
|
||||
public void addBookmark(ContentResolver cr, String title, String uri);
|
||||
|
||||
public void removeBookmark(ContentResolver cr, int id);
|
||||
|
||||
@RobocopTarget
|
||||
public void removeBookmarksWithURL(ContentResolver cr, String uri);
|
||||
|
||||
@RobocopTarget
|
||||
public void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword);
|
||||
|
||||
public void addReadingListItem(ContentResolver cr, ContentValues values);
|
||||
|
||||
public void removeReadingListItemWithURL(ContentResolver cr, String uri);
|
||||
|
||||
public void removeReadingListItem(ContentResolver cr, int id);
|
||||
|
||||
public LoadFaviconResult getFaviconForUrl(ContentResolver cr, String uri);
|
||||
|
||||
public String getFaviconUrlForHistoryUrl(ContentResolver cr, String url);
|
||||
|
||||
public void updateFaviconForUrl(ContentResolver cr, String pageUri, byte[] encodedFavicon, String faviconUri);
|
||||
|
||||
public void updateThumbnailForUrl(ContentResolver cr, String uri, BitmapDrawable thumbnail);
|
||||
|
||||
@RobocopTarget
|
||||
public byte[] getThumbnailForUrl(ContentResolver cr, String uri);
|
||||
|
||||
public Cursor getThumbnailsForUrls(ContentResolver cr, List<String> urls);
|
||||
|
||||
@RobocopTarget
|
||||
public void removeThumbnails(ContentResolver cr);
|
||||
|
||||
public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer);
|
||||
|
||||
public void registerHistoryObserver(ContentResolver cr, ContentObserver observer);
|
||||
|
||||
public int getCount(ContentResolver cr, String database);
|
||||
|
||||
public void pinSite(ContentResolver cr, String url, String title, int position);
|
||||
|
||||
public void unpinSite(ContentResolver cr, int position);
|
||||
|
||||
public void unpinAllSites(ContentResolver cr);
|
||||
|
||||
public Cursor getPinnedSites(ContentResolver cr, int limit);
|
||||
|
||||
@RobocopTarget
|
||||
public Cursor getBookmarkForUrl(ContentResolver cr, String url);
|
||||
|
||||
public int addDefaultBookmarks(Context context, ContentResolver cr, int offset);
|
||||
public int addDistributionBookmarks(ContentResolver cr, Distribution distribution, int offset);
|
||||
}
|
||||
|
||||
static {
|
||||
// Forcing local DB no option to switch to Android DB for now
|
||||
sDb = null;
|
||||
}
|
||||
private static volatile LocalBrowserDB sDb;
|
||||
private static volatile SuggestedSites sSuggestedSites;
|
||||
private static volatile boolean sAreContentProvidersEnabled = true;
|
||||
|
||||
public static void initialize(String profile) {
|
||||
sDb = new LocalBrowserDB(profile);
|
||||
@ -181,11 +78,6 @@ public class BrowserDB {
|
||||
sDb.invalidateCachedState();
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public static Cursor filter(ContentResolver cr, CharSequence constraint, int limit) {
|
||||
return filter(cr, constraint, limit, EnumSet.noneOf(FilterFlags.class));
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public static Cursor filter(ContentResolver cr, CharSequence constraint, int limit,
|
||||
EnumSet<FilterFlags> flags) {
|
||||
@ -243,13 +135,6 @@ public class BrowserDB {
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateHistoryEntry(ContentResolver cr, String uri, String title,
|
||||
long date, int visits) {
|
||||
if (sAreContentProvidersEnabled) {
|
||||
sDb.updateHistoryEntry(cr, uri, title, date, visits);
|
||||
}
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public static Cursor getAllVisitedHistory(ContentResolver cr) {
|
||||
return (sAreContentProvidersEnabled ? sDb.getAllVisitedHistory(cr) : null);
|
||||
@ -260,16 +145,11 @@ public class BrowserDB {
|
||||
}
|
||||
|
||||
public static void expireHistory(ContentResolver cr, ExpirePriority priority) {
|
||||
if (sDb == null)
|
||||
if (sDb == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (priority == null)
|
||||
priority = ExpirePriority.NORMAL;
|
||||
sDb.expireHistory(cr, priority);
|
||||
}
|
||||
|
||||
public static void removeHistoryEntry(ContentResolver cr, int id) {
|
||||
sDb.removeHistoryEntry(cr, id);
|
||||
sDb.expireHistory(cr, priority == null ? ExpirePriority.NORMAL : priority);
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
@ -296,14 +176,6 @@ public class BrowserDB {
|
||||
return sDb.getUrlForKeyword(cr, keyword);
|
||||
}
|
||||
|
||||
public static boolean isVisited(ContentResolver cr, String uri) {
|
||||
return sDb.isVisited(cr, uri);
|
||||
}
|
||||
|
||||
public static int getReadingListCount(ContentResolver cr) {
|
||||
return sDb.getReadingListCount(cr);
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public static boolean isBookmark(ContentResolver cr, String uri) {
|
||||
return (sAreContentProvidersEnabled && sDb.isBookmark(cr, uri));
|
||||
@ -324,10 +196,6 @@ public class BrowserDB {
|
||||
sDb.addBookmark(cr, title, uri);
|
||||
}
|
||||
|
||||
public static void removeBookmark(ContentResolver cr, int id) {
|
||||
sDb.removeBookmark(cr, id);
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public static void removeBookmarksWithURL(ContentResolver cr, String uri) {
|
||||
sDb.removeBookmarksWithURL(cr, uri);
|
||||
@ -346,10 +214,6 @@ public class BrowserDB {
|
||||
sDb.removeReadingListItemWithURL(cr, uri);
|
||||
}
|
||||
|
||||
public static void removeReadingListItem(ContentResolver cr, int id) {
|
||||
sDb.removeReadingListItem(cr, id);
|
||||
}
|
||||
|
||||
public static LoadFaviconResult getFaviconForFaviconUrl(ContentResolver cr, String faviconURL) {
|
||||
return sDb.getFaviconForUrl(cr, faviconURL);
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import org.mozilla.gecko.distribution.Distribution;
|
||||
import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
|
||||
import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
import org.mozilla.gecko.util.GeckoJarReader;
|
||||
|
||||
@ -55,7 +56,7 @@ import android.provider.Browser;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
public class LocalBrowserDB {
|
||||
// Calculate these once, at initialization. isLoggable is too expensive to
|
||||
// have in-line in each log call.
|
||||
private static final String LOGTAG = "GeckoLocalBrowserDB";
|
||||
@ -145,7 +146,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
* Add default bookmarks to the database.
|
||||
* Takes an offset; returns a new offset.
|
||||
*/
|
||||
@Override
|
||||
public int addDefaultBookmarks(Context context, ContentResolver cr, final int offset) {
|
||||
long folderID = getFolderIdFromGuid(cr, Bookmarks.MOBILE_FOLDER_GUID);
|
||||
if (folderID == -1L) {
|
||||
@ -238,7 +238,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
* Add bookmarks from the provided distribution.
|
||||
* Takes an offset; returns a new offset.
|
||||
*/
|
||||
@Override
|
||||
public int addDistributionBookmarks(ContentResolver cr, Distribution distribution, int offset) {
|
||||
if (!distribution.exists()) {
|
||||
Log.d(LOGTAG, "No distribution from which to add bookmarks.");
|
||||
@ -448,7 +447,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
}
|
||||
|
||||
// Invalidate cached data
|
||||
@Override
|
||||
public void invalidateCachedState() {
|
||||
mDesktopBookmarksExist = null;
|
||||
}
|
||||
@ -535,7 +533,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return new LocalDBCursor(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount(ContentResolver cr, String database) {
|
||||
int count = 0;
|
||||
String[] columns = null;
|
||||
@ -572,7 +569,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
@RobocopTarget
|
||||
public Cursor filter(ContentResolver cr, CharSequence constraint, int limit,
|
||||
EnumSet<FilterFlags> flags) {
|
||||
String selection = "";
|
||||
@ -598,7 +595,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
selection, selectionArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getTopSites(ContentResolver cr, int limit) {
|
||||
// Filter out unvisited bookmarks and the ones that don't have real
|
||||
// parents (e.g. pinned sites or reading list items).
|
||||
@ -622,7 +618,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
selectionArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateVisitedHistory(ContentResolver cr, String uri) {
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
@ -638,7 +633,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
new String[] { uri });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHistoryTitle(ContentResolver cr, String uri, String title) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(History.TITLE, title);
|
||||
@ -649,7 +643,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
new String[] { uri });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHistoryEntry(ContentResolver cr, String uri, String title,
|
||||
long date, int visits) {
|
||||
int oldVisits = 0;
|
||||
@ -682,7 +675,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
new String[] { uri });
|
||||
}
|
||||
|
||||
@Override
|
||||
@RobocopTarget
|
||||
public Cursor getAllVisitedHistory(ContentResolver cr) {
|
||||
Cursor c = cr.query(mHistoryUriWithProfile,
|
||||
new String[] { History.URL },
|
||||
@ -693,7 +686,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return new LocalDBCursor(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getRecentHistory(ContentResolver cr, int limit) {
|
||||
Cursor c = cr.query(combinedUriWithLimit(limit),
|
||||
new String[] { Combined._ID,
|
||||
@ -710,33 +702,30 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return new LocalDBCursor(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireHistory(ContentResolver cr, ExpirePriority priority) {
|
||||
Uri url = mHistoryExpireUriWithProfile;
|
||||
url = url.buildUpon().appendQueryParameter(BrowserContract.PARAM_EXPIRE_PRIORITY, priority.toString()).build();
|
||||
cr.delete(url, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHistoryEntry(ContentResolver cr, int id) {
|
||||
cr.delete(mHistoryUriWithProfile,
|
||||
History._ID + " = ?",
|
||||
new String[] { String.valueOf(id) });
|
||||
}
|
||||
|
||||
@Override
|
||||
@RobocopTarget
|
||||
public void removeHistoryEntry(ContentResolver cr, String url) {
|
||||
cr.delete(mHistoryUriWithProfile,
|
||||
History.URL + " = ?",
|
||||
new String[] { url });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearHistory(ContentResolver cr) {
|
||||
cr.delete(mHistoryUriWithProfile, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RobocopTarget
|
||||
public Cursor getBookmarksInFolder(ContentResolver cr, long folderId) {
|
||||
Cursor c = null;
|
||||
boolean addDesktopFolder = false;
|
||||
@ -745,7 +734,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
if (folderId == Bookmarks.FIXED_ROOT_ID) {
|
||||
folderId = getFolderIdFromGuid(cr, Bookmarks.MOBILE_FOLDER_GUID);
|
||||
|
||||
// We'll add a fake "Desktop Bookmarks" folder to the root view if desktop
|
||||
// We'll add a fake "Desktop Bookmarks" folder to the root view if desktop
|
||||
// bookmarks exist, so that the user can still access non-mobile bookmarks.
|
||||
addDesktopFolder = desktopBookmarksExist(cr);
|
||||
}
|
||||
@ -784,7 +773,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return new LocalDBCursor(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getReadingList(ContentResolver cr) {
|
||||
return cr.query(mReadingListUriWithProfile,
|
||||
ReadingListItems.DEFAULT_PROJECTION,
|
||||
@ -825,7 +813,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return mDesktopBookmarksExist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadingListCount(ContentResolver cr) {
|
||||
Cursor c = null;
|
||||
try {
|
||||
@ -842,7 +829,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@RobocopTarget
|
||||
public boolean isBookmark(ContentResolver cr, String uri) {
|
||||
Cursor c = null;
|
||||
try {
|
||||
@ -864,7 +851,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadingListItem(ContentResolver cr, String uri) {
|
||||
Cursor c = null;
|
||||
try {
|
||||
@ -892,7 +878,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
*
|
||||
* This will expand as necessary to eliminate multiple consecutive queries.
|
||||
*/
|
||||
@Override
|
||||
public int getItemFlags(ContentResolver cr, String uri) {
|
||||
final Cursor c = cr.query(mFlagsUriWithProfile,
|
||||
null,
|
||||
@ -912,7 +897,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlForKeyword(ContentResolver cr, String keyword) {
|
||||
Cursor c = null;
|
||||
try {
|
||||
@ -1021,13 +1005,12 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
debug("Updated " + updated + " rows to new modified time.");
|
||||
}
|
||||
|
||||
@Override
|
||||
@RobocopTarget
|
||||
public void addBookmark(ContentResolver cr, String title, String uri) {
|
||||
long folderId = getFolderIdFromGuid(cr, Bookmarks.MOBILE_FOLDER_GUID);
|
||||
addBookmarkItem(cr, title, uri, folderId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBookmark(ContentResolver cr, int id) {
|
||||
Uri contentUri = mBookmarksUriWithProfile;
|
||||
|
||||
@ -1040,7 +1023,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
cr.delete(contentUri, idEquals, idArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RobocopTarget
|
||||
public void removeBookmarksWithURL(ContentResolver cr, String uri) {
|
||||
Uri contentUri = mBookmarksUriWithProfile;
|
||||
|
||||
@ -1053,7 +1036,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
cr.delete(contentUri, urlEquals, urlArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addReadingListItem(ContentResolver cr, ContentValues values) {
|
||||
// Check that required fields are present.
|
||||
for (String field: ReadingListItems.REQUIRED_FIELDS) {
|
||||
@ -1079,27 +1061,23 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
debug("Updated " + updated + " rows to new modified time.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeReadingListItemWithURL(ContentResolver cr, String uri) {
|
||||
cr.delete(mReadingListUriWithProfile, ReadingListItems.URL + " = ? ", new String[] { uri });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeReadingListItem(ContentResolver cr, int id) {
|
||||
cr.delete(mReadingListUriWithProfile, ReadingListItems._ID + " = ? ", new String[] { String.valueOf(id) });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer) {
|
||||
cr.registerContentObserver(mBookmarksUriWithProfile, false, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerHistoryObserver(ContentResolver cr, ContentObserver observer) {
|
||||
cr.registerContentObserver(mHistoryUriWithProfile, false, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RobocopTarget
|
||||
public void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Browser.BookmarkColumns.TITLE, title);
|
||||
@ -1120,7 +1098,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
* @param faviconURL The URL of the favicon to fetch from the database.
|
||||
* @return The decoded Bitmap from the database, if any. null if none is stored.
|
||||
*/
|
||||
@Override
|
||||
public LoadFaviconResult getFaviconForUrl(ContentResolver cr, String faviconURL) {
|
||||
Cursor c = null;
|
||||
byte[] b = null;
|
||||
@ -1151,7 +1128,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return FaviconDecoder.decodeFavicon(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFaviconUrlForHistoryUrl(ContentResolver cr, String uri) {
|
||||
Cursor c = null;
|
||||
|
||||
@ -1172,7 +1148,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFaviconForUrl(ContentResolver cr, String pageUri,
|
||||
byte[] encodedFavicon, String faviconUri) {
|
||||
ContentValues values = new ContentValues();
|
||||
@ -1190,7 +1165,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
new String[] { faviconUri });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateThumbnailForUrl(ContentResolver cr, String uri,
|
||||
BitmapDrawable thumbnail) {
|
||||
|
||||
@ -1222,7 +1196,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
new String[] { uri });
|
||||
}
|
||||
|
||||
@Override
|
||||
@RobocopTarget
|
||||
public byte[] getThumbnailForUrl(ContentResolver cr, String uri) {
|
||||
Cursor c = null;
|
||||
byte[] b = null;
|
||||
@ -1255,7 +1229,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
*
|
||||
* Returns null if the provided list of URLs is empty or null.
|
||||
*/
|
||||
@Override
|
||||
public Cursor getThumbnailsForUrls(ContentResolver cr, List<String> urls) {
|
||||
if (urls == null) {
|
||||
return null;
|
||||
@ -1278,7 +1251,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RobocopTarget
|
||||
public void removeThumbnails(ContentResolver cr) {
|
||||
cr.delete(mThumbnailsUriWithProfile, null, null);
|
||||
}
|
||||
@ -1461,12 +1434,10 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return super.getCount() + mIndexOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToPosition(int position) {
|
||||
mAtDesktopBookmarksPosition = (mDesktopBookmarksIndex == position);
|
||||
|
||||
@ -1476,7 +1447,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return super.moveToPosition(position - mIndexOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int columnIndex) {
|
||||
if (!mAtDesktopBookmarksPosition)
|
||||
return super.getLong(columnIndex);
|
||||
@ -1488,7 +1458,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int columnIndex) {
|
||||
if (!mAtDesktopBookmarksPosition)
|
||||
return super.getInt(columnIndex);
|
||||
@ -1502,7 +1471,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int columnIndex) {
|
||||
if (!mAtDesktopBookmarksPosition)
|
||||
return super.getString(columnIndex);
|
||||
@ -1535,19 +1503,16 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return columnName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndex(String columnName) {
|
||||
return super.getColumnIndex(translateColumnName(columnName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndexOrThrow(String columnName) {
|
||||
return super.getColumnIndexOrThrow(translateColumnName(columnName));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void pinSite(ContentResolver cr, String url, String title, int position) {
|
||||
ContentValues values = new ContentValues();
|
||||
final long now = System.currentTimeMillis();
|
||||
@ -1572,7 +1537,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
String.valueOf(Bookmarks.FIXED_PINNED_LIST_ID) });
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getPinnedSites(ContentResolver cr, int limit) {
|
||||
return cr.query(bookmarksUriWithLimit(limit),
|
||||
new String[] { Bookmarks._ID,
|
||||
@ -1584,7 +1548,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
Bookmarks.POSITION + " ASC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpinSite(ContentResolver cr, int position) {
|
||||
cr.delete(mBookmarksUriWithProfile,
|
||||
Bookmarks.PARENT + " == ? AND " + Bookmarks.POSITION + " = ?",
|
||||
@ -1594,7 +1557,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpinAllSites(ContentResolver cr) {
|
||||
cr.delete(mBookmarksUriWithProfile,
|
||||
Bookmarks.PARENT + " == ?",
|
||||
@ -1603,7 +1565,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisited(ContentResolver cr, String uri) {
|
||||
int count = 0;
|
||||
Cursor c = null;
|
||||
@ -1625,6 +1586,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return (count > 0);
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public Cursor getBookmarkForUrl(ContentResolver cr, String url) {
|
||||
Cursor c = cr.query(bookmarksUriWithLimit(1),
|
||||
new String[] { Bookmarks._ID,
|
||||
|
@ -17,10 +17,11 @@
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<ImageView android:id="@+id/home_empty_image"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="top|center"
|
||||
android:scaleType="fitCenter"/>
|
||||
android:scaleType="center"/>
|
||||
|
||||
<TextView android:id="@+id/home_empty_text"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -92,6 +92,9 @@ function Tester(aTests, aDumper, aCallback) {
|
||||
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/MemoryStats.js", simpleTestScope);
|
||||
this._scriptLoader.loadSubScript("chrome://mochikit/content/chrome-harness.js", simpleTestScope);
|
||||
this.SimpleTest = simpleTestScope.SimpleTest;
|
||||
|
||||
this.SimpleTest.harnessParameters = gConfig;
|
||||
|
||||
this.MemoryStats = simpleTestScope.MemoryStats;
|
||||
this.Task = Task;
|
||||
this.Task.Debugging.maintainStack = true;
|
||||
|
@ -337,9 +337,10 @@ class MochitestRunner(MozbuildObject):
|
||||
manifest = TestManifest()
|
||||
manifest.tests.extend(tests)
|
||||
|
||||
if (len(tests) == 1):
|
||||
options.closeWhenDone = False
|
||||
|
||||
options.manifestFile = manifest
|
||||
if len(test_paths) == 1 and len(tests) == 1:
|
||||
options.testPath = test_paths[0]
|
||||
|
||||
if rerun_failures:
|
||||
options.testManifest = failure_file_path
|
||||
|
@ -637,6 +637,9 @@ def main():
|
||||
dm.killProcess(procName)
|
||||
|
||||
if options.robocopIni != "":
|
||||
# turning buffering off as it's not used in robocop
|
||||
message_logger.buffering = False
|
||||
|
||||
# sut may wait up to 300 s for a robocop am process before returning
|
||||
dm.default_timeout = 320
|
||||
mp = manifestparser.TestManifest(strict=False)
|
||||
@ -647,10 +650,6 @@ def main():
|
||||
my_tests = tests
|
||||
for test in robocop_tests:
|
||||
tests.append(test['name'])
|
||||
# suite_start message when running robocop tests
|
||||
log.suite_start(tests)
|
||||
# turning buffering off as it's not used in robocop
|
||||
message_logger.buffering = False
|
||||
|
||||
if options.totalChunks:
|
||||
tests_per_chunk = math.ceil(len(tests) / (options.totalChunks * 1.0))
|
||||
@ -676,6 +675,8 @@ def main():
|
||||
dm._checkCmd(["install", "-r", options.robocopApk])
|
||||
|
||||
retVal = None
|
||||
# Filtering tests
|
||||
active_tests = []
|
||||
for test in robocop_tests:
|
||||
if options.testPath and options.testPath != test['name']:
|
||||
continue
|
||||
@ -687,6 +688,11 @@ def main():
|
||||
log.info('TEST-INFO | skipping %s | %s' % (test['name'], test['disabled']))
|
||||
continue
|
||||
|
||||
active_tests.append(test)
|
||||
|
||||
log.suite_start([t['name'] for t in active_tests])
|
||||
|
||||
for test in active_tests:
|
||||
# When running in a loop, we need to create a fresh profile for each cycle
|
||||
if mochitest.localProfile:
|
||||
options.profilePath = mochitest.localProfile
|
||||
|
@ -673,7 +673,7 @@ function testListing(metadata, response)
|
||||
),
|
||||
DIV({class: "clear"}),
|
||||
DIV({class: "frameholder"},
|
||||
IFRAME({scrolling: "no", id: "testframe", width: "500", height: "300"})
|
||||
IFRAME({scrolling: "no", id: "testframe"})
|
||||
),
|
||||
DIV({class: "clear"}),
|
||||
DIV({class: "toggle"},
|
||||
|
@ -95,3 +95,33 @@ div#current-test {
|
||||
margin: 0;
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
#testframe {
|
||||
width: 500px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
|
||||
body[singletest=true] table,
|
||||
body[singletest=true] h2,
|
||||
body[singletest=true] p,
|
||||
body[singletest=true] br,
|
||||
body[singletest=true] .clear,
|
||||
body[singletest=true] .toggle,
|
||||
body[singletest=true] #current-test,
|
||||
body[singletest=true] .status {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
body[singletest=true],
|
||||
body[singletest=true] .container,
|
||||
body[singletest=true] .frameholder,
|
||||
body[singletest=true] #testframe {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g'
|
||||
[test_sanity.html]
|
||||
[test_sanityException.html]
|
||||
[test_sanityException2.html]
|
||||
[test_sanityParams.html]
|
||||
[test_sanityWindowSnapshot.html]
|
||||
[test_SpecialPowersExtension.html]
|
||||
[test_SpecialPowersExtension2.html]
|
||||
|
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for exposing test suite information</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
ok(SimpleTest.harnessParameters, "Should have parameters.");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -50,6 +50,10 @@ var isPrimaryTestWindow = !!parent.TestRunner || isSingleTestRun;
|
||||
}
|
||||
w = ancestor(w);
|
||||
}
|
||||
|
||||
if (parentRunner) {
|
||||
SimpleTest.harnessParameters = parentRunner.getParameterInfo();
|
||||
}
|
||||
})();
|
||||
|
||||
/* Helper functions pulled out of various MochiKit modules */
|
||||
@ -841,7 +845,9 @@ SimpleTest.finish = function() {
|
||||
if (parentRunner) {
|
||||
/* We're running in an iframe, and the parent has a TestRunner */
|
||||
parentRunner.testFinished(SimpleTest._tests);
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!parentRunner || parentRunner.showTestReport) {
|
||||
SpecialPowers.flushAllAppsLaunchable();
|
||||
SpecialPowers.flushPermissions(function () {
|
||||
SpecialPowers.flushPrefEnv(function() {
|
||||
|
@ -492,6 +492,14 @@ TestRunner.getLoadedTestURL = function () {
|
||||
return prefix + $('testframe').contentWindow.location.pathname;
|
||||
};
|
||||
|
||||
TestRunner.setParameterInfo = function (params) {
|
||||
this._params = params;
|
||||
};
|
||||
|
||||
TestRunner.getParameterInfo = function() {
|
||||
return this._params;
|
||||
};
|
||||
|
||||
/**
|
||||
* TestRunner entry point.
|
||||
*
|
||||
@ -506,7 +514,14 @@ TestRunner.runTests = function (/*url...*/) {
|
||||
|
||||
TestRunner._urls = flattenArguments(arguments);
|
||||
|
||||
$('testframe').src="";
|
||||
var singleTestRun = this._urls.length <= 1 && TestRunner.repeat <= 1;
|
||||
TestRunner.showTestReport = singleTestRun;
|
||||
var frame = $('testframe');
|
||||
frame.src="";
|
||||
if (singleTestRun) {
|
||||
document.body.setAttribute("singletest", singleTestRun);
|
||||
frame.removeAttribute("scrolling");
|
||||
}
|
||||
TestRunner._checkForHangs();
|
||||
TestRunner.runNextTest();
|
||||
};
|
||||
@ -551,7 +566,10 @@ TestRunner.runNextTest = function() {
|
||||
TestRunner._makeIframe(url, 0);
|
||||
} else {
|
||||
$("current-test").innerHTML = "<b>Finished</b>";
|
||||
TestRunner._makeIframe("about:blank", 0);
|
||||
// Only unload the last test to run if we're running more than one test.
|
||||
if (TestRunner._urls.length > 1) {
|
||||
TestRunner._makeIframe("about:blank", 0);
|
||||
}
|
||||
|
||||
var passCount = parseInt($("pass-count").innerHTML, 10);
|
||||
var failCount = parseInt($("fail-count").innerHTML, 10);
|
||||
@ -694,6 +712,12 @@ TestRunner.testFinished = function(tests) {
|
||||
|
||||
TestRunner.updateUI(tests);
|
||||
|
||||
// Don't show the interstitial if we just run one test with no repeats:
|
||||
if (TestRunner._urls.length == 1 && TestRunner.repeat <= 1) {
|
||||
TestRunner.testUnloaded();
|
||||
return;
|
||||
}
|
||||
|
||||
var interstitialURL;
|
||||
if ($('testframe').contentWindow.location.protocol == "chrome:") {
|
||||
interstitialURL = "tests/SimpleTest/iframe-between-tests.html";
|
||||
|
@ -176,6 +176,7 @@ RunSet.runtests = function(e) {
|
||||
my_tests[i] = tmp;
|
||||
}
|
||||
}
|
||||
TestRunner.setParameterInfo(params);
|
||||
TestRunner.runTests(my_tests);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ support-files =
|
||||
[browser_async.js]
|
||||
[browser_head.js]
|
||||
[browser_pass.js]
|
||||
[browser_parameters.js]
|
||||
[browser_popupNode.js]
|
||||
[browser_popupNode_check.js]
|
||||
[browser_privileges.js]
|
||||
|
4
testing/mochitest/tests/browser/browser_parameters.js
Normal file
@ -0,0 +1,4 @@
|
||||
function test() {
|
||||
ok(SimpleTest.harnessParameters, "Should have parameters");
|
||||
}
|
||||
|
@ -22,3 +22,4 @@
|
||||
[include:b2g/components/test/unit/xpcshell.ini]
|
||||
[include:security/manager/ssl/tests/unit/xpcshell.ini]
|
||||
[include:toolkit/devtools/discovery/tests/unit/xpcshell.ini]
|
||||
[include:toolkit/devtools/security/tests/unit/xpcshell.ini]
|
||||
|
@ -544,10 +544,16 @@ Entry.prototype = {
|
||||
},
|
||||
|
||||
_mediaToEnclosures: function Entry_mediaToEnclosures(mediaType, contentType) {
|
||||
var content = this.fields.getPropertyAsInterface(mediaType, Ci.nsIArray);
|
||||
var content;
|
||||
|
||||
if (contentType)
|
||||
content = content.getPropertyAsInterface(contentType, Ci.nsIArray);
|
||||
// If a contentType is specified, the mediaType is a simple propertybag,
|
||||
// and the contentType is an array inside it.
|
||||
if (contentType) {
|
||||
var group = this.fields.getPropertyAsInterface(mediaType, Ci.nsIPropertyBag2);
|
||||
content = group.getPropertyAsInterface(contentType, Ci.nsIArray);
|
||||
} else {
|
||||
content = this.fields.getPropertyAsInterface(mediaType, Ci.nsIArray);
|
||||
}
|
||||
|
||||
for (var i = 0; i < content.length; ++i) {
|
||||
var contentElement = content.queryElementAt(i, Ci.nsIWritablePropertyBag2);
|
||||
|
@ -5,20 +5,21 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += [
|
||||
'server',
|
||||
'acorn',
|
||||
'apps',
|
||||
'client',
|
||||
'discovery',
|
||||
'gcli',
|
||||
'jsbeautify',
|
||||
'sourcemap',
|
||||
'webconsole',
|
||||
'apps',
|
||||
'styleinspector',
|
||||
'acorn',
|
||||
'pretty-fast',
|
||||
'qrcode',
|
||||
'transport',
|
||||
'security',
|
||||
'server',
|
||||
'sourcemap',
|
||||
'styleinspector',
|
||||
'tern',
|
||||
'discovery'
|
||||
'transport',
|
||||
'webconsole',
|
||||
]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
|
||||
|
498
toolkit/devtools/security/LocalCertService.cpp
Normal file
@ -0,0 +1,498 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "LocalCertService.h"
|
||||
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "cert.h"
|
||||
#include "CryptoTask.h"
|
||||
#include "nsIPK11Token.h"
|
||||
#include "nsIPK11TokenDB.h"
|
||||
#include "nsIX509Cert.h"
|
||||
#include "nsIX509CertDB.h"
|
||||
#include "nsIX509CertValidity.h"
|
||||
#include "nsLiteralString.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "pk11pub.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class LocalCertTask : public CryptoTask
|
||||
{
|
||||
protected:
|
||||
LocalCertTask(const nsACString& aNickname)
|
||||
: mNickname(aNickname)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult RemoveExisting()
|
||||
{
|
||||
// Search for any existing certs with this name and remove them
|
||||
nsresult rv;
|
||||
|
||||
for (;;) {
|
||||
ScopedCERTCertificate cert(
|
||||
PK11_FindCertFromNickname(mNickname.get(), nullptr));
|
||||
if (!cert) {
|
||||
return NS_OK; // All done
|
||||
}
|
||||
|
||||
// Found a cert, check if generated by this service
|
||||
if (!cert->isRoot) {
|
||||
return NS_ERROR_UNEXPECTED; // Should be self-signed
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN=");
|
||||
nsAutoCString subjectNameFromNickname(commonNamePrefix + mNickname);
|
||||
if (!subjectNameFromNickname.Equals(cert->subjectName)) {
|
||||
return NS_ERROR_UNEXPECTED; // Subject should match nickname
|
||||
}
|
||||
if (!subjectNameFromNickname.Equals(cert->issuerName)) {
|
||||
return NS_ERROR_UNEXPECTED; // Issuer should match nickname
|
||||
}
|
||||
|
||||
rv = MapSECStatus(PK11_DeleteTokenCertAndKey(cert, nullptr));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv; // Some error, abort the loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCString mNickname;
|
||||
};
|
||||
|
||||
class LocalCertGetTask MOZ_FINAL : public LocalCertTask
|
||||
{
|
||||
public:
|
||||
LocalCertGetTask(const nsACString& aNickname,
|
||||
nsILocalCertGetCallback* aCallback)
|
||||
: LocalCertTask(aNickname)
|
||||
, mCallback(new nsMainThreadPtrHolder<nsILocalCertGetCallback>(aCallback))
|
||||
, mCert(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual nsresult CalculateResult() MOZ_OVERRIDE
|
||||
{
|
||||
// Try to lookup an existing cert in the DB
|
||||
nsresult rv = GetFromDB();
|
||||
// Make a new one if getting fails
|
||||
if (NS_FAILED(rv)) {
|
||||
rv = Generate();
|
||||
}
|
||||
// If generation fails, we're out of luck
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Validate cert, make a new one if it fails
|
||||
rv = Validate();
|
||||
if (NS_FAILED(rv)) {
|
||||
rv = Generate();
|
||||
}
|
||||
// If generation fails, we're out of luck
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Generate()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Get the key slot for generation later
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Remove existing certs with this name (if any)
|
||||
rv = RemoveExisting();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Generate a new cert
|
||||
NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN=");
|
||||
nsAutoCString subjectNameStr(commonNamePrefix + mNickname);
|
||||
ScopedCERTName subjectName(CERT_AsciiToName(subjectNameStr.get()));
|
||||
if (!subjectName) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Use the well-known NIST P-256 curve
|
||||
SECOidData* curveOidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
|
||||
if (!curveOidData) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Get key params from the curve
|
||||
ScopedAutoSECItem keyParams(2 + curveOidData->oid.len);
|
||||
keyParams.data[0] = SEC_ASN1_OBJECT_ID;
|
||||
keyParams.data[1] = curveOidData->oid.len;
|
||||
memcpy(keyParams.data + 2, curveOidData->oid.data, curveOidData->oid.len);
|
||||
|
||||
// Generate cert key pair
|
||||
ScopedSECKEYPrivateKey privateKey;
|
||||
ScopedSECKEYPublicKey publicKey;
|
||||
SECKEYPublicKey* tempPublicKey;
|
||||
privateKey = PK11_GenerateKeyPair(slot, CKM_EC_KEY_PAIR_GEN, &keyParams,
|
||||
&tempPublicKey, true /* token */,
|
||||
true /* sensitive */, nullptr);
|
||||
if (!privateKey) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
publicKey = tempPublicKey;
|
||||
|
||||
// Create subject public key info and cert request
|
||||
ScopedCERTSubjectPublicKeyInfo spki(
|
||||
SECKEY_CreateSubjectPublicKeyInfo(publicKey));
|
||||
if (!spki) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
ScopedCERTCertificateRequest certRequest(
|
||||
CERT_CreateCertificateRequest(subjectName, spki, nullptr));
|
||||
if (!certRequest) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Valid from one day before to 1 year after
|
||||
static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
|
||||
* PRTime(60) // sec
|
||||
* PRTime(60) // min
|
||||
* PRTime(24); // hours
|
||||
|
||||
PRTime now = PR_Now();
|
||||
PRTime notBefore = now - oneDay;
|
||||
PRTime notAfter = now + (PRTime(365) * oneDay);
|
||||
ScopedCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
|
||||
if (!validity) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Generate random serial
|
||||
unsigned long serial;
|
||||
// This serial in principle could collide, but it's unlikely
|
||||
rv = MapSECStatus(
|
||||
PK11_GenerateRandomOnSlot(slot,
|
||||
reinterpret_cast<unsigned char *>(&serial),
|
||||
sizeof(serial)));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Create the cert from these pieces
|
||||
ScopedCERTCertificate cert(
|
||||
CERT_CreateCertificate(serial, subjectName, validity, certRequest));
|
||||
if (!cert) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Update the cert version to X509v3
|
||||
if (!cert->version.data) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
*(cert->version.data) = SEC_CERTIFICATE_VERSION_3;
|
||||
cert->version.len = 1;
|
||||
|
||||
// Set cert signature algorithm
|
||||
PLArenaPool* arena = cert->arena;
|
||||
if (!arena) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
rv = MapSECStatus(
|
||||
SECOID_SetAlgorithmID(arena, &cert->signature,
|
||||
SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Encode and self-sign the cert
|
||||
ScopedSECItem certDER(
|
||||
SEC_ASN1EncodeItem(nullptr, nullptr, cert,
|
||||
SEC_ASN1_GET(CERT_CertificateTemplate)));
|
||||
if (!certDER) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
rv = MapSECStatus(
|
||||
SEC_DerSignData(arena, &cert->derCert, certDER->data, certDER->len,
|
||||
privateKey,
|
||||
SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Create a CERTCertificate from the signed data
|
||||
ScopedCERTCertificate certFromDER(
|
||||
CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &cert->derCert, nullptr,
|
||||
true /* perm */, true /* copyDER */));
|
||||
if (!certFromDER) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// Save the cert in the DB
|
||||
rv = MapSECStatus(PK11_ImportCert(slot, certFromDER, CK_INVALID_HANDLE,
|
||||
mNickname.get(), false /* unused */));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// We should now have cert in the DB, read it back in nsIX509Cert form
|
||||
return GetFromDB();
|
||||
}
|
||||
|
||||
nsresult GetFromDB()
|
||||
{
|
||||
nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
|
||||
if (!certDB) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIX509Cert> certFromDB;
|
||||
nsresult rv;
|
||||
rv = certDB->FindCertByNickname(nullptr, NS_ConvertASCIItoUTF16(mNickname),
|
||||
getter_AddRefs(certFromDB));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
mCert = certFromDB;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Validate()
|
||||
{
|
||||
// Verify cert is self-signed
|
||||
bool selfSigned;
|
||||
nsresult rv = mCert->GetIsSelfSigned(&selfSigned);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!selfSigned) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Check that subject and issuer match nickname
|
||||
nsXPIDLString subjectName;
|
||||
nsXPIDLString issuerName;
|
||||
mCert->GetSubjectName(subjectName);
|
||||
mCert->GetIssuerName(issuerName);
|
||||
if (!subjectName.Equals(issuerName)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
NS_NAMED_LITERAL_STRING(commonNamePrefix, "CN=");
|
||||
nsAutoString subjectNameFromNickname(
|
||||
commonNamePrefix + NS_ConvertASCIItoUTF16(mNickname));
|
||||
if (!subjectName.Equals(subjectNameFromNickname)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIX509CertValidity> validity;
|
||||
mCert->GetValidity(getter_AddRefs(validity));
|
||||
|
||||
PRTime notBefore, notAfter;
|
||||
validity->GetNotBefore(¬Before);
|
||||
validity->GetNotAfter(¬After);
|
||||
|
||||
// Ensure cert will last at least one more day
|
||||
static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
|
||||
* PRTime(60) // sec
|
||||
* PRTime(60) // min
|
||||
* PRTime(24); // hours
|
||||
PRTime now = PR_Now();
|
||||
if (notBefore > now ||
|
||||
notAfter < (now - oneDay)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual void ReleaseNSSResources() {}
|
||||
|
||||
virtual void CallCallback(nsresult rv)
|
||||
{
|
||||
(void) mCallback->HandleCert(mCert, rv);
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<nsILocalCertGetCallback> mCallback;
|
||||
nsCOMPtr<nsIX509Cert> mCert; // out
|
||||
};
|
||||
|
||||
class LocalCertRemoveTask MOZ_FINAL : public LocalCertTask
|
||||
{
|
||||
public:
|
||||
LocalCertRemoveTask(const nsACString& aNickname,
|
||||
nsILocalCertCallback* aCallback)
|
||||
: LocalCertTask(aNickname)
|
||||
, mCallback(new nsMainThreadPtrHolder<nsILocalCertCallback>(aCallback))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual nsresult CalculateResult() MOZ_OVERRIDE
|
||||
{
|
||||
return RemoveExisting();
|
||||
}
|
||||
|
||||
virtual void ReleaseNSSResources() {}
|
||||
|
||||
virtual void CallCallback(nsresult rv)
|
||||
{
|
||||
(void) mCallback->HandleResult(rv);
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<nsILocalCertCallback> mCallback;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(LocalCertService, nsILocalCertService)
|
||||
|
||||
LocalCertService::LocalCertService()
|
||||
{
|
||||
}
|
||||
|
||||
LocalCertService::~LocalCertService()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
LocalCertService::LoginToKeySlot()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Get access to key slot
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// If no user password yet, set it an empty one
|
||||
if (PK11_NeedUserInit(slot)) {
|
||||
rv = MapSECStatus(PK11_InitPin(slot, "", ""));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// If user has a password set, prompt to login
|
||||
if (PK11_NeedLogin(slot) && !PK11_IsLoggedIn(slot, nullptr)) {
|
||||
// Switching to XPCOM to get the UI prompt that PSM owns
|
||||
nsCOMPtr<nsIPK11TokenDB> tokenDB =
|
||||
do_GetService(NS_PK11TOKENDB_CONTRACTID);
|
||||
if (!tokenDB) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsIPK11Token> keyToken;
|
||||
tokenDB->GetInternalKeyToken(getter_AddRefs(keyToken));
|
||||
if (!keyToken) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// Prompt the user to login
|
||||
return keyToken->Login(false /* force */);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LocalCertService::GetOrCreateCert(const nsACString& aNickname,
|
||||
nsILocalCertGetCallback* aCallback)
|
||||
{
|
||||
if (NS_WARN_IF(aNickname.IsEmpty())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (NS_WARN_IF(!aCallback)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
|
||||
// Before sending off the task, login to key slot if needed
|
||||
nsresult rv = LoginToKeySlot();
|
||||
if (NS_FAILED(rv)) {
|
||||
aCallback->HandleCert(nullptr, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<LocalCertGetTask> task(new LocalCertGetTask(aNickname, aCallback));
|
||||
return task->Dispatch("LocalCertGet");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LocalCertService::RemoveCert(const nsACString& aNickname,
|
||||
nsILocalCertCallback* aCallback)
|
||||
{
|
||||
if (NS_WARN_IF(aNickname.IsEmpty())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (NS_WARN_IF(!aCallback)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
|
||||
// Before sending off the task, login to key slot if needed
|
||||
nsresult rv = LoginToKeySlot();
|
||||
if (NS_FAILED(rv)) {
|
||||
aCallback->HandleResult(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<LocalCertRemoveTask> task(
|
||||
new LocalCertRemoveTask(aNickname, aCallback));
|
||||
return task->Dispatch("LocalCertRm");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LocalCertService::GetLoginPromptRequired(bool* aRequired)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Get access to key slot
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
|
||||
if (!slot) {
|
||||
return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
|
||||
}
|
||||
|
||||
// If no user password yet, set it an empty one
|
||||
if (PK11_NeedUserInit(slot)) {
|
||||
rv = MapSECStatus(PK11_InitPin(slot, "", ""));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
*aRequired = PK11_NeedLogin(slot) && !PK11_IsLoggedIn(slot, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#define LOCALCERTSERVICE_CID \
|
||||
{ 0x47402be2, 0xe653, 0x45d0, \
|
||||
{ 0x8d, 0xaa, 0x9f, 0x0d, 0xce, 0x0a, 0xc1, 0x48 } }
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(LocalCertService)
|
||||
|
||||
NS_DEFINE_NAMED_CID(LOCALCERTSERVICE_CID);
|
||||
|
||||
static const Module::CIDEntry kLocalCertServiceCIDs[] = {
|
||||
{ &kLOCALCERTSERVICE_CID, false, nullptr, LocalCertServiceConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const Module::ContractIDEntry kLocalCertServiceContracts[] = {
|
||||
{ LOCALCERTSERVICE_CONTRACTID, &kLOCALCERTSERVICE_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const Module kLocalCertServiceModule = {
|
||||
Module::kVersion,
|
||||
kLocalCertServiceCIDs,
|
||||
kLocalCertServiceContracts
|
||||
};
|
||||
|
||||
NSMODULE_DEFN(LocalCertServiceModule) = &kLocalCertServiceModule;
|
||||
|
||||
} // namespace mozilla
|
27
toolkit/devtools/security/LocalCertService.h
Normal file
@ -0,0 +1,27 @@
|
||||
/* 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/. */
|
||||
|
||||
#ifndef LocalCertService_h
|
||||
#define LocalCertService_h
|
||||
|
||||
#include "nsILocalCertService.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class LocalCertService MOZ_FINAL : public nsILocalCertService
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSILOCALCERTSERVICE
|
||||
|
||||
LocalCertService();
|
||||
|
||||
private:
|
||||
nsresult LoginToKeySlot();
|
||||
~LocalCertService();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // LocalCertService_h
|
21
toolkit/devtools/security/moz.build
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsILocalCertService.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'devtools_security'
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'LocalCertService.cpp',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
69
toolkit/devtools/security/nsILocalCertService.idl
Normal file
@ -0,0 +1,69 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIX509Cert;
|
||||
interface nsILocalCertGetCallback;
|
||||
interface nsILocalCertCallback;
|
||||
|
||||
[scriptable, uuid(9702fdd4-4c2c-439c-ba2e-19cda018eb99)]
|
||||
interface nsILocalCertService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Get or create a new self-signed X.509 cert to represent this device over a
|
||||
* secure transport, like TLS.
|
||||
*
|
||||
* The cert is stored permanently in the profile's key store after first use,
|
||||
* and is valid for 1 year. If an expired or otherwise invalid cert is found
|
||||
* with the nickname supplied here, it is removed and a new one is made.
|
||||
*
|
||||
* @param nickname Nickname that identifies the cert
|
||||
* @param cb Callback to be notified with the result
|
||||
*/
|
||||
void getOrCreateCert(in ACString nickname, in nsILocalCertGetCallback cb);
|
||||
|
||||
/**
|
||||
* Remove a X.509 cert with the given nickname.
|
||||
*
|
||||
* @param nickname Nickname that identifies the cert
|
||||
* @param cb Callback to be notified with the result
|
||||
*/
|
||||
void removeCert(in ACString nickname, in nsILocalCertCallback cb);
|
||||
|
||||
/**
|
||||
* Whether calling |getOrCreateCert| or |removeCert| will trigger a login
|
||||
* prompt to be displayed. Generally this happens if the user has set a
|
||||
* master password, but has not yet logged in.
|
||||
*/
|
||||
readonly attribute boolean loginPromptRequired;
|
||||
};
|
||||
|
||||
[scriptable, uuid(cc09633e-7c70-4093-a9cf-79ab676ca8a9)]
|
||||
interface nsILocalCertGetCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called with the result of the getOrCreateCert operation above.
|
||||
*
|
||||
* @param cert Requested cert, or null if some error
|
||||
* @param result Result code from the get operation
|
||||
*/
|
||||
void handleCert(in nsIX509Cert cert, in nsresult result);
|
||||
};
|
||||
|
||||
[scriptable, uuid(518124e9-55e6-4e23-97c0-4995b3a1bec6)]
|
||||
interface nsILocalCertCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called with the result of the removeCert operation above.
|
||||
*
|
||||
* @param result Result code from the operation
|
||||
*/
|
||||
void handleResult(in nsresult result);
|
||||
};
|
||||
|
||||
%{ C++
|
||||
#define LOCALCERTSERVICE_CONTRACTID \
|
||||
"@mozilla.org/security/local-cert-service;1"
|
||||
%}
|
87
toolkit/devtools/security/tests/unit/test_cert.js
Normal file
@ -0,0 +1,87 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
|
||||
|
||||
const { Promise: promise } =
|
||||
Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const certService = Cc["@mozilla.org/security/local-cert-service;1"]
|
||||
.getService(Ci.nsILocalCertService);
|
||||
|
||||
const gNickname = "devtools";
|
||||
|
||||
function run_test() {
|
||||
// Need profile dir to store the key / cert
|
||||
do_get_profile();
|
||||
// Ensure PSM is initialized
|
||||
Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function getOrCreateCert(nickname) {
|
||||
let deferred = promise.defer();
|
||||
certService.getOrCreateCert(nickname, {
|
||||
handleCert: function(c, rv) {
|
||||
if (rv) {
|
||||
deferred.reject(rv);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(c);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function removeCert(nickname) {
|
||||
let deferred = promise.defer();
|
||||
certService.removeCert(nickname, {
|
||||
handleResult: function(rv) {
|
||||
if (rv) {
|
||||
deferred.reject(rv);
|
||||
return;
|
||||
}
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
// No master password, so prompt required here
|
||||
ok(!certService.loginPromptRequired);
|
||||
|
||||
let certA = yield getOrCreateCert(gNickname);
|
||||
equal(certA.nickname, gNickname);
|
||||
|
||||
// Getting again should give the same cert
|
||||
let certB = yield getOrCreateCert(gNickname);
|
||||
equal(certB.nickname, gNickname);
|
||||
|
||||
// Should be matching instances
|
||||
ok(certA.equals(certB));
|
||||
|
||||
// Check a few expected attributes
|
||||
ok(certA.isSelfSigned);
|
||||
equal(certA.certType, Ci.nsIX509Cert.USER_CERT);
|
||||
|
||||
// New nickname should give a different cert
|
||||
let diffNameCert = yield getOrCreateCert("cool-stuff");
|
||||
ok(!diffNameCert.equals(certA));
|
||||
|
||||
// Remove the cert, and get a new one again
|
||||
yield removeCert(gNickname);
|
||||
let newCert = yield getOrCreateCert(gNickname);
|
||||
ok(!newCert.equals(certA));
|
||||
|
||||
// Drop all cert references and GC
|
||||
let serial = newCert.serialNumber;
|
||||
certA = certB = diffNameCert = newCert = null;
|
||||
Cu.forceGC();
|
||||
Cu.forceCC();
|
||||
|
||||
// Should still get the same cert back
|
||||
let certAfterGC = yield getOrCreateCert(gNickname);
|
||||
equal(certAfterGC.serialNumber, serial);
|
||||
});
|
5
toolkit/devtools/security/tests/unit/xpcshell.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
|
||||
[test_cert.js]
|
@ -16,6 +16,9 @@ SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let gState;
|
||||
|
||||
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let {MAX_AUTOCOMPLETE_ATTEMPTS,MAX_AUTOCOMPLETIONS} = devtools.require("devtools/toolkit/webconsole/utils");
|
||||
|
||||
function startTest()
|
||||
{
|
||||
removeEventListener("load", startTest);
|
||||
@ -35,10 +38,21 @@ function onAttach(aState, aResponse)
|
||||
top.foobarObject.omgstr = "foobarz" +
|
||||
(new Array(DebuggerServer.LONG_STRING_LENGTH * 2)).join("abb");
|
||||
|
||||
top.largeObject1 = Object.create(null);
|
||||
for (let i = 0; i < MAX_AUTOCOMPLETE_ATTEMPTS + 1; i++) {
|
||||
top.largeObject1['a' + i] = i;
|
||||
}
|
||||
|
||||
top.largeObject2 = Object.create(null);
|
||||
for (let i = 0; i < MAX_AUTOCOMPLETIONS * 2; i++) {
|
||||
top.largeObject2['a' + i] = i;
|
||||
}
|
||||
|
||||
gState = aState;
|
||||
|
||||
let tests = [doAutocomplete1, doAutocomplete2, doAutocomplete3,
|
||||
doAutocomplete4, doSimpleEval, doWindowEval, doEvalWithException,
|
||||
doAutocomplete4, doAutocompleteLarge1, doAutocompleteLarge2,
|
||||
doSimpleEval, doWindowEval, doEvalWithException,
|
||||
doEvalWithHelper, doEvalString, doEvalLongString];
|
||||
runTests(tests, testEnd);
|
||||
}
|
||||
@ -112,6 +126,39 @@ function onAutocomplete4(aResponse)
|
||||
nextTest();
|
||||
}
|
||||
|
||||
function doAutocompleteLarge1()
|
||||
{
|
||||
// Check that completion requests with too large objects will
|
||||
// have no suggestions.
|
||||
info("test autocomplete for 'window.largeObject1.'");
|
||||
gState.client.autocomplete("window.largeObject1.", 20, onAutocompleteLarge1);
|
||||
}
|
||||
|
||||
function onAutocompleteLarge1(aResponse)
|
||||
{
|
||||
ok(!aResponse.matchProp, "matchProp");
|
||||
info (aResponse.matches.join("|"));
|
||||
is(aResponse.matches.length, 0, "Bailed out with too many properties");
|
||||
|
||||
nextTest();
|
||||
}
|
||||
|
||||
function doAutocompleteLarge2()
|
||||
{
|
||||
// Check that completion requests with pretty large objects will
|
||||
// have MAX_AUTOCOMPLETIONS suggestions
|
||||
info("test autocomplete for 'window.largeObject2.'");
|
||||
gState.client.autocomplete("window.largeObject2.", 20, onAutocompleteLarge2);
|
||||
}
|
||||
|
||||
function onAutocompleteLarge2(aResponse)
|
||||
{
|
||||
ok(!aResponse.matchProp, "matchProp");
|
||||
is(aResponse.matches.length, MAX_AUTOCOMPLETIONS, "matches.length is MAX_AUTOCOMPLETIONS");
|
||||
|
||||
nextTest();
|
||||
}
|
||||
|
||||
function doSimpleEval()
|
||||
{
|
||||
info("test eval '2+2'");
|
||||
|
@ -33,6 +33,15 @@ const REGEX_MATCH_FUNCTION_ARGS = /^\(?function\s*[^\s(]*\s*\((.+?)\)/;
|
||||
|
||||
// Number of terminal entries for the self-xss prevention to go away
|
||||
const CONSOLE_ENTRY_THRESHOLD = 5
|
||||
|
||||
// Provide an easy way to bail out of even attempting an autocompletion
|
||||
// if an object has way too many properties. Protects against large objects
|
||||
// with numeric values that wouldn't be tallied towards MAX_AUTOCOMPLETIONS.
|
||||
const MAX_AUTOCOMPLETE_ATTEMPTS = exports.MAX_AUTOCOMPLETE_ATTEMPTS = 100000;
|
||||
|
||||
// Prevent iterating over too many properties during autocomplete suggestions.
|
||||
const MAX_AUTOCOMPLETIONS = exports.MAX_AUTOCOMPLETIONS = 1500;
|
||||
|
||||
let WebConsoleUtils = {
|
||||
/**
|
||||
* Convenience function to unwrap a wrapped object.
|
||||
@ -710,8 +719,6 @@ const OPEN_CLOSE_BODY = {
|
||||
"(": ")",
|
||||
};
|
||||
|
||||
const MAX_COMPLETIONS = 1500;
|
||||
|
||||
/**
|
||||
* Analyses a given string to find the last statement that is interesting for
|
||||
* later completion.
|
||||
@ -1044,11 +1051,22 @@ function getMatchedProps(aObj, aMatch)
|
||||
function getMatchedProps_impl(aObj, aMatch, {chainIterator, getProperties})
|
||||
{
|
||||
let matches = new Set();
|
||||
let numProps = 0;
|
||||
|
||||
// We need to go up the prototype chain.
|
||||
let iter = chainIterator(aObj);
|
||||
for (let obj of iter) {
|
||||
let props = getProperties(obj);
|
||||
numProps += props.length;
|
||||
|
||||
// If there are too many properties to event attempt autocompletion,
|
||||
// or if we have already added the max number, then stop looping
|
||||
// and return the partial set that has already been discovered.
|
||||
if (numProps >= MAX_AUTOCOMPLETE_ATTEMPTS ||
|
||||
matches.size >= MAX_AUTOCOMPLETIONS) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (let i = 0; i < props.length; i++) {
|
||||
let prop = props[i];
|
||||
if (prop.indexOf(aMatch) != 0) {
|
||||
@ -1062,14 +1080,10 @@ function getMatchedProps_impl(aObj, aMatch, {chainIterator, getProperties})
|
||||
matches.add(prop);
|
||||
}
|
||||
|
||||
if (matches.size > MAX_COMPLETIONS) {
|
||||
if (matches.size >= MAX_AUTOCOMPLETIONS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.size > MAX_COMPLETIONS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|