Bug 1022797 - Device settings support. r=jryans

This commit is contained in:
Jennifer Fong 2015-01-22 11:21:00 -05:00
Родитель a126821e99
Коммит c8eb489595
23 изменённых файлов: 1244 добавлений и 464 удалений

Просмотреть файл

@ -3,25 +3,24 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Cu = Components.utils;
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm");
const {AppManager} = require("devtools/webide/app-manager");
const {Connection} = require("devtools/client/connection-manager");
const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");
const ConfigView = require("devtools/webide/config-view");
let devicePrefsKeys = {};
let table;
let configView = new ConfigView(window);
window.addEventListener("load", function onLoad() {
window.removeEventListener("load", onLoad);
document.getElementById("close").onclick = CloseUI;
AppManager.on("app-manager-update", OnAppManagerUpdate);
document.getElementById("device-preferences").onchange = UpdatePref;
document.getElementById("device-preferences").onclick = CheckReset;
document.getElementById("search-bar").onkeyup = document.getElementById("search-bar").onclick = SearchPref;
document.getElementById("custom-value").onclick = UpdateNewPref;
document.getElementById("custom-value-type").onchange = ClearNewPrefs;
document.getElementById("add-custom-preference").onkeyup = CheckNewPrefSubmit;
document.getElementById("close").onclick = CloseUI;
document.getElementById("device-fields").onchange = UpdateField;
document.getElementById("device-fields").onclick = CheckReset;
document.getElementById("search-bar").onkeyup = document.getElementById("search-bar").onclick = SearchField;
document.getElementById("custom-value").onclick = UpdateNewField;
document.getElementById("custom-value-type").onchange = ClearNewFields;
document.getElementById("add-custom-field").onkeyup = CheckNewFieldSubmit;
BuildUI();
}, true);
@ -40,280 +39,49 @@ function OnAppManagerUpdate(event, what) {
}
}
function RenderByType(input, name, value, customType) {
value = customType || typeof value;
switch (value) {
case "boolean":
input.setAttribute("data-type", "boolean");
input.setAttribute("type", "checkbox");
break;
case "number":
input.setAttribute("data-type", "number");
input.setAttribute("type", "number");
break;
default:
input.setAttribute("data-type", "string");
input.setAttribute("type", "text");
break;
}
return input;
function CheckNewFieldSubmit(event) {
configView.checkNewFieldSubmit(event);
}
let defaultPref; // Used by tests
function ResetToDefault(name, input, button) {
AppManager.preferenceFront.clearUserPref(name);
let dataType = input.getAttribute("data-type");
let tr = document.getElementById("row-" + name);
switch (dataType) {
case "boolean":
defaultPref = AppManager.preferenceFront.getBoolPref(name);
defaultPref.then(boolean => {
input.checked = boolean;
}, () => {
input.checked = false;
tr.parentNode.removeChild(tr);
});
break;
case "number":
defaultPref = AppManager.preferenceFront.getIntPref(name);
defaultPref.then(number => {
input.value = number;
}, () => {
tr.parentNode.removeChild(tr);
});
break;
default:
defaultPref = AppManager.preferenceFront.getCharPref(name);
defaultPref.then(string => {
input.value = string;
}, () => {
tr.parentNode.removeChild(tr);
});
break;
}
button.classList.add("hide");
function UpdateNewField() {
configView.updateNewField();
}
function SaveByType(options) {
let prefName = options.id;
let inputType = options.type;
let value = options.value;
let input = document.getElementById(prefName);
switch (inputType) {
case "boolean":
AppManager.preferenceFront.setBoolPref(prefName, input.checked);
break;
case "number":
AppManager.preferenceFront.setIntPref(prefName, value);
break;
default:
AppManager.preferenceFront.setCharPref(prefName, value);
break;
}
}
function CheckNewPrefSubmit(event) {
if (event.keyCode === 13) {
document.getElementById("custom-value").click();
}
}
function ClearNewPrefs() {
let customTextEl = table.querySelector("#custom-value-text");
if (customTextEl.checked) {
customTextEl.checked = false;
} else {
customTextEl.value = "";
}
UpdateFieldType();
}
function UpdateFieldType() {
let customValueType = table.querySelector("#custom-value-type").value;
let customTextEl = table.querySelector("#custom-value-text");
let customText = customTextEl.value;
if (customValueType.length === 0) {
return false;
}
switch (customValueType) {
case "boolean":
customTextEl.type = "checkbox";
customText = customTextEl.checked;
break;
case "number":
customText = parseInt(customText, 10) || 0;
customTextEl.type = "number";
break;
default:
customTextEl.type = "text";
break;
}
return customValueType;
}
function UpdateNewPref(event) {
let customValueType = UpdateFieldType();
if (!customValueType) {
return;
}
let customRow = table.querySelector("tr:nth-of-type(2)");
let customTextEl = table.querySelector("#custom-value-text");
let customTextNameEl = table.querySelector("#custom-value-name");
if (customTextEl.validity.valid) {
let customText = customTextEl.value;
if (customValueType === "boolean") {
customText = customTextEl.checked;
}
let customTextName = customTextNameEl.value.replace(/[^A-Za-z0-9\.\-_]/gi, "");
GenerateField(customTextName, customText, true, customValueType, customRow);
SaveByType({
id: customTextName,
type: customValueType,
value: customText
});
customTextNameEl.value = "";
ClearNewPrefs();
}
function ClearNewFields() {
configView.clearNewFields();
}
function CheckReset(event) {
if (event.target.classList.contains("reset")) {
let btnId = event.target.getAttribute("data-id");
let input = document.getElementById(btnId);
ResetToDefault(btnId, input, event.target);
}
configView.checkReset(event);
}
function UpdatePref(event) {
if (event.target) {
let inputType = event.target.getAttribute("data-type");
let inputValue = event.target.checked || event.target.value;
if (event.target.nodeName == "input" &&
event.target.validity.valid &&
event.target.classList.contains("editable")) {
let id = event.target.id;
if (inputType == "boolean") {
if (event.target.checked) {
inputValue = true;
} else {
inputValue = false;
}
}
SaveByType({
id: id,
type: inputType,
value: inputValue
});
document.getElementById("btn-" + id).classList.remove("hide");
}
}
function UpdateField(event) {
configView.updateField(event);
}
function GenerateField(name, value, hasUserValue, customType, newPreferenceRow) {
if (name.length < 1) {
return;
}
let sResetDefault = Strings.GetStringFromName("devicepreferences_reset_default");
devicePrefsKeys[name] = true;
let input = document.createElement("input");
let tr = document.createElement("tr");
tr.setAttribute("id", "row-" + name);
tr.classList.add("edit-row");
let td = document.createElement("td");
td.classList.add("preference-name");
td.textContent = name;
tr.appendChild(td);
td = document.createElement("td");
input.classList.add("editable");
input.setAttribute("id", name);
input = RenderByType(input, name, value, customType);
if (customType === "boolean" || input.type === "checkbox") {
input.checked = value;
} else {
input.value = value;
}
td.appendChild(input);
tr.appendChild(td);
td = document.createElement("td");
td.setAttribute("id", "td-" + name);
let button = document.createElement("button");
button.setAttribute("data-id", name);
button.setAttribute("id", "btn-" + name);
button.classList.add("reset");
button.textContent = sResetDefault;
td.appendChild(button);
if (!hasUserValue) {
button.classList.add("hide");
}
tr.appendChild(td);
// If this is a new preference, add it to the top of the table.
if (newPreferenceRow) {
let existingPref = table.querySelector("#" + name);
if (!existingPref) {
table.insertBefore(tr, newPreferenceRow);
} else {
existingPref.value = value;
}
} else {
table.appendChild(tr);
}
}
function SearchPref(event) {
if (event.target.value.length) {
let stringMatch = new RegExp(event.target.value, "i");
for (let key in devicePrefsKeys) {
let row = document.getElementById("row-" + key);
if (key.match(stringMatch)) {
row.classList.remove("hide");
} else if (row) {
row.classList.add("hide");
}
}
} else {
var trs = document.getElementsByTagName("tr");
for (let i = 0; i < trs.length; i++) {
trs[i].classList.remove("hide");
}
}
function SearchField(event) {
configView.search(event);
}
let getAllPrefs; // Used by tests
function BuildUI() {
table = document.querySelector("table");
let trs = table.querySelectorAll("tr:not(#add-custom-preference)");
for (var i = 0; i < trs.length; i++) {
table.removeChild(trs[i]);
}
configView.resetTable();
if (AppManager.connection &&
AppManager.connection.status == Connection.Status.CONNECTED &&
AppManager.preferenceFront) {
configView.front = AppManager.preferenceFront;
configView.kind = "Pref";
configView.includeTypeName = true;
getAllPrefs = AppManager.preferenceFront.getAllPrefs();
getAllPrefs.then(json => {
let devicePrefs = Object.keys(json);
devicePrefs.sort();
for (let i = 0; i < devicePrefs.length; i++) {
GenerateField(devicePrefs[i], json[devicePrefs[i]].value, json[devicePrefs[i]].hasUserValue);
let deviceItems = Object.keys(json);
deviceItems.sort();
configView.keys = deviceItems;
for (let i = 0; i < configView.keys.length; i++) {
let key = configView.keys[i];
configView.generateField(key, json[key].value, json[key].hasUserValue);
}
});
} else {

Просмотреть файл

@ -13,7 +13,7 @@
<head>
<meta charset="utf8"/>
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
<link rel="stylesheet" href="chrome://webide/skin/devicepreferences.css" type="text/css"/>
<link rel="stylesheet" href="chrome://webide/skin/config-view.css" type="text/css"/>
<script type="application/javascript;version=1.8" src="chrome://webide/content/devicepreferences.js"></script>
</head>
<body>
@ -21,27 +21,27 @@
<div id="controls">
<a id="close">&deck_close;</a>
</div>
<h1>&devicepreferences_title;</h1>
<h1>&devicepreference_title;</h1>
<div id="search">
<input type="text" id="search-bar" placeholder="&devicepreferences_search;"/>
<input type="text" id="search-bar" placeholder="&devicepreference_search;"/>
</div>
</header>
<table id="device-preferences">
<tr id="add-custom-preference">
<table id="device-fields">
<tr id="add-custom-field">
<td>
<select id="custom-value-type">
<option value="" selected="selected">&devicepreferences_typenone;</option>
<option value="boolean">&devicepreferences_typeboolean;</option>
<option value="number">&devicepreferences_typenumber;</option>
<option value="string">&devicepreferences_typestring;</option>
<option value="" selected="selected">&device_typenone;</option>
<option value="boolean">&device_typeboolean;</option>
<option value="number">&device_typenumber;</option>
<option value="string">&device_typestring;</option>
</select>
<input type="text" id="custom-value-name" placeholder="&devicepreferences_newname;"/>
<input type="text" id="custom-value-name" placeholder="&devicepreference_newname;"/>
</td>
<td class="custom-input">
<input type="text" id="custom-value-text" placeholder="&devicepreferences_newtext;"/>
<input type="text" id="custom-value-text" placeholder="&devicepreference_newtext;"/>
</td>
<td>
<button id="custom-value" class="new-editable">&devicepreferences_addnew;</button>
<button id="custom-value" class="new-editable">&devicepreference_addnew;</button>
</td>
</tr>
</table>

Просмотреть файл

@ -0,0 +1,90 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Cu = Components.utils;
const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm");
const {AppManager} = require("devtools/webide/app-manager");
const {Connection} = require("devtools/client/connection-manager");
const ConfigView = require("devtools/webide/config-view");
let configView = new ConfigView(window);
window.addEventListener("load", function onLoad() {
window.removeEventListener("load", onLoad);
AppManager.on("app-manager-update", OnAppManagerUpdate);
document.getElementById("close").onclick = CloseUI;
document.getElementById("device-fields").onchange = UpdateField;
document.getElementById("device-fields").onclick = CheckReset;
document.getElementById("search-bar").onkeyup = document.getElementById("search-bar").onclick = SearchField;
document.getElementById("custom-value").onclick = UpdateNewField;
document.getElementById("custom-value-type").onchange = ClearNewFields;
document.getElementById("add-custom-field").onkeyup = CheckNewFieldSubmit;
BuildUI();
}, true);
window.addEventListener("unload", function onUnload() {
window.removeEventListener("unload", onUnload);
AppManager.off("app-manager-update", OnAppManagerUpdate);
});
function CloseUI() {
window.parent.UI.openProject();
}
function OnAppManagerUpdate(event, what) {
if (what == "connection" || what == "list-tabs-response") {
BuildUI();
}
}
function CheckNewFieldSubmit(event) {
configView.checkNewFieldSubmit(event);
}
function UpdateNewField() {
configView.updateNewField();
}
function ClearNewFields() {
configView.clearNewFields();
}
function CheckReset(event) {
configView.checkReset(event);
}
function UpdateField(event) {
configView.updateField(event);
}
function SearchField(event) {
configView.search(event);
}
let getAllSettings; // Used by tests
function BuildUI() {
configView.resetTable();
if (AppManager.connection &&
AppManager.connection.status == Connection.Status.CONNECTED &&
AppManager.settingsFront) {
configView.front = AppManager.settingsFront;
configView.kind = "Setting";
configView.includeTypeName = false;
getAllSettings = AppManager.settingsFront.getAllSettings()
getAllSettings.then(json => {
let deviceItems = Object.keys(json);
deviceItems.sort();
configView.keys = deviceItems;
for (let i = 0; i < configView.keys.length; i++) {
let key = configView.keys[i];
configView.generateField(key, json[key].value, json[key].hasUserValue);
}
});
} else {
CloseUI();
}
}

Просмотреть файл

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % webideDTD SYSTEM "chrome://browser/locale/devtools/webide.dtd" >
%webideDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf8"/>
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
<link rel="stylesheet" href="chrome://webide/skin/config-view.css" type="text/css"/>
<script type="application/javascript;version=1.8" src="chrome://webide/content/devicesettings.js"></script>
</head>
<body>
<header>
<div id="controls">
<a id="close">&deck_close;</a>
</div>
<h1>&devicesetting_title;</h1>
<div id="search">
<input type="text" id="search-bar" placeholder="&devicesetting_search;"/>
</div>
</header>
<table id="device-fields">
<tr id="add-custom-field">
<td>
<select id="custom-value-type">
<option value="" selected="selected">&device_typenone;</option>
<option value="boolean">&device_typeboolean;</option>
<option value="number">&device_typenumber;</option>
<option value="string">&device_typestring;</option>
<option value="object">&device_typeobject;</option>
</select>
<input type="text" id="custom-value-name" placeholder="&devicesetting_newname;"/>
</td>
<td class="custom-input">
<input type="text" id="custom-value-text" placeholder="&devicesetting_newtext;"/>
</td>
<td>
<button id="custom-value" class="new-editable">&devicesetting_addnew;</button>
</td>
</tr>
</table>
</body>
</html>

Просмотреть файл

@ -22,3 +22,5 @@ webide.jar:
content/monitor.js (monitor.js)
content/devicepreferences.js (devicepreferences.js)
content/devicepreferences.xhtml (devicepreferences.xhtml)
content/devicesettings.js (devicesettings.js)
content/devicesettings.xhtml (devicesettings.xhtml)

Просмотреть файл

@ -806,6 +806,7 @@ let UI = {
document.querySelector("#cmd_stop").setAttribute("disabled", "true");
document.querySelector("#cmd_toggleToolbox").setAttribute("disabled", "true");
document.querySelector("#cmd_showDevicePrefs").setAttribute("disabled", "true");
document.querySelector("#cmd_showSettings").setAttribute("disabled", "true");
return;
}
@ -870,6 +871,7 @@ let UI = {
let detailsCmd = document.querySelector("#cmd_showRuntimeDetails");
let disconnectCmd = document.querySelector("#cmd_disconnectRuntime");
let devicePrefsCmd = document.querySelector("#cmd_showDevicePrefs");
let settingsCmd = document.querySelector("#cmd_showSettings");
let box = document.querySelector("#runtime-actions");
@ -883,6 +885,9 @@ let UI = {
if (AppManager.preferenceFront) {
devicePrefsCmd.removeAttribute("disabled");
}
if (AppManager.settingsFront) {
settingsCmd.removeAttribute("disabled");
}
disconnectCmd.removeAttribute("disabled");
runtimePanelButton.setAttribute("active", "true");
} else {
@ -891,6 +896,7 @@ let UI = {
screenshotCmd.setAttribute("disabled", "true");
disconnectCmd.setAttribute("disabled", "true");
devicePrefsCmd.setAttribute("disabled", "true");
settingsCmd.setAttribute("disabled", "true");
runtimePanelButton.removeAttribute("active");
}
@ -1248,6 +1254,10 @@ let Cmds = {
UI.selectDeckPanel("devicepreferences");
},
showSettings: function() {
UI.selectDeckPanel("devicesettings");
},
showMonitor: function() {
UI.selectDeckPanel("monitor");
},

Просмотреть файл

@ -36,6 +36,7 @@
<command id="cmd_importPackagedApp" oncommand="Cmds.importPackagedApp()" label="&projectMenu_importPackagedApp_label;"/>
<command id="cmd_importHostedApp" oncommand="Cmds.importHostedApp()" label="&projectMenu_importHostedApp_label;"/>
<command id="cmd_showDevicePrefs" label="&runtimeMenu_showDevicePrefs_label;" oncommand="Cmds.showDevicePrefs()"/>
<command id="cmd_showSettings" label="&runtimeMenu_showSettings_label;" oncommand="Cmds.showSettings()"/>
<command id="cmd_removeProject" oncommand="Cmds.removeProject()" label="&projectMenu_remove_label;"/>
<command id="cmd_showProjectPanel" oncommand="Cmds.showProjectPanel()"/>
<command id="cmd_showRuntimePanel" oncommand="Cmds.showRuntimePanel()"/>
@ -83,6 +84,7 @@
<menuitem command="cmd_showPermissionsTable" accesskey="&runtimeMenu_showPermissionTable_accesskey;"/>
<menuitem command="cmd_showRuntimeDetails" accesskey="&runtimeMenu_showDetails_accesskey;"/>
<menuitem command="cmd_showDevicePrefs" accesskey="&runtimeMenu_showDevicePrefs_accesskey;"/>
<menuitem command="cmd_showSettings" accesskey="&runtimeMenu_showSettings_accesskey;"/>
<menuseparator/>
<menuitem command="cmd_disconnectRuntime" accesskey="&runtimeMenu_disconnect_accesskey;"/>
</menupopup>
@ -179,6 +181,7 @@
<toolbarbutton class="panel-item" id="runtime-details" command="cmd_showRuntimeDetails"/>
<toolbarbutton class="panel-item" id="runtime-permissions" command="cmd_showPermissionsTable"/>
<toolbarbutton class="panel-item" id="runtime-preferences" command="cmd_showDevicePrefs"/>
<toolbarbutton class="panel-item" id="runtime-preferences" command="cmd_showSettings"/>
<toolbarbutton class="panel-item" id="runtime-screenshot" command="cmd_takeScreenshot"/>
<toolbarbutton class="panel-item" id="runtime-disconnect" command="cmd_disconnectRuntime"/>
</vbox>
@ -197,6 +200,7 @@
<iframe id="deck-panel-runtimedetails" flex="1" src="runtimedetails.xhtml"/>
<iframe id="deck-panel-monitor" flex="1" lazysrc="monitor.xhtml"/>
<iframe id="deck-panel-devicepreferences" flex="1" src="devicepreferences.xhtml"/>
<iframe id="deck-panel-devicesettings" flex="1" src="devicesettings.xhtml"/>
</deck>
<splitter hidden="true" class="devtools-horizontal-splitter" orient="vertical"/>
<!-- toolbox iframe will be inserted here -->

Просмотреть файл

@ -18,6 +18,7 @@ const {ConnectionManager, Connection} = require("devtools/client/connection-mana
const {AppActorFront} = require("devtools/app-actor-front");
const {getDeviceFront} = require("devtools/server/actors/device");
const {getPreferenceFront} = require("devtools/server/actors/preference");
const {getSettingsFront} = require("devtools/server/actors/settings");
const {setTimeout} = require("sdk/timers");
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
const {RuntimeScanners, RuntimeTypes} = require("devtools/webide/runtimes");
@ -415,6 +416,13 @@ let AppManager = exports.AppManager = {
return getPreferenceFront(this.connection.client, this._listTabsResponse);
},
get settingsFront() {
if (!this._listTabsResponse) {
return null;
}
return getSettingsFront(this.connection.client, this._listTabsResponse);
},
disconnectRuntime: function() {
if (!this.connected) {
return promise.resolve();

Просмотреть файл

@ -0,0 +1,360 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const {Cu} = require("chrome");
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm");
const EventEmitter = require("devtools/toolkit/event-emitter");
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");
let ConfigView;
module.exports = ConfigView = function(window) {
EventEmitter.decorate(this);
this._doc = window.document;
this._keys = [];
return this;
};
ConfigView.prototype = {
_renderByType: function(input, name, value, customType) {
value = customType || typeof value;
switch (value) {
case "boolean":
input.setAttribute("data-type", "boolean");
input.setAttribute("type", "checkbox");
break;
case "number":
input.setAttribute("data-type", "number");
input.setAttribute("type", "number");
break;
case "object":
input.setAttribute("data-type", "object");
input.setAttribute("type", "text");
break;
default:
input.setAttribute("data-type", "string");
input.setAttribute("type", "text");
break;
}
return input;
},
set front(front) {
this._front = front;
},
set keys(keys) {
this._keys = keys;
},
get keys() {
return this._keys;
},
set kind(kind) {
this._kind = kind;
},
set includeTypeName(include) {
this._includeTypeName = include;
},
search: function(event) {
if (event.target.value.length) {
let stringMatch = new RegExp(event.target.value, "i");
for (let i = 0; i < this._keys.length; i++) {
let key = this._keys[i];
let row = this._doc.getElementById("row-" + key);
if (key.match(stringMatch)) {
row.classList.remove("hide");
} else if (row) {
row.classList.add("hide");
}
}
} else {
var trs = this._doc.getElementById("device-fields").querySelectorAll("tr");
for (let i = 0; i < trs.length; i++) {
trs[i].classList.remove("hide");
}
}
},
generateField: function(name, value, hasUserValue, customType, newRow) {
let table = this._doc.querySelector("table");
let sResetDefault = Strings.GetStringFromName("device_reset_default");
if (this._keys.indexOf(name) === -1) {
this._keys.push(name);
}
let input = this._doc.createElement("input");
let tr = this._doc.createElement("tr");
tr.setAttribute("id", "row-" + name);
tr.classList.add("edit-row");
let td = this._doc.createElement("td");
td.classList.add("field-name");
td.textContent = name;
tr.appendChild(td);
td = this._doc.createElement("td");
input.classList.add("editable");
input.setAttribute("id", name);
input = this._renderByType(input, name, value, customType);
if (customType === "boolean" || input.type === "checkbox") {
input.checked = value;
} else {
if (typeof value === "object") {
value = JSON.stringify(value);
}
input.value = value;
}
td.appendChild(input);
tr.appendChild(td);
td = this._doc.createElement("td");
td.setAttribute("id", "td-" + name);
let button = this._doc.createElement("button");
button.setAttribute("data-id", name);
button.setAttribute("id", "btn-" + name);
button.classList.add("reset");
button.textContent = sResetDefault;
td.appendChild(button);
if (!hasUserValue) {
button.classList.add("hide");
}
tr.appendChild(td);
// If this is a new field, add it to the top of the table.
if (newRow) {
let existing = table.querySelector("#" + name);
if (!existing) {
table.insertBefore(tr, newRow);
} else {
existing.value = value;
}
} else {
table.appendChild(tr);
}
},
resetTable: function() {
let table = this._doc.querySelector("table");
let trs = table.querySelectorAll("tr:not(#add-custom-field)");
for (var i = 0; i < trs.length; i++) {
table.removeChild(trs[i]);
}
return table;
},
_getCallType: function(type, name) {
let frontName = "get";
if (this._includeTypeName) {
frontName += type;
}
return this._front[frontName + this._kind](name);
},
_setCallType: function(type, name, value) {
let frontName = "set";
if (this._includeTypeName) {
frontName += type;
}
return this._front[frontName + this._kind](name, value);
},
_saveByType: function(options) {
let fieldName = options.id;
let inputType = options.type;
let value = options.value;
let input = this._doc.getElementById(fieldName);
switch(inputType) {
case "boolean":
this._setCallType("Bool", fieldName, input.checked);
break;
case "number":
this._setCallType("Int", fieldName, value);
break;
case "object":
try {
value = JSON.parse(value);
} catch(e) {}
this._setCallType("Object", fieldName, value);
break;
default:
this._setCallType("Char", fieldName, value);
break;
}
},
updateField: function(event) {
if (event.target) {
let inputType = event.target.getAttribute("data-type");
let inputValue = event.target.checked || event.target.value;
if (event.target.nodeName == "input" &&
event.target.validity.valid &&
event.target.classList.contains("editable")) {
let id = event.target.id;
if (inputType === "boolean") {
if (event.target.checked) {
inputValue = true;
} else {
inputValue = false;
}
}
this._saveByType({
id: id,
type: inputType,
value: inputValue
});
this._doc.getElementById("btn-" + id).classList.remove("hide");
}
}
},
_resetToDefault: function(name, input, button) {
this._front["clearUser" + this._kind](name);
let dataType = input.getAttribute("data-type");
let tr = this._doc.getElementById("row-" + name);
switch (dataType) {
case "boolean":
this._defaultField = this._getCallType("Bool", name);
this._defaultField.then(boolean => {
input.checked = boolean;
}, () => {
input.checked = false;
tr.parentNode.removeChild(tr);
});
break;
case "number":
this._defaultField = this._getCallType("Int", name);
this._defaultField.then(number => {
input.value = number;
}, () => {
tr.parentNode.removeChild(tr);
});
break;
case "object":
this._defaultField = this._getCallType("Object", name);
this._defaultField.then(object => {
input.value = JSON.stringify(object);
}, () => {
tr.parentNode.removeChild(tr);
});
break;
default:
this._defaultField = this._getCallType("Char", name);
this._defaultField.then(string => {
input.value = string;
}, () => {
tr.parentNode.removeChild(tr);
});
break;
}
button.classList.add("hide");
},
checkReset: function(event) {
if (event.target.classList.contains("reset")) {
let btnId = event.target.getAttribute("data-id");
let input = this._doc.getElementById(btnId);
this._resetToDefault(btnId, input, event.target);
}
},
updateFieldType: function() {
let table = this._doc.querySelector("table");
let customValueType = table.querySelector("#custom-value-type").value;
let customTextEl = table.querySelector("#custom-value-text");
let customText = customTextEl.value;
if (customValueType.length === 0) {
return false;
}
switch (customValueType) {
case "boolean":
customTextEl.type = "checkbox";
customText = customTextEl.checked;
break;
case "number":
customText = parseInt(customText, 10) || 0;
customTextEl.type = "number";
break;
default:
customTextEl.type = "text";
break;
}
return customValueType;
},
clearNewFields: function() {
let table = this._doc.querySelector("table");
let customTextEl = table.querySelector("#custom-value-text");
if (customTextEl.checked) {
customTextEl.checked = false;
} else {
customTextEl.value = "";
}
this.updateFieldType();
},
updateNewField: function() {
let table = this._doc.querySelector("table");
let customValueType = this.updateFieldType();
if (!customValueType) {
return;
}
let customRow = table.querySelector("tr:nth-of-type(2)");
let customTextEl = table.querySelector("#custom-value-text");
let customTextNameEl = table.querySelector("#custom-value-name");
if (customTextEl.validity.valid) {
let customText = customTextEl.value;
if (customValueType === "boolean") {
customText = customTextEl.checked;
}
let customTextName = customTextNameEl.value.replace(/[^A-Za-z0-9\.\-_]/gi, "");
this.generateField(customTextName, customText, true, customValueType, customRow);
this._saveByType({
id: customTextName,
type: customValueType,
value: customText
});
customTextNameEl.value = "";
this.clearNewFields();
}
},
checkNewFieldSubmit: function(event) {
if (event.keyCode === 13) {
this._doc.getElementById("custom-value").click();
}
}
};

Просмотреть файл

@ -17,8 +17,9 @@ EXTRA_JS_MODULES.devtools.webide += [
'modules/addons.js',
'modules/app-manager.js',
'modules/build.js',
'modules/config-view.js',
'modules/remote-resources.js',
'modules/runtimes.js',
'modules/tab-store.js',
'modules/utils.js',
'modules/utils.js'
]

Просмотреть файл

@ -32,6 +32,7 @@ support-files =
build_app_windows2/manifest.webapp
build_app_windows2/package.json
build_app_windows2/stage/empty-directory
device_front_shared.js
head.js
hosted_app.manifest
templates.json
@ -47,6 +48,7 @@ support-files =
[test_autoconnect_runtime.html]
[test_telemetry.html]
[test_device_preferences.html]
[test_device_settings.html]
[test_fullscreenToolbox.html]
[test_zoom.html]
[test_build.html]

Просмотреть файл

@ -0,0 +1,219 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let customName;
let customValue;
let customValueType;
let customBtn;
let newField;
let change;
let doc;
let iframe;
let resetBtn;
let found = false;
function setDocument(frame) {
iframe = frame;
doc = iframe.contentWindow.document;
}
function fieldChange(fields, id) {
// Trigger existing field change
for (let field of fields) {
if (field.id == id) {
let button = doc.getElementById("btn-" + id);
found = true;
ok(button.classList.contains("hide"), "Default field detected");
field.value = "custom";
field.click();
ok(!button.classList.contains("hide"), "Custom field detected");
break;
}
}
ok(found, "Found " + id + " line");
}
function addNewField() {
found = false;
customName = doc.querySelector("#custom-value-name");
customValue = doc.querySelector("#custom-value-text");
customValueType = doc.querySelector("#custom-value-type");
customBtn = doc.querySelector("#custom-value");
change = doc.createEvent("HTMLEvents");
change.initEvent("change", false, true);
// Add a new custom string
customValueType.value = "string";
customValueType.dispatchEvent(change);
customName.value = "new-string-field!";
customValue.value = "test";
customBtn.click();
let newField = doc.querySelector("#new-string-field");
if (newField) {
found = true;
is(newField.type, "text", "Custom type is a string");
is(newField.value, "test", "Custom string new value is correct");
}
ok(found, "Found new string field line");
is(customName.value, "", "Custom string name reset");
is(customValue.value, "", "Custom string value reset");
}
function addNewFieldWithEnter() {
// Add a new custom value with the <enter> key
found = false;
customName.value = "new-string-field-two";
customValue.value = "test";
let newAddField = doc.querySelector("#add-custom-field");
let enter = doc.createEvent("KeyboardEvent");
enter.initKeyEvent(
"keyup", true, true, null, false, false, false, false, 13, 0);
newAddField.dispatchEvent(enter);
newField = doc.querySelector("#new-string-field-two");
if (newField) {
found = true;
is(newField.type, "text", "Custom type is a string");
is(newField.value, "test", "Custom string new value is correct");
}
ok(found, "Found new string field line");
is(customName.value, "", "Custom string name reset");
is(customValue.value, "", "Custom string value reset");
}
function editExistingField() {
// Edit existing custom string preference
newField.value = "test2";
newField.click();
is(newField.value, "test2", "Custom string existing value is correct");
}
function addNewFieldInteger() {
// Add a new custom integer preference with a valid integer
customValueType.value = "number";
customValueType.dispatchEvent(change);
customName.value = "new-integer-field";
customValue.value = 1;
found = false;
customBtn.click();
newField = doc.querySelector("#new-integer-field");
if (newField) {
found = true;
is(newField.type, "number", "Custom type is a number");
is(newField.value, 1, "Custom integer value is correct");
}
ok(found, "Found new integer field line");
is(customName.value, "", "Custom integer name reset");
is(customValue.value, 0, "Custom integer value reset");
}
let editFieldInteger = Task.async(function*() {
// Edit existing custom integer preference
newField.value = 3;
newField.click();
is(newField.value, 3, "Custom integer existing value is correct");
// Reset a custom field
let resetBtn = doc.querySelector("#btn-new-integer-field");
resetBtn.click();
try {
yield iframe.contentWindow.configView._defaultField;
} catch(err) {
let fieldRow = doc.querySelector("#row-new-integer-field");
if (!fieldRow) {
found = false;
}
ok(!found, "Custom field removed");
}
});
let resetExistingField = Task.async(function*(id) {
let existing = doc.getElementById(id);
existing.click();
is(existing.checked, false, "Existing boolean value is correct");
resetBtn = doc.getElementById("btn-" + id);
resetBtn.click();
yield iframe.contentWindow.configView._defaultField;
ok(resetBtn.classList.contains("hide"), true, "Reset button hidden");
is(existing.checked, true, "Existing field reset");
});
let resetNewField = Task.async(function*(id) {
let custom = doc.getElementById(id);
custom.click();
is(custom.value, "test", "New string value is correct");
resetBtn = doc.getElementById("btn-" + id);
resetBtn.click();
yield iframe.contentWindow.configView._defaultField;
ok(resetBtn.classList.contains("hide"), true, "Reset button hidden");
});
function addNewFieldBoolean() {
customValueType.value = "boolean";
customValueType.dispatchEvent(change);
customName.value = "new-boolean-field";
customValue.checked = true;
found = false;
customBtn.click();
newField = doc.querySelector("#new-boolean-field");
if (newField) {
found = true;
is(newField.type, "checkbox", "Custom type is a checkbox");
is(newField.checked, true, "Custom boolean value is correctly true");
}
ok(found, "Found new boolean field line");
// Mouse event trigger
var mouseClick = new MouseEvent("click", {
canBubble: true,
cancelable: true,
view: doc.parent,
});
found = false;
customValueType.value = "boolean";
customValueType.dispatchEvent(change);
customName.value = "new-boolean-field2";
customValue.dispatchEvent(mouseClick);
customBtn.dispatchEvent(mouseClick);
newField = doc.querySelector("#new-boolean-field2");
if (newField) {
found = true;
is(newField.checked, true, "Custom boolean value is correctly false");
}
ok(found, "Found new second boolean field line");
is(customName.value, "", "Custom boolean name reset");
is(customValue.checked, false, "Custom boolean value reset");
newField.click();
is(newField.checked, false, "Custom boolean existing value is correct");
}
function searchFields(deck, keyword) {
// Search for a non-existent field
let searchField = doc.querySelector("#search-bar");
searchField.value = "![o_O]!";
searchField.click();
let fieldsTotal = doc.querySelectorAll("tr.edit-row").length;
let hiddenFields = doc.querySelectorAll("tr.hide");
is(hiddenFields.length, fieldsTotal, "Search keyword not found");
// Search for existing fields
searchField.value = keyword;
searchField.click();
hiddenFields = doc.querySelectorAll("tr.hide");
isnot(hiddenFields.length, fieldsTotal, "Search keyword found");
doc.querySelector("#close").click();
ok(!deck.selectedPanel, "No panel selected");
}

Просмотреть файл

@ -8,6 +8,7 @@
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
<script type="application/javascript;version=1.8" src="head.js"></script>
<script type="application/javascript;version=1.8" src="device_front_shared.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
@ -49,190 +50,34 @@
yield prefIframe.contentWindow.getAllPrefs;
yield nextTick();
let doc = prefIframe.contentWindow.document;
setDocument(prefIframe);
let fields = doc.querySelectorAll(".editable");
let preference = "accessibility.blockautorefresh";
let found = false;
// Trigger existing field change
for (let field of fields) {
if (field.id == preference) {
let button = doc.getElementById("btn-" + preference);
found = true;
ok(button.classList.contains("hide"), "Default field detected");
field.value = "custom";
field.click();
ok(!button.classList.contains("hide"), "Custom field detected");
break;
}
}
ok(found, "Found accessibility preference line");
fieldChange(fields, preference);
// Add new preferences
found = false;
let customName = doc.querySelector("#custom-value-name");
let customValue = doc.querySelector("#custom-value-text");
let customValueType = doc.querySelector("#custom-value-type");
let customBtn = doc.querySelector("#custom-value");
let change = doc.createEvent("HTMLEvents");
change.initEvent("change", false, true);
addNewField();
// Add a new custom string preference
customValueType.value = "string";
customValueType.dispatchEvent(change);
customName.value = "new-string-pref!";
customValue.value = "test";
customBtn.click();
let newPref = doc.querySelector("#new-string-pref");
if (newPref) {
found = true;
is(newPref.type, "text", "Custom type is a string");
is(newPref.value, "test", "Custom string new value is correct");
}
ok(found, "Found new string preference line");
is(customName.value, "", "Custom string name reset");
is(customValue.value, "", "Custom string value reset");
addNewFieldWithEnter();
// Add a new custom value with the <enter> key
found = false;
customName.value = "new-string-pref-two";
customValue.value = "test";
let newField = doc.querySelector("#add-custom-preference");
let enter = doc.createEvent("KeyboardEvent");
enter.initKeyEvent(
"keyup", true, true, null, false, false, false, false, 13, 0);
newField.dispatchEvent(enter);
newPref = doc.querySelector("#new-string-pref-two");
if (newPref) {
found = true;
is(newPref.type, "text", "Custom type is a string");
is(newPref.value, "test", "Custom string new value is correct");
}
ok(found, "Found new string preference line");
is(customName.value, "", "Custom string name reset");
is(customValue.value, "", "Custom string value reset");
editExistingField();
// Edit existing custom string preference
newPref.value = "test2";
newPref.click();
is(newPref.value, "test2", "Custom string existing value is correct");
addNewFieldInteger();
// Add a new custom integer preference with a valid integer
customValueType.value = "number";
customValueType.dispatchEvent(change);
customName.value = "new-integer-pref";
customValue.value = 1;
found = false;
yield editFieldInteger();
customBtn.click();
newPref = doc.querySelector("#new-integer-pref");
if (newPref) {
found = true;
is(newPref.type, "number", "Custom type is a number");
is(newPref.value, 1, "Custom integer value is correct");
}
ok(found, "Found new integer preference line");
is(customName.value, "", "Custom integer name reset");
is(customValue.value, 0, "Custom integer value reset");
yield resetExistingField("accessibility.accesskeycausesactivation");
// Edit existing custom integer preference
newPref.value = 3;
newPref.click();
is(newPref.value, 3, "Custom integer existing value is correct");
addNewFieldBoolean();
// Reset a custom preference
let resetBtn = doc.querySelector("#btn-new-integer-pref");
resetBtn.click();
try {
yield prefIframe.contentWindow.defaultPref;
} catch(err) {
let prefRow = doc.querySelector("#row-new-integer-pref");
if (!prefRow) {
found = false;
}
ok(!found, "Custom preference removed");
}
// Reset an existing preference
let existingPref = doc.getElementById("accessibility.accesskeycausesactivation");
existingPref.click();
is(existingPref.checked, false, "Existing boolean value is correct");
resetBtn = doc.getElementById("btn-accessibility.accesskeycausesactivation");
resetBtn.click();
yield prefIframe.contentWindow.defaultPref;
ok(resetBtn.classList.contains("hide"), true, "Reset button hidden");
is(existingPref.checked, true, "Existing preference reset");
// Add a new custom boolean preference
customValueType.value = "boolean";
customValueType.dispatchEvent(change);
customName.value = "new-boolean-pref";
customValue.checked = true;
found = false;
customBtn.click();
newPref = doc.querySelector("#new-boolean-pref");
if (newPref) {
found = true;
is(newPref.type, "checkbox", "Custom type is a checkbox");
is(newPref.checked, true, "Custom boolean value is correctly true");
}
ok(found, "Found new boolean preference line");
// Mouse event trigger
var mouseClick = new MouseEvent("click", {
canBubble: true,
cancelable: true,
view: doc.parent,
});
found = false;
customValueType.value = "boolean";
customValueType.dispatchEvent(change);
customName.value = "new-boolean-pref2";
customValue.dispatchEvent(mouseClick);
customBtn.dispatchEvent(mouseClick);
newPref = doc.querySelector("#new-boolean-pref2");
if (newPref) {
found = true;
is(newPref.checked, true, "Custom boolean value is correctly false");
}
ok(found, "Found new second boolean preference line");
is(customName.value, "", "Custom boolean name reset");
is(customValue.checked, false, "Custom boolean value reset");
// Edit existing custom boolean preference
newPref.click();
is(newPref.checked, false, "Custom boolean existing value is correct");
// Search for a non-existent field
let searchField = doc.querySelector("#search-bar");
searchField.value = "![o_O]!";
searchField.click();
let preferencesTotal = doc.querySelectorAll("tr.edit-row").length;
let hiddenPreferences = doc.querySelectorAll("tr.hide");
is(hiddenPreferences.length, preferencesTotal, "Search keyword not found");
// Search for existing fields
searchField.value = "debugger";
searchField.click();
hiddenPreferences = doc.querySelectorAll("tr.hide");
isnot(hiddenPreferences.length, preferencesTotal, "Search keyword found");
doc.querySelector("#close").click();
ok(!deck.selectedPanel, "No panel selected");
searchFields(deck, "debugger");
DebuggerServer.destroy();
yield closeWebIDE(win);
SimpleTest.finish();
}).then(null, e => {
ok(false, "Exception: " + e);
SimpleTest.finish();

Просмотреть файл

@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
<title></title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
<script type="application/javascript;version=1.8" src="head.js"></script>
<script type="application/javascript;version=1.8" src="device_front_shared.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<script type="application/javascript;version=1.8">
window.onload = function() {
SimpleTest.waitForExplicitFinish();
Task.spawn(function*() {
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
if (SpecialPowers.isMainProcess()) {
Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
}
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
let win = yield openWebIDE();
let settingIframe = win.document.querySelector("#deck-panel-devicesettings");
yield documentIsLoaded(settingIframe.contentWindow.document);
win.AppManager.update("runtimelist");
yield connectToLocalRuntime(win);
yield nextTick();
let settings = win.document.querySelector("#cmd_showSettings");
ok(!settings.hasAttribute("disabled"), "device settings cmd enabled");
let deck = win.document.querySelector("#deck");
win.Cmds.showSettings();
is(deck.selectedPanel, settingIframe, "device settings iframe selected");
yield settingIframe.contentWindow.getAllSettings;
yield nextTick();
setDocument(settingIframe);
let fields = doc.querySelectorAll(".editable");
addNewField();
addNewFieldWithEnter();
editExistingField();
addNewFieldInteger();
yield editFieldInteger();
yield resetNewField("new-string-field");
addNewFieldBoolean();
searchFields(deck, "new-boolean-field2");
DebuggerServer.destroy();
yield closeWebIDE(win);
SimpleTest.finish();
}).then(null, e => {
ok(false, "Exception: " + e);
SimpleTest.finish();
});
}
</script>
</body>
</html>

Просмотреть файл

@ -14,7 +14,7 @@ html, body {
display: none;
}
#device-preferences {
#device-fields {
font-family: sans-serif;
padding-left: 6px;
width: 100%;
@ -37,31 +37,34 @@ header {
padding: 10px 20px;
}
#device-preferences td {
#device-fields td {
background-color: #f1f1f1;
border-bottom: 1px solid #ccc;
border-right: 1px solid #fff;
width: 33.3%;
}
#device-preferences td.preference-name {
#device-fields td:first-child {
min-width: 400px;
}
#device-fields td.preference-name, #device-fields td.setting-name {
width: 50%;
min-width: 400px;
word-break: break-all;
}
#device-preferences button {
#device-fields button {
display: inline-block;
font-family: sans-serif;
font-size: 0.7rem;
white-space: nowrap;
}
#device-preferences tr.hide, #device-preferences button.hide {
#device-fields tr.hide, #device-fields button.hide {
display: none;
}
#device-preferences .custom-input {
#device-fields .custom-input {
width: 300px;
}

Просмотреть файл

@ -15,4 +15,4 @@ webide.jar:
skin/runtimedetails.css (runtimedetails.css)
skin/permissionstable.css (permissionstable.css)
skin/monitor.css (monitor.css)
skin/devicepreferences.css (devicepreferences.css)
skin/config-view.css (config-view.css)

Просмотреть файл

@ -41,6 +41,8 @@
<!ENTITY runtimeMenu_showMonitor_accesskey "M">
<!ENTITY runtimeMenu_showDevicePrefs_label "Device Preferences">
<!ENTITY runtimeMenu_showDevicePrefs_accesskey "D">
<!ENTITY runtimeMenu_showSettings_label "Device Settings">
<!ENTITY runtimeMenu_showSettings_accesskey "s">
<!ENTITY viewMenu_label "View">
<!ENTITY viewMenu_accesskey "V">
@ -147,16 +149,26 @@
<!ENTITY runtimedetails_requestPrivileges "request higher privileges">
<!ENTITY runtimedetails_privilegesWarning "(Will reboot device. Requires root access.)">
<!-- Device Preferences and Settings -->
<!ENTITY device_typeboolean "Boolean">
<!ENTITY device_typenumber "Integer">
<!ENTITY device_typestring "String">
<!ENTITY device_typeobject "Object">
<!ENTITY device_typenone "Select a type">
<!-- Device Preferences -->
<!ENTITY devicepreferences_title "Device Preferences">
<!ENTITY devicepreferences_search "Search preferences">
<!ENTITY devicepreferences_newname "New preference name">
<!ENTITY devicepreferences_newtext "Preference value">
<!ENTITY devicepreferences_addnew "Add new preference">
<!ENTITY devicepreferences_typeboolean "Boolean">
<!ENTITY devicepreferences_typenumber "Integer">
<!ENTITY devicepreferences_typestring "String">
<!ENTITY devicepreferences_typenone "Select a type">
<!ENTITY devicepreference_title "Device Preferences">
<!ENTITY devicepreference_search "Search preferences">
<!ENTITY devicepreference_newname "New preference name">
<!ENTITY devicepreference_newtext "Preference value">
<!ENTITY devicepreference_addnew "Add new preference">
<!-- Device Settings -->
<!ENTITY devicesetting_title "Device Settings">
<!ENTITY devicesetting_search "Search settings">
<!ENTITY devicesetting_newname "New setting name">
<!ENTITY devicesetting_newtext "Setting value">
<!ENTITY devicesetting_addnew "Add new setting">
<!-- Monitor -->
<!ENTITY monitor_title "Monitor">

Просмотреть файл

@ -75,5 +75,5 @@ status_warning=WARNINGS
status_error=ERRORS
status_unknown=UNKNOWN
# Preferences
devicepreferences_reset_default=Reset to default
# Device preferences and settings
device_reset_default=Reset to default

Просмотреть файл

@ -0,0 +1,179 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const {Cc, Ci, Cu, CC} = require("chrome");
const protocol = require("devtools/server/protocol");
const {Arg, method, RetVal} = protocol;
const {DebuggerServer} = require("devtools/server/main");
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let defaultSettings = {};
let settingsFile;
exports.register = function(handle) {
handle.addGlobalActor(SettingsActor, "settingsActor");
};
exports.unregister = function(handle) {
};
function getDefaultSettings() {
let chan = NetUtil.newChannel(settingsFile);
let stream = chan.open();
// Obtain a converter to read from a UTF-8 encoded input stream.
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
let rawstr = converter.ConvertToUnicode(NetUtil.readInputStreamToString(
stream,
stream.available()) || "");
try {
defaultSettings = JSON.parse(rawstr);
} catch(e) { }
stream.close();
}
function loadSettingsFile() {
// Loading resource://app/defaults/settings.json doesn't work because
// settings.json is not in the omnijar.
// So we look for the app dir instead and go from here...
if (settingsFile) {
return;
}
settingsFile = FileUtils.getFile("DefRt", ["settings.json"], false);
if (!settingsFile || (settingsFile && !settingsFile.exists())) {
// On b2g desktop builds the settings.json file is moved in the
// profile directory by the build system.
settingsFile = FileUtils.getFile("ProfD", ["settings.json"], false);
if (!settingsFile || (settingsFile && !settingsFile.exists())) {
console.log("settings.json file does not exist");
}
}
if (settingsFile.exists()) {
getDefaultSettings();
}
}
let SettingsActor = exports.SettingsActor = protocol.ActorClass({
typeName: "settings",
_getSettingsService: function() {
let win = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
return win.navigator.mozSettings;
},
getSetting: method(function(name) {
let deferred = promise.defer();
let lock = this._getSettingsService().createLock();
let req = lock.get(name);
req.onsuccess = function() {
deferred.resolve(req.result[name]);
};
req.onerror = function() {
deferred.reject(req.error);
};
return deferred.promise;
}, {
request: { value: Arg(0) },
response: { value: RetVal("json") }
}),
setSetting: method(function(name, value) {
let deferred = promise.defer();
let data = {};
data[name] = value;
let lock = this._getSettingsService().createLock();
let req = lock.set(data);
req.onsuccess = function() {
deferred.resolve(true);
};
req.onerror = function() {
deferred.reject(req.error);
};
return deferred.promise;
}, {
request: { name: Arg(0), value: Arg(1) },
response: {}
}),
_hasUserSetting: function(name, value) {
if (typeof value === "object") {
return JSON.stringify(defaultSettings[name]) !== JSON.stringify(value);
}
return (defaultSettings[name] !== value);
},
getAllSettings: method(function() {
loadSettingsFile();
let settings = {};
let self = this;
let deferred = promise.defer();
let lock = this._getSettingsService().createLock();
let req = lock.get("*");
req.onsuccess = function() {
for (var name in req.result) {
settings[name] = {
value: req.result[name],
hasUserValue: self._hasUserSetting(name, req.result[name])
};
}
deferred.resolve(settings);
};
req.onfailure = function() {
deferred.reject(req.error);
};
return deferred.promise;
}, {
request: {},
response: { value: RetVal("json") }
}),
clearUserSetting: method(function(name) {
loadSettingsFile();
try {
this.setSetting(name, defaultSettings[name]);
} catch (e) {
console.log(e);
}
}, {
request: { name: Arg(0) },
response: {}
})
});
let SettingsFront = protocol.FrontClass(SettingsActor, {
initialize: function(client, form) {
protocol.Front.prototype.initialize.call(this, client);
this.actorID = form.settingsActor;
this.manage(this);
},
});
const _knownSettingsFronts = new WeakMap();
exports.getSettingsFront = function(client, form) {
if (!form.settingsActor) {
return null;
}
if (_knownSettingsFronts.has(client)) {
return _knownSettingsFronts.get(client);
}
let front = new SettingsFront(client, form);
_knownSettingsFronts.set(client, front);
return front;
};
// For tests
exports._setDefaultSettings = function(settings) {
defaultSettings = settings || {};
};

Просмотреть файл

@ -376,7 +376,14 @@ var DebuggerServer = {
type: { global: true }
});
}
let win = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
if (win && win.navigator.mozSettings) {
this.registerModule("devtools/server/actors/settings", {
prefix: "settings",
constructor: "SettingsActor",
type: { global: true }
});
}
this.registerModule("devtools/server/actors/webapps", {
prefix: "webapps",
constructor: "WebappsActor",

Просмотреть файл

@ -55,6 +55,7 @@ EXTRA_JS_MODULES.devtools.server.actors += [
'actors/profiler.js',
'actors/root.js',
'actors/script.js',
'actors/settings.js',
'actors/storage.js',
'actors/string.js',
'actors/styleeditor.js',

Просмотреть файл

@ -71,6 +71,7 @@ skip-if = buildapp == 'mulet'
[test_memory_census.html]
[test_memory_gc_01.html]
[test_preference.html]
[test_settings.html]
[test_connectToChild.html]
skip-if = buildapp == 'mulet'
[test_attachProcess.html]

Просмотреть файл

@ -0,0 +1,129 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 1022797 - Settings support from WebIDE
-->
<head>
<meta charset="utf-8">
<title>Test Settings Actor</title>
<script type="text/javascript" src="chrome://mochikit/content/MochiKit/MochiKit.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<pre id="test">
<script>
function runTests() {
var Cu = Components.utils;
var Cc = Components.classes;
var Ci = Components.interfaces;
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
if (SpecialPowers.isMainProcess()) {
Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
}
SimpleTest.waitForExplicitFinish();
var {getSettingsFront, _setDefaultSettings} = devtools.require("devtools/server/actors/settings");
DebuggerServer.init(function () { return true; });
DebuggerServer.addBrowserActors();
var client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(function onConnect() {
client.listTabs(function onListTabs(aResponse) {
var s = getSettingsFront(client, aResponse);
var settings = {};
var resetSettings = {};
var fakeSettings = {
"wifi.enabled": true,
"audio.volume.alarm": 15,
"app.reportCrashes": "ask",
"app.someObject": { active: true }
};
var localSetting = {
"wifi.enabled": false,
"audio.volume.alarm": 0,
"app.reportCrashes": "none",
"app.someObject": {}
};
function checkValues() {
is(settings.allSettings["wifi.enabled"].hasUserValue, false, "original unchanged bool setting");
is(settings.allSettings["audio.volume.alarm"].hasUserValue, false, "original unchanged int setting");
is(settings.allSettings["app.reportCrashes"].hasUserValue, false, "original unchanged string setting");
is(settings.allSettings["app.someObject"].hasUserValue, false, "original unchanged object setting");
is(settings.allSettings["wifi.enabled"].value, fakeSettings["wifi.enabled"], "original read/write bool setting");
is(settings.allSettings["audio.volume.alarm"].value, fakeSettings["audio.volume.alarm"], "original read/write int setting");
is(settings.allSettings["app.reportCrashes"].value, fakeSettings["app.reportCrashes"], "original read/write string setting");
is(JSON.stringify(settings.allSettings["app.someObject"].value), JSON.stringify(fakeSettings["app.someObject"]), "original read/write object setting");
is(settings.allUpdatedSettings["wifi.enabled"].hasUserValue, true, "updated user-changed bool setting");
is(settings.allUpdatedSettings["audio.volume.alarm"].hasUserValue, true, "updated user-changed int setting");
is(settings.allUpdatedSettings["app.reportCrashes"].hasUserValue, true, "updated user-changed string setting");
is(settings.allUpdatedSettings["app.someObject"].hasUserValue, true, "updated user-changed object setting");
is(settings["wifi.enabled"], localSetting["wifi.enabled"], "updated bool setting");
is(settings["audio.volume.alarm"], localSetting["audio.volume.alarm"], "updated int setting");
is(settings["app.reportCrashes"], localSetting["app.reportCrashes"], "updated string setting");
is(JSON.stringify(settings["app.someObject"]), JSON.stringify(localSetting["app.someObject"]), "updated object as string setting");
is(resetSettings["wifi.enabled"], fakeSettings["wifi.enabled"], "reset to original bool setting");
is(resetSettings["audio.volume.alarm"], fakeSettings["audio.volume.alarm"], "reset to original int setting");
is(resetSettings["app.reportCrashes"], fakeSettings["app.reportCrashes"], "reset to original string setting");
is(JSON.stringify(resetSettings["app.someObject"]), JSON.stringify(fakeSettings["app.someObject"]), "reset to original object setting");
client.close(() => {
DebuggerServer.destroy();
SimpleTest.finish();
});
}
// settings.json doesn't exist outside of b2g so we will fake it.
_setDefaultSettings(fakeSettings);
s.setSetting("wifi.enabled", fakeSettings["wifi.enabled"])
.then(() => s.setSetting("audio.volume.alarm", fakeSettings["audio.volume.alarm"]))
.then(() => s.setSetting("app.reportCrashes", fakeSettings["app.reportCrashes"]))
.then(() => s.setSetting("app.someObject", fakeSettings["app.someObject"]))
.then(() => s.getAllSettings().then(json => settings.allSettings = json))
.then(() => s.setSetting("wifi.enabled", localSetting["wifi.enabled"]))
.then(() => s.setSetting("audio.volume.alarm", localSetting["audio.volume.alarm"]))
.then(() => s.setSetting("app.reportCrashes", localSetting["app.reportCrashes"]))
.then(() => s.setSetting("app.someObject", localSetting["app.someObject"]))
.then(() => s.getAllSettings().then(json => settings.allUpdatedSettings = json))
.then(() => s.getSetting("wifi.enabled")).then(value => settings["wifi.enabled"] = value)
.then(() => s.getSetting("audio.volume.alarm")).then(value => settings["audio.volume.alarm"] = value)
.then(() => s.getSetting("app.reportCrashes")).then(value => settings["app.reportCrashes"] = value)
.then(() => s.getSetting("app.someObject")).then(value => settings["app.someObject"] = value)
.then(() => s.clearUserSetting("wifi.enabled")).then(() => {
s.getSetting("wifi.enabled").then(value => resetSettings["wifi.enabled"] = value);
})
.then(() => s.clearUserSetting("audio.volume.alarm")).then(() => {
s.getSetting("audio.volume.alarm").then(value => resetSettings["audio.volume.alarm"] = value);
})
.then(() => s.clearUserSetting("app.reportCrashes")).then(() => {
s.getSetting("app.reportCrashes").then(value => resetSettings["app.reportCrashes"] = value);
})
.then(() => s.clearUserSetting("app.someObject")).then(() => {
s.getSetting("app.someObject").then(value => {
resetSettings["app.someObject"] = value
}).then(checkValues);
});
});
});
}
window.onload = function () {
runTests();
}
</script>
</pre>
</body>
</html>