mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Merge m-c to inbound
This commit is contained in:
commit
934871f3a2
@ -7,7 +7,6 @@ support-files =
|
||||
systemapp_helper.js
|
||||
|
||||
[test_sandbox_permission.html]
|
||||
skip-if = true # bug 984274 - frequent timeouts
|
||||
[test_filepicker_path.html]
|
||||
[test_permission_deny.html]
|
||||
[test_permission_gum_remember.html]
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0c637f14265291ed81934058ec1cc019612127c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0750f66a0004870773c9a743fa6bdbe124379336"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="dbb66e540194a187326cece28ae0b51cdd500184"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8e4420c0c5c8e8c8e58a000278a7129403769f96"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e0c637f14265291ed81934058ec1cc019612127c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0750f66a0004870773c9a743fa6bdbe124379336"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="dbb66e540194a187326cece28ae0b51cdd500184"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="cabebb87fcd32f8596af08e6b5e80764ee0157dd"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e0c637f14265291ed81934058ec1cc019612127c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0750f66a0004870773c9a743fa6bdbe124379336"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="dbb66e540194a187326cece28ae0b51cdd500184"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0c637f14265291ed81934058ec1cc019612127c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0750f66a0004870773c9a743fa6bdbe124379336"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="dbb66e540194a187326cece28ae0b51cdd500184"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8e4420c0c5c8e8c8e58a000278a7129403769f96"/>
|
||||
|
@ -28,7 +28,8 @@
|
||||
"VARIANT": "user",
|
||||
"MOZILLA_OFFICIAL": "1",
|
||||
"MOZ_TELEMETRY_REPORTING": "1",
|
||||
"B2G_UPDATE_CHANNEL": "nightly"
|
||||
"B2G_UPDATE_CHANNEL": "nightly",
|
||||
"GAIA_KEYBOARD_LAYOUTS": "en,pt-BR,es,de,fr,pl,zh-Hans-Pinyin,zh-Hant-Zhuyin,en-Dvorak"
|
||||
},
|
||||
"b2g_manifest": "flame.xml",
|
||||
"b2g_manifest_intree": true,
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e0c637f14265291ed81934058ec1cc019612127c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0750f66a0004870773c9a743fa6bdbe124379336"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="dbb66e540194a187326cece28ae0b51cdd500184"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="cabebb87fcd32f8596af08e6b5e80764ee0157dd"/>
|
||||
@ -134,7 +134,7 @@
|
||||
<project name="platform/hardware/libhardware" path="hardware/libhardware" revision="484802559ed106bac4811bd01c024ca64f741e60"/>
|
||||
<project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="83f363a26069e9c188d2aaef5b9ef63e84ad1511"/>
|
||||
<project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="5e110615212302c5d798a3c223dcee458817651c"/>
|
||||
<project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="e38444b6ce12c7c25883272a439800376d5308eb"/>
|
||||
<project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="280d29203b2aa30d713c5a6cc63d626e5a7df822"/>
|
||||
<project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="5dc48bd46f9589653f8bf297be5d73676f2e2867"/>
|
||||
<project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="8a0d0b0d9889ef99c4c6317c810db4c09295f15a"/>
|
||||
<project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2208fa3537ace873b8f9ec2355055761c79dfd5f"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "37cd9a62b2cb570985d99a5519b136672614b980",
|
||||
"revision": "16222b6246385cf793ae5fdced0ea6d548b7b949",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0c637f14265291ed81934058ec1cc019612127c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0750f66a0004870773c9a743fa6bdbe124379336"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="dbb66e540194a187326cece28ae0b51cdd500184"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0c637f14265291ed81934058ec1cc019612127c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0750f66a0004870773c9a743fa6bdbe124379336"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="dbb66e540194a187326cece28ae0b51cdd500184"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="e0c637f14265291ed81934058ec1cc019612127c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0750f66a0004870773c9a743fa6bdbe124379336"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="dbb66e540194a187326cece28ae0b51cdd500184"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="cabebb87fcd32f8596af08e6b5e80764ee0157dd"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0c637f14265291ed81934058ec1cc019612127c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0750f66a0004870773c9a743fa6bdbe124379336"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="dbb66e540194a187326cece28ae0b51cdd500184"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -1929,13 +1929,21 @@ let CustomizableUIInternal = {
|
||||
this.notifyListeners("onWidgetCreated", widget.id);
|
||||
|
||||
if (widget.defaultArea) {
|
||||
let addToDefaultPlacements = false;
|
||||
let area = gAreas.get(widget.defaultArea);
|
||||
//XXXgijs this won't have any effect for legacy items. Sort of OK because
|
||||
// consumers can modify currentset? Maybe?
|
||||
if (area.has("defaultPlacements")) {
|
||||
area.get("defaultPlacements").push(widget.id);
|
||||
} else {
|
||||
area.set("defaultPlacements", [widget.id]);
|
||||
if (widget.source == CustomizableUI.SOURCE_BUILTIN) {
|
||||
addToDefaultPlacements = true;
|
||||
} else if (!CustomizableUI.isBuiltinToolbar(widget.defaultArea) &&
|
||||
widget.defaultArea != CustomizableUI.AREA_PANEL) {
|
||||
addToDefaultPlacements = true;
|
||||
}
|
||||
|
||||
if (addToDefaultPlacements) {
|
||||
if (area.has("defaultPlacements")) {
|
||||
area.get("defaultPlacements").push(widget.id);
|
||||
} else {
|
||||
area.set("defaultPlacements", [widget.id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -665,7 +665,7 @@ CustomizeMode.prototype = {
|
||||
if (customizationTarget && customizationTarget != areaNode) {
|
||||
areas.push(customizationTarget.id);
|
||||
}
|
||||
let overflowTarget = areaNode.getAttribute("overflowtarget");
|
||||
let overflowTarget = areaNode && areaNode.getAttribute("overflowtarget");
|
||||
if (overflowTarget) {
|
||||
areas.push(overflowTarget);
|
||||
}
|
||||
|
@ -94,6 +94,7 @@ skip-if = os == "linux"
|
||||
[browser_978084_dragEnd_after_move.js]
|
||||
[browser_980155_add_overflow_toolbar.js]
|
||||
[browser_981418-widget-onbeforecreated-handler.js]
|
||||
[browser_982656_restore_defaults_builtin_widgets.js]
|
||||
|
||||
[browser_984455_bookmarks_items_reparenting.js]
|
||||
skip-if = os == "linux"
|
||||
|
@ -0,0 +1,57 @@
|
||||
/* 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";
|
||||
|
||||
// Restoring default should not place addon widgets back in the toolbar
|
||||
add_task(function() {
|
||||
ok(CustomizableUI.inDefaultState, "Default state to begin");
|
||||
|
||||
const kWidgetId = "bug982656-add-on-widget-should-not-restore-to-default-area";
|
||||
let widgetSpec = {
|
||||
id: kWidgetId,
|
||||
defaultArea: CustomizableUI.AREA_NAVBAR
|
||||
};
|
||||
CustomizableUI.createWidget(widgetSpec);
|
||||
|
||||
ok(!CustomizableUI.inDefaultState, "Not in default state after widget added");
|
||||
is(CustomizableUI.getPlacementOfWidget(kWidgetId).area, CustomizableUI.AREA_NAVBAR, "Widget should be in navbar");
|
||||
|
||||
yield resetCustomization();
|
||||
|
||||
ok(CustomizableUI.inDefaultState, "Back in default state after reset");
|
||||
is(CustomizableUI.getPlacementOfWidget(kWidgetId), null, "Widget now in palette");
|
||||
CustomizableUI.destroyWidget(kWidgetId);
|
||||
});
|
||||
|
||||
|
||||
// resetCustomization shouldn't move 3rd party widgets out of custom toolbars
|
||||
add_task(function() {
|
||||
const kToolbarId = "bug982656-toolbar-with-defaultset";
|
||||
const kWidgetId = "bug982656-add-on-widget-should-restore-to-default-area-when-area-is-not-builtin";
|
||||
ok(CustomizableUI.inDefaultState, "Everything should be in its default state.");
|
||||
let toolbar = createToolbarWithPlacements(kToolbarId);
|
||||
ok(CustomizableUI.areas.indexOf(kToolbarId) != -1,
|
||||
"Toolbar has been registered.");
|
||||
is(CustomizableUI.getAreaType(kToolbarId), CustomizableUI.TYPE_TOOLBAR,
|
||||
"Area should be registered as toolbar");
|
||||
|
||||
let widgetSpec = {
|
||||
id: kWidgetId,
|
||||
defaultArea: kToolbarId
|
||||
};
|
||||
CustomizableUI.createWidget(widgetSpec);
|
||||
|
||||
ok(!CustomizableUI.inDefaultState, "No longer in default state after toolbar is registered and visible.");
|
||||
is(CustomizableUI.getPlacementOfWidget(kWidgetId).area, kToolbarId, "Widget should be in custom toolbar");
|
||||
|
||||
yield resetCustomization();
|
||||
ok(CustomizableUI.inDefaultState, "Back in default state after reset");
|
||||
is(CustomizableUI.getPlacementOfWidget(kWidgetId).area, kToolbarId, "Widget still in custom toolbar");
|
||||
ok(toolbar.collapsed, "Custom toolbar should be collapsed after reset");
|
||||
|
||||
toolbar.remove();
|
||||
CustomizableUI.destroyWidget(kWidgetId);
|
||||
CustomizableUI.unregisterArea(kToolbarId);
|
||||
});
|
@ -41,25 +41,35 @@ let startTests = Task.async(function*() {
|
||||
|
||||
function* performTests(inspector, ruleview) {
|
||||
yield togglePseudoClass(inspector);
|
||||
yield testAdded(inspector, ruleview);
|
||||
yield assertPseudoAddedToNode(inspector, ruleview);
|
||||
|
||||
yield togglePseudoClass(inspector);
|
||||
yield testRemoved();
|
||||
yield testRemovedFromUI(inspector, ruleview);
|
||||
yield assertPseudoRemovedFromNode();
|
||||
yield assertPseudoRemovedFromView(inspector, ruleview);
|
||||
|
||||
yield togglePseudoClass(inspector);
|
||||
yield testNavigate(inspector, ruleview);
|
||||
}
|
||||
|
||||
function* togglePseudoClass(inspector) {
|
||||
info("Toggle the pseudoclass, wait for the pseudoclass event and wait for the refresh of the rule view");
|
||||
info("Toggle the pseudoclass, wait for it to be applied");
|
||||
|
||||
// Give the inspector panels a chance to update when the pseudoclass changes
|
||||
let onPseudo = inspector.selection.once("pseudoclass");
|
||||
let onRefresh = inspector.once("rule-view-refreshed");
|
||||
inspector.togglePseudoClass(PSEUDO);
|
||||
let onMutations = waitForMutation(inspector);
|
||||
|
||||
yield inspector.togglePseudoClass(PSEUDO);
|
||||
|
||||
yield onPseudo;
|
||||
yield onRefresh;
|
||||
yield onMutations;
|
||||
}
|
||||
|
||||
function waitForMutation(inspector) {
|
||||
let def = promise.defer();
|
||||
inspector.walker.once("mutations", def.resolve);
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
function* testNavigate(inspector, ruleview) {
|
||||
@ -87,7 +97,7 @@ function showPickerOn(node, inspector) {
|
||||
return highlighter.showBoxModel(getNodeFront(node));
|
||||
}
|
||||
|
||||
function* testAdded(inspector, ruleview) {
|
||||
function* assertPseudoAddedToNode(inspector, ruleview) {
|
||||
info("Make sure the pseudoclass lock is applied to #div-1 and its ancestors");
|
||||
let node = getNode("#div-1");
|
||||
do {
|
||||
@ -110,7 +120,7 @@ function* testAdded(inspector, ruleview) {
|
||||
yield inspector.toolbox.highlighter.hideBoxModel();
|
||||
}
|
||||
|
||||
function* testRemoved() {
|
||||
function* assertPseudoRemovedFromNode() {
|
||||
info("Make sure the pseudoclass lock is removed from #div-1 and its ancestors");
|
||||
let node = getNode("#div-1");
|
||||
do {
|
||||
@ -120,7 +130,7 @@ function* testRemoved() {
|
||||
} while (node.parentNode)
|
||||
}
|
||||
|
||||
function* testRemovedFromUI(inspector, ruleview) {
|
||||
function* assertPseudoRemovedFromView(inspector, ruleview) {
|
||||
info("Check that the ruleview no longer contains the pseudo-class rule");
|
||||
let rules = ruleview.element.querySelectorAll(".ruleview-rule.theme-separator");
|
||||
is(rules.length, 2, "rule view is showing 2 rules after removing lock");
|
||||
@ -137,6 +147,6 @@ function* finishUp(toolbox) {
|
||||
toolbox.destroy();
|
||||
yield onDestroy;
|
||||
|
||||
yield testRemoved(getNode("#div-1"));
|
||||
yield assertPseudoRemovedFromNode(getNode("#div-1"));
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
|
@ -20,7 +20,12 @@ function* performTest() {
|
||||
doc.body.setAttribute("style", "position: fixed; width: 100%; height: 100%; margin: 0;");
|
||||
|
||||
let graph = new LineGraphWidget(doc.body, "fps");
|
||||
yield graph.once("ready");
|
||||
|
||||
let readyEventEmitted;
|
||||
graph.once("ready", () => readyEventEmitted = true);
|
||||
|
||||
yield graph.ready();
|
||||
ok(readyEventEmitted, "The 'ready' event should have been emitted");
|
||||
|
||||
testGraph(host, graph);
|
||||
|
||||
|
@ -5,8 +5,9 @@
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/event-emitter.js");
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
|
||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["LineGraphWidget"];
|
||||
|
||||
@ -116,6 +117,7 @@ GraphSelectionResizer.prototype = {
|
||||
this.AbstractCanvasGraph = function(parent, name, sharpness) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this._ready = promise.defer();
|
||||
this._parent = parent;
|
||||
this._uid = "canvas-graph-" + Date.now();
|
||||
|
||||
@ -165,6 +167,7 @@ this.AbstractCanvasGraph = function(parent, name, sharpness) {
|
||||
|
||||
this._animationId = this._window.requestAnimationFrame(this._onAnimationFrame);
|
||||
|
||||
this._ready.resolve(this);
|
||||
this.emit("ready", this);
|
||||
});
|
||||
}
|
||||
@ -181,6 +184,13 @@ AbstractCanvasGraph.prototype = {
|
||||
return this._height;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a promise resolved once this graph is ready to receive data.
|
||||
*/
|
||||
ready: function() {
|
||||
return this._ready.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroys this graph.
|
||||
*/
|
||||
@ -501,15 +511,16 @@ AbstractCanvasGraph.prototype = {
|
||||
let ctx = this._ctx;
|
||||
ctx.clearRect(0, 0, this._width, this._height);
|
||||
|
||||
// Draw the graph underneath the cursor and selection.
|
||||
if (this.hasData()) {
|
||||
ctx.drawImage(this._cachedGraphImage, 0, 0, this._width, this._height);
|
||||
}
|
||||
if (this.hasCursor()) {
|
||||
this._drawCliphead();
|
||||
}
|
||||
if (this.hasSelection() || this.hasSelectionInProgress()) {
|
||||
this._drawSelection();
|
||||
}
|
||||
if (this.hasData()) {
|
||||
ctx.drawImage(this._cachedGraphImage, 0, 0, this._width, this._height);
|
||||
}
|
||||
|
||||
this._shouldRedraw = false;
|
||||
},
|
||||
@ -957,24 +968,17 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
||||
let width = canvas.width = this._width;
|
||||
let height = canvas.height = this._height;
|
||||
|
||||
let totalTicks = this._data.length;
|
||||
let firstTick = this._data[0].delta;
|
||||
let lastTick = this._data[totalTicks - 1].delta;
|
||||
let maxValue = Number.MIN_SAFE_INTEGER;
|
||||
let minValue = Number.MAX_SAFE_INTEGER;
|
||||
let sumValues = 0;
|
||||
let totalTicks = 0;
|
||||
let firstTick;
|
||||
let lastTick;
|
||||
|
||||
for (let { delta, value } of this._data) {
|
||||
maxValue = Math.max(value, maxValue);
|
||||
minValue = Math.min(value, minValue);
|
||||
sumValues += value;
|
||||
totalTicks++;
|
||||
|
||||
if (!firstTick) {
|
||||
firstTick = delta;
|
||||
} else {
|
||||
lastTick = delta;
|
||||
}
|
||||
}
|
||||
|
||||
let dataScaleX = this.dataScaleX = width / lastTick;
|
||||
@ -997,7 +1001,6 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.strokeStyle = LINE_GRAPH_STROKE_COLOR;
|
||||
ctx.lineWidth = LINE_GRAPH_STROKE_WIDTH;
|
||||
ctx.setLineDash([]);
|
||||
ctx.beginPath();
|
||||
|
||||
let prevX = 0;
|
||||
|
@ -377,7 +377,8 @@ exports.AppManager = AppManager = {
|
||||
project.manifest);
|
||||
}
|
||||
|
||||
function waitUntilProjectRuns() {
|
||||
let manifest = self.getProjectManifestURL(project);
|
||||
if (!self._runningApps.has(manifest)) {
|
||||
let deferred = promise.defer();
|
||||
self.on("app-manager-update", function onUpdate(event, what) {
|
||||
if (what == "project-is-running") {
|
||||
@ -385,13 +386,8 @@ exports.AppManager = AppManager = {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
let manifest = self.getProjectManifestURL(project);
|
||||
if (!self._runningApps.has(manifest)) {
|
||||
yield AppActorFront.launchApp(client, actor, manifest);
|
||||
yield waitUntilProjectRuns();
|
||||
yield deferred.promise;
|
||||
|
||||
} else {
|
||||
yield AppActorFront.reloadApp(client, actor, manifest);
|
||||
|
@ -1829,14 +1829,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
||||
margin: 0 0 @tabToolbarNavbarOverlap@;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-up {
|
||||
-moz-border-start: 0;
|
||||
-moz-border-end: 2px solid transparent;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-down {
|
||||
-moz-border-start: 2px solid transparent;
|
||||
-moz-border-end: 0;
|
||||
transition: 1s box-shadow ease-out;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@ -1846,20 +1839,6 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(ltr),
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(rtl) {
|
||||
border-width: 0 2px 0 0;
|
||||
border-style: solid;
|
||||
border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(ltr),
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(rtl) {
|
||||
border-width: 0 0 0 2px;
|
||||
border-style: solid;
|
||||
border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
|
||||
}
|
||||
|
||||
#TabsToolbar .toolbarbutton-1 {
|
||||
margin-bottom: @tabToolbarNavbarOverlap@;
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ browser.jar:
|
||||
skin/classic/browser/tabbrowser/tab-background-end.png (tabbrowser/tab-background-end.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-middle.png (tabbrowser/tab-background-middle.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-start.png (tabbrowser/tab-background-start.png)
|
||||
skin/classic/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png)
|
||||
skin/classic/browser/tabbrowser/tab-overflow-indicator.png (../shared/tabbrowser/tab-overflow-indicator.png)
|
||||
|
||||
# NOTE: The following two files (tab-selected-end.svg, tab-selected-start.svg) get pre-processed in
|
||||
# Makefile.in with a non-default marker of "%" and the result of that gets packaged.
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 193 B |
@ -2971,20 +2971,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
|
||||
}
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(ltr),
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(rtl) {
|
||||
border-width: 0 2px 0 0;
|
||||
border-style: solid;
|
||||
border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(ltr),
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(rtl) {
|
||||
border-width: 0 0 0 2px;
|
||||
border-style: solid;
|
||||
border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tabstrip & add-on bar toolbar buttons
|
||||
*/
|
||||
|
@ -277,6 +277,7 @@ browser.jar:
|
||||
skin/classic/browser/tabbrowser/tab-background-middle@2x.png (tabbrowser/tab-background-middle@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-start.png (tabbrowser/tab-background-start.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-start@2x.png (tabbrowser/tab-background-start@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-overflow-indicator.png (../shared/tabbrowser/tab-overflow-indicator.png)
|
||||
|
||||
# NOTE: The following two files (tab-selected-end.svg, tab-selected-start.svg) get pre-processed in
|
||||
# Makefile.in with a non-default marker of "%" and the result of that gets packaged.
|
||||
@ -287,7 +288,6 @@ browser.jar:
|
||||
skin/classic/browser/tabbrowser/tab-stroke-end@2x.png (tabbrowser/tab-stroke-end@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-stroke-start.png (tabbrowser/tab-stroke-start.png)
|
||||
skin/classic/browser/tabbrowser/tab-stroke-start@2x.png (tabbrowser/tab-stroke-start@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png)
|
||||
skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
|
||||
skin/classic/browser/tabbrowser/tabDragIndicator@2x.png (tabbrowser/tabDragIndicator@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-separator.png (tabbrowser/tab-separator.png)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 184 B |
BIN
browser/themes/shared/tabbrowser/tab-overflow-indicator.png
Normal file
BIN
browser/themes/shared/tabbrowser/tab-overflow-indicator.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 578 B |
@ -130,6 +130,43 @@
|
||||
-moz-padding-start: @tabCurveHalfWidth@;
|
||||
}
|
||||
|
||||
/* Tab Overflow */
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:not([collapsed]),
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:not([collapsed]) {
|
||||
background-image: url(chrome://browser/skin/tabbrowser/tab-overflow-indicator.png);
|
||||
background-size: 100% 100%;
|
||||
width: 14px;
|
||||
margin-bottom: @tabToolbarNavbarOverlap@;
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
z-index: 3; /* the selected tab's z-index + 1 */
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:-moz-locale-dir(rtl),
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:-moz-locale-dir(ltr) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:not([collapsed]) {
|
||||
-moz-margin-start: -2px;
|
||||
-moz-margin-end: -12px;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:not([collapsed]) {
|
||||
-moz-margin-start: -12px;
|
||||
-moz-margin-end: -2px;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator[collapsed],
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator[collapsed] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator,
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator {
|
||||
transition: opacity 150ms ease;
|
||||
}
|
||||
|
||||
.tab-background-start[selected=true]::after,
|
||||
.tab-background-start[selected=true]::before,
|
||||
.tab-background-start,
|
||||
|
@ -1855,9 +1855,6 @@ toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-down {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png");
|
||||
margin: 0 0 @tabToolbarNavbarOverlap@;
|
||||
padding-right: 2px;
|
||||
border-right: 2px solid transparent;
|
||||
background-origin: border-box;
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-up,
|
||||
@ -1884,13 +1881,6 @@ toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]),
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]) {
|
||||
border-width: 0 2px 0 0;
|
||||
border-style: solid;
|
||||
border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
|
||||
}
|
||||
|
||||
.tabs-newtab-button > .toolbarbutton-icon {
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
|
@ -196,7 +196,7 @@ browser.jar:
|
||||
skin/classic/browser/tabbrowser/tab-background-middle@2x.png (tabbrowser/tab-background-middle@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-end.png (tabbrowser/tab-background-end.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-end@2x.png (tabbrowser/tab-background-end@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png)
|
||||
skin/classic/browser/tabbrowser/tab-overflow-indicator.png (../shared/tabbrowser/tab-overflow-indicator.png)
|
||||
|
||||
# NOTE: The following two files (tab-selected-end.svg, tab-selected-start.svg) get pre-processed in
|
||||
# Makefile.in with a non-default marker of "%" and the result of that gets packaged.
|
||||
@ -601,7 +601,7 @@ browser.jar:
|
||||
skin/classic/aero/browser/tabbrowser/tab-background-middle@2x.png (tabbrowser/tab-background-middle@2x.png)
|
||||
skin/classic/aero/browser/tabbrowser/tab-background-end.png (tabbrowser/tab-background-end.png)
|
||||
skin/classic/aero/browser/tabbrowser/tab-background-end@2x.png (tabbrowser/tab-background-end@2x.png)
|
||||
skin/classic/aero/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png)
|
||||
skin/classic/aero/browser/tabbrowser/tab-overflow-indicator.png (../shared/tabbrowser/tab-overflow-indicator.png)
|
||||
|
||||
# NOTE: The following two files (tab-selected-end.svg, tab-selected-start.svg) get pre-processed in
|
||||
# Makefile.in with a non-default marker of "%" and the result of that gets packaged.
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 193 B |
14
configure.in
14
configure.in
@ -3941,7 +3941,6 @@ MOZ_USE_NATIVE_POPUP_WINDOWS=
|
||||
MOZ_ANDROID_HISTORY=
|
||||
MOZ_WEBSMS_BACKEND=
|
||||
MOZ_ANDROID_BEAM=
|
||||
MOZ_ANDROID_SYNTHAPKS=
|
||||
MOZ_LOCALE_SWITCHER=
|
||||
ACCESSIBILITY=1
|
||||
MOZ_TIME_MANAGER=
|
||||
@ -4966,18 +4965,6 @@ if test -n "$MOZ_ANDROID_BEAM"; then
|
||||
AC_DEFINE(MOZ_ANDROID_BEAM)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Synthesized Webapp APKs on Android
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(android-synthapks,
|
||||
[ --enable-android-synthapks Enable synthesized APKs],
|
||||
MOZ_ANDROID_SYNTHAPKS=1,
|
||||
MOZ_ANDROID_SYNTHAPKS=)
|
||||
|
||||
if test -n "$MOZ_ANDROID_SYNTHAPKS"; then
|
||||
AC_DEFINE(MOZ_ANDROID_SYNTHAPKS)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = JS Debugger XPCOM component (js/jsd)
|
||||
dnl ========================================================
|
||||
@ -8574,7 +8561,6 @@ AC_SUBST(MOZ_METRO)
|
||||
AC_SUBST(MOZ_ANDROID_HISTORY)
|
||||
AC_SUBST(MOZ_WEBSMS_BACKEND)
|
||||
AC_SUBST(MOZ_ANDROID_BEAM)
|
||||
AC_SUBST(MOZ_ANDROID_SYNTHAPKS)
|
||||
AC_SUBST(MOZ_LOCALE_SWITCHER)
|
||||
AC_SUBST(MOZ_DISABLE_GECKOVIEW)
|
||||
AC_SUBST(ENABLE_STRIP)
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIDOMWindow.idl"
|
||||
|
||||
[scriptable,uuid(ff9c0e45-4646-45ec-b2f0-3b16d9e41875)]
|
||||
[scriptable,uuid(b3a7a402-2760-4583-b4a3-af095fe00c84)]
|
||||
interface nsITabSource : nsISupports
|
||||
{
|
||||
nsIDOMWindow getTabToStream();
|
||||
|
@ -71,7 +71,7 @@ function _setAppProperties(aObj, aApp) {
|
||||
aObj.csp = aApp.csp;
|
||||
aObj.installOrigin = aApp.installOrigin;
|
||||
aObj.origin = aApp.origin;
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
aObj.apkPackageName = aApp.apkPackageName;
|
||||
#endif
|
||||
aObj.receipts = aApp.receipts ? JSON.parse(JSON.stringify(aApp.receipts)) : null;
|
||||
|
@ -1090,7 +1090,7 @@ this.DOMApplicationRegistry = {
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "Webapps:Install": {
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg));
|
||||
#else
|
||||
this.doInstall(msg, mm);
|
||||
@ -1119,7 +1119,7 @@ this.DOMApplicationRegistry = {
|
||||
this.doGetAll(msg, mm);
|
||||
break;
|
||||
case "Webapps:InstallPackage": {
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg));
|
||||
#else
|
||||
this.doInstallPackage(msg, mm);
|
||||
@ -2510,15 +2510,15 @@ this.DOMApplicationRegistry = {
|
||||
} else if (manifest.package_path) {
|
||||
// If it is a local app then it must been installed from a local file
|
||||
// instead of web.
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
// In that case, we would already have the manifest, not just the update
|
||||
// manifest.
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
dontNeedNetwork = !!aData.app.manifest;
|
||||
#else
|
||||
if (aData.app.localInstallPath) {
|
||||
dontNeedNetwork = true;
|
||||
jsonManifest.package_path = "file://" + aData.app.localInstallPath;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// origin for install apps is meaningless here, since it's app:// and this
|
||||
|
@ -1,4 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 et filetype=javascript
|
||||
* 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/. */
|
||||
|
||||
@ -34,14 +36,7 @@ const BDADDR_ALL = "ff:ff:ff:ff:ff:ff";
|
||||
const BDADDR_LOCAL = "ff:ff:ff:00:00:00";
|
||||
|
||||
// A user friendly name for remote BT device.
|
||||
const REMOTE_DEVICE_NAME = "Remote_BT_Device";
|
||||
|
||||
// A system message signature of pairing request event
|
||||
const BT_PAIRING_REQ = "bluetooth-pairing-request";
|
||||
|
||||
// Passkey and pincode used to reply pairing requst
|
||||
const BT_PAIRING_PASSKEY = 123456;
|
||||
const BT_PAIRING_PINCODE = "ABCDEFG";
|
||||
const REMOTE_DEVICE_NAME = "Remote BT Device";
|
||||
|
||||
let Promise =
|
||||
SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
|
||||
@ -85,33 +80,6 @@ function runEmulatorCmdSafe(aCommand) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
|
||||
*
|
||||
* Fulfill params: A DOMEvent.
|
||||
* Reject params: A DOMEvent.
|
||||
*
|
||||
* @param aRequest
|
||||
* A DOMRequest instance.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function wrapDomRequestAsPromise(aRequest) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
ok(aRequest instanceof DOMRequest,
|
||||
"aRequest is instanceof " + aRequest.constructor);
|
||||
|
||||
aRequest.onsuccess = function(aEvent) {
|
||||
deferred.resolve(aEvent);
|
||||
};
|
||||
aRequest.onerror = function(aEvent) {
|
||||
deferred.reject(aEvent);
|
||||
};
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Bluetooth remote device to scatternet and set its properties.
|
||||
*
|
||||
@ -220,11 +188,13 @@ function setEmulatorDeviceProperty(aAddress, aPropertyName, aValue) {
|
||||
function getEmulatorDeviceProperty(aAddress, aPropertyName) {
|
||||
let cmd = "bt property " + aAddress + " " + aPropertyName;
|
||||
return runEmulatorCmdSafe(cmd)
|
||||
.then(aResults => aResults[0]);
|
||||
.then(function(aResults) {
|
||||
return aResults[0];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start discovering Bluetooth devices.
|
||||
* Start dicovering Bluetooth devices.
|
||||
*
|
||||
* Allows the device's adapter to start seeking for remote devices.
|
||||
*
|
||||
@ -232,28 +202,32 @@ function getEmulatorDeviceProperty(aAddress, aPropertyName) {
|
||||
* Reject params: a DOMError
|
||||
*
|
||||
* @param aAdapter
|
||||
* A BluetoothAdapter which is used to interact with local BT device.
|
||||
* A BluetoothAdapter which is used to interact with local BT dev
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function startDiscovery(aAdapter) {
|
||||
let request = aAdapter.startDiscovery();
|
||||
let deferred = Promise.defer();
|
||||
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then(function resolve() {
|
||||
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
|
||||
// Currently, discovering state wouldn't change immediately here.
|
||||
// We would turn on this check when the redesigned API are landed.
|
||||
// is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
|
||||
log(" Start discovery - Success");
|
||||
}, function reject(aEvent) {
|
||||
ok(false, "Start discovery - Fail");
|
||||
throw aEvent.target.error;
|
||||
});
|
||||
let request = aAdapter.startDiscovery();
|
||||
request.onsuccess = function () {
|
||||
log(" Start discovery - Success");
|
||||
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
|
||||
// Currently, discovering state wouldn't change immediately here.
|
||||
// We would turn on this check when the redesigned API are landed.
|
||||
// is(aAdapter.discovering, true, "BluetoothAdapter.discovering");
|
||||
deferred.resolve();
|
||||
}
|
||||
request.onerror = function (aEvent) {
|
||||
ok(false, "Start discovery - Fail");
|
||||
deferred.reject(aEvent.target.error);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop discovering Bluetooth devices.
|
||||
* Stop dicovering Bluetooth devices.
|
||||
*
|
||||
* Allows the device's adapter to stop seeking for remote devices.
|
||||
*
|
||||
@ -266,184 +240,24 @@ function startDiscovery(aAdapter) {
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function stopDiscovery(aAdapter) {
|
||||
let request = aAdapter.stopDiscovery();
|
||||
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then(function resolve() {
|
||||
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
|
||||
// Currently, discovering state wouldn't change immediately here.
|
||||
// We would turn on this check when the redesigned API are landed.
|
||||
// is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
|
||||
log(" Stop discovery - Success");
|
||||
}, function reject(aEvent) {
|
||||
ok(false, "Stop discovery - Fail");
|
||||
throw aEvent.target.error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for 'devicefound' event of specified devices.
|
||||
*
|
||||
* Resolve if that every specified devices has been found. Never reject.
|
||||
*
|
||||
* Fulfill params: an array which contains addresses of remote devices.
|
||||
*
|
||||
* @param aAdapter
|
||||
* A BluetoothAdapter which is used to interact with local BT device.
|
||||
* @param aRemoteAddresses
|
||||
* An array which contains addresses of remote devices.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForDevicesFound(aAdapter, aRemoteAddresses) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
var addrArray = [];
|
||||
aAdapter.addEventListener("devicefound", function onevent(aEvent) {
|
||||
if(aRemoteAddresses.indexOf(aEvent.device.address) != -1) {
|
||||
addrArray.push(aEvent.device.address);
|
||||
}
|
||||
if(addrArray.length == aRemoteAddresses.length) {
|
||||
aAdapter.removeEventListener("devicefound", onevent);
|
||||
ok(true, "BluetoothAdapter has found all remote devices.");
|
||||
|
||||
deferred.resolve(addrArray);
|
||||
}
|
||||
});
|
||||
|
||||
let request = aAdapter.stopDiscovery();
|
||||
request.onsuccess = function () {
|
||||
log(" Stop discovery - Success");
|
||||
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
|
||||
// Currently, discovering state wouldn't change immediately here.
|
||||
// We would turn on this check when the redesigned API are landed.
|
||||
// is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
|
||||
deferred.resolve();
|
||||
}
|
||||
request.onerror = function (aEvent) {
|
||||
ok(false, "Stop discovery - Fail");
|
||||
deferred.reject(aEvent.target.error);
|
||||
}
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start discovering Bluetooth devices and wait for 'devicefound' events.
|
||||
*
|
||||
* Allows the device's adapter to start seeking for remote devices and wait for
|
||||
* the 'devicefound' events of specified devices.
|
||||
*
|
||||
* Fulfill params: an array of addresses of found devices.
|
||||
*
|
||||
* @param aAdapter
|
||||
* A BluetoothAdapter which is used to interact with local BT device.
|
||||
* @param aRemoteAddresses
|
||||
* An array which contains addresses of remote devices.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function startDiscoveryAndWaitDevicesFound(aAdapter, aRemoteAddresses) {
|
||||
let promises = [];
|
||||
|
||||
promises.push(waitForDevicesFound(aAdapter, aRemoteAddresses));
|
||||
promises.push(startDiscovery(aAdapter));
|
||||
return Promise.all(promises)
|
||||
.then(aResults => aResults[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start pairing a remote device.
|
||||
*
|
||||
* Start pairing a remote device with the device's adapter.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: a DOMError
|
||||
*
|
||||
* @param aAdapter
|
||||
* A BluetoothAdapter which is used to interact with local BT device.
|
||||
* @param aDeviceAddress
|
||||
* The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function pair(aAdapter, aDeviceAddress) {
|
||||
let request = aAdapter.pair(aDeviceAddress);
|
||||
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then(function resolve() {
|
||||
log(" Pair - Success");
|
||||
}, function reject(aEvent) {
|
||||
ok(false, "Pair - Fail");
|
||||
throw aEvent.target.error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the paired device from the paired device list.
|
||||
*
|
||||
* Remove the paired device from the paired device list of the device's adapter.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: a DOMError
|
||||
*
|
||||
* @param aAdapter
|
||||
* A BluetoothAdapter which is used to interact with local BT device.
|
||||
* @param aDeviceAddress
|
||||
* The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function unpair(aAdapter, aDeviceAddress) {
|
||||
let request = aAdapter.unpair(aDeviceAddress);
|
||||
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then(function resolve() {
|
||||
log(" Unpair - Success");
|
||||
}, function reject(aEvent) {
|
||||
ok(false, "Unpair - Fail");
|
||||
throw aEvent.target.error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start pairing a remote device and wait for "pairedstatuschanged" event.
|
||||
*
|
||||
* Start pairing a remote device with the device's adapter and wait for
|
||||
* "pairedstatuschanged" event.
|
||||
*
|
||||
* Fulfill params: an array of promise results contains the fulfilled params of
|
||||
* 'waitForAdapterEvent' and 'pair'.
|
||||
*
|
||||
* @param aAdapter
|
||||
* A BluetoothAdapter which is used to interact with local BT device.
|
||||
* @param aDeviceAddress
|
||||
* The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function pairDeviceAndWait(aAdapter, aDeviceAddress) {
|
||||
let promises = [];
|
||||
promises.push(waitForAdapterEvent(aAdapter, "pairedstatuschanged"));
|
||||
promises.push(pair(aAdapter, aDeviceAddress));
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get paried Bluetooth devices.
|
||||
*
|
||||
* The getPairedDevices method is used to retrieve the full list of all devices
|
||||
* paired with the device's adapter.
|
||||
*
|
||||
* Fulfill params: a shallow copy of the Array of paired BluetoothDevice
|
||||
* objects.
|
||||
* Reject params: a DOMError
|
||||
*
|
||||
* @param aAdapter
|
||||
* A BluetoothAdapter which is used to interact with local BT device.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function getPairedDevices(aAdapter) {
|
||||
let request = aAdapter.getPairedDevices();
|
||||
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then(function resolve() {
|
||||
log(" getPairedDevices - Success");
|
||||
let pairedDevices = request.result.slice();
|
||||
return pairedDevices;
|
||||
}, function reject(aEvent) {
|
||||
ok(false, "getPairedDevices - Fail");
|
||||
throw aEvent.target.error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mozSettings value specified by @aKey.
|
||||
*
|
||||
@ -460,16 +274,19 @@ function getPairedDevices(aAdapter) {
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function getSettings(aKey) {
|
||||
let request = navigator.mozSettings.createLock().get(aKey);
|
||||
let deferred = Promise.defer();
|
||||
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then(function resolve(aEvent) {
|
||||
ok(true, "getSettings(" + aKey + ")");
|
||||
return aEvent.target.result[aKey];
|
||||
}, function reject(aEvent) {
|
||||
ok(false, "getSettings(" + aKey + ")");
|
||||
throw aEvent.target.error;
|
||||
});
|
||||
let request = navigator.mozSettings.createLock().get(aKey);
|
||||
request.addEventListener("success", function(aEvent) {
|
||||
ok(true, "getSettings(" + aKey + ")");
|
||||
deferred.resolve(aEvent.target.result[aKey]);
|
||||
});
|
||||
request.addEventListener("error", function() {
|
||||
ok(false, "getSettings(" + aKey + ")");
|
||||
deferred.reject();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -486,15 +303,19 @@ function getSettings(aKey) {
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function setSettings(aSettings) {
|
||||
let request = navigator.mozSettings.createLock().set(aSettings);
|
||||
let deferred = Promise.defer();
|
||||
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then(function resolve() {
|
||||
ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
|
||||
}, function reject(aEvent) {
|
||||
ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
|
||||
throw aEvent.target.error;
|
||||
});
|
||||
let request = navigator.mozSettings.createLock().set(aSettings);
|
||||
request.addEventListener("success", function() {
|
||||
ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
|
||||
deferred.resolve();
|
||||
});
|
||||
request.addEventListener("error", function() {
|
||||
ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
|
||||
deferred.reject();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -617,7 +438,7 @@ function waitForManagerEvent(aEventName) {
|
||||
* Fulfill params: the DOMEvent passed.
|
||||
*
|
||||
* @param aAdapter
|
||||
* A BluetoothAdapter which is used to interact with local BT device.
|
||||
* The BluetoothAdapter you want to use.
|
||||
* @param aEventName
|
||||
* The name of the EventHandler.
|
||||
*
|
||||
@ -642,8 +463,7 @@ function waitForAdapterEvent(aAdapter, aEventName) {
|
||||
*
|
||||
* Resolve if that named event occurs. Reject if we can't set settings.
|
||||
*
|
||||
* Fulfill params: an array of promise results contains the fulfill params of
|
||||
* 'waitForManagerEvent' and 'setBluetoothEnabled'.
|
||||
* Fulfill params: the DOMEvent passed.
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @return A deferred promise.
|
||||
|
@ -3,10 +3,8 @@ b2g = true
|
||||
browser = false
|
||||
qemu = true
|
||||
|
||||
[test_navigate_to_default_url.py]
|
||||
[test_dom_BluetoothManager_enabled.js]
|
||||
[test_dom_BluetoothManager_adapteradded.js]
|
||||
[test_dom_BluetoothAdapter_setters.js]
|
||||
[test_dom_BluetoothAdapter_getters.js]
|
||||
[test_dom_BluetoothAdapter_discovery.js]
|
||||
[test_dom_BluetoothAdapter_pair.js]
|
||||
|
@ -1,11 +1,13 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 et filetype=javascript
|
||||
* 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 Purpose:
|
||||
// To verify that discovery process of BluetoothAdapter is correct.
|
||||
// Use B2G emulator commands to add/remove remote devices to simulate
|
||||
// Use B2G emulator commands to add/remote remote devices to simulate
|
||||
// discovering behavior.
|
||||
//
|
||||
// Test Coverage:
|
||||
@ -22,9 +24,15 @@ MARIONETTE_HEAD_JS = 'head.js';
|
||||
startBluetoothTest(true, function testCaseMain(aAdapter) {
|
||||
log("Testing the discovery process of BluetoothAdapter ...");
|
||||
|
||||
// The properties of remote device.
|
||||
let theProperties = {
|
||||
"name": REMOTE_DEVICE_NAME,
|
||||
"discoverable": true
|
||||
};
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => removeEmulatorRemoteDevice(BDADDR_ALL))
|
||||
.then(() => addEmulatorRemoteDevice(null))
|
||||
.then(() => addEmulatorRemoteDevice(/*theProperties*/ null))
|
||||
.then(function(aRemoteAddress) {
|
||||
let promises = [];
|
||||
promises.push(waitForAdapterEvent(aAdapter, "devicefound"));
|
||||
|
@ -1,4 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 et filetype=javascript
|
||||
* 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/. */
|
||||
|
||||
|
@ -1,66 +0,0 @@
|
||||
/* 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 Purpose:
|
||||
// To verify that pairing process of BluetoothAdapter is correct.
|
||||
// Use B2G emulator commands to add/remove remote devices to simulate
|
||||
// discovering behavior. With current emulator implemation, the pair method
|
||||
// between adapter and remote device would be 'confirmation'.
|
||||
//
|
||||
// Test Coverage:
|
||||
// - BluetoothAdapter.startDiscovery()
|
||||
// - BluetoothAdapter.stopDiscovery()
|
||||
// - BluetoothAdapter.pair()
|
||||
// - BluetoothAdapter.unpair()
|
||||
// - BluetoothAdapter.onpairedstatuschanged()
|
||||
// - BluetoothAdapter.setPairingConfirmation()
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
function replyPairingReq(aAdapter, aPairingEvent) {
|
||||
switch (aPairingEvent.method) {
|
||||
case 'confirmation':
|
||||
log("The pairing passkey is " + aPairingEvent.passkey);
|
||||
aAdapter.setPairingConfirmation(aPairingEvent.address, true);
|
||||
break;
|
||||
case 'pincode':
|
||||
let pincode = BT_PAIRING_PINCODE;
|
||||
aAdapter.setPinCode(aPairingEvent.address, pincode);
|
||||
break;
|
||||
case 'passkey':
|
||||
let passkey = BT_PAIRING_PASSKEY;
|
||||
aAdapter.setPasskey(aPairingEvent.address, passkey);
|
||||
break;
|
||||
default:
|
||||
ok(false, "Unsupported pairing method. [" + aPairingEvent.method + "]");
|
||||
}
|
||||
}
|
||||
|
||||
startBluetoothTest(true, function testCaseMain(aAdapter) {
|
||||
log("Testing the pairing process of BluetoothAdapter ...");
|
||||
|
||||
// listens to the system message BT_PAIRING_REQ
|
||||
navigator.mozSetMessageHandler(BT_PAIRING_REQ,
|
||||
(evt) => replyPairingReq(aAdapter, evt));
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => removeEmulatorRemoteDevice(BDADDR_ALL))
|
||||
.then(() => addEmulatorRemoteDevice())
|
||||
.then((aRemoteAddress) =>
|
||||
startDiscoveryAndWaitDevicesFound(aAdapter, [aRemoteAddress]))
|
||||
.then((aRemoteAddresses) =>
|
||||
stopDiscovery(aAdapter).then(() => aRemoteAddresses))
|
||||
// 'aRemoteAddresses' is an arrary which contains addresses of discovered
|
||||
// remote devices.
|
||||
// Pairs with the first device in 'aRemoteAddresses' to test the functionality
|
||||
// of BluetoothAdapter.pair and BluetoothAdapter.onpairedstatuschanged.
|
||||
.then((aRemoteAddresses) => pairDeviceAndWait(aAdapter, aRemoteAddresses.pop()))
|
||||
.then(() => getPairedDevices(aAdapter))
|
||||
.then((aPairedDevices) => unpair(aAdapter, aPairedDevices.pop().address))
|
||||
.then(() => removeEmulatorRemoteDevice(BDADDR_ALL));
|
||||
});
|
@ -1,4 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 et filetype=javascript
|
||||
* 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/. */
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 et filetype=javascript
|
||||
* 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/. */
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 et filetype=javascript
|
||||
* 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/. */
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
# 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/.
|
||||
|
||||
from marionette_test import MarionetteTestCase
|
||||
|
||||
class testNavigateToDefault(MarionetteTestCase):
|
||||
def test_navigate_to_default_url(self):
|
||||
try:
|
||||
self.marionette.navigate("app://system.gaiamobile.org/index.html")
|
||||
except:
|
||||
self.assertTrue(Flase, "Can not navigate to system app.")
|
@ -505,17 +505,10 @@ nsDOMCameraControl::GetFocusDistanceFar(ErrorResult& aRv)
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraControl::SetExposureCompensation(const Optional<double>& aCompensation, ErrorResult& aRv)
|
||||
nsDOMCameraControl::SetExposureCompensation(double aCompensation, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(mCameraControl);
|
||||
|
||||
if (!aCompensation.WasPassed()) {
|
||||
// use NaN to switch the camera back into auto mode
|
||||
aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, aCompensation.Value());
|
||||
aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, aCompensation);
|
||||
}
|
||||
|
||||
double
|
||||
|
@ -76,7 +76,7 @@ public:
|
||||
double GetFocusDistanceNear(ErrorResult& aRv);
|
||||
double GetFocusDistanceOptimum(ErrorResult& aRv);
|
||||
double GetFocusDistanceFar(ErrorResult& aRv);
|
||||
void SetExposureCompensation(const dom::Optional<double>& aCompensation, ErrorResult& aRv);
|
||||
void SetExposureCompensation(double aCompensation, ErrorResult& aRv);
|
||||
double GetExposureCompensation(ErrorResult& aRv);
|
||||
int32_t SensorAngle();
|
||||
already_AddRefed<dom::CameraCapabilities> Capabilities();
|
||||
|
@ -72,10 +72,11 @@ nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId)
|
||||
, mDeferConfigUpdate(0)
|
||||
, mMediaProfiles(nullptr)
|
||||
, mRecorder(nullptr)
|
||||
, mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
|
||||
, mProfileManager(nullptr)
|
||||
, mRecorderProfile(nullptr)
|
||||
, mVideoFile(nullptr)
|
||||
, mReentrantMonitor("GonkCameraControl::OnTakePictureMonitor")
|
||||
, mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor")
|
||||
{
|
||||
// Constructor runs on the main thread...
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
@ -952,6 +953,8 @@ nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescri
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mRecorderMonitor);
|
||||
|
||||
NS_ENSURE_TRUE(mRecorderProfile, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE);
|
||||
|
||||
@ -1041,7 +1044,7 @@ nsGonkCameraControl::StopRecordingImpl()
|
||||
nsRefPtr<DeviceStorageFile> mFile;
|
||||
};
|
||||
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
ReentrantMonitorAutoEnter mon(mRecorderMonitor);
|
||||
|
||||
// nothing to do if we have no mRecorder
|
||||
NS_ENSURE_TRUE(mRecorder, NS_OK);
|
||||
@ -1570,6 +1573,8 @@ nsGonkCameraControl::SetupRecording(int aFd, int aRotation,
|
||||
const size_t SIZE = 256;
|
||||
char buffer[SIZE];
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mRecorderMonitor);
|
||||
|
||||
mRecorder = new GonkRecorder();
|
||||
CHECK_SETARG_RETURN(mRecorder->init(), NS_ERROR_FAILURE);
|
||||
|
||||
|
@ -155,6 +155,10 @@ protected:
|
||||
|
||||
android::MediaProfiles* mMediaProfiles;
|
||||
nsRefPtr<android::GonkRecorder> mRecorder;
|
||||
// Touching mRecorder happens inside this monitor because the destructor
|
||||
// can run on any thread, and we need to be able to clean up properly if
|
||||
// GonkCameraControl goes away.
|
||||
ReentrantMonitor mRecorderMonitor;
|
||||
|
||||
// Camcorder profile settings for the desired quality level
|
||||
nsRefPtr<GonkRecorderProfileManager> mProfileManager;
|
||||
|
@ -228,43 +228,43 @@ var tests = [
|
||||
"exposureCompensation = " + cam.exposureCompensation);
|
||||
|
||||
// Check normal values
|
||||
cam.setExposureCompensation(0.0);
|
||||
cam.exposureCompensation = 0.0;
|
||||
ok(cam.exposureCompensation == 0.0,
|
||||
"exposureCompensation = " + cam.exposureCompensation);
|
||||
cam.setExposureCompensation(cap.minExposureCompensation);
|
||||
cam.exposureCompensation = cap.minExposureCompensation;
|
||||
ok(cam.exposureCompensation == cap.minExposureCompensation,
|
||||
"exposureCompensation(min) = " + cam.exposureCompensation);
|
||||
cam.setExposureCompensation(cap.maxExposureCompensation);
|
||||
cam.exposureCompensation = cap.maxExposureCompensation;
|
||||
ok(cam.exposureCompensation == cap.maxExposureCompensation,
|
||||
"exposureCompensation(max) = " + cam.exposureCompensation);
|
||||
|
||||
// Rounding
|
||||
cam.setExposureCompensation(1.24);
|
||||
cam.exposureCompensation = 1.24;
|
||||
ok(cam.exposureCompensation == 1.0,
|
||||
"exposureCompensation(1.24) = " + cam.exposureCompensation);
|
||||
cam.setExposureCompensation(1.25);
|
||||
cam.exposureCompensation = 1.25;
|
||||
ok(cam.exposureCompensation == 1.5,
|
||||
"exposureCompensation(1.25) = " + cam.exposureCompensation);
|
||||
cam.setExposureCompensation(-1.24);
|
||||
cam.exposureCompensation = -1.24;
|
||||
ok(cam.exposureCompensation == -1.0,
|
||||
"exposureCompensation(-1.24) = " + cam.exposureCompensation);
|
||||
cam.setExposureCompensation(-1.25);
|
||||
cam.exposureCompensation = -1.25;
|
||||
ok(cam.exposureCompensation == -1.5,
|
||||
"exposureCompensation(-1.25) = " + cam.exposureCompensation);
|
||||
|
||||
// Check out-of-bounds values
|
||||
cam.setExposureCompensation(cap.minExposureCompensation - 1.0);
|
||||
cam.exposureCompensation = cap.minExposureCompensation - 1.0;
|
||||
ok(cam.exposureCompensation == cap.minExposureCompensation,
|
||||
"exposureCompensation(min - 1.0) = " + cam.exposureCompensation);
|
||||
cam.setExposureCompensation(cap.maxExposureCompensation + 1.0);
|
||||
cam.exposureCompensation = cap.maxExposureCompensation + 1.0;
|
||||
ok(cam.exposureCompensation == cap.maxExposureCompensation,
|
||||
"exposureCompensation(max + 1.0) = " + cam.exposureCompensation);
|
||||
|
||||
// Check extreme values
|
||||
cam.setExposureCompensation(-1 * Math.pow(2, 32));
|
||||
cam.exposureCompensation = -1 * Math.pow(2, 32);
|
||||
ok(cam.exposureCompensation == cap.minExposureCompensation,
|
||||
"exposureCompensation(-2^32) = " + cam.exposureCompensation);
|
||||
cam.setExposureCompensation(Math.pow(2, 32));
|
||||
cam.exposureCompensation = Math.pow(2, 32);
|
||||
ok(cam.exposureCompensation == cap.maxExposureCompensation,
|
||||
"exposureCompensation(2^32) = " + cam.exposureCompensation);
|
||||
|
||||
|
@ -7,7 +7,8 @@ MARIONETTE_HEAD_JS = "head.js";
|
||||
let url = "http://www.mozilla.org";
|
||||
|
||||
// TODO : Get this from emulator console command.
|
||||
const T2T_RE_INDEX = 2;
|
||||
const T1T_RE_INDEX = 2;
|
||||
const T2T_RE_INDEX = 3;
|
||||
|
||||
function activateRE(re) {
|
||||
let deferred = Promise.defer();
|
||||
@ -35,7 +36,7 @@ function setTagData(re, flag, tnf, type, payload) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testUrlTagDiscover() {
|
||||
function testUrlTagDiscover(re) {
|
||||
log("Running \'testUrlTagDiscover\'");
|
||||
// TODO : Make flag value readable.
|
||||
let flag = 0xd0;
|
||||
@ -60,12 +61,21 @@ function testUrlTagDiscover() {
|
||||
});
|
||||
|
||||
toggleNFC(true)
|
||||
.then(() => setTagData(T2T_RE_INDEX, flag, tnf, btoa(type), btoa(payload)))
|
||||
.then(() => activateRE(T2T_RE_INDEX));
|
||||
.then(() => setTagData(re, flag, tnf, btoa(type), btoa(payload)))
|
||||
.then(() => activateRE(re));
|
||||
}
|
||||
|
||||
function testUrlT1TDiscover() {
|
||||
testUrlTagDiscover(T1T_RE_INDEX);
|
||||
}
|
||||
|
||||
function testUrlT2TDiscover() {
|
||||
testUrlTagDiscover(T2T_RE_INDEX);
|
||||
}
|
||||
|
||||
let tests = [
|
||||
testUrlTagDiscover
|
||||
testUrlT1TDiscover,
|
||||
testUrlT2TDiscover
|
||||
];
|
||||
|
||||
SpecialPowers.pushPermissions(
|
||||
|
@ -15,6 +15,7 @@ const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
@ -23,15 +24,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageListenerManager");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gEncoder", function() {
|
||||
return new TextEncoder();
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gDecoder", function() {
|
||||
return new TextDecoder();
|
||||
});
|
||||
|
||||
|
||||
const NOTIFICATION_STORE_DIR = OS.Constants.Path.profileDir;
|
||||
const NOTIFICATION_STORE_PATH =
|
||||
OS.Path.join(NOTIFICATION_STORE_DIR, "notificationstore.json");
|
||||
@ -58,7 +50,7 @@ let NotificationDB = {
|
||||
this.loaded = false;
|
||||
|
||||
this.tasks = []; // read/write operation queue
|
||||
this.runningTask = false;
|
||||
this.runningTask = null;
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
this.registerListeners();
|
||||
@ -86,15 +78,13 @@ let NotificationDB = {
|
||||
},
|
||||
|
||||
// Attempt to read notification file, if it's not there we will create it.
|
||||
load: function(callback) {
|
||||
var promise = OS.File.read(NOTIFICATION_STORE_PATH);
|
||||
promise.then(
|
||||
load: function() {
|
||||
var promise = OS.File.read(NOTIFICATION_STORE_PATH, { encoding: "utf-8"});
|
||||
return promise.then(
|
||||
function onSuccess(data) {
|
||||
try {
|
||||
this.notifications = JSON.parse(gDecoder.decode(data));
|
||||
} catch (e) {
|
||||
if (DEBUG) { debug("Unable to parse file data " + e); }
|
||||
}
|
||||
// JSON parsing failure will get handled by a later catch in the promise
|
||||
// chain
|
||||
this.notifications = JSON.parse(data);
|
||||
// populate the list of notifications by tag
|
||||
if (this.notifications) {
|
||||
for (var origin in this.notifications) {
|
||||
@ -108,70 +98,43 @@ let NotificationDB = {
|
||||
}
|
||||
}
|
||||
this.loaded = true;
|
||||
callback && callback();
|
||||
}.bind(this),
|
||||
|
||||
// If read failed, we assume we have no notifications to load.
|
||||
function onFailure(reason) {
|
||||
this.loaded = true;
|
||||
this.createStore(callback);
|
||||
return this.createStore();
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
// Creates the notification directory.
|
||||
createStore: function(callback) {
|
||||
createStore: function() {
|
||||
var promise = OS.File.makeDir(NOTIFICATION_STORE_DIR, {
|
||||
ignoreExisting: true
|
||||
});
|
||||
promise.then(
|
||||
function onSuccess() {
|
||||
this.createFile(callback);
|
||||
}.bind(this),
|
||||
|
||||
function onFailure(reason) {
|
||||
if (DEBUG) { debug("Directory creation failed:" + reason); }
|
||||
callback && callback();
|
||||
}
|
||||
return promise.then(
|
||||
this.createFile.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
// Creates the notification file once the directory is created.
|
||||
createFile: function(callback) {
|
||||
var promise = OS.File.open(NOTIFICATION_STORE_PATH, {create: true});
|
||||
promise.then(
|
||||
function onSuccess(handle) {
|
||||
handle.close();
|
||||
callback && callback();
|
||||
},
|
||||
function onFailure(reason) {
|
||||
if (DEBUG) { debug("File creation failed:" + reason); }
|
||||
callback && callback();
|
||||
}
|
||||
);
|
||||
createFile: function() {
|
||||
return OS.File.writeAtomic(NOTIFICATION_STORE_PATH, "");
|
||||
},
|
||||
|
||||
// Save current notifications to the file.
|
||||
save: function(callback) {
|
||||
var data = gEncoder.encode(JSON.stringify(this.notifications));
|
||||
var promise = OS.File.writeAtomic(NOTIFICATION_STORE_PATH, data);
|
||||
promise.then(
|
||||
function onSuccess() {
|
||||
callback && callback();
|
||||
},
|
||||
function onFailure(reason) {
|
||||
if (DEBUG) { debug("Save failed:" + reason); }
|
||||
callback && callback();
|
||||
}
|
||||
);
|
||||
save: function() {
|
||||
var data = JSON.stringify(this.notifications);
|
||||
return OS.File.writeAtomic(NOTIFICATION_STORE_PATH, data, { encoding: "utf-8"});
|
||||
},
|
||||
|
||||
// Helper function: callback will be called once file exists and/or is loaded.
|
||||
ensureLoaded: function(callback) {
|
||||
// Helper function: promise will be resolved once file exists and/or is loaded.
|
||||
ensureLoaded: function() {
|
||||
if (!this.loaded) {
|
||||
this.load(callback);
|
||||
return this.load();
|
||||
} else {
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
@ -190,37 +153,57 @@ let NotificationDB = {
|
||||
|
||||
switch (message.name) {
|
||||
case "Notification:GetAll":
|
||||
this.queueTask("getall", message.data, function(notifications) {
|
||||
this.queueTask("getall", message.data).then(function(notifications) {
|
||||
returnMessage("Notification:GetAll:Return:OK", {
|
||||
requestID: message.data.requestID,
|
||||
origin: message.data.origin,
|
||||
notifications: notifications
|
||||
});
|
||||
}).catch(function(error) {
|
||||
returnMessage("Notification:GetAll:Return:KO", {
|
||||
requestID: message.data.requestID,
|
||||
origin: message.data.origin,
|
||||
errorMsg: error
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case "Notification:GetAllCrossOrigin":
|
||||
this.queueTask("getallaccrossorigin", message.data,
|
||||
this.queueTask("getallaccrossorigin", message.data).then(
|
||||
function(notifications) {
|
||||
returnMessage("Notification:GetAllCrossOrigin:Return:OK", {
|
||||
notifications: notifications
|
||||
});
|
||||
}).catch(function(error) {
|
||||
returnMessage("Notification:GetAllCrossOrigin:Return:KO", {
|
||||
errorMsg: error
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case "Notification:Save":
|
||||
this.queueTask("save", message.data, function() {
|
||||
this.queueTask("save", message.data).then(function() {
|
||||
returnMessage("Notification:Save:Return:OK", {
|
||||
requestID: message.data.requestID
|
||||
});
|
||||
}).catch(function(error) {
|
||||
returnMessage("Notification:Save:Return:KO", {
|
||||
requestID: message.data.requestID,
|
||||
errorMsg: error
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case "Notification:Delete":
|
||||
this.queueTask("delete", message.data, function() {
|
||||
this.queueTask("delete", message.data).then(function() {
|
||||
returnMessage("Notification:Delete:Return:OK", {
|
||||
requestID: message.data.requestID
|
||||
});
|
||||
}).catch(function(error) {
|
||||
returnMessage("Notification:Delete:Return:KO", {
|
||||
requestID: message.data.requestID,
|
||||
errorMsg: error
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
@ -231,12 +214,20 @@ let NotificationDB = {
|
||||
|
||||
// We need to make sure any read/write operations are atomic,
|
||||
// so use a queue to run each operation sequentially.
|
||||
queueTask: function(operation, data, callback) {
|
||||
queueTask: function(operation, data) {
|
||||
if (DEBUG) { debug("Queueing task: " + operation); }
|
||||
|
||||
var defer = {};
|
||||
|
||||
this.tasks.push({
|
||||
operation: operation,
|
||||
data: data,
|
||||
callback: callback
|
||||
defer: defer
|
||||
});
|
||||
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
defer.resolve = resolve;
|
||||
defer.reject = reject;
|
||||
});
|
||||
|
||||
// Only run immediately if we aren't currently running another task.
|
||||
@ -244,49 +235,60 @@ let NotificationDB = {
|
||||
if (DEBUG) { debug("Task queue was not running, starting now..."); }
|
||||
this.runNextTask();
|
||||
}
|
||||
|
||||
return promise;
|
||||
},
|
||||
|
||||
runNextTask: function() {
|
||||
if (this.tasks.length === 0) {
|
||||
if (DEBUG) { debug("No more tasks to run, queue depleted"); }
|
||||
this.runningTask = false;
|
||||
this.runningTask = null;
|
||||
return;
|
||||
}
|
||||
this.runningTask = true;
|
||||
this.runningTask = this.tasks.shift();
|
||||
|
||||
// Always make sure we are loaded before performing any read/write tasks.
|
||||
this.ensureLoaded(function() {
|
||||
var task = this.tasks.shift();
|
||||
|
||||
// Wrap the task callback to make sure we immediately
|
||||
// run the next task after running the original callback.
|
||||
var wrappedCallback = function() {
|
||||
if (DEBUG) { debug("Finishing task: " + task.operation); }
|
||||
task.callback.apply(this, arguments);
|
||||
this.runNextTask();
|
||||
}.bind(this);
|
||||
this.ensureLoaded()
|
||||
.then(function() {
|
||||
var task = this.runningTask;
|
||||
|
||||
switch (task.operation) {
|
||||
case "getall":
|
||||
this.taskGetAll(task.data, wrappedCallback);
|
||||
return this.taskGetAll(task.data);
|
||||
break;
|
||||
|
||||
case "getallaccrossorigin":
|
||||
this.taskGetAllCrossOrigin(wrappedCallback);
|
||||
return this.taskGetAllCrossOrigin();
|
||||
break;
|
||||
|
||||
case "save":
|
||||
this.taskSave(task.data, wrappedCallback);
|
||||
return this.taskSave(task.data);
|
||||
break;
|
||||
|
||||
case "delete":
|
||||
this.taskDelete(task.data, wrappedCallback);
|
||||
return this.taskDelete(task.data);
|
||||
break;
|
||||
}
|
||||
|
||||
}.bind(this))
|
||||
.then(function(payload) {
|
||||
if (DEBUG) {
|
||||
debug("Finishing task: " + this.runningTask.operation);
|
||||
}
|
||||
this.runningTask.defer.resolve(payload);
|
||||
}.bind(this))
|
||||
.catch(function(err) {
|
||||
if (DEBUG) {
|
||||
debug("Error while running " + this.runningTask.operation + ": " + err);
|
||||
}
|
||||
this.runningTask.defer.reject(new String(err));
|
||||
}.bind(this))
|
||||
.then(function() {
|
||||
this.runNextTask();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
taskGetAll: function(data, callback) {
|
||||
taskGetAll: function(data) {
|
||||
if (DEBUG) { debug("Task, getting all"); }
|
||||
var origin = data.origin;
|
||||
var notifications = [];
|
||||
@ -294,10 +296,10 @@ let NotificationDB = {
|
||||
for (var i in this.notifications[origin]) {
|
||||
notifications.push(this.notifications[origin][i]);
|
||||
}
|
||||
callback(notifications);
|
||||
return Promise.resolve(notifications);
|
||||
},
|
||||
|
||||
taskGetAllCrossOrigin: function(callback) {
|
||||
taskGetAllCrossOrigin: function() {
|
||||
if (DEBUG) { debug("Task, getting all whatever origin"); }
|
||||
var notifications = [];
|
||||
for (var origin in this.notifications) {
|
||||
@ -315,10 +317,10 @@ let NotificationDB = {
|
||||
notifications.push(notification);
|
||||
}
|
||||
}
|
||||
callback(notifications);
|
||||
return Promise.resolve(notifications);
|
||||
},
|
||||
|
||||
taskSave: function(data, callback) {
|
||||
taskSave: function(data) {
|
||||
if (DEBUG) { debug("Task, saving"); }
|
||||
var origin = data.origin;
|
||||
var notification = data.notification;
|
||||
@ -336,32 +338,30 @@ let NotificationDB = {
|
||||
}
|
||||
|
||||
this.notifications[origin][notification.id] = notification;
|
||||
this.save(callback);
|
||||
return this.save();
|
||||
},
|
||||
|
||||
taskDelete: function(data, callback) {
|
||||
taskDelete: function(data) {
|
||||
if (DEBUG) { debug("Task, deleting"); }
|
||||
var origin = data.origin;
|
||||
var id = data.id;
|
||||
if (!this.notifications[origin]) {
|
||||
if (DEBUG) { debug("No notifications found for origin: " + origin); }
|
||||
callback();
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Make sure we can find the notification to delete.
|
||||
var oldNotification = this.notifications[origin][id];
|
||||
if (!oldNotification) {
|
||||
if (DEBUG) { debug("No notification found with id: " + id); }
|
||||
callback();
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (oldNotification.tag) {
|
||||
delete this.byTag[origin][oldNotification.tag];
|
||||
}
|
||||
delete this.notifications[origin][id];
|
||||
this.save(callback);
|
||||
return this.save();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -16,10 +16,24 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const NOTIFICATIONSTORAGE_CID = "{37f819b0-0b5c-11e3-8ffd-0800200c9a66}";
|
||||
const NOTIFICATIONSTORAGE_CONTRACTID = "@mozilla.org/notificationStorage;1";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
const kMessageNotificationGetAllOk = "Notification:GetAll:Return:OK";
|
||||
const kMessageNotificationGetAllKo = "Notification:GetAll:Return:KO";
|
||||
const kMessageNotificationSaveKo = "Notification:Save:Return:KO";
|
||||
const kMessageNotificationDeleteKo = "Notification:Delete:Return:KO";
|
||||
|
||||
const kMessages = [
|
||||
kMessageNotificationGetAllOk,
|
||||
kMessageNotificationGetAllKo,
|
||||
kMessageNotificationSaveKo,
|
||||
kMessageNotificationDeleteKo
|
||||
];
|
||||
|
||||
function NotificationStorage() {
|
||||
// cache objects
|
||||
@ -30,12 +44,33 @@ function NotificationStorage() {
|
||||
this._requests = {};
|
||||
this._requestCount = 0;
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
// Register for message listeners.
|
||||
cpmm.addMessageListener("Notification:GetAll:Return:OK", this);
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
NotificationStorage.prototype = {
|
||||
|
||||
registerListeners: function() {
|
||||
for (let message of kMessages) {
|
||||
cpmm.addMessageListener(message, this);
|
||||
}
|
||||
},
|
||||
|
||||
unregisterListeners: function() {
|
||||
for (let message of kMessages) {
|
||||
cpmm.removeMessageListener(message, this);
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (DEBUG) debug("Topic: " + aTopic);
|
||||
if (aTopic == "xpcom-shutdown") {
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
this.unregisterListeners();
|
||||
}
|
||||
},
|
||||
|
||||
put: function(origin, id, title, dir, lang, body, tag, icon, alertName) {
|
||||
if (DEBUG) { debug("PUT: " + id + ": " + title); }
|
||||
var notification = {
|
||||
@ -99,14 +134,27 @@ NotificationStorage.prototype = {
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
var request = this._requests[message.data.requestID];
|
||||
|
||||
switch (message.name) {
|
||||
case "Notification:GetAll:Return:OK":
|
||||
var request = this._requests[message.data.requestID];
|
||||
case kMessageNotificationGetAllOk:
|
||||
delete this._requests[message.data.requestID];
|
||||
this._populateCache(message.data.notifications);
|
||||
this._fetchFromCache(request.origin, request.tag, request.callback);
|
||||
break;
|
||||
|
||||
case kMessageNotificationGetAllKo:
|
||||
delete this._requests[message.data.requestID];
|
||||
try {
|
||||
request.callback.done();
|
||||
} catch (e) {
|
||||
debug("Error calling callback done: " + e);
|
||||
}
|
||||
case kMessageNotificationSaveOk:
|
||||
case kMessageNotificationDeleteOk:
|
||||
debug("Error received when treating: '" + message.name + "': " + message.data.errorMsg);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (DEBUG) debug("Unrecognized message: " + message.name);
|
||||
break;
|
||||
|
@ -218,16 +218,13 @@ interface CameraControl : MediaStream
|
||||
[Throws]
|
||||
readonly attribute unrestricted double focusDistanceFar;
|
||||
|
||||
/* 'compensation' is optional, and if missing, will
|
||||
set the camera to use automatic exposure compensation.
|
||||
|
||||
acceptable values must range from minExposureCompensation
|
||||
to maxExposureCompensation in steps of stepExposureCompensation;
|
||||
invalid values will be rounded to the nearest valid value. */
|
||||
/* over- or under-expose the image; acceptable values must range from
|
||||
minExposureCompensation to maxExposureCompensation in steps of
|
||||
stepExposureCompensation. Invalid values will be rounded to the nearest
|
||||
valid value; out-of-bounds values will be limited to the range
|
||||
supported by the camera. */
|
||||
[Throws]
|
||||
void setExposureCompensation(optional double compensation);
|
||||
[Throws]
|
||||
readonly attribute unrestricted double exposureCompensation;
|
||||
attribute double exposureCompensation;
|
||||
|
||||
/* one of the values chosen from capabilities.isoModes; default
|
||||
value is "auto" if supported. */
|
||||
|
@ -1,15 +0,0 @@
|
||||
[DEFAULT]
|
||||
; true if the test requires an emulator, otherwise false
|
||||
qemu = false
|
||||
|
||||
; true if the test is compatible with the browser, otherwise false
|
||||
browser = true
|
||||
|
||||
; true if the test is compatible with b2g, otherwise false
|
||||
b2g = true
|
||||
|
||||
; true if the test should be skipped
|
||||
skip = false
|
||||
|
||||
[test_touchcaret.py]
|
||||
b2g = false ; Bug 1020261
|
@ -1,307 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import string
|
||||
|
||||
from by import By
|
||||
from marionette import Actions
|
||||
from marionette_test import MarionetteTestCase
|
||||
|
||||
|
||||
class TouchCaretTest(MarionetteTestCase):
|
||||
_input_selector = (By.ID, 'input')
|
||||
_textarea_selector = (By.ID, 'textarea')
|
||||
_contenteditable_selector = (By.ID, 'contenteditable')
|
||||
|
||||
def setUp(self):
|
||||
# Code to execute before a tests are run.
|
||||
MarionetteTestCase.setUp(self)
|
||||
self.actions = Actions(self.marionette)
|
||||
|
||||
def openTestHtml(self, enabled=True):
|
||||
'''Open html for testing and locate elements, and enable/disable touch
|
||||
caret.'''
|
||||
self.marionette.execute_script(
|
||||
'SpecialPowers.setBoolPref("touchcaret.enabled", %s);' %
|
||||
('true' if enabled else 'false'))
|
||||
|
||||
test_html = self.marionette.absolute_url('test_touchcaret.html')
|
||||
self.marionette.navigate(test_html)
|
||||
|
||||
self._input = self.marionette.find_element(*self._input_selector)
|
||||
self._textarea = self.marionette.find_element(*self._textarea_selector)
|
||||
self._contenteditable = self.marionette.find_element(*self._contenteditable_selector)
|
||||
|
||||
def is_input_or_textarea(self, element):
|
||||
'''Return True if element is either <input> or <textarea>'''
|
||||
return element.tag_name in ('input', 'textarea')
|
||||
|
||||
def get_js_selection_cmd(self, element):
|
||||
'''Return a command snippet to get selection object.
|
||||
|
||||
If the element is <input> or <textarea>, return the selection object
|
||||
associated with it. Otherwise, return the current selection object.
|
||||
|
||||
Note: "element" must be provided as the first argument to
|
||||
execute_script().
|
||||
|
||||
'''
|
||||
if self.is_input_or_textarea(element):
|
||||
# We must unwrap sel so that DOMRect could be returned to Python
|
||||
# side.
|
||||
return '''var sel = SpecialPowers.wrap(arguments[0]).editor.selection;
|
||||
sel = SpecialPowers.unwrap(sel);'''
|
||||
else:
|
||||
return '''var sel = window.getSelection();'''
|
||||
|
||||
def caret_rect(self, element):
|
||||
'''Return the caret's DOMRect object.
|
||||
|
||||
If the element is either <input> or <textarea>, return the caret's
|
||||
DOMRect within the element. Otherwise, return the DOMRect of the
|
||||
current selected caret.
|
||||
|
||||
'''
|
||||
cmd = self.get_js_selection_cmd(element) +\
|
||||
'''return sel.getRangeAt(0).getClientRects()[0];'''
|
||||
return self.marionette.execute_script(cmd, script_args=[element])
|
||||
|
||||
def caret_location(self, element):
|
||||
'''Return caret's center location by the number of characters offset
|
||||
within the given element.
|
||||
|
||||
Return (x, y) coordinates of the caret's center by the number of
|
||||
characters offset relative to the top left-hand corner of the given
|
||||
element.
|
||||
|
||||
'''
|
||||
rect = self.caret_rect(element)
|
||||
x = rect['left'] + rect['width'] / 2.0 - element.location['x']
|
||||
y = rect['top'] + rect['height'] / 2.0 - element.location['y']
|
||||
return x, y
|
||||
|
||||
def touch_caret_location(self, element):
|
||||
'''Return touch caret's location (based on current caret location).
|
||||
|
||||
Return (x, y) coordinates of the touch caret's tip relative to the top
|
||||
left-hand corner of the given element.
|
||||
|
||||
'''
|
||||
rect = self.caret_rect(element)
|
||||
x = rect['left'] - element.location['x']
|
||||
|
||||
# Touch caret's tip is below the bottom of the caret. Add 5px to y
|
||||
# should be sufficient to locate it.
|
||||
y = rect['bottom'] + 5 - element.location['y']
|
||||
|
||||
return x, y
|
||||
|
||||
def move_caret_by_offset(self, element, offset, backward=False):
|
||||
'''Move caret in the element by offset.'''
|
||||
cmd = self.get_js_selection_cmd(element) +\
|
||||
'''sel.modify("move", arguments[1], "character");'''
|
||||
direction = 'backward' if backward else 'forward'
|
||||
|
||||
for i in range(offset):
|
||||
self.marionette.execute_script(
|
||||
cmd, script_args=[element, direction])
|
||||
|
||||
def move_caret_to_front(self, element):
|
||||
if self.is_input_or_textarea(element):
|
||||
cmd = '''arguments[0].setSelectionRange(0, 0);'''
|
||||
else:
|
||||
cmd = '''var sel = window.getSelection();
|
||||
sel.collapse(arguments[0].firstChild, 0);'''
|
||||
|
||||
self.marionette.execute_script(cmd, script_args=[element])
|
||||
|
||||
def move_caret_to_end(self, element):
|
||||
if self.is_input_or_textarea(element):
|
||||
cmd = '''var len = arguments[0].value.length;
|
||||
arguments[0].setSelectionRange(len, len);'''
|
||||
else:
|
||||
cmd = '''var sel = window.getSelection();
|
||||
sel.collapse(arguments[0].lastChild, arguments[0].lastChild.length);'''
|
||||
|
||||
self.marionette.execute_script(cmd, script_args=[element])
|
||||
|
||||
def get_content(self, element):
|
||||
'''Return the content of the element.'''
|
||||
if self.is_input_or_textarea(element):
|
||||
return element.get_attribute('value')
|
||||
else:
|
||||
return element.text
|
||||
|
||||
def _test_move_caret_to_the_right_by_one_character(self, el, assertFunc):
|
||||
content_to_add = '!'
|
||||
target_content = self.get_content(el)
|
||||
target_content = target_content[:1] + content_to_add + target_content[1:]
|
||||
|
||||
# Get touch caret (x, y) at position 1 and 2.
|
||||
self.move_caret_to_front(el)
|
||||
caret0_x, caret0_y = self.caret_location(el)
|
||||
touch_caret0_x, touch_caret0_y = self.touch_caret_location(el)
|
||||
self.move_caret_by_offset(el, 1)
|
||||
touch_caret1_x, touch_caret1_y = self.touch_caret_location(el)
|
||||
|
||||
# Tap the front of the input to make touch caret appear.
|
||||
el.tap(caret0_x, caret0_y)
|
||||
|
||||
# Move touch caret
|
||||
self.actions.flick(el, touch_caret0_x, touch_caret0_y,
|
||||
touch_caret1_x, touch_caret1_y).perform()
|
||||
|
||||
el.send_keys(content_to_add)
|
||||
assertFunc(target_content, self.get_content(el))
|
||||
|
||||
def _test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self, el, assertFunc):
|
||||
content_to_add = '!'
|
||||
target_content = self.get_content(el) + content_to_add
|
||||
|
||||
# Tap the front of the input to make touch caret appear.
|
||||
self.move_caret_to_front(el)
|
||||
el.tap(*self.caret_location(el))
|
||||
|
||||
# Move touch caret to the bottom-right corner of the element.
|
||||
src_x, src_y = self.touch_caret_location(el)
|
||||
dest_x, dest_y = el.size['width'], el.size['height']
|
||||
self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform()
|
||||
|
||||
el.send_keys(content_to_add)
|
||||
assertFunc(target_content, self.get_content(el))
|
||||
|
||||
def _test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self, el, assertFunc):
|
||||
content_to_add = '!'
|
||||
target_content = content_to_add + self.get_content(el)
|
||||
|
||||
# Tap to make touch caret appear. Note: it's strange that when the caret
|
||||
# is at the end, the rect of the caret in <textarea> cannot be obtained.
|
||||
# A bug perhaps.
|
||||
self.move_caret_to_end(el)
|
||||
self.move_caret_by_offset(el, 1, backward=True)
|
||||
el.tap(*self.caret_location(el))
|
||||
|
||||
# Move touch caret to the top-left corner of the input box.
|
||||
src_x, src_y = self.touch_caret_location(el)
|
||||
dest_x, dest_y = 0, 0
|
||||
self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform()
|
||||
|
||||
el.send_keys(content_to_add)
|
||||
assertFunc(target_content, self.get_content(el))
|
||||
|
||||
def _test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self, el, assertFunc):
|
||||
content_to_add = '!'
|
||||
non_target_content = content_to_add + self.get_content(el)
|
||||
|
||||
# Get touch caret timeout in millisecond, and convert it to second.
|
||||
timeout = self.marionette.execute_script(
|
||||
'return SpecialPowers.getIntPref("touchcaret.expiration.time");')
|
||||
timeout /= 1000.0
|
||||
|
||||
# Tap to make touch caret appear. Note: it's strange that when the caret
|
||||
# is at the end, the rect of the caret in <textarea> cannot be obtained.
|
||||
# A bug perhaps.
|
||||
self.move_caret_to_end(el)
|
||||
self.move_caret_by_offset(el, 1, backward=True)
|
||||
el.tap(*self.caret_location(el))
|
||||
|
||||
# Wait until touch caret disappears, then pretend to move it to the
|
||||
# top-left corner of the input box.
|
||||
src_x, src_y = self.touch_caret_location(el)
|
||||
dest_x, dest_y = 0, 0
|
||||
self.actions.wait(timeout).flick(el, src_x, src_y, dest_x, dest_y).perform()
|
||||
|
||||
el.send_keys(content_to_add)
|
||||
assertFunc(non_target_content, self.get_content(el))
|
||||
|
||||
########################################################################
|
||||
# <input> test cases with touch caret enabled
|
||||
########################################################################
|
||||
def test_input_move_caret_to_the_right_by_one_character(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_move_caret_to_the_right_by_one_character(self._input, self.assertEqual)
|
||||
|
||||
def test_input_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._input, self.assertEqual)
|
||||
|
||||
def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._input, self.assertEqual)
|
||||
|
||||
def test_input_touch_caret_timeout(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._input, self.assertNotEqual)
|
||||
|
||||
########################################################################
|
||||
# <input> test cases with touch caret disabled
|
||||
########################################################################
|
||||
def test_input_move_caret_to_the_right_by_one_character_disabled(self):
|
||||
self.openTestHtml(enabled=False)
|
||||
self._test_move_caret_to_the_right_by_one_character(self._input, self.assertNotEqual)
|
||||
|
||||
def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self):
|
||||
self.openTestHtml(enabled=False)
|
||||
self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._input, self.assertNotEqual)
|
||||
|
||||
########################################################################
|
||||
# <textarea> test cases with touch caret enabled
|
||||
########################################################################
|
||||
def test_textarea_move_caret_to_the_right_by_one_character(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertEqual)
|
||||
|
||||
def test_textarea_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._textarea, self.assertEqual)
|
||||
|
||||
def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._textarea, self.assertEqual)
|
||||
|
||||
def test_textarea_touch_caret_timeout(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._textarea, self.assertNotEqual)
|
||||
|
||||
########################################################################
|
||||
# <textarea> test cases with touch caret disabled
|
||||
########################################################################
|
||||
def test_textarea_move_caret_to_the_right_by_one_character_disabled(self):
|
||||
self.openTestHtml(enabled=False)
|
||||
self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertNotEqual)
|
||||
|
||||
def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self):
|
||||
self.openTestHtml(enabled=False)
|
||||
self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._textarea, self.assertNotEqual)
|
||||
|
||||
########################################################################
|
||||
# <div> contenteditable test cases with touch caret enabled
|
||||
########################################################################
|
||||
def test_contenteditable_move_caret_to_the_right_by_one_character(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertEqual)
|
||||
|
||||
def test_contenteditable_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._contenteditable, self.assertEqual)
|
||||
|
||||
def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._contenteditable, self.assertEqual)
|
||||
|
||||
def test_contenteditable_touch_caret_timeout(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._contenteditable, self.assertNotEqual)
|
||||
|
||||
########################################################################
|
||||
# <div> contenteditable test cases with touch caret disabled
|
||||
########################################################################
|
||||
def test_contenteditable_move_caret_to_the_right_by_one_character_disabled(self):
|
||||
self.openTestHtml(enabled=False)
|
||||
self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertNotEqual)
|
||||
|
||||
def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self):
|
||||
self.openTestHtml(enabled=False)
|
||||
self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._contenteditable, self.assertNotEqual)
|
@ -825,7 +825,6 @@ pref("browser.snippets.statsUrl", "https://snippets-stats.mozilla.org/mobile");
|
||||
pref("browser.snippets.enabled", true);
|
||||
pref("browser.snippets.syncPromo.enabled", true);
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
// The URL of the APK factory from which we obtain APKs for webapps.
|
||||
pref("browser.webapps.apkFactoryUrl", "https://controller.apk.firefox.com/application.apk");
|
||||
|
||||
@ -850,8 +849,6 @@ pref("browser.webapps.checkForUpdates", 1);
|
||||
// which is a test server that always reports all apps as having updates.
|
||||
pref("browser.webapps.updateCheckUrl", "https://controller.apk.firefox.com/app_updates");
|
||||
|
||||
#endif
|
||||
|
||||
// The mode of home provider syncing.
|
||||
// 0: Sync always
|
||||
// 1: Sync only when on wifi
|
||||
|
@ -188,7 +188,6 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
<activity android:name="org.mozilla.gecko.webapp.Dispatcher"
|
||||
android:noHistory="true" >
|
||||
<intent-filter>
|
||||
@ -212,7 +211,6 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
#endif
|
||||
|
||||
<activity android:name=".Webapp"
|
||||
android:label="@string/webapp_generic_name"
|
||||
|
@ -149,13 +149,6 @@ public class AppConstants {
|
||||
false;
|
||||
#endif
|
||||
|
||||
public static final boolean MOZ_ANDROID_SYNTHAPKS =
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
// See this wiki page for more details about channel specific build defines:
|
||||
// https://wiki.mozilla.org/Platform/Channel-specific_build_defines
|
||||
public static final boolean RELEASE_BUILD =
|
||||
|
@ -793,41 +793,19 @@ public class GeckoAppShell
|
||||
public static Intent getWebappIntent(String aURI, String aOrigin, String aTitle, Bitmap aIcon) {
|
||||
Intent intent;
|
||||
|
||||
if (AppConstants.MOZ_ANDROID_SYNTHAPKS) {
|
||||
Allocator slots = Allocator.getInstance(getContext());
|
||||
int index = slots.getIndexForOrigin(aOrigin);
|
||||
Allocator slots = Allocator.getInstance(getContext());
|
||||
int index = slots.getIndexForOrigin(aOrigin);
|
||||
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
String packageName = slots.getAppForIndex(index);
|
||||
intent = getContext().getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
if (aURI != null) {
|
||||
intent.setData(Uri.parse(aURI));
|
||||
}
|
||||
} else {
|
||||
int index;
|
||||
if (aIcon != null && !TextUtils.isEmpty(aTitle))
|
||||
index = WebappAllocator.getInstance(getContext()).findAndAllocateIndex(aOrigin, aTitle, aIcon);
|
||||
else
|
||||
index = WebappAllocator.getInstance(getContext()).getIndexForApp(aOrigin);
|
||||
|
||||
if (index == -1)
|
||||
return null;
|
||||
|
||||
intent = getWebappIntent(index, aURI);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return intent;
|
||||
}
|
||||
String packageName = slots.getAppForIndex(index);
|
||||
intent = getContext().getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
if (aURI != null) {
|
||||
intent.setData(Uri.parse(aURI));
|
||||
}
|
||||
|
||||
// The old implementation of getWebappIntent. Not used by MOZ_ANDROID_SYNTHAPKS.
|
||||
public static Intent getWebappIntent(int aIndex, String aURI) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(GeckoApp.ACTION_WEBAPP_PREFIX + aIndex);
|
||||
intent.setData(Uri.parse(aURI));
|
||||
intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
|
||||
AppConstants.ANDROID_PACKAGE_NAME + ".WebApps$WebApp" + aIndex);
|
||||
return intent;
|
||||
}
|
||||
|
||||
|
@ -5,24 +5,28 @@
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
class GlobalHistory {
|
||||
private static final String LOGTAG = "GeckoGlobalHistory";
|
||||
|
||||
private static GlobalHistory sInstance = new GlobalHistory();
|
||||
private static final String TELEMETRY_HISTOGRAM_ADD = "FENNEC_GLOBALHISTORY_ADD_MS";
|
||||
private static final String TELEMETRY_HISTOGRAM_UPDATE = "FENNEC_GLOBALHISTORY_UPDATE_MS";
|
||||
private static final String TELEMETRY_HISTOGRAM_BUILD_VISITED_LINK = "FENNEC_GLOBALHISTORY_VISITED_BUILD_MS";
|
||||
|
||||
private static final GlobalHistory sInstance = new GlobalHistory();
|
||||
|
||||
static GlobalHistory getInstance() {
|
||||
return sInstance;
|
||||
@ -48,35 +52,38 @@ class GlobalHistory {
|
||||
public void run() {
|
||||
Set<String> visitedSet = mVisitedCache.get();
|
||||
if (visitedSet == null) {
|
||||
// the cache was wiped away, repopulate it
|
||||
// The cache was wiped. Repopulate it.
|
||||
Log.w(LOGTAG, "Rebuilding visited link set...");
|
||||
visitedSet = new HashSet<String>();
|
||||
Cursor c = null;
|
||||
try {
|
||||
c = BrowserDB.getAllVisitedHistory(GeckoAppShell.getContext().getContentResolver());
|
||||
if (c == null) {
|
||||
return;
|
||||
}
|
||||
final long start = SystemClock.uptimeMillis();
|
||||
final Cursor c = BrowserDB.getAllVisitedHistory(GeckoAppShell.getContext().getContentResolver());
|
||||
if (c == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
visitedSet = new HashSet<String>();
|
||||
if (c.moveToFirst()) {
|
||||
do {
|
||||
visitedSet.add(c.getString(0));
|
||||
} while (c.moveToNext());
|
||||
}
|
||||
mVisitedCache = new SoftReference<Set<String>>(visitedSet);
|
||||
final long end = SystemClock.uptimeMillis();
|
||||
final long took = end - start;
|
||||
Telemetry.HistogramAdd(TELEMETRY_HISTOGRAM_BUILD_VISITED_LINK, (int) Math.min(took, Integer.MAX_VALUE));
|
||||
} finally {
|
||||
if (c != null)
|
||||
c.close();
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
// this runs on the same handler thread as the checkUriVisited code,
|
||||
// so no synchronization needed
|
||||
// This runs on the same handler thread as the checkUriVisited code,
|
||||
// so no synchronization is needed.
|
||||
while (true) {
|
||||
String uri = mPendingUris.poll();
|
||||
final String uri = mPendingUris.poll();
|
||||
if (uri == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (visitedSet.contains(uri)) {
|
||||
GeckoAppShell.notifyUriVisited(uri);
|
||||
}
|
||||
@ -95,12 +102,23 @@ class GlobalHistory {
|
||||
}
|
||||
|
||||
public void add(String uri) {
|
||||
final long start = SystemClock.uptimeMillis();
|
||||
BrowserDB.updateVisitedHistory(GeckoAppShell.getContext().getContentResolver(), uri);
|
||||
final long end = SystemClock.uptimeMillis();
|
||||
final long took = end - start;
|
||||
Log.d(LOGTAG, "GlobalHistory.add took " + took + "msec.");
|
||||
Telemetry.HistogramAdd(TELEMETRY_HISTOGRAM_ADD, (int) Math.min(took, Integer.MAX_VALUE));
|
||||
addToGeckoOnly(uri);
|
||||
}
|
||||
|
||||
@SuppressWarnings("static-method")
|
||||
public void update(String uri, String title) {
|
||||
final long start = SystemClock.uptimeMillis();
|
||||
BrowserDB.updateHistoryTitle(GeckoAppShell.getContext().getContentResolver(), uri, title);
|
||||
final long end = SystemClock.uptimeMillis();
|
||||
final long took = end - start;
|
||||
Log.d(LOGTAG, "GlobalHistory.update took " + took + "msec.");
|
||||
Telemetry.HistogramAdd(TELEMETRY_HISTOGRAM_UPDATE, (int) Math.min(took, Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
public void checkUriVisited(final String uri) {
|
||||
|
@ -6,11 +6,7 @@
|
||||
#filter substitution
|
||||
package @ANDROID_PACKAGE_NAME@;
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
import org.mozilla.gecko.webapp.WebappImpl;
|
||||
#else
|
||||
import org.mozilla.gecko.WebappImpl;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This class serves only as a namespace wrapper for WebappImpl.
|
||||
|
@ -1,132 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class WebappAllocator {
|
||||
private final String LOGTAG = "GeckoWebappAllocator";
|
||||
// The number of Webapp# and WEBAPP# activites/apps/intents
|
||||
private final static int MAX_WEB_APPS = 100;
|
||||
|
||||
protected static WebappAllocator sInstance = null;
|
||||
public static WebappAllocator getInstance() {
|
||||
return getInstance(GeckoAppShell.getContext());
|
||||
}
|
||||
|
||||
public static synchronized WebappAllocator getInstance(Context cx) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new WebappAllocator(cx);
|
||||
}
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
SharedPreferences mPrefs;
|
||||
|
||||
protected WebappAllocator(Context context) {
|
||||
mPrefs = context.getSharedPreferences("webapps", Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
|
||||
}
|
||||
|
||||
public static String appKey(int index) {
|
||||
return "app" + index;
|
||||
}
|
||||
|
||||
static public String iconKey(int index) {
|
||||
return "icon" + index;
|
||||
}
|
||||
|
||||
public synchronized int findAndAllocateIndex(String app, String name, String aIconData) {
|
||||
Bitmap icon = (aIconData != null) ? BitmapUtils.getBitmapFromDataURI(aIconData) : null;
|
||||
return findAndAllocateIndex(app, name, icon);
|
||||
}
|
||||
|
||||
public synchronized int findAndAllocateIndex(final String app, final String name, final Bitmap aIcon) {
|
||||
int index = getIndexForApp(app);
|
||||
if (index != -1)
|
||||
return index;
|
||||
|
||||
for (int i = 0; i < MAX_WEB_APPS; ++i) {
|
||||
if (!mPrefs.contains(appKey(i))) {
|
||||
// found unused index i
|
||||
updateAppAllocation(app, i, aIcon);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// no more apps!
|
||||
return -1;
|
||||
}
|
||||
|
||||
public synchronized void updateAppAllocation(final String app,
|
||||
final int index,
|
||||
final Bitmap aIcon) {
|
||||
if (aIcon != null) {
|
||||
ThreadUtils.getBackgroundHandler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int color = 0;
|
||||
try {
|
||||
color = BitmapUtils.getDominantColor(aIcon);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "Exception during getDominantColor", e);
|
||||
}
|
||||
mPrefs.edit()
|
||||
.putString(appKey(index), app)
|
||||
.putInt(iconKey(index), color).commit();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mPrefs.edit()
|
||||
.putString(appKey(index), app)
|
||||
.putInt(iconKey(index), 0).commit();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int getIndexForApp(String app) {
|
||||
for (int i = 0; i < MAX_WEB_APPS; ++i) {
|
||||
if (mPrefs.getString(appKey(i), "").equals(app)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public synchronized String getAppForIndex(int index) {
|
||||
return mPrefs.getString(appKey(index), null);
|
||||
}
|
||||
|
||||
public synchronized int releaseIndexForApp(String app) {
|
||||
int index = getIndexForApp(app);
|
||||
if (index == -1)
|
||||
return -1;
|
||||
|
||||
releaseIndex(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
public synchronized void releaseIndex(final int index) {
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mPrefs.edit()
|
||||
.remove(appKey(index))
|
||||
.remove(iconKey(index))
|
||||
.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.TextView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ImageView;
|
||||
import android.view.Display;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
|
||||
public class WebappImpl extends GeckoApp {
|
||||
private static final String LOGTAG = "GeckoWebappImpl";
|
||||
|
||||
private URI mOrigin;
|
||||
private TextView mTitlebarText = null;
|
||||
private View mTitlebar = null;
|
||||
|
||||
private View mSplashscreen;
|
||||
|
||||
protected int getIndex() { return 0; }
|
||||
|
||||
@Override
|
||||
public int getLayout() { return R.layout.web_app; }
|
||||
|
||||
@Override
|
||||
public boolean hasTabsSideBar() { return false; }
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mSplashscreen = (RelativeLayout) findViewById(R.id.splashscreen);
|
||||
if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
|
||||
overridePendingTransition(R.anim.grow_fade_in_center, android.R.anim.fade_out);
|
||||
showSplash();
|
||||
}
|
||||
|
||||
String action = getIntent().getAction();
|
||||
Bundle extras = getIntent().getExtras();
|
||||
String title = extras != null ? extras.getString(Intent.EXTRA_SHORTCUT_NAME) : null;
|
||||
setTitle(title != null ? title : "Web App");
|
||||
|
||||
mTitlebarText = (TextView)findViewById(R.id.webapp_title);
|
||||
mTitlebar = findViewById(R.id.webapp_titlebar);
|
||||
if (!action.startsWith(ACTION_WEBAPP_PREFIX)) {
|
||||
Log.e(LOGTAG, "Webapp launch, but intent action is " + action + "!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to use the origin stored in the WebappAllocator first
|
||||
String origin = WebappAllocator.getInstance(this).getAppForIndex(getIndex());
|
||||
try {
|
||||
mOrigin = new URI(origin);
|
||||
} catch (java.net.URISyntaxException ex) {
|
||||
// If we can't parse the this is an app protocol, just settle for not having an origin
|
||||
if (!origin.startsWith("app://")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If that failed fall back to the origin stored in the shortcut
|
||||
Log.i(LOGTAG, "Webapp is not registered with allocator");
|
||||
try {
|
||||
mOrigin = new URI(getIntent().getData().toString());
|
||||
} catch (java.net.URISyntaxException ex2) {
|
||||
Log.e(LOGTAG, "Unable to parse intent url: ", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadStartupTab(String uri) {
|
||||
String action = getIntent().getAction();
|
||||
if (GeckoApp.ACTION_WEBAPP_PREFIX.equals(action)) {
|
||||
// This action assumes the uri is not an installed Webapp. We will
|
||||
// use the WebappAllocator to register the uri with an Android
|
||||
// process so it can run chromeless.
|
||||
int index = WebappAllocator.getInstance(this).findAndAllocateIndex(uri, "App", (Bitmap) null);
|
||||
Intent appIntent = GeckoAppShell.getWebappIntent(index, uri);
|
||||
startActivity(appIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void showSplash() {
|
||||
SharedPreferences prefs = getSharedPreferences("webapps", Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
|
||||
|
||||
// get the favicon dominant color, stored when the app was installed
|
||||
int[] colors = new int[2];
|
||||
int dominantColor = prefs.getInt(WebappAllocator.iconKey(getIndex()), -1);
|
||||
|
||||
// now lighten it, to ensure that the icon stands out in the center
|
||||
float[] f = new float[3];
|
||||
Color.colorToHSV(dominantColor, f);
|
||||
f[2] = Math.min(f[2]*2, 1.0f);
|
||||
colors[0] = Color.HSVToColor(255, f);
|
||||
|
||||
// now generate a second, slightly darker version of the same color
|
||||
f[2] *= 0.75;
|
||||
colors[1] = Color.HSVToColor(255, f);
|
||||
|
||||
// Draw the background gradient
|
||||
GradientDrawable gd = new GradientDrawable(GradientDrawable.Orientation.TL_BR, colors);
|
||||
gd.setGradientType(GradientDrawable.RADIAL_GRADIENT);
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
gd.setGradientCenter(0.5f, 0.5f);
|
||||
gd.setGradientRadius(Math.max(display.getWidth()/2, display.getHeight()/2));
|
||||
mSplashscreen.setBackgroundDrawable((Drawable)gd);
|
||||
|
||||
// look for a logo.png in the profile dir and show it. If we can't find a logo show nothing
|
||||
File profile = getProfile().getDir();
|
||||
File logoFile = new File(profile, "logo.png");
|
||||
if (logoFile.exists()) {
|
||||
ImageView image = (ImageView)findViewById(R.id.splashscreen_icon);
|
||||
Drawable d = Drawable.createFromPath(logoFile.getPath());
|
||||
image.setImageDrawable(d);
|
||||
|
||||
Animation fadein = AnimationUtils.loadAnimation(this, R.anim.grow_fade_in_center);
|
||||
fadein.setStartOffset(500);
|
||||
fadein.setDuration(1000);
|
||||
image.startAnimation(fadein);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDefaultProfileName() {
|
||||
String action = getIntent().getAction();
|
||||
if (!action.startsWith(ACTION_WEBAPP_PREFIX)) {
|
||||
Log.e(LOGTAG, "Webapp launch, but intent action is " + action + "!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return "webapp" + action.substring(ACTION_WEBAPP_PREFIX.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean getSessionRestoreState(Bundle savedInstanceState) {
|
||||
// for now webapps never restore your session
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
|
||||
switch(msg) {
|
||||
case SELECTED:
|
||||
case LOCATION_CHANGE:
|
||||
if (Tabs.getInstance().isSelectedTab(tab)) {
|
||||
final String urlString = tab.getURL();
|
||||
final URI uri;
|
||||
|
||||
try {
|
||||
uri = new URI(urlString);
|
||||
} catch (java.net.URISyntaxException ex) {
|
||||
mTitlebarText.setText(urlString);
|
||||
|
||||
// If we can't parse the url, and its an app protocol hide
|
||||
// the titlebar and return, otherwise show the titlebar
|
||||
// and the full url
|
||||
if (!urlString.startsWith("app://")) {
|
||||
mTitlebar.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mTitlebar.setVisibility(View.GONE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mOrigin != null && mOrigin.getHost().equals(uri.getHost())) {
|
||||
mTitlebar.setVisibility(View.GONE);
|
||||
} else {
|
||||
mTitlebarText.setText(uri.getScheme() + "://" + uri.getHost());
|
||||
mTitlebar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LOADED:
|
||||
if (mSplashscreen != null && mSplashscreen.getVisibility() == View.VISIBLE) {
|
||||
Animation fadeout = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
|
||||
fadeout.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
mSplashscreen.setVisibility(View.GONE);
|
||||
}
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) { }
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) { }
|
||||
});
|
||||
mSplashscreen.startAnimation(fadeout);
|
||||
}
|
||||
break;
|
||||
case START:
|
||||
if (mSplashscreen != null && mSplashscreen.getVisibility() == View.VISIBLE) {
|
||||
View area = findViewById(R.id.splashscreen_progress);
|
||||
area.setVisibility(View.VISIBLE);
|
||||
Animation fadein = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
|
||||
fadein.setDuration(1000);
|
||||
area.startAnimation(fadein);
|
||||
}
|
||||
break;
|
||||
}
|
||||
super.onTabChanged(tab, msg, data);
|
||||
}
|
||||
};
|
@ -4,19 +4,6 @@
|
||||
android:windowSoftInputMode="stateUnspecified|adjustResize"
|
||||
android:process=":@ANDROID_PACKAGE_NAME@.Webapp@APPNUM@"
|
||||
android:theme="@style/Gecko.App"
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
android:launchMode="singleTop"
|
||||
android:exported="true"
|
||||
/>
|
||||
#else
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity="org.mozilla.gecko.WEBAPP@APPNUM@"
|
||||
android:excludeFromRecents="true">
|
||||
<intent-filter>
|
||||
<action android:name="org.mozilla.gecko.WEBAPP@APPNUM@" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="org.mozilla.gecko.ACTION_ALERT_CALLBACK" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
#endif
|
||||
|
@ -5,20 +5,24 @@
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Encapsulates the implementation of the search cursor loader.
|
||||
*/
|
||||
class SearchLoader {
|
||||
// Key for search terms
|
||||
public static final String LOGTAG = "GeckoSearchLoader";
|
||||
|
||||
private static final String KEY_SEARCH_TERM = "search_term";
|
||||
|
||||
private SearchLoader() {
|
||||
@ -53,6 +57,8 @@ class SearchLoader {
|
||||
}
|
||||
|
||||
public static class SearchCursorLoader extends SimpleCursorLoader {
|
||||
private static final String TELEMETRY_HISTOGRAM_LOAD_CURSOR = "FENNEC_SEARCH_LOADER_TIME_MS";
|
||||
|
||||
// Max number of search results
|
||||
private static final int SEARCH_LIMIT = 100;
|
||||
|
||||
@ -66,7 +72,12 @@ class SearchLoader {
|
||||
|
||||
@Override
|
||||
public Cursor loadCursor() {
|
||||
return BrowserDB.filter(getContext().getContentResolver(), mSearchTerm, SEARCH_LIMIT);
|
||||
final long start = SystemClock.uptimeMillis();
|
||||
final Cursor cursor = BrowserDB.filter(getContext().getContentResolver(), mSearchTerm, SEARCH_LIMIT);
|
||||
final long end = SystemClock.uptimeMillis();
|
||||
final long took = end - start;
|
||||
Telemetry.HistogramAdd(TELEMETRY_HISTOGRAM_LOAD_CURSOR, (int) Math.min(took, Integer.MAX_VALUE));
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public String getSearchTerm() {
|
||||
|
@ -31,9 +31,9 @@ import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
@ -430,8 +430,9 @@ public class TopSitesPanel extends HomeFragment {
|
||||
}
|
||||
|
||||
private static class TopSitesLoader extends SimpleCursorLoader {
|
||||
// Max number of search results
|
||||
// Max number of search results.
|
||||
private static final int SEARCH_LIMIT = 30;
|
||||
private static final String TELEMETRY_HISTOGRAM_LOAD_CURSOR = "FENNEC_TOPSITES_LOADER_TIME_MS";
|
||||
private int mMaxGridEntries;
|
||||
|
||||
public TopSitesLoader(Context context) {
|
||||
@ -441,8 +442,12 @@ public class TopSitesPanel extends HomeFragment {
|
||||
|
||||
@Override
|
||||
public Cursor loadCursor() {
|
||||
trace("TopSitesLoader.loadCursor()");
|
||||
return BrowserDB.getTopSites(getContext().getContentResolver(), mMaxGridEntries, SEARCH_LIMIT);
|
||||
final long start = SystemClock.uptimeMillis();
|
||||
final Cursor cursor = BrowserDB.getTopSites(getContext().getContentResolver(), mMaxGridEntries, SEARCH_LIMIT);
|
||||
final long end = SystemClock.uptimeMillis();
|
||||
final long took = end - start;
|
||||
Telemetry.HistogramAdd(TELEMETRY_HISTOGRAM_LOAD_CURSOR, (int) Math.min(took, Integer.MAX_VALUE));
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,8 +404,6 @@ gbjar.sources += [
|
||||
'webapp/TaskKiller.java',
|
||||
'webapp/UninstallListener.java',
|
||||
'webapp/WebappImpl.java',
|
||||
'WebappAllocator.java',
|
||||
'WebappImpl.java',
|
||||
'widget/ActivityChooserModel.java',
|
||||
'widget/AllCapsTextView.java',
|
||||
'widget/AnimatedHeightLayout.java',
|
||||
|
@ -5,33 +5,6 @@
|
||||
|
||||
package org.mozilla.gecko.webapp;
|
||||
|
||||
import org.mozilla.gecko.ActivityHandlerHelper;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.util.ActivityResultHandler;
|
||||
import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.NativeEventListener;
|
||||
import org.mozilla.gecko.util.NativeJSObject;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.WebappAllocator;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@ -40,6 +13,25 @@ import java.util.Set;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.ActivityHandlerHelper;
|
||||
import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.util.ActivityResultHandler;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.NativeEventListener;
|
||||
import org.mozilla.gecko.util.NativeJSObject;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
public class EventListener implements NativeEventListener {
|
||||
|
||||
@ -68,18 +60,10 @@ public class EventListener implements NativeEventListener {
|
||||
@Override
|
||||
public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
|
||||
try {
|
||||
if (AppConstants.MOZ_ANDROID_SYNTHAPKS && event.equals("Webapps:InstallApk")) {
|
||||
if (event.equals("Webapps:InstallApk")) {
|
||||
installApk(GeckoAppShell.getGeckoInterface().getActivity(), message, callback);
|
||||
} else if (event.equals("Webapps:Postinstall")) {
|
||||
if (AppConstants.MOZ_ANDROID_SYNTHAPKS) {
|
||||
postInstallWebapp(message.getString("apkPackageName"), message.getString("origin"));
|
||||
} else {
|
||||
postInstallWebapp(message.getString("name"),
|
||||
message.getString("manifestURL"),
|
||||
message.getString("origin"),
|
||||
message.getString("iconURL"),
|
||||
message.getString("originalOrigin"));
|
||||
}
|
||||
postInstallWebapp(message.getString("apkPackageName"), message.getString("origin"));
|
||||
} else if (event.equals("Webapps:Open")) {
|
||||
Intent intent = GeckoAppShell.getWebappIntent(message.getString("manifestURL"),
|
||||
message.getString("origin"),
|
||||
@ -88,16 +72,6 @@ public class EventListener implements NativeEventListener {
|
||||
return;
|
||||
}
|
||||
GeckoAppShell.getGeckoInterface().getActivity().startActivity(intent);
|
||||
} else if (!AppConstants.MOZ_ANDROID_SYNTHAPKS && event.equals("Webapps:Uninstall")) {
|
||||
uninstallWebapp(message.getString("origin"));
|
||||
} else if (!AppConstants.MOZ_ANDROID_SYNTHAPKS && event.equals("Webapps:Preinstall")) {
|
||||
String name = message.getString("name");
|
||||
String manifestURL = message.getString("manifestURL");
|
||||
String origin = message.getString("origin");
|
||||
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("profile", preInstallWebapp(name, manifestURL, origin).toString());
|
||||
callback.sendSuccess(obj);
|
||||
} else if (event.equals("Webapps:GetApkVersions")) {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("versions", getApkVersions(GeckoAppShell.getGeckoInterface().getActivity(),
|
||||
@ -109,30 +83,6 @@ public class EventListener implements NativeEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
// Not used by MOZ_ANDROID_SYNTHAPKS.
|
||||
public static File preInstallWebapp(String aTitle, String aURI, String aOrigin) {
|
||||
int index = WebappAllocator.getInstance(GeckoAppShell.getContext()).findAndAllocateIndex(aOrigin, aTitle, (String) null);
|
||||
GeckoProfile profile = GeckoProfile.get(GeckoAppShell.getContext(), "webapp" + index);
|
||||
return profile.getDir();
|
||||
}
|
||||
|
||||
// Not used by MOZ_ANDROID_SYNTHAPKS.
|
||||
public static void postInstallWebapp(String aTitle, String aURI, String aOrigin, String aIconURL, String aOriginalOrigin) {
|
||||
WebappAllocator allocator = WebappAllocator.getInstance(GeckoAppShell.getContext());
|
||||
int index = allocator.getIndexForApp(aOriginalOrigin);
|
||||
|
||||
assert aIconURL != null;
|
||||
|
||||
final int preferredSize = GeckoAppShell.getPreferredIconSize();
|
||||
Bitmap icon = FaviconDecoder.getMostSuitableBitmapFromDataURI(aIconURL, preferredSize);
|
||||
|
||||
assert aOrigin != null && index != -1;
|
||||
allocator.updateAppAllocation(aOrigin, index, icon);
|
||||
|
||||
GeckoAppShell.createShortcut(aTitle, aURI, aOrigin, icon, "webapp");
|
||||
}
|
||||
|
||||
// Used by MOZ_ANDROID_SYNTHAPKS.
|
||||
public static void postInstallWebapp(String aPackageName, String aOrigin) {
|
||||
Allocator allocator = Allocator.getInstance(GeckoAppShell.getContext());
|
||||
int index = allocator.findOrAllocatePackage(aPackageName);
|
||||
|
@ -147,9 +147,7 @@ public class UninstallListener extends BroadcastReceiver {
|
||||
ThreadUtils.assertOnBackgroundThread();
|
||||
|
||||
// Perform webapp uninstalls as appropiate.
|
||||
if (AppConstants.MOZ_ANDROID_SYNTHAPKS) {
|
||||
UninstallListener.initUninstallPackageScan(mApp.getApplicationContext());
|
||||
}
|
||||
UninstallListener.initUninstallPackageScan(mApp.getApplicationContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,9 @@ Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
|
||||
Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
|
||||
Cu.import("resource://gre/modules/ContactService.jsm");
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm");
|
||||
#endif
|
||||
|
||||
function pref(name, value) {
|
||||
return {
|
||||
@ -70,14 +68,12 @@ let WebappRT = {
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
// If the app is in debug mode, configure and enable the remote debugger.
|
||||
sendMessageToJava({ type: "NativeApp:IsDebuggable" }, (response) => {
|
||||
if (response.isDebuggable) {
|
||||
this._enableRemoteDebugger(aUrl);
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
this.findManifestUrlFor(aUrl, aCallback);
|
||||
},
|
||||
@ -163,7 +159,6 @@ let WebappRT = {
|
||||
}
|
||||
},
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
_enableRemoteDebugger: function(aUrl) {
|
||||
// Skip the connection prompt in favor of notifying the user below.
|
||||
Services.prefs.setBoolPref("devtools.debugger.prompt-connection", false);
|
||||
@ -195,7 +190,6 @@ let WebappRT = {
|
||||
});
|
||||
});
|
||||
},
|
||||
#endif
|
||||
|
||||
handleEvent: function(event) {
|
||||
let target = event.target;
|
||||
|
@ -12,9 +12,7 @@ Cu.import("resource://gre/modules/Services.jsm")
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebappManager", "resource://gre/modules/WebappManager.jsm");
|
||||
#endif
|
||||
|
||||
const DEFAULT_ICON = "chrome://browser/skin/images/default-app-icon.png";
|
||||
|
||||
@ -45,51 +43,9 @@ function openLink(aEvent) {
|
||||
} catch (ex) {}
|
||||
}
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
function checkForUpdates(aEvent) {
|
||||
WebappManager.checkForUpdates(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MOZ_ANDROID_SYNTHAPKS
|
||||
var ContextMenus = {
|
||||
target: null,
|
||||
|
||||
init: function() {
|
||||
document.addEventListener("contextmenu", this, false);
|
||||
document.getElementById("addToHomescreenLabel").addEventListener("click", this.addToHomescreen, false);
|
||||
document.getElementById("uninstallLabel").addEventListener("click", this.uninstall, false);
|
||||
},
|
||||
|
||||
handleEvent: function(event) {
|
||||
// store the target of context menu events so that we know which app to act on
|
||||
this.target = event.target;
|
||||
while (!this.target.hasAttribute("contextmenu")) {
|
||||
this.target = this.target.parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
addToHomescreen: function() {
|
||||
let manifest = this.target.manifest;
|
||||
gChromeWin.WebappsUI.createShortcut(manifest.name, manifest.fullLaunchPath(), manifest.biggestIconURL || DEFAULT_ICON, "webapp");
|
||||
this.target = null;
|
||||
},
|
||||
|
||||
uninstall: function() {
|
||||
navigator.mozApps.mgmt.uninstall(this.target.app);
|
||||
|
||||
let manifest = this.target.manifest;
|
||||
gChromeWin.sendMessageToJava({
|
||||
type: "Shortcut:Remove",
|
||||
title: manifest.name,
|
||||
url: manifest.fullLaunchPath(),
|
||||
origin: this.target.app.origin,
|
||||
shortcutType: "webapp"
|
||||
});
|
||||
this.target = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
function onLoad(aEvent) {
|
||||
let elmts = document.querySelectorAll("[pref]");
|
||||
@ -97,17 +53,14 @@ function onLoad(aEvent) {
|
||||
elmts[i].addEventListener("click", openLink, false);
|
||||
}
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
document.getElementById("update-item").addEventListener("click", checkForUpdates, false);
|
||||
#endif
|
||||
|
||||
navigator.mozApps.mgmt.oninstall = onInstall;
|
||||
navigator.mozApps.mgmt.onuninstall = onUninstall;
|
||||
updateList();
|
||||
|
||||
#ifndef MOZ_ANDROID_SYNTHAPKS
|
||||
ContextMenus.init();
|
||||
#endif
|
||||
// XXX - Hack to fix bug 985867 for now
|
||||
document.addEventListener("touchstart", function() { });
|
||||
}
|
||||
|
||||
function updateList() {
|
||||
@ -131,9 +84,6 @@ function addApplication(aApp) {
|
||||
|
||||
let container = document.createElement("div");
|
||||
container.className = "app list-item";
|
||||
#ifndef MOZ_ANDROID_SYNTHAPKS
|
||||
container.setAttribute("contextmenu", "appmenu");
|
||||
#endif
|
||||
container.setAttribute("id", "app-" + aApp.origin);
|
||||
container.setAttribute("mozApp", aApp.origin);
|
||||
container.setAttribute("title", manifest.name);
|
||||
|
@ -28,14 +28,6 @@
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
|
||||
#ifndef MOZ_ANDROID_SYNTHAPKS
|
||||
<menu type="context" id="appmenu">
|
||||
<menuitem id="addToHomescreenLabel" label="&aboutApps.addToHomescreen;"></menuitem>
|
||||
<menuitem id="uninstallLabel" label="&aboutApps.uninstall;"></menuitem>
|
||||
</menu>
|
||||
#endif
|
||||
|
||||
<div class="header">
|
||||
<div>&aboutApps.header;</div>
|
||||
<div id="header-button" role="button" aria-label="&aboutApps.browseMarketplace;" pref="app.marketplaceURL"/>
|
||||
@ -55,13 +47,11 @@
|
||||
<div id="browse-title" class="title">&aboutApps.browseMarketplace;</div>
|
||||
</div>
|
||||
</div>
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
<div class="list-item" id="update-item" role="button">
|
||||
<img class="icon" src="chrome://browser/skin/images/update.png" />
|
||||
<div class="inner">
|
||||
<div id="browse-title" class="title">&aboutApps.checkForUpdates;</div>
|
||||
</div>
|
||||
</div>
|
||||
#endif
|
||||
</body>
|
||||
</html>
|
||||
|
@ -77,10 +77,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils",
|
||||
"resource://shumway/ShumwayUtils.jsm");
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebappManager",
|
||||
"resource://gre/modules/WebappManager.jsm");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
|
||||
"resource://gre/modules/CharsetMenu.jsm");
|
||||
@ -326,7 +324,6 @@ var BrowserApp = {
|
||||
Services.obs.addObserver(this, "FormHistory:Init", false);
|
||||
Services.obs.addObserver(this, "gather-telemetry", false);
|
||||
Services.obs.addObserver(this, "keyword-search", false);
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
Services.obs.addObserver(this, "webapps-runtime-install", false);
|
||||
Services.obs.addObserver(this, "webapps-runtime-install-package", false);
|
||||
Services.obs.addObserver(this, "webapps-ask-install", false);
|
||||
@ -335,7 +332,6 @@ var BrowserApp = {
|
||||
Services.obs.addObserver(this, "Webapps:AutoInstall", false);
|
||||
Services.obs.addObserver(this, "Webapps:Load", false);
|
||||
Services.obs.addObserver(this, "Webapps:AutoUninstall", false);
|
||||
#endif
|
||||
Services.obs.addObserver(this, "sessionstore-state-purge-complete", false);
|
||||
|
||||
function showFullScreenWarning() {
|
||||
@ -377,13 +373,9 @@ var BrowserApp = {
|
||||
XPInstallObserver.init();
|
||||
CharacterEncoding.init();
|
||||
ActivityObserver.init();
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
// TODO: replace with Android implementation of WebappOSUtils.isLaunchable.
|
||||
Cu.import("resource://gre/modules/Webapps.jsm");
|
||||
DOMApplicationRegistry.allAppsLaunchable = true;
|
||||
#else
|
||||
WebappsUI.init();
|
||||
#endif
|
||||
RemoteDebugger.init();
|
||||
Reader.init();
|
||||
UserAgentOverrides.init();
|
||||
@ -770,9 +762,6 @@ var BrowserApp = {
|
||||
HealthReportStatusListener.uninit();
|
||||
CharacterEncoding.uninit();
|
||||
SearchEngines.uninit();
|
||||
#ifndef MOZ_ANDROID_SYNTHAPKS
|
||||
WebappsUI.uninit();
|
||||
#endif
|
||||
RemoteDebugger.uninit();
|
||||
Reader.uninit();
|
||||
UserAgentOverrides.uninit();
|
||||
@ -961,7 +950,6 @@ var BrowserApp = {
|
||||
sendMessageToJava(message);
|
||||
},
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
_loadWebapp: function(aMessage) {
|
||||
|
||||
this._initRuntime(this._startupStatus, aMessage.url, aUrl => {
|
||||
@ -969,7 +957,6 @@ var BrowserApp = {
|
||||
this.addTab(aUrl, { title: aMessage.name });
|
||||
});
|
||||
},
|
||||
#endif
|
||||
|
||||
// Calling this will update the state in BrowserApp after a tab has been
|
||||
// closed in the Java UI.
|
||||
@ -1627,7 +1614,6 @@ var BrowserApp = {
|
||||
this.notifyPrefObservers(aData);
|
||||
break;
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
case "webapps-runtime-install":
|
||||
WebappManager.install(JSON.parse(aData), aSubject);
|
||||
break;
|
||||
@ -1661,7 +1647,6 @@ var BrowserApp = {
|
||||
case "Webapps:AutoUninstall":
|
||||
WebappManager.autoUninstall(JSON.parse(aData));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case "Locale:Changed":
|
||||
if (aData) {
|
||||
@ -1676,6 +1661,14 @@ var BrowserApp = {
|
||||
}
|
||||
|
||||
Services.prefs.setBoolPref("intl.locale.matchOS", !aData);
|
||||
|
||||
// Ensure that this choice is immediately persisted, because
|
||||
// Gecko won't be told again if it forgets.
|
||||
Services.prefs.savePrefFile(null);
|
||||
|
||||
// Blow away the string cache so that future lookups get the
|
||||
// correct locale.
|
||||
Services.strings.flushBundles();
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -2290,16 +2283,46 @@ var NativeWindow = {
|
||||
return res;
|
||||
},
|
||||
|
||||
_findTarget: function(x, y) {
|
||||
let isDescendant = function(parent, child) {
|
||||
let node = child;
|
||||
while (node) {
|
||||
if (node === parent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
let target = BrowserEventHandler._highlightElement;
|
||||
let touchTarget = ElementTouchHelper.anyElementFromPoint(x, y);
|
||||
|
||||
// If we have a highlighted element that has a click handler, we want to ensure our target is inside it
|
||||
if (isDescendant(target, touchTarget)) {
|
||||
target = touchTarget;
|
||||
} else if (!target) {
|
||||
// Otherwise, let's try to find something clickable
|
||||
target = ElementTouchHelper.elementFromPoint(x, y);
|
||||
|
||||
// If that failed, we'll just fall back to anything under the user's finger
|
||||
if (!target) {
|
||||
target = touchTarget;
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
},
|
||||
|
||||
/* Checks if there are context menu items to show, and if it finds them
|
||||
* sends a contextmenu event to content. We also send showing events to
|
||||
* any html5 context menus we are about to show, and fire some local notifications
|
||||
* for chrome consumers to do lazy menuitem construction
|
||||
*/
|
||||
_sendToContent: function(x, y) {
|
||||
let target = BrowserEventHandler._highlightElement || ElementTouchHelper.elementFromPoint(x, y);
|
||||
if (!target)
|
||||
target = ElementTouchHelper.anyElementFromPoint(x, y);
|
||||
|
||||
let target = this._findTarget(x, y);
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
@ -7030,246 +7053,6 @@ var ActivityObserver = {
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef MOZ_ANDROID_SYNTHAPKS
|
||||
var WebappsUI = {
|
||||
init: function init() {
|
||||
Cu.import("resource://gre/modules/Webapps.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
DOMApplicationRegistry.allAppsLaunchable = true;
|
||||
|
||||
Services.obs.addObserver(this, "webapps-ask-install", false);
|
||||
Services.obs.addObserver(this, "webapps-launch", false);
|
||||
Services.obs.addObserver(this, "webapps-uninstall", false);
|
||||
Services.obs.addObserver(this, "webapps-install-error", false);
|
||||
},
|
||||
|
||||
uninit: function unint() {
|
||||
Services.obs.removeObserver(this, "webapps-ask-install");
|
||||
Services.obs.removeObserver(this, "webapps-launch");
|
||||
Services.obs.removeObserver(this, "webapps-uninstall");
|
||||
Services.obs.removeObserver(this, "webapps-install-error");
|
||||
},
|
||||
|
||||
DEFAULT_ICON: "chrome://browser/skin/images/default-app-icon.png",
|
||||
DEFAULT_PREFS_FILENAME: "default-prefs.js",
|
||||
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
let data = {};
|
||||
try {
|
||||
data = JSON.parse(aData);
|
||||
data.mm = aSubject;
|
||||
} catch(ex) { }
|
||||
switch (aTopic) {
|
||||
case "webapps-install-error":
|
||||
let msg = "";
|
||||
switch (aData) {
|
||||
case "INVALID_MANIFEST":
|
||||
case "MANIFEST_PARSE_ERROR":
|
||||
msg = Strings.browser.GetStringFromName("webapps.manifestInstallError");
|
||||
break;
|
||||
case "NETWORK_ERROR":
|
||||
case "MANIFEST_URL_ERROR":
|
||||
msg = Strings.browser.GetStringFromName("webapps.networkInstallError");
|
||||
break;
|
||||
default:
|
||||
msg = Strings.browser.GetStringFromName("webapps.installError");
|
||||
}
|
||||
NativeWindow.toast.show(msg, "short");
|
||||
console.log("Error installing app: " + aData);
|
||||
break;
|
||||
case "webapps-ask-install":
|
||||
this.doInstall(data);
|
||||
break;
|
||||
case "webapps-launch":
|
||||
this.openURL(data.manifestURL, data.origin);
|
||||
break;
|
||||
case "webapps-uninstall":
|
||||
sendMessageToJava({
|
||||
type: "Webapps:Uninstall",
|
||||
origin: data.origin
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
doInstall: function doInstall(aData) {
|
||||
let jsonManifest = aData.isPackage ? aData.app.updateManifest : aData.app.manifest;
|
||||
let manifest = new ManifestHelper(jsonManifest, aData.app.origin);
|
||||
|
||||
if (Services.prompt.confirm(null, Strings.browser.GetStringFromName("webapps.installTitle"), manifest.name + "\n" + aData.app.origin)) {
|
||||
// Get a profile for the app to be installed in. We'll download everything before creating the icons.
|
||||
let origin = aData.app.origin;
|
||||
sendMessageToJava({
|
||||
type: "Webapps:Preinstall",
|
||||
name: manifest.name,
|
||||
manifestURL: aData.app.manifestURL,
|
||||
origin: origin
|
||||
}, (data) => {
|
||||
let profilePath = data.profile;
|
||||
if (!profilePath)
|
||||
return;
|
||||
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(profilePath);
|
||||
|
||||
let self = this;
|
||||
DOMApplicationRegistry.confirmInstall(aData, file,
|
||||
function (aManifest) {
|
||||
let localeManifest = new ManifestHelper(aManifest, aData.app.origin);
|
||||
|
||||
// the manifest argument is the manifest from within the zip file,
|
||||
// TODO so now would be a good time to ask about permissions.
|
||||
self.makeBase64Icon(localeManifest.biggestIconURL || this.DEFAULT_ICON,
|
||||
function(scaledIcon, fullsizeIcon) {
|
||||
// if java returned a profile path to us, try to use it to pre-populate the app cache
|
||||
// also save the icon so that it can be used in the splash screen
|
||||
try {
|
||||
let iconFile = file.clone();
|
||||
iconFile.append("logo.png");
|
||||
let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
|
||||
persist.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
|
||||
persist.persistFlags |= Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
|
||||
|
||||
let source = Services.io.newURI(fullsizeIcon, "UTF8", null);
|
||||
persist.saveURI(source, null, null, null, null, iconFile, null);
|
||||
|
||||
// aData.app.origin may now point to the app: url that hosts this app
|
||||
sendMessageToJava({
|
||||
type: "Webapps:Postinstall",
|
||||
name: localeManifest.name,
|
||||
manifestURL: aData.app.manifestURL,
|
||||
originalOrigin: origin,
|
||||
origin: aData.app.origin,
|
||||
iconURL: fullsizeIcon
|
||||
});
|
||||
if (!!aData.isPackage) {
|
||||
// For packaged apps, put a notification in the notification bar.
|
||||
let message = Strings.browser.GetStringFromName("webapps.alertSuccess");
|
||||
let alerts = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
|
||||
alerts.showAlertNotification("drawable://alert_app", localeManifest.name, message, true, "", {
|
||||
observe: function () {
|
||||
self.openURL(aData.app.manifestURL, aData.app.origin);
|
||||
}
|
||||
}, "webapp");
|
||||
}
|
||||
|
||||
// Create a system notification allowing the user to launch the app
|
||||
let observer = {
|
||||
observe: function (aSubject, aTopic) {
|
||||
if (aTopic == "alertclickcallback") {
|
||||
WebappsUI.openURL(aData.app.manifestURL, origin);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let message = Strings.browser.GetStringFromName("webapps.alertSuccess");
|
||||
let alerts = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
|
||||
alerts.showAlertNotification("drawable://alert_app", localeManifest.name, message, true, "", observer, "webapp");
|
||||
} catch(ex) {
|
||||
console.log(ex);
|
||||
}
|
||||
self.writeDefaultPrefs(file, localeManifest);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
DOMApplicationRegistry.denyInstall(aData);
|
||||
}
|
||||
},
|
||||
|
||||
writeDefaultPrefs: function webapps_writeDefaultPrefs(aProfile, aManifest) {
|
||||
// build any app specific default prefs
|
||||
let prefs = [];
|
||||
if (aManifest.orientation) {
|
||||
prefs.push({name:"app.orientation.default", value: aManifest.orientation.join(",") });
|
||||
}
|
||||
|
||||
// write them into the app profile
|
||||
let defaultPrefsFile = aProfile.clone();
|
||||
defaultPrefsFile.append(this.DEFAULT_PREFS_FILENAME);
|
||||
this._writeData(defaultPrefsFile, prefs);
|
||||
},
|
||||
|
||||
_writeData: function(aFile, aPrefs) {
|
||||
if (aPrefs.length > 0) {
|
||||
let array = new TextEncoder().encode(JSON.stringify(aPrefs));
|
||||
OS.File.writeAtomic(aFile.path, array, { tmpPath: aFile.path + ".tmp" }).then(null, function onError(reason) {
|
||||
console.log("Error writing default prefs: " + reason);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
openURL: function openURL(aManifestURL, aOrigin) {
|
||||
sendMessageToJava({
|
||||
type: "Webapps:Open",
|
||||
manifestURL: aManifestURL,
|
||||
origin: aOrigin
|
||||
});
|
||||
},
|
||||
|
||||
get iconSize() {
|
||||
let iconSize = 64;
|
||||
try {
|
||||
let jni = new JNI();
|
||||
let cls = jni.findClass("org/mozilla/gecko/GeckoAppShell");
|
||||
let method = jni.getStaticMethodID(cls, "getPreferredIconSize", "()I");
|
||||
iconSize = jni.callStaticIntMethod(cls, method);
|
||||
jni.close();
|
||||
} catch(ex) {
|
||||
console.log(ex);
|
||||
}
|
||||
|
||||
delete this.iconSize;
|
||||
return this.iconSize = iconSize;
|
||||
},
|
||||
|
||||
makeBase64Icon: function loadAndMakeBase64Icon(aIconURL, aCallbackFunction) {
|
||||
let size = this.iconSize;
|
||||
|
||||
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
canvas.width = canvas.height = size;
|
||||
let ctx = canvas.getContext("2d");
|
||||
let favicon = new Image();
|
||||
favicon.onload = function() {
|
||||
ctx.drawImage(favicon, 0, 0, size, size);
|
||||
let scaledIcon = canvas.toDataURL("image/png", "");
|
||||
|
||||
canvas.width = favicon.width;
|
||||
canvas.height = favicon.height;
|
||||
ctx.drawImage(favicon, 0, 0, favicon.width, favicon.height);
|
||||
let fullsizeIcon = canvas.toDataURL("image/png", "");
|
||||
|
||||
canvas = null;
|
||||
aCallbackFunction.call(null, scaledIcon, fullsizeIcon);
|
||||
};
|
||||
favicon.onerror = function() {
|
||||
Cu.reportError("CreateShortcut: favicon image load error");
|
||||
|
||||
// if the image failed to load, and it was not our default icon, attempt to
|
||||
// use our default as a fallback
|
||||
if (favicon.src != WebappsUI.DEFAULT_ICON) {
|
||||
favicon.src = WebappsUI.DEFAULT_ICON;
|
||||
}
|
||||
};
|
||||
|
||||
favicon.src = aIconURL;
|
||||
},
|
||||
|
||||
createShortcut: function createShortcut(aTitle, aURL, aIconURL, aType) {
|
||||
this.makeBase64Icon(aIconURL, function _createShortcut(icon) {
|
||||
try {
|
||||
let shell = Cc["@mozilla.org/browser/shell-service;1"].createInstance(Ci.nsIShellService);
|
||||
shell.createShortcut(aTitle, aURL, icon, aType);
|
||||
} catch(e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
var RemoteDebugger = {
|
||||
init: function rd_init() {
|
||||
Services.prefs.addObserver("devtools.debugger.", this, false);
|
||||
|
@ -113,12 +113,10 @@ contract @mozilla.org/snippets;1 {a78d7e59-b558-4321-a3d6-dffe2f1e76dd}
|
||||
category profile-after-change Snippets @mozilla.org/snippets;1
|
||||
category update-timer Snippets @mozilla.org/snippets;1,getService,snippets-update-timer,browser.snippets.updateInterval,86400
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
# WebappsUpdateTimer.js
|
||||
component {8f7002cb-e959-4f0a-a2e8-563232564385} WebappsUpdateTimer.js
|
||||
contract @mozilla.org/webapps-update-timer;1 {8f7002cb-e959-4f0a-a2e8-563232564385}
|
||||
category update-timer WebappsUpdateTimer @mozilla.org/webapps-update-timer;1,getService,webapp-background-update-timer,browser.webapps.updateInterval,86400
|
||||
#endif
|
||||
|
||||
# ColorPicker.js
|
||||
component {430b987f-bb9f-46a3-99a5-241749220b29} ColorPicker.js
|
||||
|
@ -67,8 +67,5 @@ MOZ_DATA_REPORTING=1
|
||||
# Enable runtime locale switching.
|
||||
MOZ_LOCALE_SWITCHER=1
|
||||
|
||||
# Enable the "synthetic APKs" implementation of Open Web Apps.
|
||||
MOZ_ANDROID_SYNTHAPKS=1
|
||||
|
||||
# Enable second screen and casting support for external devices.
|
||||
MOZ_DEVICES=1
|
||||
|
@ -621,10 +621,7 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
|
||||
@BINPATH@/components/marionettecomponent.js
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
@BINPATH@/components/WebappsUpdateTimer.js
|
||||
#endif
|
||||
|
||||
@BINPATH@/components/DataStore.manifest
|
||||
@BINPATH@/components/DataStoreImpl.js
|
||||
@BINPATH@/components/dom_datastore.xpt
|
||||
|
@ -242,16 +242,6 @@ timer.start=%S: timer started
|
||||
# %1$S=name of timer, %2$S=number of milliseconds
|
||||
timer.end=%1$S: %2$Sms
|
||||
|
||||
# Webapps
|
||||
webapps.installTitle=Install Application
|
||||
webapps.alertSuccess=Successfully installed
|
||||
# Shown when there is a generic problem installing an app
|
||||
webapps.installError=Error installing application
|
||||
# Shown when there is something wrong with an apps manifest
|
||||
webapps.manifestInstallError=Invalid application manifest
|
||||
# Shown when a network error prevented installing an app
|
||||
webapps.networkInstallError=Could not download manifest
|
||||
|
||||
# Click to play plugins
|
||||
clickToPlayPlugins.message2=%S contains plugin content. Would you like to activate it?
|
||||
clickToPlayPlugins.activate=Activate
|
||||
|
@ -37,9 +37,7 @@
|
||||
locale/@AB_CD@/browser/phishing.dtd (%chrome/phishing.dtd)
|
||||
locale/@AB_CD@/browser/payments.properties (%chrome/payments.properties)
|
||||
locale/@AB_CD@/browser/handling.properties (%chrome/handling.properties)
|
||||
#ifdef MOZ_ANDROID_SYNTHAPKS
|
||||
locale/@AB_CD@/browser/webapp.properties (%chrome/webapp.properties)
|
||||
#endif
|
||||
|
||||
# overrides for toolkit l10n, also for en-US
|
||||
relativesrcdir toolkit/locales:
|
||||
|
@ -22,16 +22,10 @@ EXTRA_JS_MODULES += [
|
||||
'SharedPreferences.jsm',
|
||||
'SimpleServiceDiscovery.jsm',
|
||||
'SSLExceptions.jsm',
|
||||
'WebappManagerWorker.js',
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'RokuApp.jsm',
|
||||
'WebappManager.jsm',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_ANDROID_SYNTHAPKS']:
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'WebappManager.jsm',
|
||||
]
|
||||
EXTRA_JS_MODULES += [
|
||||
'WebappManagerWorker.js',
|
||||
]
|
||||
|
@ -649,6 +649,7 @@ FxAccountsInternal.prototype = {
|
||||
data.kB = CommonUtils.bytesAsHex(kB_hex);
|
||||
|
||||
delete data.keyFetchToken;
|
||||
delete data.unwrapBKey;
|
||||
|
||||
log.debug("Keys Obtained: kA=" + !!data.kA + ", kB=" + !!data.kB);
|
||||
if (logPII) {
|
||||
|
@ -294,8 +294,9 @@ add_test(function test_getKeys() {
|
||||
// Before getKeys, we have no keys
|
||||
do_check_eq(!!user.kA, false);
|
||||
do_check_eq(!!user.kB, false);
|
||||
// And we still have a key-fetch token to use
|
||||
// And we still have a key-fetch token and unwrapBKey to use
|
||||
do_check_eq(!!user.keyFetchToken, true);
|
||||
do_check_eq(!!user.unwrapBKey, true);
|
||||
|
||||
fxa.internal.getKeys().then(() => {
|
||||
fxa.getSignedInUser().then((user) => {
|
||||
@ -305,6 +306,7 @@ add_test(function test_getKeys() {
|
||||
do_check_eq(user.kA, expandHex("11"));
|
||||
do_check_eq(user.kB, expandHex("66"));
|
||||
do_check_eq(user.keyFetchToken, undefined);
|
||||
do_check_eq(user.unwrapBKey, undefined);
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -28,8 +28,5 @@ skip = false
|
||||
[include:../../../../../dom/events/test/marionette/manifest.ini]
|
||||
[include:../../../../../dom/wifi/test/marionette/manifest.ini]
|
||||
|
||||
; layout tests
|
||||
[include:../../../../../layout/base/tests/marionette/manifest.ini]
|
||||
|
||||
; loop tests
|
||||
[include:../../../../../browser/components/loop/manifest.ini]
|
||||
|
@ -1,17 +0,0 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html id="html">
|
||||
<head>
|
||||
<title>Bug 960897: Marionette tests for touch caret</title>
|
||||
</head>
|
||||
<body>
|
||||
<div><input id="input" value="ABCDEFGHI"></input></div>
|
||||
<br />
|
||||
<div><textarea name="textarea" id="textarea" rows="4" cols="6">ABCDEFGHI</textarea></div>
|
||||
<br />
|
||||
<div style="width: 10em; height: 2em; word-wrap: break-word; overflow: auto;" contenteditable="true" id="contenteditable">ABCDEFGHI</div>
|
||||
</body>
|
||||
</html>
|
@ -63,8 +63,7 @@ function isFilenameWithSameDate(aSourceName, aTargetName) {
|
||||
let targetMatches = aTargetName.match(filenamesRegex);
|
||||
|
||||
return sourceMatches && targetMatches &&
|
||||
sourceMatches[1] == targetMatches[1] &&
|
||||
sourceMatches[4] == targetMatches[4];
|
||||
sourceMatches[1] == targetMatches[1];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -438,6 +437,10 @@ this.PlacesBackups = {
|
||||
this._backupFiles.shift();
|
||||
this._entries.shift();
|
||||
newBackupFile = mostRecentBackupFile;
|
||||
// Ensure we retain the proper extension when renaming
|
||||
// the most recent backup file.
|
||||
if (/\.json$/.test(OS.Path.basename(mostRecentBackupFile)))
|
||||
newBackupFilename = this.getFilenameForDate();
|
||||
newFilenameWithMetaData = appendMetaDataToFilename(
|
||||
newBackupFilename,
|
||||
{ count: this.getBookmarkCountForFile(mostRecentBackupFile),
|
||||
|
@ -0,0 +1,103 @@
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/* Bug 1016953 - When a previous bookmark backup exists with the same hash
|
||||
regardless of date, an automatic backup should attempt to either rename it to
|
||||
today's date if the backup was for an old date or leave it alone if it was for
|
||||
the same date. However if the file ext was json it will accidentally rename it
|
||||
to jsonlz4 while keeping the json contents
|
||||
*/
|
||||
|
||||
add_task(function* test_same_date_same_hash() {
|
||||
// If old file has been created on the same date and has the same hash
|
||||
// the file should be left alone
|
||||
let backupFolder = yield PlacesBackups.getBackupFolder();
|
||||
// Save to profile dir to obtain hash and nodeCount to append to filename
|
||||
let tempPath = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
"bug10169583_bookmarks.json");
|
||||
let {count, hash} = yield BookmarkJSONUtils.exportToFile(tempPath);
|
||||
|
||||
// Save JSON file in backup folder with hash appended
|
||||
let dateObj = new Date();
|
||||
let filename = "bookmarks-" + dateObj.toLocaleFormat("%Y-%m-%d") + "_" +
|
||||
count + "_" + hash + ".json";
|
||||
let backupFile = OS.Path.join(backupFolder, filename);
|
||||
yield OS.File.move(tempPath, backupFile);
|
||||
|
||||
// Force a compressed backup which fallbacks to rename
|
||||
yield PlacesBackups.create();
|
||||
let mostRecentBackupFile = yield PlacesBackups.getMostRecentBackup();
|
||||
// check to ensure not renamed to jsonlz4
|
||||
Assert.equal(mostRecentBackupFile, backupFile);
|
||||
// inspect contents and check if valid json
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let result = yield OS.File.read(mostRecentBackupFile);
|
||||
let jsonString = converter.convertFromByteArray(result, result.length);
|
||||
do_log_info("Check is valid JSON");
|
||||
JSON.parse(jsonString);
|
||||
|
||||
// Cleanup
|
||||
yield OS.File.remove(backupFile);
|
||||
yield OS.File.remove(tempPath);
|
||||
PlacesBackups._backupFiles = null; // To force re-cache of backupFiles
|
||||
});
|
||||
|
||||
add_task(function* test_same_date_diff_hash() {
|
||||
// If the old file has been created on the same date, but has a different hash
|
||||
// the existing file should be overwritten with the newer compressed version
|
||||
let backupFolder = yield PlacesBackups.getBackupFolder();
|
||||
let tempPath = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
"bug10169583_bookmarks.json");
|
||||
let {count, hash} = yield BookmarkJSONUtils.exportToFile(tempPath);
|
||||
let dateObj = new Date();
|
||||
let filename = "bookmarks-" + dateObj.toLocaleFormat("%Y-%m-%d") + "_" +
|
||||
count + "_" + "differentHash==" + ".json";
|
||||
let backupFile = OS.Path.join(backupFolder, filename);
|
||||
yield OS.File.move(tempPath, backupFile);
|
||||
yield PlacesBackups.create(); // Force compressed backup
|
||||
mostRecentBackupFile = yield PlacesBackups.getMostRecentBackup();
|
||||
|
||||
// Decode lz4 compressed file to json and check if json is valid
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let result = yield OS.File.read(mostRecentBackupFile, { compression: "lz4" });
|
||||
let jsonString = converter.convertFromByteArray(result, result.length);
|
||||
do_log_info("Check is valid JSON");
|
||||
JSON.parse(jsonString);
|
||||
|
||||
// Cleanup
|
||||
yield OS.File.remove(mostRecentBackupFile);
|
||||
yield OS.File.remove(tempPath);
|
||||
PlacesBackups._backupFiles = null; // To force re-cache of backupFiles
|
||||
});
|
||||
|
||||
add_task(function* test_diff_date_same_hash() {
|
||||
// If the old file has been created on an older day but has the same hash
|
||||
// it should be renamed with today's date without altering the contents.
|
||||
let backupFolder = yield PlacesBackups.getBackupFolder();
|
||||
let tempPath = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
"bug10169583_bookmarks.json");
|
||||
let {count, hash} = yield BookmarkJSONUtils.exportToFile(tempPath);
|
||||
let oldDate = new Date(2014, 1, 1);
|
||||
let curDate = new Date();
|
||||
let oldFilename = "bookmarks-" + oldDate.toLocaleFormat("%Y-%m-%d") + "_" +
|
||||
count + "_" + hash + ".json";
|
||||
let newFilename = "bookmarks-" + curDate.toLocaleFormat("%Y-%m-%d") + "_" +
|
||||
count + "_" + hash + ".json";
|
||||
let backupFile = OS.Path.join(backupFolder, oldFilename);
|
||||
let newBackupFile = OS.Path.join(backupFolder, newFilename);
|
||||
yield OS.File.move(tempPath, backupFile);
|
||||
|
||||
// Ensure file has been renamed correctly
|
||||
yield PlacesBackups.create();
|
||||
let mostRecentBackupFile = yield PlacesBackups.getMostRecentBackup();
|
||||
Assert.equal(mostRecentBackupFile, newBackupFile);
|
||||
|
||||
// Cleanup
|
||||
yield OS.File.remove(mostRecentBackupFile);
|
||||
yield OS.File.remove(tempPath);
|
||||
});
|
@ -33,3 +33,4 @@ tail =
|
||||
[test_818587_compress-bookmarks-backups.js]
|
||||
[test_992901-backup-unsorted-hierarchy.js]
|
||||
[test_997030-bookmarks-html-encode.js]
|
||||
[test_1016953-renaming-uncompressed.js]
|
||||
|
@ -2948,7 +2948,7 @@
|
||||
"n_buckets": 10,
|
||||
"cpp_guard": "ANDROID",
|
||||
"extended_statistics_ok": true,
|
||||
"description": "FENNEC: (Places) Number of favicons stored"
|
||||
"description": "Number of favicons stored in the browser DB"
|
||||
},
|
||||
"FENNEC_THUMBNAILS_COUNT": {
|
||||
"expires_in_version": "never",
|
||||
@ -2957,7 +2957,7 @@
|
||||
"n_buckets": 10,
|
||||
"cpp_guard": "ANDROID",
|
||||
"extended_statistics_ok": true,
|
||||
"description": "FENNEC: (Places) Number of thumbnails stored"
|
||||
"description": "Number of thumbnails stored in the browser DB"
|
||||
},
|
||||
"PLACES_SORTED_BOOKMARKS_PERC": {
|
||||
"expires_in_version": "never",
|
||||
@ -4281,13 +4281,31 @@
|
||||
"description": "Number of history entries in the original XUL places database",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_AWESOMEBAR_ALLPAGES_EMPTY_TIME": {
|
||||
"FENNEC_GLOBALHISTORY_ADD_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 10,
|
||||
"high": "20000",
|
||||
"n_buckets": 20,
|
||||
"description": "Fennec: Time for the Awesomebar Top Sites query to return with no filter set (ms)",
|
||||
"description": "Time for a record to be added to history (ms)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_GLOBALHISTORY_UPDATE_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 10,
|
||||
"high": "20000",
|
||||
"n_buckets": 20,
|
||||
"description": "Time for a record to be updated in history (ms)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_GLOBALHISTORY_VISITED_BUILD_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 10,
|
||||
"high": "20000",
|
||||
"n_buckets": 20,
|
||||
"description": "Time to update the visited link set (ms)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_LOWMEM_TAB_COUNT": {
|
||||
@ -4304,13 +4322,20 @@
|
||||
"description": "Fennec is starting up but the Gecko thread was still running",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_STARTUP_TIME_JAVAUI": {
|
||||
"FENNEC_SEARCH_LOADER_TIME_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 100,
|
||||
"high": "5000",
|
||||
"low": 10,
|
||||
"high": "20000",
|
||||
"n_buckets": 20,
|
||||
"description": "Time for the Java UI to load (ms)",
|
||||
"description": "Time for a URL bar DB search to return (ms)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_STARTUP_GECKOAPP_ACTION": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "enumerated",
|
||||
"n_values": 4,
|
||||
"description": "The way the GeckoApp was launched. (Normal, URL, Prefetch, Redirector)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_STARTUP_TIME_ABOUTHOME": {
|
||||
@ -4331,11 +4356,13 @@
|
||||
"description": "Time for the Gecko:Ready message to arrive (ms)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_STARTUP_GECKOAPP_ACTION": {
|
||||
"FENNEC_STARTUP_TIME_JAVAUI": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "enumerated",
|
||||
"n_values": 4,
|
||||
"description": "The way the GeckoApp was launched. (Normal, URL, Prefetch, Redirector)",
|
||||
"kind": "exponential",
|
||||
"low": 100,
|
||||
"high": "5000",
|
||||
"n_buckets": 20,
|
||||
"description": "Time for the Java UI to load (ms)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_TAB_EXPIRED": {
|
||||
@ -4358,6 +4385,15 @@
|
||||
"description": "How long (in seconds) a tab was inactive when it was OOM-zombified",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_TOPSITES_LOADER_TIME_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 10,
|
||||
"high": "20000",
|
||||
"n_buckets": 20,
|
||||
"description": "Time for the home screen Top Sites query to return with no filter set (ms)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_WAS_KILLED": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "flag",
|
||||
|
@ -27,23 +27,30 @@
|
||||
<content>
|
||||
<xul:autorepeatbutton class="autorepeatbutton-up"
|
||||
anonid="scrollbutton-up"
|
||||
collapsed="true"
|
||||
xbl:inherits="orient"
|
||||
xbl:inherits="orient,collapsed=notoverflowing,disabled=scrolledtostart"
|
||||
oncommand="_autorepeatbuttonScroll(event);"/>
|
||||
<xul:spacer class="arrowscrollbox-overflow-start-indicator"
|
||||
xbl:inherits="collapsed=scrolledtostart"/>
|
||||
<xul:scrollbox class="arrowscrollbox-scrollbox"
|
||||
anonid="scrollbox"
|
||||
flex="1"
|
||||
xbl:inherits="orient,align,pack,dir">
|
||||
<children/>
|
||||
</xul:scrollbox>
|
||||
<xul:spacer class="arrowscrollbox-overflow-end-indicator"
|
||||
xbl:inherits="collapsed=scrolledtoend"/>
|
||||
<xul:autorepeatbutton class="autorepeatbutton-down"
|
||||
anonid="scrollbutton-down"
|
||||
collapsed="true"
|
||||
xbl:inherits="orient"
|
||||
xbl:inherits="orient,collapsed=notoverflowing,disabled=scrolledtoend"
|
||||
oncommand="_autorepeatbuttonScroll(event);"/>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor><![CDATA[
|
||||
this.setAttribute("notoverflowing", "true");
|
||||
this._updateScrollButtonsDisabledState();
|
||||
]]></constructor>
|
||||
|
||||
<destructor><![CDATA[
|
||||
this._stopSmoothScroll();
|
||||
]]></destructor>
|
||||
@ -454,28 +461,39 @@
|
||||
|
||||
<method name="_updateScrollButtonsDisabledState">
|
||||
<body><![CDATA[
|
||||
var disableUpButton = false;
|
||||
var disableDownButton = false;
|
||||
var scrolledToStart = false;
|
||||
var scrolledToEnd = false;
|
||||
|
||||
if (this.scrollPosition == 0) {
|
||||
if (this.hasAttribute("notoverflowing")) {
|
||||
scrolledToStart = true;
|
||||
scrolledToEnd = true;
|
||||
}
|
||||
else if (this.scrollPosition == 0) {
|
||||
// In the RTL case, this means the _last_ element in the
|
||||
// scrollbox is visible
|
||||
if (this._isRTLScrollbox)
|
||||
disableDownButton = true;
|
||||
scrolledToEnd = true;
|
||||
else
|
||||
disableUpButton = true;
|
||||
scrolledToStart = true;
|
||||
}
|
||||
else if (this.scrollClientSize + this.scrollPosition == this.scrollSize) {
|
||||
// In the RTL case, this means the _first_ element in the
|
||||
// scrollbox is visible
|
||||
if (this._isRTLScrollbox)
|
||||
disableUpButton = true;
|
||||
scrolledToStart = true;
|
||||
else
|
||||
disableDownButton = true;
|
||||
scrolledToEnd = true;
|
||||
}
|
||||
|
||||
this._scrollButtonUp.disabled = disableUpButton;
|
||||
this._scrollButtonDown.disabled = disableDownButton;
|
||||
if (scrolledToEnd)
|
||||
this.setAttribute("scrolledtoend", "true");
|
||||
else
|
||||
this.removeAttribute("scrolledtoend");
|
||||
|
||||
if (scrolledToStart)
|
||||
this.setAttribute("scrolledtostart", "true");
|
||||
else
|
||||
this.removeAttribute("scrolledtostart");
|
||||
]]></body>
|
||||
</method>
|
||||
</implementation>
|
||||
@ -535,8 +553,8 @@
|
||||
return;
|
||||
}
|
||||
|
||||
this._scrollButtonUp.collapsed = true;
|
||||
this._scrollButtonDown.collapsed = true;
|
||||
this.setAttribute("notoverflowing", "true");
|
||||
|
||||
try {
|
||||
// See bug 341047 and comments in overflow handler as to why
|
||||
// try..catch is needed here
|
||||
@ -545,8 +563,7 @@
|
||||
this.ensureElementIsVisible(childNodes[0], false);
|
||||
}
|
||||
catch(e) {
|
||||
this._scrollButtonUp.collapsed = false;
|
||||
this._scrollButtonDown.collapsed = false;
|
||||
this.removeAttribute("notoverflowing");
|
||||
}
|
||||
]]></handler>
|
||||
|
||||
@ -569,20 +586,18 @@
|
||||
return;
|
||||
}
|
||||
|
||||
this._scrollButtonUp.collapsed = false;
|
||||
this._scrollButtonDown.collapsed = false;
|
||||
this.removeAttribute("notoverflowing");
|
||||
|
||||
try {
|
||||
// See bug 341047, the overflow event is dispatched when the
|
||||
// scrollbox already is mostly destroyed. This causes some code in
|
||||
// _updateScrollButtonsDisabledState() to throw an error. It also
|
||||
// means that the scrollbarbuttons were uncollapsed when that should
|
||||
// not be happening, because the whole overflow event should not be
|
||||
// happening in that case.
|
||||
// means that the notoverflowing attribute was removed erroneously,
|
||||
// as the whole overflow event should not be happening in that case.
|
||||
this._updateScrollButtonsDisabledState();
|
||||
}
|
||||
catch(e) {
|
||||
this._scrollButtonUp.collapsed = true;
|
||||
this._scrollButtonDown.collapsed = true;
|
||||
this.setAttribute("notoverflowing", "true");
|
||||
}
|
||||
]]></handler>
|
||||
|
||||
@ -598,22 +613,26 @@
|
||||
|
||||
<binding id="arrowscrollbox-clicktoscroll" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox">
|
||||
<content>
|
||||
<xul:toolbarbutton class="scrollbutton-up" collapsed="true"
|
||||
xbl:inherits="orient"
|
||||
<xul:toolbarbutton class="scrollbutton-up"
|
||||
xbl:inherits="orient,collapsed=notoverflowing,disabled=scrolledtostart"
|
||||
anonid="scrollbutton-up"
|
||||
onclick="_distanceScroll(event);"
|
||||
onmousedown="if (event.button == 0) _startScroll(-1);"
|
||||
onmouseup="if (event.button == 0) _stopScroll();"
|
||||
onmouseover="_continueScroll(-1);"
|
||||
onmouseout="_pauseScroll();"/>
|
||||
<xul:spacer class="arrowscrollbox-overflow-start-indicator"
|
||||
xbl:inherits="collapsed=scrolledtostart"/>
|
||||
<xul:scrollbox class="arrowscrollbox-scrollbox"
|
||||
anonid="scrollbox"
|
||||
flex="1"
|
||||
xbl:inherits="orient,align,pack,dir">
|
||||
<children/>
|
||||
</xul:scrollbox>
|
||||
<xul:toolbarbutton class="scrollbutton-down" collapsed="true"
|
||||
xbl:inherits="orient"
|
||||
<xul:spacer class="arrowscrollbox-overflow-end-indicator"
|
||||
xbl:inherits="collapsed=scrolledtoend"/>
|
||||
<xul:toolbarbutton class="scrollbutton-down"
|
||||
xbl:inherits="orient,collapsed=notoverflowing,disabled=scrolledtoend"
|
||||
anonid="scrollbutton-down"
|
||||
onclick="_distanceScroll(event);"
|
||||
onmousedown="if (event.button == 0) _startScroll(1);"
|
||||
|
@ -290,6 +290,12 @@ exports.hasSafeGetter = function hasSafeGetter(aDesc) {
|
||||
* True if it is safe to read properties from aObj, or false otherwise.
|
||||
*/
|
||||
exports.isSafeJSObject = function isSafeJSObject(aObj) {
|
||||
// If we are running on a worker thread, Cu is not available. In this case,
|
||||
// we always return false, just to be on the safe side.
|
||||
if (!Cu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Cu.getGlobalForObject(aObj) ==
|
||||
Cu.getGlobalForObject(exports.isSafeJSObject)) {
|
||||
return true; // aObj is not a cross-compartment wrapper.
|
||||
|
@ -6,19 +6,23 @@
|
||||
* EventEmitter.
|
||||
*/
|
||||
|
||||
(function (factory) { // Module boilerplate
|
||||
if (this.module && module.id.indexOf("event-emitter") >= 0) { // require
|
||||
factory.call(this, require, exports, module);
|
||||
} else { // Cu.import
|
||||
const Cu = Components.utils;
|
||||
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
this.promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
|
||||
factory.call(this, devtools.require, this, { exports: this });
|
||||
this.EXPORTED_SYMBOLS = ["EventEmitter"];
|
||||
}
|
||||
}).call(this, function (require, exports, module) {
|
||||
|
||||
this.EventEmitter = function EventEmitter() {};
|
||||
module.exports = EventEmitter;
|
||||
|
||||
if (typeof(require) === "function") {
|
||||
module.exports = EventEmitter;
|
||||
var {Cu, components} = require("chrome");
|
||||
} else {
|
||||
var EXPORTED_SYMBOLS = ["EventEmitter"];
|
||||
var Cu = this["Components"].utils;
|
||||
var components = Components;
|
||||
}
|
||||
|
||||
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm");
|
||||
const { Cu, components } = require("chrome");
|
||||
const Services = require("Services");
|
||||
|
||||
/**
|
||||
* Decorate an object with event emitter functionality.
|
||||
@ -190,3 +194,5 @@ EventEmitter.prototype = {
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
});
|
||||
|
@ -54,18 +54,22 @@ let FramerateActor = exports.FramerateActor = protocol.ActorClass({
|
||||
/**
|
||||
* Stops monitoring framerate, returning the recorded values.
|
||||
*/
|
||||
stopRecording: method(function() {
|
||||
stopRecording: method(function(beginAt = 0, endAt = Number.MAX_SAFE_INTEGER) {
|
||||
if (!this._recording) {
|
||||
return [];
|
||||
}
|
||||
this._recording = false;
|
||||
|
||||
// We don't need to store the ticks array for future use, release it.
|
||||
let ticks = this._ticks;
|
||||
let ticks = this._ticks.filter(e => e >= beginAt && e <= endAt);
|
||||
this._ticks = null;
|
||||
return ticks;
|
||||
}, {
|
||||
response: { timeline: RetVal("array:number") }
|
||||
request: {
|
||||
beginAt: Arg(0, "nullable:number"),
|
||||
endAt: Arg(1, "nullable:number")
|
||||
},
|
||||
response: { ticks: RetVal("array:number") }
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -935,9 +935,12 @@ var WalkerActor = protocol.ActorClass({
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._hoveredNode = null;
|
||||
this._destroyed = true;
|
||||
|
||||
this.clearPseudoClassLocks();
|
||||
this._activePseudoClassLocks = null;
|
||||
|
||||
this._hoveredNode = null;
|
||||
this.rootDoc = null;
|
||||
|
||||
this.reflowObserver.off("reflows", this._onReflows);
|
||||
@ -1711,13 +1714,14 @@ var WalkerActor = protocol.ActorClass({
|
||||
if (!node.writePseudoClassLocks()) {
|
||||
this._activePseudoClassLocks.delete(node);
|
||||
}
|
||||
|
||||
this._queuePseudoClassMutation(node);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear all the pseudo-classes on a given node
|
||||
* or all nodes.
|
||||
* Clear all the pseudo-classes on a given node or all nodes.
|
||||
* @param {NodeActor} node Optional node to clear pseudo-classes on
|
||||
*/
|
||||
clearPseudoClassLocks: method(function(node) {
|
||||
if (node) {
|
||||
@ -1929,7 +1933,7 @@ var WalkerActor = protocol.ActorClass({
|
||||
}),
|
||||
|
||||
queueMutation: function(mutation) {
|
||||
if (!this.actorID) {
|
||||
if (!this.actorID || this._destroyed) {
|
||||
// We've been destroyed, don't bother queueing this mutation.
|
||||
return;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ support-files =
|
||||
[test_device.html]
|
||||
[test_framerate_01.html]
|
||||
[test_framerate_02.html]
|
||||
[test_framerate_03.html]
|
||||
[test_inspector-changeattrs.html]
|
||||
[test_inspector-changevalue.html]
|
||||
[test_inspector-hide.html]
|
||||
|
@ -0,0 +1,81 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Bug 1023018 - Tests whether or not the framerate actor can handle time ranges.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Framerate actor test</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>
|
||||
|
||||
window.onload = function() {
|
||||
var Cu = Components.utils;
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Always log packets when running tests.
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
SimpleTest.registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("devtools.debugger.log");
|
||||
});
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/Loader.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var {FramerateFront} = devtools.require("devtools/server/actors/framerate");
|
||||
var START_TICK = 2000;
|
||||
var STOP_TICK = 3000;
|
||||
var TOTAL_TIME = 5000;
|
||||
|
||||
DebuggerServer.init(function () { return true; });
|
||||
DebuggerServer.addBrowserActors();
|
||||
|
||||
var client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
client.connect(function onConnect() {
|
||||
client.listTabs(function onListTabs(aResponse) {
|
||||
var form = aResponse.tabs[aResponse.selected];
|
||||
var front = FramerateFront(client, form);
|
||||
|
||||
front.startRecording().then(() => {
|
||||
window.setTimeout(() => {
|
||||
front.stopRecording(START_TICK, STOP_TICK).then(rawData => {
|
||||
onRecordingStopped(front, rawData);
|
||||
});
|
||||
}, TOTAL_TIME);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function onRecordingStopped(front, rawData) {
|
||||
ok(rawData, "There should be a recording available.");
|
||||
|
||||
ok(!rawData.find(e => e < START_TICK),
|
||||
"There should be no tick before 2000ms.");
|
||||
ok(!rawData.find(e => e > STOP_TICK),
|
||||
"There should be no tick after 3000ms.");
|
||||
|
||||
for (var tick of rawData) {
|
||||
info("Testing tick: " + tick);
|
||||
is(typeof tick, "number", "All values should be numbers.");
|
||||
}
|
||||
|
||||
client.close(() => {
|
||||
DebuggerServer.destroy();
|
||||
SimpleTest.finish()
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -4,23 +4,32 @@
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
var gCallback;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-grips");
|
||||
run_test_with_server(DebuggerServer, function () {
|
||||
run_test_with_server(WorkerDebuggerServer, do_test_finished);
|
||||
});
|
||||
do_test_pending();
|
||||
};
|
||||
|
||||
function run_test_with_server(aServer, aCallback)
|
||||
{
|
||||
gCallback = aCallback;
|
||||
initTestDebuggerServer(aServer);
|
||||
gDebuggee = addTestGlobal("test-grips", aServer);
|
||||
gDebuggee.eval(function stopMe(arg1) {
|
||||
debugger;
|
||||
}.toString());
|
||||
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient = new DebuggerClient(aServer.connectPipe());
|
||||
gClient.connect(function() {
|
||||
attachTestTabAndResume(gClient, "test-grips", function(aResponse, aTabClient, aThreadClient) {
|
||||
gThreadClient = aThreadClient;
|
||||
test_object_grip();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_object_grip()
|
||||
@ -38,7 +47,7 @@ function test_object_grip()
|
||||
do_check_eq(aResponse.ownPropertyNames[2], "c");
|
||||
|
||||
gThreadClient.resume(function() {
|
||||
finishClient(gClient);
|
||||
gClient.close(gCallback);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -4,23 +4,32 @@
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
var gCallback;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-grips");
|
||||
run_test_with_server(DebuggerServer, function () {
|
||||
run_test_with_server(WorkerDebuggerServer, do_test_finished);
|
||||
});
|
||||
do_test_pending();
|
||||
};
|
||||
|
||||
function run_test_with_server(aServer, aCallback)
|
||||
{
|
||||
gCallback = aCallback;
|
||||
initTestDebuggerServer(aServer);
|
||||
gDebuggee = addTestGlobal("test-grips", aServer);
|
||||
gDebuggee.eval(function stopMe(arg1) {
|
||||
debugger;
|
||||
}.toString());
|
||||
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient = new DebuggerClient(aServer.connectPipe());
|
||||
gClient.connect(function() {
|
||||
attachTestTabAndResume(gClient, "test-grips", function(aResponse, aTabClient, aThreadClient) {
|
||||
gThreadClient = aThreadClient;
|
||||
test_object_grip();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_object_grip()
|
||||
@ -41,7 +50,7 @@ function test_object_grip()
|
||||
do_check_eq(aResponse.ownPropertyNames[1], "c");
|
||||
|
||||
gThreadClient.resume(function() {
|
||||
finishClient(gClient);
|
||||
gClient.close(gCallback);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,23 +4,32 @@
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
var gCallback;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-grips");
|
||||
run_test_with_server(DebuggerServer, function () {
|
||||
run_test_with_server(WorkerDebuggerServer, do_test_finished);
|
||||
});
|
||||
do_test_pending();
|
||||
};
|
||||
|
||||
function run_test_with_server(aServer, aCallback)
|
||||
{
|
||||
gCallback = aCallback;
|
||||
initTestDebuggerServer(aServer);
|
||||
gDebuggee = addTestGlobal("test-grips", aServer);
|
||||
gDebuggee.eval(function stopMe(arg1) {
|
||||
debugger;
|
||||
}.toString());
|
||||
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient = new DebuggerClient(aServer.connectPipe());
|
||||
gClient.connect(function() {
|
||||
attachTestTabAndResume(gClient, "test-grips", function(aResponse, aTabClient, aThreadClient) {
|
||||
gThreadClient = aThreadClient;
|
||||
test_object_grip();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_object_grip()
|
||||
@ -51,7 +60,7 @@ function test_object_grip()
|
||||
do_check_eq(aResponse.descriptor.set.type, "undefined");
|
||||
|
||||
gThreadClient.resume(function() {
|
||||
finishClient(gClient);
|
||||
gClient.close(gCallback);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,23 +4,32 @@
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
var gCallback;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-grips");
|
||||
run_test_with_server(DebuggerServer, function () {
|
||||
run_test_with_server(WorkerDebuggerServer, do_test_finished);
|
||||
});
|
||||
do_test_pending();
|
||||
};
|
||||
|
||||
function run_test_with_server(aServer, aCallback)
|
||||
{
|
||||
gCallback = aCallback;
|
||||
initTestDebuggerServer(aServer);
|
||||
gDebuggee = addTestGlobal("test-grips", aServer);
|
||||
gDebuggee.eval(function stopMe(arg1) {
|
||||
debugger;
|
||||
}.toString());
|
||||
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient = new DebuggerClient(aServer.connectPipe());
|
||||
gClient.connect(function() {
|
||||
attachTestTabAndResume(gClient, "test-grips", function(aResponse, aTabClient, aThreadClient) {
|
||||
gThreadClient = aThreadClient;
|
||||
test_object_grip();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_object_grip()
|
||||
@ -55,7 +64,7 @@ function test_object_grip()
|
||||
do_check_true(aResponse.ownPropertyNames.toString != undefined);
|
||||
|
||||
gThreadClient.resume(function() {
|
||||
finishClient(gClient);
|
||||
gClient.close(gCallback);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -9,23 +9,32 @@
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
var gCallback;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-grips");
|
||||
run_test_with_server(DebuggerServer, function () {
|
||||
run_test_with_server(WorkerDebuggerServer, do_test_finished);
|
||||
});
|
||||
do_test_pending();
|
||||
};
|
||||
|
||||
function run_test_with_server(aServer, aCallback)
|
||||
{
|
||||
gCallback = aCallback;
|
||||
initTestDebuggerServer(aServer);
|
||||
gDebuggee = addTestGlobal("test-grips", aServer);
|
||||
gDebuggee.eval(function stopMe(arg1, arg2) {
|
||||
debugger;
|
||||
}.toString());
|
||||
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient = new DebuggerClient(aServer.connectPipe());
|
||||
gClient.connect(function() {
|
||||
attachTestTabAndResume(gClient, "test-grips", function(aResponse, aTabClient, aThreadClient) {
|
||||
gThreadClient = aThreadClient;
|
||||
test_object_grip();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_object_grip()
|
||||
@ -44,7 +53,7 @@ function test_object_grip()
|
||||
do_check_false(obj2Client.isFrozen);
|
||||
|
||||
gThreadClient.resume(_ => {
|
||||
finishClient(gClient);
|
||||
gClient.close(gCallback);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -9,23 +9,32 @@
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
var gCallback;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-grips");
|
||||
run_test_with_server(DebuggerServer, function () {
|
||||
run_test_with_server(WorkerDebuggerServer, do_test_finished);
|
||||
});
|
||||
do_test_pending();
|
||||
};
|
||||
|
||||
function run_test_with_server(aServer, aCallback)
|
||||
{
|
||||
gCallback = aCallback;
|
||||
initTestDebuggerServer(aServer);
|
||||
gDebuggee = addTestGlobal("test-grips", aServer);
|
||||
gDebuggee.eval(function stopMe(arg1, arg2) {
|
||||
debugger;
|
||||
}.toString());
|
||||
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient = new DebuggerClient(aServer.connectPipe());
|
||||
gClient.connect(function() {
|
||||
attachTestTabAndResume(gClient, "test-grips", function(aResponse, aTabClient, aThreadClient) {
|
||||
gThreadClient = aThreadClient;
|
||||
test_object_grip();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_object_grip()
|
||||
@ -44,7 +53,7 @@ function test_object_grip()
|
||||
do_check_false(obj2Client.isSealed);
|
||||
|
||||
gThreadClient.resume(_ => {
|
||||
finishClient(gClient);
|
||||
gClient.close(gCallback);
|
||||
});
|
||||
});
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user