mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to a CLOSED TREE m-i
This commit is contained in:
commit
ef1df9316b
@ -132,6 +132,9 @@ window.addEventListener('ContentStart', function() {
|
||||
GlobalSimulatorScreen.width = width;
|
||||
GlobalSimulatorScreen.height = height;
|
||||
|
||||
Services.prefs.setCharPref('layout.css.devPixelsPerPx',
|
||||
ratio == 1 ? -1 : ratio);
|
||||
|
||||
// In order to do rescaling, we set the <browser> tag to the specified
|
||||
// width and height, and then use a CSS transform to scale it so that
|
||||
// it appears at the correct size on the host display. We also set
|
||||
@ -175,8 +178,6 @@ window.addEventListener('ContentStart', function() {
|
||||
style.transform +=
|
||||
' rotate(0.25turn) translate(-' + shift + 'px, -' + shift + 'px)';
|
||||
}
|
||||
|
||||
Services.prefs.setCharPref('layout.css.devPixelsPerPx', ratio);
|
||||
}
|
||||
|
||||
// Resize on startup
|
||||
|
@ -108,7 +108,7 @@ SettingsListener.observe('language.current', 'en-US', function(value) {
|
||||
Services.prefs.setCharPref(prefName, value);
|
||||
|
||||
if (shell.hasStarted() == false) {
|
||||
shell.start();
|
||||
shell.bootstrap();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -222,6 +222,20 @@ var shell = {
|
||||
return this._started;
|
||||
},
|
||||
|
||||
bootstrap: function() {
|
||||
let startManifestURL =
|
||||
Cc['@mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap']
|
||||
.getService(Ci.nsISupports).wrappedJSObject.startManifestURL;
|
||||
if (startManifestURL) {
|
||||
Cu.import('resource://gre/modules/Bootstraper.jsm');
|
||||
Bootstraper.ensureSystemAppInstall(startManifestURL)
|
||||
.then(this.start.bind(this))
|
||||
.catch(Bootstraper.bailout);
|
||||
} else {
|
||||
this.start();
|
||||
}
|
||||
},
|
||||
|
||||
start: function shell_start() {
|
||||
this._started = true;
|
||||
|
||||
|
@ -100,6 +100,11 @@ contract @mozilla.org/commandlinehandler/general-startup;1?type=b2gcmds {385993f
|
||||
category command-line-handler m-b2gcmds @mozilla.org/commandlinehandler/general-startup;1?type=b2gcmds
|
||||
#endif
|
||||
|
||||
# BootstrapCommandLine.js
|
||||
component {fd663ec8-cf3f-4c2b-aacb-17a6915ccb44} BootstrapCommandLine.js
|
||||
contract @mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap {fd663ec8-cf3f-4c2b-aacb-17a6915ccb44}
|
||||
category command-line-handler m-b2gbootstrap @mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap
|
||||
|
||||
# MobileIdentityUIGlue.js
|
||||
component {83dbe26a-81f3-4a75-9541-3d0b7ca496b5} MobileIdentityUIGlue.js
|
||||
contract @mozilla.org/services/mobileid-ui-glue;1 {83dbe26a-81f3-4a75-9541-3d0b7ca496b5}
|
||||
|
52
b2g/components/BootstrapCommandLine.js
Normal file
52
b2g/components/BootstrapCommandLine.js
Normal file
@ -0,0 +1,52 @@
|
||||
/* 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 { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
|
||||
function BootstrapCommandlineHandler() {
|
||||
this.wrappedJSObject = this;
|
||||
this.startManifestURL = null;
|
||||
}
|
||||
|
||||
BootstrapCommandlineHandler.prototype = {
|
||||
bailout: function(aMsg) {
|
||||
dump("************************************************************\n");
|
||||
dump("* /!\\ " + aMsg + "\n");
|
||||
dump("************************************************************\n");
|
||||
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
|
||||
.getService(Ci.nsIAppStartup);
|
||||
appStartup.quit(appStartup.eForceQuit);
|
||||
},
|
||||
|
||||
handle: function(aCmdLine) {
|
||||
this.startManifestURL = null;
|
||||
|
||||
try {
|
||||
// Returns null if the argument was not specified. Throws
|
||||
// NS_ERROR_INVALID_ARG if there is no parameter specified (because
|
||||
// it was the last argument or the next argument starts with '-').
|
||||
// However, someone could still explicitly pass an empty argument!
|
||||
this.startManifestURL = aCmdLine.handleFlagWithParam("start-manifest", false);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.startManifestURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isAbsoluteURI(this.startManifestURL)) {
|
||||
this.bailout("The start manifest url must be absolute.");
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
helpInfo: "--start-manifest=manifest_url",
|
||||
classID: Components.ID("{fd663ec8-cf3f-4c2b-aacb-17a6915ccb44}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BootstrapCommandlineHandler]);
|
147
b2g/components/Bootstraper.jsm
Normal file
147
b2g/components/Bootstraper.jsm
Normal file
@ -0,0 +1,147 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Bootstraper"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const CC = Components.Constructor;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Webapps.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
|
||||
function debug(aMsg) {
|
||||
//dump("-*- Bootstraper: " + aMsg + "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* This module loads the manifest for app from the --start-url enpoint and
|
||||
* ensures that it's installed as the system app.
|
||||
*/
|
||||
this.Bootstraper = {
|
||||
_manifestURL: null,
|
||||
_startupURL: null,
|
||||
|
||||
bailout: function(aMsg) {
|
||||
dump("************************************************************\n");
|
||||
dump("* /!\\ " + aMsg + "\n");
|
||||
dump("************************************************************\n");
|
||||
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
|
||||
.getService(Ci.nsIAppStartup);
|
||||
appStartup.quit(appStartup.eForceQuit);
|
||||
},
|
||||
|
||||
installSystemApp: function(aManifest) {
|
||||
// Get the appropriate startup url from the manifest launch_path.
|
||||
let base = Services.io.newURI(this._manifestURL, null, null);
|
||||
let origin = base.prePath;
|
||||
let helper = new ManifestHelper(aManifest, origin, this._manifestURL);
|
||||
this._startupURL = helper.fullLaunchPath();
|
||||
|
||||
return new Promise((aResolve, aReject) => {
|
||||
debug("Origin is " + origin);
|
||||
let appData = {
|
||||
app: {
|
||||
installOrigin: origin,
|
||||
origin: origin,
|
||||
manifest: aManifest,
|
||||
manifestURL: this._manifestURL,
|
||||
manifestHash: AppsUtils.computeHash(JSON.stringify(aManifest)),
|
||||
appStatus: Ci.nsIPrincipal.APP_STATUS_CERTIFIED
|
||||
},
|
||||
appId: 1,
|
||||
isBrowser: false,
|
||||
isPackage: false
|
||||
};
|
||||
|
||||
DOMApplicationRegistry.confirmInstall(appData, null, aResolve);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves to a json manifest.
|
||||
*/
|
||||
loadManifest: function() {
|
||||
return new Promise((aResolve, aReject) => {
|
||||
debug("Loading manifest " + this._manifestURL);
|
||||
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.mozBackgroundRequest = true;
|
||||
xhr.open("GET", this._manifestURL);
|
||||
xhr.responseType = "json";
|
||||
xhr.addEventListener("load", () => {
|
||||
if (xhr.status >= 200 && xhr.status < 400) {
|
||||
debug("Success loading " + this._manifestURL);
|
||||
aResolve(xhr.response);
|
||||
} else {
|
||||
aReject("Error loading " + this._manifestURL);
|
||||
}
|
||||
});
|
||||
xhr.addEventListener("error", () => {
|
||||
aReject("Error loading " + this._manifestURL);
|
||||
});
|
||||
xhr.send(null);
|
||||
});
|
||||
},
|
||||
|
||||
configure: function() {
|
||||
debug("Setting startup prefs... " + this._startupURL);
|
||||
Services.prefs.setCharPref("b2g.system_manifest_url", this._manifestURL);
|
||||
Services.prefs.setCharPref("b2g.system_startup_url", this._startupURL);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* If a system app is already installed, uninstall it so that we can
|
||||
* cleanly replace it by the current one.
|
||||
*/
|
||||
uninstallPreviousSystemApp: function() {
|
||||
let oldManifestURL;
|
||||
try{
|
||||
oldManifestURL = Services.prefs.getCharPref("b2g.system_manifest_url");
|
||||
} catch(e) {
|
||||
// No preference set, so nothing to uninstall.
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let id = DOMApplicationRegistry.getAppLocalIdByManifestURL(oldManifestURL);
|
||||
if (id == Ci.nsIScriptSecurityManager.NO_APP_ID) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
debug("Uninstalling " + oldManifestURL);
|
||||
return DOMApplicationRegistry.uninstall(oldManifestURL);
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves once we have installed the app.
|
||||
*/
|
||||
ensureSystemAppInstall: function(aManifestURL) {
|
||||
this._manifestURL = aManifestURL;
|
||||
debug("Installing app from " + this._manifestURL);
|
||||
|
||||
// Check if we are already configured to run from this manifest url, and
|
||||
// skip reinstall if that's the case.
|
||||
try {
|
||||
if (Services.prefs.getCharPref("b2g.system_manifest_url") == this._manifestURL) {
|
||||
debug("Already configured for " + this._manifestURL);
|
||||
return Promise.resolve();
|
||||
}
|
||||
} catch(e) { }
|
||||
|
||||
return new Promise((aResolve, aReject) => {
|
||||
DOMApplicationRegistry.registryReady
|
||||
.then(this.uninstallPreviousSystemApp.bind(this))
|
||||
.then(this.loadManifest.bind(this))
|
||||
.then(this.installSystemApp.bind(this))
|
||||
.then(this.configure.bind(this))
|
||||
.then(aResolve)
|
||||
.catch(aReject);
|
||||
});
|
||||
}
|
||||
};
|
@ -12,6 +12,7 @@ EXTRA_COMPONENTS += [
|
||||
'B2GAboutRedirector.js',
|
||||
'B2GAppMigrator.js',
|
||||
'B2GPresentationDevicePrompt.js',
|
||||
'BootstrapCommandLine.js',
|
||||
'ContentPermissionPrompt.js',
|
||||
'FilePicker.js',
|
||||
'FxAccountsUIGlue.js',
|
||||
@ -49,6 +50,7 @@ if CONFIG['MOZ_UPDATER']:
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'AlertsHelper.jsm',
|
||||
'Bootstraper.jsm',
|
||||
'ContentRequestHelper.jsm',
|
||||
'DebuggerActors.js',
|
||||
'ErrorPage.jsm',
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e06971db7acf7a35c32eb74d675a4e12e288e6be">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -116,7 +116,7 @@
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="cfcef469537869947abb9aa1d656774cc2678d4c"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="f0d6ce5f727eca4b10850c610a06a45772689a75"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="d3e2de81952c45d6ed658cdf367a6e7283b9c3ce"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="10e78a05252b3de785f88c2d0b9ea8a428009c50"/>
|
||||
<project name="platform/system/media" path="system/media" revision="7ff72c2ea2496fa50b5e8a915e56e901c3ccd240"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8fcd25d64f0f67d1a6f7037a4c83ce6d95466770"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
|
||||
@ -118,7 +118,7 @@
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
|
||||
<project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="f0d6ce5f727eca4b10850c610a06a45772689a75"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="d3e2de81952c45d6ed658cdf367a6e7283b9c3ce"/>
|
||||
<project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8fcd25d64f0f67d1a6f7037a4c83ce6d95466770"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e06971db7acf7a35c32eb74d675a4e12e288e6be">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -116,7 +116,7 @@
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="b562b01c93de9578d5db537b6a602a38e1aaa0ce"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="387f03e815f57d536dd922706db1622bddba8d81"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="f0d6ce5f727eca4b10850c610a06a45772689a75"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="d3e2de81952c45d6ed658cdf367a6e7283b9c3ce"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="5356165f67f4a81c2ef28671c13697f1657590df"/>
|
||||
<project name="platform/system/media" path="system/media" revision="be0e2fe59a8043fa5200f75697df9220a99abe9d"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8fcd25d64f0f67d1a6f7037a4c83ce6d95466770"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e06971db7acf7a35c32eb74d675a4e12e288e6be">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -111,7 +111,7 @@
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="69d524e80cdf3981006627c65ac85f3a871238a3"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="f0d6ce5f727eca4b10850c610a06a45772689a75"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="d3e2de81952c45d6ed658cdf367a6e7283b9c3ce"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="576f57b6510de59c08568b53c0fb60588be8689e"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8fcd25d64f0f67d1a6f7037a4c83ce6d95466770"/>
|
||||
<project name="platform/system/netd" path="system/netd" revision="a6531f7befb49b1c81bc0de7e51c5482b308e1c5"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"git": {
|
||||
"git_revision": "2535321f1bd55e68fd52291b193693a8995f8e62",
|
||||
"git_revision": "f74795eade46f4613741dd9e16fc43a905b40d2b",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "885e5176711ceb1401648c571a1a9290325582e7",
|
||||
"revision": "a0f2cafbae71c95e9bc97d253ecacbc079e1e138",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
|
||||
@ -118,7 +118,7 @@
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
|
||||
<project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="f0d6ce5f727eca4b10850c610a06a45772689a75"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="d3e2de81952c45d6ed658cdf367a6e7283b9c3ce"/>
|
||||
<project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8fcd25d64f0f67d1a6f7037a4c83ce6d95466770"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2535321f1bd55e68fd52291b193693a8995f8e62"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -477,6 +477,7 @@
|
||||
@BINPATH@/components/OopCommandLine.js
|
||||
@BINPATH@/components/CommandLine.js
|
||||
#endif
|
||||
@BINPATH@/components/BootstrapCommandLine.js
|
||||
|
||||
#ifdef MOZ_UPDATER
|
||||
@BINPATH@/components/nsUpdateService.manifest
|
||||
|
@ -1173,7 +1173,9 @@ toolbarpaletteitem[dragover] {
|
||||
|
||||
toolbarpaletteitem[place="palette"] {
|
||||
width: 10em;
|
||||
height: calc(40px + 2em);
|
||||
/* icon (32) + margin (2 * 4) + button padding/border (2 * 4) + label margin (~2) + label
|
||||
* line-height (1.5em): */
|
||||
height: calc(50px + 1.5em);
|
||||
margin-bottom: 5px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
|
@ -208,16 +208,20 @@ PlayerWidget.prototype = {
|
||||
titleHTML += L10N.getStr("player.animationDurationLabel");
|
||||
titleHTML += "<strong>" + L10N.getFormatStr("player.timeLabel",
|
||||
this.getFormattedTime(state.duration)) + "</strong>";
|
||||
|
||||
if (state.delay) {
|
||||
titleHTML += L10N.getStr("player.animationDelayLabel");
|
||||
titleHTML += "<strong>" + L10N.getFormatStr("player.timeLabel",
|
||||
this.getFormattedTime(state.delay)) + "</strong>";
|
||||
}
|
||||
titleHTML += L10N.getStr("player.animationIterationCountLabel");
|
||||
let count = state.iterationCount || L10N.getStr("player.infiniteIterationCount");
|
||||
titleHTML += "<strong>" + count + "</strong>";
|
||||
titleHTML += "</span>"
|
||||
|
||||
if (state.iterationCount !== 1) {
|
||||
titleHTML += L10N.getStr("player.animationIterationCountLabel");
|
||||
let count = state.iterationCount || L10N.getStr("player.infiniteIterationCount");
|
||||
titleHTML += "<strong>" + count + "</strong>";
|
||||
}
|
||||
|
||||
titleHTML += "</span>";
|
||||
titleEl.innerHTML = titleHTML;
|
||||
|
||||
// Timeline widget.
|
||||
@ -254,9 +258,9 @@ PlayerWidget.prototype = {
|
||||
}
|
||||
});
|
||||
|
||||
let max = state.duration; // Infinite iterations.
|
||||
let max = state.duration;
|
||||
if (state.iterationCount) {
|
||||
// Finite iterations.
|
||||
// If there's a finite nb of iterations.
|
||||
max = state.iterationCount * state.duration;
|
||||
}
|
||||
|
||||
@ -285,10 +289,11 @@ PlayerWidget.prototype = {
|
||||
"class": "time-display"
|
||||
}
|
||||
});
|
||||
this.timeDisplayEl.textContent = L10N.getFormatStr("player.timeLabel",
|
||||
this.getFormattedTime(0));
|
||||
|
||||
this.containerEl.appendChild(this.el);
|
||||
|
||||
// Show the initial time.
|
||||
this.displayTime(state.currentTime);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -323,11 +328,13 @@ PlayerWidget.prototype = {
|
||||
*/
|
||||
onStateChanged: function() {
|
||||
let state = this.player.state;
|
||||
this.updatePlayPauseButton(state.playState);
|
||||
this.updateWidgetState(state.playState);
|
||||
|
||||
switch (state.playState) {
|
||||
case "finished":
|
||||
this.destroy();
|
||||
this.stopTimelineAnimation();
|
||||
this.displayTime(this.player.state.duration);
|
||||
this.stopListeners();
|
||||
break;
|
||||
case "running":
|
||||
this.startTimelineAnimation();
|
||||
@ -347,7 +354,7 @@ PlayerWidget.prototype = {
|
||||
pause: function() {
|
||||
// Switch to the right className on the element right away to avoid waiting
|
||||
// for the next state update to change the playPause icon.
|
||||
this.updatePlayPauseButton("paused");
|
||||
this.updateWidgetState("paused");
|
||||
return this.player.pause().then(() => {
|
||||
this.stopTimelineAnimation();
|
||||
});
|
||||
@ -361,12 +368,12 @@ PlayerWidget.prototype = {
|
||||
play: function() {
|
||||
// Switch to the right className on the element right away to avoid waiting
|
||||
// for the next state update to change the playPause icon.
|
||||
this.updatePlayPauseButton("running");
|
||||
this.updateWidgetState("running");
|
||||
this.startTimelineAnimation();
|
||||
return this.player.play();
|
||||
},
|
||||
|
||||
updatePlayPauseButton: function(playState) {
|
||||
updateWidgetState: function(playState) {
|
||||
this.el.className = "player-widget " + playState;
|
||||
},
|
||||
|
||||
@ -377,10 +384,12 @@ PlayerWidget.prototype = {
|
||||
startTimelineAnimation: function() {
|
||||
this.stopTimelineAnimation();
|
||||
|
||||
let state = this.player.state;
|
||||
|
||||
let start = performance.now();
|
||||
let loop = () => {
|
||||
this.rafID = requestAnimationFrame(loop);
|
||||
let now = this.player.state.currentTime + performance.now() - start;
|
||||
let now = state.currentTime + performance.now() - start;
|
||||
this.displayTime(now);
|
||||
};
|
||||
|
||||
@ -399,13 +408,22 @@ PlayerWidget.prototype = {
|
||||
time = Math.max(0, time - state.delay);
|
||||
}
|
||||
|
||||
// For finite animations, make sure the displayed time does not go beyond
|
||||
// the animation total duration (this may happen due to the local
|
||||
// requestAnimationFrame loop).
|
||||
if (state.iterationCount) {
|
||||
time = Math.min(time, state.iterationCount * state.duration);
|
||||
}
|
||||
|
||||
// Set the time label value.
|
||||
this.timeDisplayEl.textContent = L10N.getFormatStr("player.timeLabel",
|
||||
this.getFormattedTime(time));
|
||||
|
||||
// Set the timeline slider value.
|
||||
if (!state.iterationCount && time !== state.duration) {
|
||||
this.currentTimeEl.value = time % state.duration;
|
||||
} else {
|
||||
this.currentTimeEl.value = time;
|
||||
time = time % state.duration;
|
||||
}
|
||||
this.currentTimeEl.value = time;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -6,15 +6,19 @@ support-files =
|
||||
head.js
|
||||
|
||||
[browser_animation_empty_on_invalid_nodes.js]
|
||||
[browser_animation_iterationCount_hidden_by_default.js]
|
||||
[browser_animation_panel_exists.js]
|
||||
[browser_animation_participate_in_inspector_update.js]
|
||||
[browser_animation_play_pause_button.js]
|
||||
[browser_animation_playerFronts_are_refreshed.js]
|
||||
[browser_animation_playerWidgets_destroy.js]
|
||||
[browser_animation_playerWidgets_disables_on_finished.js]
|
||||
[browser_animation_playerWidgets_dont_show_time_after_duration.js]
|
||||
[browser_animation_playerWidgets_meta_data.js]
|
||||
[browser_animation_playerWidgets_state_after_pause.js]
|
||||
[browser_animation_refresh_when_active.js]
|
||||
[browser_animation_same_nb_of_playerWidgets_and_playerFronts.js]
|
||||
[browser_animation_shows_player_on_valid_node.js]
|
||||
[browser_animation_timeline_animates.js]
|
||||
[browser_animation_timeline_waits_for_delay.js]
|
||||
[browser_animation_ui_updates_when_animation_changes.js]
|
||||
[browser_animation_ui_updates_when_animation_changes.js]
|
||||
|
@ -0,0 +1,24 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Check that iteration count is only shown in the UI when it's different than 1
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
let {inspector, panel} = yield openAnimationInspector();
|
||||
|
||||
info("Selecting a node with an animation that doesn't repeat");
|
||||
yield selectNode(".long", inspector);
|
||||
let widget = panel.playerWidgets[0];
|
||||
let metaDataLabels = widget.el.querySelectorAll(".animation-title .meta-data strong");
|
||||
is(metaDataLabels.length, 1, "Only the duration is shown");
|
||||
|
||||
info("Selecting a node with an animation that repeats several times");
|
||||
yield selectNode(".delayed", inspector);
|
||||
widget = panel.playerWidgets[0];
|
||||
let iterationLabel = widget.el.querySelectorAll(".animation-title .meta-data strong")[2];
|
||||
is(iterationLabel.textContent, "10", "The iteration is shown");
|
||||
});
|
@ -0,0 +1,40 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that when animations end, the corresponding player widgets are disabled.
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
let {inspector, panel, controller} = yield openAnimationInspector();
|
||||
|
||||
info("Apply 2 finite animations to the test node");
|
||||
getNode(".still").classList.add("multi-finite");
|
||||
|
||||
info("Select the test node");
|
||||
yield selectNode(".still", inspector);
|
||||
|
||||
is(controller.animationPlayers.length, 2, "2 animation players exist");
|
||||
|
||||
info("Wait for both animations to end");
|
||||
|
||||
let promises = controller.animationPlayers.map(front => {
|
||||
let def = promise.defer();
|
||||
let onStateChanged = () => {
|
||||
if (front.state.playState === "finished") {
|
||||
front.off(front.AUTO_REFRESH_EVENT, onStateChanged);
|
||||
def.resolve();
|
||||
}
|
||||
};
|
||||
front.on(front.AUTO_REFRESH_EVENT, onStateChanged);
|
||||
return def.promise;
|
||||
});
|
||||
|
||||
yield promise.all(promises);
|
||||
|
||||
for (let widgetEl of panel.playersEl.querySelectorAll(".player-widget")) {
|
||||
ok(widgetEl.classList.contains("finished"), "The player widget has the right class");
|
||||
}
|
||||
});
|
@ -0,0 +1,40 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that after the animation has ended, the current time label and timeline
|
||||
// slider don't show values bigger than the animation duration (which would
|
||||
// happen if the local requestAnimationFrame loop didn't stop correctly).
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
let {inspector, panel} = yield openAnimationInspector();
|
||||
|
||||
info("Start an animation on the test node");
|
||||
getNode(".still").classList.add("short");
|
||||
|
||||
info("Select the node");
|
||||
yield selectNode(".still", inspector);
|
||||
|
||||
info("Wait until the animation ends");
|
||||
let widget = panel.playerWidgets[0];
|
||||
let front = widget.player;
|
||||
|
||||
let def = promise.defer();
|
||||
let onStateChanged = () => {
|
||||
if (front.state.playState === "finished") {
|
||||
front.off(front.AUTO_REFRESH_EVENT, onStateChanged);
|
||||
def.resolve();
|
||||
}
|
||||
};
|
||||
front.on(front.AUTO_REFRESH_EVENT, onStateChanged);
|
||||
yield def.promise;
|
||||
|
||||
is(widget.currentTimeEl.value, front.state.duration,
|
||||
"The timeline slider has the right value");
|
||||
is(widget.timeDisplayEl.textContent,
|
||||
widget.getFormattedTime(front.state.duration) + "s",
|
||||
"The timeline slider has the right value");
|
||||
});
|
@ -0,0 +1,30 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that once an animation is paused and its widget is refreshed, the right
|
||||
// initial time is displayed.
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
let {inspector, panel} = yield openAnimationInspector();
|
||||
|
||||
info("Selecting the test node");
|
||||
yield selectNode(".animated", inspector);
|
||||
|
||||
info("Pausing the animation by using the widget");
|
||||
let widget = panel.playerWidgets[0];
|
||||
yield widget.pause();
|
||||
|
||||
info("Selecting another node and then the same node again to refresh the widget");
|
||||
yield selectNode(".still", inspector);
|
||||
yield selectNode(".animated", inspector);
|
||||
|
||||
widget = panel.playerWidgets[0];
|
||||
ok(widget.el.classList.contains("paused"), "The widget is still in paused mode");
|
||||
is(widget.timeDisplayEl.textContent,
|
||||
widget.getFormattedTime(widget.player.state.currentTime) + "s",
|
||||
"The initial time has been set to the player's");
|
||||
});
|
@ -40,6 +40,31 @@
|
||||
animation: simple-animation 3s 60s 10;
|
||||
}
|
||||
|
||||
.multi-finite {
|
||||
top: 400px;
|
||||
left: 10px;
|
||||
background: yellow;
|
||||
|
||||
animation: simple-animation 3s,
|
||||
other-animation 4s;
|
||||
}
|
||||
|
||||
.short {
|
||||
top: 500px;
|
||||
left: 10px;
|
||||
background: red;
|
||||
|
||||
animation: simple-animation 2s
|
||||
}
|
||||
|
||||
.long {
|
||||
top: 600px;
|
||||
left: 10px;
|
||||
background: blue;
|
||||
|
||||
animation: simple-animation 120s
|
||||
}
|
||||
|
||||
@keyframes simple-animation {
|
||||
100% {
|
||||
transform: translateX(300px);
|
||||
@ -59,5 +84,8 @@
|
||||
<div class="ball animated"></div>
|
||||
<div class="ball multi"></div>
|
||||
<div class="ball delayed"></div>
|
||||
<div class="ball multi-finite"></div>
|
||||
<div class="ball short"></div>
|
||||
<div class="ball long"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -26,6 +26,9 @@ const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
|
||||
const WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
|
||||
const l10n = new WebConsoleUtils.l10n(STRINGS_URI);
|
||||
|
||||
const MAX_STRING_GRIP_LENGTH = 36;
|
||||
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
|
||||
|
||||
// Constants for compatibility with the Web Console output implementation before
|
||||
// bug 778766.
|
||||
// TODO: remove these once bug 778766 is fixed.
|
||||
@ -1126,6 +1129,29 @@ Messages.Extended.prototype = Heritage.extend(Messages.Simple.prototype,
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Shorten grips of the type string, leaves other grips unmodified.
|
||||
*
|
||||
* @param object grip
|
||||
* Value grip from the server.
|
||||
* @return object
|
||||
* Possible values of object:
|
||||
* - A shortened string, if original grip was of string type.
|
||||
* - The unmodified input grip, if it wasn't of string type.
|
||||
*/
|
||||
shortenValueGrip: function(grip)
|
||||
{
|
||||
let shortVal = grip;
|
||||
if (typeof(grip)=="string") {
|
||||
shortVal = grip.replace(/(\r\n|\n|\r)/gm," ");
|
||||
if (shortVal.length > MAX_STRING_GRIP_LENGTH) {
|
||||
shortVal = shortVal.substring(0,MAX_STRING_GRIP_LENGTH - 1) + ELLIPSIS;
|
||||
}
|
||||
}
|
||||
|
||||
return shortVal;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a CodeMirror-compatible class name for a given value grip.
|
||||
*
|
||||
@ -2356,7 +2382,8 @@ Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
|
||||
if (valueIsText) {
|
||||
this._text(value);
|
||||
} else {
|
||||
let valueElem = this.message._renderValueGrip(value, { concise: true });
|
||||
let shortVal = this.message.shortenValueGrip(value);
|
||||
let valueElem = this.message._renderValueGrip(shortVal, { concise: true });
|
||||
container.appendChild(valueElem);
|
||||
}
|
||||
},
|
||||
@ -2676,7 +2703,9 @@ Widgets.ObjectRenderers.add({
|
||||
this._renderEmptySlots(emptySlots);
|
||||
emptySlots = 0;
|
||||
}
|
||||
let elem = this.message._renderValueGrip(item, { concise: true });
|
||||
|
||||
let shortVal = this.message.shortenValueGrip(item);
|
||||
let elem = this.message._renderValueGrip(shortVal, { concise: true });
|
||||
this.element.appendChild(elem);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
// Test the webconsole output for various types of objects.
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf8,test for console output - 05";
|
||||
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
|
||||
|
||||
let dateNow = Date.now();
|
||||
|
||||
@ -103,6 +104,14 @@ let inputTests = [
|
||||
printOutput: "[object Promise]",
|
||||
inspectable: true,
|
||||
variablesViewLabel: "Promise"
|
||||
},
|
||||
|
||||
//11
|
||||
{
|
||||
input: "new Object({1: 'this\\nis\\nsupposed\\nto\\nbe\\na\\nvery\\nlong\\nstring\\n,shown\\non\\na\\nsingle\\nline', 2: 'a shorter string', 3: 100})",
|
||||
output: 'Object { 1: "this is supposed to be a very long ' + ELLIPSIS + '", 2: "a shorter string", 3: 100 }',
|
||||
printOutput: "[object Object]",
|
||||
inspectable: false,
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -6,6 +6,9 @@
|
||||
// Test the webconsole output for various arrays.
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf8,test for console output - 06";
|
||||
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
|
||||
const test_str_in = "SHOW\\nALL\\nOF\\nTHIS\\nON\\nA\\nSINGLE\\nLINE ONLY. ESCAPE ALL NEWLINE";
|
||||
const test_str_out = "SHOW ALL OF THIS ON A SINGLE LINE O" + ELLIPSIS;
|
||||
|
||||
let inputTests = [
|
||||
// 1 - array with empty slots only
|
||||
@ -97,6 +100,15 @@ let inputTests = [
|
||||
printOutput: "0,,,3,4,5",
|
||||
inspectable: true,
|
||||
variablesViewLabel: "Array[6]"
|
||||
},
|
||||
|
||||
//12 - array with long strings as elements
|
||||
{
|
||||
input: '["' + test_str_in + '", "' + test_str_in + '", "' + test_str_in + '"]',
|
||||
output: 'Array [ "' + test_str_out + '", "' + test_str_out + '", "' + test_str_out + '" ]',
|
||||
inspectable: false,
|
||||
printOutput: "SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\nLINE ONLY. ESCAPE ALL NEWLINE,SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\nLINE ONLY. ESCAPE ALL NEWLINE,SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\nLINE ONLY. ESCAPE ALL NEWLINE",
|
||||
variablesViewLabel: "Array[3]"
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -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>
|
||||
|
90
browser/devtools/webide/content/devicesettings.js
Normal file
90
browser/devtools/webide/content/devicesettings.js
Normal file
@ -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();
|
||||
}
|
||||
}
|
50
browser/devtools/webide/content/devicesettings.xhtml
Normal file
50
browser/devtools/webide/content/devicesettings.xhtml
Normal file
@ -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();
|
||||
|
360
browser/devtools/webide/modules/config-view.js
Normal file
360
browser/devtools/webide/modules/config-view.js
Normal file
@ -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]
|
||||
|
219
browser/devtools/webide/test/device_front_shared.js
Normal file
219
browser/devtools/webide/test/device_front_shared.js
Normal file
@ -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();
|
||||
|
89
browser/devtools/webide/test/test_device_settings.html
Normal file
89
browser/devtools/webide/test/test_device_settings.html
Normal file
@ -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
|
||||
|
@ -43,6 +43,13 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
/* Disabled playerWidget when the animation has ended */
|
||||
|
||||
.finished {
|
||||
pointer-events: none;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/* Animation title gutter, contains the name, duration, iteration */
|
||||
|
||||
.animation-title {
|
||||
@ -91,7 +98,8 @@ body {
|
||||
background-image: url(debugger-pause.png);
|
||||
}
|
||||
|
||||
.paused .timeline .toggle::before {
|
||||
.paused .timeline .toggle::before,
|
||||
.finished .timeline .toggle::before {
|
||||
background-image: url(debugger-play.png);
|
||||
}
|
||||
|
||||
@ -100,7 +108,8 @@ body {
|
||||
background-image: url(debugger-pause@2x.png);
|
||||
}
|
||||
|
||||
.paused .timeline .toggle::before {
|
||||
.paused .timeline .toggle::before,
|
||||
.finished .timeline .toggle::before {
|
||||
background-image: url(debugger-play@2x.png);
|
||||
}
|
||||
}
|
||||
|
@ -491,7 +491,8 @@ RtspMediaResource::RtspMediaResource(MediaDecoder* aDecoder,
|
||||
nsIChannel* aChannel, nsIURI* aURI, const nsACString& aContentType)
|
||||
: BaseMediaResource(aDecoder, aChannel, aURI, aContentType)
|
||||
, mIsConnected(false)
|
||||
, mRealTime(false)
|
||||
, mIsLiveStream(false)
|
||||
, mHasTimestamp(true)
|
||||
, mIsSuspend(true)
|
||||
{
|
||||
#ifndef NECKO_PROTOCOL_rtsp
|
||||
@ -639,9 +640,6 @@ RtspMediaResource::OnMediaDataAvailable(uint8_t aTrackIdx,
|
||||
uint32_t frameType;
|
||||
meta->GetTimeStamp(&time);
|
||||
meta->GetFrameType(&frameType);
|
||||
if (mRealTime) {
|
||||
time = 0;
|
||||
}
|
||||
mTrackBuffer[aTrackIdx]->WriteBuffer(data.BeginReading(), length, time,
|
||||
frameType);
|
||||
return NS_OK;
|
||||
@ -727,7 +725,7 @@ RtspMediaResource::OnConnected(uint8_t aTrackIdx,
|
||||
// If the durationUs is 0, imply the stream is live stream.
|
||||
if (durationUs) {
|
||||
// Not live stream.
|
||||
mRealTime = false;
|
||||
mIsLiveStream = false;
|
||||
mDecoder->SetInfinite(false);
|
||||
mDecoder->SetDuration((double)(durationUs) / USECS_PER_S);
|
||||
} else {
|
||||
@ -740,7 +738,7 @@ RtspMediaResource::OnConnected(uint8_t aTrackIdx,
|
||||
NS_DispatchToMainThread(event);
|
||||
return NS_ERROR_FAILURE;
|
||||
} else {
|
||||
mRealTime = true;
|
||||
mIsLiveStream = true;
|
||||
bool seekable = false;
|
||||
mDecoder->SetInfinite(true);
|
||||
mDecoder->SetMediaSeekable(seekable);
|
||||
|
@ -92,8 +92,10 @@ public:
|
||||
return mMediaStreamController;
|
||||
}
|
||||
|
||||
// Even it is a live stream, as long as it provides valid timestamps,
|
||||
// we tell state machine it's not a live stream.
|
||||
virtual bool IsRealTime() MOZ_OVERRIDE {
|
||||
return mRealTime;
|
||||
return !mHasTimestamp;
|
||||
}
|
||||
|
||||
// Called by RtspOmxReader, dispatch a runnable to notify mDecoder.
|
||||
@ -154,7 +156,7 @@ public:
|
||||
virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { *aIsReliable = false; return 0; }
|
||||
|
||||
virtual int64_t GetLength() MOZ_OVERRIDE {
|
||||
if (mRealTime) {
|
||||
if (mIsLiveStream) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@ -247,8 +249,10 @@ private:
|
||||
// A flag that indicates the |RtspMediaResource::OnConnected| has already been
|
||||
// called.
|
||||
bool mIsConnected;
|
||||
// live stream
|
||||
bool mRealTime;
|
||||
// Whether it's a live stream.
|
||||
bool mIsLiveStream;
|
||||
// Whether it provides timestamps.
|
||||
bool mHasTimestamp;
|
||||
// Indicate the rtsp controller is suspended or not. Main thread only.
|
||||
bool mIsSuspend;
|
||||
};
|
||||
|
@ -134,7 +134,7 @@ NfcContentHelper.prototype = {
|
||||
.getService(Ci.nsIXULRuntime)
|
||||
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
||||
if (inParent) {
|
||||
this._tabId = -1;
|
||||
this._tabId = Ci.nsINfcBrowserAPI.SYSTEM_APP_ID;
|
||||
} else {
|
||||
throw Components.Exception("Can't get tab id in child process",
|
||||
Cr.NS_ERROR_UNEXPECTED);
|
||||
|
@ -181,7 +181,7 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
||||
this.focusApp = id;
|
||||
} else if (this.focusApp == id){
|
||||
// Set focusApp to null means currently there is no foreground app.
|
||||
this.focusApp = null;
|
||||
this.focusApp = NFC.SYSTEM_APP_ID;
|
||||
}
|
||||
},
|
||||
|
||||
@ -228,27 +228,30 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
||||
},
|
||||
|
||||
onTagFound: function onTagFound(message) {
|
||||
let target = this.eventListeners[this.focusApp] ||
|
||||
this.eventListeners[NFC.SYSTEM_APP_ID];
|
||||
|
||||
message.event = NFC.TAG_EVENT_FOUND;
|
||||
for (let id in this.eventListeners) {
|
||||
this.notifyDOMEvent(this.eventListeners[id], message);
|
||||
}
|
||||
|
||||
this.notifyDOMEvent(target, message);
|
||||
|
||||
delete message.event;
|
||||
},
|
||||
|
||||
onTagLost: function onTagLost(sessionToken) {
|
||||
for (let id in this.eventListeners) {
|
||||
this.notifyDOMEvent(this.eventListeners[id],
|
||||
{ event: NFC.TAG_EVENT_LOST,
|
||||
sessionToken: sessionToken });
|
||||
}
|
||||
let target = this.eventListeners[this.focusApp] ||
|
||||
this.eventListeners[NFC.SYSTEM_APP_ID];
|
||||
|
||||
this.notifyDOMEvent(target, { event: NFC.TAG_EVENT_LOST,
|
||||
sessionToken: sessionToken });
|
||||
},
|
||||
|
||||
onPeerEvent: function onPeerEvent(eventType, sessionToken) {
|
||||
for (let id in this.eventListeners) {
|
||||
this.notifyDOMEvent(this.eventListeners[id],
|
||||
{ event: eventType,
|
||||
sessionToken: sessionToken });
|
||||
}
|
||||
let target = this.eventListeners[this.focusApp] ||
|
||||
this.eventListeners[NFC.SYSTEM_APP_ID];
|
||||
|
||||
this.notifyDOMEvent(target, { event: eventType,
|
||||
sessionToken: sessionToken });
|
||||
},
|
||||
|
||||
onRFStateChange: function onRFStateChange(rfState) {
|
||||
|
@ -46,5 +46,8 @@ this.TAG_EVENT_LOST = 0x04;
|
||||
this.PEER_EVENT_FOUND = 0x05;
|
||||
this.RF_EVENT_STATE_CHANGE = 0x06;
|
||||
|
||||
// This value should sync with |SYSTEM_APP_ID| in nsINfcContentHelper.idl
|
||||
this.SYSTEM_APP_ID = -1;
|
||||
|
||||
// Allow this file to be imported via Components.utils.import().
|
||||
this.EXPORTED_SYMBOLS = Object.keys(this);
|
||||
|
@ -102,9 +102,11 @@ interface nsINfcRequestCallback : nsISupports
|
||||
void notifyError(in DOMString errorMsg);
|
||||
};
|
||||
|
||||
[scriptable, uuid(7ae46728-bc42-44bd-8093-bc7563abf52d)]
|
||||
[scriptable, uuid(7ae94107-adc9-4467-ae44-72f9a91f3ee8)]
|
||||
interface nsINfcBrowserAPI : nsISupports
|
||||
{
|
||||
const int32_t SYSTEM_APP_ID = -1;
|
||||
|
||||
void setFocusApp(in uint64_t tabId,
|
||||
in boolean isFocus);
|
||||
};
|
||||
|
@ -2954,7 +2954,7 @@ RilObject.prototype = {
|
||||
GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_COMMAND_DETAILS |
|
||||
COMPREHENSIONTLV_FLAG_CR);
|
||||
GsmPDUHelper.writeHexOctet(3);
|
||||
if (response.command) {
|
||||
if (command) {
|
||||
GsmPDUHelper.writeHexOctet(command.commandNumber);
|
||||
GsmPDUHelper.writeHexOctet(command.typeOfCommand);
|
||||
GsmPDUHelper.writeHexOctet(command.commandQualifier);
|
||||
@ -3010,52 +3010,46 @@ RilObject.prototype = {
|
||||
// No need to process Text data if user requests help information.
|
||||
if (response.resultCode != STK_RESULT_HELP_INFO_REQUIRED) {
|
||||
let text;
|
||||
if (response.isYesNo !== undefined) {
|
||||
// GET_INKEY
|
||||
// When the ME issues a successful TERMINAL RESPONSE for a GET INKEY
|
||||
// ("Yes/No") command with command qualifier set to "Yes/No", it shall
|
||||
// supply the value '01' when the answer is "positive" and the value
|
||||
// '00' when the answer is "negative" in the Text string data object.
|
||||
text = response.isYesNo ? String.fromCharCode(0x01)
|
||||
: String.fromCharCode(0x00);
|
||||
} else {
|
||||
text = response.input;
|
||||
}
|
||||
|
||||
if (text !== undefined) {
|
||||
GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_TEXT_STRING |
|
||||
COMPREHENSIONTLV_FLAG_CR);
|
||||
|
||||
// 2nd mark for text length
|
||||
Buf.startCalOutgoingSize(function(size) {
|
||||
// Text length is in number of hexOctets, which costs 4 uint8 per hexOctet.
|
||||
GsmPDUHelper.writeHexOctet(size / 4);
|
||||
});
|
||||
|
||||
let coding = command.options.isUCS2 ?
|
||||
let coding = command.options.isUCS2 ?
|
||||
STK_TEXT_CODING_UCS2 :
|
||||
(command.options.isPacked ?
|
||||
STK_TEXT_CODING_GSM_7BIT_PACKED :
|
||||
STK_TEXT_CODING_GSM_8BIT);
|
||||
if (response.isYesNo !== undefined) {
|
||||
// Tag: GET_INKEY
|
||||
// When the ME issues a successful TERMINAL RESPONSE for a GET INKEY
|
||||
// ("Yes/No") command with command qualifier set to "Yes/No", it shall
|
||||
// supply the value '01' when the answer is "positive" and the value
|
||||
// '00' when the answer is "negative" in the Text string data object.
|
||||
GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_TEXT_STRING |
|
||||
COMPREHENSIONTLV_FLAG_CR);
|
||||
// Length: 2
|
||||
GsmPDUHelper.writeHexOctet(2);
|
||||
// Value: Coding, Yes/No.
|
||||
GsmPDUHelper.writeHexOctet(coding);
|
||||
|
||||
// Write Text String.
|
||||
switch (coding) {
|
||||
case STK_TEXT_CODING_UCS2:
|
||||
GsmPDUHelper.writeUCS2String(text);
|
||||
break;
|
||||
case STK_TEXT_CODING_GSM_7BIT_PACKED:
|
||||
GsmPDUHelper.writeStringAsSeptets(text, 0, 0, 0);
|
||||
break;
|
||||
case STK_TEXT_CODING_GSM_8BIT:
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
GsmPDUHelper.writeHexOctet(text.charCodeAt(i));
|
||||
}
|
||||
break;
|
||||
GsmPDUHelper.writeHexOctet(response.isYesNo ? 0x01 : 0x00);
|
||||
} else {
|
||||
if (response.input !== undefined) {
|
||||
ComprehensionTlvHelper.writeTextStringTlv(response.input, coding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate and write text length to 2nd mark
|
||||
Buf.stopCalOutgoingSize();
|
||||
// Duration
|
||||
if (response.resultCode === STK_RESULT_NO_RESPONSE_FROM_USER) {
|
||||
// In TS102 223, 6.4.2 GET INKEY, "if the UICC requests a variable timeout,
|
||||
// the terminal shall wait until either the user enters a single character
|
||||
// or the timeout expires. The timer starts when the text is displayed on
|
||||
// the screen and stops when the TERMINAL RESPONSE is sent. The terminal
|
||||
// shall pass the total display text duration (command execution duration)
|
||||
// to the UICC using the TERMINAL RESPONSE. The time unit of the response
|
||||
// is identical to the time unit of the requested variable timeout."
|
||||
let duration = command && command.options && command.options.duration;
|
||||
if (duration) {
|
||||
GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_DURATION);
|
||||
GsmPDUHelper.writeHexOctet(2);
|
||||
GsmPDUHelper.writeHexOctet(duration.timeUnit);
|
||||
GsmPDUHelper.writeHexOctet(duration.timeInterval);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7205,6 +7199,36 @@ GsmPDUHelperObject.prototype = {
|
||||
return array;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to write data into a temporary buffer for easier length encoding when
|
||||
* the number of octets for the length encoding is varied.
|
||||
*
|
||||
* @param writeFunction
|
||||
* Function of how the data to be written into temporary buffer.
|
||||
*
|
||||
* @return array of written octets.
|
||||
**/
|
||||
writeWithBuffer: function(writeFunction) {
|
||||
let buf = [];
|
||||
let writeHexOctet = this.writeHexOctet;
|
||||
this.writeHexOctet = function(octet) {
|
||||
buf.push(octet);
|
||||
}
|
||||
|
||||
try {
|
||||
writeFunction();
|
||||
} catch (e) {
|
||||
if (DEBUG) {
|
||||
debug("Error when writeWithBuffer: " + e);
|
||||
}
|
||||
buf = [];
|
||||
} finally {
|
||||
this.writeHexOctet = writeHexOctet;
|
||||
}
|
||||
|
||||
return buf;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert an octet (number) to a BCD number.
|
||||
*
|
||||
@ -7557,6 +7581,28 @@ GsmPDUHelperObject.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
writeStringAs8BitUnpacked: function(text) {
|
||||
const langTable = PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
|
||||
const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
|
||||
|
||||
let len = text ? text.length : 0;
|
||||
for (let i = 0; i < len; i++) {
|
||||
let c = text.charAt(i);
|
||||
let octet = langTable.indexOf(c);
|
||||
|
||||
if (octet == -1) {
|
||||
octet = langShiftTable.indexOf(c);
|
||||
if (octet == -1) {
|
||||
// Fallback to ASCII space.
|
||||
octet = langTable.indexOf(' ');
|
||||
} else {
|
||||
this.writeHexOctet(PDU_NL_EXTENDED_ESCAPE);
|
||||
}
|
||||
}
|
||||
this.writeHexOctet(octet);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Read user data and decode as a UCS2 string.
|
||||
*
|
||||
@ -10389,9 +10435,10 @@ ICCPDUHelperObject.prototype = {
|
||||
if (octet == -1) {
|
||||
// Fallback to ASCII space.
|
||||
octet = langTable.indexOf(' ');
|
||||
} else {
|
||||
GsmPDUHelper.writeHexOctet(PDU_NL_EXTENDED_ESCAPE);
|
||||
j++;
|
||||
}
|
||||
GsmPDUHelper.writeHexOctet(PDU_NL_EXTENDED_ESCAPE);
|
||||
j++;
|
||||
}
|
||||
GsmPDUHelper.writeHexOctet(octet);
|
||||
j++;
|
||||
@ -12166,6 +12213,40 @@ ComprehensionTlvHelperObject.prototype = {
|
||||
GsmPDUHelper.writeSwappedNibbleBCDNum(seconds % 60);
|
||||
},
|
||||
|
||||
writeTextStringTlv: function(text, coding) {
|
||||
let GsmPDUHelper = this.context.GsmPDUHelper;
|
||||
let buf = GsmPDUHelper.writeWithBuffer(() => {
|
||||
// Write Coding.
|
||||
GsmPDUHelper.writeHexOctet(coding);
|
||||
|
||||
// Write Text String.
|
||||
switch (coding) {
|
||||
case STK_TEXT_CODING_UCS2:
|
||||
GsmPDUHelper.writeUCS2String(text);
|
||||
break;
|
||||
case STK_TEXT_CODING_GSM_7BIT_PACKED:
|
||||
GsmPDUHelper.writeStringAsSeptets(text, 0, 0, 0);
|
||||
break;
|
||||
case STK_TEXT_CODING_GSM_8BIT:
|
||||
GsmPDUHelper.writeStringAs8BitUnpacked(text);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
let length = buf.length;
|
||||
if (length) {
|
||||
// Write Tag.
|
||||
GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_TEXT_STRING |
|
||||
COMPREHENSIONTLV_FLAG_CR);
|
||||
// Write Length.
|
||||
this.writeLength(length);
|
||||
// Write Value.
|
||||
for (let i = 0; i < length; i++) {
|
||||
GsmPDUHelper.writeHexOctet(buf[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getSizeOfLengthOctets: function(length) {
|
||||
if (length >= 0x10000) {
|
||||
return 4; // 0x83, len_1, len_2, len_3
|
||||
|
@ -227,6 +227,163 @@ add_test(function test_stk_terminal_response_get_input_empty_string() {
|
||||
context.RIL.sendStkTerminalResponse(response);
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify STK terminal response : GET INPUT with 160 unpacked characters.
|
||||
*
|
||||
* @See |TERMINAL RESPONSE: GET INPUT 1.8.1| of 27.22.4.3.1 GET INPUT (normal)
|
||||
* in TS 102 384.
|
||||
*/
|
||||
add_test(function test_stk_terminal_response_get_input_160_unpacked_characters() {
|
||||
let worker = newUint8SupportOutgoingIndexWorker();
|
||||
let context = worker.ContextPool._contexts[0];
|
||||
let buf = context.Buf;
|
||||
let pduHelper = context.GsmPDUHelper;
|
||||
let iccPduHelper = context.ICCPDUHelper;
|
||||
let TEST_TEXT_STRING = "***1111111111###" +
|
||||
"***2222222222###" +
|
||||
"***3333333333###" +
|
||||
"***4444444444###" +
|
||||
"***5555555555###" +
|
||||
"***6666666666###" +
|
||||
"***7777777777###" +
|
||||
"***8888888888###" +
|
||||
"***9999999999###" +
|
||||
"***0000000000###";
|
||||
|
||||
buf.sendParcel = function() {
|
||||
// Type
|
||||
do_check_eq(this.readInt32(), REQUEST_STK_SEND_TERMINAL_RESPONSE);
|
||||
|
||||
// Token : we don't care
|
||||
this.readInt32();
|
||||
|
||||
// Data Size, 352 = 2 * (TLV_COMMAND_DETAILS_SIZE(5) +
|
||||
// TLV_DEVICE_ID_SIZE(4) +
|
||||
// TLV_RESULT_SIZE(3) +
|
||||
// TEXT LENGTH(164))
|
||||
do_check_eq(this.readInt32(), 352);
|
||||
|
||||
// Command Details, Type-Length-Value
|
||||
do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_COMMAND_DETAILS |
|
||||
COMPREHENSIONTLV_FLAG_CR);
|
||||
do_check_eq(pduHelper.readHexOctet(), 3);
|
||||
do_check_eq(pduHelper.readHexOctet(), 0x01);
|
||||
do_check_eq(pduHelper.readHexOctet(), STK_CMD_GET_INPUT);
|
||||
do_check_eq(pduHelper.readHexOctet(), 0x00);
|
||||
|
||||
// Device Identifies, Type-Length-Value(Source ID-Destination ID)
|
||||
do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID);
|
||||
do_check_eq(pduHelper.readHexOctet(), 2);
|
||||
do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_ME);
|
||||
do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
|
||||
|
||||
// Result
|
||||
do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_RESULT |
|
||||
COMPREHENSIONTLV_FLAG_CR);
|
||||
do_check_eq(pduHelper.readHexOctet(), 1);
|
||||
do_check_eq(pduHelper.readHexOctet(), STK_RESULT_OK);
|
||||
|
||||
// Text
|
||||
do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_TEXT_STRING |
|
||||
COMPREHENSIONTLV_FLAG_CR);
|
||||
// C-TLV Length Encoding: 161 = 0x81 0xA1
|
||||
do_check_eq(pduHelper.readHexOctet(), 0x81);
|
||||
do_check_eq(pduHelper.readHexOctet(), 0xA1);
|
||||
do_check_eq(pduHelper.readHexOctet(), STK_TEXT_CODING_GSM_8BIT);
|
||||
do_check_eq(iccPduHelper.read8BitUnpackedToString(160), TEST_TEXT_STRING);
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
let response = {
|
||||
command: {
|
||||
commandNumber: 0x01,
|
||||
typeOfCommand: STK_CMD_GET_INPUT,
|
||||
commandQualifier: 0x00,
|
||||
options: {
|
||||
minLength: 160,
|
||||
maxLength: 160,
|
||||
text: TEST_TEXT_STRING
|
||||
}
|
||||
},
|
||||
input: TEST_TEXT_STRING,
|
||||
resultCode: STK_RESULT_OK
|
||||
};
|
||||
context.RIL.sendStkTerminalResponse(response);
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify STK terminal response : GET_INKEY - NO_RESPONSE_FROM_USER with
|
||||
* duration provided.
|
||||
*
|
||||
* @See |27.22.4.2.8 GET INKEY (Variable Time out)| in TS 102 384.
|
||||
*/
|
||||
add_test(function test_stk_terminal_response_get_inkey_no_response_from_user() {
|
||||
let worker = newUint8SupportOutgoingIndexWorker();
|
||||
let context = worker.ContextPool._contexts[0];
|
||||
let buf = context.Buf;
|
||||
let pduHelper = context.GsmPDUHelper;
|
||||
|
||||
buf.sendParcel = function() {
|
||||
// Type
|
||||
do_check_eq(this.readInt32(), REQUEST_STK_SEND_TERMINAL_RESPONSE);
|
||||
|
||||
// Token : we don't care
|
||||
this.readInt32();
|
||||
|
||||
// Data Size, 32 = 2 * (TLV_COMMAND_DETAILS_SIZE(5) +
|
||||
// TLV_DEVICE_ID_SIZE(4) +
|
||||
// TLV_RESULT_SIZE(3) +
|
||||
// DURATION(4))
|
||||
do_check_eq(this.readInt32(), 32);
|
||||
|
||||
// Command Details, Type-Length-Value
|
||||
do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_COMMAND_DETAILS |
|
||||
COMPREHENSIONTLV_FLAG_CR);
|
||||
do_check_eq(pduHelper.readHexOctet(), 3);
|
||||
do_check_eq(pduHelper.readHexOctet(), 0x01);
|
||||
do_check_eq(pduHelper.readHexOctet(), STK_CMD_GET_INKEY);
|
||||
do_check_eq(pduHelper.readHexOctet(), 0x00);
|
||||
|
||||
// Device Identifies, Type-Length-Value(Source ID-Destination ID)
|
||||
do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID);
|
||||
do_check_eq(pduHelper.readHexOctet(), 2);
|
||||
do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_ME);
|
||||
do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
|
||||
|
||||
// Result
|
||||
do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_RESULT |
|
||||
COMPREHENSIONTLV_FLAG_CR);
|
||||
do_check_eq(pduHelper.readHexOctet(), 1);
|
||||
do_check_eq(pduHelper.readHexOctet(), STK_RESULT_NO_RESPONSE_FROM_USER);
|
||||
|
||||
// Duration, Type-Length-Value(Time unit, Time interval)
|
||||
do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DURATION);
|
||||
do_check_eq(pduHelper.readHexOctet(), 2);
|
||||
do_check_eq(pduHelper.readHexOctet(), STK_TIME_UNIT_SECOND);
|
||||
do_check_eq(pduHelper.readHexOctet(), 10);
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
let response = {
|
||||
command: {
|
||||
commandNumber: 0x01,
|
||||
typeOfCommand: STK_CMD_GET_INKEY,
|
||||
commandQualifier: 0x00,
|
||||
options: {
|
||||
duration: {
|
||||
timeUnit: STK_TIME_UNIT_SECOND,
|
||||
timeInterval: 10
|
||||
},
|
||||
text: 'Enter "+"'
|
||||
}
|
||||
},
|
||||
resultCode: STK_RESULT_NO_RESPONSE_FROM_USER
|
||||
};
|
||||
context.RIL.sendStkTerminalResponse(response);
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify STK terminal response : GET_INKEY - YES/NO request
|
||||
*/
|
||||
|
@ -4,13 +4,27 @@
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
let connection;
|
||||
const normalNumber = "0912345678";
|
||||
const emergencyNumber = "112";
|
||||
let outCall;
|
||||
|
||||
function setRadioEnabledAll(enabled) {
|
||||
let promises = [];
|
||||
let numOfSim = navigator.mozMobileConnections.length;
|
||||
|
||||
for (let i = 0; i < numOfSim; i++) {
|
||||
let connection = navigator.mozMobileConnections[i];
|
||||
ok(connection instanceof MozMobileConnection,
|
||||
"connection[" + i + "] is instanceof " + connection.constructor);
|
||||
|
||||
promises.push(gSetRadioEnabled(connection, enabled));
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function testDial_NormalNumber() {
|
||||
return gSetRadioEnabled(connection, false)
|
||||
return setRadioEnabledAll(false)
|
||||
.then(() => gDial(normalNumber))
|
||||
.catch(cause => {
|
||||
is(cause, "RadioNotAvailable");
|
||||
@ -19,7 +33,7 @@ function testDial_NormalNumber() {
|
||||
}
|
||||
|
||||
function testDial_EmergencyNumber() {
|
||||
return gSetRadioEnabled(connection, false)
|
||||
return setRadioEnabledAll(false)
|
||||
.then(() => gDial(emergencyNumber))
|
||||
.then(call => { outCall = call; })
|
||||
.then(() => gRemoteAnswer(outCall))
|
||||
@ -28,7 +42,7 @@ function testDial_EmergencyNumber() {
|
||||
}
|
||||
|
||||
function testDialEmergency_NormalNumber() {
|
||||
return gSetRadioEnabled(connection, false)
|
||||
return setRadioEnabledAll(false)
|
||||
.then(() => gDialEmergency(normalNumber))
|
||||
.catch(cause => {
|
||||
is(cause, "RadioNotAvailable");
|
||||
@ -37,7 +51,7 @@ function testDialEmergency_NormalNumber() {
|
||||
}
|
||||
|
||||
function testDialEmergency_EmergencyNumber() {
|
||||
return gSetRadioEnabled(connection, false)
|
||||
return setRadioEnabledAll(false)
|
||||
.then(() => gDialEmergency(emergencyNumber))
|
||||
.then(call => { outCall = call; })
|
||||
.then(() => gRemoteAnswer(outCall))
|
||||
@ -46,16 +60,12 @@ function testDialEmergency_EmergencyNumber() {
|
||||
}
|
||||
|
||||
startTestWithPermissions(['mobileconnection'], function() {
|
||||
connection = navigator.mozMobileConnections[0];
|
||||
ok(connection instanceof MozMobileConnection,
|
||||
"connection is instanceof " + connection.constructor);
|
||||
|
||||
Promise.resolve()
|
||||
.then(() => testDial_NormalNumber())
|
||||
.then(() => testDial_EmergencyNumber())
|
||||
.then(() => testDialEmergency_NormalNumber())
|
||||
.then(() => testDialEmergency_EmergencyNumber())
|
||||
.then(() => gSetRadioEnabled(connection, true))
|
||||
.then(() => setRadioEnabledAll(true))
|
||||
.catch(error => ok(false, "Promise reject: " + error))
|
||||
.then(finish);
|
||||
});
|
||||
|
@ -152,8 +152,7 @@ public class BrowserApp extends GeckoApp
|
||||
OnUrlOpenListener,
|
||||
OnUrlOpenInBackgroundListener,
|
||||
ActionModeCompat.Presenter,
|
||||
LayoutInflater.Factory,
|
||||
TopSitesPanel.BrowserTilesRecorderProvider {
|
||||
LayoutInflater.Factory {
|
||||
private static final String LOGTAG = "GeckoBrowserApp";
|
||||
|
||||
private static final int TABS_ANIMATION_DURATION = 450;
|
||||
@ -260,8 +259,6 @@ public class BrowserApp extends GeckoApp
|
||||
|
||||
private final DynamicToolbar mDynamicToolbar = new DynamicToolbar();
|
||||
|
||||
private TilesRecorder mTilesRecorder;
|
||||
|
||||
@Override
|
||||
public View onCreateView(final String name, final Context context, final AttributeSet attrs) {
|
||||
final View view;
|
||||
@ -663,8 +660,6 @@ public class BrowserApp extends GeckoApp
|
||||
|
||||
// Set the maximum bits-per-pixel the favicon system cares about.
|
||||
IconDirectoryEntry.setMaxBPP(GeckoAppShell.getScreenDepth());
|
||||
|
||||
mTilesRecorder = new TilesRecorder();
|
||||
}
|
||||
|
||||
private void setupSystemUITinting() {
|
||||
@ -3364,9 +3359,4 @@ public class BrowserApp extends GeckoApp
|
||||
appLocale,
|
||||
previousSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TilesRecorder getTilesRecorder() {
|
||||
return mTilesRecorder;
|
||||
}
|
||||
}
|
||||
|
@ -128,22 +128,13 @@ public class TopSitesPanel extends HomeFragment {
|
||||
}
|
||||
}
|
||||
|
||||
public interface BrowserTilesRecorderProvider {
|
||||
public TilesRecorder getTilesRecorder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
|
||||
mMaxGridEntries = activity.getResources().getInteger(R.integer.number_of_top_sites);
|
||||
|
||||
try {
|
||||
mTilesRecorder = ((BrowserTilesRecorderProvider) activity).getTilesRecorder();
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(activity.toString()
|
||||
+ " must implement TopSitesPanel.BrowserTilesRecorderProvider");
|
||||
}
|
||||
mTilesRecorder = new TilesRecorder();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -14,6 +14,7 @@ import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.util.WindowUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
|
||||
|
||||
@ -28,7 +29,6 @@ import android.graphics.Shader;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
@ -204,8 +204,7 @@ public class LightweightTheme implements GeckoEventListener {
|
||||
}
|
||||
|
||||
// Get the max display dimension so we can crop or expand the theme.
|
||||
DisplayMetrics dm = mApplication.getResources().getDisplayMetrics();
|
||||
int maxWidth = Math.max(dm.widthPixels, dm.heightPixels);
|
||||
final int maxWidth = WindowUtils.getLargestDimension(mApplication);
|
||||
|
||||
// The lightweight theme image's width and height.
|
||||
final int bitmapWidth = bitmap.getWidth();
|
||||
|
@ -66,7 +66,9 @@ public class MenuPopup extends PopupWindow {
|
||||
@Override
|
||||
public void showAsDropDown(View anchor) {
|
||||
// Set a height, so that the popup will not be displayed below the bottom of the screen.
|
||||
setHeight(mPopupMinHeight);
|
||||
// We use the exact height of the internal content, which is the technique described in
|
||||
// http://stackoverflow.com/a/7698709
|
||||
setHeight(mPanel.getHeight());
|
||||
|
||||
// Attempt to align the center of the popup with the center of the anchor. If the anchor is
|
||||
// near the edge of the screen, the popup will just align with the edge of the screen.
|
||||
|
@ -84,6 +84,7 @@ gujar.sources += [
|
||||
'util/ThreadUtils.java',
|
||||
'util/UIAsyncTask.java',
|
||||
'util/WebActivityMapper.java',
|
||||
'util/WindowUtils.java',
|
||||
]
|
||||
gujar.extra_jars = [
|
||||
'constants.jar',
|
||||
|
@ -226,16 +226,33 @@ class TabsGridLayout extends GridView
|
||||
break;
|
||||
|
||||
case CLOSED:
|
||||
if(mTabsAdapter.getCount() > 0) {
|
||||
if (mTabsAdapter.getCount() > 0) {
|
||||
animateRemoveTab(tab);
|
||||
}
|
||||
if (tab.isPrivate() == mIsPrivate && mTabsAdapter.getCount() > 0) {
|
||||
if (mTabsAdapter.removeTab(tab)) {
|
||||
int selected = mTabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
|
||||
updateSelectedStyle(selected);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
final Tabs tabsInstance = Tabs.getInstance();
|
||||
|
||||
if (mTabsAdapter.removeTab(tab)) {
|
||||
if (tab.isPrivate() == mIsPrivate && mTabsAdapter.getCount() > 0) {
|
||||
int selected = mTabsAdapter.getPositionForTab(tabsInstance.getSelectedTab());
|
||||
updateSelectedStyle(selected);
|
||||
}
|
||||
if(!tab.isPrivate()) {
|
||||
// Make sure we always have at least one normal tab
|
||||
final Iterable<Tab> tabs = tabsInstance.getTabsInOrder();
|
||||
boolean removedTabIsLastNormalTab = true;
|
||||
for (Tab singleTab : tabs) {
|
||||
if (!singleTab.isPrivate()) {
|
||||
removedTabIsLastNormalTab = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (removedTabIsLastNormalTab) {
|
||||
tabsInstance.addTab();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SELECTED:
|
||||
// Update the selected position, then fall through...
|
||||
|
@ -151,6 +151,10 @@ skip-if = android_version == "10"
|
||||
[testSelectionHandler]
|
||||
skip-if = android_version == "10"
|
||||
|
||||
# testInputSelections disabled on Android 2.3 by trailing skip-if, due to bug 980074
|
||||
[testInputSelections]
|
||||
skip-if = android_version == "10"
|
||||
|
||||
# testTextareaSelections disabled on Android 2.3 by trailing skip-if, due to bug 980074
|
||||
[testTextareaSelections]
|
||||
skip-if = android_version == "10"
|
||||
|
543
mobile/android/base/tests/roboextender/testInputSelections.html
Normal file
543
mobile/android/base/tests/roboextender/testInputSelections.html
Normal file
@ -0,0 +1,543 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Automated RTL/LTR Text Selection tests for Input elements</title>
|
||||
<meta name="viewport" content="initial-scale=1.0"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
// Used to create handle movement events for SelectionHandler.
|
||||
const ANCHOR = "ANCHOR";
|
||||
const FOCUS = "FOCUS";
|
||||
|
||||
// Types of DOM nodes that serve as Selection Anchor/Focus nodes.
|
||||
const DIV_NODE = "DIV";
|
||||
const TEXT_NODE = "#text";
|
||||
|
||||
// Used to specifiy midpoint selection text left/right of center.
|
||||
const EST_SEL_TEXT_BOUND_CHARS = 5;
|
||||
|
||||
// Used to create test scenarios, and verify results.
|
||||
const LTR_INPUT_TEXT_VALUE = "This input text is one character short of it's maxmimum.";
|
||||
const RTL_INPUT_TEXT_VALUE = "טקסט קלט זה קצר תו אחד של זה גדול.";
|
||||
|
||||
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/Messaging.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import('resource://gre/modules/Geometry.jsm');
|
||||
|
||||
/* =================================================================================
|
||||
*
|
||||
* Start of all text selection tests, check initialization state.
|
||||
*/
|
||||
function startTests() {
|
||||
testLTR_selectAll().
|
||||
then(testRTL_selectAll).
|
||||
|
||||
then(testLTR_dragFocusHandleToSelf).
|
||||
then(testLTR_dragAnchorHandleToSelf).
|
||||
then(testRTL_dragFocusHandleToSelf).
|
||||
then(testRTL_dragAnchorHandleToSelf).
|
||||
|
||||
then(finishTests, function(err) {
|
||||
ok(false, "Error in selection test " + err);
|
||||
finishTests();
|
||||
});
|
||||
}
|
||||
|
||||
/* =================================================================================
|
||||
*
|
||||
* LTR selectAll() test selects the entire single-line <input> element and ensures:
|
||||
* ) The Selection exists.
|
||||
* ) The Selection text matches an expected value.
|
||||
*
|
||||
* ) Assumptions about the DOM Selection Anchor node are correct.
|
||||
* ) Assumptions about the DOM Selection Focus node are correct.
|
||||
*
|
||||
* ) The UI Selection anchor handle is aligned vertically with the focus handle.
|
||||
* ) The UI Selection anchor handle is left of the focus handle.
|
||||
*/
|
||||
function testLTR_selectAll() {
|
||||
// Select entire LTR Input element.
|
||||
var sh = getSelectionHandler();
|
||||
var element = document.getElementById("LTRInput");
|
||||
element.value = LTR_INPUT_TEXT_VALUE;
|
||||
sh.startSelection(element);
|
||||
|
||||
var selection = sh._getSelection();
|
||||
|
||||
var anchorNode = selection.anchorNode;
|
||||
var anchorOffset = selection.anchorOffset;
|
||||
var focusNode = selection.focusNode;
|
||||
var focusOffset = selection.focusOffset;
|
||||
|
||||
var anchorPt = new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y);
|
||||
var focusPt = new Point(sh._cache.focusPt.x, sh._cache.focusPt.y);
|
||||
|
||||
return Promise.all([
|
||||
ok(sh.isSelectionActive(),
|
||||
"testLTR_selectAll starts, selection should be active."),
|
||||
is(sh._targetElement, element,
|
||||
"LTR SelectionHandler reference is the node we provided."),
|
||||
is(sh._getSelectedText(), LTR_INPUT_TEXT_VALUE,
|
||||
"LTR Selection text should match expected value."),
|
||||
|
||||
isNot(anchorNode, element,
|
||||
"LTR Selection Anchor isn't the LTRInput node."),
|
||||
is(anchorNode.nodeName, DIV_NODE, "LTR Anchor node is a DIV node."),
|
||||
ok(!document.contains(anchorNode), "LTR Anchor node is an anonymous DIV node."),
|
||||
is(anchorNode.parentNode, element, "LTR Anchor node is a child of the LTRInput node."),
|
||||
is(anchorOffset, 0,
|
||||
"LTR Selection starts at Anchor node with offset 0."),
|
||||
|
||||
isNot(focusNode, element,
|
||||
"LTR Selection Focus isn't the LTRInput node."),
|
||||
is(focusNode.nodeName, TEXT_NODE, "LTR Focus node is a TEXT node."),
|
||||
ok(!document.contains(focusNode), "LTR Focus node is an anonymous TEXT node."),
|
||||
is(focusNode.parentNode, anchorNode, "LTR Focus node is a child of the Anchor DIV node."),
|
||||
is(focusOffset, LTR_INPUT_TEXT_VALUE.length,
|
||||
"LTR Selection ends at Focus node with offset of the LTRInput node length."),
|
||||
|
||||
is(anchorPt.y, focusPt.y,
|
||||
"LTR UI Selection anchor should match focus vertically."),
|
||||
lessThan(anchorPt.x, focusPt.x,
|
||||
"LTR UI Selection anchor should be to the left of focus."),
|
||||
|
||||
]).then(function() {
|
||||
// Close selection and complete test.
|
||||
sh.observe(null, "TextSelection:End", {});
|
||||
|
||||
return Promise.all([
|
||||
ok(!sh.isSelectionActive(),
|
||||
"testLTR_selectAll finishes, selection should not be active."),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/* =================================================================================
|
||||
*
|
||||
* RTL selectAll() test selects the entire single-line <input> element and ensures:
|
||||
* ) The Selection exists.
|
||||
* ) The Selection text matches an expected value.
|
||||
*
|
||||
* ) Assumptions about the DOM Selection Anchor node are correct.
|
||||
* ) Assumptions about the DOM Selection Focus node are correct.
|
||||
*
|
||||
* ) The UI Selection anchor handle is aligned vertically with the focus handle.
|
||||
* ) The UI Selection anchor handle is right of the focus handle.
|
||||
*/
|
||||
function testRTL_selectAll() {
|
||||
// Select entire RTL Input element.
|
||||
var sh = getSelectionHandler();
|
||||
var element = document.getElementById("RTLInput");
|
||||
element.value = RTL_INPUT_TEXT_VALUE;
|
||||
sh.startSelection(element);
|
||||
|
||||
var selection = sh._getSelection();
|
||||
|
||||
var anchorNode = selection.anchorNode;
|
||||
var anchorOffset = selection.anchorOffset;
|
||||
var focusNode = selection.focusNode;
|
||||
var focusOffset = selection.focusOffset;
|
||||
|
||||
var anchorPt = new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y);
|
||||
var focusPt = new Point(sh._cache.focusPt.x, sh._cache.focusPt.y);
|
||||
|
||||
return Promise.all([
|
||||
ok(sh.isSelectionActive(),
|
||||
"testRTL_selectAll starts, selection should be active."),
|
||||
is(sh._targetElement, element,
|
||||
"RTL SelectionHandler reference is the node we provided."),
|
||||
is(sh._getSelectedText(), RTL_INPUT_TEXT_VALUE,
|
||||
"RTL Selection text should match expected value."),
|
||||
|
||||
isNot(anchorNode, element,
|
||||
"RTL Selection Anchor isn't the RTLInput node."),
|
||||
is(anchorNode.nodeName, DIV_NODE, "RTL Anchor node is a DIV node."),
|
||||
ok(!document.contains(anchorNode), "RTL Anchor node is an anonymous DIV node."),
|
||||
is(anchorNode.parentNode, element, "RTL Anchor node is a child of the RTLInput node."),
|
||||
is(anchorOffset, 0,
|
||||
"RTL Selection starts at Anchor node with offset 0."),
|
||||
|
||||
isNot(focusNode, element,
|
||||
"RTL Selection Focus isn't the RTLInput node."),
|
||||
is(focusNode.nodeName, TEXT_NODE, "RTL Focus node is a TEXT node."),
|
||||
ok(!document.contains(focusNode), "RTL Focus node is an anonymous TEXT node."),
|
||||
is(focusNode.parentNode, anchorNode, "RTL Focus node is a child of the Anchor DIV node."),
|
||||
is(focusOffset, RTL_INPUT_TEXT_VALUE.length,
|
||||
"RTL Selection ends at Focus node with offset of the RTLInput node length."),
|
||||
|
||||
is(anchorPt.y, focusPt.y,
|
||||
"RTL UI Selection anchor should match focus vertically."),
|
||||
greaterThan(anchorPt.x, focusPt.x,
|
||||
"RTL UI Selection anchor should be to the right of focus."),
|
||||
|
||||
]).then(function() {
|
||||
// Close selection and complete test.
|
||||
sh.observe(null, "TextSelection:End", {});
|
||||
|
||||
return Promise.all([
|
||||
ok(!sh.isSelectionActive(),
|
||||
"testRTL_selectAll finishes, selection should not be active."),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/* =================================================================================
|
||||
*
|
||||
* If we selectAll() in a LTR <input>, then:
|
||||
* ) drag the focus handle to itself, the selected text, and the
|
||||
* selection anchor and focus points should all remain the same.
|
||||
*/
|
||||
function testLTR_dragFocusHandleToSelf() {
|
||||
// Select entire LTR Input element.
|
||||
var sh = getSelectionHandler();
|
||||
var element = document.getElementById("LTRInput");
|
||||
element.value = LTR_INPUT_TEXT_VALUE;
|
||||
sh.startSelection(element);
|
||||
|
||||
// Note initial Selection handle points.
|
||||
var initialSelection =
|
||||
{ anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
|
||||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
var initialSelectionText = sh._getSelectedText();
|
||||
|
||||
// Drag focus handle and note results.
|
||||
sh.observe(null, "TextSelection:Move",
|
||||
JSON.stringify({ handleType : FOCUS,
|
||||
x : initialSelection.focusPt.x,
|
||||
y : initialSelection.focusPt.y
|
||||
})
|
||||
);
|
||||
sh.observe(null, "TextSelection:Position",
|
||||
JSON.stringify({ handleType : FOCUS })
|
||||
);
|
||||
|
||||
var focusDraggedSelection =
|
||||
{ anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
|
||||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
var focusDragSelectionText = sh._getSelectedText();
|
||||
|
||||
// Complete test, and report.
|
||||
sh.observe(null, "TextSelection:End", {});
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testLTR_dragFocusHandleToSelf - Test Starts."),
|
||||
|
||||
is(initialSelectionText, LTR_INPUT_TEXT_VALUE,
|
||||
"LTR Selection text initially should match expected value."),
|
||||
selectionExists(initialSelection,
|
||||
"LTR Selection initially existed at points"),
|
||||
|
||||
is(focusDragSelectionText, LTR_INPUT_TEXT_VALUE,
|
||||
"LTR Selection text after focus drag should match expected value."),
|
||||
selectionExists(focusDraggedSelection,
|
||||
"LTR Selection after focus drag existed at points"),
|
||||
selectionEquals(focusDraggedSelection, initialSelection,
|
||||
"LTR Selection points after focus drag " +
|
||||
"should match initial selection points."),
|
||||
|
||||
ok(true, "testLTR_dragFocusHandleToSelf - Test Finishes."),
|
||||
]);
|
||||
}
|
||||
|
||||
/* =================================================================================
|
||||
*
|
||||
* If we selectAll() in a LTR <input>, then:
|
||||
* ) drag the anchor handle to itself, the selected text, and the
|
||||
* selection anchor and focus points should all remain the same.
|
||||
*/
|
||||
function testLTR_dragAnchorHandleToSelf() {
|
||||
// Select entire LTR Input element.
|
||||
var sh = getSelectionHandler();
|
||||
var element = document.getElementById("LTRInput");
|
||||
element.value = LTR_INPUT_TEXT_VALUE;
|
||||
sh.startSelection(element);
|
||||
|
||||
// Note initial Selection handle points.
|
||||
var initialSelection =
|
||||
{ anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
|
||||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
var initialSelectionText = sh._getSelectedText();
|
||||
|
||||
// Drag anchor handle and note results.
|
||||
sh.observe(null, "TextSelection:Move",
|
||||
JSON.stringify({ handleType : ANCHOR,
|
||||
x : initialSelection.anchorPt.x,
|
||||
y : initialSelection.anchorPt.y
|
||||
})
|
||||
);
|
||||
sh.observe(null, "TextSelection:Position",
|
||||
JSON.stringify({ handleType : ANCHOR })
|
||||
);
|
||||
var anchorDraggedSelection =
|
||||
{ anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
|
||||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
var anchorDragSelectionText = sh._getSelectedText();
|
||||
|
||||
// Complete test, and report.
|
||||
sh.observe(null, "TextSelection:End", {});
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testLTR_dragAnchorHandleToSelf - Test Starts."),
|
||||
|
||||
is(initialSelectionText, LTR_INPUT_TEXT_VALUE,
|
||||
"LTR Selection text initially should match expected value."),
|
||||
selectionExists(initialSelection,
|
||||
"LTR Selection initially existed at points"),
|
||||
|
||||
todo(false, "testLTR_dragAnchorHandleToSelf: " +
|
||||
// is(anchorDragSelectionText, LTR_INPUT_TEXT_VALUE,
|
||||
"LTR Selection text after anchor drag should match expected value."),
|
||||
todo(false, "testLTR_dragAnchorHandleToSelf: " +
|
||||
// selectionExists(anchorDraggedSelection,
|
||||
"LTR Selection after anchor drag existed at points"),
|
||||
todo(false, "testLTR_dragAnchorHandleToSelf: " +
|
||||
// selectionEquals(anchorDraggedSelection, initialSelection,
|
||||
"LTR Selection points after anchor drag " +
|
||||
"should match initial selection points."),
|
||||
|
||||
ok(true, "testLTR_dragAnchorHandleToSelf - Test Finishes."),
|
||||
]);
|
||||
}
|
||||
|
||||
/* =================================================================================
|
||||
*
|
||||
* If we selectAll() in a RTL <input>, then:
|
||||
* ) drag the focus handle to itself, the selected text, and the
|
||||
* selection anchor and focus points should all remain the same.
|
||||
*/
|
||||
function testRTL_dragFocusHandleToSelf() {
|
||||
// Select entire RTL Input element.
|
||||
var sh = getSelectionHandler();
|
||||
var element = document.getElementById("RTLInput");
|
||||
element.value = RTL_INPUT_TEXT_VALUE;
|
||||
sh.startSelection(element);
|
||||
|
||||
// Note initial Selection handle points.
|
||||
var initialSelection =
|
||||
{ anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
|
||||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
var initialSelectionText = sh._getSelectedText();
|
||||
|
||||
// Drag focus handle and note results.
|
||||
sh.observe(null, "TextSelection:Move",
|
||||
JSON.stringify({ handleType : FOCUS,
|
||||
x : initialSelection.focusPt.x,
|
||||
y : initialSelection.focusPt.y
|
||||
})
|
||||
);
|
||||
sh.observe(null, "TextSelection:Position",
|
||||
JSON.stringify({ handleType : FOCUS })
|
||||
);
|
||||
|
||||
var focusDraggedSelection =
|
||||
{ anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
|
||||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
var focusDragSelectionText = sh._getSelectedText();
|
||||
|
||||
// Complete test, and report.
|
||||
sh.observe(null, "TextSelection:End", {});
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testRTL_dragFocusHandleToSelf - Test Starts."),
|
||||
|
||||
is(initialSelectionText, RTL_INPUT_TEXT_VALUE,
|
||||
"RTL Selection text initially should match expected value."),
|
||||
selectionExists(initialSelection,
|
||||
"RTL Selection initially existed at points"),
|
||||
|
||||
todo(false, "testRTL_dragAnchorHandleToSelf: " +
|
||||
// is(focusDragSelectionText, RTL_INPUT_TEXT_VALUE,
|
||||
"RTL Selection text after focus drag should match expected value."),
|
||||
todo(false, "testRTL_dragAnchorHandleToSelf: " +
|
||||
// selectionExists(focusDraggedSelection,
|
||||
"RTL Selection after focus drag existed at points"),
|
||||
todo(false, "testRTL_dragAnchorHandleToSelf: " +
|
||||
// selectionEquals(focusDraggedSelection, initialSelection,
|
||||
"RTL Selection points after focus drag " +
|
||||
"should match initial selection points."),
|
||||
|
||||
ok(true, "testRTL_dragFocusHandleToSelf - Test Finishes."),
|
||||
]);
|
||||
}
|
||||
|
||||
/* =================================================================================
|
||||
*
|
||||
* If we selectAll() in a RTL <input>, then:
|
||||
* ) drag the anchor handle to itself, the selected text, and the
|
||||
* selection anchor and focus points should all remain the same.
|
||||
*/
|
||||
function testRTL_dragAnchorHandleToSelf() {
|
||||
// Select entire RTL Input element.
|
||||
var sh = getSelectionHandler();
|
||||
var element = document.getElementById("RTLInput");
|
||||
element.value = RTL_INPUT_TEXT_VALUE;
|
||||
sh.startSelection(element);
|
||||
|
||||
// Note initial Selection handle points.
|
||||
var initialSelection =
|
||||
{ anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
|
||||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
var initialSelectionText = sh._getSelectedText();
|
||||
|
||||
// Drag anchor handle and note results.
|
||||
sh.observe(null, "TextSelection:Move",
|
||||
JSON.stringify({ handleType : ANCHOR,
|
||||
x : initialSelection.anchorPt.x,
|
||||
y : initialSelection.anchorPt.y
|
||||
})
|
||||
);
|
||||
sh.observe(null, "TextSelection:Position",
|
||||
JSON.stringify({ handleType : ANCHOR })
|
||||
);
|
||||
var anchorDraggedSelection =
|
||||
{ anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
|
||||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
var anchorDragSelectionText = sh._getSelectedText();
|
||||
|
||||
// Complete test, and report.
|
||||
sh.observe(null, "TextSelection:End", {});
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testRTL_dragAnchorHandleToSelf - Test Starts."),
|
||||
|
||||
is(initialSelectionText, RTL_INPUT_TEXT_VALUE,
|
||||
"RTL Selection text initially should match expected value."),
|
||||
selectionExists(initialSelection,
|
||||
"RTL Selection initially existed at points"),
|
||||
|
||||
is(anchorDragSelectionText, RTL_INPUT_TEXT_VALUE,
|
||||
"RTL Selection text after anchor drag should match expected value."),
|
||||
selectionExists(anchorDraggedSelection,
|
||||
"RTL Selection after anchor drag existed at points"),
|
||||
selectionEquals(anchorDraggedSelection, initialSelection,
|
||||
"RTL Selection points after anchor drag " +
|
||||
"should match initial selection points."),
|
||||
|
||||
ok(true, "testRTL_dragAnchorHandleToSelf - Test Finishes."),
|
||||
]);
|
||||
}
|
||||
|
||||
/* =================================================================================
|
||||
*
|
||||
* After finish of all selection tests, wrap up and go home.
|
||||
*
|
||||
*/
|
||||
function finishTests() {
|
||||
Messaging.sendRequest({
|
||||
type: "Robocop:testInputSelections",
|
||||
result: true,
|
||||
msg: "Done!",
|
||||
done: true
|
||||
});
|
||||
}
|
||||
|
||||
/* ============================== Utility functions ======================
|
||||
*
|
||||
* Common functions available to all tests.
|
||||
*
|
||||
*/
|
||||
function getSelectionHandler() {
|
||||
return (!this._selectionHandler) ?
|
||||
this._selectionHandler = Services.wm.getMostRecentWindow("navigator:browser").SelectionHandler :
|
||||
this._selectionHandler;
|
||||
}
|
||||
|
||||
function todo(result, msg) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "Robocop:testInputSelections",
|
||||
todo: result,
|
||||
msg: msg
|
||||
});
|
||||
}
|
||||
|
||||
function ok(result, msg) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "Robocop:testInputSelections",
|
||||
result: result,
|
||||
msg: msg
|
||||
});
|
||||
}
|
||||
|
||||
function is(one, two, msg) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "Robocop:testInputSelections",
|
||||
result: one === two,
|
||||
msg: msg + " : " + one + " === " + two
|
||||
});
|
||||
}
|
||||
|
||||
function isNot(one, two, msg) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "Robocop:testInputSelections",
|
||||
result: one !== two,
|
||||
msg: msg + " : " + one + " !== " + two
|
||||
});
|
||||
}
|
||||
|
||||
function lessThan(n1, n2, msg) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "Robocop:testInputSelections",
|
||||
result: n1 < n2,
|
||||
msg: msg + " : " + n1 + " < " + n2
|
||||
});
|
||||
}
|
||||
|
||||
function greaterThan(n1, n2, msg) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "Robocop:testInputSelections",
|
||||
result: n1 > n2,
|
||||
msg: msg + " : " + n1 + " > " + n2
|
||||
});
|
||||
}
|
||||
|
||||
function pointEquals(p1, p2, msg) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "Robocop:testInputSelections",
|
||||
result: p1.equals(p2),
|
||||
msg: msg + " : " + p1.toString() + " == " + p2.toString()
|
||||
});
|
||||
}
|
||||
|
||||
function pointNotEquals(p1, p2, msg) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "Robocop:testInputSelections",
|
||||
result: !p1.equals(p2),
|
||||
msg: msg + " : " + p1.toString() + " == " + p2.toString()
|
||||
});
|
||||
}
|
||||
|
||||
function selectionExists(selection, msg) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "Robocop:testInputSelections",
|
||||
result: !selection.anchorPt.equals(selection.focusPt),
|
||||
msg: msg + " : anchor:" + selection.anchorPt.toString() +
|
||||
" focus:" + selection.focusPt.toString()
|
||||
});
|
||||
}
|
||||
|
||||
function selectionEquals(s1, s2, msg) {
|
||||
return Messaging.sendRequestForResult({
|
||||
type: "Robocop:testInputSelections",
|
||||
result: s1.anchorPt.equals(s2.anchorPt) && s1.focusPt.equals(s2.focusPt),
|
||||
msg: msg
|
||||
});
|
||||
}
|
||||
|
||||
/* =================================================================================
|
||||
*
|
||||
* Page definition for all tests.
|
||||
*
|
||||
*/
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="startTests();">
|
||||
<input id="LTRInput" dir="ltr" type="text" maxlength="57" size="57" value="">
|
||||
<br>
|
||||
<input id="RTLInput" dir="rtl" type="text" maxlength="35" size="35" value="">
|
||||
</body>
|
||||
|
||||
</html>
|
50
mobile/android/base/tests/testInputSelections.java
Normal file
50
mobile/android/base/tests/testInputSelections.java
Normal file
@ -0,0 +1,50 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.Actions;
|
||||
import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.tests.helpers.GeckoHelper;
|
||||
import org.mozilla.gecko.tests.helpers.NavigationHelper;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
public class testInputSelections extends UITest {
|
||||
|
||||
public void testInputSelections() {
|
||||
GeckoHelper.blockForReady();
|
||||
|
||||
Actions.EventExpecter robocopTestExpecter =
|
||||
getActions().expectGeckoEvent("Robocop:testInputSelections");
|
||||
final String url = "chrome://roboextender/content/testInputSelections.html";
|
||||
NavigationHelper.enterAndLoadUrl(url);
|
||||
mToolbar.assertTitle(url);
|
||||
|
||||
while (!test(robocopTestExpecter)) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
robocopTestExpecter.unregisterListener();
|
||||
}
|
||||
|
||||
private boolean test(Actions.EventExpecter expecter) {
|
||||
final JSONObject eventData;
|
||||
try {
|
||||
eventData = new JSONObject(expecter.blockForEventData());
|
||||
} catch(Exception ex) {
|
||||
// Log and ignore
|
||||
getAsserter().ok(false, "JS Test", "Error decoding data " + ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eventData.has("result")) {
|
||||
getAsserter().ok(eventData.optBoolean("result"), "JS Test", eventData.optString("msg"));
|
||||
} else if (eventData.has("todo")) {
|
||||
getAsserter().todo(eventData.optBoolean("todo"), "JS TODO", eventData.optString("msg"));
|
||||
}
|
||||
|
||||
EventDispatcher.sendResponse(eventData, new JSONObject());
|
||||
return eventData.optBoolean("done", false);
|
||||
}
|
||||
}
|
62
mobile/android/base/util/WindowUtils.java
Normal file
62
mobile/android/base/util/WindowUtils.java
Normal file
@ -0,0 +1,62 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.util;
|
||||
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class WindowUtils {
|
||||
private static final String LOGTAG = "Gecko" + WindowUtils.class.getSimpleName();
|
||||
|
||||
private WindowUtils() { /* To prevent instantiation */ }
|
||||
|
||||
/**
|
||||
* Returns the best-guess physical device dimensions, including the system status bars. Note
|
||||
* that DisplayMetrics.height/widthPixels does not include the system bars.
|
||||
*
|
||||
* via http://stackoverflow.com/a/23861333
|
||||
*
|
||||
* @param context the calling Activity's Context
|
||||
* @return The number of pixels of the device's largest dimension, ignoring software status bars
|
||||
*/
|
||||
public static int getLargestDimension(final Context context) {
|
||||
final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
|
||||
if (Versions.feature17Plus) {
|
||||
final DisplayMetrics realMetrics = new DisplayMetrics();
|
||||
display.getRealMetrics(realMetrics);
|
||||
return Math.max(realMetrics.widthPixels, realMetrics.heightPixels);
|
||||
|
||||
} else if (Versions.feature14Plus) {
|
||||
int tempWidth;
|
||||
int tempHeight;
|
||||
try {
|
||||
final Method getRawH = Display.class.getMethod("getRawHeight");
|
||||
final Method getRawW = Display.class.getMethod("getRawWidth");
|
||||
tempWidth = (Integer) getRawW.invoke(display);
|
||||
tempHeight = (Integer) getRawH.invoke(display);
|
||||
} catch (Exception e) {
|
||||
// This is the best we can do.
|
||||
tempWidth = display.getWidth();
|
||||
tempHeight = display.getHeight();
|
||||
Log.w(LOGTAG, "Couldn't use reflection to get the real display metrics.");
|
||||
}
|
||||
|
||||
return Math.max(tempWidth, tempHeight);
|
||||
|
||||
} else {
|
||||
// This should be close, as lower API devices should not have window navigation bars.
|
||||
return Math.max(display.getWidth(), display.getHeight());
|
||||
}
|
||||
}
|
||||
}
|
@ -233,6 +233,9 @@ struct RtspConnectionHandler : public AHandler {
|
||||
}
|
||||
|
||||
void pause() {
|
||||
if (!mSeekable) {
|
||||
return;
|
||||
}
|
||||
AString request = "PAUSE ";
|
||||
request.append(mSessionURL);
|
||||
request.append(" RTSP/1.0\r\n");
|
||||
|
@ -51,8 +51,9 @@ RTSPSource::RTSPSource(
|
||||
mDisconnectReplyID(0),
|
||||
mLatestPausedUnit(0),
|
||||
mPlayPending(false),
|
||||
mSeekGeneration(0)
|
||||
|
||||
mSeekGeneration(0),
|
||||
mDisconnectedToPauseLiveStream(false),
|
||||
mPlayOnConnected(false)
|
||||
{
|
||||
CHECK(aListener != NULL);
|
||||
|
||||
@ -71,6 +72,8 @@ RTSPSource::~RTSPSource()
|
||||
|
||||
void RTSPSource::start()
|
||||
{
|
||||
mDisconnectedToPauseLiveStream = false;
|
||||
|
||||
if (mLooper == NULL) {
|
||||
mLooper = new ALooper;
|
||||
mLooper->setName("rtsp");
|
||||
@ -95,6 +98,9 @@ void RTSPSource::start()
|
||||
|
||||
void RTSPSource::stop()
|
||||
{
|
||||
if (mState == DISCONNECTED) {
|
||||
return;
|
||||
}
|
||||
sp<AMessage> msg = new AMessage(kWhatDisconnect, mReflector->id());
|
||||
|
||||
sp<AMessage> dummy;
|
||||
@ -113,6 +119,14 @@ void RTSPSource::play()
|
||||
void RTSPSource::pause()
|
||||
{
|
||||
LOGI("RTSPSource::pause()");
|
||||
|
||||
// Live streams can't be paused, so we have to disconnect now.
|
||||
if (isLiveStream()) {
|
||||
mDisconnectedToPauseLiveStream = true;
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
sp<AMessage> msg = new AMessage(kWhatPerformPause, mReflector->id());
|
||||
msg->post();
|
||||
}
|
||||
@ -212,6 +226,7 @@ status_t RTSPSource::seekTo(int64_t seekTimeUs) {
|
||||
void RTSPSource::performPlay(int64_t playTimeUs) {
|
||||
if (mState == DISCONNECTED) {
|
||||
LOGI("We are in a idle state, restart play");
|
||||
mPlayOnConnected = true;
|
||||
start();
|
||||
return;
|
||||
}
|
||||
@ -641,6 +656,11 @@ void RTSPSource::onConnected(bool isSeekable)
|
||||
}
|
||||
|
||||
mState = CONNECTED;
|
||||
|
||||
if (mPlayOnConnected) {
|
||||
mPlayOnConnected = false;
|
||||
play();
|
||||
}
|
||||
}
|
||||
|
||||
void RTSPSource::onDisconnected(const sp<AMessage> &msg) {
|
||||
@ -659,7 +679,9 @@ void RTSPSource::onDisconnected(const sp<AMessage> &msg) {
|
||||
if (mDisconnectReplyID != 0) {
|
||||
finishDisconnectIfPossible();
|
||||
}
|
||||
if (mListener) {
|
||||
// If the disconnection is caused by pausing live stream,
|
||||
// do not report back to the controller.
|
||||
if (mListener && !mDisconnectedToPauseLiveStream) {
|
||||
nsresult reason = (err == OK) ? NS_OK : NS_ERROR_NET_TIMEOUT;
|
||||
mListener->OnDisconnected(0, reason);
|
||||
// Break the cycle reference between RtspController and us.
|
||||
@ -728,7 +750,9 @@ void RTSPSource::onTrackDataAvailable(size_t trackIndex)
|
||||
|
||||
void RTSPSource::onTrackEndOfStream(size_t trackIndex)
|
||||
{
|
||||
if (!mListener) {
|
||||
// If we are disconnecting to pretend pausing a live stream,
|
||||
// do not report the end of stream.
|
||||
if (!mListener || mDisconnectedToPauseLiveStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -741,4 +765,11 @@ void RTSPSource::onTrackEndOfStream(size_t trackIndex)
|
||||
|
||||
mListener->OnMediaDataAvailable(trackIndex, data, data.Length(), 0, meta.get());
|
||||
}
|
||||
|
||||
|
||||
bool RTSPSource::isLiveStream() {
|
||||
int64_t duration = 0;
|
||||
getDuration(&duration);
|
||||
return duration == 0;
|
||||
}
|
||||
} // namespace android
|
||||
|
@ -152,6 +152,18 @@ private:
|
||||
|
||||
void onTrackEndOfStream(size_t trackIndex);
|
||||
|
||||
bool isLiveStream();
|
||||
|
||||
// This flag is set if we have just disconnected
|
||||
// in order to pretend pausing a live stream.
|
||||
bool mDisconnectedToPauseLiveStream;
|
||||
|
||||
// While performing a play operation, if the current state of RTSP connection
|
||||
// is disconnected, we will start over establishing connection to the server.
|
||||
// In this case (mPlayOnConnected = true), we have to perform play again when
|
||||
// onConnected, to ensure we complete the play operation.
|
||||
bool mPlayOnConnected;
|
||||
|
||||
nsMainThreadPtrHandle<nsIStreamingProtocolListener> mListener;
|
||||
int mPrintCount;
|
||||
|
||||
|
@ -159,11 +159,11 @@ let AnimationPlayerActor = ActorClass({
|
||||
* @return {Object}
|
||||
*/
|
||||
getCurrentState: method(function() {
|
||||
return {
|
||||
/**
|
||||
* Return the player's current startTime value.
|
||||
* Will be null whenever the animation is paused or waiting to start.
|
||||
*/
|
||||
// Note that if you add a new property to the state object, make sure you
|
||||
// add the corresponding property in the AnimationPlayerFront' initialState
|
||||
// getter.
|
||||
let newState = {
|
||||
// startTime is null whenever the animation is paused or waiting to start.
|
||||
startTime: this.player.startTime,
|
||||
currentTime: this.player.currentTime,
|
||||
playState: this.player.playState,
|
||||
@ -171,16 +171,32 @@ let AnimationPlayerActor = ActorClass({
|
||||
duration: this.getDuration(),
|
||||
delay: this.getDelay(),
|
||||
iterationCount: this.getIterationCount(),
|
||||
/**
|
||||
* Is the animation currently running on the compositor. This is important for
|
||||
* developers to know if their animation is hitting the fast path or not.
|
||||
* Currently this will only be true for Firefox OS though (where we have
|
||||
* compositor animations enabled).
|
||||
* Returns false whenever the animation is paused as it is taken off the
|
||||
* compositor then.
|
||||
*/
|
||||
// isRunningOnCompositor is important for developers to know if their
|
||||
// animation is hitting the fast path or not. Currently only true for
|
||||
// Firefox OS (where we have compositor animations enabled).
|
||||
// Returns false whenever the animation is paused as it is taken off the
|
||||
// compositor then.
|
||||
isRunningOnCompositor: this.player.isRunningOnCompositor
|
||||
};
|
||||
|
||||
// If we've saved a state before, compare and only send what has changed.
|
||||
// It's expected of the front to also save old states to re-construct the
|
||||
// full state when an incomplete one is received.
|
||||
// This is to minimize protocol traffic.
|
||||
let sentState = {};
|
||||
if (this.currentState) {
|
||||
for (let key in newState) {
|
||||
if (typeof this.currentState[key] === "undefined" ||
|
||||
this.currentState[key] !== newState[key]) {
|
||||
sentState[key] = newState[key];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sentState = newState;
|
||||
}
|
||||
this.currentState = newState;
|
||||
|
||||
return sentState;
|
||||
}, {
|
||||
request: {},
|
||||
response: {
|
||||
@ -323,20 +339,36 @@ let AnimationPlayerFront = FrontClass(AnimationPlayerActor, {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if something has changed
|
||||
let hasChanged = false;
|
||||
for (let key in data) {
|
||||
if (this.state[key] !== data[key]) {
|
||||
hasChanged = true;
|
||||
break;
|
||||
}
|
||||
// If the animationplayer is now finished, stop auto-refreshing.
|
||||
if (data.playState === "finished") {
|
||||
this.stopAutoRefresh();
|
||||
}
|
||||
|
||||
if (hasChanged) {
|
||||
if (this.currentStateHasChanged) {
|
||||
this.state = data;
|
||||
this.emit(this.AUTO_REFRESH_EVENT, this.state);
|
||||
}
|
||||
})
|
||||
}),
|
||||
|
||||
/**
|
||||
* getCurrentState interceptor re-constructs incomplete states since the actor
|
||||
* only sends the values that have changed.
|
||||
*/
|
||||
getCurrentState: protocol.custom(function() {
|
||||
this.currentStateHasChanged = false;
|
||||
return this._getCurrentState().then(data => {
|
||||
for (let key in this.state) {
|
||||
if (typeof data[key] === "undefined") {
|
||||
data[key] = this.state[key];
|
||||
} else if (data[key] !== this.state[key]) {
|
||||
this.currentStateHasChanged = true;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
});
|
||||
}, {
|
||||
impl: "_getCurrentState"
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
|
179
toolkit/devtools/server/actors/settings.js
Normal file
179
toolkit/devtools/server/actors/settings.js
Normal file
@ -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',
|
||||
|
@ -20,6 +20,7 @@ support-files =
|
||||
[browser_animation_actors_04.js]
|
||||
[browser_animation_actors_05.js]
|
||||
[browser_animation_actors_06.js]
|
||||
[browser_animation_actors_07.js]
|
||||
[browser_navigateEvents.js]
|
||||
[browser_storage_dynamic_windows.js]
|
||||
[browser_storage_listings.js]
|
||||
|
@ -0,0 +1,46 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Check that, even though the AnimationPlayerActor only sends the bits of its
|
||||
// state that change, the front reconstructs the whole state everytime.
|
||||
|
||||
const {AnimationsFront} = require("devtools/server/actors/animation");
|
||||
const {InspectorFront} = require("devtools/server/actors/inspector");
|
||||
|
||||
add_task(function*() {
|
||||
let doc = yield addTab(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
initDebuggerServer();
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
let form = yield connectDebuggerClient(client);
|
||||
let inspector = InspectorFront(client, form);
|
||||
let walker = yield inspector.getWalker();
|
||||
let front = AnimationsFront(client, form);
|
||||
|
||||
yield playerHasCompleteStateAtAllTimes(walker, front);
|
||||
|
||||
yield closeDebuggerClient(client);
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
function* playerHasCompleteStateAtAllTimes(walker, front) {
|
||||
let node = yield walker.querySelector(walker.rootNode, ".simple-animation");
|
||||
let [player] = yield front.getAnimationPlayersForNode(node);
|
||||
yield player.ready();
|
||||
|
||||
// Get the list of state key names from the initialstate.
|
||||
let keys = Object.keys(player.initialState);
|
||||
|
||||
// Get the state over and over again and check that the object returned
|
||||
// contains all keys.
|
||||
// Normally, only the currentTime will have changed in between 2 calls.
|
||||
for (let i = 0; i < 10; i ++) {
|
||||
let state = yield player.getCurrentState();
|
||||
keys.forEach(key => {
|
||||
ok(typeof state[key] !== "undefined", "The state retrieved has key " + key);
|
||||
});
|
||||
}
|
||||
}
|
@ -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]
|
||||
|
129
toolkit/devtools/server/tests/mochitest/test_settings.html
Normal file
129
toolkit/devtools/server/tests/mochitest/test_settings.html
Normal file
@ -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>
|
Loading…
Reference in New Issue
Block a user