mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
merge fx-team to mozilla-central
This commit is contained in:
commit
b24c75eef3
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 988168 requires a clobber because of the location change for the linked libxul.
|
||||
Bug 991256 requires a clobber because it renames generated mobile/android/base/widget files.
|
||||
|
@ -1208,6 +1208,9 @@ pref("devtools.shadereditor.enabled", false);
|
||||
// Enable the Canvas Debugger.
|
||||
pref("devtools.canvasdebugger.enabled", false);
|
||||
|
||||
// Enable the Web Audio Editor
|
||||
pref("devtools.webaudioeditor.enabled", false);
|
||||
|
||||
// Default theme ("dark" or "light")
|
||||
pref("devtools.theme", "light");
|
||||
|
||||
|
@ -7,7 +7,6 @@ let gSyncUI = {
|
||||
DEFAULT_EOL_URL: "https://www.mozilla.org/firefox/?utm_source=synceol",
|
||||
|
||||
_obs: ["weave:service:sync:start",
|
||||
"weave:service:sync:delayed",
|
||||
"weave:service:quota:remaining",
|
||||
"weave:service:setup-complete",
|
||||
"weave:service:login:start",
|
||||
@ -85,8 +84,6 @@ let gSyncUI = {
|
||||
Services.obs.removeObserver(this, "weave:notification:added");
|
||||
},
|
||||
|
||||
_wasDelayed: false,
|
||||
|
||||
_needsSetup: function SUI__needsSetup() {
|
||||
// We want to treat "account needs verification" as "needs setup". So
|
||||
// "reach in" to Weave.Status._authManager to check whether we the signed-in
|
||||
@ -163,21 +160,6 @@ let gSyncUI = {
|
||||
});
|
||||
},
|
||||
|
||||
onSyncDelay: function SUI_onSyncDelay() {
|
||||
// basically, we want to just inform users that stuff is going to take a while
|
||||
let title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
|
||||
let description = this._stringBundle.GetStringFromName("error.sync.no_node_found");
|
||||
let buttons = [new Weave.NotificationButton(
|
||||
this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
|
||||
this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
|
||||
function() { gSyncUI.openServerStatus(); return true; }
|
||||
)];
|
||||
let notification = new Weave.Notification(
|
||||
title, description, null, Weave.Notifications.PRIORITY_INFO, buttons);
|
||||
Weave.Notifications.replaceTitle(notification);
|
||||
this._wasDelayed = true;
|
||||
},
|
||||
|
||||
onLoginFinish: function SUI_onLoginFinish() {
|
||||
// Clear out any login failure notifications
|
||||
let title = this._stringBundle.GetStringFromName("error.login.title");
|
||||
@ -410,12 +392,6 @@ let gSyncUI = {
|
||||
|
||||
// Clear out sync failures on a successful sync
|
||||
this.clearError(title);
|
||||
|
||||
if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
|
||||
title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
|
||||
this.clearError(title);
|
||||
this._wasDelayed = false;
|
||||
}
|
||||
},
|
||||
|
||||
onSyncError: function SUI_onSyncError() {
|
||||
@ -487,12 +463,6 @@ let gSyncUI = {
|
||||
new Weave.Notification(title, description, null, priority, buttons);
|
||||
Weave.Notifications.replaceTitle(notification);
|
||||
|
||||
if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
|
||||
title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
|
||||
Weave.Notifications.removeAll(title);
|
||||
this._wasDelayed = false;
|
||||
}
|
||||
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
@ -519,9 +489,6 @@ let gSyncUI = {
|
||||
case "weave:ui:sync:error":
|
||||
this.onSyncError();
|
||||
break;
|
||||
case "weave:service:sync:delayed":
|
||||
this.onSyncDelay();
|
||||
break;
|
||||
case "weave:service:quota:remaining":
|
||||
this.onQuotaNotice();
|
||||
break;
|
||||
|
@ -14,7 +14,6 @@ BRANDING_FILES := \
|
||||
branding.nsi \
|
||||
appname.bmp \
|
||||
bgintro.bmp \
|
||||
bgplain.bmp \
|
||||
clock.bmp \
|
||||
particles.bmp \
|
||||
pencil.bmp \
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 666 KiB |
@ -37,8 +37,8 @@
|
||||
!define FOOTER_CONTROL_TEXT_COLOR_FADED 0x999999
|
||||
!define FOOTER_BKGRD_COLOR 0xFFFFFF
|
||||
!define INTRO_BLURB_TEXT_COLOR 0xFFFFFF
|
||||
!define OPTIONS_TEXT_COLOR_NORMAL 0xFFFFFF
|
||||
!define OPTIONS_TEXT_COLOR_FADED 0xA1AAB3
|
||||
!define OPTIONS_BKGRD_COLOR 0x0F1B26
|
||||
!define INSTALL_BLURB_TEXT_COLOR 0xFFFFFF
|
||||
!define INSTALL_PROGRESS_TEXT_COLOR_NORMAL 0xFFFFFF
|
||||
!define COMMON_TEXT_COLOR_NORMAL 0xFFFFFF
|
||||
!define COMMON_TEXT_COLOR_FADED 0xA1AAB3
|
||||
!define COMMON_BKGRD_COLOR 0x0F1B26
|
||||
|
@ -14,7 +14,6 @@ BRANDING_FILES := \
|
||||
branding.nsi \
|
||||
appname.bmp \
|
||||
bgintro.bmp \
|
||||
bgplain.bmp \
|
||||
clock.bmp \
|
||||
particles.bmp \
|
||||
pencil.bmp \
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 666 KiB |
@ -37,8 +37,8 @@
|
||||
!define FOOTER_CONTROL_TEXT_COLOR_FADED 0x999999
|
||||
!define FOOTER_BKGRD_COLOR 0xFFFFFF
|
||||
!define INTRO_BLURB_TEXT_COLOR 0xFFFFFF
|
||||
!define OPTIONS_TEXT_COLOR_NORMAL 0xFFFFFF
|
||||
!define OPTIONS_TEXT_COLOR_FADED 0xA1AAB3
|
||||
!define OPTIONS_BKGRD_COLOR 0x0F1B26
|
||||
!define INSTALL_BLURB_TEXT_COLOR 0xFFFFFF
|
||||
!define INSTALL_PROGRESS_TEXT_COLOR_NORMAL 0xFFFFFF
|
||||
!define COMMON_TEXT_COLOR_NORMAL 0xFFFFFF
|
||||
!define COMMON_TEXT_COLOR_FADED 0xA1AAB3
|
||||
!define COMMON_BKGRD_COLOR 0x0F1B26
|
||||
|
@ -14,7 +14,6 @@ BRANDING_FILES := \
|
||||
branding.nsi \
|
||||
appname.bmp \
|
||||
bgintro.bmp \
|
||||
bgplain.bmp \
|
||||
clock.bmp \
|
||||
particles.bmp \
|
||||
pencil.bmp \
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 666 KiB |
@ -42,8 +42,8 @@
|
||||
!define FOOTER_CONTROL_TEXT_COLOR_FADED 0x666666
|
||||
!define FOOTER_BKGRD_COLOR 0xFFFFFF
|
||||
!define INTRO_BLURB_TEXT_COLOR 0x666666
|
||||
!define OPTIONS_TEXT_COLOR_NORMAL 0x000000
|
||||
!define OPTIONS_TEXT_COLOR_FADED 0x666666
|
||||
!define OPTIONS_BKGRD_COLOR 0xF0F0F0
|
||||
!define INSTALL_BLURB_TEXT_COLOR 0x666666
|
||||
!define INSTALL_PROGRESS_TEXT_COLOR_NORMAL 0x666666
|
||||
!define COMMON_TEXT_COLOR_NORMAL 0x000000
|
||||
!define COMMON_TEXT_COLOR_FADED 0x666666
|
||||
!define COMMON_BKGRD_COLOR 0xF0F0F0
|
||||
|
@ -14,7 +14,6 @@ BRANDING_FILES := \
|
||||
branding.nsi \
|
||||
appname.bmp \
|
||||
bgintro.bmp \
|
||||
bgplain.bmp \
|
||||
clock.bmp \
|
||||
particles.bmp \
|
||||
pencil.bmp \
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 666 KiB |
@ -37,8 +37,8 @@
|
||||
!define FOOTER_CONTROL_TEXT_COLOR_FADED 0x999999
|
||||
!define FOOTER_BKGRD_COLOR 0xFFFFFF
|
||||
!define INTRO_BLURB_TEXT_COLOR 0xFFFFFF
|
||||
!define OPTIONS_TEXT_COLOR_NORMAL 0xFFFFFF
|
||||
!define OPTIONS_TEXT_COLOR_FADED 0xA1AAB3
|
||||
!define OPTIONS_BKGRD_COLOR 0x0F1B26
|
||||
!define INSTALL_BLURB_TEXT_COLOR 0xFFFFFF
|
||||
!define INSTALL_PROGRESS_TEXT_COLOR_NORMAL 0xFFFFFF
|
||||
!define COMMON_TEXT_COLOR_NORMAL 0xFFFFFF
|
||||
!define COMMON_TEXT_COLOR_FADED 0xA1AAB3
|
||||
!define COMMON_BKGRD_COLOR 0x0F1B26
|
||||
|
@ -24,6 +24,7 @@ DIRS += [
|
||||
'styleeditor',
|
||||
'styleinspector',
|
||||
'tilt',
|
||||
'webaudioeditor',
|
||||
'webconsole',
|
||||
]
|
||||
|
||||
@ -32,4 +33,4 @@ EXTRA_COMPONENTS += [
|
||||
'devtools-clhandler.manifest',
|
||||
]
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
12
browser/devtools/webaudioeditor/moz.build
Normal file
12
browser/devtools/webaudioeditor/moz.build
Normal file
@ -0,0 +1,12 @@
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
TEST_DIRS += ['test']
|
||||
|
||||
JS_MODULES_PATH = 'modules/devtools/webaudioeditor'
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
]
|
||||
|
12
browser/devtools/webaudioeditor/test/browser.ini
Normal file
12
browser/devtools/webaudioeditor/test/browser.ini
Normal file
@ -0,0 +1,12 @@
|
||||
[DEFAULT]
|
||||
|
||||
support-files =
|
||||
doc_simple-context.html
|
||||
doc_complex-context.html
|
||||
doc_simple-node-creation.html
|
||||
head.js
|
||||
|
||||
[browser_webaudio-actor-simple.js]
|
||||
[browser_audionode-actor-get-set-param.js]
|
||||
[browser_audionode-actor-is-source.js]
|
||||
[browser_audionode-actor-get-type.js]
|
@ -0,0 +1,51 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test AudioNode#getParam() / AudioNode#setParam()
|
||||
*/
|
||||
|
||||
function spawnTest () {
|
||||
let [target, debuggee, front] = yield initBackend(SIMPLE_CONTEXT_URL);
|
||||
let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
|
||||
front.setup({ reload: true }),
|
||||
get3(front, "create-node")
|
||||
]);
|
||||
|
||||
let freq = yield oscNode.getParam("frequency");
|
||||
info(typeof freq);
|
||||
ise(freq, 440, "AudioNode:getParam correctly fetches AudioParam");
|
||||
|
||||
let type = yield oscNode.getParam("type");
|
||||
ise(type, "sine", "AudioNode:getParam correctly fetches non-AudioParam");
|
||||
|
||||
let type = yield oscNode.getParam("not-a-valid-param");
|
||||
is(type, undefined, "AudioNode:getParam correctly returns false for invalid param");
|
||||
|
||||
let resSuccess = yield oscNode.setParam("frequency", 220);
|
||||
let freq = yield oscNode.getParam("frequency");
|
||||
ise(freq, 220, "AudioNode:setParam correctly sets a `number` AudioParam");
|
||||
is(resSuccess, undefined, "AudioNode:setParam returns undefined for correctly set AudioParam");
|
||||
|
||||
resSuccess = yield oscNode.setParam("type", "square");
|
||||
let type = yield oscNode.getParam("type");
|
||||
ise(type, "square", "AudioNode:setParam correctly sets a `string` non-AudioParam");
|
||||
is(resSuccess, undefined, "AudioNode:setParam returns undefined for correctly set AudioParam");
|
||||
|
||||
resSuccess = yield oscNode.setParam("type", "\"triangle\"");
|
||||
type = yield oscNode.getParam("type");
|
||||
ise(type, "triangle", "AudioNode:setParam correctly removes quotes in `string` non-AudioParam");
|
||||
|
||||
try {
|
||||
yield oscNode.setParam("frequency", "hello");
|
||||
ok(false, "setParam with invalid types should throw");
|
||||
} catch (e) {
|
||||
ok(/is not a finite floating-point/.test(e.message), "AudioNode:setParam returns error with correct message when attempting an invalid assignment");
|
||||
is(e.type, "TypeError", "AudioNode:setParam returns error with correct type when attempting an invalid assignment");
|
||||
freq = yield oscNode.getParam("frequency");
|
||||
ise(freq, 220, "AudioNode:setParam does not modify value when an error occurs");
|
||||
}
|
||||
|
||||
yield removeTab(target.tab);
|
||||
finish();
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test AudioNode#getType()
|
||||
*/
|
||||
|
||||
function spawnTest () {
|
||||
let [target, debuggee, front] = yield initBackend(SIMPLE_NODES_URL);
|
||||
let [_, nodes] = yield Promise.all([
|
||||
front.setup({ reload: true }),
|
||||
getN(front, "create-node", 14)
|
||||
]);
|
||||
|
||||
let actualTypes = yield Promise.all(nodes.map(node => node.getType()));
|
||||
let expectedTypes = [
|
||||
"AudioDestinationNode",
|
||||
"AudioBufferSourceNode", "ScriptProcessorNode", "AnalyserNode", "GainNode",
|
||||
"DelayNode", "BiquadFilterNode", "WaveShaperNode", "PannerNode", "ConvolverNode",
|
||||
"ChannelSplitterNode", "ChannelMergerNode", "DynamicsCompressorNode", "OscillatorNode"
|
||||
];
|
||||
|
||||
expectedTypes.forEach((type, i) => {
|
||||
is(actualTypes[i], type, type + " successfully created with correct type");
|
||||
});
|
||||
|
||||
yield removeTab(target.tab);
|
||||
finish();
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test AudioNode#isSource()
|
||||
*/
|
||||
|
||||
function spawnTest () {
|
||||
let [target, debuggee, front] = yield initBackend(SIMPLE_NODES_URL);
|
||||
let [_, nodes] = yield Promise.all([
|
||||
front.setup({ reload: true }),
|
||||
getN(front, "create-node", 14)
|
||||
]);
|
||||
|
||||
let actualTypes = yield Promise.all(nodes.map(node => node.getType()));
|
||||
let isSourceResult = yield Promise.all(nodes.map(node => node.isSource()));
|
||||
|
||||
actualTypes.forEach((type, i) => {
|
||||
let shouldBeSource = type === "AudioBufferSourceNode" || type === "OscillatorNode";
|
||||
if (shouldBeSource)
|
||||
is(isSourceResult[i], true, type + "'s isSource() yields into `true`");
|
||||
else
|
||||
is(isSourceResult[i], false, type + "'s isSource() yields into `false`");
|
||||
});
|
||||
|
||||
yield removeTab(target.tab);
|
||||
finish();
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test basic communication of Web Audio actor
|
||||
*/
|
||||
|
||||
function spawnTest () {
|
||||
let [target, debuggee, front] = yield initBackend(SIMPLE_CONTEXT_URL);
|
||||
let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = yield Promise.all([
|
||||
front.setup({ reload: true }),
|
||||
once(front, "start-context"),
|
||||
get3(front, "create-node"),
|
||||
get2(front, "connect-node")
|
||||
]);
|
||||
|
||||
let destType = yield destNode.getType();
|
||||
let oscType = yield oscNode.getType();
|
||||
let gainType = yield gainNode.getType();
|
||||
|
||||
is(destType, "AudioDestinationNode", "WebAudioActor:create-node returns AudioNodeActor for AudioDestination");
|
||||
is(oscType, "OscillatorNode", "WebAudioActor:create-node returns AudioNodeActor");
|
||||
is(gainType, "GainNode", "WebAudioActor:create-node returns AudioNodeActor");
|
||||
|
||||
let { source, dest } = connect1;
|
||||
is(source.actorID, oscNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on source (osc->gain)");
|
||||
is(dest.actorID, gainNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on dest (osc->gain)");
|
||||
|
||||
let { source, dest } = connect2;
|
||||
is(source.actorID, gainNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on source (gain->dest)");
|
||||
is(dest.actorID, destNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on dest (gain->dest)");
|
||||
|
||||
yield removeTab(target.tab);
|
||||
finish();
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Web Audio Editor test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script type="text/javascript;version=1.8">
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
↱ proc
|
||||
osc → gain →
|
||||
osc → gain → destination
|
||||
buffer →↳ filter →
|
||||
*/
|
||||
let ctx = new AudioContext();
|
||||
let osc1 = ctx.createOscillator();
|
||||
let gain1 = ctx.createGain();
|
||||
let proc = ctx.createScriptProcessor();
|
||||
osc1.connect(gain1);
|
||||
osc1.connect(proc);
|
||||
gain1.connect(ctx.destination);
|
||||
|
||||
let osc2 = ctx.createOscillator();
|
||||
let gain2 = ctx.createGain();
|
||||
osc2.connect(gain2);
|
||||
gain2.connect(ctx.destination);
|
||||
|
||||
let buf = ctx.createBufferSource();
|
||||
let filter = ctx.createBiquadFilter();
|
||||
buf.connect(filter);
|
||||
osc2.connect(filter);
|
||||
filter.connect(ctx.destination);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
26
browser/devtools/webaudioeditor/test/doc_simple-context.html
Normal file
26
browser/devtools/webaudioeditor/test/doc_simple-context.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Web Audio Editor test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script type="text/javascript;version=1.8">
|
||||
"use strict";
|
||||
|
||||
let ctx = new AudioContext();
|
||||
let osc = ctx.createOscillator();
|
||||
let gain = ctx.createGain();
|
||||
gain.gain.value = 0;
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(0);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,28 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Web Audio Editor test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script type="text/javascript;version=1.8">
|
||||
"use strict";
|
||||
|
||||
let ctx = new AudioContext();
|
||||
let NODE_CREATION_METHODS = [
|
||||
"createBufferSource", "createScriptProcessor", "createAnalyser",
|
||||
"createGain", "createDelay", "createBiquadFilter", "createWaveShaper",
|
||||
"createPanner", "createConvolver", "createChannelSplitter", "createChannelMerger",
|
||||
"createDynamicsCompressor", "createOscillator"
|
||||
];
|
||||
let nodes = NODE_CREATION_METHODS.map(method => ctx[method]());
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
160
browser/devtools/webaudioeditor/test/head.js
Normal file
160
browser/devtools/webaudioeditor/test/head.js
Normal file
@ -0,0 +1,160 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
// Enable logging for all the tests. Both the debugger server and frontend will
|
||||
// be affected by this pref.
|
||||
let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
|
||||
let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
|
||||
let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
||||
let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
|
||||
|
||||
let { WebAudioFront } = devtools.require("devtools/server/actors/webaudio");
|
||||
let TargetFactory = devtools.TargetFactory;
|
||||
let Toolbox = devtools.Toolbox;
|
||||
|
||||
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/webaudioeditor/test/";
|
||||
const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html";
|
||||
const COMPLEX_CONTEXT_URL = EXAMPLE_URL + "doc_complex-context.html";
|
||||
const SIMPLE_NODES_URL = EXAMPLE_URL + "doc_simple-node-creation.html";
|
||||
|
||||
// All tests are asynchronous.
|
||||
waitForExplicitFinish();
|
||||
|
||||
let gToolEnabled = Services.prefs.getBoolPref("devtools.webaudioeditor.enabled");
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
info("finish() was called, cleaning up...");
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
|
||||
Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", gToolEnabled);
|
||||
Cu.forceGC();
|
||||
});
|
||||
|
||||
function addTab(aUrl, aWindow) {
|
||||
info("Adding tab: " + aUrl);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
let targetWindow = aWindow || window;
|
||||
let targetBrowser = targetWindow.gBrowser;
|
||||
|
||||
targetWindow.focus();
|
||||
let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
|
||||
let linkedBrowser = tab.linkedBrowser;
|
||||
|
||||
linkedBrowser.addEventListener("load", function onLoad() {
|
||||
linkedBrowser.removeEventListener("load", onLoad, true);
|
||||
info("Tab added and finished loading: " + aUrl);
|
||||
deferred.resolve(tab);
|
||||
}, true);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function removeTab(aTab, aWindow) {
|
||||
info("Removing tab.");
|
||||
|
||||
let deferred = Promise.defer();
|
||||
let targetWindow = aWindow || window;
|
||||
let targetBrowser = targetWindow.gBrowser;
|
||||
let tabContainer = targetBrowser.tabContainer;
|
||||
|
||||
tabContainer.addEventListener("TabClose", function onClose(aEvent) {
|
||||
tabContainer.removeEventListener("TabClose", onClose, false);
|
||||
info("Tab removed and finished closing.");
|
||||
deferred.resolve();
|
||||
}, false);
|
||||
|
||||
targetBrowser.removeTab(aTab);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function handleError(aError) {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
finish();
|
||||
}
|
||||
|
||||
function once(aTarget, aEventName, aUseCapture = false) {
|
||||
info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
for (let [add, remove] of [
|
||||
["on", "off"], // Use event emitter before DOM events for consistency
|
||||
["addEventListener", "removeEventListener"],
|
||||
["addListener", "removeListener"]
|
||||
]) {
|
||||
if ((add in aTarget) && (remove in aTarget)) {
|
||||
aTarget[add](aEventName, function onEvent(...aArgs) {
|
||||
aTarget[remove](aEventName, onEvent, aUseCapture);
|
||||
deferred.resolve(...aArgs);
|
||||
}, aUseCapture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function test () {
|
||||
Task.spawn(spawnTest).then(finish, handleError);
|
||||
}
|
||||
|
||||
function initBackend(aUrl) {
|
||||
info("Initializing a web audio editor front.");
|
||||
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init(() => true);
|
||||
DebuggerServer.addBrowserActors();
|
||||
}
|
||||
|
||||
return Task.spawn(function*() {
|
||||
let tab = yield addTab(aUrl);
|
||||
let target = TargetFactory.forTab(tab);
|
||||
let debuggee = target.window.wrappedJSObject;
|
||||
|
||||
yield target.makeRemote();
|
||||
|
||||
let front = new WebAudioFront(target.client, target.form);
|
||||
return [target, debuggee, front];
|
||||
});
|
||||
}
|
||||
|
||||
// Due to web audio will fire most events synchronously back-to-back,
|
||||
// and we can't yield them in a chain without missing actors, this allows
|
||||
// us to listen for `n` events and return a promise resolving to them.
|
||||
//
|
||||
// Takes a `front` object that is an event emitter, the number of
|
||||
// programs that should be listened to and waited on, and an optional
|
||||
// `onAdd` function that calls with the entire actors array on program link
|
||||
function getN (front, eventName, count, spread) {
|
||||
let actors = [];
|
||||
let deferred = Promise.defer();
|
||||
front.on(eventName, function onEvent (...args) {
|
||||
let actor = args[0];
|
||||
if (actors.length !== count) {
|
||||
actors.push(spread ? args : actor);
|
||||
}
|
||||
if (actors.length === count) {
|
||||
front.off(eventName, onEvent);
|
||||
deferred.resolve(actors);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function get (front, eventName) { return getN(front, eventName, 1); }
|
||||
function get2 (front, eventName) { return getN(front, eventName, 2); }
|
||||
function get3 (front, eventName) { return getN(front, eventName, 3); }
|
||||
function getSpread (front, eventName) { return getN(front, eventName, 1, true); }
|
||||
function get2Spread (front, eventName) { return getN(front, eventName, 2, true); }
|
||||
function get3Spread (front, eventName) { return getN(front, eventName, 3, true); }
|
||||
function getNSpread (front, eventName, count) { return getN(front, eventName, count, true); }
|
6
browser/devtools/webaudioeditor/test/moz.build
Normal file
6
browser/devtools/webaudioeditor/test/moz.build
Normal file
@ -0,0 +1,6 @@
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
@ -28,7 +28,6 @@ BRANDING_FILES = \
|
||||
branding.nsi \
|
||||
appname.bmp \
|
||||
bgintro.bmp \
|
||||
bgplain.bmp \
|
||||
clock.bmp \
|
||||
particles.bmp \
|
||||
pencil.bmp \
|
||||
|
@ -422,7 +422,6 @@ Function .onInit
|
||||
|
||||
InitPluginsDir
|
||||
File /oname=$PLUGINSDIR\bgintro.bmp "bgintro.bmp"
|
||||
File /oname=$PLUGINSDIR\bgplain.bmp "bgplain.bmp"
|
||||
File /oname=$PLUGINSDIR\appname.bmp "appname.bmp"
|
||||
File /oname=$PLUGINSDIR\clock.bmp "clock.bmp"
|
||||
File /oname=$PLUGINSDIR\particles.bmp "particles.bmp"
|
||||
@ -866,12 +865,12 @@ Function createOptions
|
||||
Pop $Dialog
|
||||
; Since the text color for controls is set in this Dialog the foreground and
|
||||
; background colors of the Dialog must also be hardcoded.
|
||||
SetCtlColors $Dialog ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $Dialog ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
|
||||
${NSD_CreateLabel} ${OPTIONS_ITEM_EDGE_DU} 18u ${OPTIONS_ITEM_WIDTH_DU} \
|
||||
12u "$(CREATE_SHORTCUTS)"
|
||||
Pop $0
|
||||
SetCtlColors $0 ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $0 ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $0 ${WM_SETFONT} $FontNormal 0
|
||||
|
||||
${If} ${AtLeastWin7}
|
||||
@ -885,7 +884,7 @@ Function createOptions
|
||||
; The uxtheme must be disabled on checkboxes in order to override the system
|
||||
; font color.
|
||||
System::Call 'uxtheme::SetWindowTheme(i $CheckboxShortcutOnBar, w " ", w " ")'
|
||||
SetCtlColors $CheckboxShortcutOnBar ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $CheckboxShortcutOnBar ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $CheckboxShortcutOnBar ${WM_SETFONT} $FontNormal 0
|
||||
${NSD_Check} $CheckboxShortcutOnBar
|
||||
|
||||
@ -895,7 +894,7 @@ Function createOptions
|
||||
; The uxtheme must be disabled on checkboxes in order to override the system
|
||||
; font color.
|
||||
System::Call 'uxtheme::SetWindowTheme(i $CheckboxShortcutInStartMenu, w " ", w " ")'
|
||||
SetCtlColors $CheckboxShortcutInStartMenu ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $CheckboxShortcutInStartMenu ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $CheckboxShortcutInStartMenu ${WM_SETFONT} $FontNormal 0
|
||||
${NSD_Check} $CheckboxShortcutInStartMenu
|
||||
|
||||
@ -905,19 +904,19 @@ Function createOptions
|
||||
; The uxtheme must be disabled on checkboxes in order to override the system
|
||||
; font color.
|
||||
System::Call 'uxtheme::SetWindowTheme(i $CheckboxShortcutOnDesktop, w " ", w " ")'
|
||||
SetCtlColors $CheckboxShortcutOnDesktop ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $CheckboxShortcutOnDesktop ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $CheckboxShortcutOnDesktop ${WM_SETFONT} $FontNormal 0
|
||||
${NSD_Check} $CheckboxShortcutOnDesktop
|
||||
|
||||
${NSD_CreateLabel} ${OPTIONS_ITEM_EDGE_DU} 100u ${OPTIONS_ITEM_WIDTH_DU} \
|
||||
12u "$(DEST_FOLDER)"
|
||||
Pop $0
|
||||
SetCtlColors $0 ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $0 ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $0 ${WM_SETFONT} $FontNormal 0
|
||||
|
||||
${NSD_CreateDirRequest} ${OPTIONS_SUBITEM_EDGE_DU} 116u 159u 14u "$INSTDIR"
|
||||
Pop $DirRequest
|
||||
SetCtlColors $DirRequest ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $DirRequest ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $DirRequest ${WM_SETFONT} $FontNormal 0
|
||||
System::Call shlwapi::SHAutoComplete(i $DirRequest, i ${SHACF_FILESYSTEM})
|
||||
${NSD_OnChange} $DirRequest OnChange_DirRequest
|
||||
@ -933,7 +932,7 @@ Function createOptions
|
||||
|
||||
${NSD_CreateBrowseButton} 280u 116u 50u 14u "$(BROWSE_BUTTON)"
|
||||
Pop $ButtonBrowse
|
||||
SetCtlColors $ButtonBrowse "" ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $ButtonBrowse "" ${COMMON_BKGRD_COLOR}
|
||||
${NSD_OnClick} $ButtonBrowse OnClick_ButtonBrowse
|
||||
|
||||
; Get the number of pixels from the left of the Dialog to the right side of
|
||||
@ -954,13 +953,13 @@ Function createOptions
|
||||
; Make both controls the same width as the widest control
|
||||
${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 134u $0 $ControlHeightPX "$(SPACE_REQUIRED)"
|
||||
Pop $5
|
||||
SetCtlColors $5 ${OPTIONS_TEXT_COLOR_FADED} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $5 ${COMMON_TEXT_COLOR_FADED} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $5 ${WM_SETFONT} $FontItalic 0
|
||||
|
||||
IntOp $2 $2 + 8 ; Add padding to the control's width
|
||||
${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 145u $2 $ControlHeightPX "$(SPACE_AVAILABLE)"
|
||||
Pop $6
|
||||
SetCtlColors $6 ${OPTIONS_TEXT_COLOR_FADED} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $6 ${COMMON_TEXT_COLOR_FADED} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $6 ${WM_SETFONT} $FontItalic 0
|
||||
|
||||
; Use the widest label for aligning the labels next to them
|
||||
@ -975,14 +974,14 @@ Function createOptions
|
||||
${NSD_CreateLabel} $ControlRightPX 134u 100% $ControlHeightPX \
|
||||
"${APPROXIMATE_REQUIRED_SPACE_MB} $(MEGA)$(BYTE)"
|
||||
Pop $7
|
||||
SetCtlColors $7 ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $7 ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $7 ${WM_SETFONT} $FontNormal 0
|
||||
|
||||
; Create the free space label with an empty string and update it by calling
|
||||
; UpdateFreeSpaceLabel
|
||||
${NSD_CreateLabel} $ControlRightPX 145u 100% $ControlHeightPX " "
|
||||
Pop $LabelFreeSpace
|
||||
SetCtlColors $LabelFreeSpace ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $LabelFreeSpace ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $LabelFreeSpace ${WM_SETFONT} $FontNormal 0
|
||||
|
||||
Call UpdateFreeSpaceLabel
|
||||
@ -993,7 +992,7 @@ Function createOptions
|
||||
; The uxtheme must be disabled on checkboxes in order to override the system
|
||||
; font color.
|
||||
System::Call 'uxtheme::SetWindowTheme(i $CheckboxSendPing, w " ", w " ")'
|
||||
SetCtlColors $CheckboxSendPing ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $CheckboxSendPing ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $CheckboxSendPing ${WM_SETFONT} $FontNormal 0
|
||||
${NSD_Check} $CheckboxSendPing
|
||||
|
||||
@ -1018,7 +1017,7 @@ Function createOptions
|
||||
12u "$(INSTALL_MAINT_SERVICE)"
|
||||
Pop $CheckboxInstallMaintSvc
|
||||
System::Call 'uxtheme::SetWindowTheme(i $CheckboxInstallMaintSvc, w " ", w " ")'
|
||||
SetCtlColors $CheckboxInstallMaintSvc ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SetCtlColors $CheckboxInstallMaintSvc ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
SendMessage $CheckboxInstallMaintSvc ${WM_SETFONT} $FontNormal 0
|
||||
${NSD_Check} $CheckboxInstallMaintSvc
|
||||
${EndIf}
|
||||
@ -1105,6 +1104,9 @@ FunctionEnd
|
||||
Function createInstall
|
||||
nsDialogs::Create /NOUNLOAD 1018
|
||||
Pop $Dialog
|
||||
; Since the text color for controls is set in this Dialog the foreground and
|
||||
; background colors of the Dialog must also be hardcoded.
|
||||
SetCtlColors $Dialog ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
|
||||
${NSD_CreateLabel} 0 0 49u 64u ""
|
||||
Pop $0
|
||||
@ -1197,10 +1199,6 @@ Function createInstall
|
||||
Pop $2
|
||||
${SetStretchedTransparentImage} $2 $PLUGINSDIR\appname.bmp $0
|
||||
|
||||
${NSD_CreateBitmap} 0 0 100% 100% ""
|
||||
Pop $3
|
||||
${NSD_SetStretchedImage} $3 $PLUGINSDIR\bgplain.bmp $1
|
||||
|
||||
GetDlgItem $0 $HWNDPARENT 1 ; Install button
|
||||
EnableWindow $0 0
|
||||
ShowWindow $0 ${SW_HIDE}
|
||||
@ -1280,7 +1278,6 @@ Function createInstall
|
||||
nsDialogs::Show
|
||||
|
||||
${NSD_FreeImage} $0
|
||||
${NSD_FreeImage} $1
|
||||
${NSD_FreeImage} $HwndBitmapBlurb1
|
||||
${NSD_FreeImage} $HwndBitmapBlurb2
|
||||
${NSD_FreeImage} $HWndBitmapBlurb3
|
||||
|
@ -2703,6 +2703,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
final String appLocale,
|
||||
final SessionInformation previousSession) {
|
||||
return new BrowserHealthRecorder(context,
|
||||
GeckoSharedPrefs.forApp(context),
|
||||
profilePath,
|
||||
dispatcher,
|
||||
osLocale,
|
||||
|
@ -5,14 +5,14 @@
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.widget.GeckoEditText;
|
||||
import org.mozilla.gecko.widget.ThemedEditText;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
|
||||
public class CustomEditText extends GeckoEditText {
|
||||
public class CustomEditText extends ThemedEditText {
|
||||
private OnKeyPreImeListener mOnKeyPreImeListener;
|
||||
private OnSelectionChangedListener mOnSelectionChangedListener;
|
||||
private OnWindowFocusChangeListener mOnWindowFocusChangeListener;
|
||||
|
@ -175,7 +175,6 @@ public abstract class GeckoApp
|
||||
private View mCameraView;
|
||||
private OrientationEventListener mCameraOrientationEventListener;
|
||||
public List<GeckoAppShell.AppStateListener> mAppStateListeners;
|
||||
private static volatile GeckoApp sAppContext;
|
||||
protected MenuPanel mMenuPanel;
|
||||
protected Menu mMenu;
|
||||
protected GeckoProfile mProfile;
|
||||
@ -234,20 +233,12 @@ public abstract class GeckoApp
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return sAppContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferences getSharedPreferences() {
|
||||
return GeckoApp.getAppSharedPreferences();
|
||||
}
|
||||
|
||||
public static SharedPreferences getAppSharedPreferences() {
|
||||
if (sAppContext == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return GeckoSharedPrefs.forApp(sAppContext);
|
||||
return GeckoSharedPrefs.forApp(this);
|
||||
}
|
||||
|
||||
public Activity getActivity() {
|
||||
@ -674,7 +665,7 @@ public abstract class GeckoApp
|
||||
Log.e(LOGTAG, "Received Contact:Add message with no email nor phone number");
|
||||
}
|
||||
} else if (event.equals("Intent:GetHandlers")) {
|
||||
Intent intent = GeckoAppShell.getOpenURIIntent(sAppContext, message.optString("url"),
|
||||
Intent intent = GeckoAppShell.getOpenURIIntent((Context) this, message.optString("url"),
|
||||
message.optString("mime"), message.optString("action"), message.optString("title"));
|
||||
String[] handlers = GeckoAppShell.getHandlersForIntent(intent);
|
||||
List<String> appList = Arrays.asList(handlers);
|
||||
@ -1031,16 +1022,16 @@ public abstract class GeckoApp
|
||||
intent.setData(Uri.parse(path));
|
||||
|
||||
// Removes the image from storage once the chooser activity ends.
|
||||
ActivityHandlerHelper.startIntentForActivity(this,
|
||||
Intent.createChooser(intent, sAppContext.getString(R.string.set_image_chooser_title)),
|
||||
new ActivityResultHandler() {
|
||||
@Override
|
||||
public void onActivityResult (int resultCode, Intent data) {
|
||||
getContentResolver().delete(intent.getData(), null, null);
|
||||
}
|
||||
});
|
||||
Intent chooser = Intent.createChooser(intent, getString(R.string.set_image_chooser_title));
|
||||
ActivityResultHandler handler = new ActivityResultHandler() {
|
||||
@Override
|
||||
public void onActivityResult (int resultCode, Intent data) {
|
||||
getContentResolver().delete(intent.getData(), null, null);
|
||||
}
|
||||
};
|
||||
ActivityHandlerHelper.startIntentForActivity(this, chooser, handler);
|
||||
} else {
|
||||
Toast.makeText(sAppContext, R.string.set_image_fail, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText((Context) this, R.string.set_image_fail, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} catch(OutOfMemoryError ome) {
|
||||
Log.e(LOGTAG, "Out of Memory when converting to byte array", ome);
|
||||
@ -1179,10 +1170,16 @@ public abstract class GeckoApp
|
||||
mJavaUiStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_JAVAUI");
|
||||
mGeckoReadyStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_GECKOREADY");
|
||||
|
||||
Intent intent = getIntent();
|
||||
String args = intent.getStringExtra("args");
|
||||
final Intent intent = getIntent();
|
||||
final String args = intent.getStringExtra("args");
|
||||
|
||||
earlyStartJavaSampler(intent);
|
||||
|
||||
// GeckoLoader wants to dig some environment variables out of the
|
||||
// incoming intent, so pass it in here. GeckoLoader will do its
|
||||
// business later and dispose of the reference.
|
||||
GeckoLoader.setLastIntent(intent);
|
||||
|
||||
if (mProfile == null) {
|
||||
String profileName = null;
|
||||
String profilePath = null;
|
||||
@ -1232,9 +1229,15 @@ public abstract class GeckoApp
|
||||
|
||||
MemoryMonitor.getInstance().init(getApplicationContext());
|
||||
|
||||
sAppContext = this;
|
||||
// GeckoAppShell is tightly coupled to us, rather than
|
||||
// the app context, because various parts of Fennec (e.g.,
|
||||
// GeckoScreenOrientation) use GAS to access the Activity in
|
||||
// the guise of fetching a Context.
|
||||
// When that's fixed, `this` can change to
|
||||
// `(GeckoApplication) getApplication()` here.
|
||||
GeckoAppShell.setContextGetter(this);
|
||||
GeckoAppShell.setGeckoInterface(this);
|
||||
|
||||
ThreadUtils.setUiThread(Thread.currentThread(), new Handler());
|
||||
|
||||
Tabs.getInstance().attachToContext(this);
|
||||
@ -1284,7 +1287,7 @@ public abstract class GeckoApp
|
||||
// only intended to be used internally via Robocop, so a boolean
|
||||
// is read from a private shared pref to prevent other apps from
|
||||
// injecting states.
|
||||
SharedPreferences prefs = getAppSharedPreferences();
|
||||
final SharedPreferences prefs = getSharedPreferences();
|
||||
if (prefs.getBoolean(PREFS_ALLOW_STATE_BUNDLE, false)) {
|
||||
Log.i(LOGTAG, "Restoring state from intent bundle");
|
||||
prefs.edit().remove(PREFS_ALLOW_STATE_BUNDLE).commit();
|
||||
@ -1324,12 +1327,11 @@ public abstract class GeckoApp
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
|
||||
final SharedPreferences prefs = GeckoApp.this.getSharedPreferences();
|
||||
|
||||
// Wait until now to set this, because we'd rather throw an exception than
|
||||
// have a caller of LocaleManager regress startup.
|
||||
LocaleManager.setContextGetter(GeckoApp.this);
|
||||
LocaleManager.initialize();
|
||||
LocaleManager.initialize(getApplicationContext());
|
||||
|
||||
SessionInformation previousSession = SessionInformation.fromSharedPrefs(prefs);
|
||||
if (previousSession.wasKilled()) {
|
||||
@ -1353,7 +1355,7 @@ public abstract class GeckoApp
|
||||
Log.i(LOGTAG, "Creating HealthRecorder.");
|
||||
|
||||
final String osLocale = Locale.getDefault().toString();
|
||||
String appLocale = LocaleManager.getAndApplyPersistedLocale();
|
||||
String appLocale = LocaleManager.getAndApplyPersistedLocale(GeckoApp.this);
|
||||
Log.d(LOGTAG, "OS locale is " + osLocale + ", app locale is " + appLocale);
|
||||
|
||||
if (appLocale == null) {
|
||||
@ -1743,7 +1745,7 @@ public abstract class GeckoApp
|
||||
* @return Whether to restore
|
||||
*/
|
||||
protected boolean getSessionRestoreState(Bundle savedInstanceState) {
|
||||
final SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
|
||||
final SharedPreferences prefs = getSharedPreferences();
|
||||
boolean shouldRestore = false;
|
||||
|
||||
final int versionCode = getVersionCode();
|
||||
@ -1770,7 +1772,7 @@ public abstract class GeckoApp
|
||||
}
|
||||
|
||||
private String getSessionRestorePreference() {
|
||||
return getAppSharedPreferences().getString(GeckoPreferences.PREFS_RESTORE_SESSION, "quit");
|
||||
return getSharedPreferences().getString(GeckoPreferences.PREFS_RESTORE_SESSION, "quit");
|
||||
}
|
||||
|
||||
private boolean getRestartFromIntent() {
|
||||
@ -2002,7 +2004,7 @@ public abstract class GeckoApp
|
||||
// so it can benefit from a single near-startup prefs commit.
|
||||
SessionInformation currentSession = new SessionInformation(now, realTime);
|
||||
|
||||
SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
|
||||
SharedPreferences prefs = GeckoApp.this.getSharedPreferences();
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, false);
|
||||
currentSession.recordBegin(editor);
|
||||
@ -2040,7 +2042,7 @@ public abstract class GeckoApp
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
|
||||
SharedPreferences prefs = GeckoApp.this.getSharedPreferences();
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, true);
|
||||
if (rec != null) {
|
||||
@ -2079,7 +2081,7 @@ public abstract class GeckoApp
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
|
||||
SharedPreferences prefs = GeckoApp.this.getSharedPreferences();
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, false);
|
||||
editor.commit();
|
||||
@ -2181,7 +2183,7 @@ public abstract class GeckoApp
|
||||
|
||||
// Get a temporary directory, may return null
|
||||
public static File getTempDirectory() {
|
||||
File dir = sAppContext.getExternalFilesDir("temp");
|
||||
File dir = GeckoApplication.get().getExternalFilesDir("temp");
|
||||
return dir;
|
||||
}
|
||||
|
||||
@ -2201,7 +2203,7 @@ public abstract class GeckoApp
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
Log.d(LOGTAG, "onConfigurationChanged: " + newConfig.locale);
|
||||
LocaleManager.correctLocale(getResources(), newConfig);
|
||||
LocaleManager.correctLocale(this, getResources(), newConfig);
|
||||
|
||||
// onConfigurationChanged is not called for 180 degree orientation changes,
|
||||
// we will miss such rotations and the screen orientation will not be
|
||||
@ -2273,8 +2275,6 @@ public abstract class GeckoApp
|
||||
final File profileDir = getProfile().getDir();
|
||||
|
||||
if (profileDir != null) {
|
||||
final GeckoApp app = GeckoApp.sAppContext;
|
||||
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -2295,7 +2295,7 @@ public abstract class GeckoApp
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long cleanupVersion = getAppSharedPreferences().getInt(CLEANUP_VERSION, 0);
|
||||
long cleanupVersion = getSharedPreferences().getInt(CLEANUP_VERSION, 0);
|
||||
|
||||
if (cleanupVersion < 1) {
|
||||
// Reduce device storage footprint by removing .ttf files from
|
||||
@ -2321,7 +2321,7 @@ public abstract class GeckoApp
|
||||
// Additional cleanup needed for future versions would go here
|
||||
|
||||
if (cleanupVersion != CURRENT_CLEANUP_VERSION) {
|
||||
SharedPreferences.Editor editor = getAppSharedPreferences().edit();
|
||||
SharedPreferences.Editor editor = GeckoApp.this.getSharedPreferences().edit();
|
||||
editor.putInt(CLEANUP_VERSION, CURRENT_CLEANUP_VERSION);
|
||||
editor.commit();
|
||||
}
|
||||
@ -2802,7 +2802,7 @@ public abstract class GeckoApp
|
||||
if (locale == null) {
|
||||
return;
|
||||
}
|
||||
final String resultant = LocaleManager.setSelectedLocale(locale);
|
||||
final String resultant = LocaleManager.setSelectedLocale(this, locale);
|
||||
if (resultant == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -14,17 +14,40 @@ import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.util.Log;
|
||||
|
||||
public class GeckoApplication extends Application {
|
||||
public class GeckoApplication extends Application
|
||||
implements ContextGetter {
|
||||
private static final String LOG_TAG = "GeckoApplication";
|
||||
|
||||
private static volatile GeckoApplication instance;
|
||||
|
||||
private boolean mInBackground;
|
||||
private boolean mPausedGecko;
|
||||
|
||||
private LightweightTheme mLightweightTheme;
|
||||
|
||||
public GeckoApplication() {
|
||||
super();
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public static GeckoApplication get() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferences getSharedPreferences() {
|
||||
return GeckoSharedPrefs.forApp(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to do locale work here, because we need to intercept
|
||||
* each hit to onConfigurationChanged.
|
||||
@ -44,7 +67,7 @@ public class GeckoApplication extends Application {
|
||||
// Otherwise, correct the locale. This catches some cases that GeckoApp
|
||||
// doesn't get a chance to.
|
||||
try {
|
||||
LocaleManager.correctLocale(getResources(), config);
|
||||
LocaleManager.correctLocale(this, getResources(), config);
|
||||
} catch (IllegalStateException ex) {
|
||||
// GeckoApp hasn't started, so we have no ContextGetter in LocaleManager.
|
||||
Log.w(LOG_TAG, "Couldn't correct locale.", ex);
|
||||
|
@ -59,9 +59,14 @@ public class GeckoNetworkManager extends BroadcastReceiver {
|
||||
|
||||
static private final ConnectionType kDefaultConnectionType = ConnectionType.NONE;
|
||||
|
||||
private Context mApplicationContext;
|
||||
private static Context getApplicationContext() {
|
||||
Context context = GeckoAppShell.getContext();
|
||||
if (null == context)
|
||||
return null;
|
||||
return context.getApplicationContext();
|
||||
}
|
||||
private ConnectionType mConnectionType = ConnectionType.NONE;
|
||||
private final IntentFilter mNetworkFilter = new IntentFilter();
|
||||
private final IntentFilter mNetworkFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
|
||||
// Whether the manager should be listening to Network Information changes.
|
||||
private boolean mShouldBeListening = false;
|
||||
@ -81,9 +86,7 @@ public class GeckoNetworkManager extends BroadcastReceiver {
|
||||
|
||||
public void start(final Context context) {
|
||||
// Note that this initialization clause only runs once.
|
||||
if (mApplicationContext == null) {
|
||||
mApplicationContext = context.getApplicationContext();
|
||||
mNetworkFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
if (mConnectionType == ConnectionType.NONE) {
|
||||
mConnectionType = getConnectionType();
|
||||
}
|
||||
|
||||
@ -96,7 +99,8 @@ public class GeckoNetworkManager extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
private void startListening() {
|
||||
mApplicationContext.registerReceiver(sInstance, mNetworkFilter);
|
||||
if (null !=getApplicationContext())
|
||||
getApplicationContext().registerReceiver(sInstance, mNetworkFilter);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@ -108,15 +112,21 @@ public class GeckoNetworkManager extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
private void stopListening() {
|
||||
mApplicationContext.unregisterReceiver(sInstance);
|
||||
if (null != getApplicationContext())
|
||||
getApplicationContext().unregisterReceiver(sInstance);
|
||||
}
|
||||
|
||||
private int wifiDhcpGatewayAddress() {
|
||||
if (mConnectionType != ConnectionType.WIFI) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (null == getApplicationContext()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
WifiManager mgr = (WifiManager)sInstance.mApplicationContext.getSystemService(Context.WIFI_SERVICE);
|
||||
WifiManager mgr = (WifiManager)getApplicationContext().getSystemService(Context.WIFI_SERVICE);
|
||||
DhcpInfo d = mgr.getDhcpInfo();
|
||||
if (d == null) {
|
||||
return 0;
|
||||
@ -173,8 +183,12 @@ public class GeckoNetworkManager extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
private static ConnectionType getConnectionType() {
|
||||
if (null == getApplicationContext()) {
|
||||
return ConnectionType.NONE;
|
||||
}
|
||||
|
||||
ConnectivityManager cm =
|
||||
(ConnectivityManager)sInstance.mApplicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
(ConnectivityManager)getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (cm == null) {
|
||||
Log.e(LOGTAG, "Connectivity service does not exist");
|
||||
return ConnectionType.NONE;
|
||||
@ -206,7 +220,10 @@ public class GeckoNetworkManager extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
private static int getNetworkOperator(InfoType type) {
|
||||
TelephonyManager tel = (TelephonyManager)sInstance.mApplicationContext.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (null == getApplicationContext())
|
||||
return -1;
|
||||
|
||||
TelephonyManager tel = (TelephonyManager)getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (tel == null) {
|
||||
Log.e(LOGTAG, "Telephony service does not exist");
|
||||
return -1;
|
||||
|
@ -19,10 +19,8 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.app.Activity;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class GeckoThread extends Thread implements GeckoEventListener {
|
||||
@ -97,7 +95,7 @@ public class GeckoThread extends Thread implements GeckoEventListener {
|
||||
Locale.setDefault(locale);
|
||||
}
|
||||
|
||||
Context app = GeckoAppShell.getContext();
|
||||
Context context = GeckoAppShell.getContext();
|
||||
String resourcePath = "";
|
||||
Resources res = null;
|
||||
String[] pluginDirs = null;
|
||||
@ -106,16 +104,14 @@ public class GeckoThread extends Thread implements GeckoEventListener {
|
||||
} catch (Exception e) {
|
||||
Log.w(LOGTAG, "Caught exception getting plugin dirs.", e);
|
||||
}
|
||||
|
||||
if (app instanceof Activity) {
|
||||
Activity activity = (Activity)app;
|
||||
resourcePath = activity.getApplication().getPackageResourcePath();
|
||||
res = activity.getBaseContext().getResources();
|
||||
GeckoLoader.setupGeckoEnvironment(activity, pluginDirs, app.getFilesDir().getPath());
|
||||
}
|
||||
GeckoLoader.loadSQLiteLibs(app, resourcePath);
|
||||
GeckoLoader.loadNSSLibs(app, resourcePath);
|
||||
GeckoLoader.loadGeckoLibs(app, resourcePath);
|
||||
|
||||
resourcePath = context.getPackageResourcePath();
|
||||
res = context.getResources();
|
||||
GeckoLoader.setupGeckoEnvironment(context, pluginDirs, context.getFilesDir().getPath());
|
||||
|
||||
GeckoLoader.loadSQLiteLibs(context, resourcePath);
|
||||
GeckoLoader.loadNSSLibs(context, resourcePath);
|
||||
GeckoLoader.loadGeckoLibs(context, resourcePath);
|
||||
GeckoJavaSampler.setLibsLoaded();
|
||||
|
||||
Locale.setDefault(locale);
|
||||
|
@ -33,22 +33,24 @@ import java.util.Locale;
|
||||
*/
|
||||
public class LocaleManager {
|
||||
private static final String LOG_TAG = "GeckoLocales";
|
||||
private static final String PREF_LOCALE = "locale";
|
||||
|
||||
// These are both volatile because we don't impose restrictions
|
||||
// This is volatile because we don't impose restrictions
|
||||
// over which thread calls our methods.
|
||||
private static volatile ContextGetter getter = null;
|
||||
private static volatile Locale currentLocale = null;
|
||||
|
||||
private static volatile boolean inited = false;
|
||||
private static boolean systemLocaleDidChange = false;
|
||||
private static BroadcastReceiver receiver;
|
||||
|
||||
public static void setContextGetter(ContextGetter getter) {
|
||||
Log.d(LOG_TAG, "Calling setContextGetter: " + getter);
|
||||
LocaleManager.getter = getter;
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
/**
|
||||
* Ensure that you call this early in your application startup,
|
||||
* and with a context that's sufficiently long-lived (typically
|
||||
* the application context).
|
||||
*
|
||||
* Calling multiple times is harmless.
|
||||
*/
|
||||
public static void initialize(final Context context) {
|
||||
if (inited) {
|
||||
return;
|
||||
}
|
||||
@ -59,7 +61,7 @@ public class LocaleManager {
|
||||
systemLocaleDidChange = true;
|
||||
}
|
||||
};
|
||||
getContext().registerReceiver(receiver, new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
|
||||
context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
|
||||
inited = true;
|
||||
}
|
||||
|
||||
@ -67,26 +69,12 @@ public class LocaleManager {
|
||||
return systemLocaleDidChange;
|
||||
}
|
||||
|
||||
private static Context getContext() {
|
||||
if (getter == null) {
|
||||
throw new IllegalStateException("No ContextGetter; cannot fetch context.");
|
||||
}
|
||||
return getter.getContext();
|
||||
}
|
||||
|
||||
private static SharedPreferences getSharedPreferences() {
|
||||
if (getter == null) {
|
||||
throw new IllegalStateException("No ContextGetter; cannot fetch prefs.", new RuntimeException("No prefs."));
|
||||
}
|
||||
return getter.getSharedPreferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Every time the system gives us a new configuration, it
|
||||
* carries the external locale. Fix it.
|
||||
*/
|
||||
public static void correctLocale(Resources res, Configuration config) {
|
||||
Locale current = getCurrentLocale();
|
||||
public static void correctLocale(Context context, Resources res, Configuration config) {
|
||||
final Locale current = getCurrentLocale(context);
|
||||
if (current == null) {
|
||||
return;
|
||||
}
|
||||
@ -152,12 +140,12 @@ public class LocaleManager {
|
||||
return language + "-" + country;
|
||||
}
|
||||
|
||||
public static Locale getCurrentLocale() {
|
||||
public static Locale getCurrentLocale(Context context) {
|
||||
if (currentLocale != null) {
|
||||
return currentLocale;
|
||||
}
|
||||
|
||||
final String current = getPersistedLocale();
|
||||
final String current = getPersistedLocale(context);
|
||||
if (current == null) {
|
||||
return null;
|
||||
}
|
||||
@ -171,7 +159,7 @@ public class LocaleManager {
|
||||
*
|
||||
* Does not notify Gecko.
|
||||
*/
|
||||
private static String updateLocale(String localeCode) {
|
||||
private static String updateLocale(Context context, String localeCode) {
|
||||
// Fast path.
|
||||
final Locale defaultLocale = Locale.getDefault();
|
||||
if (defaultLocale.toString().equals(localeCode)) {
|
||||
@ -189,7 +177,7 @@ public class LocaleManager {
|
||||
currentLocale = locale;
|
||||
|
||||
// Update resources.
|
||||
Resources res = getContext().getResources();
|
||||
Resources res = context.getResources();
|
||||
Configuration config = res.getConfiguration();
|
||||
config.locale = locale;
|
||||
res.updateConfiguration(config, res.getDisplayMetrics());
|
||||
@ -203,17 +191,9 @@ public class LocaleManager {
|
||||
GeckoAppShell.sendEventToGecko(ev);
|
||||
}
|
||||
|
||||
private static String getPrefName() {
|
||||
return getContext().getPackageName() + ".locale";
|
||||
}
|
||||
|
||||
public static String getPersistedLocale() {
|
||||
final SharedPreferences settings = getSharedPreferences();
|
||||
|
||||
// N.B., it is expected that any per-profile settings will be
|
||||
// implemented via SharedPreferences multiplexing in ContextGetter, not
|
||||
// via profile-annotated preference names.
|
||||
final String locale = settings.getString(getPrefName(), "");
|
||||
public static String getPersistedLocale(Context context) {
|
||||
final SharedPreferences settings = getSharedPreferences(context);
|
||||
final String locale = settings.getString(PREF_LOCALE, "");
|
||||
|
||||
if ("".equals(locale)) {
|
||||
return null;
|
||||
@ -221,21 +201,26 @@ public class LocaleManager {
|
||||
return locale;
|
||||
}
|
||||
|
||||
private static void persistLocale(String localeCode) {
|
||||
final SharedPreferences settings = getSharedPreferences();
|
||||
settings.edit().putString(getPrefName(), localeCode).commit();
|
||||
private static void persistLocale(Context context, String localeCode) {
|
||||
final SharedPreferences settings = getSharedPreferences(context);
|
||||
settings.edit().putString(PREF_LOCALE, localeCode).commit();
|
||||
}
|
||||
|
||||
public static String getAndApplyPersistedLocale() {
|
||||
private static SharedPreferences getSharedPreferences(Context context) {
|
||||
// TODO: this should be per-profile, but we don't want to pay the price.
|
||||
return GeckoSharedPrefs.forApp(context);
|
||||
}
|
||||
|
||||
public static String getAndApplyPersistedLocale(Context context) {
|
||||
final long t1 = android.os.SystemClock.uptimeMillis();
|
||||
final String localeCode = getPersistedLocale();
|
||||
final String localeCode = getPersistedLocale(context);
|
||||
if (localeCode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note that we don't tell Gecko about this. We notify Gecko when the
|
||||
// locale is set, not when we update Java.
|
||||
final String resultant = updateLocale(localeCode);
|
||||
final String resultant = updateLocale(context, localeCode);
|
||||
|
||||
final long t2 = android.os.SystemClock.uptimeMillis();
|
||||
Log.i(LOG_TAG, "Locale read and update took: " + (t2 - t1) + "ms.");
|
||||
@ -247,16 +232,16 @@ public class LocaleManager {
|
||||
*
|
||||
* Always persists and notifies Gecko.
|
||||
*/
|
||||
public static String setSelectedLocale(String localeCode) {
|
||||
final String resultant = updateLocale(localeCode);
|
||||
public static String setSelectedLocale(Context context, String localeCode) {
|
||||
final String resultant = updateLocale(context, localeCode);
|
||||
|
||||
// We always persist and notify Gecko, even if nothing seemed to
|
||||
// change. This might happen if you're picking a locale that's the same
|
||||
// as the current OS locale. The OS locale might change next time we
|
||||
// launch, and we need the Gecko pref and persisted locale to have been
|
||||
// set by the time that happens.
|
||||
persistLocale(localeCode);
|
||||
notifyGeckoOfLocaleChange(getCurrentLocale());
|
||||
persistLocale(context, localeCode);
|
||||
notifyGeckoOfLocaleChange(getCurrentLocale(context));
|
||||
return resultant;
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ public class Tab {
|
||||
private ErrorType mErrorType = ErrorType.NONE;
|
||||
private static final int MAX_HISTORY_LIST_SIZE = 50;
|
||||
private volatile int mLoadProgress;
|
||||
private volatile int mRecordingCount = 0;
|
||||
|
||||
public static final int STATE_DELAYED = 0;
|
||||
public static final int STATE_LOADING = 1;
|
||||
@ -814,4 +815,16 @@ public class Tab {
|
||||
public int getLoadProgress() {
|
||||
return mLoadProgress;
|
||||
}
|
||||
|
||||
public void setRecording(boolean isRecording) {
|
||||
if (isRecording) {
|
||||
mRecordingCount++;
|
||||
} else {
|
||||
mRecordingCount--;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRecording() {
|
||||
return mRecordingCount > 0;
|
||||
}
|
||||
}
|
||||
|
@ -104,6 +104,9 @@ public class Tabs implements GeckoEventListener {
|
||||
registerEventListener("Link:OpenSearch");
|
||||
registerEventListener("DesktopMode:Changed");
|
||||
registerEventListener("Tab:ViewportMetadata");
|
||||
registerEventListener("Tab:StreamStart");
|
||||
registerEventListener("Tab:StreamStop");
|
||||
|
||||
}
|
||||
|
||||
public synchronized void attachToContext(Context context) {
|
||||
@ -379,7 +382,6 @@ public class Tabs implements GeckoEventListener {
|
||||
}
|
||||
|
||||
// GeckoEventListener implementation
|
||||
|
||||
@Override
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
Log.d(LOGTAG, "handleMessage: " + event);
|
||||
@ -486,7 +488,14 @@ public class Tabs implements GeckoEventListener {
|
||||
tab.setZoomConstraints(new ZoomConstraints(message));
|
||||
tab.setIsRTL(message.getBoolean("isRTL"));
|
||||
notifyListeners(tab, TabEvents.VIEWPORT_CHANGE);
|
||||
} else if (event.equals("Tab:StreamStart")) {
|
||||
tab.setRecording(true);
|
||||
notifyListeners(tab, TabEvents.RECORDING_CHANGE);
|
||||
} else if (event.equals("Tab:StreamStop")) {
|
||||
tab.setRecording(false);
|
||||
notifyListeners(tab, TabEvents.RECORDING_CHANGE);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.w(LOGTAG, "handleMessage threw for " + event, e);
|
||||
}
|
||||
@ -557,7 +566,8 @@ public class Tabs implements GeckoEventListener {
|
||||
SECURITY_CHANGE,
|
||||
READER_ENABLED,
|
||||
DESKTOP_MODE_CHANGE,
|
||||
VIEWPORT_CHANGE
|
||||
VIEWPORT_CHANGE,
|
||||
RECORDING_CHANGE
|
||||
}
|
||||
|
||||
public void notifyListeners(Tab tab, TabEvents msg) {
|
||||
|
@ -12,6 +12,7 @@ import org.mozilla.gecko.animation.PropertyAnimator;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator.Property;
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.widget.TwoWayView;
|
||||
import org.mozilla.gecko.widget.TabThumbnailWrapper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
@ -31,7 +32,7 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class TabsTray extends TwoWayView
|
||||
implements TabsPanel.PanelView {
|
||||
implements TabsPanel.PanelView {
|
||||
private static final String LOGTAG = "GeckoTabsTray";
|
||||
|
||||
private Context mContext;
|
||||
@ -122,12 +123,14 @@ public class TabsTray extends TwoWayView
|
||||
ImageView thumbnail;
|
||||
ImageButton close;
|
||||
ViewGroup info;
|
||||
TabThumbnailWrapper thumbnailWrapper;
|
||||
|
||||
public TabRow(View view) {
|
||||
info = (ViewGroup) view;
|
||||
title = (TextView) view.findViewById(R.id.title);
|
||||
thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
|
||||
close = (ImageButton) view.findViewById(R.id.close);
|
||||
thumbnailWrapper = (TabThumbnailWrapper) view.findViewById(R.id.wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,6 +176,7 @@ public class TabsTray extends TwoWayView
|
||||
// We just need to update the style for the unselected tab...
|
||||
case THUMBNAIL:
|
||||
case TITLE:
|
||||
case RECORDING_CHANGE:
|
||||
View view = TabsTray.this.getChildAt(getPositionForTab(tab) - TabsTray.this.getFirstVisiblePosition());
|
||||
if (view == null)
|
||||
return;
|
||||
@ -270,7 +274,9 @@ public class TabsTray extends TwoWayView
|
||||
} else {
|
||||
row.thumbnail.setImageResource(R.drawable.tab_thumbnail_default);
|
||||
}
|
||||
|
||||
if (row.thumbnailWrapper != null) {
|
||||
row.thumbnailWrapper.setRecording(tab.isRecording());
|
||||
}
|
||||
row.title.setText(tab.getDisplayTitle());
|
||||
row.close.setTag(row);
|
||||
}
|
||||
|
@ -92,8 +92,7 @@ public class EnvironmentBuilder {
|
||||
// Corresponds to Gecko pref "extensions.blocklist.enabled".
|
||||
e.isBlocklistEnabled = (info.isBlocklistEnabled() ? 1 : 0);
|
||||
|
||||
// Corresponds to one of two Gecko telemetry prefs. We reflect these into
|
||||
// GeckoPreferences as "datareporting.telemetry.enabled".
|
||||
// Corresponds to Gecko pref "toolkit.telemetry.enabled".
|
||||
e.isTelemetryEnabled = (info.isTelemetryEnabled() ? 1 : 0);
|
||||
|
||||
e.extensionCount = 0;
|
||||
|
@ -15,6 +15,9 @@ import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.util.GeckoJarReader;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.util.UiAsyncTask;
|
||||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.ThumbnailHelper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
@ -71,6 +74,11 @@ public final class BitmapUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.startsWith("thumbnail:")) {
|
||||
getThumbnailDrawable(context, data, loader);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.startsWith("jar:") || data.startsWith("file://")) {
|
||||
(new UiAsyncTask<Void, Void, Drawable>(ThreadUtils.getBackgroundHandler()) {
|
||||
@Override
|
||||
@ -131,6 +139,21 @@ public final class BitmapUtils {
|
||||
runOnBitmapFoundOnUiThread(loader, null);
|
||||
}
|
||||
|
||||
public static void getThumbnailDrawable(final Context context, final String data, final BitmapLoader loader) {
|
||||
int id = Integer.parseInt(data.substring(10), 10);
|
||||
final Tab tab = Tabs.getInstance().getTab(id);
|
||||
runOnBitmapFoundOnUiThread(loader, tab.getThumbnail());
|
||||
Tabs.registerOnTabsChangedListener(new Tabs.OnTabsChangedListener() {
|
||||
public void onTabChanged(Tab t, Tabs.TabEvents msg, Object data) {
|
||||
if (tab == t && msg == Tabs.TabEvents.THUMBNAIL) {
|
||||
Tabs.unregisterOnTabsChangedListener(this);
|
||||
runOnBitmapFoundOnUiThread(loader, t.getThumbnail());
|
||||
}
|
||||
}
|
||||
});
|
||||
ThumbnailHelper.getInstance().getAndProcessThumbnailFor(tab);
|
||||
}
|
||||
|
||||
public static Bitmap decodeByteArray(byte[] bytes) {
|
||||
return decodeByteArray(bytes, null);
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.Distribution;
|
||||
import org.mozilla.gecko.Distribution.DistributionDescriptor;
|
||||
import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
|
||||
@ -91,6 +90,7 @@ public class BrowserHealthRecorder implements HealthRecorder, GeckoEventListener
|
||||
private volatile HealthReportDatabaseStorage storage;
|
||||
private final ProfileInformationCache profileCache;
|
||||
private final EventDispatcher dispatcher;
|
||||
private final SharedPreferences prefs;
|
||||
|
||||
// We track previousSession to avoid order-of-initialization confusion. We
|
||||
// accept it in the constructor, and process it after init.
|
||||
@ -114,20 +114,13 @@ public class BrowserHealthRecorder implements HealthRecorder, GeckoEventListener
|
||||
this.session.setTimedJavaStartup(duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the opaque identifier for the current Firefox Health Report environment.
|
||||
* This changes in certain circumstances; be sure to use the current value when recording data.
|
||||
*/
|
||||
private void setHealthEnvironment(final int env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor does IO. Run it on a background thread.
|
||||
*
|
||||
* appLocale can be null, which indicates that it will be provided later.
|
||||
*/
|
||||
public BrowserHealthRecorder(final Context context,
|
||||
final SharedPreferences appPrefs,
|
||||
final String profilePath,
|
||||
final EventDispatcher dispatcher,
|
||||
final String osLocale,
|
||||
@ -161,6 +154,8 @@ public class BrowserHealthRecorder implements HealthRecorder, GeckoEventListener
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Exception initializing.", e);
|
||||
}
|
||||
|
||||
this.prefs = appPrefs;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
@ -544,8 +539,7 @@ public class BrowserHealthRecorder implements HealthRecorder, GeckoEventListener
|
||||
return;
|
||||
}
|
||||
|
||||
final SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
final SharedPreferences.Editor editor = this.prefs.edit();
|
||||
|
||||
recordSessionEnd(sessionEndReason, editor, prev);
|
||||
|
||||
|
@ -403,6 +403,7 @@ gbjar.sources += [
|
||||
'widget/IconTabWidget.java',
|
||||
'widget/SquaredImageView.java',
|
||||
'widget/TabRow.java',
|
||||
'widget/TabThumbnailWrapper.java',
|
||||
'widget/ThumbnailView.java',
|
||||
'widget/TwoWayView.java',
|
||||
'ZoomConstraints.java',
|
||||
@ -423,13 +424,13 @@ gbjar.generated_sources += [ android_package_dir + f for f in [
|
||||
gbjar.generated_sources += [
|
||||
'org/mozilla/gecko/AppConstants.java',
|
||||
'org/mozilla/gecko/SysInfo.java',
|
||||
'org/mozilla/gecko/widget/GeckoEditText.java',
|
||||
'org/mozilla/gecko/widget/GeckoImageButton.java',
|
||||
'org/mozilla/gecko/widget/GeckoImageView.java',
|
||||
'org/mozilla/gecko/widget/GeckoLinearLayout.java',
|
||||
'org/mozilla/gecko/widget/GeckoRelativeLayout.java',
|
||||
'org/mozilla/gecko/widget/GeckoTextSwitcher.java',
|
||||
'org/mozilla/gecko/widget/GeckoTextView.java',
|
||||
'org/mozilla/gecko/widget/ThemedEditText.java',
|
||||
'org/mozilla/gecko/widget/ThemedImageButton.java',
|
||||
'org/mozilla/gecko/widget/ThemedImageView.java',
|
||||
'org/mozilla/gecko/widget/ThemedLinearLayout.java',
|
||||
'org/mozilla/gecko/widget/ThemedRelativeLayout.java',
|
||||
'org/mozilla/gecko/widget/ThemedTextSwitcher.java',
|
||||
'org/mozilla/gecko/widget/ThemedTextView.java',
|
||||
]
|
||||
if CONFIG['MOZ_CRASHREPORTER']:
|
||||
gbjar.sources += [ 'CrashReporter.java' ]
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
package org.mozilla.gecko.mozglue;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
@ -21,6 +20,7 @@ import java.util.Locale;
|
||||
public final class GeckoLoader {
|
||||
private static final String LOGTAG = "GeckoLoader";
|
||||
|
||||
private static volatile Intent sIntent;
|
||||
private static File sCacheFile;
|
||||
private static File sGREDir;
|
||||
|
||||
@ -119,10 +119,14 @@ public final class GeckoLoader {
|
||||
return tmpDir;
|
||||
}
|
||||
|
||||
public static void setupGeckoEnvironment(Activity context, String[] pluginDirs, String profilePath) {
|
||||
public static void setLastIntent(Intent intent) {
|
||||
sIntent = intent;
|
||||
}
|
||||
|
||||
public static void setupGeckoEnvironment(Context context, String[] pluginDirs, String profilePath) {
|
||||
// if we have an intent (we're being launched by an activity)
|
||||
// read in any environmental variables from it here
|
||||
Intent intent = context.getIntent();
|
||||
final Intent intent = sIntent;
|
||||
if (intent != null) {
|
||||
String env = intent.getStringExtra("env0");
|
||||
Log.d(LOGTAG, "Gecko environment env0: " + env);
|
||||
@ -181,6 +185,9 @@ public final class GeckoLoader {
|
||||
}
|
||||
|
||||
setupLocaleEnvironment();
|
||||
|
||||
// We don't need this any more.
|
||||
sIntent = null;
|
||||
}
|
||||
|
||||
private static void loadLibsSetup(Context context) {
|
||||
|
@ -53,12 +53,15 @@ public class PromptListItem {
|
||||
isParent = aObject.optBoolean("isParent") || aObject.optBoolean("menu");
|
||||
}
|
||||
|
||||
BitmapUtils.getDrawable(GeckoAppShell.getContext(), aObject.optString("icon"), new BitmapUtils.BitmapLoader() {
|
||||
@Override
|
||||
public void onBitmapFound(Drawable d) {
|
||||
mIcon = d;
|
||||
}
|
||||
});
|
||||
final String iconStr = aObject.optString("icon");
|
||||
if (iconStr != null) {
|
||||
BitmapUtils.getDrawable(GeckoAppShell.getContext(), iconStr, new BitmapUtils.BitmapLoader() {
|
||||
@Override
|
||||
public void onBitmapFound(Drawable d) {
|
||||
mIcon = d;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void setIntent(Intent i) {
|
||||
|
@ -3,7 +3,8 @@
|
||||
- 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/. -->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:state_focused="true">
|
||||
|
||||
@ -18,6 +19,15 @@
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#B3FF9500"/>
|
||||
<corners android:radius="3dp"/>
|
||||
</shape>
|
||||
|
||||
</item>
|
||||
|
||||
<item gecko:state_recording="true">
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFF0000"/>
|
||||
<corners android:radius="3dp"/>
|
||||
</shape>
|
||||
|
||||
@ -25,7 +35,8 @@
|
||||
|
||||
<item android:state_focused="false"
|
||||
android:state_pressed="false"
|
||||
android:state_checked="true">
|
||||
android:state_checked="true"
|
||||
gecko:state_recording="false">
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFF9500"/>
|
||||
|
@ -82,21 +82,21 @@
|
||||
android:layout_toLeftOf="@id/menu"
|
||||
android:layout_alignWithParentIfMissing="true"/>
|
||||
|
||||
<org.mozilla.gecko.widget.GeckoImageButton android:id="@+id/menu"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="56dip"
|
||||
android:layout_alignParentRight="true"
|
||||
android:contentDescription="@string/menu"
|
||||
android:background="@drawable/action_bar_button"
|
||||
android:visibility="gone"/>
|
||||
<org.mozilla.gecko.widget.ThemedImageButton android:id="@+id/menu"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="56dip"
|
||||
android:layout_alignParentRight="true"
|
||||
android:contentDescription="@string/menu"
|
||||
android:background="@drawable/action_bar_button"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mozilla.gecko.widget.GeckoImageView android:id="@+id/menu_icon"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_alignLeft="@id/menu"
|
||||
android:layout_alignRight="@id/menu"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/menu_level"
|
||||
android:visibility="gone"/>
|
||||
<org.mozilla.gecko.widget.ThemedImageView android:id="@+id/menu_icon"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_alignLeft="@id/menu"
|
||||
android:layout_alignRight="@id/menu"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/menu_level"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ImageView android:id="@+id/shadow"
|
||||
android:layout_width="fill_parent"
|
||||
|
@ -52,13 +52,13 @@
|
||||
android:background="@drawable/shaped_button"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mozilla.gecko.widget.GeckoImageView android:id="@+id/menu_icon"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_alignLeft="@id/menu"
|
||||
android:layout_alignRight="@id/menu"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/menu_level"
|
||||
android:visibility="gone"/>
|
||||
<org.mozilla.gecko.widget.ThemedImageView android:id="@+id/menu_icon"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_alignLeft="@id/menu"
|
||||
android:layout_alignRight="@id/menu"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/menu_level"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mozilla.gecko.toolbar.ShapedButton android:id="@+id/tabs"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
|
@ -3,15 +3,15 @@
|
||||
- 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/. -->
|
||||
|
||||
<org.mozilla.gecko.widget.GeckoTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="24dip"
|
||||
android:layout_height="24dip"
|
||||
android:layout_margin="12dip"
|
||||
android:paddingTop="2dip"
|
||||
android:paddingLeft="4dip"
|
||||
android:background="@drawable/tabs_count_foreground"
|
||||
android:textAppearance="@style/TextAppearance.Micro"
|
||||
android:textColor="#FF43484E"
|
||||
android:textStyle="bold"
|
||||
android:duplicateParentState="true"
|
||||
android:gravity="center"/>
|
||||
<org.mozilla.gecko.widget.ThemedTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="24dip"
|
||||
android:layout_height="24dip"
|
||||
android:layout_margin="12dip"
|
||||
android:paddingTop="2dip"
|
||||
android:paddingLeft="4dip"
|
||||
android:background="@drawable/tabs_count_foreground"
|
||||
android:textAppearance="@style/TextAppearance.Micro"
|
||||
android:textColor="#FF43484E"
|
||||
android:textStyle="bold"
|
||||
android:duplicateParentState="true"
|
||||
android:gravity="center"/>
|
||||
|
@ -14,7 +14,9 @@
|
||||
android:paddingBottom="6dip"
|
||||
android:background="@drawable/tab_row">
|
||||
|
||||
<LinearLayout android:layout_width="wrap_content"
|
||||
<org.mozilla.gecko.widget.TabThumbnailWrapper
|
||||
android:id="@+id/wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dip"
|
||||
android:background="@drawable/tab_thumbnail"
|
||||
@ -25,7 +27,7 @@
|
||||
android:layout_height="@dimen/tab_thumbnail_height"
|
||||
android:src="@drawable/tab_thumbnail_default"/>
|
||||
|
||||
</LinearLayout>
|
||||
</org.mozilla.gecko.widget.TabThumbnailWrapper>
|
||||
|
||||
<TextView android:id="@+id/title"
|
||||
android:layout_width="0dip"
|
||||
|
@ -25,19 +25,19 @@
|
||||
android:contentDescription="@string/site_security"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mozilla.gecko.widget.GeckoTextView android:id="@+id/url_bar_title"
|
||||
style="@style/UrlBar.Button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1.0"
|
||||
android:singleLine="true"
|
||||
android:paddingRight="8dp"
|
||||
android:textColor="@color/url_bar_title"
|
||||
android:textColorHint="@color/url_bar_title_hint"
|
||||
android:gravity="center_vertical|left"
|
||||
android:hint="@string/url_bar_default_text"
|
||||
android:layout_gravity="center_vertical"
|
||||
gecko:autoUpdateTheme="false"/>
|
||||
<org.mozilla.gecko.widget.ThemedTextView android:id="@+id/url_bar_title"
|
||||
style="@style/UrlBar.Button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1.0"
|
||||
android:singleLine="true"
|
||||
android:paddingRight="8dp"
|
||||
android:textColor="@color/url_bar_title"
|
||||
android:textColorHint="@color/url_bar_title_hint"
|
||||
android:gravity="center_vertical|left"
|
||||
android:hint="@string/url_bar_default_text"
|
||||
android:layout_gravity="center_vertical"
|
||||
gecko:autoUpdateTheme="false"/>
|
||||
|
||||
<org.mozilla.gecko.toolbar.PageActionLayout android:id="@+id/page_action_layout"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -74,6 +74,10 @@
|
||||
<attr name="state_more" format="boolean"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TabThumbnailWrapper">
|
||||
<attr name="state_recording" format="boolean"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="FlowLayout">
|
||||
<attr name="spacing" format="dimension"/>
|
||||
</declare-styleable>
|
||||
|
@ -31,9 +31,9 @@ import org.mozilla.gecko.util.Clipboard;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.MenuUtils;
|
||||
import org.mozilla.gecko.widget.GeckoImageButton;
|
||||
import org.mozilla.gecko.widget.GeckoImageView;
|
||||
import org.mozilla.gecko.widget.GeckoRelativeLayout;
|
||||
import org.mozilla.gecko.widget.ThemedImageButton;
|
||||
import org.mozilla.gecko.widget.ThemedImageView;
|
||||
import org.mozilla.gecko.widget.ThemedRelativeLayout;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
@ -75,7 +75,7 @@ import android.widget.PopupWindow;
|
||||
* as a set of listeners to allow {@code BrowserToolbar} users to react
|
||||
* to state changes accordingly.
|
||||
*/
|
||||
public class BrowserToolbar extends GeckoRelativeLayout
|
||||
public class BrowserToolbar extends ThemedRelativeLayout
|
||||
implements Tabs.OnTabsChangedListener,
|
||||
GeckoMenu.ActionItemBarPresenter,
|
||||
GeckoEventListener {
|
||||
@ -126,8 +126,8 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
|
||||
private ToolbarProgressView mProgressBar;
|
||||
private TabCounter mTabsCounter;
|
||||
private GeckoImageButton mMenu;
|
||||
private GeckoImageView mMenuIcon;
|
||||
private ThemedImageButton mMenu;
|
||||
private ThemedImageView mMenuIcon;
|
||||
private LinearLayout mActionItemBar;
|
||||
private MenuPopup mMenuPopup;
|
||||
private List<View> mFocusOrder;
|
||||
@ -202,8 +202,8 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
mForward = (ImageButton) findViewById(R.id.forward);
|
||||
setButtonEnabled(mForward, false);
|
||||
|
||||
mMenu = (GeckoImageButton) findViewById(R.id.menu);
|
||||
mMenuIcon = (GeckoImageView) findViewById(R.id.menu_icon);
|
||||
mMenu = (ThemedImageButton) findViewById(R.id.menu);
|
||||
mMenuIcon = (ThemedImageView) findViewById(R.id.menu_icon);
|
||||
mActionItemBar = (LinearLayout) findViewById(R.id.menu_items);
|
||||
mHasSoftMenuButton = !HardwareUtils.hasMenuButton();
|
||||
|
||||
|
@ -8,7 +8,7 @@ import org.mozilla.gecko.GeckoApplication;
|
||||
import org.mozilla.gecko.LightweightTheme;
|
||||
import org.mozilla.gecko.LightweightThemeDrawable;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.widget.GeckoImageButton;
|
||||
import org.mozilla.gecko.widget.ThemedImageButton;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
@ -19,7 +19,7 @@ import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
public class ShapedButton extends GeckoImageButton
|
||||
public class ShapedButton extends ThemedImageButton
|
||||
implements CanvasDelegate.DrawManager {
|
||||
protected final LightweightTheme mTheme;
|
||||
|
||||
|
@ -7,7 +7,7 @@ package org.mozilla.gecko.toolbar;
|
||||
|
||||
import org.mozilla.gecko.animation.Rotate3DAnimation;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.widget.GeckoTextSwitcher;
|
||||
import org.mozilla.gecko.widget.ThemedTextSwitcher;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
@ -20,7 +20,7 @@ import android.view.View;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ViewSwitcher;
|
||||
|
||||
public class TabCounter extends GeckoTextSwitcher
|
||||
public class TabCounter extends ThemedTextSwitcher
|
||||
implements ViewSwitcher.ViewFactory {
|
||||
|
||||
private static final float CENTER_X = 0.5f;
|
||||
|
@ -16,8 +16,8 @@ import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.toolbar.BrowserToolbar.ForwardButtonAnimation;
|
||||
import org.mozilla.gecko.util.StringUtils;
|
||||
import org.mozilla.gecko.widget.GeckoLinearLayout;
|
||||
import org.mozilla.gecko.widget.GeckoTextView;
|
||||
import org.mozilla.gecko.widget.ThemedLinearLayout;
|
||||
import org.mozilla.gecko.widget.ThemedTextView;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
@ -62,7 +62,7 @@ import java.util.List;
|
||||
* {@code ToolbarDisplayLayout} is meant to be owned by {@code BrowserToolbar}
|
||||
* which is the main event bus for the toolbar subsystem.
|
||||
*/
|
||||
public class ToolbarDisplayLayout extends GeckoLinearLayout
|
||||
public class ToolbarDisplayLayout extends ThemedLinearLayout
|
||||
implements Animation.AnimationListener {
|
||||
|
||||
private static final String LOGTAG = "GeckoToolbarDisplayLayout";
|
||||
@ -99,7 +99,7 @@ public class ToolbarDisplayLayout extends GeckoLinearLayout
|
||||
|
||||
private UIMode mUiMode;
|
||||
|
||||
private GeckoTextView mTitle;
|
||||
private ThemedTextView mTitle;
|
||||
private int mTitlePadding;
|
||||
private ToolbarTitlePrefs mTitlePrefs;
|
||||
private OnTitleChangeListener mTitleChangeListener;
|
||||
@ -139,7 +139,7 @@ public class ToolbarDisplayLayout extends GeckoLinearLayout
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.toolbar_display_layout, this);
|
||||
|
||||
mTitle = (GeckoTextView) findViewById(R.id.url_bar_title);
|
||||
mTitle = (ThemedTextView) findViewById(R.id.url_bar_title);
|
||||
mTitlePadding = mTitle.getPaddingRight();
|
||||
|
||||
final Resources res = getResources();
|
||||
|
@ -14,7 +14,7 @@ import org.mozilla.gecko.toolbar.BrowserToolbar.OnDismissListener;
|
||||
import org.mozilla.gecko.toolbar.BrowserToolbar.OnFilterListener;
|
||||
import org.mozilla.gecko.toolbar.ToolbarEditText.OnTextTypeChangeListener;
|
||||
import org.mozilla.gecko.toolbar.ToolbarEditText.TextType;
|
||||
import org.mozilla.gecko.widget.GeckoLinearLayout;
|
||||
import org.mozilla.gecko.widget.ThemedLinearLayout;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
@ -31,7 +31,7 @@ import android.widget.ImageButton;
|
||||
* and its matching 'go' button which changes depending on the
|
||||
* current type of text in the entry.
|
||||
*/
|
||||
public class ToolbarEditLayout extends GeckoLinearLayout {
|
||||
public class ToolbarEditLayout extends ThemedLinearLayout {
|
||||
|
||||
private final ToolbarEditText mEditText;
|
||||
private final ImageButton mGo;
|
||||
|
42
mobile/android/base/widget/TabThumbnailWrapper.java
Normal file
42
mobile/android/base/widget/TabThumbnailWrapper.java
Normal file
@ -0,0 +1,42 @@
|
||||
package org.mozilla.gecko.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
import org.mozilla.gecko.R;
|
||||
|
||||
|
||||
public class TabThumbnailWrapper extends FrameLayout {
|
||||
private boolean mRecording = false;
|
||||
private static final int[] STATE_RECORDING = { R.attr.state_recording };
|
||||
|
||||
public TabThumbnailWrapper(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
public TabThumbnailWrapper(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public TabThumbnailWrapper(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] onCreateDrawableState(int extraSpace) {
|
||||
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
||||
|
||||
if (mRecording) {
|
||||
mergeDrawableStates(drawableState, STATE_RECORDING);
|
||||
}
|
||||
return drawableState;
|
||||
}
|
||||
|
||||
public void setRecording(boolean recording) {
|
||||
if (mRecording != recording) {
|
||||
mRecording = recording;
|
||||
refreshDrawableState();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
#filter substitution
|
||||
#define VIEWTYPE EditText
|
||||
#include GeckoView.java.frag
|
||||
#include ThemedView.java.frag
|
@ -1,3 +1,3 @@
|
||||
#filter substitution
|
||||
#define VIEWTYPE ImageButton
|
||||
#include GeckoView.java.frag
|
||||
#include ThemedView.java.frag
|
@ -1,3 +1,3 @@
|
||||
#filter substitution
|
||||
#define VIEWTYPE ImageView
|
||||
#include GeckoView.java.frag
|
||||
#include ThemedView.java.frag
|
@ -1,3 +1,3 @@
|
||||
#filter substitution
|
||||
#define VIEWTYPE LinearLayout
|
||||
#include GeckoView.java.frag
|
||||
#include ThemedView.java.frag
|
@ -1,3 +1,3 @@
|
||||
#filter substitution
|
||||
#define VIEWTYPE RelativeLayout
|
||||
#include GeckoView.java.frag
|
||||
#include ThemedView.java.frag
|
@ -1,3 +1,3 @@
|
||||
#filter substitution
|
||||
#define VIEWTYPE TextSwitcher
|
||||
#include GeckoView.java.frag
|
||||
#include ThemedView.java.frag
|
@ -1,3 +1,3 @@
|
||||
#filter substitution
|
||||
#define VIEWTYPE TextView
|
||||
#include GeckoView.java.frag
|
||||
#include ThemedView.java.frag
|
@ -14,8 +14,8 @@ import android.graphics.drawable.ColorDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.@VIEWTYPE@;
|
||||
|
||||
public class Gecko@VIEWTYPE@ extends @VIEWTYPE@
|
||||
implements LightweightTheme.OnChangeListener {
|
||||
public class Themed@VIEWTYPE@ extends @VIEWTYPE@
|
||||
implements LightweightTheme.OnChangeListener {
|
||||
private final LightweightTheme mTheme;
|
||||
|
||||
private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private };
|
||||
@ -31,7 +31,7 @@ public class Gecko@VIEWTYPE@ extends @VIEWTYPE@
|
||||
private boolean mIsDark = false;
|
||||
private boolean mAutoUpdateTheme = true;
|
||||
|
||||
public Gecko@VIEWTYPE@(Context context, AttributeSet attrs) {
|
||||
public Themed@VIEWTYPE@(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme();
|
||||
|
@ -1,3 +1,9 @@
|
||||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* 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"
|
||||
|
||||
const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
@ -31,7 +37,15 @@ TabSource.prototype = {
|
||||
title: title,
|
||||
window: null
|
||||
}).setSingleChoiceItems(tabs.map(function(tab) {
|
||||
return { label: tab.browser.contentTitle || tab.browser.contentURI.spec }
|
||||
let label;
|
||||
if (tab.browser.contentTitle)
|
||||
label = tab.browser.contentTitle;
|
||||
else if (tab.browser.contentURI && tab.browser.contentURI.spec)
|
||||
label = tab.browser.contentURI.spec;
|
||||
else
|
||||
label = tab.originalURI;
|
||||
return { label: label,
|
||||
icon: "thumbnail:" + tab.id }
|
||||
}));
|
||||
|
||||
let result = null;
|
||||
@ -56,7 +70,7 @@ TabSource.prototype = {
|
||||
let tabs = app.tabs;
|
||||
for (var i in tabs) {
|
||||
if (tabs[i].browser.contentWindow == window) {
|
||||
sendMessageToJava({ type: "Tab:Streaming", tabID: tabs[i].id });
|
||||
sendMessageToJava({ type: "Tab:StreamStart", tabID: tabs[i].id });
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -66,7 +80,7 @@ TabSource.prototype = {
|
||||
let tabs = app.tabs;
|
||||
for (let i in tabs) {
|
||||
if (tabs[i].browser.contentWindow == window) {
|
||||
sendMessageToJava({ type: "Tab:NotStreaming", tabID: tabs[i].id });
|
||||
sendMessageToJava({ type: "Tab:StreamStop", tabID: tabs[i].id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* 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"
|
||||
@ -36,7 +37,7 @@ function Prompt(aOptions) {
|
||||
if ("hint" in aOptions && aOptions.hint != null)
|
||||
this.msg.hint = aOptions.hint;
|
||||
|
||||
let idService = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
||||
let idService = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
||||
}
|
||||
|
||||
Prompt.prototype = {
|
||||
@ -178,6 +179,9 @@ Prompt.prototype = {
|
||||
|
||||
obj.label = item.label;
|
||||
|
||||
if (item.icon)
|
||||
obj.icon = item.icon;
|
||||
|
||||
if (item.disabled)
|
||||
obj.disabled = true;
|
||||
|
||||
|
@ -25,8 +25,6 @@ error.logout.description = Sync encountered an error while connecting. It's pro
|
||||
error.sync.title = Error While Syncing
|
||||
error.sync.description = Sync encountered an error while syncing: %1$S. Sync will automatically retry this action.
|
||||
error.sync.prolonged_failure = Sync has not been able to complete during the last %1$S days. Please check your network settings.
|
||||
error.sync.no_node_found = The Sync server is a little busy right now, but you don't need to do anything about it. We'll start syncing your data as soon as we can!
|
||||
error.sync.no_node_found.title = Sync Delay
|
||||
error.sync.serverStatusButton.label = Server Status
|
||||
error.sync.serverStatusButton.accesskey = V
|
||||
error.sync.needUpdate.description = You need to update Firefox Sync to continue syncing your data.
|
||||
|
@ -690,7 +690,6 @@ Sync11Service.prototype = {
|
||||
// to succeed, since that probably means we just don't have storage.
|
||||
if (this.clusterURL == "" && !this._clusterManager.setCluster()) {
|
||||
this.status.sync = NO_SYNC_NODE_FOUND;
|
||||
Svc.Obs.notify("weave:service:sync:delayed");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ let FunctionCallActor = protocol.ActorClass({
|
||||
name: name,
|
||||
stack: stack,
|
||||
args: args,
|
||||
return: result
|
||||
result: result
|
||||
};
|
||||
|
||||
this.meta = {
|
||||
|
431
toolkit/devtools/server/actors/webaudio.js
Normal file
431
toolkit/devtools/server/actors/webaudio.js
Normal file
@ -0,0 +1,431 @@
|
||||
/* 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";
|
||||
|
||||
const {Cc, Ci, Cu, Cr} = require("chrome");
|
||||
|
||||
const Services = require("Services");
|
||||
|
||||
const events = require("sdk/event/core");
|
||||
const protocol = require("devtools/server/protocol");
|
||||
const { CallWatcherActor, CallWatcherFront } = require("devtools/server/actors/call-watcher");
|
||||
|
||||
const { on, once, off, emit } = events;
|
||||
const { method, Arg, Option, RetVal } = protocol;
|
||||
|
||||
exports.register = function(handle) {
|
||||
handle.addTabActor(WebAudioActor, "webaudioActor");
|
||||
};
|
||||
|
||||
exports.unregister = function(handle) {
|
||||
handle.removeTabActor(WebAudioActor);
|
||||
};
|
||||
|
||||
const AUDIO_GLOBALS = [
|
||||
"AudioContext", "AudioNode"
|
||||
];
|
||||
|
||||
const NODE_CREATION_METHODS = [
|
||||
"createBufferSource", "createMediaElementSource", "createMediaStreamSource",
|
||||
"createMediaStreamDestination", "createScriptProcessor", "createAnalyser",
|
||||
"createGain", "createDelay", "createBiquadFilter", "createWaveShaper",
|
||||
"createPanner", "createConvolver", "createChannelSplitter", "createChannelMerger",
|
||||
"createDynamicsCompressor", "createOscillator"
|
||||
];
|
||||
|
||||
const NODE_ROUTING_METHODS = [
|
||||
"connect", "disconnect"
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* An Audio Node actor allowing communication to a specific audio node in the
|
||||
* Audio Context graph.
|
||||
*/
|
||||
let AudioNodeActor = exports.AudioNodeActor = protocol.ActorClass({
|
||||
typeName: "audionode",
|
||||
|
||||
/**
|
||||
* Create the Audio Node actor.
|
||||
*
|
||||
* @param DebuggerServerConnection conn
|
||||
* The server connection.
|
||||
* @param AudioNode node
|
||||
* The AudioNode that was created.
|
||||
*/
|
||||
initialize: function (conn, node) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.node = unwrap(node);
|
||||
try {
|
||||
this.type = this.node.toString().match(/\[object (.*)\]$/)[1];
|
||||
} catch (e) {
|
||||
this.type = "";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the name of the audio type.
|
||||
* Examples: "OscillatorNode", "MediaElementAudioSourceNode"
|
||||
*/
|
||||
getType: method(function () {
|
||||
return this.type;
|
||||
}, {
|
||||
response: { type: RetVal("string") }
|
||||
}),
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating if the node is a source node,
|
||||
* like BufferSourceNode, MediaElementAudioSourceNode, OscillatorNode, etc.
|
||||
*/
|
||||
isSource: method(function () {
|
||||
return !!~this.type.indexOf("Source") || this.type === "OscillatorNode";
|
||||
}, {
|
||||
response: { source: RetVal("boolean") }
|
||||
}),
|
||||
|
||||
/**
|
||||
* Changes a param on the audio node. Responds with a `string` that's either
|
||||
* an empty string `""` on success, or a description of the error upon
|
||||
* param set failure.
|
||||
*
|
||||
* @param String param
|
||||
* Name of the AudioParam to change.
|
||||
* @param String value
|
||||
* Value to change AudioParam to.
|
||||
*/
|
||||
setParam: method(function (param, value) {
|
||||
// Strip quotes because sometimes UIs include that for strings
|
||||
if (typeof value === "string") {
|
||||
value = value.replace(/[\'\"]*/g, "");
|
||||
}
|
||||
try {
|
||||
if (isAudioParam(this.node, param))
|
||||
this.node[param].value = value;
|
||||
else
|
||||
this.node[param] = value;
|
||||
return undefined;
|
||||
} catch (e) {
|
||||
return constructError(e);
|
||||
}
|
||||
}, {
|
||||
request: {
|
||||
param: Arg(0, "string"),
|
||||
value: Arg(1, "nullable:primitive")
|
||||
},
|
||||
response: { error: RetVal("nullable:json") }
|
||||
}),
|
||||
|
||||
/**
|
||||
* Gets a param on the audio node.
|
||||
*
|
||||
* @param String param
|
||||
* Name of the AudioParam to fetch.
|
||||
*/
|
||||
getParam: method(function (param) {
|
||||
// If property does not exist, just return "undefined"
|
||||
if (!this.node[param])
|
||||
return undefined;
|
||||
let value = isAudioParam(this.node, param) ? this.node[param].value : this.node[param];
|
||||
let type = typeof type;
|
||||
return value;
|
||||
return { type: type, value: value };
|
||||
}, {
|
||||
request: {
|
||||
param: Arg(0, "string")
|
||||
},
|
||||
response: { text: RetVal("nullable:primitive") }
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the AudioNodeActor.
|
||||
*/
|
||||
let AudioNodeFront = protocol.FrontClass(AudioNodeActor, {
|
||||
initialize: function (client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
client.addActorPool(this);
|
||||
this.manage(this);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The Web Audio Actor handles simple interaction with an AudioContext
|
||||
* high-level methods. After instantiating this actor, you'll need to set it
|
||||
* up by calling setup().
|
||||
*/
|
||||
let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({
|
||||
typeName: "webaudio",
|
||||
initialize: function(conn, tabActor) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.tabActor = tabActor;
|
||||
this._onContentFunctionCall = this._onContentFunctionCall.bind(this);
|
||||
},
|
||||
|
||||
destroy: function(conn) {
|
||||
protocol.Actor.prototype.destroy.call(this, conn);
|
||||
this.finalize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts waiting for the current tab actor's document global to be
|
||||
* created, in order to instrument the Canvas context and become
|
||||
* aware of everything the content does with Web Audio.
|
||||
*
|
||||
* See ContentObserver and WebAudioInstrumenter for more details.
|
||||
*/
|
||||
setup: method(function({ reload }) {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
|
||||
// Weak map mapping audio nodes to their corresponding actors
|
||||
this._nodeActors = new Map();
|
||||
|
||||
this._callWatcher = new CallWatcherActor(this.conn, this.tabActor);
|
||||
this._callWatcher.onCall = this._onContentFunctionCall;
|
||||
this._callWatcher.setup({
|
||||
tracedGlobals: AUDIO_GLOBALS,
|
||||
startRecording: true,
|
||||
performReload: reload
|
||||
});
|
||||
|
||||
// Used to track when something is happening with the web audio API
|
||||
// the first time, to ultimately fire `start-context` event
|
||||
this._firstNodeCreated = false;
|
||||
}, {
|
||||
request: { reload: Option(0, "boolean") },
|
||||
oneway: true
|
||||
}),
|
||||
|
||||
/**
|
||||
* Invoked whenever an instrumented function is called, like an AudioContext
|
||||
* method or an AudioNode method.
|
||||
*/
|
||||
_onContentFunctionCall: function(functionCall) {
|
||||
let { name } = functionCall.details;
|
||||
|
||||
// All Web Audio nodes inherit from AudioNode's prototype, so
|
||||
// hook into the `connect` and `disconnect` methods
|
||||
if (WebAudioFront.NODE_ROUTING_METHODS.has(name)) {
|
||||
this._handleRoutingCall(functionCall);
|
||||
}
|
||||
else if (WebAudioFront.NODE_CREATION_METHODS.has(name)) {
|
||||
this._handleCreationCall(functionCall);
|
||||
}
|
||||
},
|
||||
|
||||
_handleRoutingCall: function(functionCall) {
|
||||
let { caller, args, window, name } = functionCall.details;
|
||||
let source = unwrap(caller);
|
||||
let dest = unwrap(args[0]);
|
||||
let isAudioParam = dest instanceof unwrap(window.AudioParam);
|
||||
|
||||
// audionode.connect(param)
|
||||
if (name === "connect" && isAudioParam) {
|
||||
this._onConnectParam(source, dest);
|
||||
}
|
||||
// audionode.connect(node)
|
||||
else if (name === "connect") {
|
||||
this._onConnectNode(source, dest);
|
||||
}
|
||||
// audionode.disconnect()
|
||||
else if (name === "disconnect") {
|
||||
this._onDisconnectNode(source);
|
||||
}
|
||||
},
|
||||
|
||||
_handleCreationCall: function (functionCall) {
|
||||
let { caller, result } = functionCall.details;
|
||||
// Keep track of the first node created, so we can alert
|
||||
// the front end that an audio context is being used since
|
||||
// we're not hooking into the constructor itself, just its
|
||||
// instance's methods.
|
||||
if (!this._firstNodeCreated) {
|
||||
// Fire the start-up event if this is the first node created
|
||||
// and trigger a `create-node` event for the context destination
|
||||
this._onStartContext();
|
||||
this._onCreateNode(unwrap(caller.destination));
|
||||
this._firstNodeCreated = true;
|
||||
}
|
||||
this._onCreateNode(result);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops listening for document global changes and puts this actor
|
||||
* to hibernation. This method is called automatically just before the
|
||||
* actor is destroyed.
|
||||
*/
|
||||
finalize: method(function() {
|
||||
if (!this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._initialized = false;
|
||||
this._callWatcher.eraseRecording();
|
||||
|
||||
this._callWatcher.finalize();
|
||||
this._callWatcher = null;
|
||||
}, {
|
||||
oneway: true
|
||||
}),
|
||||
|
||||
/**
|
||||
* Events emitted by this actor.
|
||||
*/
|
||||
events: {
|
||||
"start-context": {
|
||||
type: "startContext"
|
||||
},
|
||||
"connect-node": {
|
||||
type: "connectNode",
|
||||
source: Option(0, "audionode"),
|
||||
dest: Option(0, "audionode")
|
||||
},
|
||||
"disconnect-node": {
|
||||
type: "disconnectNode",
|
||||
source: Arg(0, "audionode")
|
||||
},
|
||||
"connect-param": {
|
||||
type: "connectParam",
|
||||
source: Arg(0, "audionode"),
|
||||
param: Arg(1, "string")
|
||||
},
|
||||
"change-param": {
|
||||
type: "changeParam",
|
||||
source: Option(0, "audionode"),
|
||||
param: Option(0, "string"),
|
||||
value: Option(0, "string")
|
||||
},
|
||||
"create-node": {
|
||||
type: "createNode",
|
||||
source: Arg(0, "audionode")
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper for constructing an AudioNodeActor, assigning to
|
||||
* internal weak map, and tracking via `manage` so it is assigned
|
||||
* an `actorID`.
|
||||
*/
|
||||
_constructAudioNode: function (node) {
|
||||
let actor = new AudioNodeActor(this.conn, node);
|
||||
this.manage(actor);
|
||||
this._nodeActors.set(node, actor);
|
||||
return actor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an AudioNode and returns the stored actor for it.
|
||||
* In some cases, we won't have an actor stored (for example,
|
||||
* connecting to an AudioDestinationNode, since it's implicitly
|
||||
* created), so make a new actor and store that.
|
||||
*/
|
||||
_actorFor: function (node) {
|
||||
let actor = this._nodeActors.get(node);
|
||||
if (!actor) {
|
||||
actor = this._constructAudioNode(node);
|
||||
}
|
||||
return actor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called on first audio node creation, signifying audio context usage
|
||||
*/
|
||||
_onStartContext: function () {
|
||||
events.emit(this, "start-context");
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when one audio node is connected to another.
|
||||
*/
|
||||
_onConnectNode: function (source, dest) {
|
||||
let sourceActor = this._actorFor(source);
|
||||
let destActor = this._actorFor(dest);
|
||||
events.emit(this, "connect-node", {
|
||||
source: sourceActor,
|
||||
dest: destActor
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when an audio node is connected to an audio param.
|
||||
* Implement in bug 986705
|
||||
*/
|
||||
_onConnectParam: function (source, dest) {
|
||||
// TODO bug 986705
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when an audio node is disconnected.
|
||||
*/
|
||||
_onDisconnectNode: function (node) {
|
||||
let actor = this._actorFor(node);
|
||||
events.emit(this, "disconnect-node", actor);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a parameter changes on an audio node
|
||||
*/
|
||||
_onParamChange: function (node, param, value) {
|
||||
let actor = this._actorFor(node);
|
||||
events.emit(this, "param-change", {
|
||||
source: actor,
|
||||
param: param,
|
||||
value: value
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Called on node creation.
|
||||
*/
|
||||
_onCreateNode: function (node) {
|
||||
let actor = this._constructAudioNode(node);
|
||||
events.emit(this, "create-node", actor);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the WebAudioActor.
|
||||
*/
|
||||
let WebAudioFront = exports.WebAudioFront = protocol.FrontClass(WebAudioActor, {
|
||||
initialize: function(client, { webaudioActor }) {
|
||||
protocol.Front.prototype.initialize.call(this, client, { actor: webaudioActor });
|
||||
client.addActorPool(this);
|
||||
this.manage(this);
|
||||
}
|
||||
});
|
||||
|
||||
WebAudioFront.NODE_CREATION_METHODS = new Set(NODE_CREATION_METHODS);
|
||||
WebAudioFront.NODE_ROUTING_METHODS = new Set(NODE_ROUTING_METHODS);
|
||||
|
||||
/**
|
||||
* Determines whether or not property is an AudioParam.
|
||||
*
|
||||
* @param AudioNode node
|
||||
* An AudioNode.
|
||||
* @param String prop
|
||||
* Property of `node` to evaluate to see if it's an AudioParam.
|
||||
* @return Boolean
|
||||
*/
|
||||
function isAudioParam (node, prop) {
|
||||
return /AudioParam/.test(node[prop].toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an `Error` object and constructs a JSON-able response
|
||||
*
|
||||
* @param Error err
|
||||
* A TypeError, RangeError, etc.
|
||||
* @return Object
|
||||
*/
|
||||
function constructError (err) {
|
||||
return {
|
||||
message: err.message,
|
||||
type: err.constructor.name
|
||||
};
|
||||
}
|
||||
|
||||
function unwrap (obj) {
|
||||
return XPCNativeWrapper.unwrap(obj);
|
||||
}
|
@ -24,12 +24,14 @@ let chromeGlobal = this;
|
||||
// time we load child.js
|
||||
DebuggerServer.addChildActors();
|
||||
|
||||
let conn;
|
||||
|
||||
let onConnect = DevToolsUtils.makeInfallible(function (msg) {
|
||||
removeMessageListener("debug:connect", onConnect);
|
||||
|
||||
let mm = msg.target;
|
||||
|
||||
let conn = DebuggerServer.connectToParent(msg.data.prefix, mm);
|
||||
conn = DebuggerServer.connectToParent(msg.data.prefix, mm);
|
||||
|
||||
let actor = new DebuggerServer.ContentActor(conn, chromeGlobal);
|
||||
let actorPool = new ActorPool(conn);
|
||||
@ -40,4 +42,15 @@ let chromeGlobal = this;
|
||||
});
|
||||
|
||||
addMessageListener("debug:connect", onConnect);
|
||||
|
||||
let onDisconnect = DevToolsUtils.makeInfallible(function (msg) {
|
||||
removeMessageListener("debug:disconnect", onDisconnect);
|
||||
|
||||
// Call DebuggerServerConnection.close to destroy all child actors
|
||||
// (It should end up calling DebuggerServerConnection.onClosed
|
||||
// that would actually cleanup all actor pools)
|
||||
conn.close();
|
||||
conn = null;
|
||||
});
|
||||
addMessageListener("debug:disconnect", onDisconnect);
|
||||
})();
|
||||
|
@ -59,6 +59,7 @@ function loadSubScript(aURL)
|
||||
}
|
||||
}
|
||||
|
||||
let events = require("sdk/event/core");
|
||||
let {defer, resolve, reject, promised, all} = require("sdk/core/promise");
|
||||
this.defer = defer;
|
||||
this.resolve = resolve;
|
||||
@ -396,6 +397,7 @@ var DebuggerServer = {
|
||||
this.registerModule("devtools/server/actors/call-watcher");
|
||||
this.registerModule("devtools/server/actors/canvas");
|
||||
this.registerModule("devtools/server/actors/webgl");
|
||||
this.registerModule("devtools/server/actors/webaudio");
|
||||
this.registerModule("devtools/server/actors/stylesheets");
|
||||
this.registerModule("devtools/server/actors/styleeditor");
|
||||
this.registerModule("devtools/server/actors/storage");
|
||||
@ -571,6 +573,7 @@ var DebuggerServer = {
|
||||
// If we have a child transport, the actor has already
|
||||
// been created. We need to stop using this message manager.
|
||||
childTransport.close();
|
||||
childTransport = null;
|
||||
aConnection.cancelForwarding(prefix);
|
||||
} else {
|
||||
// Otherwise, the app has been closed before the actor
|
||||
@ -594,6 +597,20 @@ var DebuggerServer = {
|
||||
Services.obs.addObserver(onMessageManagerDisconnect,
|
||||
"message-manager-disconnect", false);
|
||||
|
||||
events.once(aConnection, "closed", () => {
|
||||
if (childTransport) {
|
||||
// When the client disconnects, we have to unplug the dedicated
|
||||
// ChildDebuggerTransport...
|
||||
childTransport.close();
|
||||
childTransport = null;
|
||||
aConnection.cancelForwarding(prefix);
|
||||
|
||||
// ... and notify the child process to clean the tab actors.
|
||||
mm.sendAsyncMessage("debug:disconnect");
|
||||
}
|
||||
Services.obs.removeObserver(onMessageManagerDisconnect, "message-manager-disconnect");
|
||||
});
|
||||
|
||||
mm.sendAsyncMessage("debug:connect", { prefix: prefix });
|
||||
|
||||
return deferred.promise;
|
||||
@ -1156,6 +1173,11 @@ DebuggerServerConnection.prototype = {
|
||||
*/
|
||||
onClosed: function DSC_onClosed(aStatus) {
|
||||
dumpn("Cleaning up connection.");
|
||||
if (!this._actorPool) {
|
||||
// Ignore this call if the connection is already closed.
|
||||
return;
|
||||
}
|
||||
events.emit(this, "closed", aStatus);
|
||||
|
||||
this._actorPool.cleanup();
|
||||
this._actorPool = null;
|
||||
|
@ -43,3 +43,4 @@ support-files =
|
||||
[test_inspector_getImageData.html]
|
||||
[test_memory.html]
|
||||
[test_preference.html]
|
||||
[test_connectToChild.html]
|
||||
|
127
toolkit/devtools/server/tests/mochitest/test_connectToChild.html
Normal file
127
toolkit/devtools/server/tests/mochitest/test_connectToChild.html
Normal file
@ -0,0 +1,127 @@
|
||||
<SDOCTYPv HTM.>
|
||||
<html>
|
||||
<!--
|
||||
Bug 966991 - Test DebuggerServer.connectToChild
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mozilla Bug</title>
|
||||
<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 type="application/javascript;version=1.8">
|
||||
|
||||
let Cu = Components.utils;
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
|
||||
|
||||
window.onload = function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Always log packets when running tests.
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["devtools.debugger.log", true]
|
||||
]
|
||||
}, runTests);
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
// Create a minimal iframe with a message manager
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.setAttribute("mozbrowser", "true");
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
let mm = iframe.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
|
||||
|
||||
// Register a test actor in the child process so that we can know if and when
|
||||
// this fake actor is disconnected.
|
||||
mm.loadFrameScript("data:text/javascript,new " + function FrameScriptScope() {
|
||||
const {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
||||
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
}
|
||||
|
||||
function TestActor() {dump("instanciate test actor");}
|
||||
TestActor.prototype = {
|
||||
actorPrefix: "test",
|
||||
|
||||
disconnect: function () {
|
||||
sendAsyncMessage("test-actor-disconnected", null);
|
||||
},
|
||||
hello: function () {
|
||||
return {msg:"world"};
|
||||
}
|
||||
};
|
||||
TestActor.prototype.requestTypes = {
|
||||
"hello": TestActor.prototype.hello
|
||||
};
|
||||
DebuggerServer.addTabActor(TestActor, "testActor");
|
||||
}, false);
|
||||
|
||||
// Instantiate a minimal server
|
||||
DebuggerServer.init(function () { return true; });
|
||||
DebuggerServer.addBrowserActors();
|
||||
|
||||
function firstClient() {
|
||||
// Fake a first connection to an iframe
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
let conn = transport._serverConnection;
|
||||
let client = new DebuggerClient(transport);
|
||||
DebuggerServer.connectToChild(conn, mm).then(actor => {
|
||||
ok(actor.testActor, "Got the test actor");
|
||||
|
||||
// Ensure sending at least one request to our actor,
|
||||
// otherwise it won't be instanciated, nor be disconnected...
|
||||
client.request({
|
||||
to: actor.testActor,
|
||||
type: "hello",
|
||||
}, function (response) {
|
||||
|
||||
// Then close the client. That should end up cleaning our test actor
|
||||
client.close();
|
||||
|
||||
// Ensure that our test actor got cleaned up;
|
||||
// its disconnect method should be called
|
||||
mm.addMessageListener("test-actor-disconnected", function listener() {
|
||||
mm.removeMessageListener("test-actor-disconnected", listener);
|
||||
ok(true, "Actor is cleaned up");
|
||||
|
||||
secondClient(actor.testActor);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function secondClient(firstActor) {
|
||||
// Then fake a second one, that should spawn a new set of tab actors
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
let conn = transport._serverConnection;
|
||||
let client = new DebuggerClient(transport);
|
||||
DebuggerServer.connectToChild(conn, mm).then(actor => {
|
||||
ok(actor.testActor, "Got a test actor for the second connection");
|
||||
isnot(actor.testActor, firstActor, "We get different actor instances between two connections");
|
||||
|
||||
client.close(cleanup);
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
DebuggerServer.destroy();
|
||||
iframe.remove();
|
||||
SimpleTest.finish()
|
||||
}
|
||||
|
||||
firstClient();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user