mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge fx-team to m-c. a=merge
IGNORE IDL CHANGES
This commit is contained in:
commit
b091610554
@ -109,7 +109,7 @@ add_task(function* setup() {
|
||||
extraFile.remove(false);
|
||||
};
|
||||
|
||||
Services.obs.addObserver(crashObserver, "plugin-crashed");
|
||||
Services.obs.addObserver(crashObserver, "plugin-crashed", false);
|
||||
// plugins.testmode will make BrowserPlugins:Test:ClearCrashData work.
|
||||
Services.prefs.setBoolPref("plugins.testmode", true);
|
||||
registerCleanupFunction(() => {
|
||||
|
@ -1044,6 +1044,14 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
<xul:label anonid="searchbar-oneoffheader-searchtext" flex="1" crop="end" class="search-panel-input-value"/>
|
||||
<xul:label anonid="searchbar-oneoffheader-after" flex="10000" value="&searchWith.label;"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox anonid="search-panel-searchonengine"
|
||||
class="search-panel-current-input">
|
||||
<xul:label anonid="searchbar-oneoffheader-beforeengine" value="&search.label;"/>
|
||||
<xul:label anonid="searchbar-oneoffheader-engine" flex="1" crop="end"
|
||||
class="search-panel-input-value"/>
|
||||
<xul:label anonid="searchbar-oneoffheader-afterengine" flex="10000"
|
||||
value="&searchAfter.label;"/>
|
||||
</xul:hbox>
|
||||
</xul:deck>
|
||||
<xul:description anonid="search-panel-one-offs"
|
||||
role="group"
|
||||
@ -1140,19 +1148,24 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
let inputHandler = function() {
|
||||
headerSearchText.setAttribute("value", textbox.value);
|
||||
let groupText;
|
||||
let isOneOffSelected =
|
||||
this.selectedButton &&
|
||||
this.selectedButton.classList.contains("searchbar-engine-one-off-item");
|
||||
if (textbox.value) {
|
||||
self.removeAttribute("showonlysettings");
|
||||
groupText = headerSearchText.previousSibling.value +
|
||||
'"' + headerSearchText.value + '"' +
|
||||
headerSearchText.nextSibling.value;
|
||||
headerPanel.selectedIndex = 1;
|
||||
if (!isOneOffSelected)
|
||||
headerPanel.selectedIndex = 1;
|
||||
}
|
||||
else {
|
||||
let noSearchHeader =
|
||||
document.getAnonymousElementByAttribute(self, "anonid",
|
||||
"searchbar-oneoffheader-search");
|
||||
groupText = noSearchHeader.value;
|
||||
headerPanel.selectedIndex = 0;
|
||||
if (!isOneOffSelected)
|
||||
headerPanel.selectedIndex = 0;
|
||||
}
|
||||
list.setAttribute("aria-label", groupText);
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ struct RedirEntry {
|
||||
required before adding new map entries without
|
||||
URI_SAFE_FOR_UNTRUSTED_CONTENT. Also note, however, that adding
|
||||
URI_SAFE_FOR_UNTRUSTED_CONTENT will allow random web sites to link to that
|
||||
URI. Perhaps we should separate the two concepts out...
|
||||
URI. If you want to prevent this, add MAKE_UNLINKABLE as well.
|
||||
*/
|
||||
static RedirEntry kRedirMap[] = {
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
@ -122,6 +122,7 @@ static RedirEntry kRedirMap[] = {
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
|
||||
nsIAboutModule::MAKE_UNLINKABLE |
|
||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
|
||||
};
|
||||
static const int kRedirTotal = ArrayLength(kRedirMap);
|
||||
|
@ -1032,18 +1032,44 @@ body[dir=rtl] .share-service-dropdown .share-panel-header {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.standalone .room-conversation h2.room-name,
|
||||
.standalone .room-conversation h2.room-info-failure {
|
||||
.standalone-room-info {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
display: block;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
/* 20px is 10px for left and right margins. */
|
||||
width: calc(25% - 20px);
|
||||
color: #fff;
|
||||
z-index: 2000000;
|
||||
font-size: 1.2em;
|
||||
padding: .4em;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.standalone-room-info > h2 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.standalone-context-url {
|
||||
color: #fff;
|
||||
/* Try and keep clear of local video */
|
||||
height: 40%;
|
||||
}
|
||||
|
||||
.standalone-context-url.screen-share-active {
|
||||
/* Try and keep clear of remote video when screensharing */
|
||||
height: 15%;
|
||||
}
|
||||
|
||||
.standalone-context-url > img {
|
||||
margin: 1em auto;
|
||||
max-width: 50%;
|
||||
/* allows 20% for the description wrapper plus the margins */
|
||||
max-height: calc(80% - 2em);
|
||||
}
|
||||
|
||||
.standalone-context-url-description-wrapper {
|
||||
/* So that we can use max-height for the image */
|
||||
height: 20%;
|
||||
}
|
||||
|
||||
.standalone .room-conversation .media {
|
||||
@ -1071,6 +1097,20 @@ body[dir=rtl] .share-service-dropdown .share-panel-header {
|
||||
|
||||
|
||||
@media screen and (max-width:640px) {
|
||||
.standalone-room-info {
|
||||
/* This isn't perfect, we just center the heading for now. Bug 1141493
|
||||
should fix this. */
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.standalone-context-url {
|
||||
/* XXX We haven't got UX for standalone yet, so temporarily not displaying
|
||||
on narrow window widths. See bug 1153827. */
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Rooms specific responsive styling */
|
||||
.standalone .room-conversation {
|
||||
background: #000;
|
||||
@ -1091,6 +1131,10 @@ body[dir=rtl] .share-service-dropdown .share-panel-header {
|
||||
.standalone .room-conversation .video_wrapper.remote_wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
.standalone .room-conversation .video_wrapper.remote_wrapper.not-joined {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.standalone .conversation-toolbar {
|
||||
height: 38px;
|
||||
padding: 8px;
|
||||
|
@ -419,6 +419,8 @@ loop.shared.actions = (function() {
|
||||
// roomName: String - Optional.
|
||||
roomOwner: String,
|
||||
roomUrl: String
|
||||
// urls: Array - Optional.
|
||||
// See https://wiki.mozilla.org/Loop/Architecture/Context#Format_of_context.value
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -80,10 +80,14 @@ loop.store.ActiveRoomStore = (function() {
|
||||
remoteVideoDimensions: {},
|
||||
screenSharingState: SCREEN_SHARE_STATES.INACTIVE,
|
||||
receivingScreenShare: false,
|
||||
// Any urls (aka context) associated with the room.
|
||||
roomContextUrls: null,
|
||||
// The roomCryptoKey to decode the context data if necessary.
|
||||
roomCryptoKey: null,
|
||||
// Room information failed to be obtained for a reason. See ROOM_INFO_FAILURES.
|
||||
roomInfoFailure: null,
|
||||
// The name of the room.
|
||||
roomName: null,
|
||||
// Social API state.
|
||||
socialShareButtonAvailable: false,
|
||||
socialShareProviders: null
|
||||
@ -271,6 +275,7 @@ loop.store.ActiveRoomStore = (function() {
|
||||
.then(function(decryptedResult) {
|
||||
var realResult = JSON.parse(decryptedResult);
|
||||
|
||||
roomInfoData.urls = realResult.urls;
|
||||
roomInfoData.roomName = realResult.roomName;
|
||||
|
||||
dispatcher.dispatch(roomInfoData);
|
||||
@ -320,6 +325,7 @@ loop.store.ActiveRoomStore = (function() {
|
||||
*/
|
||||
updateRoomInfo: function(actionData) {
|
||||
this.setStoreState({
|
||||
roomContextUrls: actionData.urls,
|
||||
roomInfoFailure: actionData.roomInfoFailure,
|
||||
roomName: actionData.roomName,
|
||||
roomOwner: actionData.roomOwner,
|
||||
|
@ -199,8 +199,43 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomContextItem = React.createClass({displayName: "StandaloneRoomContextItem",
|
||||
propTypes: {
|
||||
receivingScreenShare: React.PropTypes.bool,
|
||||
roomContextUrl: React.PropTypes.object
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.props.roomContextUrl ||
|
||||
!this.props.roomContextUrl.location) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var location = this.props.roomContextUrl.location;
|
||||
|
||||
var cx = React.addons.classSet;
|
||||
|
||||
var classes = cx({
|
||||
"standalone-context-url": true,
|
||||
"screen-share-active": this.props.receivingScreenShare
|
||||
});
|
||||
|
||||
return (
|
||||
React.createElement("div", {className: classes},
|
||||
React.createElement("img", {src: this.props.roomContextUrl.thumbnail}),
|
||||
React.createElement("div", {className: "standalone-context-url-description-wrapper"},
|
||||
this.props.roomContextUrl.description,
|
||||
React.createElement("br", null), React.createElement("a", {href: location}, location)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomContextView = React.createClass({displayName: "StandaloneRoomContextView",
|
||||
propTypes: {
|
||||
receivingScreenShare: React.PropTypes.bool.isRequired,
|
||||
roomContextUrls: React.PropTypes.array,
|
||||
roomName: React.PropTypes.string,
|
||||
roomInfoFailure: React.PropTypes.string
|
||||
},
|
||||
@ -216,8 +251,17 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
));
|
||||
}
|
||||
|
||||
// We only support one item in the context Urls array for now.
|
||||
var roomContextUrl = (this.props.roomContextUrls &&
|
||||
this.props.roomContextUrls.length > 0) ?
|
||||
this.props.roomContextUrls[0] : null;
|
||||
return (
|
||||
React.createElement("h2", {className: "room-name"}, this.props.roomName)
|
||||
React.createElement("div", {className: "standalone-room-info"},
|
||||
React.createElement("h2", {className: "room-name"}, this.props.roomName),
|
||||
React.createElement(StandaloneRoomContextItem, {
|
||||
receivingScreenShare: this.props.receivingScreenShare,
|
||||
roomContextUrl: roomContextUrl})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -482,8 +526,11 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
roomUsed: this.state.used}),
|
||||
React.createElement("div", {className: "video-layout-wrapper"},
|
||||
React.createElement("div", {className: "conversation room-conversation"},
|
||||
React.createElement(StandaloneRoomContextView, {roomName: this.state.roomName,
|
||||
roomInfoFailure: this.state.roomInfoFailure}),
|
||||
React.createElement(StandaloneRoomContextView, {
|
||||
receivingScreenShare: this.state.receivingScreenShare,
|
||||
roomContextUrls: this.state.roomContextUrls,
|
||||
roomName: this.state.roomName,
|
||||
roomInfoFailure: this.state.roomInfoFailure}),
|
||||
React.createElement("div", {className: "media nested"},
|
||||
React.createElement("span", {className: "self-view-hidden-message"},
|
||||
mozL10n.get("self_view_hidden_message")
|
||||
|
@ -199,8 +199,43 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomContextItem = React.createClass({
|
||||
propTypes: {
|
||||
receivingScreenShare: React.PropTypes.bool,
|
||||
roomContextUrl: React.PropTypes.object
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.props.roomContextUrl ||
|
||||
!this.props.roomContextUrl.location) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var location = this.props.roomContextUrl.location;
|
||||
|
||||
var cx = React.addons.classSet;
|
||||
|
||||
var classes = cx({
|
||||
"standalone-context-url": true,
|
||||
"screen-share-active": this.props.receivingScreenShare
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<img src={this.props.roomContextUrl.thumbnail} />
|
||||
<div className="standalone-context-url-description-wrapper">
|
||||
{this.props.roomContextUrl.description}
|
||||
<br /><a href={location}>{location}</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomContextView = React.createClass({
|
||||
propTypes: {
|
||||
receivingScreenShare: React.PropTypes.bool.isRequired,
|
||||
roomContextUrls: React.PropTypes.array,
|
||||
roomName: React.PropTypes.string,
|
||||
roomInfoFailure: React.PropTypes.string
|
||||
},
|
||||
@ -216,8 +251,17 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
</h2>);
|
||||
}
|
||||
|
||||
// We only support one item in the context Urls array for now.
|
||||
var roomContextUrl = (this.props.roomContextUrls &&
|
||||
this.props.roomContextUrls.length > 0) ?
|
||||
this.props.roomContextUrls[0] : null;
|
||||
return (
|
||||
<h2 className="room-name">{this.props.roomName}</h2>
|
||||
<div className="standalone-room-info">
|
||||
<h2 className="room-name">{this.props.roomName}</h2>
|
||||
<StandaloneRoomContextItem
|
||||
receivingScreenShare={this.props.receivingScreenShare}
|
||||
roomContextUrl={roomContextUrl} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -482,8 +526,11 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
roomUsed={this.state.used} />
|
||||
<div className="video-layout-wrapper">
|
||||
<div className="conversation room-conversation">
|
||||
<StandaloneRoomContextView roomName={this.state.roomName}
|
||||
roomInfoFailure={this.state.roomInfoFailure} />
|
||||
<StandaloneRoomContextView
|
||||
receivingScreenShare={this.state.receivingScreenShare}
|
||||
roomContextUrls={this.state.roomContextUrls}
|
||||
roomName={this.state.roomName}
|
||||
roomInfoFailure={this.state.roomInfoFailure} />
|
||||
<div className="media nested">
|
||||
<span className="self-view-hidden-message">
|
||||
{mozL10n.get("self_view_hidden_message")}
|
||||
|
@ -457,15 +457,24 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
}, expectedDetails)));
|
||||
});
|
||||
|
||||
it("should dispatch UpdateRoomInfo message with the room name if decryption was successful", function() {
|
||||
it("should dispatch UpdateRoomInfo message with the context if decryption was successful", function() {
|
||||
fetchServerAction.cryptoKey = "fakeKey";
|
||||
|
||||
var roomContext = {
|
||||
roomName: "The wonderful Loopy room",
|
||||
urls: [{
|
||||
description: "An invalid page",
|
||||
location: "http://invalid.com",
|
||||
thumbnail: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
||||
}]
|
||||
};
|
||||
|
||||
// This is a work around to turn promise into a sync action to make handling test failures
|
||||
// easier.
|
||||
sandbox.stub(loop.crypto, "decryptBytes", function() {
|
||||
return {
|
||||
then: function(resolve, reject) {
|
||||
resolve(JSON.stringify({roomName: "The wonderful Loopy room"}));
|
||||
resolve(JSON.stringify(roomContext));
|
||||
}
|
||||
};
|
||||
});
|
||||
@ -474,9 +483,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo(_.extend({
|
||||
roomName: "The wonderful Loopy room"
|
||||
}, expectedDetails)));
|
||||
new sharedActions.UpdateRoomInfo(_.extend(roomContext, expectedDetails)));
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -562,7 +569,12 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
fakeRoomInfo = {
|
||||
roomName: "Its a room",
|
||||
roomOwner: "Me",
|
||||
roomUrl: "http://invalid"
|
||||
roomUrl: "http://invalid",
|
||||
urls: [{
|
||||
description: "fake site",
|
||||
location: "http://invalid.com",
|
||||
thumbnail: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
@ -573,6 +585,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
expect(state.roomName).eql(fakeRoomInfo.roomName);
|
||||
expect(state.roomOwner).eql(fakeRoomInfo.roomOwner);
|
||||
expect(state.roomUrl).eql(fakeRoomInfo.roomUrl);
|
||||
expect(state.roomContextUrls).eql(fakeRoomInfo.urls);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -44,7 +44,8 @@ describe("loop.standaloneRoomViews", function() {
|
||||
sandbox.stub(navigator.mozL10n, "get").returnsArg(0);
|
||||
});
|
||||
|
||||
function mountTestComponent(props) {
|
||||
function mountTestComponent(extraProps) {
|
||||
var props = _.extend({ receivingScreenShare: false }, extraProps);
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(
|
||||
loop.standaloneRoomViews.StandaloneRoomContextView, props));
|
||||
@ -52,7 +53,8 @@ describe("loop.standaloneRoomViews", function() {
|
||||
|
||||
it("should display the room name if no failures are known", function() {
|
||||
var view = mountTestComponent({
|
||||
roomName: "Mike's room"
|
||||
roomName: "Mike's room",
|
||||
receivingScreenShare: false
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().textContent).eql("Mike's room");
|
||||
@ -75,6 +77,27 @@ describe("loop.standaloneRoomViews", function() {
|
||||
|
||||
expect(view.getDOMNode().textContent).match(/not_available/);
|
||||
});
|
||||
|
||||
it("should display context information if a url is supplied", function() {
|
||||
var view = mountTestComponent({
|
||||
roomName: "Mike's room",
|
||||
roomContextUrls: [{
|
||||
description: "Mark's super page",
|
||||
location: "http://invalid.com",
|
||||
thumbnail: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
||||
}]
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".standalone-context-url")).not.eql(null);
|
||||
});
|
||||
|
||||
it("should not display context information if no urls are supplied", function() {
|
||||
var view = mountTestComponent({
|
||||
roomName: "Mike's room"
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".standalone-context-url")).eql(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("StandaloneRoomView", function() {
|
||||
|
@ -1021,14 +1021,29 @@
|
||||
if (this._selectedButton)
|
||||
this._selectedButton.removeAttribute("selected");
|
||||
|
||||
let textbox = document.getBindingParent(this).textbox;
|
||||
let header =
|
||||
document.getAnonymousElementByAttribute(this.popup, "anonid",
|
||||
"search-panel-one-offs-header");
|
||||
// Avoid selecting dummy buttons.
|
||||
if (val && !val.classList.contains("dummy")) {
|
||||
val.setAttribute("selected", "true");
|
||||
this._selectedButton = val;
|
||||
if (val.classList.contains("searchbar-engine-one-off-item")) {
|
||||
let headerEngineText =
|
||||
document.getAnonymousElementByAttribute(this.popup, "anonid",
|
||||
"searchbar-oneoffheader-engine");
|
||||
header.selectedIndex = 2;
|
||||
headerEngineText.value = val.engine.name;
|
||||
}
|
||||
else {
|
||||
header.selectedIndex = textbox.value ? 1 : 0;
|
||||
}
|
||||
this.setAttribute("aria-activedescendant", val.id);
|
||||
return;
|
||||
}
|
||||
|
||||
header.selectedIndex = textbox.value ? 1 : 0;
|
||||
this.removeAttribute("aria-activedescendant");
|
||||
this._selectedButton = null;
|
||||
]]></setter>
|
||||
|
@ -36,6 +36,8 @@ skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Err
|
||||
[browser_healthreport.js]
|
||||
[browser_hiddenOneOffs_cleanup.js]
|
||||
[browser_hiddenOneOffs_diacritics.js]
|
||||
[browser_oneOffHeader.js]
|
||||
skip-if = e10s # bug ?????? - Test alters the searchbar textbox value which causes issues with other tests in e10s.
|
||||
[browser_private_search_perwindowpb.js]
|
||||
skip-if = e10s # Bug ?????? - Test uses load event and checks event.target.
|
||||
[browser_yahoo.js]
|
||||
|
141
browser/components/search/test/browser_oneOffHeader.js
Normal file
141
browser/components/search/test/browser_oneOffHeader.js
Normal file
@ -0,0 +1,141 @@
|
||||
/* 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/. */
|
||||
// Tests that keyboard navigation in the search panel works as designed.
|
||||
|
||||
const isMac = ("nsILocalFileMac" in Ci);
|
||||
|
||||
const searchbar = document.getElementById("searchbar");
|
||||
const textbox = searchbar._textbox;
|
||||
const searchPopup = document.getElementById("PopupSearchAutoComplete");
|
||||
const searchIcon = document.getAnonymousElementByAttribute(searchbar, "anonid",
|
||||
"searchbar-search-button");
|
||||
const searchSettings =
|
||||
document.getAnonymousElementByAttribute(searchPopup, "anonid",
|
||||
"search-settings");
|
||||
let header =
|
||||
document.getAnonymousElementByAttribute(searchPopup, "anonid",
|
||||
"search-panel-one-offs-header");
|
||||
function getHeaderText() {
|
||||
let headerChild = header.selectedPanel;
|
||||
while (headerChild.hasChildNodes()) {
|
||||
headerChild = headerChild.firstChild;
|
||||
}
|
||||
let headerStrings = [];
|
||||
for (let label = headerChild; label; label = label.nextSibling) {
|
||||
headerStrings.push(label.value);
|
||||
}
|
||||
return headerStrings.join("");
|
||||
}
|
||||
|
||||
// Get an array of the one-off buttons.
|
||||
function getOneOffs() {
|
||||
let oneOffs = [];
|
||||
let oneOff =
|
||||
document.getAnonymousElementByAttribute(searchPopup, "anonid",
|
||||
"search-panel-one-offs");
|
||||
for (oneOff = oneOff.firstChild; oneOff; oneOff = oneOff.nextSibling) {
|
||||
if (oneOff.classList.contains("dummy"))
|
||||
break;
|
||||
oneOffs.push(oneOff);
|
||||
}
|
||||
|
||||
return oneOffs;
|
||||
}
|
||||
|
||||
const msg = isMac ? 5 : 1;
|
||||
const utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
const scale = utils.screenPixelsPerCSSPixel;
|
||||
function* synthesizeNativeMouseMove(aElement) {
|
||||
let rect = aElement.getBoundingClientRect();
|
||||
let win = aElement.ownerDocument.defaultView;
|
||||
let x = win.mozInnerScreenX + (rect.left + rect.right) / 2;
|
||||
let y = win.mozInnerScreenY + (rect.top + rect.bottom) / 2;
|
||||
|
||||
// Wait for the mouseup event to occur before continuing.
|
||||
return new Promise((resolve, reject) => {
|
||||
function eventOccurred(e)
|
||||
{
|
||||
aElement.removeEventListener("mouseover", eventOccurred, true);
|
||||
resolve();
|
||||
}
|
||||
|
||||
aElement.addEventListener("mouseover", eventOccurred, true);
|
||||
|
||||
utils.sendNativeMouseEvent(x * scale, y * scale, msg, 0, null);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
add_task(function* init() {
|
||||
yield promiseNewEngine("testEngine.xml");
|
||||
});
|
||||
|
||||
add_task(function* test_notext() {
|
||||
let promise = promiseEvent(searchPopup, "popupshown");
|
||||
info("Opening search panel");
|
||||
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
|
||||
yield promise;
|
||||
|
||||
is(header.getAttribute("selectedIndex"), 0,
|
||||
"Header has the correct index selected with no search terms.");
|
||||
|
||||
is(getHeaderText(), "Search with:",
|
||||
"Search header string is correct when no search terms have been entered");
|
||||
|
||||
yield synthesizeNativeMouseMove(searchSettings);
|
||||
is(header.getAttribute("selectedIndex"), 0,
|
||||
"Header has the correct index when no search terms have been entered and the Change Search Settings button is selected.");
|
||||
is(getHeaderText(), "Search with:",
|
||||
"Header has the correct text when no search terms have been entered and the Change Search Settings button is selected.");
|
||||
|
||||
let buttons = getOneOffs();
|
||||
yield synthesizeNativeMouseMove(buttons[0]);
|
||||
is(header.getAttribute("selectedIndex"), 2,
|
||||
"Header has the correct index selected when a search engine has been selected");
|
||||
is(getHeaderText(), "Search " + buttons[0].engine.name,
|
||||
"Is the header text correct when a search engine is selected and no terms have been entered.");
|
||||
|
||||
promise = promiseEvent(searchPopup, "popuphidden");
|
||||
info("Closing search panel");
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield promise;
|
||||
});
|
||||
|
||||
add_task(function* test_text() {
|
||||
textbox.value = "foo";
|
||||
registerCleanupFunction(() => {
|
||||
textbox.value = "";
|
||||
});
|
||||
|
||||
let promise = promiseEvent(searchPopup, "popupshown");
|
||||
info("Opening search panel");
|
||||
SimpleTest.executeSoon(() => {
|
||||
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
|
||||
});
|
||||
yield promise;
|
||||
|
||||
is(header.getAttribute("selectedIndex"), 1,
|
||||
"Header has the correct index selected with a search term.");
|
||||
is(getHeaderText(), "Search for foo with:",
|
||||
"Search header string is correct when a search term has been entered");
|
||||
|
||||
let buttons = getOneOffs();
|
||||
yield synthesizeNativeMouseMove(buttons[0]);
|
||||
is(header.getAttribute("selectedIndex"), 2,
|
||||
"Header has the correct index selected when a search engine has been selected");
|
||||
is(getHeaderText(), "Search " + buttons[0].engine.name,
|
||||
"Is the header text correct when search terms are entered after a search engine has been selected.");
|
||||
|
||||
yield synthesizeNativeMouseMove(searchSettings);
|
||||
is(header.getAttribute("selectedIndex"), 1,
|
||||
"Header has the correct index selected when search terms have been entered and the Change Search Settings button is selected.");
|
||||
is(getHeaderText(), "Search for foo with:",
|
||||
"Header has the correct text when search terms have been entered and the Change Search Settings button is selected.");
|
||||
|
||||
promise = promiseEvent(searchPopup, "popuphidden");
|
||||
info("Closing search panel");
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield promise;
|
||||
});
|
@ -281,6 +281,10 @@ if (typeof Mozilla == 'undefined') {
|
||||
});
|
||||
};
|
||||
|
||||
Mozilla.UITour.forceShowReaderIcon = function() {
|
||||
_sendEvent('forceShowReaderIcon');
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
// Make this library Require-able.
|
||||
|
@ -27,6 +27,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
|
||||
"resource:///modules/BrowserUITelemetry.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
|
||||
"resource://gre/modules/Metrics.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent",
|
||||
"resource:///modules/ReaderParent.jsm");
|
||||
|
||||
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
|
||||
const PREF_LOG_LEVEL = "browser.uitour.loglevel";
|
||||
@ -34,6 +36,7 @@ const PREF_SEENPAGEIDS = "browser.uitour.seenPageIDs";
|
||||
|
||||
const BACKGROUND_PAGE_ACTIONS_ALLOWED = new Set([
|
||||
"endUrlbarCapture",
|
||||
"forceShowReaderIcon",
|
||||
"getConfiguration",
|
||||
"getTreatmentTag",
|
||||
"hideHighlight",
|
||||
@ -677,6 +680,11 @@ this.UITour = {
|
||||
this.sendPageCallback(messageManager, data.callbackID);
|
||||
break;
|
||||
}
|
||||
|
||||
case "forceShowReaderIcon": {
|
||||
ReaderParent.forceShowReaderIcon(browser);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.tourBrowsersByWindow.has(window)) {
|
||||
|
@ -18,6 +18,8 @@ skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
|
||||
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
|
||||
[browser_UITour_annotation_size_attributes.js]
|
||||
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
|
||||
[browser_UITour_forceReaderMode.js]
|
||||
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
|
||||
[browser_UITour_heartbeat.js]
|
||||
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
|
||||
[browser_UITour_loop.js]
|
||||
|
@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
|
||||
let gTestTab;
|
||||
let gContentAPI;
|
||||
let gContentWindow;
|
||||
|
||||
Components.utils.import("resource:///modules/UITour.jsm");
|
||||
|
||||
function test() {
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
let tests = [
|
||||
taskify(function*() {
|
||||
ok(!gBrowser.selectedBrowser.isArticle, "Should not be an article when we start");
|
||||
ok(document.getElementById("reader-mode-button").hidden, "Button should be hidden.");
|
||||
gContentAPI.forceShowReaderIcon();
|
||||
yield waitForConditionPromise(() => gBrowser.selectedBrowser.isArticle);
|
||||
ok(gBrowser.selectedBrowser.isArticle, "Should suddenly be an article.");
|
||||
ok(!document.getElementById("reader-mode-button").hidden, "Button should now be visible.");
|
||||
})
|
||||
];
|
||||
|
@ -425,16 +425,12 @@ SelectorSearch.prototype = {
|
||||
/**
|
||||
* Populates the suggestions list and show the suggestion popup.
|
||||
*/
|
||||
_showPopup: function(aList, aFirstPart) {
|
||||
_showPopup: function(aList, aFirstPart, aState) {
|
||||
let total = 0;
|
||||
let query = this.searchBox.value;
|
||||
let toLowerCase = false;
|
||||
let items = [];
|
||||
// In case of tagNames, change the case to small.
|
||||
if (query.match(/.*[\.#][^\.#]{0,}$/) == null) {
|
||||
toLowerCase = true;
|
||||
}
|
||||
for (let [value, count] of aList) {
|
||||
|
||||
for (let [value, count, state] of aList) {
|
||||
// for cases like 'div ' or 'div >' or 'div+'
|
||||
if (query.match(/[\s>+]$/)) {
|
||||
value = query + value;
|
||||
@ -449,14 +445,27 @@ SelectorSearch.prototype = {
|
||||
let lastPart = query.match(/[a-zA-Z][#\.][^#\.\s>+]*$/)[0];
|
||||
value = query.slice(0, -1 * lastPart.length + 1) + value;
|
||||
}
|
||||
|
||||
let item = {
|
||||
preLabel: query,
|
||||
label: value,
|
||||
count: count
|
||||
};
|
||||
if (toLowerCase) {
|
||||
|
||||
// In case of tagNames, change te case to small
|
||||
if (value.match(/.*[\.#][^\.#]{0,}$/) == null) {
|
||||
item.label = value.toLowerCase();
|
||||
}
|
||||
|
||||
// In case the query's state is tag and the item's state is id or class
|
||||
// adjust the preLabel
|
||||
if (aState === this.States.TAG && state === this.States.CLASS) {
|
||||
item.preLabel = "." + item.preLabel;
|
||||
}
|
||||
if (aState === this.States.TAG && state === this.States.ID) {
|
||||
item.preLabel = "#" + item.preLabel;
|
||||
}
|
||||
|
||||
items.unshift(item);
|
||||
if (++total > MAX_SUGGESTIONS - 1) {
|
||||
break;
|
||||
@ -477,19 +486,21 @@ SelectorSearch.prototype = {
|
||||
*/
|
||||
showSuggestions: function() {
|
||||
let query = this.searchBox.value;
|
||||
let state = this.state;
|
||||
let firstPart = "";
|
||||
if (this.state == this.States.TAG) {
|
||||
|
||||
if (state == this.States.TAG) {
|
||||
// gets the tag that is being completed. For ex. 'div.foo > s' returns 's',
|
||||
// 'di' returns 'di' and likewise.
|
||||
firstPart = (query.match(/[\s>+]?([a-zA-Z]*)$/) || ["", query])[1];
|
||||
query = query.slice(0, query.length - firstPart.length);
|
||||
}
|
||||
else if (this.state == this.States.CLASS) {
|
||||
else if (state == this.States.CLASS) {
|
||||
// gets the class that is being completed. For ex. '.foo.b' returns 'b'
|
||||
firstPart = query.match(/\.([^\.]*)$/)[1];
|
||||
query = query.slice(0, query.length - firstPart.length - 1);
|
||||
}
|
||||
else if (this.state == this.States.ID) {
|
||||
else if (state == this.States.ID) {
|
||||
// gets the id that is being completed. For ex. '.foo#b' returns 'b'
|
||||
firstPart = query.match(/#([^#]*)$/)[1];
|
||||
query = query.slice(0, query.length - firstPart.length - 1);
|
||||
@ -499,21 +510,24 @@ SelectorSearch.prototype = {
|
||||
if (/[\s+>~]$/.test(query)) {
|
||||
query += "*";
|
||||
}
|
||||
|
||||
this._currentSuggesting = query;
|
||||
return this.walker.getSuggestionsForQuery(query, firstPart, this.state).then(result => {
|
||||
return this.walker.getSuggestionsForQuery(query, firstPart, state).then(result => {
|
||||
if (this._currentSuggesting != result.query) {
|
||||
// This means that this response is for a previous request and the user
|
||||
// as since typed something extra leading to a new request.
|
||||
return;
|
||||
}
|
||||
this._lastToLastValidSearch = this._lastValidSearch;
|
||||
if (this.state == this.States.CLASS) {
|
||||
|
||||
if (state == this.States.CLASS) {
|
||||
firstPart = "." + firstPart;
|
||||
}
|
||||
else if (this.state == this.States.ID) {
|
||||
else if (state == this.States.ID) {
|
||||
firstPart = "#" + firstPart;
|
||||
}
|
||||
this._showPopup(result.suggestions, firstPart);
|
||||
|
||||
this._showPopup(result.suggestions, firstPart, state);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -15,7 +15,11 @@ const TEST_URL = TEST_URL_ROOT + "doc_inspector_search-suggestions.html";
|
||||
const TEST_DATA = [
|
||||
{
|
||||
key: "d",
|
||||
suggestions: [{label: "div", count: 4}]
|
||||
suggestions: [
|
||||
{label: "div", count: 4},
|
||||
{label: "#d1", count: 1},
|
||||
{label: "#d2", count: 1}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "i",
|
||||
@ -67,7 +71,11 @@ const TEST_DATA = [
|
||||
},
|
||||
{
|
||||
key: "VK_BACK_SPACE",
|
||||
suggestions: [{label: "div", count: 4}]
|
||||
suggestions: [
|
||||
{label: "div", count: 4},
|
||||
{label: "#d1", count: 1},
|
||||
{label: "#d2", count: 1}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "VK_BACK_SPACE",
|
||||
@ -135,16 +143,17 @@ add_task(function* () {
|
||||
info("Waiting for search query to complete");
|
||||
yield inspector.searchSuggestions._lastQuery;
|
||||
|
||||
info("Query completed. Performing checks for input '" + searchBox.value + "'");
|
||||
info("Query completed. Performing checks for input '" + searchBox.value +
|
||||
"' - key pressed: " + key);
|
||||
let actualSuggestions = popup.getItems().reverse();
|
||||
|
||||
is(popup.isOpen ? actualSuggestions.length: 0, suggestions.length,
|
||||
"There are expected number of suggestions.");
|
||||
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
is(suggestions[i].label, actualSuggestions[i].label,
|
||||
is(actualSuggestions[i].label, suggestions[i].label,
|
||||
"The suggestion at " + i + "th index is correct.");
|
||||
is(suggestions[i].count || 1, actualSuggestions[i].count,
|
||||
is(actualSuggestions[i].count, suggestions[i].count || 1,
|
||||
"The count for suggestion at " + i + "th index is correct.");
|
||||
}
|
||||
}
|
||||
@ -152,6 +161,6 @@ add_task(function* () {
|
||||
|
||||
function formatSuggestions(suggestions) {
|
||||
return "[" + suggestions
|
||||
.map(s => "'" + s.label + "' (" + s.count || 1 + ")")
|
||||
.map(s => "'" + s.label + "' (" + (s.count || 1) + ")")
|
||||
.join(", ") + "]";
|
||||
}
|
||||
|
@ -15,7 +15,11 @@ const TEST_URL = TEST_URL_ROOT + "doc_inspector_search.html";
|
||||
let TEST_DATA = [
|
||||
{
|
||||
key: "d",
|
||||
suggestions: [{label: "div", count: 2}]
|
||||
suggestions: [
|
||||
{label: "div", count: 2},
|
||||
{label: "#d1", count: 1},
|
||||
{label: "#d2", count: 1}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "i",
|
||||
@ -50,7 +54,11 @@ let TEST_DATA = [
|
||||
},
|
||||
{
|
||||
key: "VK_BACK_SPACE",
|
||||
suggestions: [{label: "div", count: 2}]
|
||||
suggestions: [
|
||||
{label: "div", count: 2},
|
||||
{label: "#d1", count: 1},
|
||||
{label: "#d2", count: 1}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "VK_BACK_SPACE",
|
||||
@ -179,9 +187,9 @@ add_task(function* () {
|
||||
"There are expected number of suggestions.");
|
||||
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
is(suggestions[i].label, actualSuggestions[i].label,
|
||||
is(actualSuggestions[i].label, suggestions[i].label,
|
||||
"The suggestion at " + i + "th index is correct.");
|
||||
is(suggestions[i].count || 1, actualSuggestions[i].count,
|
||||
is(actualSuggestions[i].count, suggestions[i].count || 1,
|
||||
"The count for suggestion at " + i + "th index is correct.");
|
||||
}
|
||||
}
|
||||
@ -189,6 +197,6 @@ add_task(function* () {
|
||||
|
||||
function formatSuggestions(suggestions) {
|
||||
return "[" + suggestions
|
||||
.map(s => "'" + s.label + "' (" + s.count || 1 + ")")
|
||||
.map(s => "'" + s.label + "' (" + (s.count || 1) + ")")
|
||||
.join(", ") + "]";
|
||||
}
|
||||
|
@ -18,7 +18,11 @@ const TEST_URL = "data:text/html;charset=utf-8," +
|
||||
let TEST_DATA = [
|
||||
{
|
||||
key: "d",
|
||||
suggestions: [{label: "div", count: 5}]
|
||||
suggestions: [
|
||||
{label: "div", count: 5},
|
||||
{label: "#d1", count: 2},
|
||||
{label: "#d2", count: 2}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "i",
|
||||
@ -34,7 +38,11 @@ let TEST_DATA = [
|
||||
},
|
||||
{
|
||||
key: "VK_BACK_SPACE",
|
||||
suggestions: [{label: "div", count: 5}]
|
||||
suggestions: [
|
||||
{label: "div", count: 5},
|
||||
{label: "#d1", count: 2},
|
||||
{label: "#d2", count: 2}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "VK_BACK_SPACE",
|
||||
@ -90,9 +98,9 @@ add_task(function* () {
|
||||
"There are expected number of suggestions.");
|
||||
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
is(suggestions[i].label, actualSuggestions[i].label,
|
||||
is(actualSuggestions[i].label, suggestions[i].label,
|
||||
"The suggestion at " + i + "th index is correct.");
|
||||
is(suggestions[i].count || 1, actualSuggestions[i].count,
|
||||
is(actualSuggestions[i].count, suggestions[i].count || 1,
|
||||
"The count for suggestion at " + i + "th index is correct.");
|
||||
}
|
||||
}
|
||||
@ -100,6 +108,6 @@ add_task(function* () {
|
||||
|
||||
function formatSuggestions(suggestions) {
|
||||
return "[" + suggestions
|
||||
.map(s => "'" + s.label + "' (" + s.count || 1 + ")")
|
||||
.map(s => "'" + s.label + "' (" + (s.count || 1) + ")")
|
||||
.join(", ") + "]";
|
||||
}
|
||||
|
@ -0,0 +1,125 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the selector-search input proposes ids and classes even when . and
|
||||
// # is missing, but that this only occurs when the query is one word (no
|
||||
// selector combination)
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
let inspector, searchBox, state, popup;
|
||||
|
||||
// The various states of the inspector: [key, suggestions array]
|
||||
// [
|
||||
// what key to press,
|
||||
// suggestions array with count [
|
||||
// [suggestion1, count1], [suggestion2] ...
|
||||
// ] count can be left to represent 1
|
||||
// ]
|
||||
let keyStates = [
|
||||
["s", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["p", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["a", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["n", []],
|
||||
[" ", [["span div", 1]]],
|
||||
["d", [["span div", 1]]], // mixed tag/class/id suggestions only work for the first word
|
||||
["VK_BACK_SPACE", [["span div", 1]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
// Test that mixed tags, classes and ids are grouped by types, sorted by
|
||||
// count and alphabetical order
|
||||
["b", [
|
||||
["button", 3],
|
||||
["body", 1],
|
||||
[".bc", 3],
|
||||
[".ba", 1],
|
||||
[".bb", 1],
|
||||
["#ba", 1],
|
||||
["#bb", 1],
|
||||
["#bc", 1]
|
||||
]],
|
||||
];
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
waitForFocus(setupTest, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html," +
|
||||
"<span class='span' id='span'>" +
|
||||
" <div class='div' id='div'></div>" +
|
||||
"</span>" +
|
||||
"<button class='ba bc' id='bc'></button>" +
|
||||
"<button class='bb bc' id='bb'></button>" +
|
||||
"<button class='bc' id='ba'></button>";
|
||||
|
||||
function $(id) {
|
||||
if (id == null) return null;
|
||||
return content.document.getElementById(id);
|
||||
}
|
||||
|
||||
function setupTest()
|
||||
{
|
||||
openInspector(startTest);
|
||||
}
|
||||
|
||||
function startTest(aInspector)
|
||||
{
|
||||
inspector = aInspector;
|
||||
|
||||
searchBox =
|
||||
inspector.panelWin.document.getElementById("inspector-searchbox");
|
||||
popup = inspector.searchSuggestions.searchPopup;
|
||||
|
||||
focusSearchBoxUsingShortcut(inspector.panelWin, function() {
|
||||
searchBox.addEventListener("command", checkState, true);
|
||||
checkStateAndMoveOn(0);
|
||||
});
|
||||
}
|
||||
|
||||
function checkStateAndMoveOn(index) {
|
||||
if (index == keyStates.length) {
|
||||
finishUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let [key, suggestions] = keyStates[index];
|
||||
state = index;
|
||||
|
||||
info("pressing key " + key + " to get suggestions " +
|
||||
JSON.stringify(suggestions));
|
||||
EventUtils.synthesizeKey(key, {}, inspector.panelWin);
|
||||
}
|
||||
|
||||
function checkState(event) {
|
||||
inspector.searchSuggestions._lastQuery.then(() => {
|
||||
let [key, suggestions] = keyStates[state];
|
||||
let actualSuggestions = popup.getItems();
|
||||
is(popup.isOpen ? actualSuggestions.length: 0, suggestions.length,
|
||||
"There are expected number of suggestions at " + state + "th step.");
|
||||
actualSuggestions.reverse();
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
is(suggestions[i][0], actualSuggestions[i].label,
|
||||
"The suggestion at " + i + "th index for " + state +
|
||||
"th step is correct.")
|
||||
is(suggestions[i][1] || 1, actualSuggestions[i].count,
|
||||
"The count for suggestion at " + i + "th index for " + state +
|
||||
"th step is correct.")
|
||||
}
|
||||
checkStateAndMoveOn(state + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
searchBox = null;
|
||||
popup = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
@ -60,6 +60,16 @@ body {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.legend {
|
||||
position: absolute;
|
||||
margin: 5px 6px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.legend[data-box="margin"] {
|
||||
color: var(--theme-highlight-blue);
|
||||
}
|
||||
|
||||
#main > p {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
@ -169,16 +179,9 @@ body {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 2px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
body.dim > #header > #element-position,
|
||||
body.dim > #main > p,
|
||||
body.dim > #main > .tooltip {
|
||||
body.dim > #main > p {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
@ -500,19 +500,15 @@ LayoutView.prototype = {
|
||||
};
|
||||
|
||||
let elts;
|
||||
let tooltip;
|
||||
|
||||
let onmouseover = function(e) {
|
||||
let region = e.target.getAttribute("data-box");
|
||||
|
||||
tooltip.textContent = e.target.getAttribute("tooltip");
|
||||
this.layoutview.showBoxModel({region});
|
||||
|
||||
return false;
|
||||
}.bind(window);
|
||||
|
||||
let onmouseout = function(e) {
|
||||
tooltip.textContent = "";
|
||||
this.layoutview.hideBoxModel();
|
||||
|
||||
return false;
|
||||
@ -521,9 +517,8 @@ let onmouseout = function(e) {
|
||||
window.setPanel = function(panel) {
|
||||
this.layoutview = new LayoutView(panel, window);
|
||||
|
||||
// Tooltip mechanism
|
||||
elts = document.querySelectorAll("*[tooltip]");
|
||||
tooltip = document.querySelector(".tooltip");
|
||||
// Box model highlighter mechanism
|
||||
elts = document.querySelectorAll("*[title]");
|
||||
for (let i = 0; i < elts.length; i++) {
|
||||
let elt = elts[i];
|
||||
elt.addEventListener("mouseover", onmouseover, true);
|
||||
|
@ -30,33 +30,34 @@
|
||||
|
||||
<div id="main">
|
||||
|
||||
<div id="margins" data-box="margin" tooltip="&margin.tooltip;">
|
||||
<div id="borders" data-box="border" tooltip="&border.tooltip;">
|
||||
<div id="padding" data-box="padding" tooltip="&padding.tooltip;">
|
||||
<div id="content" data-box="content" tooltip="&content.tooltip;">
|
||||
<span class="legend" data-box="margin">&margin.tooltip;</span>
|
||||
<div id="margins" data-box="margin" title="&margin.tooltip;">
|
||||
<span class="legend" data-box="border">&border.tooltip;</span>
|
||||
<div id="borders" data-box="border" title="&border.tooltip;">
|
||||
<span class="legend" data-box="padding">&padding.tooltip;</span>
|
||||
<div id="padding" data-box="padding" title="&padding.tooltip;">
|
||||
<div id="content" data-box="content" title="&content.tooltip;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="border top"><span data-box="border" class="editable" tooltip="border-top"></span></p>
|
||||
<p class="border right"><span data-box="border" class="editable" tooltip="border-right"></span></p>
|
||||
<p class="border bottom"><span data-box="border" class="editable" tooltip="border-bottom"></span></p>
|
||||
<p class="border left"><span data-box="border" class="editable" tooltip="border-left"></span></p>
|
||||
<p class="border top"><span data-box="border" class="editable" title="border-top"></span></p>
|
||||
<p class="border right"><span data-box="border" class="editable" title="border-right"></span></p>
|
||||
<p class="border bottom"><span data-box="border" class="editable" title="border-bottom"></span></p>
|
||||
<p class="border left"><span data-box="border" class="editable" title="border-left"></span></p>
|
||||
|
||||
<p class="margin top"><span data-box="margin" class="editable" tooltip="margin-top"></span></p>
|
||||
<p class="margin right"><span data-box="margin" class="editable" tooltip="margin-right"></span></p>
|
||||
<p class="margin bottom"><span data-box="margin" class="editable" tooltip="margin-bottom"></span></p>
|
||||
<p class="margin left"><span data-box="margin" class="editable" tooltip="margin-left"></span></p>
|
||||
<p class="margin top"><span data-box="margin" class="editable" title="margin-top"></span></p>
|
||||
<p class="margin right"><span data-box="margin" class="editable" title="margin-right"></span></p>
|
||||
<p class="margin bottom"><span data-box="margin" class="editable" title="margin-bottom"></span></p>
|
||||
<p class="margin left"><span data-box="margin" class="editable" title="margin-left"></span></p>
|
||||
|
||||
<p class="padding top"><span data-box="padding" class="editable" tooltip="padding-top"></span></p>
|
||||
<p class="padding right"><span data-box="padding" class="editable" tooltip="padding-right"></span></p>
|
||||
<p class="padding bottom"><span data-box="padding" class="editable" tooltip="padding-bottom"></span></p>
|
||||
<p class="padding left"><span data-box="padding" class="editable" tooltip="padding-left"></span></p>
|
||||
<p class="padding top"><span data-box="padding" class="editable" title="padding-top"></span></p>
|
||||
<p class="padding right"><span data-box="padding" class="editable" title="padding-right"></span></p>
|
||||
<p class="padding bottom"><span data-box="padding" class="editable" title="padding-bottom"></span></p>
|
||||
<p class="padding left"><span data-box="padding" class="editable" title="padding-left"></span></p>
|
||||
|
||||
<p class="size"><span data-box="content" tooltip="&content.tooltip;"></span></p>
|
||||
|
||||
<span class="tooltip"></span>
|
||||
<p class="size"><span data-box="content" title="&content.tooltip;"></span></p>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -835,6 +835,7 @@ OutputPanel.prototype._init = function(devtoolbar) {
|
||||
this._frame.removeEventListener("load", onload, true);
|
||||
|
||||
this.document = this._frame.contentDocument;
|
||||
this._copyTheme();
|
||||
|
||||
this._div = this.document.getElementById("gcli-output-root");
|
||||
this._div.classList.add('gcli-row-out');
|
||||
@ -851,6 +852,16 @@ OutputPanel.prototype._init = function(devtoolbar) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/* Copy the current devtools theme attribute into the iframe,
|
||||
so it can be styled correctly. */
|
||||
OutputPanel.prototype._copyTheme = function() {
|
||||
if (this.document) {
|
||||
let theme =
|
||||
this._devtoolbar._doc.documentElement.getAttribute("devtoolstheme");
|
||||
this.document.documentElement.setAttribute("devtoolstheme", theme);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevent the popup from hiding if it is not permitted via this.canHide.
|
||||
*/
|
||||
@ -875,6 +886,7 @@ OutputPanel.prototype.show = function() {
|
||||
this._frame.style.minHeight = this._frame.style.maxHeight = 0;
|
||||
this._frame.style.minWidth = 0;
|
||||
|
||||
this._copyTheme();
|
||||
this._panel.openPopup(this._input, "before_start", 0, 0, false, false, null);
|
||||
this._resize();
|
||||
|
||||
@ -1090,6 +1102,7 @@ TooltipPanel.prototype._init = function(devtoolbar) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let chromeDocument = devtoolbar._doc;
|
||||
this._devtoolbar = devtoolbar;
|
||||
this._input = devtoolbar._doc.querySelector(".gclitoolbar-input-node");
|
||||
this._toolbar = devtoolbar._doc.querySelector("#developer-toolbar");
|
||||
this._dimensions = { start: 0, end: 0 };
|
||||
@ -1146,6 +1159,7 @@ TooltipPanel.prototype._init = function(devtoolbar) {
|
||||
this._frame.removeEventListener("load", onload, true);
|
||||
|
||||
this.document = this._frame.contentDocument;
|
||||
this._copyTheme();
|
||||
this.hintElement = this.document.getElementById("gcli-tooltip-root");
|
||||
this._connector = this.document.getElementById("gcli-tooltip-connector");
|
||||
|
||||
@ -1158,7 +1172,17 @@ TooltipPanel.prototype._init = function(devtoolbar) {
|
||||
this._frame.addEventListener("load", onload, true);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
/* Copy the current devtools theme attribute into the iframe,
|
||||
so it can be styled correctly. */
|
||||
TooltipPanel.prototype._copyTheme = function() {
|
||||
if (this.document) {
|
||||
let theme =
|
||||
this._devtoolbar._doc.documentElement.getAttribute("devtoolstheme");
|
||||
this.document.documentElement.setAttribute("devtoolstheme", theme);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevent the popup from hiding if it is not permitted via this.canHide.
|
||||
@ -1191,6 +1215,7 @@ TooltipPanel.prototype.show = function(dimensions) {
|
||||
this.canHide = false;
|
||||
}
|
||||
|
||||
this._copyTheme();
|
||||
this._resize();
|
||||
this._panel.openPopup(this._input, "before_start", dimensions.start * 10, 0,
|
||||
false, false, null);
|
||||
|
@ -11,17 +11,16 @@
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Protocol error (unknownError): Error: Got an invalid root window in DocumentWalker");
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf-8,<p>Tooltip Tests</p>";
|
||||
const PREF_DEVTOOLS_THEME = "devtools.theme";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI, function() {
|
||||
Task.spawn(runTest).catch(err => {
|
||||
ok(false, ex);
|
||||
console.error(ex);
|
||||
}).then(finish);
|
||||
});
|
||||
}
|
||||
registerCleanupFunction(() => {
|
||||
// Set preferences back to their original values
|
||||
Services.prefs.clearUserPref(PREF_DEVTOOLS_THEME);
|
||||
});
|
||||
|
||||
add_task(function* showToolbar() {
|
||||
yield promiseTab(TEST_URI);
|
||||
|
||||
function* runTest() {
|
||||
info("Starting browser_toolbar_tooltip.js");
|
||||
|
||||
ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible in runTest");
|
||||
@ -29,7 +28,9 @@ function* runTest() {
|
||||
let showPromise = observeOnce(DeveloperToolbar.NOTIFICATIONS.SHOW);
|
||||
document.getElementById("Tools:DevToolbar").doCommand();
|
||||
yield showPromise;
|
||||
});
|
||||
|
||||
add_task(function* testDimensions() {
|
||||
let tooltipPanel = DeveloperToolbar.tooltipPanel;
|
||||
|
||||
DeveloperToolbar.display.focusManager.helpRequest();
|
||||
@ -54,7 +55,26 @@ function* runTest() {
|
||||
is(tooltipPanel._dimensions.start, 0,
|
||||
'search param start, when cursor at start');
|
||||
ok(getLeftMargin() > 9, 'tooltip offset, when cursor at start')
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* testThemes() {
|
||||
let tooltipPanel = DeveloperToolbar.tooltipPanel;
|
||||
ok(tooltipPanel.document, "Tooltip panel is initialized");
|
||||
|
||||
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "dark");
|
||||
|
||||
yield DeveloperToolbar.display.inputter.setInput("");
|
||||
yield DeveloperToolbar.display.inputter.setInput("help help");
|
||||
is(tooltipPanel.document.documentElement.getAttribute("devtoolstheme"),
|
||||
"dark", "Tooltip panel has correct theme");
|
||||
|
||||
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "light");
|
||||
|
||||
yield DeveloperToolbar.display.inputter.setInput("");
|
||||
yield DeveloperToolbar.display.inputter.setInput("help help");
|
||||
is(tooltipPanel.document.documentElement.getAttribute("devtoolstheme"),
|
||||
"light", "Tooltip panel has correct theme");
|
||||
});
|
||||
|
||||
function getLeftMargin() {
|
||||
let style = DeveloperToolbar.tooltipPanel._panel.style.marginLeft;
|
||||
|
@ -583,20 +583,16 @@ CubicBezierPresetWidget.prototype = {
|
||||
preset.classList.add("preset");
|
||||
preset.id = presetLabel;
|
||||
preset.coordinates = PRESETS[categoryLabel][presetLabel];
|
||||
|
||||
// Create preset preview
|
||||
let curve = doc.createElement("canvas");
|
||||
let bezier = new CubicBezier(preset.coordinates);
|
||||
|
||||
curve.setAttribute("height", 55);
|
||||
curve.setAttribute("width", 55);
|
||||
|
||||
curve.setAttribute("height", 50);
|
||||
curve.setAttribute("width", 50);
|
||||
preset.bezierCanvas = new BezierCanvas(curve, bezier, [0.15, 0]);
|
||||
preset.bezierCanvas.plot({
|
||||
drawHandles: false,
|
||||
bezierThickness: 0.025
|
||||
});
|
||||
|
||||
preset.appendChild(curve);
|
||||
|
||||
// Create preset label
|
||||
|
@ -136,19 +136,19 @@ canvas#curve {
|
||||
/* Preset Widget */
|
||||
|
||||
.preset-pane {
|
||||
width:50%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
border-right: 1px solid var(--theme-splitter-color);
|
||||
padding-right: 4px; /* Visual balance for the panel-arrowcontent border on the left */
|
||||
}
|
||||
|
||||
#preset-categories {
|
||||
display: flex;
|
||||
width: 94%;
|
||||
width: 95%;
|
||||
border: 1px solid var(--theme-splitter-color);
|
||||
border-radius: 2px;
|
||||
background-color: var(--theme-toolbar-background);
|
||||
margin-left: 4px;
|
||||
margin-top: 3px;
|
||||
margin: 3px auto 0 auto;
|
||||
}
|
||||
|
||||
#preset-categories .category:last-child {
|
||||
@ -156,8 +156,7 @@ canvas#curve {
|
||||
}
|
||||
|
||||
.category {
|
||||
flex: 1 1 auto;
|
||||
padding: 5px;
|
||||
padding: 5px 0px;
|
||||
width: 33.33%;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
@ -183,26 +182,24 @@ canvas#curve {
|
||||
padding: 0px;
|
||||
width: 100%;
|
||||
height: 331px;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.preset-list {
|
||||
display: none;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.active-preset-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: left;
|
||||
padding-left: 4px;
|
||||
padding-top: 3px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.preset {
|
||||
cursor: pointer;
|
||||
width: 55px;
|
||||
margin: 5px 11px 0px 0px;
|
||||
text-transform: capitalize;
|
||||
width: 33.33%;
|
||||
margin: 5px 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -211,6 +208,7 @@ canvas#curve {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
background-color: var(--theme-body-background);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.theme-dark .preset canvas {
|
||||
@ -218,11 +216,10 @@ canvas#curve {
|
||||
}
|
||||
|
||||
.preset p {
|
||||
text-align: center;
|
||||
font-size: 0.9em;
|
||||
line-height: 0px;
|
||||
margin: 2px 0px 0px 0p;
|
||||
margin: 2px auto 0px auto;
|
||||
color: var(--theme-body-color-alt);
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.active-preset p, .active-preset:hover p {
|
||||
|
@ -112,9 +112,7 @@ function initializeAutoCompletion(ctx, options = {}) {
|
||||
|
||||
if (!autocompleteState.suggestionInsertedOnce && popup.selectedItem) {
|
||||
autocompleteMap.get(ed).insertingSuggestion = true;
|
||||
let {label, preLabel, text} = popup.selectedItem;
|
||||
let cur = ed.getCursor();
|
||||
ed.replaceText(text.slice(preLabel.length), cur, cur);
|
||||
insertPopupItem(ed, popup.selectedItem);
|
||||
}
|
||||
|
||||
popup.hidePopup();
|
||||
@ -226,6 +224,28 @@ function autoComplete({ ed, cm }) {
|
||||
}).then(null, Cu.reportError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a popup item into the current cursor location
|
||||
* in the editor.
|
||||
*/
|
||||
function insertPopupItem(ed, popupItem) {
|
||||
let {label, preLabel, text} = popupItem;
|
||||
let cur = ed.getCursor();
|
||||
let textBeforeCursor = ed.getText(cur.line).substring(0, cur.ch);
|
||||
let backwardsTextBeforeCursor = textBeforeCursor.split("").reverse().join("");
|
||||
let backwardsPreLabel = preLabel.split("").reverse().join("");
|
||||
|
||||
// If there is additional text in the preLabel vs the line, then
|
||||
// just insert the entire autocomplete text. An example:
|
||||
// if you type 'a' and select '#about' from the autocomplete menu,
|
||||
// then the final text needs to the end up as '#about'.
|
||||
if (backwardsPreLabel.indexOf(backwardsTextBeforeCursor) === 0) {
|
||||
ed.replaceText(text, {line: cur.line, ch: 0}, cur);
|
||||
} else {
|
||||
ed.replaceText(text.slice(preLabel.length), cur, cur);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycles through provided suggestions by the popup in a top to bottom manner
|
||||
* when `reverse` is not true. Opposite otherwise.
|
||||
@ -250,7 +270,7 @@ function cycleSuggestions(ed, reverse) {
|
||||
}
|
||||
if (popup.itemCount == 1)
|
||||
popup.hidePopup();
|
||||
ed.replaceText(firstItem.text.slice(firstItem.preLabel.length), cur, cur);
|
||||
insertPopupItem(ed, firstItem);
|
||||
} else {
|
||||
let fromCur = {
|
||||
line: cur.line,
|
||||
|
@ -757,39 +757,54 @@ CSSCompleter.prototype = {
|
||||
result = result.suggestions;
|
||||
let query = this.selector;
|
||||
let completion = [];
|
||||
for (let value of result) {
|
||||
for (let [value, count, state] of result) {
|
||||
switch(this.selectorState) {
|
||||
case SELECTOR_STATES.id:
|
||||
case SELECTOR_STATES.class:
|
||||
case SELECTOR_STATES.pseudo:
|
||||
if (/^[.:#]$/.test(this.completing)) {
|
||||
value[0] = query.slice(0, query.length - this.completing.length) +
|
||||
value[0];
|
||||
value = query.slice(0, query.length - this.completing.length) +
|
||||
value;
|
||||
} else {
|
||||
value[0] = query.slice(0, query.length - this.completing.length - 1) +
|
||||
value[0];
|
||||
value = query.slice(0, query.length - this.completing.length - 1) +
|
||||
value;
|
||||
}
|
||||
break;
|
||||
|
||||
case SELECTOR_STATES.tag:
|
||||
value[0] = query.slice(0, query.length - this.completing.length) +
|
||||
value[0];
|
||||
value = query.slice(0, query.length - this.completing.length) +
|
||||
value;
|
||||
break;
|
||||
|
||||
case SELECTOR_STATES.null:
|
||||
value[0] = query + value[0];
|
||||
value = query + value;
|
||||
break;
|
||||
|
||||
default:
|
||||
value[0] = query.slice(0, query.length - this.completing.length) +
|
||||
value[0];
|
||||
value = query.slice(0, query.length - this.completing.length) +
|
||||
value;
|
||||
}
|
||||
completion.push({
|
||||
label: value[0],
|
||||
|
||||
let item = {
|
||||
label: value,
|
||||
preLabel: query,
|
||||
text: value[0],
|
||||
score: value[1]
|
||||
});
|
||||
text: value,
|
||||
score: count
|
||||
};
|
||||
|
||||
// In case the query's state is tag and the item's state is id or class
|
||||
// adjust the preLabel
|
||||
if (this.selectorState === SELECTOR_STATES.tag &&
|
||||
state === SELECTOR_STATES.class) {
|
||||
item.preLabel = "." + item.preLabel;
|
||||
}
|
||||
if (this.selectorState === SELECTOR_STATES.tag &&
|
||||
state === SELECTOR_STATES.id) {
|
||||
item.preLabel = "#" + item.preLabel;
|
||||
}
|
||||
|
||||
completion.push(item);
|
||||
|
||||
if (completion.length > this.maxEntries - 1)
|
||||
break;
|
||||
}
|
||||
|
@ -463,8 +463,8 @@ Editor.prototype = {
|
||||
|
||||
/**
|
||||
* Replaces contents of a text area within the from/to {line, ch}
|
||||
* range. If neither from nor to arguments are provided works
|
||||
* exactly like setText. If only from object is provided, inserts
|
||||
* range. If neither `from` nor `to` arguments are provided works
|
||||
* exactly like setText. If only `from` object is provided, inserts
|
||||
* text at that point, *overwriting* as many characters as needed.
|
||||
*/
|
||||
replaceText: function (value, from, to) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
const {InspectorFront} = require("devtools/server/actors/inspector");
|
||||
const AUTOCOMPLETION_PREF = "devtools.editor.autocomplete";
|
||||
const TEST_URI = "data:text/html;charset=UTF-8,<html><body><b1></b1><b2></b2><body></html>";
|
||||
const TEST_URI = "data:text/html;charset=UTF-8,<html><body><bar></bar><div id='baz'></div><body></html>";
|
||||
|
||||
add_task(function*() {
|
||||
yield promiseTab(TEST_URI);
|
||||
@ -25,6 +25,8 @@ function* runTests() {
|
||||
});
|
||||
yield testMouse(ed, edWin);
|
||||
yield testKeyboard(ed, edWin);
|
||||
yield testKeyboardCycle(ed, edWin);
|
||||
yield testKeyboardCycleForPrefixedString(ed, edWin);
|
||||
teardown(ed, win);
|
||||
}
|
||||
|
||||
@ -42,7 +44,47 @@ function* testKeyboard(ed, win) {
|
||||
yield popupOpened;
|
||||
|
||||
EventUtils.synthesizeKey("VK_RETURN", { }, win);
|
||||
is (ed.getText(), "b1", "Editor text has been updated");
|
||||
is (ed.getText(), "bar", "Editor text has been updated");
|
||||
}
|
||||
|
||||
function* testKeyboardCycle(ed, win) {
|
||||
ed.focus();
|
||||
ed.setText("b");
|
||||
ed.setCursor({line: 1, ch: 1});
|
||||
|
||||
let popupOpened = ed.getAutocompletionPopup().once("popup-opened");
|
||||
|
||||
let autocompleteKey = Editor.keyFor("autocompletion", { noaccel: true }).toUpperCase();
|
||||
EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
|
||||
|
||||
info ("Waiting for popup to be opened");
|
||||
yield popupOpened;
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", { }, win);
|
||||
is (ed.getText(), "bar", "Editor text has been updated");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", { }, win);
|
||||
is (ed.getText(), "body", "Editor text has been updated");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", { }, win);
|
||||
is (ed.getText(), "#baz", "Editor text has been updated");
|
||||
}
|
||||
|
||||
function* testKeyboardCycleForPrefixedString(ed, win) {
|
||||
ed.focus();
|
||||
ed.setText("#b");
|
||||
ed.setCursor({line: 1, ch: 2});
|
||||
|
||||
let popupOpened = ed.getAutocompletionPopup().once("popup-opened");
|
||||
|
||||
let autocompleteKey = Editor.keyFor("autocompletion", { noaccel: true }).toUpperCase();
|
||||
EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
|
||||
|
||||
info ("Waiting for popup to be opened");
|
||||
yield popupOpened;
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", { }, win);
|
||||
is (ed.getText(), "#baz", "Editor text has been updated");
|
||||
}
|
||||
|
||||
function* testMouse(ed, win) {
|
||||
@ -57,6 +99,6 @@ function* testMouse(ed, win) {
|
||||
|
||||
info ("Waiting for popup to be opened");
|
||||
yield popupOpened;
|
||||
ed.getAutocompletionPopup()._list.firstChild.click();
|
||||
is (ed.getText(), "b1", "Editor text has been updated");
|
||||
ed.getAutocompletionPopup()._list.children[2].click();
|
||||
is (ed.getText(), "#baz", "Editor text has been updated");
|
||||
}
|
||||
|
@ -21,8 +21,8 @@
|
||||
[[21, 9], ["-moz-calc", "auto", "calc", "inherit", "initial","unset"]],
|
||||
[[22, 5], ['color', 'color-interpolation', 'color-interpolation-filters']],
|
||||
[[25, 26], ['.devtools-toolbarbutton > tab',
|
||||
'.devtools-toolbarbutton > .toolbarbutton-menubutton-button',
|
||||
'.devtools-toolbarbutton > hbox']],
|
||||
'.devtools-toolbarbutton > hbox',
|
||||
'.devtools-toolbarbutton > .toolbarbutton-menubutton-button']],
|
||||
[[25, 31], ['.devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button']],
|
||||
[[29, 20], ['.devtools-menulist:after', '.devtools-menulist:active']],
|
||||
[[30, 10], ['#devtools-anotherone', '#devtools-itjustgoeson', '#devtools-menu',
|
||||
@ -31,6 +31,6 @@
|
||||
[[43, 51], ['.devtools-toolbarbutton:not([checked=true]):hover:after',
|
||||
'.devtools-toolbarbutton:not([checked=true]):hover:active']],
|
||||
[[58, 36], ['!important;']],
|
||||
[[73, 42], [':last-child', ':lang(', ':last-of-type', ':link']],
|
||||
[[73, 42], [':lang(', ':last-of-type', ':link', ':last-child']],
|
||||
[[77, 25], ['.visible']],
|
||||
]
|
||||
|
@ -423,6 +423,16 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
||||
search providers: "Search for <used typed keywords> with:" -->
|
||||
<!ENTITY searchFor.label "Search for ">
|
||||
<!ENTITY searchWith.label " with:">
|
||||
|
||||
<!-- LOCALIZATION NOTE (search.label, searchAfter.label):
|
||||
This string is used to build the header above the list of one-click search
|
||||
providers when a one off engine has been selected. The searchAfter text is
|
||||
intentionally left empty for en-US and can be used by other localizations to
|
||||
display a string after the search engine name. This string will be displayed
|
||||
as: "Search <selected engine name><searchAfter.label text>" -->
|
||||
<!ENTITY search.label "Search ">
|
||||
<!ENTITY searchAfter.label "">
|
||||
|
||||
<!-- LOCALIZATION NOTE (searchWithHeader.label):
|
||||
The wording of this string should be as close as possible to
|
||||
searchFor.label and searchWith.label. This string will be used instead of
|
||||
|
@ -159,6 +159,11 @@ let ReaderParent = {
|
||||
}
|
||||
},
|
||||
|
||||
forceShowReaderIcon: function(browser) {
|
||||
browser.isArticle = true;
|
||||
this.updateReaderButton(browser);
|
||||
},
|
||||
|
||||
buttonClick: function(event) {
|
||||
if (event.button != 0) {
|
||||
return;
|
||||
|
@ -2,18 +2,36 @@
|
||||
* 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/. */
|
||||
|
||||
|
||||
/* NOTE: THESE NEED TO STAY IN SYNC WITH LIGHT-THEME.CSS AND DARK-THEME.CSS.
|
||||
We are copy/pasting variables from light-theme and dark-theme,
|
||||
since they aren't loaded in this context (within commandlineoutput.xhtml
|
||||
and commandlinetooltip.xhtml). */
|
||||
:root[devtoolstheme="light"] {
|
||||
--gcli-background-color: #ebeced; /* --theme-tab-toolbar-background */
|
||||
--gcli-input-focused-background: #f7f7f7; /* --theme-sidebar-background */
|
||||
--gcli-input-color: #18191a; /* --theme-body-color */
|
||||
--gcli-border-color: #aaaaaa; /* --theme-splitter-color */
|
||||
}
|
||||
|
||||
:root[devtoolstheme="dark"] {
|
||||
--gcli-background-color: #343c45; /* --theme-toolbar-background */
|
||||
--gcli-input-focused-background: #252c33; /* --theme-tab-toolbar-background */
|
||||
--gcli-input-color: #b6babf; /* --theme-body-color-alt */
|
||||
--gcli-border-color: black; /* --theme-splitter-color */
|
||||
}
|
||||
|
||||
.gcli-body {
|
||||
margin: 0;
|
||||
font: message-box;
|
||||
color: hsl(210,30%,85%);
|
||||
color: var(--gcli-input-color);
|
||||
}
|
||||
|
||||
#gcli-output-root,
|
||||
#gcli-tooltip-root {
|
||||
border: 1px solid hsl(206,37%,4%);
|
||||
box-shadow: 0 1px 0 hsla(209,29%,72%,.25) inset;
|
||||
background-image: linear-gradient(hsla(209,18%,18%,0.9), hsl(209,23%,18%));
|
||||
border: 1px solid var(--gcli-border-color);
|
||||
border-radius: 3px;
|
||||
background-color: var(--gcli-background-color);
|
||||
}
|
||||
|
||||
#gcli-output-root {
|
||||
@ -32,9 +50,9 @@
|
||||
margin-left: 8px;
|
||||
width: 20px;
|
||||
height: 10px;
|
||||
border-left: 1px solid hsl(206,37%,4%);
|
||||
border-right: 1px solid hsl(206,37%,4%);
|
||||
background-color: hsl(209,23%,18%);
|
||||
border-left: 1px solid var(--gcli-border-color);
|
||||
border-right: 1px solid var(--gcli-border-color);
|
||||
background-color: var(--gcli-background-color);
|
||||
}
|
||||
|
||||
.gcli-tt-description,
|
||||
@ -47,7 +65,7 @@
|
||||
line-height: 1.2em;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
color: hsl(210,30%,85%);
|
||||
color: var(--gcli-input-color);
|
||||
}
|
||||
|
||||
.gcli-row-out p,
|
||||
@ -65,7 +83,7 @@
|
||||
.gcli-row-out th,
|
||||
.gcli-row-out strong,
|
||||
.gcli-row-out pre {
|
||||
color: hsl(210,30%,95%);
|
||||
color: var(--gcli-input-color);
|
||||
}
|
||||
|
||||
.gcli-row-out pre {
|
||||
@ -83,13 +101,13 @@
|
||||
font-weight: normal;
|
||||
font-size: 90%;
|
||||
border-radius: 3px;
|
||||
background-color: hsl(209,23%,18%);
|
||||
border: 1px solid hsl(206,37%,4%);
|
||||
background-color: var(--gcli-background-color);
|
||||
border: 1px solid var(--gcli-border-color);
|
||||
}
|
||||
|
||||
.gcli-out-shortcut:before,
|
||||
.gcli-help-synopsis:before {
|
||||
color: hsl(210,30%,85%);
|
||||
color: var(--gcli-input-color);
|
||||
-moz-padding-end: 2px;
|
||||
}
|
||||
|
||||
@ -117,12 +135,12 @@
|
||||
|
||||
.gcli-menu-desc {
|
||||
-moz-padding-end: 8px;
|
||||
color: hsl(210,30%,75%);
|
||||
color: var(--gcli-input-color);
|
||||
}
|
||||
|
||||
.gcli-menu-name:hover,
|
||||
.gcli-menu-desc:hover {
|
||||
background-color: hsla(0,0%,0%,.3);
|
||||
background-color: var(--gcli-input-focused-background);
|
||||
}
|
||||
|
||||
.gcli-menu-highlight,
|
||||
|
@ -6,13 +6,35 @@
|
||||
|
||||
/* Developer toolbar */
|
||||
|
||||
/* NOTE: THESE NEED TO STAY IN SYNC WITH LIGHT-THEME.CSS AND DARK-THEME.CSS.
|
||||
We are copy/pasting variables from light-theme and dark-theme,
|
||||
since they aren't loaded in this context (within browser.css). */
|
||||
:root[devtoolstheme="light"] #developer-toolbar {
|
||||
--gcli-background-color: #ebeced; /* --theme-tab-toolbar-background */
|
||||
--gcli-input-background: #f0f1f2; /* --theme-toolbar-background */
|
||||
--gcli-input-focused-background: #f7f7f7; /* --theme-sidebar-background */
|
||||
--gcli-input-color: #18191a; /* --theme-body-color */
|
||||
--gcli-border-color: #aaaaaa; /* --theme-splitter-color */
|
||||
--selection-background: #4c9ed9; /* --theme-selection-background */
|
||||
--selection-color: #f5f7fa; /* --theme-selection-color */
|
||||
}
|
||||
|
||||
:root[devtoolstheme="dark"] #developer-toolbar {
|
||||
--gcli-background-color: #343c45; /* --theme-toolbar-background */
|
||||
--gcli-input-background: rgba(37, 44, 51, .6); /* --theme-tab-toolbar-background */
|
||||
--gcli-input-focused-background: #252c33; /* --theme-tab-toolbar-background */
|
||||
--gcli-input-color: #b6babf; /* --theme-body-color-alt */
|
||||
--gcli-border-color: black; /* --theme-splitter-color */
|
||||
--selection-background: #1d4f73; /* --theme-selection-background */
|
||||
--selection-color: #f5f7fa; /* --theme-selection-color */
|
||||
}
|
||||
|
||||
#developer-toolbar {
|
||||
-moz-appearance: none;
|
||||
padding: 0;
|
||||
min-height: 32px;
|
||||
background-color: #343C45; /* Toolbars */
|
||||
border-top: 1px solid #060a0d;
|
||||
box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset, 0 -1px 0 hsla(206,37%,4%,.1) inset;
|
||||
background-color: var(--gcli-background-color);
|
||||
border-top: 1px solid var(--gcli-border-color);
|
||||
}
|
||||
|
||||
#developer-toolbar > toolbarbutton {
|
||||
@ -28,6 +50,11 @@
|
||||
margin: auto 10px;
|
||||
}
|
||||
|
||||
:root[devtoolstheme="light"] #developer-toolbar > toolbarbutton:not([checked=true]) > image,
|
||||
:root[devtoolstheme="light"] .gclitoolbar-input-node:not([focused=true])::before {
|
||||
filter: url("chrome://browser/skin/devtools/filters.svg#invert");
|
||||
}
|
||||
|
||||
.developer-toolbar-button > .toolbarbutton-icon,
|
||||
#developer-toolbar-closebutton > .toolbarbutton-icon {
|
||||
width: 16px;
|
||||
@ -135,24 +162,25 @@ html|*#gcli-output-frame {
|
||||
box-shadow: none;
|
||||
border-width: 0;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node {
|
||||
-moz-appearance: none;
|
||||
color: hsl(210,30%,85%);
|
||||
background-color: #242b33;
|
||||
color: var(--gcli-input-color);
|
||||
background-color: var(--gcli-input-background);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 4px center;
|
||||
box-shadow: 0 1px 1px hsla(206,37%,4%,.2) inset,
|
||||
1px 0 0 hsla(206,37%,4%,.2) inset,
|
||||
-1px 0 0 hsla(206,37%,4%,.2) inset;
|
||||
box-shadow: 1px 0 0 var(--gcli-border-color) inset,
|
||||
-1px 0 0 var(--gcli-border-color) inset;
|
||||
|
||||
line-height: 32px;
|
||||
outline-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node[focused="true"] {
|
||||
background-color: #232e38;
|
||||
background-color: var(--gcli-input-focused-background);
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node::before {
|
||||
@ -178,8 +206,8 @@ html|*#gcli-output-frame {
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
|
||||
background-color: hsl(210,30%,85%);
|
||||
color: hsl(210,24%,16%);
|
||||
background-color: var(--selection-background);
|
||||
color: var(--selection-color);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,10 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Colors are taken from:
|
||||
* https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors
|
||||
* https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors.
|
||||
* Changes should be kept in sync with commandline.css and commandline.inc.css.
|
||||
*/
|
||||
:root {
|
||||
|
||||
--theme-body-background: #14171a;
|
||||
--theme-sidebar-background: #181d20;
|
||||
--theme-contrast-background: #b28025;
|
||||
|
@ -47,7 +47,7 @@
|
||||
}
|
||||
|
||||
.editable {
|
||||
border-bottom: 1px dashed transparent;
|
||||
border: 1px dashed transparent;
|
||||
}
|
||||
|
||||
.editable:hover {
|
||||
|
@ -4,7 +4,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Colors are taken from:
|
||||
* https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors
|
||||
* https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors.
|
||||
* Changes should be kept in sync with commandline.css and commandline.inc.css.
|
||||
*/
|
||||
:root {
|
||||
--theme-body-background: #fcfcfc;
|
||||
|
@ -317,32 +317,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ::::: fullscreen window controls ::::: */
|
||||
|
||||
#window-controls {
|
||||
-moz-box-align: start;
|
||||
}
|
||||
|
||||
#minimize-button,
|
||||
#restore-button,
|
||||
#close-button {
|
||||
-moz-appearance: none;
|
||||
border-style: none;
|
||||
margin: 0;
|
||||
}
|
||||
#close-button {
|
||||
-moz-image-region: rect(0, 49px, 16px, 32px);
|
||||
}
|
||||
#close-button:hover {
|
||||
-moz-image-region: rect(16px, 49px, 32px, 32px);
|
||||
}
|
||||
#close-button:hover:active {
|
||||
-moz-image-region: rect(32px, 49px, 48px, 32px);
|
||||
}
|
||||
|
||||
#minimize-button:-moz-locale-dir(rtl),
|
||||
#restore-button:-moz-locale-dir(rtl),
|
||||
#close-button:-moz-locale-dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
@ -1128,6 +1128,35 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
||||
-moz-image-region: rect(32px, 48px, 48px, 32px);
|
||||
}
|
||||
|
||||
@media not all and (-moz-os-version: windows-xp) {
|
||||
#window-controls {
|
||||
-moz-box-align: start;
|
||||
}
|
||||
|
||||
#minimize-button,
|
||||
#restore-button,
|
||||
#close-button {
|
||||
-moz-appearance: none;
|
||||
border-style: none;
|
||||
margin: 0;
|
||||
}
|
||||
#close-button {
|
||||
-moz-image-region: rect(0, 49px, 16px, 32px);
|
||||
}
|
||||
#close-button:hover {
|
||||
-moz-image-region: rect(16px, 49px, 32px, 32px);
|
||||
}
|
||||
#close-button:hover:active {
|
||||
-moz-image-region: rect(32px, 49px, 48px, 32px);
|
||||
}
|
||||
|
||||
#minimize-button:-moz-locale-dir(rtl),
|
||||
#restore-button:-moz-locale-dir(rtl),
|
||||
#close-button:-moz-locale-dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ::::: Location Bar ::::: */
|
||||
|
||||
#urlbar,
|
||||
|
@ -62,6 +62,7 @@ let modules = {
|
||||
reader: {
|
||||
uri: "chrome://global/content/reader/aboutReader.html",
|
||||
privileged: false,
|
||||
dontLink: true,
|
||||
hide: true
|
||||
},
|
||||
feedback: {
|
||||
@ -109,6 +110,8 @@ AboutRedirector.prototype = {
|
||||
let moduleInfo = this._getModuleInfo(aURI);
|
||||
if (moduleInfo.hide)
|
||||
flags = Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT;
|
||||
if (moduleInfo.dontLink)
|
||||
flags = flags | Ci.nsIAboutModule.MAKE_UNLINKABLE;
|
||||
|
||||
return flags | Ci.nsIAboutModule.ALLOW_SCRIPT;
|
||||
},
|
||||
|
@ -28,6 +28,14 @@ static bool IsSafeForUntrustedContent(nsIAboutModule *aModule, nsIURI *aURI) {
|
||||
|
||||
return (flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) != 0;
|
||||
}
|
||||
|
||||
static bool IsSafeToLinkForUntrustedContent(nsIAboutModule *aModule, nsIURI *aURI) {
|
||||
uint32_t flags;
|
||||
nsresult rv = aModule->GetURIFlags(aURI, &flags);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
return (flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) && !(flags & nsIAboutModule::MAKE_UNLINKABLE);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler)
|
||||
@ -82,7 +90,7 @@ nsAboutProtocolHandler::NewURI(const nsACString &aSpec,
|
||||
nsCOMPtr<nsIAboutModule> aboutMod;
|
||||
rv = NS_GetAboutModule(url, getter_AddRefs(aboutMod));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
isSafe = IsSafeForUntrustedContent(aboutMod, url);
|
||||
isSafe = IsSafeToLinkForUntrustedContent(aboutMod, url);
|
||||
}
|
||||
|
||||
if (isSafe) {
|
||||
|
@ -25,10 +25,10 @@ interface nsIAboutModule : nsISupports
|
||||
/**
|
||||
* A flag that indicates whether a URI is safe for untrusted
|
||||
* content. If it is, web pages and so forth will be allowed to
|
||||
* link to this about: URI, and the about: protocol handler will
|
||||
* enforce that the principal of channels created for it be based
|
||||
* on their originalURI or URI (depending on the channel flags),
|
||||
* by setting their "owner" to null.
|
||||
* link to this about: URI (unless MAKE_UNLINKABLE is also specified),
|
||||
* and the about: protocol handler will enforce that the principal
|
||||
* of channels created for it be based on their originalURI or URI
|
||||
* (depending on the channel flags), by setting their "owner" to null.
|
||||
* Otherwise, only chrome will be able to link to it.
|
||||
*/
|
||||
const unsigned long URI_SAFE_FOR_UNTRUSTED_CONTENT = (1 << 0);
|
||||
@ -60,6 +60,12 @@ interface nsIAboutModule : nsISupports
|
||||
*/
|
||||
const unsigned long URI_MUST_LOAD_IN_CHILD = (1 << 5);
|
||||
|
||||
/**
|
||||
* A flag that indicates that this URI should be unlinkable despite being
|
||||
* safe for untrusted content.
|
||||
*/
|
||||
const unsigned long MAKE_UNLINKABLE = (1 << 6);
|
||||
|
||||
/**
|
||||
* A method to get the flags that apply to a given about: URI. The URI
|
||||
* passed in is guaranteed to be one of the URIs that this module
|
||||
|
@ -40,36 +40,47 @@
|
||||
}
|
||||
td.jank0 {
|
||||
color: rgb(0, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank1 {
|
||||
color: rgb(25, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank2 {
|
||||
color: rgb(50, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank3 {
|
||||
color: rgb(75, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank4 {
|
||||
color: rgb(100, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank5 {
|
||||
color: rgb(125, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank6 {
|
||||
color: rgb(150, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank7 {
|
||||
color: rgb(175, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank8 {
|
||||
color: rgb(200, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank9 {
|
||||
color: rgb(225, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank9 {
|
||||
td.jank10 {
|
||||
color: rgb(255, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
@ -1015,7 +1015,7 @@ var NodeListActor = exports.NodeListActor = protocol.ActorClass({
|
||||
form: function() {
|
||||
return {
|
||||
actor: this.actorID,
|
||||
length: this.nodeList.length
|
||||
length: this.nodeList ? this.nodeList.length : 0
|
||||
}
|
||||
},
|
||||
|
||||
@ -1868,7 +1868,7 @@ var WalkerActor = protocol.ActorClass({
|
||||
sugs.classes.delete(HIDDEN_CLASS);
|
||||
for (let [className, count] of sugs.classes) {
|
||||
if (className.startsWith(completing)) {
|
||||
result.push(["." + className, count]);
|
||||
result.push(["." + className, count, selectorState]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1885,7 +1885,7 @@ var WalkerActor = protocol.ActorClass({
|
||||
}
|
||||
for (let [id, count] of sugs.ids) {
|
||||
if (id.startsWith(completing)) {
|
||||
result.push(["#" + id, count]);
|
||||
result.push(["#" + id, count, selectorState]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1903,9 +1903,20 @@ var WalkerActor = protocol.ActorClass({
|
||||
}
|
||||
for (let [tag, count] of sugs.tags) {
|
||||
if ((new RegExp("^" + completing + ".*", "i")).test(tag)) {
|
||||
result.push([tag, count]);
|
||||
result.push([tag, count, selectorState]);
|
||||
}
|
||||
}
|
||||
|
||||
// For state 'tag' (no preceding # or .) and when there's no query (i.e.
|
||||
// only one word) then search for the matching classes and ids
|
||||
if (!query) {
|
||||
result = [
|
||||
...result,
|
||||
...this.getSuggestionsForQuery(null, completing, "class").suggestions,
|
||||
...this.getSuggestionsForQuery(null, completing, "id").suggestions
|
||||
];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "null":
|
||||
@ -1935,11 +1946,38 @@ var WalkerActor = protocol.ActorClass({
|
||||
}
|
||||
}
|
||||
|
||||
// Sort alphabetically in increaseing order.
|
||||
result = result.sort();
|
||||
// Sort based on count in decreasing order.
|
||||
result = result.sort(function(a, b) {
|
||||
return b[1] - a[1];
|
||||
// Sort by count (desc) and name (asc)
|
||||
result = result.sort((a, b) => {
|
||||
// Computed a sortable string with first the inverted count, then the name
|
||||
let sortA = (10000-a[1]) + a[0];
|
||||
let sortB = (10000-b[1]) + b[0];
|
||||
|
||||
// Prefixing ids, classes and tags, to group results
|
||||
let firstA = a[0].substring(0, 1);
|
||||
let firstB = b[0].substring(0, 1);
|
||||
|
||||
if (firstA === "#") {
|
||||
sortA = "2" + sortA;
|
||||
}
|
||||
else if (firstA === ".") {
|
||||
sortA = "1" + sortA;
|
||||
}
|
||||
else {
|
||||
sortA = "0" + sortA;
|
||||
}
|
||||
|
||||
if (firstB === "#") {
|
||||
sortB = "2" + sortB;
|
||||
}
|
||||
else if (firstB === ".") {
|
||||
sortB = "1" + sortB;
|
||||
}
|
||||
else {
|
||||
sortB = "0" + sortB;
|
||||
}
|
||||
|
||||
// String compare
|
||||
return sortA.localeCompare(sortB);
|
||||
});
|
||||
|
||||
result.slice(0, 25);
|
||||
|
@ -608,16 +608,20 @@ function getCanStageUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (AppConstants.platform == "win" && isServiceInstalled() &&
|
||||
shouldUseService()) {
|
||||
// No need to perform directory write checks, the maintenance service will
|
||||
// be able to write to all directories.
|
||||
LOG("getCanStageUpdates - able to stage updates because we'll use the service");
|
||||
return true;
|
||||
}
|
||||
|
||||
// For Gonk, the updater will remount the /system partition to move staged
|
||||
// files into place and it uses the app.update.service.enabled preference for
|
||||
// this purpose.
|
||||
if (AppConstants.platform == "win" || AppConstants.platform == "gonk") {
|
||||
if (getPref("getBoolPref", PREF_APP_UPDATE_SERVICE_ENABLED, false)) {
|
||||
// No need to perform directory write checks, the maintenance service will
|
||||
// be able to write to all directories.
|
||||
LOG("getCanStageUpdates - able to stage updates because we'll use the service");
|
||||
return true;
|
||||
}
|
||||
// files into place.
|
||||
if (AppConstants.platform == "gonk") {
|
||||
LOG("getCanStageUpdates - able to stage updates because this is gonk");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!hasUpdateMutex()) {
|
||||
|
@ -329,7 +329,8 @@ body.loaded {
|
||||
background-color: #fbfbfb;
|
||||
visibility: hidden;
|
||||
border-radius: 4px;
|
||||
border: 1px 1px 0 1px solid #b5b5b5;
|
||||
border: 1px solid #b5b5b5;
|
||||
border-bottom-width: 0;
|
||||
box-shadow: 0 1px 12px #666;
|
||||
}
|
||||
|
||||
|
@ -387,7 +387,7 @@ CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir,
|
||||
return false;
|
||||
CopyFileIntoUpdateDir(appDir, kUpdaterINI, updateDir);
|
||||
#endif
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
nsCOMPtr<nsIFile> iconDir;
|
||||
appDir->Clone(getter_AddRefs(iconDir));
|
||||
iconDir->AppendNative(NS_LITERAL_CSTRING("icons"));
|
||||
|
Loading…
Reference in New Issue
Block a user