merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-11-28 13:42:19 +01:00
Родитель ea326643ff ebc0fda013
Коммит a6d609feb2
42 изменённых файлов: 685 добавлений и 287 удалений

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

@ -34,20 +34,22 @@ function close() {
return p.kill();
}
let appinfo = {};
let name;
AddonManager.getAddonByID(require("addon").id, function (addon) {
appinfo.label = addon.name.replace(" Simulator", "");
name = addon.name.replace(" Simulator", "");
Simulator.register(appinfo.label, {
appinfo: appinfo,
Simulator.register(name, {
// We keep the deprecated `appinfo` object so that recent simulator addons
// remain forward-compatible with older Firefox.
appinfo: { label: name },
launch: launch,
close: close
});
});
exports.shutdown = function () {
Simulator.unregister(appinfo.label);
Simulator.unregister(name);
close();
}

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

@ -76,6 +76,36 @@ a {
min-width: 70px;
}
#searchIcon {
border: 1px solid transparent;
-moz-margin-end: 5px;
height: 38px;
width: 38px;
background-image: url("chrome://browser/skin/magnifier.png");
background-size: 26px;
background-position: center center;
background-repeat: no-repeat;
}
#searchIcon[active],
#searchIcon:hover {
background-color: #e9e9e9;
border: 1px solid rgb(226, 227, 229);
border-radius: 2.5px;
}
html[searchUIConfiguration="oldsearchui"] #searchIcon {
display: none;
}
html:not([searchUIConfiguration="oldsearchui"]) #searchText::-moz-placeholder {
color: transparent;
}
html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
display: none;
}
#searchText {
-moz-box-flex: 1;
padding: 6px 8px;
@ -368,6 +398,10 @@ body[narrow] #restorePreviousSession::before {
background-image: url("chrome://branding/content/about-logo@2x.png");
}
#searchIcon {
background-image: url("chrome://browser/skin/magnifier@2x.png");
}
#defaultSnippet1,
#defaultSnippet2,
#rightsSnippet {

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

@ -41,7 +41,8 @@
<div id="searchContainer">
<form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)">
<div id="searchLogoContainer"><img id="searchEngineLogo"/></div>
<div id="searchLogoContainer" hidden="true"><img id="searchEngineLogo"/></div>
<button id="searchIcon" type="button" />
<input type="text" name="q" value="" id="searchText" maxlength="256"
autofocus="autofocus" dir="auto"/>
<input id="searchSubmit" type="submit" value="&abouthome.searchEngineButton.label;"/>

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

@ -1221,6 +1221,29 @@ toolbarpaletteitem[place="palette"][hidden] {
animation-duration: 2s;
}
#abouthome-search-panel .panel-arrowcontent {
-moz-padding-start: 0;
-moz-padding-end: 0;
padding-top: 0;
padding-bottom: 0;
background: rgb(248, 250, 251);
font-size: 110%;
}
.abouthome-search-panel-item {
-moz-box-align: center;
padding-top: 4px;
padding-bottom: 4px;
-moz-padding-start: 24px;
-moz-padding-end: 24px;
}
.abouthome-search-panel-item > label {
-moz-padding-start: 0;
-moz-margin-start: 0;
color: rgb(130, 132, 133);
}
/* Combined context-menu items */
#context-navigation > .menuitem-iconic > .menu-iconic-text,
#context-navigation > .menuitem-iconic > .menu-accel-container {

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

@ -276,6 +276,14 @@
</hbox>
</panel>
<panel id="abouthome-search-panel" orient="vertical" type="arrow" hidden="true"
onclick="this.hidePopup()">
<hbox id="abouthome-search-panel-manage" class="abouthome-search-panel-item"
onclick="openPreferences('paneSearch')">
<label>&changeSearchSettings.button;</label>
</hbox>
</panel>
<panel id="social-share-panel"
class="social-panel"
type="arrow"

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

@ -269,6 +269,9 @@ let AboutHomeListener = {
case "AboutHomeSearchEvent":
this.onSearch(aEvent);
break;
case "AboutHomeSearchPanel":
this.onOpenSearchPanel(aEvent);
break;
case "click":
this.onClick(aEvent);
break;
@ -320,6 +323,10 @@ let AboutHomeListener = {
addEventListener("click", this, true);
addEventListener("pagehide", this, true);
if (!Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
doc.documentElement.setAttribute("searchUIConfiguration", "oldsearchui");
}
// XXX bug 738646 - when Marketplace is launched, remove this statement and
// the hidden attribute set on the apps button in aboutHome.xhtml
if (Services.prefs.getPrefType("browser.aboutHome.apps") == Services.prefs.PREF_BOOL &&
@ -328,6 +335,7 @@ let AboutHomeListener = {
sendAsyncMessage("AboutHome:RequestUpdate");
doc.addEventListener("AboutHomeSearchEvent", this, true, true);
doc.addEventListener("AboutHomeSearchPanel", this, true, true);
},
onClick: function(aEvent) {
@ -378,6 +386,10 @@ let AboutHomeListener = {
case "settings":
sendAsyncMessage("AboutHome:Settings");
break;
case "searchIcon":
sendAsyncMessage("AboutHome:OpenSearchPanel", null, { anchor: originalTarget });
break;
}
},
@ -397,6 +409,10 @@ let AboutHomeListener = {
sendAsyncMessage("AboutHome:Search", { searchData: aEvent.detail });
},
onOpenSearchPanel: function(aEvent) {
sendAsyncMessage("AboutHome:OpenSearchPanel");
},
onFocusInput: function () {
let searchInput = content.document.getElementById("searchText");
if (searchInput) {

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

@ -345,6 +345,19 @@ input[type=button] {
background-size: 26px 26px;
}
#newtab-search-logo.magnifier {
width: 38px; /* 26 image width + 6 left "padding" + 6 right "padding" */
-moz-margin-end: 5px;
background-size: 26px;
background-image: url("chrome://browser/skin/magnifier.png");
}
@media not all and (max-resolution: 1dppx) {
#newtab-search-icon.magnifier {
background-image: url("chrome://browser/skin/magnifier@2x.png");
}
}
#newtab-search-logo[type="logo"] {
background-size: 65px 26px;
width: 77px; /* 65 image width + 6 left "padding" + 6 right "padding" */
@ -366,7 +379,7 @@ input[type=button] {
}
#newtab-search-text {
height: 38px;
height: 38px; /* same height as #newtab-search-logo */
-moz-box-flex: 1;
padding: 0 8px;
@ -390,7 +403,7 @@ input[type=button] {
}
#newtab-search-submit {
height: 38px;
height: 38px; /* same height as #newtab-search-logo */
font-size: 13px !important;
-moz-margin-start: -1px;

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

@ -14,6 +14,8 @@
%newTabDTD;
<!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd">
%searchBarDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
@ -28,7 +30,7 @@
<xul:panel id="newtab-search-panel" orient="vertical" type="arrow"
noautohide="true" hidden="true">
<xul:hbox id="newtab-search-manage" class="newtab-search-panel-engine">
<xul:label>&cmd_engineManager.label;</xul:label>
<xul:label>&changeSearchSettings.button;</xul:label>
</xul:hbox>
</xul:panel>

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

@ -8,12 +8,23 @@ let gSearch = {
currentEngineName: null,
get useNewUI() {
let newUI = Services.prefs.getBoolPref("browser.search.showOneOffButtons");
delete this.useNewUI;
this.useNewUI = newUI;
return newUI;
},
init: function () {
for (let idSuffix of this._nodeIDSuffixes) {
this._nodes[idSuffix] =
document.getElementById("newtab-search-" + idSuffix);
}
if (this.useNewUI) {
this._nodes.logo.classList.add("magnifier");
}
window.addEventListener("ContentSearchService", this);
this._send("GetState");
},
@ -122,6 +133,11 @@ let gSearch = {
},
_setUpPanel: function () {
// The new search UI only contains the "manage" engine entry in the panel
if (this.useNewUI) {
return;
}
// Build the panel if necessary.
if (this._newEngines) {
this._buildPanel(this._newEngines);
@ -198,28 +214,30 @@ let gSearch = {
_setCurrentEngine: function (engine) {
this.currentEngineName = engine.name;
let type = "";
let uri;
let logoBuf = window.devicePixelRatio >= 2 ?
engine.logo2xBuffer || engine.logoBuffer :
engine.logoBuffer || engine.logo2xBuffer;
if (logoBuf) {
uri = URL.createObjectURL(new Blob([logoBuf]));
type = "logo";
}
else if (engine.iconBuffer) {
uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
type = "favicon";
}
this._nodes.logo.setAttribute("type", type);
if (!this.useNewUI) {
let type = "";
let uri;
let logoBuf = window.devicePixelRatio >= 2 ?
engine.logo2xBuffer || engine.logoBuffer :
engine.logoBuffer || engine.logo2xBuffer;
if (logoBuf) {
uri = URL.createObjectURL(new Blob([logoBuf]));
type = "logo";
}
else if (engine.iconBuffer) {
uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
type = "favicon";
}
this._nodes.logo.setAttribute("type", type);
if (uri) {
this._nodes.logo.style.backgroundImage = "url(" + uri + ")";
if (uri) {
this._nodes.logo.style.backgroundImage = "url(" + uri + ")";
}
else {
this._nodes.logo.style.backgroundImage = "";
}
this._nodes.text.placeholder = engine.placeholder;
}
else {
this._nodes.logo.style.backgroundImage = "";
}
this._nodes.text.placeholder = engine.placeholder;
// Set up the suggestion controller.
if (!this._suggestionController) {

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

@ -78,25 +78,6 @@ let gTests = [
}
},
{
desc: "Check that search engine logo has alt text",
setup: function () { },
run: function ()
{
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let searchEngineLogoElt = doc.getElementById("searchEngineLogo");
ok(searchEngineLogoElt, "Found search engine logo");
let altText = searchEngineLogoElt.alt;
ok(typeof altText == "string" && altText.length > 0,
"Search engine logo's alt text is a nonempty string");
isnot(altText, "undefined",
"Search engine logo's alt text shouldn't be the string 'undefined'");
}
},
// Disabled on Linux for intermittent issues with FHR, see Bug 945667.
{
desc: "Check that performing a search fires a search event and records to " +
@ -252,52 +233,6 @@ let gTests = [
}
},
{
desc: "Check that the search UI/ action is updated when the search engine is changed",
setup: function() {},
run: function()
{
let currEngine = Services.search.currentEngine;
let unusedEngines = [].concat(Services.search.getVisibleEngines()).filter(x => x != currEngine);
let searchbar = document.getElementById("searchbar");
function checkSearchUI(engine) {
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let searchText = doc.getElementById("searchText");
let logoElt = doc.getElementById("searchEngineLogo");
let engineName = doc.documentElement.getAttribute("searchEngineName");
is(engineName, engine.name, "Engine name should've been updated");
if (!logoElt.parentNode.hidden) {
is(logoElt.alt, engineName, "Alt text of logo image should match search engine name")
} else {
is(searchText.placeholder, engineName, "Placeholder text should match search engine name");
}
}
// Do a sanity check that all attributes are correctly set to begin with
checkSearchUI(currEngine);
let deferred = Promise.defer();
promiseBrowserAttributes(gBrowser.selectedTab).then(function() {
// Test if the update propagated
checkSearchUI(unusedEngines[0]);
searchbar.currentEngine = currEngine;
deferred.resolve();
});
// The following cleanup function will set currentEngine back to the previous
// engine if we fail to do so above.
registerCleanupFunction(function() {
searchbar.currentEngine = currEngine;
});
// Set the current search engine to an unused one
searchbar.currentEngine = unusedEngines[0];
searchbar.select();
return deferred.promise;
}
},
{
desc: "Check POST search engine support",
setup: function() {},
@ -492,6 +427,26 @@ let gTests = [
is(gBrowser.currentURI.spec, "about:accounts?entrypoint=abouthome",
"Entry point should be `abouthome`.");
})
},
{
desc: "Clicking the icon should open the popup",
setup: function () {},
run: Task.async(function* () {
let doc = gBrowser.selectedBrowser.contentDocument;
let searchIcon = doc.getElementById("searchIcon");
let panel = window.document.getElementById("abouthome-search-panel");
info("Waiting for popup to open");
EventUtils.synthesizeMouseAtCenter(searchIcon, {}, gBrowser.selectedBrowser.contentWindow);
yield promiseWaitForEvent(panel, "popupshown");
ok("Saw popup open");
let promise = promisePrefsOpen();
let item = window.document.getElementById("abouthome-search-panel-manage");
EventUtils.synthesizeMouseAtCenter(item, {});
yield promise;
})
}
];
@ -667,6 +622,24 @@ function waitForLoad(cb) {
}, true);
}
function promiseWaitForEvent(node, type, capturing) {
return new Promise((resolve) => {
node.addEventListener(type, function listener(event) {
node.removeEventListener(type, listener, capturing);
resolve(event);
}, capturing);
});
}
let promisePrefsOpen = Task.async(function*() {
info("Waiting for the preferences tab to open...");
let event = yield promiseWaitForEvent(gBrowser.tabContainer, "TabOpen", true);
let tab = event.target;
yield promiseTabLoadEvent(tab);
is(tab.linkedBrowser.currentURI.spec, "about:preferences#search", "Should have seen the prefs tab");
gBrowser.removeTab(tab);
});
function promiseNewEngine(basename) {
info("Waiting for engine to be added: " + basename);
let addDeferred = Promise.defer();

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

@ -119,41 +119,12 @@ let runTaskifiedTests = Task.async(function* () {
promiseClick(logoImg()),
]);
// In the search panel, click the no-logo engine. It should become the
// current engine.
let noLogoBox = null;
for (let box of panel.childNodes) {
if (box.getAttribute("engine") == noLogoEngine.name) {
noLogoBox = box;
break;
}
}
ok(noLogoBox, "Search panel should contain the no-logo engine");
yield Promise.all([
promiseSearchEvents(["CurrentEngine"]),
promiseClick(noLogoBox),
]);
yield checkCurrentEngine(ENGINE_NO_LOGO);
// Switch back to the 1x-and-2x logo engine.
Services.search.currentEngine = logo1x2xEngine;
yield promiseSearchEvents(["CurrentEngine"]);
yield checkCurrentEngine(ENGINE_1X_2X_LOGO);
// Open the panel again.
yield Promise.all([
promisePanelShown(panel),
promiseClick(logoImg()),
]);
// In the search panel, click the Manage Engines box.
let manageBox = $("manage");
ok(!!manageBox, "The Manage Engines box should be present in the document");
yield Promise.all([
promiseManagerOpen(),
promiseClick(manageBox),
]);
is(panel.childNodes.length, 1, "Search panel should only contain the Manage Engines entry");
is(panel.childNodes[0], manageBox, "Search panel should contain the Manage Engines entry");
panel.hidePopup();
// Add the engine that provides search suggestions and switch to it.
let suggestionEngine = yield promiseNewSearchEngine(ENGINE_SUGGESTIONS);
@ -340,42 +311,6 @@ let checkCurrentEngine = Task.async(function* ({name: basename, logoPrefix1x, lo
// gSearch.currentEngineName
is(gSearch().currentEngineName, engine.name,
"currentEngineName: " + engine.name);
let expectedLogoPrefix = window.devicePixelRatio >= 2 ? logoPrefix2x : logoPrefix1x;
// Check that the right logo is set.
let logo = logoImg();
if (expectedLogoPrefix) {
let objectURL = logo.style.backgroundImage.match(/^url\("([^"]*)"\)$/)[1];
ok(objectURL, "ObjectURL should be there.");
let blob = yield objectURLToBlob(objectURL);
let base64 = yield blobToBase64(blob);
ok(base64.startsWith(expectedLogoPrefix), "Checking image prefix.");
logo.click();
let panel = searchPanel();
yield promisePanelShown(panel);
panel.hidePopup();
for (let engineBox of panel.childNodes) {
let engineName = engineBox.getAttribute("engine");
if (engineName == engine.name) {
is(engineBox.getAttribute("selected"), "true",
"Engine box's selected attribute should be true for " +
"selected engine: " + engineName);
}
else {
ok(!engineBox.hasAttribute("selected"),
"Engine box's selected attribute should be absent for " +
"non-selected engine: " + engineName);
}
}
}
else {
is(logo.style.backgroundImage, "", "backgroundImage should be empty");
}
});
function promisePanelShown(panel) {
@ -399,31 +334,6 @@ function promiseClick(node) {
return deferred.promise;
}
function promiseManagerOpen() {
info("Waiting for the search manager window to open...");
let deferred = Promise.defer();
let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher);
winWatcher.registerNotification(function onWin(subj, topic, data) {
if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
subj.addEventListener("load", function onLoad() {
subj.removeEventListener("load", onLoad);
if (subj.document.documentURI ==
"chrome://browser/content/search/engineManager.xul") {
winWatcher.unregisterNotification(onWin);
ok(true, "Observed search manager window opened");
is(subj.opener, gWindow,
"Search engine manager opener should be the chrome browser " +
"window containing the newtab page");
subj.close();
deferred.resolve();
}
});
}
});
return deferred.promise;
}
function searchPanel() {
return $("panel");
}

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

@ -629,6 +629,12 @@
]]></body>
</method>
<method name="inputChanged">
<body><![CDATA[
this.updateGoButtonVisibility();
]]></body>
</method>
<method name="updateGoButtonVisibility">
<body><![CDATA[
document.getAnonymousElementByAttribute(this, "anonid",
@ -657,8 +663,8 @@
</method>
</implementation>
<handlers>
<handler event="input" action="this.updateGoButtonVisibility();"/>
<handler event="drop" action="this.updateGoButtonVisibility();"/>
<handler event="input" action="this.inputChanged();"/>
<handler event="drop" action="this.inputChanged();"/>
<handler event="focus">
<![CDATA[
if (this._textbox.value)

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

@ -137,7 +137,7 @@ let UI = {
startSimulator: function(version) {
this._portBeforeSimulatorStarted = this.connection.port;
let port = ConnectionManager.getFreeTCPPort();
let simulator = Simulator.getByVersion(version);
let simulator = Simulator.getByName(version);
if (!simulator) {
this.connection.log("Error: can't find simulator: " + version);
return;

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

@ -9,11 +9,11 @@ const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
let store = new ObservableObject({versions:[]});
function feedStore() {
store.object.versions = Simulator.availableVersions().map(v => {
let simulator = Simulator.getByVersion(v);
store.object.versions = Simulator.availableNames().map(name => {
let simulator = Simulator.getByName(name);
return {
version: v,
label: simulator.appinfo.label
version: name,
label: simulator ? name : "Unknown"
}
});
}

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

@ -159,11 +159,14 @@ BrowserToolboxProcess.prototype = {
try {
debuggingProfileDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
} catch (ex) {
if (ex.result !== Cr.NS_ERROR_FILE_ALREADY_EXISTS) {
// Don't re-copy over the prefs again if this profile already exists
if (ex.result === Cr.NS_ERROR_FILE_ALREADY_EXISTS) {
this._dbgProfilePath = debuggingProfileDir.path;
} else {
dumpn("Error trying to create a profile directory, failing.");
dumpn("Error: " + (ex.message || ex));
return;
}
return;
}
this._dbgProfilePath = debuggingProfileDir.path;

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

@ -71,9 +71,21 @@ function openToolbox(form) {
};
devtools.TargetFactory.forRemoteTab(options).then(target => {
let frame = document.getElementById("toolbox-iframe");
let selectedTool = "jsdebugger";
try {
// Remember the last panel that was used inside of this profile.
selectedTool = Services.prefs.getCharPref("devtools.toolbox.selectedTool");
} catch(e) {}
try {
// But if we are testing, then it should always open the debugger panel.
selectedTool = Services.prefs.getCharPref("devtools.browsertoolbox.panel");
} catch(e) {}
let options = { customIframe: frame };
gDevTools.showToolbox(target,
"jsdebugger",
selectedTool,
devtools.Toolbox.HostType.CUSTOM,
options)
.then(onNewToolbox);

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

@ -204,7 +204,7 @@ var TextEditor = Class({
*/
load: function(resource) {
// Wait for the editor.appendTo and resource.load before proceeding.
// They can run in parallel.
// They can run in parallel.
return promise.all([
resource.load(),
this.appended
@ -245,7 +245,9 @@ var TextEditor = Class({
*/
focus: function() {
return this.appended.then(() => {
this.editor.focus();
if (this.editor) {
this.editor.focus();
}
});
}
});

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

@ -6,8 +6,6 @@ const {Cu} = require("chrome");
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm");
const {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm");
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
const {GetAddonsJSON} = require("devtools/webide/remote-resources");

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

@ -205,8 +205,8 @@ let SimulatorScanner = {
_updateRuntimes() {
this._runtimes = [];
for (let version of Simulator.availableVersions()) {
this._runtimes.push(new SimulatorRuntime(version));
for (let name of Simulator.availableNames()) {
this._runtimes.push(new SimulatorRuntime(name));
}
this._emitUpdated();
},
@ -463,15 +463,15 @@ WiFiRuntime.prototype = {
// For testing use only
exports._WiFiRuntime = WiFiRuntime;
function SimulatorRuntime(version) {
this.version = version;
function SimulatorRuntime(name) {
this.name = name;
}
SimulatorRuntime.prototype = {
type: RuntimeTypes.SIMULATOR,
connect: function(connection) {
let port = ConnectionManager.getFreeTCPPort();
let simulator = Simulator.getByVersion(this.version);
let simulator = Simulator.getByName(this.name);
if (!simulator || !simulator.launch) {
return promise.reject("Can't find simulator: " + this.name);
}
@ -484,14 +484,7 @@ SimulatorRuntime.prototype = {
});
},
get id() {
return this.version;
},
get name() {
let simulator = Simulator.getByVersion(this.version);
if (!simulator) {
return "Unknown";
}
return Simulator.getByVersion(this.version).appinfo.label;
return this.name;
},
};

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

@ -28,10 +28,14 @@
adbAddonsInstalled.resolve();
});
function onSimulatorInstalled(version) {
function getVersion(name) {
return name.match(/(\d+\.\d+)/)[0];
}
function onSimulatorInstalled(name) {
let deferred = promise.defer();
Simulator.on("register", function onUpdate() {
if (Simulator.getByVersion(version)) {
if (Simulator.getByName(name)) {
Simulator.off("register", onUpdate);
nextTick().then(deferred.resolve);
}
@ -39,17 +43,17 @@
return deferred.promise;
}
function installSimulatorFromUI(doc, version) {
let li = doc.querySelector('[addon="simulator-' + version + '"]');
function installSimulatorFromUI(doc, name) {
let li = doc.querySelector('[addon="simulator-' + getVersion(name) + '"]');
li.querySelector(".install-button").click();
return onSimulatorInstalled(version);
return onSimulatorInstalled(name);
}
function uninstallSimulatorFromUI(doc, version) {
function uninstallSimulatorFromUI(doc, name) {
let deferred = promise.defer();
Simulator.on("unregister", function onUpdate() {
nextTick().then(() => {
let li = doc.querySelector('[status="uninstalled"][addon="simulator-' + version + '"]');
let li = doc.querySelector('[status="uninstalled"][addon="simulator-' + getVersion(name) + '"]');
if (li) {
Simulator.off("unregister", onUpdate);
deferred.resolve();
@ -58,7 +62,7 @@
}
})
});
let li = doc.querySelector('[status="installed"][addon="simulator-' + version + '"]');
let li = doc.querySelector('[status="installed"][addon="simulator-' + getVersion(name) + '"]');
li.querySelector(".uninstall-button").click();
return deferred.promise;
}
@ -100,7 +104,7 @@
let sim10 = addons.simulators.filter(a => a.version == "1.0")[0];
sim10.install();
yield onSimulatorInstalled("1.0");
yield onSimulatorInstalled("Firefox OS 1.0");
win.Cmds.showAddons();
@ -119,11 +123,11 @@
info("Uninstalling Simulator 2.0");
yield installSimulatorFromUI(addonDoc, "2.0");
yield installSimulatorFromUI(addonDoc, "Firefox OS 2.0");
info("Uninstalling Simulator 3.0");
yield installSimulatorFromUI(addonDoc, "3.0");
yield installSimulatorFromUI(addonDoc, "Firefox OS 3.0");
yield nextTick();
@ -136,9 +140,9 @@
items = panelNode.querySelectorAll(".runtime-panel-item-simulator");
is(items.length, 3, "Found 3 simulators button");
yield uninstallSimulatorFromUI(addonDoc, "1.0");
yield uninstallSimulatorFromUI(addonDoc, "2.0");
yield uninstallSimulatorFromUI(addonDoc, "3.0");
yield uninstallSimulatorFromUI(addonDoc, "Firefox OS 1.0");
yield uninstallSimulatorFromUI(addonDoc, "Firefox OS 2.0");
yield uninstallSimulatorFromUI(addonDoc, "Firefox OS 3.0");
items = panelNode.querySelectorAll(".runtime-panel-item-simulator");
is(items.length, 0, "No simulator listed");

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

@ -170,6 +170,7 @@
@RESPATH@/components/browser-element.xpt
@RESPATH@/browser/components/browsercompsbase.xpt
@RESPATH@/browser/components/browser-feeds.xpt
@RESPATH@/browser/components/browsermodules.manifest
@RESPATH@/components/caps.xpt
@RESPATH@/components/chrome.xpt
@RESPATH@/components/commandhandler.xpt

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

@ -97,6 +97,7 @@ let AboutHome = {
"AboutHome:Settings",
"AboutHome:RequestUpdate",
"AboutHome:Search",
"AboutHome:OpenSearchPanel",
],
init: function() {
@ -203,6 +204,18 @@ let AboutHome = {
});
break;
case "AboutHome:OpenSearchPanel":
let panel = window.document.getElementById("abouthome-search-panel");
let anchor = aMessage.objects.anchor;
panel.hidden = false;
panel.openPopup(anchor);
anchor.setAttribute("active", "true");
panel.addEventListener("popuphidden", function onHidden() {
panel.removeEventListener("popuphidden", onHidden);
anchor.removeAttribute("active");
});
break;
}
},

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

@ -220,6 +220,12 @@ this.ContentSearch = {
_onMessageManageEngines: function (msg, data) {
let browserWin = msg.target.ownerDocument.defaultView;
if (Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
browserWin.openPreferences("paneSearch");
return Promise.resolve();
}
let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService(Components.interfaces.nsIWindowMediator);
let window = wm.getMostRecentWindow("Browser:SearchManager");

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

@ -4,7 +4,7 @@
"use strict";
this.EXPORTED_SYMBOLS = ["UITour"];
this.EXPORTED_SYMBOLS = ["UITour", "UITourMetricsProvider"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
@ -23,7 +23,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
"resource://gre/modules/UITelemetry.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
"resource:///modules/BrowserUITelemetry.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
"resource://gre/modules/Metrics.jsm");
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
const PREF_LOG_LEVEL = "browser.uitour.loglevel";
@ -521,6 +522,64 @@ this.UITour = {
}).catch(log.error);
break;
}
case "setDefaultSearchEngine": {
let enginePromise = this.selectSearchEngine(data.identifier);
enginePromise.catch(Cu.reportError);
break;
}
case "setTreatmentTag": {
let name = data.name;
let value = data.value;
let string = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
string.data = value;
Services.prefs.setComplexValue("browser.uitour.treatment." + name,
Ci.nsISupportsString, string);
UITourHealthReport.recordTreatmentTag(name, value);
break;
}
case "getTreatmentTag": {
let name = data.name;
let value;
try {
value = Services.prefs.getComplexValue("browser.uitour.treatment." + name,
Ci.nsISupportsString).data;
} catch (ex) {}
this.sendPageCallback(messageManager, data.callbackID, { value: value });
break;
}
case "setSearchTerm": {
let targetPromise = this.getTarget(window, "search");
targetPromise.then(target => {
let searchbar = target.node;
searchbar.value = data.term;
searchbar.inputChanged();
}).then(null, Cu.reportError);
break;
}
case "openSearchPanel": {
let targetPromise = this.getTarget(window, "search");
targetPromise.then(target => {
let searchbar = target.node;
if (searchbar.textbox.open) {
this.sendPageCallback(messageManager, data.callbackID);
} else {
let onPopupShown = () => {
searchbar.textbox.popup.removeEventListener("popupshown", onPopupShown);
this.sendPageCallback(messageManager, data.callbackID);
};
searchbar.textbox.popup.addEventListener("popupshown", onPopupShown);
searchbar.openSuggestionsPanel();
}
}).then(null, Cu.reportError);
break;
}
}
if (!window.gMultiProcessBrowser) { // Non-e10s. See bug 1089000.
@ -1105,9 +1164,14 @@ this.UITour = {
tooltip.setAttribute("targetName", aAnchor.targetName);
tooltip.hidden = false;
let xOffset = 0, yOffset = 0;
let alignment = "bottomcenter topright";
if (aAnchor.targetName == "search") {
alignment = "after_start";
xOffset = 18;
}
this._addAnnotationPanelMutationObserver(tooltip);
tooltip.openPopup(aAnchorEl, alignment);
tooltip.openPopup(aAnchorEl, alignment, xOffset, yOffset);
if (tooltip.state == "closed") {
document.defaultView.addEventListener("endmodalstate", function endModalStateHandler() {
document.defaultView.removeEventListener("endmodalstate", endModalStateHandler);
@ -1290,6 +1354,19 @@ this.UITour = {
props.forEach(property => appinfo[property] = Services.appinfo[property]);
this.sendPageCallback(aMessageManager, aCallbackID, appinfo);
break;
case "selectedSearchEngine":
Services.search.init(rv => {
let engine;
if (Components.isSuccessCode(rv)) {
engine = Services.search.defaultEngine;
} else {
engine = { identifier: "" };
}
this.sendPageCallback(aMessageManager, aCallbackID, {
searchEngineIdentifier: engine.identifier
});
});
break;
default:
log.error("getConfiguration: Unknown configuration requested: " + aConfiguration);
break;
@ -1397,6 +1474,26 @@ this.UITour = {
}
},
selectSearchEngine(aID) {
return new Promise((resolve, reject) => {
Services.search.init((rv) => {
if (!Components.isSuccessCode(rv)) {
reject("selectSearchEngine: search service init failed: " + rv);
return;
}
let engines = Services.search.getVisibleEngines();
for (let engine of engines) {
if (engine.identifier == aID) {
Services.search.defaultEngine = engine;
return resolve();
}
}
reject("selectSearchEngine could not find engine with given ID");
});
});
},
getAvailableSearchEngineTargets(aWindow) {
return new Promise(resolve => {
this.getTarget(aWindow, "search").then(searchTarget => {
@ -1448,3 +1545,105 @@ this.UITour = {
};
this.UITour.init();
/**
* UITour Health Report
*/
const DAILY_DISCRETE_TEXT_FIELD = Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT;
/**
* Public API to be called by the UITour code
*/
const UITourHealthReport = {
recordTreatmentTag: function(tag, value) {
#ifdef MOZ_SERVICES_HEALTHREPORT
Task.spawn(function*() {
let reporter = Cc["@mozilla.org/datareporting/service;1"]
.getService()
.wrappedJSObject
.healthReporter;
// This can happen if the FHR component of the data reporting service is
// disabled. This is controlled by a pref that most will never use.
if (!reporter) {
return;
}
yield reporter.onInit();
// Get the UITourMetricsProvider instance from the Health Reporter
reporter.getProvider("org.mozilla.uitour").recordTreatmentTag(tag, value);
});
#endif
}
};
this.UITourMetricsProvider = function() {
Metrics.Provider.call(this);
}
UITourMetricsProvider.prototype = Object.freeze({
__proto__: Metrics.Provider.prototype,
name: "org.mozilla.uitour",
measurementTypes: [
UITourTreatmentMeasurement1,
],
recordTreatmentTag: function(tag, value) {
let m = this.getMeasurement(UITourTreatmentMeasurement1.prototype.name,
UITourTreatmentMeasurement1.prototype.version);
let field = tag;
if (this.storage.hasFieldFromMeasurement(m.id, field,
DAILY_DISCRETE_TEXT_FIELD)) {
let fieldID = this.storage.fieldIDFromMeasurement(m.id, field);
return this.enqueueStorageOperation(function recordKnownField() {
return this.storage.addDailyDiscreteTextFromFieldID(fieldID, value);
}.bind(this));
}
// Otherwise, we first need to create the field.
return this.enqueueStorageOperation(function recordField() {
// This function has to return a promise.
return Task.spawn(function () {
let fieldID = yield this.storage.registerField(m.id, field,
DAILY_DISCRETE_TEXT_FIELD);
yield this.storage.addDailyDiscreteTextFromFieldID(fieldID, value);
}.bind(this));
}.bind(this));
},
});
function UITourTreatmentMeasurement1() {
Metrics.Measurement.call(this);
this._serializers = {};
this._serializers[this.SERIALIZE_JSON] = {
//singular: We don't need a singular serializer because we have none of this data
daily: this._serializeJSONDaily.bind(this)
};
}
UITourTreatmentMeasurement1.prototype = Object.freeze({
__proto__: Metrics.Measurement.prototype,
name: "treatment",
version: 1,
// our fields are dynamic
fields: { },
// We need a custom serializer because the default one doesn't accept unknown fields
_serializeJSONDaily: function(data) {
let result = {_v: this.version };
for (let [field, data] of data) {
result[field] = data;
}
return result;
}
});

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

@ -0,0 +1,3 @@
#ifdef MOZ_SERVICES_HEALTHREPORT
category healthreport-js-provider-default UITourMetricsProvider resource:///modules/UITour.jsm
#endif

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

@ -55,5 +55,9 @@ EXTRA_PP_JS_MODULES += [
'webrtcUI.jsm',
]
EXTRA_PP_COMPONENTS += [
'browsermodules.manifest',
]
if CONFIG['MOZILLA_OFFICIAL']:
DEFINES['MOZILLA_OFFICIAL'] = 1

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

@ -70,34 +70,6 @@ add_task(function* SetCurrentEngine() {
});
});
add_task(function* ManageEngines() {
yield addTab();
gMsgMan.sendAsyncMessage(TEST_MSG, {
type: "ManageEngines",
});
let deferred = Promise.defer();
let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher);
winWatcher.registerNotification(function onOpen(subj, topic, data) {
if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
subj.addEventListener("load", function onLoad() {
subj.removeEventListener("load", onLoad);
if (subj.document.documentURI ==
"chrome://browser/content/search/engineManager.xul") {
winWatcher.unregisterNotification(onOpen);
ok(true, "Observed search manager window open");
is(subj.opener, window,
"Search engine manager opener should be this chrome window");
subj.close();
deferred.resolve();
}
});
}
});
info("Waiting for search engine manager window to open...");
yield deferred.promise;
});
add_task(function* modifyEngine() {
yield addTab();
let engine = Services.search.currentEngine;

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

@ -369,6 +369,44 @@ let tests = [
});
});
},
function test_select_search_engine(done) {
Services.search.init(rv => {
if (!Components.isSuccessCode(rv)) {
ok(false, "search service init failed: " + rv);
done();
return;
}
let defaultEngine = Services.search.defaultEngine;
gContentAPI.getConfiguration("availableTargets", data => {
let searchEngines = data.targets.filter(t => t.startsWith("searchEngine-"));
let someOtherEngineID = searchEngines.filter(t => t != "searchEngine-" + defaultEngine.identifier)[0];
someOtherEngineID = someOtherEngineID.replace(/^searchEngine-/, "");
let observe = function (subject, topic, verb) {
info("browser-search-engine-modified: " + verb);
if (verb == "engine-current") {
is(Services.search.defaultEngine.identifier, someOtherEngineID, "correct engine was switched to");
done();
}
};
Services.obs.addObserver(observe, "browser-search-engine-modified", false);
registerCleanupFunction(() => {
// Clean up
Services.obs.removeObserver(observe, "browser-search-engine-modified");
Services.search.defaultEngine = defaultEngine;
});
gContentAPI.setDefaultSearchEngine(someOtherEngineID);
});
});
},
function test_treatment_tag(done) {
gContentAPI.setTreatmentTag("foobar", "baz");
gContentAPI.getTreatmentTag("foobar", (data) => {
is(data.value, "baz", "set and retrieved treatmentTag");
done();
});
},
// Make sure this test is last in the file so the appMenu gets left open and done will confirm it got tore down.
taskify(function* cleanupMenus() {

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

@ -8,6 +8,7 @@ let gContentAPI;
let gContentWindow;
Components.utils.import("resource:///modules/UITour.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
requestLongerTimeout(2);
@ -145,4 +146,54 @@ let tests = [
popup.removeAttribute("animate");
}),
function test_getConfiguration_selectedSearchEngine(done) {
Services.search.init(rv => {
ok(Components.isSuccessCode(rv), "Search service initialized");
let engine = Services.search.defaultEngine;
gContentAPI.getConfiguration("selectedSearchEngine", (data) => {
is(data.searchEngineIdentifier, engine.identifier, "Correct engine identifier");
done();
});
});
},
function test_setSearchTerm(done) {
const TERM = "UITour Search Term";
gContentAPI.setSearchTerm(TERM);
let searchbar = document.getElementById("searchbar");
// The UITour gets to the searchbar element through a promise, so the value setting
// only happens after a tick.
waitForCondition(() => searchbar.value == TERM, done, "Correct term set");
},
function test_clearSearchTerm(done) {
gContentAPI.setSearchTerm("");
let searchbar = document.getElementById("searchbar");
// The UITour gets to the searchbar element through a promise, so the value setting
// only happens after a tick.
waitForCondition(() => searchbar.value == "", done, "Search term cleared");
},
function test_openSearchPanel(done) {
let searchbar = document.getElementById("searchbar");
// If suggestions are enabled, the panel will attempt to use the network to connect
// to the suggestions provider, causing the test suite to fail.
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("browser.search.suggest.enabled");
});
ok(!searchbar.textbox.open, "Popup starts as closed");
gContentAPI.openSearchPanel(() => {
ok(searchbar.textbox.open, "Popup was opened");
searchbar.textbox.closePopup();
ok(!searchbar.textbox.open, "Popup was closed");
done();
});
},
];

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

@ -2,7 +2,8 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource:///modules/UITour.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
"resource:///modules/UITour.jsm");
Cu.import("resource://gre/modules/Task.jsm");
const SINGLE_TRY_TIMEOUT = 100;
@ -219,6 +220,7 @@ function UITourTest() {
registerCleanupFunction(function() {
delete window.UITour;
delete window.UITourMetricsProvider;
delete window.gContentWindow;
delete window.gContentAPI;
if (gTestTab)

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

@ -207,4 +207,36 @@ if (typeof Mozilla == 'undefined') {
});
};
Mozilla.UITour.setDefaultSearchEngine = function(identifier) {
_sendEvent('setDefaultSearchEngine', {
identifier: identifier,
});
};
Mozilla.UITour.setTreatmentTag = function(name, value) {
_sendEvent('setTreatmentTag', {
name: name,
value: value
});
};
Mozilla.UITour.getTreatmentTag = function(name, callback) {
_sendEvent('getTreatmentTag', {
name: name,
callbackID: _waitForCallback(callback)
});
};
Mozilla.UITour.setSearchTerm = function(term) {
_sendEvent('setSearchTerm', {
term: term
});
};
Mozilla.UITour.openSearchPanel = function(callback) {
_sendEvent('openSearchPanel', {
callbackID: _waitForCallback(callback)
});
};
})();

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

@ -40,6 +40,8 @@ browser.jar:
skin/classic/browser/identity-icons-https-mixed-active.png
skin/classic/browser/identity-icons-https-mixed-display.png
skin/classic/browser/Info.png
skin/classic/browser/magnifier.png (../shared/magnifier.png)
skin/classic/browser/magnifier@2x.png (../shared/magnifier@2x.png)
skin/classic/browser/mask.png (../shared/mask.png)
skin/classic/browser/mask@2x.png (../shared/mask@2x.png)
skin/classic/browser/menuPanel.png

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

@ -58,6 +58,8 @@ browser.jar:
skin/classic/browser/notification-16@2x.png
skin/classic/browser/notification-64.png
skin/classic/browser/notification-64@2x.png
skin/classic/browser/magnifier.png (../shared/magnifier.png)
skin/classic/browser/magnifier@2x.png (../shared/magnifier@2x.png)
skin/classic/browser/mask.png (../shared/mask.png)
skin/classic/browser/mask@2x.png (../shared/mask@2x.png)
skin/classic/browser/menuPanel.png

Двоичные данные
browser/themes/shared/magnifier.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 371 B

Двоичные данные
browser/themes/shared/magnifier@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 689 B

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

@ -45,6 +45,8 @@ browser.jar:
skin/classic/browser/keyhole-forward-mask.svg
skin/classic/browser/KUI-background.png
skin/classic/browser/livemark-folder.png
skin/classic/browser/magnifier.png (../shared/magnifier.png)
skin/classic/browser/magnifier@2x.png (../shared/magnifier@2x.png)
skin/classic/browser/mask.png (../shared/mask.png)
skin/classic/browser/mask@2x.png (../shared/mask@2x.png)
skin/classic/browser/menu-back.png
@ -483,6 +485,8 @@ browser.jar:
skin/classic/aero/browser/keyhole-forward-mask.svg
skin/classic/aero/browser/KUI-background.png
skin/classic/aero/browser/livemark-folder.png (livemark-folder-aero.png)
skin/classic/aero/browser/magnifier.png (../shared/magnifier.png)
skin/classic/aero/browser/magnifier@2x.png (../shared/magnifier@2x.png)
skin/classic/aero/browser/mask.png (../shared/mask.png)
skin/classic/aero/browser/mask@2x.png (../shared/mask@2x.png)
skin/classic/aero/browser/menu-back.png (menu-back-aero.png)

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

@ -1831,3 +1831,32 @@ Example
"lastActiveBranch": "control"
}
org.mozilla.uitour.treatment
----------------------------
Daily measurement reporting information about treatment tagging done
by the UITour module.
Version 1
^^^^^^^^^
Daily text values in the following properties:
<tag>:
Array of discrete strings corresponding to calls for setTreatmentTag(tag, value).
Example
^^^^^^^
::
"org.mozilla.uitour.treatment": {
"_v": 1,
"treatment": [
"optin",
"optin-DNT"
],
"another-tag": [
"foobar-value"
]
}

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

@ -23,6 +23,7 @@ user_pref("shell.checkDefaultClient", false);
user_pref("browser.warnOnQuit", false);
user_pref("accessibility.typeaheadfind.autostart", false);
user_pref("javascript.options.showInConsole", true);
user_pref("devtools.browsertoolbox.panel", "jsdebugger");
user_pref("devtools.errorconsole.enabled", true);
user_pref("devtools.debugger.remote-port", 6023);
user_pref("layout.debug.enable_data_xbl", true);

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

@ -3905,16 +3905,7 @@ nsNavHistory::RowToResult(mozIStorageValueArray* aRow,
if (IsQueryURI(url)) {
// Special case "place:" URIs: turn them into containers.
nsRefPtr<nsNavHistoryResultNode> resultNode;
rv = QueryRowToResult(itemId, url, title, accessCount, time, favicon,
getter_AddRefs(resultNode));
NS_ENSURE_SUCCESS(rv,rv);
if (itemId != -1) {
rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid,
resultNode->mBookmarkGuid);
NS_ENSURE_SUCCESS(rv, rv);
// We should never expose the history title for query nodes if the
// bookmark-item's title is set to null (the history title may be the
// query string without the place: prefix). Thus we call getItemTitle
@ -3923,7 +3914,18 @@ nsNavHistory::RowToResult(mozIStorageValueArray* aRow,
nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
rv = bookmarks->GetItemTitle(itemId, resultNode->mTitle);
rv = bookmarks->GetItemTitle(itemId, title);
NS_ENSURE_SUCCESS(rv, rv);
}
nsRefPtr<nsNavHistoryResultNode> resultNode;
rv = QueryRowToResult(itemId, url, title, accessCount, time, favicon,
getter_AddRefs(resultNode));
NS_ENSURE_SUCCESS(rv,rv);
if (itemId != -1) {
rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid,
resultNode->mBookmarkGuid);
NS_ENSURE_SUCCESS(rv, rv);
}

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

@ -0,0 +1,24 @@
// Test that result node for folder shortcuts get the target folder title if
// the shortcut itself has no title set.
add_task(function* () {
let shortcutInfo = yield PlacesUtils.bookmarks.insert({
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: "place:folder=TOOLBAR"
});
let unfiledRoot =
PlacesUtils.getFolderContents(PlacesUtils.unfiledBookmarksFolderId).root;
let shortcutNode = unfiledRoot.getChild(unfiledRoot.childCount - 1);
Assert.equal(shortcutNode.bookmarkGuid, shortcutInfo.guid);
let toolbarInfo =
yield PlacesUtils.bookmarks.fetch(PlacesUtils.bookmarks.toolbarGuid);
Assert.equal(shortcutNode.title, toolbarInfo.title);
unfiledRoot.containerOpen = false;
});
function run_test() {
run_next_test();
}

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

@ -56,6 +56,7 @@ skip-if = os == "android"
[test_486978_sort_by_date_queries.js]
[test_536081.js]
[test_1085291.js]
[test_1105208.js]
[test_adaptive.js]
# Bug 676989: test hangs consistently on Android
skip-if = os == "android"

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

@ -8,33 +8,27 @@ Components.utils.import("resource://gre/modules/devtools/event-emitter.js");
const EXPORTED_SYMBOLS = ["Simulator"];
function getVersionNumber(fullVersion) {
return fullVersion.match(/(\d+\.\d+)/)[0];
}
const Simulator = {
_simulators: {},
register: function (label, simulator) {
register: function (name, simulator) {
// simulators register themselves as "Firefox OS X.Y"
let versionNumber = getVersionNumber(label);
this._simulators[versionNumber] = simulator;
this.emit("register", versionNumber);
this._simulators[name] = simulator;
this.emit("register", name);
},
unregister: function (label) {
let versionNumber = getVersionNumber(label);
delete this._simulators[versionNumber];
this.emit("unregister", versionNumber);
unregister: function (name) {
delete this._simulators[name];
this.emit("unregister", name);
},
availableVersions: function () {
availableNames: function () {
return Object.keys(this._simulators).sort();
},
getByVersion: function (version) {
return this._simulators[version];
}
getByName: function (name) {
return this._simulators[name];
},
};
EventEmitter.decorate(Simulator);