зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound a=merge CLOSED TREE
This commit is contained in:
Коммит
b6f591574c
|
@ -982,7 +982,7 @@ let MozLoopServiceInternal = {
|
|||
code: code,
|
||||
state: state,
|
||||
};
|
||||
return this.hawkRequest(LOOP_SESSION_TYPE.FXA, "/fxa-oauth/token", "POST", payload).then(response => {
|
||||
return this.hawkRequestInternal(LOOP_SESSION_TYPE.FXA, "/fxa-oauth/token", "POST", payload).then(response => {
|
||||
return JSON.parse(response.body);
|
||||
},
|
||||
error => {this._hawkRequestError(error);});
|
||||
|
|
|
@ -94,15 +94,10 @@ loop.roomViews = (function(mozL10n) {
|
|||
handleFormSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var newRoomName = this.state.newRoomName;
|
||||
|
||||
if (newRoomName && this.state.roomName != newRoomName) {
|
||||
this.props.dispatcher.dispatch(
|
||||
new sharedActions.RenameRoom({
|
||||
roomToken: this.state.roomToken,
|
||||
newRoomName: newRoomName
|
||||
}));
|
||||
}
|
||||
this.props.dispatcher.dispatch(new sharedActions.RenameRoom({
|
||||
roomToken: this.state.roomToken,
|
||||
newRoomName: this.state.newRoomName
|
||||
}));
|
||||
},
|
||||
|
||||
handleEmailButtonClick: function(event) {
|
||||
|
|
|
@ -94,15 +94,10 @@ loop.roomViews = (function(mozL10n) {
|
|||
handleFormSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var newRoomName = this.state.newRoomName;
|
||||
|
||||
if (newRoomName && this.state.roomName != newRoomName) {
|
||||
this.props.dispatcher.dispatch(
|
||||
new sharedActions.RenameRoom({
|
||||
roomToken: this.state.roomToken,
|
||||
newRoomName: newRoomName
|
||||
}));
|
||||
}
|
||||
this.props.dispatcher.dispatch(new sharedActions.RenameRoom({
|
||||
roomToken: this.state.roomToken,
|
||||
newRoomName: this.state.newRoomName
|
||||
}));
|
||||
},
|
||||
|
||||
handleEmailButtonClick: function(event) {
|
||||
|
|
|
@ -81,7 +81,6 @@ p {
|
|||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
|
@ -89,12 +88,16 @@ p {
|
|||
color: #fff;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: .9em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn.btn-constrained {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background-color: #0096dd;
|
||||
border: 1px solid #0095dd;
|
||||
|
|
|
@ -262,7 +262,7 @@
|
|||
}
|
||||
|
||||
.call-action-group > .btn {
|
||||
height: 26px;
|
||||
min-height: 26px;
|
||||
border-radius: 2px;
|
||||
margin: 0 4px;
|
||||
min-width: 64px;
|
||||
|
|
|
@ -154,10 +154,13 @@ loop.store = loop.store || {};
|
|||
* @param {Object} addedRoomData The added room data.
|
||||
*/
|
||||
_onRoomAdded: function(eventName, addedRoomData) {
|
||||
addedRoomData.participants = [];
|
||||
addedRoomData.ctime = new Date().getTime();
|
||||
addedRoomData.participants = addedRoomData.participants || [];
|
||||
addedRoomData.ctime = addedRoomData.ctime || new Date().getTime();
|
||||
this.dispatchAction(new sharedActions.UpdateRoomList({
|
||||
roomList: this._storeState.rooms.concat(new Room(addedRoomData))
|
||||
// Ensure the room isn't part of the list already, then add it.
|
||||
roomList: this._storeState.rooms.filter(function(room) {
|
||||
return addedRoomData.roomToken !== room.roomToken;
|
||||
}).concat(new Room(addedRoomData))
|
||||
}));
|
||||
},
|
||||
|
||||
|
@ -420,8 +423,15 @@ loop.store = loop.store || {};
|
|||
* @param {sharedActions.RenameRoom} actionData
|
||||
*/
|
||||
renameRoom: function(actionData) {
|
||||
var newRoomName = actionData.newRoomName.trim();
|
||||
|
||||
// Skip update if name is unchanged or empty.
|
||||
if (!newRoomName || this.getStoreState("roomName") === newRoomName) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setStoreState({error: null});
|
||||
this._mozLoop.rooms.rename(actionData.roomToken, actionData.newRoomName,
|
||||
this._mozLoop.rooms.rename(actionData.roomToken, newRoomName,
|
||||
function(err) {
|
||||
if (err) {
|
||||
this.dispatchAction(new sharedActions.RenameRoomError({error: err}));
|
||||
|
|
|
@ -391,7 +391,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||
React.DOM.div({className: "standalone-btn-chevron-menu-group"},
|
||||
React.DOM.div({className: "btn-group-chevron"},
|
||||
React.DOM.div({className: "btn-group"},
|
||||
React.DOM.button({className: "btn btn-large btn-accept",
|
||||
React.DOM.button({className: "btn btn-constrained btn-large btn-accept",
|
||||
onClick: this.props.startCall("audio-video"),
|
||||
disabled: this.props.disabled,
|
||||
title: mozL10n.get("initiate_audio_video_call_tooltip2")},
|
||||
|
|
|
@ -391,7 +391,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||
<div className="standalone-btn-chevron-menu-group">
|
||||
<div className="btn-group-chevron">
|
||||
<div className="btn-group">
|
||||
<button className="btn btn-large btn-accept"
|
||||
<button className="btn btn-constrained btn-large btn-accept"
|
||||
onClick={this.props.startCall("audio-video")}
|
||||
disabled={this.props.disabled}
|
||||
title={mozL10n.get("initiate_audio_video_call_tooltip2")}>
|
||||
|
|
|
@ -128,14 +128,14 @@ describe("loop.roomViews", function () {
|
|||
});
|
||||
|
||||
roomNameBox = view.getDOMNode().querySelector('.input-room-name');
|
||||
|
||||
React.addons.TestUtils.Simulate.change(roomNameBox, { target: {
|
||||
value: "reallyFake"
|
||||
}});
|
||||
});
|
||||
|
||||
it("should dispatch a RenameRoom action when the focus is lost",
|
||||
function() {
|
||||
React.addons.TestUtils.Simulate.change(roomNameBox, { target: {
|
||||
value: "reallyFake"
|
||||
}});
|
||||
|
||||
React.addons.TestUtils.Simulate.blur(roomNameBox);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
|
@ -148,6 +148,10 @@ describe("loop.roomViews", function () {
|
|||
|
||||
it("should dispatch a RenameRoom action when Enter key is pressed",
|
||||
function() {
|
||||
React.addons.TestUtils.Simulate.change(roomNameBox, { target: {
|
||||
value: "reallyFake"
|
||||
}});
|
||||
|
||||
TestUtils.Simulate.keyDown(roomNameBox, {key: "Enter", which: 13});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
|
|
|
@ -21,34 +21,34 @@ describe("loop.store.RoomStore", function () {
|
|||
var sharedActions = loop.shared.actions;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var sandbox, dispatcher;
|
||||
|
||||
var fakeRoomList = [{
|
||||
roomToken: "_nxD4V4FflQ",
|
||||
roomUrl: "http://sample/_nxD4V4FflQ",
|
||||
roomName: "First Room Name",
|
||||
maxSize: 2,
|
||||
participants: [],
|
||||
ctime: 1405517546
|
||||
}, {
|
||||
roomToken: "QzBbvGmIZWU",
|
||||
roomUrl: "http://sample/QzBbvGmIZWU",
|
||||
roomName: "Second Room Name",
|
||||
maxSize: 2,
|
||||
participants: [],
|
||||
ctime: 1405517418
|
||||
}, {
|
||||
roomToken: "3jKS_Els9IU",
|
||||
roomUrl: "http://sample/3jKS_Els9IU",
|
||||
roomName: "Third Room Name",
|
||||
maxSize: 3,
|
||||
clientMaxSize: 2,
|
||||
participants: [],
|
||||
ctime: 1405518241
|
||||
}];
|
||||
var fakeRoomList;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
dispatcher = new loop.Dispatcher();
|
||||
fakeRoomList = [{
|
||||
roomToken: "_nxD4V4FflQ",
|
||||
roomUrl: "http://sample/_nxD4V4FflQ",
|
||||
roomName: "First Room Name",
|
||||
maxSize: 2,
|
||||
participants: [],
|
||||
ctime: 1405517546
|
||||
}, {
|
||||
roomToken: "QzBbvGmIZWU",
|
||||
roomUrl: "http://sample/QzBbvGmIZWU",
|
||||
roomName: "Second Room Name",
|
||||
maxSize: 2,
|
||||
participants: [],
|
||||
ctime: 1405517418
|
||||
}, {
|
||||
roomToken: "3jKS_Els9IU",
|
||||
roomUrl: "http://sample/3jKS_Els9IU",
|
||||
roomName: "Third Room Name",
|
||||
maxSize: 3,
|
||||
clientMaxSize: 2,
|
||||
participants: [],
|
||||
ctime: 1405518241
|
||||
}];
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
|
@ -121,6 +121,17 @@ describe("loop.store.RoomStore", function () {
|
|||
|
||||
expect(store.getStoreState().rooms).to.have.length.of(4);
|
||||
});
|
||||
|
||||
it("should avoid adding a duplicate room", function() {
|
||||
var sampleRoom = fakeRoomList[0];
|
||||
|
||||
fakeMozLoop.rooms.trigger("add", "add", sampleRoom);
|
||||
|
||||
expect(store.getStoreState().rooms).to.have.length.of(3);
|
||||
expect(store.getStoreState().rooms.reduce(function(count, room) {
|
||||
return count += room.roomToken === sampleRoom.roomToken ? 1 : 0;
|
||||
}, 0)).eql(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("update", function() {
|
||||
|
@ -539,5 +550,16 @@ describe("loop.store.RoomStore", function () {
|
|||
|
||||
expect(store.getStoreState().error).eql(err);
|
||||
});
|
||||
|
||||
it("should ensure only submitting a non-empty room name", function() {
|
||||
fakeMozLoop.rooms.rename = sinon.spy();
|
||||
|
||||
dispatcher.dispatch(new sharedActions.RenameRoom({
|
||||
roomToken: "42abc",
|
||||
newRoomName: " \t \t "
|
||||
}));
|
||||
|
||||
sinon.assert.notCalled(fakeMozLoop.rooms.rename);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -90,6 +90,9 @@ var gSearchPane = {
|
|||
onTreeKeyPress: function(aEvent) {
|
||||
let index = gEngineView.selectedIndex;
|
||||
let tree = document.getElementById("engineList");
|
||||
if (tree.hasAttribute("editing"))
|
||||
return;
|
||||
|
||||
if (aEvent.charCode == KeyEvent.DOM_VK_SPACE) {
|
||||
// Space toggles the checkbox.
|
||||
let newValue = !gEngineView._engineStore.engines[index].shown;
|
||||
|
|
|
@ -83,6 +83,9 @@ var gSearchPane = {
|
|||
onTreeKeyPress: function(aEvent) {
|
||||
let index = gEngineView.selectedIndex;
|
||||
let tree = document.getElementById("engineList");
|
||||
if (tree.hasAttribute("editing"))
|
||||
return;
|
||||
|
||||
if (aEvent.charCode == KeyEvent.DOM_VK_SPACE) {
|
||||
// Space toggles the checkbox.
|
||||
let newValue = !gEngineView._engineStore.engines[index].shown;
|
||||
|
|
|
@ -5,20 +5,38 @@
|
|||
font-family: bar;
|
||||
src: url(bad/font/name.ttf), url(ostrich-regular.ttf) format("truetype");
|
||||
}
|
||||
@font-face {
|
||||
font-family: barnormal;
|
||||
font-weight: normal;
|
||||
src: url(ostrich-regular.ttf);
|
||||
}
|
||||
@font-face {
|
||||
font-family: bar;
|
||||
font-weight: bold;
|
||||
src: url(ostrich-black.ttf);
|
||||
}
|
||||
@font-face {
|
||||
font-family: bar;
|
||||
font-weight: 800;
|
||||
src: url(ostrich-black.ttf);
|
||||
}
|
||||
|
||||
body{
|
||||
font-family:Arial;
|
||||
font-size: 36px;
|
||||
}
|
||||
div {
|
||||
font-family:Arial;
|
||||
font-family:bar;
|
||||
}
|
||||
.normal-text {
|
||||
font-family: barnormal;
|
||||
font-weight: normal;
|
||||
}
|
||||
.bold-text {
|
||||
font-family: bar;
|
||||
font-weight: bold;
|
||||
}
|
||||
.black-text {
|
||||
font-family: bar;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
@ -27,4 +45,7 @@
|
|||
<body>
|
||||
BODY
|
||||
<div>DIV</div>
|
||||
<div class="normal-text">NORMAL DIV</div>
|
||||
<div class="bold-text">BOLD DIV</div>
|
||||
<div class="black-text">800 DIV</div>
|
||||
</body>
|
||||
|
|
|
@ -10,6 +10,19 @@ let TEST_URI = "http://mochi.test:8888/browser/browser/devtools/fontinspector/te
|
|||
|
||||
let view, viewDoc;
|
||||
|
||||
const BASE_URI = "http://mochi.test:8888/browser/browser/devtools/fontinspector/test/"
|
||||
|
||||
const FONTS = [
|
||||
{name: "Ostrich Sans Medium", remote: true, url: BASE_URI + "ostrich-regular.ttf",
|
||||
format: "truetype", cssName: "bar"},
|
||||
{name: "Ostrich Sans Black", remote: true, url: BASE_URI + "ostrich-black.ttf",
|
||||
format: "", cssName: "bar"},
|
||||
{name: "Ostrich Sans Black", remote: true, url: BASE_URI + "ostrich-black.ttf",
|
||||
format: "", cssName: "bar"},
|
||||
{name: "Ostrich Sans Medium", remote: true, url: BASE_URI + "ostrich-regular.ttf",
|
||||
format: "", cssName: "barnormal"},
|
||||
];
|
||||
|
||||
add_task(function*() {
|
||||
yield loadTab(TEST_URI);
|
||||
let {toolbox, inspector} = yield openInspector();
|
||||
|
@ -39,32 +52,41 @@ add_task(function*() {
|
|||
|
||||
function* testBodyFonts(inspector) {
|
||||
let s = viewDoc.querySelectorAll("#all-fonts > section");
|
||||
is(s.length, 2, "Found 2 fonts");
|
||||
is(s.length, 5, "Found 5 fonts");
|
||||
|
||||
// test first web font
|
||||
is(s[0].querySelector(".font-name").textContent,
|
||||
"Ostrich Sans Medium", "font 0: Right font name");
|
||||
ok(s[0].classList.contains("is-remote"),
|
||||
"font 0: is remote");
|
||||
is(s[0].querySelector(".font-url").value,
|
||||
"http://mochi.test:8888/browser/browser/devtools/fontinspector/test/ostrich-regular.ttf",
|
||||
"font 0: right url");
|
||||
is(s[0].querySelector(".font-format").textContent,
|
||||
"truetype", "font 0: right font format");
|
||||
is(s[0].querySelector(".font-css-name").textContent,
|
||||
"bar", "font 0: right css name");
|
||||
for (let i = 0; i < FONTS.length; i++) {
|
||||
let section = s[i];
|
||||
let font = FONTS[i];
|
||||
is(section.querySelector(".font-name").textContent, font.name,
|
||||
"font " + i + " right font name");
|
||||
is(section.classList.contains("is-remote"), font.remote,
|
||||
"font " + i + " remote value correct");
|
||||
is(section.querySelector(".font-url").value, font.url,
|
||||
"font " + i + " url correct");
|
||||
is(section.querySelector(".font-format").hidden, !font.format,
|
||||
"font " + i + " format hidden value correct");
|
||||
is(section.querySelector(".font-format").textContent,
|
||||
font.format, "font " + i + " format correct");
|
||||
is(section.querySelector(".font-css-name").textContent,
|
||||
font.cssName, "font " + i + " css name correct");
|
||||
}
|
||||
|
||||
// test that the bold and regular fonts have different previews
|
||||
let regSrc = s[0].querySelector(".font-preview").src;
|
||||
let boldSrc = s[1].querySelector(".font-preview").src;
|
||||
isnot(regSrc, boldSrc, "preview for bold font is different from regular");
|
||||
|
||||
// test system font
|
||||
let font2Name = s[1].querySelector(".font-name").textContent;
|
||||
let font2CssName = s[1].querySelector(".font-css-name").textContent;
|
||||
let localFontName = s[4].querySelector(".font-name").textContent;
|
||||
let localFontCSSName = s[4].querySelector(".font-css-name").textContent;
|
||||
|
||||
// On Linux test machines, the Arial font doesn't exist.
|
||||
// The fallback is "Liberation Sans"
|
||||
ok((font2Name == "Arial") || (font2Name == "Liberation Sans"),
|
||||
"font 1: Right font name");
|
||||
ok(s[1].classList.contains("is-local"), "font 2: is local");
|
||||
ok((font2CssName == "Arial") || (font2CssName == "Liberation Sans"),
|
||||
"Arial", "font 2: right css name");
|
||||
ok((localFontName == "Arial") || (localFontName == "Liberation Sans"),
|
||||
"local font right font name");
|
||||
ok(s[4].classList.contains("is-local"), "local font is local");
|
||||
ok((localFontCSSName == "Arial") || (localFontCSSName == "Liberation Sans"),
|
||||
"Arial", "local font has right css name");
|
||||
}
|
||||
|
||||
function* testDivFonts(inspector) {
|
||||
|
@ -87,5 +109,5 @@ function* testShowAllFonts(inspector) {
|
|||
|
||||
is(inspector.selection.nodeFront.nodeName, "BODY", "Show all fonts selected the body node");
|
||||
let sections = viewDoc.querySelectorAll("#all-fonts > section");
|
||||
is(sections.length, 2, "And font-inspector still shows 2 fonts for body");
|
||||
is(sections.length, 5, "And font-inspector still shows 5 fonts for body");
|
||||
}
|
||||
|
|
|
@ -29,24 +29,30 @@
|
|||
<h2>&prefs_general_title;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label title="&prefs_options_templatesurl_tooltip;">
|
||||
<span>&prefs_options_templatesurl;</span>
|
||||
<input data-pref="devtools.webide.templatesURL"/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_rememberlastproject_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.restoreLastProject"/>
|
||||
<span>&prefs_options_rememberlastproject;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autoconnectruntime_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.autoConnectRuntime"/>
|
||||
<span>&prefs_options_autoconnectruntime;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_showeditor_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.showProjectEditor"/>
|
||||
<span>&prefs_options_showeditor;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_templatesurl_tooltip;">
|
||||
<span>&prefs_options_templatesurl;</span>
|
||||
<input data-pref="devtools.webide.templatesURL"/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>&prefs_editor_title;</h2>
|
||||
|
|
|
@ -94,8 +94,6 @@ let UI = {
|
|||
Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);
|
||||
Services.prefs.setBoolPref("devtools.webide.autoinstallFxdtAdapters", false);
|
||||
|
||||
this.lastConnectedRuntime = Services.prefs.getCharPref("devtools.webide.lastConnectedRuntime");
|
||||
|
||||
if (Services.prefs.getBoolPref("devtools.webide.widget.autoinstall") &&
|
||||
!Services.prefs.getBoolPref("devtools.webide.widget.enabled")) {
|
||||
Services.prefs.setBoolPref("devtools.webide.widget.enabled", true);
|
||||
|
@ -393,10 +391,20 @@ let UI = {
|
|||
}
|
||||
},
|
||||
|
||||
get lastConnectedRuntime() {
|
||||
return Services.prefs.getCharPref("devtools.webide.lastConnectedRuntime");
|
||||
},
|
||||
|
||||
set lastConnectedRuntime(runtime) {
|
||||
Services.prefs.setCharPref("devtools.webide.lastConnectedRuntime", runtime);
|
||||
},
|
||||
|
||||
autoConnectRuntime: function () {
|
||||
// Automatically reconnect to the previously selected runtime,
|
||||
// if available and has an ID
|
||||
if (AppManager.selectedRuntime || !this.lastConnectedRuntime) {
|
||||
// if available and has an ID and feature is enabled
|
||||
if (AppManager.selectedRuntime ||
|
||||
!Services.prefs.getBoolPref("devtools.webide.autoConnectRuntime") ||
|
||||
!this.lastConnectedRuntime) {
|
||||
return;
|
||||
}
|
||||
let [_, type, id] = this.lastConnectedRuntime.match(/^(\w+):(.+)$/);
|
||||
|
@ -415,6 +423,8 @@ let UI = {
|
|||
// Some runtimes do not expose an id and don't support autoconnect (like
|
||||
// remote connection)
|
||||
if (runtime.id == id) {
|
||||
// Only want one auto-connect attempt, so clear last runtime value
|
||||
this.lastConnectedRuntime = "";
|
||||
this.connectToRuntime(runtime);
|
||||
}
|
||||
}
|
||||
|
@ -446,8 +456,6 @@ let UI = {
|
|||
} else {
|
||||
this.lastConnectedRuntime = "";
|
||||
}
|
||||
Services.prefs.setCharPref("devtools.webide.lastConnectedRuntime",
|
||||
this.lastConnectedRuntime);
|
||||
},
|
||||
|
||||
_actionsToLog: new Set(),
|
||||
|
|
|
@ -11,6 +11,7 @@ pref("devtools.webide.autoinstallFxdtAdapters", true);
|
|||
#else
|
||||
pref("devtools.webide.autoinstallFxdtAdapters", false);
|
||||
#endif
|
||||
pref("devtools.webide.autoConnectRuntime", true);
|
||||
pref("devtools.webide.restoreLastProject", true);
|
||||
pref("devtools.webide.enableLocalRuntime", false);
|
||||
pref("devtools.webide.addonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/index.json");
|
||||
|
|
|
@ -114,6 +114,8 @@
|
|||
<!ENTITY prefs_general_title "General">
|
||||
<!ENTITY prefs_restore "Restore Defaults">
|
||||
<!ENTITY prefs_manage_components "Manage Extra Components">
|
||||
<!ENTITY prefs_options_autoconnectruntime "Reconnect to previous runtime">
|
||||
<!ENTITY prefs_options_autoconnectruntime_tooltip "Reconnect to previous runtime when WebIDE starts">
|
||||
<!ENTITY prefs_options_rememberlastproject "Remember last project">
|
||||
<!ENTITY prefs_options_rememberlastproject_tooltip "Restore previous project when WebIDE starts">
|
||||
<!ENTITY prefs_options_templatesurl "Templates URL">
|
||||
|
|
|
@ -235,6 +235,9 @@ description > html|a {
|
|||
background-color: #fbfbfb;
|
||||
color: #424e5a;
|
||||
font-size: 14px;
|
||||
/* `transparent` will use the dialogText color in high-contrast themes and
|
||||
when page colors are disabled */
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2.5px;
|
||||
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.5);
|
||||
display: -moz-box;
|
||||
|
|
|
@ -1650,6 +1650,7 @@ public abstract class GeckoApp
|
|||
// updated before Gecko has restored.
|
||||
if (mShouldRestore) {
|
||||
final JSONArray tabs = new JSONArray();
|
||||
final JSONObject windowObject = new JSONObject();
|
||||
SessionParser parser = new SessionParser() {
|
||||
@Override
|
||||
public void onTabRead(SessionTab sessionTab) {
|
||||
|
@ -1670,6 +1671,11 @@ public abstract class GeckoApp
|
|||
}
|
||||
tabs.put(tabObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosedTabsRead(final JSONArray closedTabData) throws JSONException {
|
||||
windowObject.put("closedTabs", closedTabData);
|
||||
}
|
||||
};
|
||||
|
||||
if (mPrivateBrowsingSession == null) {
|
||||
|
@ -1679,7 +1685,8 @@ public abstract class GeckoApp
|
|||
}
|
||||
|
||||
if (tabs.length() > 0) {
|
||||
sessionString = new JSONObject().put("windows", new JSONArray().put(new JSONObject().put("tabs", tabs))).toString();
|
||||
windowObject.put("tabs", tabs);
|
||||
sessionString = new JSONObject().put("windows", new JSONArray().put(windowObject)).toString();
|
||||
} else {
|
||||
throw new SessionRestoreException("No tabs could be read from session file");
|
||||
}
|
||||
|
@ -1688,7 +1695,6 @@ public abstract class GeckoApp
|
|||
JSONObject restoreData = new JSONObject();
|
||||
restoreData.put("sessionString", sessionString);
|
||||
return restoreData.toString();
|
||||
|
||||
} catch (JSONException e) {
|
||||
throw new SessionRestoreException(e);
|
||||
}
|
||||
|
|
|
@ -52,6 +52,15 @@ public abstract class SessionParser {
|
|||
|
||||
abstract public void onTabRead(SessionTab tab);
|
||||
|
||||
/**
|
||||
* Placeholder method that must be overloaded to handle closedTabs while parsing session data.
|
||||
*
|
||||
* @param closedTabs, JSONArray of recently closed tab entries.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void onClosedTabsRead(final JSONArray closedTabs) throws JSONException{
|
||||
}
|
||||
|
||||
public void parse(String... sessionStrings) {
|
||||
final LinkedList<SessionTab> sessionTabs = new LinkedList<SessionTab>();
|
||||
int totalCount = 0;
|
||||
|
@ -61,6 +70,10 @@ public abstract class SessionParser {
|
|||
final JSONObject window = new JSONObject(sessionString).getJSONArray("windows").getJSONObject(0);
|
||||
final JSONArray tabs = window.getJSONArray("tabs");
|
||||
final int optSelected = window.optInt("selected", -1);
|
||||
final JSONArray closedTabs = window.optJSONArray("closedTabs");
|
||||
if (closedTabs != null) {
|
||||
onClosedTabsRead(closedTabs);
|
||||
}
|
||||
|
||||
for (int i = 0; i < tabs.length(); i++) {
|
||||
final JSONObject tab = tabs.getJSONObject(i);
|
||||
|
|
|
@ -140,7 +140,6 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
|
|||
private final Paint shadowPaint;
|
||||
private final int shadowSize;
|
||||
|
||||
private final LightweightTheme theme;
|
||||
private final ToolbarPrefs prefs;
|
||||
|
||||
public abstract boolean isAnimating();
|
||||
|
@ -179,7 +178,6 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
|
|||
setWillNotDraw(false);
|
||||
|
||||
isNewTablet = NewTabletUI.isEnabled(context);
|
||||
theme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme();
|
||||
|
||||
// BrowserToolbar is attached to BrowserApp only.
|
||||
activity = (BrowserApp) context;
|
||||
|
|
|
@ -63,7 +63,7 @@ abstract class NavButton extends ShapedButton {
|
|||
public void onLightweightThemeChanged() {
|
||||
final Drawable drawable;
|
||||
if (!NewTabletUI.isEnabled(getContext())) {
|
||||
drawable = mTheme.getDrawable(this);
|
||||
drawable = getTheme().getDrawable(this);
|
||||
} else {
|
||||
drawable = BrowserToolbar.getLightweightThemeDrawable(this, getResources(), getTheme(),
|
||||
R.color.background_normal);
|
||||
|
|
|
@ -23,14 +23,12 @@ import android.util.AttributeSet;
|
|||
|
||||
public class ShapedButton extends ThemedImageButton
|
||||
implements CanvasDelegate.DrawManager {
|
||||
protected final LightweightTheme mTheme;
|
||||
|
||||
protected final Path mPath;
|
||||
protected final CanvasDelegate mCanvasDelegate;
|
||||
|
||||
public ShapedButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme();
|
||||
|
||||
// Path is clipped.
|
||||
mPath = new Path();
|
||||
|
@ -61,7 +59,7 @@ public class ShapedButton extends ThemedImageButton
|
|||
@Override
|
||||
public void onLightweightThemeChanged() {
|
||||
final int background = getResources().getColor(R.color.background_tabs);
|
||||
final LightweightThemeDrawable lightWeight = mTheme.getColorDrawable(this, background);
|
||||
final LightweightThemeDrawable lightWeight = getTheme().getColorDrawable(this, background);
|
||||
|
||||
if (lightWeight == null)
|
||||
return;
|
||||
|
|
|
@ -434,6 +434,10 @@ SessionStore.prototype = {
|
|||
normalWin[prop] = data[prop];
|
||||
}
|
||||
normalWin.tabs = [];
|
||||
|
||||
// Save normal closed tabs. Forget about private closed tabs.
|
||||
normalWin.closedTabs = win.closedTabs.filter(tab => !tab.isPrivate);
|
||||
|
||||
normalData.windows.push(normalWin);
|
||||
privateData.windows.push({ tabs: [] });
|
||||
|
||||
|
@ -865,6 +869,11 @@ SessionStore.prototype = {
|
|||
|
||||
tab.browser.__SS_extdata = tabData.extData;
|
||||
}
|
||||
|
||||
// Restore the closed tabs array on the current window.
|
||||
if (state.windows[0].closedTabs) {
|
||||
this._windows[window.__SSID].closedTabs = state.windows[0].closedTabs;
|
||||
}
|
||||
},
|
||||
|
||||
getClosedTabCount: function ss_getClosedTabCount(aWindow) {
|
||||
|
@ -994,26 +1003,9 @@ SessionStore.prototype = {
|
|||
let notifyMessage = "";
|
||||
|
||||
try {
|
||||
// Normally, we'll receive the session string from Java, but there are
|
||||
// cases where we may want to restore that Java cannot detect (e.g., if
|
||||
// browser.sessionstore.resume_session_once is true). In these cases, the
|
||||
// session will be read from sessionstore.bak (which is also used for
|
||||
// "tabs from last time").
|
||||
let data = aSessionString;
|
||||
|
||||
if (data == null) {
|
||||
let bytes = yield OS.File.read(this._sessionFileBackup.path);
|
||||
data = JSON.parse(new TextDecoder().decode(bytes) || "");
|
||||
}
|
||||
|
||||
this._restoreWindow(data);
|
||||
this._restoreWindow(aSessionString);
|
||||
} catch (e) {
|
||||
if (e instanceof OS.File.Error) {
|
||||
Cu.reportError("SessionStore: " + e.message);
|
||||
} else {
|
||||
Cu.reportError("SessionStore: " + e);
|
||||
}
|
||||
|
||||
Cu.reportError("SessionStore: " + e);
|
||||
notifyMessage = "fail";
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
|||
const FONT_PREVIEW_TEXT = "Abc";
|
||||
const FONT_PREVIEW_FONT_SIZE = 40;
|
||||
const FONT_PREVIEW_FILLSTYLE = "black";
|
||||
const NORMAL_FONT_WEIGHT = 400;
|
||||
const BOLD_FONT_WEIGHT = 700;
|
||||
|
||||
// Predeclare the domnode actor type for use in requests.
|
||||
types.addActorType("domnode");
|
||||
|
@ -276,10 +278,26 @@ var PageStyleActor = protocol.ActorClass({
|
|||
fontFace.ruleText = font.rule.cssText;
|
||||
}
|
||||
|
||||
// Get the weight and style of this font for the preview and sort order
|
||||
let weight = NORMAL_FONT_WEIGHT, style = "";
|
||||
if (font.rule) {
|
||||
weight = font.rule.style.getPropertyValue("font-weight")
|
||||
|| NORMAL_FONT_WEIGHT;
|
||||
if (weight == "bold") {
|
||||
weight = BOLD_FONT_WEIGHT;
|
||||
} else if (weight == "normal") {
|
||||
weight = NORMAL_FONT_WEIGHT;
|
||||
}
|
||||
style = font.rule.style.getPropertyValue("font-style") || "";
|
||||
}
|
||||
fontFace.weight = weight;
|
||||
fontFace.style = style;
|
||||
|
||||
if (options.includePreviews) {
|
||||
let opts = {
|
||||
previewText: options.previewText,
|
||||
previewFontSize: options.previewFontSize,
|
||||
fontStyle: weight + " " + style,
|
||||
fillStyle: options.previewFillStyle
|
||||
}
|
||||
let { dataURL, size } = getFontPreviewData(font.CSSFamilyName,
|
||||
|
@ -293,6 +311,9 @@ var PageStyleActor = protocol.ActorClass({
|
|||
}
|
||||
|
||||
// @font-face fonts at the top, then alphabetically, then by weight
|
||||
fontsArray.sort(function(a, b) {
|
||||
return a.weight > b.weight ? 1 : -1;
|
||||
});
|
||||
fontsArray.sort(function(a, b) {
|
||||
if (a.CSSFamilyName == b.CSSFamilyName) {
|
||||
return 0;
|
||||
|
|
|
@ -844,6 +844,11 @@ this.AddonRepository = {
|
|||
if (idIndex == -1)
|
||||
continue;
|
||||
|
||||
// Ignore add-on if the add-on manager doesn't know about its type:
|
||||
if (!(result.addon.type in AddonManager.addonTypes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
results.push(result);
|
||||
// Ignore this add-on from now on
|
||||
ids.splice(idIndex, 1);
|
||||
|
@ -1089,10 +1094,8 @@ this.AddonRepository = {
|
|||
addon.type = "search";
|
||||
break;
|
||||
case 5:
|
||||
addon.type = "langpack";
|
||||
break;
|
||||
case 6:
|
||||
addon.type = "langpack-addon";
|
||||
addon.type = "locale";
|
||||
break;
|
||||
case 7:
|
||||
addon.type = "plugin";
|
||||
|
@ -1303,6 +1306,10 @@ this.AddonRepository = {
|
|||
if (requiredAttributes.some(function parseAddons_attributeFilter(aAttribute) !result.addon[aAttribute]))
|
||||
continue;
|
||||
|
||||
// Ignore add-on with a type AddonManager doesn't understand:
|
||||
if (!(result.addon.type in AddonManager.addonTypes))
|
||||
continue;
|
||||
|
||||
// Add only if the add-on is compatible with the platform
|
||||
if (!result.addon.isPlatformCompatible)
|
||||
continue;
|
||||
|
|
|
@ -31,6 +31,37 @@
|
|||
<compatible_os>ALL</compatible_os>
|
||||
<install size="1">http://example.com/addon1.xpi</install>
|
||||
</addon>
|
||||
<addon>
|
||||
<name>FAIL</name>
|
||||
<type id='9'>lightweight theme</type>
|
||||
<guid>addon12345@tests.mozilla.org</guid>
|
||||
<version>1.0</version>
|
||||
<authors>
|
||||
<author>
|
||||
<name>Test Creator</name>
|
||||
<link>http://example.com/creator.html</link>
|
||||
</author>
|
||||
</authors>
|
||||
<status id='4'>Public</status>
|
||||
<summary>Addon with uninstallable type shouldn't be visible in search</summary>
|
||||
<description>Test description</description>
|
||||
<compatible_applications>
|
||||
<application>
|
||||
<name>Firefox</name>
|
||||
<appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
|
||||
<min_version>0</min_version>
|
||||
<max_version>*</max_version>
|
||||
</application>
|
||||
<application>
|
||||
<name>SeaMonkey</name>
|
||||
<appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
|
||||
<min_version>0</min_version>
|
||||
<max_version>*</max_version>
|
||||
</application>
|
||||
</compatible_applications>
|
||||
<compatible_os>ALL</compatible_os>
|
||||
<install size="1">http://example.com/addon1.xpi</install>
|
||||
</addon>
|
||||
<addon>
|
||||
<name>FAIL</name>
|
||||
<type id='1'>Extension</type>
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
|
||||
<addon>
|
||||
<name>Repo Add-on 3</name>
|
||||
<type id="9999">Unknown</type>
|
||||
<type id="2">Theme</type>
|
||||
<guid>test_AddonRepository_3@tests.mozilla.org</guid>
|
||||
<version>2.3</version>
|
||||
<icon size="32">http://localhost/repo/3/icon.png</icon>
|
||||
|
|
|
@ -169,7 +169,7 @@
|
|||
-->
|
||||
<addon>
|
||||
<name>PASS</name>
|
||||
<type id="9999">Unknown</type>
|
||||
<type id="2">Theme</type>
|
||||
<guid>test_AddonRepository_1@tests.mozilla.org</guid>
|
||||
<version>1.4</version>
|
||||
<status id="9999">Unknown</status>
|
||||
|
|
|
@ -105,6 +105,7 @@ var GET_RESULTS = [{
|
|||
}]
|
||||
}, {
|
||||
id: "test_AddonRepository_1@tests.mozilla.org",
|
||||
type: "theme",
|
||||
version: "1.4",
|
||||
repositoryStatus: 9999,
|
||||
icons: {}
|
||||
|
|
|
@ -152,6 +152,7 @@ const REPOSITORY_ADDONS = [{
|
|||
repositoryStatus: 9
|
||||
}, {
|
||||
id: ADDON_IDS[2],
|
||||
type: "theme",
|
||||
name: "Repo Add-on 3",
|
||||
version: "2.3",
|
||||
iconURL: BASE_URL + "/repo/3/icon.png",
|
||||
|
|
Загрузка…
Ссылка в новой задаче