зеркало из https://github.com/mozilla/gecko-dev.git
merge fx-team to mozilla-central a=merge
--HG-- extra : amend_source : 6b1927dd9d7d8eedfc694b66e8aceb7c3fc24935
This commit is contained in:
Коммит
8248ac6196
|
@ -782,7 +782,7 @@ let CustomizableUIInternal = {
|
|||
}
|
||||
|
||||
if (!widgetNode || !container.contains(widgetNode)) {
|
||||
INFO("Widget not found, unable to remove");
|
||||
INFO("Widget " + aWidgetId + " not found, unable to remove from " + aArea);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ support-files =
|
|||
doc_simple-canvas-bitmasks.html
|
||||
doc_simple-canvas-deep-stack.html
|
||||
doc_simple-canvas-transparent.html
|
||||
doc_webgl-enum.html
|
||||
head.js
|
||||
|
||||
[browser_canvas-actor-test-01.js]
|
||||
|
@ -15,6 +16,7 @@ support-files =
|
|||
[browser_canvas-actor-test-06.js]
|
||||
[browser_canvas-actor-test-07.js]
|
||||
[browser_canvas-actor-test-08.js]
|
||||
[browser_canvas-actor-test-09.js]
|
||||
[browser_canvas-frontend-call-highlight.js]
|
||||
[browser_canvas-frontend-call-list.js]
|
||||
[browser_canvas-frontend-call-search.js]
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that integers used in arguments are not cast to their constant, enum value
|
||||
* forms if the method's signature does not expect an enum. Bug 999687.
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCanavsDebuggerBackend(WEBGL_ENUM_URL);
|
||||
|
||||
let navigated = once(target, "navigate");
|
||||
|
||||
yield front.setup({ reload: true });
|
||||
ok(true, "The front was setup up successfully.");
|
||||
|
||||
yield navigated;
|
||||
ok(true, "Target automatically navigated when the front was set up.");
|
||||
|
||||
let snapshotActor = yield front.recordAnimationFrame();
|
||||
let animationOverview = yield snapshotActor.getOverview();
|
||||
let functionCalls = animationOverview.calls;
|
||||
|
||||
is(functionCalls[0].name, "clear",
|
||||
"The function's name is correct.");
|
||||
is(functionCalls[0].argsPreview, "DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT",
|
||||
"The bits passed into `gl.clear` have been cast to their enum values.");
|
||||
|
||||
yield removeTab(target.tab);
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>WebGL editor test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="canvas" width="128" height="128"></canvas>
|
||||
|
||||
<script type="text/javascript;version=1.8">
|
||||
"use strict";
|
||||
|
||||
let canvas, gl;
|
||||
|
||||
window.onload = function() {
|
||||
canvas = document.querySelector("canvas");
|
||||
gl = canvas.getContext("webgl", { preserveDrawingBuffer: true });
|
||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
drawScene();
|
||||
}
|
||||
|
||||
function drawScene() {
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
||||
window.requestAnimationFrame(drawScene);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -29,6 +29,7 @@ const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html";
|
|||
const SIMPLE_BITMASKS_URL = EXAMPLE_URL + "doc_simple-canvas-bitmasks.html";
|
||||
const SIMPLE_CANVAS_TRANSPARENT_URL = EXAMPLE_URL + "doc_simple-canvas-transparent.html";
|
||||
const SIMPLE_CANVAS_DEEP_STACK_URL = EXAMPLE_URL + "doc_simple-canvas-deep-stack.html";
|
||||
const WEBGL_ENUM_URL = EXAMPLE_URL + "doc_webgl-enum.html";
|
||||
|
||||
// All tests are asynchronous.
|
||||
waitForExplicitFinish();
|
||||
|
|
|
@ -398,6 +398,8 @@ HTMLBreadcrumbs.prototype = {
|
|||
this.separators = null;
|
||||
|
||||
this.nodeHierarchy = null;
|
||||
|
||||
this.isDestroyed = true;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -625,6 +627,10 @@ HTMLBreadcrumbs.prototype = {
|
|||
|
||||
updateSelectors: function BC_updateSelectors()
|
||||
{
|
||||
if (this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = this.nodeHierarchy.length - 1; i >= 0; i--) {
|
||||
let crumb = this.nodeHierarchy[i];
|
||||
let button = crumb.button;
|
||||
|
@ -642,6 +648,10 @@ HTMLBreadcrumbs.prototype = {
|
|||
*/
|
||||
update: function BC_update(reason)
|
||||
{
|
||||
if (this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reason !== "markupmutation") {
|
||||
this.inspector.hideNodeMenu();
|
||||
}
|
||||
|
@ -688,6 +698,10 @@ HTMLBreadcrumbs.prototype = {
|
|||
let doneUpdating = this.inspector.updating("breadcrumbs");
|
||||
// Add the first child of the very last node of the breadcrumbs if possible.
|
||||
this.ensureFirstChild().then(this.selectionGuard()).then(() => {
|
||||
if (this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateSelectors();
|
||||
|
||||
// Make sure the selected node and its neighbours are visible.
|
||||
|
|
|
@ -1,78 +1,69 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
"use strict";
|
||||
|
||||
let DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||
// Test that the inspector has the correct pseudo-class locking menu items and
|
||||
// that these items actually work
|
||||
|
||||
let pseudos = ["hover", "active", "focus"];
|
||||
const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||
const PSEUDOS = ["hover", "active", "focus"];
|
||||
|
||||
let doc;
|
||||
let div;
|
||||
let menu;
|
||||
let inspector;
|
||||
let test = asyncTest(function*() {
|
||||
yield addTab("data:text/html,pseudo-class lock node menu tests");
|
||||
|
||||
ignoreAllUncaughtExceptions();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
doc = content.document;
|
||||
waitForFocus(createDocument, content);
|
||||
info("Creating the test element");
|
||||
let div = content.document.createElement("div");
|
||||
div.textContent = "test div";
|
||||
content.document.body.appendChild(div);
|
||||
|
||||
let {inspector} = yield openInspector();
|
||||
yield selectNode(div, inspector);
|
||||
|
||||
info("Getting the inspector ctx menu and opening it");
|
||||
let menu = inspector.panelDoc.getElementById("inspector-node-popup");
|
||||
yield openMenu(menu);
|
||||
|
||||
yield testMenuItems(div, menu, inspector);
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
function openMenu(menu) {
|
||||
let def = promise.defer();
|
||||
|
||||
menu.addEventListener("popupshowing", function onOpen() {
|
||||
menu.removeEventListener("popupshowing", onOpen, true);
|
||||
def.resolve();
|
||||
}, true);
|
||||
menu.openPopup();
|
||||
|
||||
content.location = "data:text/html,pseudo-class lock node menu tests";
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
function createDocument()
|
||||
{
|
||||
div = doc.createElement("div");
|
||||
div.textContent = "test div";
|
||||
function* testMenuItems(div,menu, inspector) {
|
||||
for (let pseudo of PSEUDOS) {
|
||||
let menuitem = inspector.panelDoc.getElementById("node-menu-pseudo-" + pseudo);
|
||||
ok(menuitem, ":" + pseudo + " menuitem exists");
|
||||
|
||||
doc.body.appendChild(div);
|
||||
// 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");
|
||||
let onMutations = waitForMutation(inspector);
|
||||
|
||||
openInspector(selectNode);
|
||||
}
|
||||
menuitem.doCommand();
|
||||
|
||||
function selectNode(aInspector)
|
||||
{
|
||||
inspector = aInspector;
|
||||
inspector.selection.setNode(div);
|
||||
inspector.once("inspector-updated", performTests);
|
||||
}
|
||||
yield onPseudo;
|
||||
yield onRefresh;
|
||||
yield onMutations;
|
||||
|
||||
function performTests()
|
||||
{
|
||||
menu = inspector.panelDoc.getElementById("inspector-node-popup");
|
||||
menu.addEventListener("popupshowing", testMenuItems, true);
|
||||
menu.openPopup();
|
||||
}
|
||||
|
||||
function testMenuItems()
|
||||
{
|
||||
menu.removeEventListener("popupshowing", testMenuItems, true);
|
||||
|
||||
var tryNext = () => {
|
||||
if (pseudos.length === 0) {
|
||||
finishUp();
|
||||
return;
|
||||
}
|
||||
let pseudo = pseudos.shift();
|
||||
|
||||
let menuitem = inspector.panelDoc.getElementById("node-menu-pseudo-" + pseudo);
|
||||
ok(menuitem, ":" + pseudo + " menuitem exists");
|
||||
|
||||
menuitem.doCommand();
|
||||
inspector.selection.once("pseudoclass", () => {
|
||||
is(DOMUtils.hasPseudoClassLock(div, ":" + pseudo), true,
|
||||
"pseudo-class lock has been applied");
|
||||
tryNext();
|
||||
});
|
||||
}
|
||||
tryNext();
|
||||
}
|
||||
|
||||
function finishUp()
|
||||
{
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(DOMUtils.hasPseudoClassLock(div, ":" + pseudo), true,
|
||||
"pseudo-class lock has been applied");
|
||||
}
|
||||
}
|
||||
|
||||
function waitForMutation(inspector) {
|
||||
let def = promise.defer();
|
||||
inspector.walker.once("mutations", def.resolve);
|
||||
return def.promise;
|
||||
}
|
||||
|
|
|
@ -16,38 +16,31 @@ const TEST_URL = 'data:text/html,' +
|
|||
' </div>' +
|
||||
'</body>';
|
||||
|
||||
function test() {
|
||||
ignoreAllUncaughtExceptions();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
waitForFocus(startTests, content);
|
||||
}, true);
|
||||
|
||||
content.location = TEST_URL;
|
||||
}
|
||||
|
||||
let startTests = Task.async(function*() {
|
||||
let test = asyncTest(function*() {
|
||||
info("Creating the test tab and opening the rule-view");
|
||||
yield addTab(TEST_URL);
|
||||
let {toolbox, inspector, view} = yield openRuleView();
|
||||
|
||||
info("Selecting the test node");
|
||||
yield selectNode("#div-1", inspector);
|
||||
|
||||
yield performTests(inspector, view);
|
||||
|
||||
yield finishUp(toolbox);
|
||||
finish();
|
||||
});
|
||||
|
||||
function* performTests(inspector, ruleview) {
|
||||
yield togglePseudoClass(inspector);
|
||||
yield assertPseudoAddedToNode(inspector, ruleview);
|
||||
yield assertPseudoAddedToNode(inspector, view);
|
||||
|
||||
yield togglePseudoClass(inspector);
|
||||
yield assertPseudoRemovedFromNode();
|
||||
yield assertPseudoRemovedFromView(inspector, ruleview);
|
||||
yield assertPseudoRemovedFromView(inspector, view);
|
||||
|
||||
yield togglePseudoClass(inspector);
|
||||
yield testNavigate(inspector, ruleview);
|
||||
}
|
||||
yield testNavigate(inspector, view);
|
||||
|
||||
info("Destroying the toolbox");
|
||||
yield toolbox.destroy();
|
||||
yield assertPseudoRemovedFromNode(getNode("#div-1"));
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
||||
function* togglePseudoClass(inspector) {
|
||||
info("Toggle the pseudoclass, wait for it to be applied");
|
||||
|
@ -139,12 +132,3 @@ function* assertPseudoRemovedFromView(inspector, ruleview) {
|
|||
is(pseudoClassesBox.textContent, "", "pseudo-class removed from infobar selector");
|
||||
yield inspector.toolbox.highlighter.hideBoxModel();
|
||||
}
|
||||
|
||||
function* finishUp(toolbox) {
|
||||
let onDestroy = gDevTools.once("toolbox-destroyed");
|
||||
toolbox.destroy();
|
||||
yield onDestroy;
|
||||
|
||||
yield assertPseudoRemovedFromNode(getNode("#div-1"));
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
|
|
|
@ -218,7 +218,7 @@ SyntaxTreesPool.prototype = {
|
|||
// Can't guarantee that the tree traversal logic is forever perfect :)
|
||||
// Language features may be added, in which case the recursive methods
|
||||
// need to be updated. If an exception is thrown here, file a bug.
|
||||
DevToolsUtils.reportException("Syntax tree visitor for " + aUrl, e);
|
||||
DevToolsUtils.reportException("Syntax tree visitor for " + this._url, e);
|
||||
}
|
||||
}
|
||||
this._cache.set(requestId, results);
|
||||
|
|
|
@ -195,11 +195,13 @@ function Tooltip(doc, options) {
|
|||
// Listen to keypress events to close the tooltip if configured to do so
|
||||
let win = this.doc.querySelector("window");
|
||||
this._onKeyPress = event => {
|
||||
if (this.panel.hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit("keypress", event.keyCode);
|
||||
if (this.options.get("closeOnKeys").indexOf(event.keyCode) !== -1) {
|
||||
if (!this.panel.hidden) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
event.stopPropagation();
|
||||
this.hide();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -225,7 +225,8 @@ StyleSheetEditor.prototype = {
|
|||
fetchSource: function(callback) {
|
||||
return this.styleSheet.getText().then((longStr) => {
|
||||
longStr.string().then((source) => {
|
||||
this._state.text = CssLogic.prettifyCSS(source);
|
||||
let ruleCount = this.styleSheet.ruleCount;
|
||||
this._state.text = CssLogic.prettifyCSS(source, ruleCount);
|
||||
this.sourceLoaded = true;
|
||||
|
||||
if (callback) {
|
||||
|
|
|
@ -23,7 +23,7 @@ let editorTestedCount = 0;
|
|||
function testEditor(aEditor)
|
||||
{
|
||||
if (aEditor.styleSheet.styleSheetIndex == 0) {
|
||||
let prettifiedSource = "body\{\r?\n\tbackground\:white;\r?\n\}\r?\n\r?\ndiv\{\r?\n\tfont\-size\:4em;\r?\n\tcolor\:red\r?\n\}\r?\n";
|
||||
let prettifiedSource = "body\{\r?\n\tbackground\:white;\r?\n\}\r?\n\r?\ndiv\{\r?\n\tfont\-size\:4em;\r?\n\tcolor\:red\r?\n\}\r?\n\r?\nspan\{\r?\n\tcolor\:green;\r?\n\}\r?\n";
|
||||
let prettifiedSourceRE = new RegExp(prettifiedSource);
|
||||
|
||||
ok(prettifiedSourceRE.test(aEditor.sourceEditor.getText()),
|
||||
|
|
|
@ -1,5 +1,2 @@
|
|||
|
||||
|
||||
body{background:white;}div{font-size:4em;color:red}
|
||||
|
||||
|
||||
body{background:white;}div{font-size:4em;color:red}span{color:green;}
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
"use strict";
|
||||
|
||||
// Testing various inplace-editor behaviors in the rule-view
|
||||
// FIXME: To be split in several test files, and some of the inplace-editor
|
||||
// focus/blur/commit/revert stuff should be factored out in head.js
|
||||
|
||||
let TEST_URL = 'url("' + TEST_URL_ROOT + 'doc_test_image.png")';
|
||||
let PAGE_CONTENT = [
|
||||
|
@ -14,11 +12,8 @@ let PAGE_CONTENT = [
|
|||
' #testid {',
|
||||
' background-color: blue;',
|
||||
' }',
|
||||
' .testclass {',
|
||||
' background-color: green;',
|
||||
' }',
|
||||
'</style>',
|
||||
'<div id="testid" class="testclass" style="background-color:red;">Styled Node</div>'
|
||||
'<div id="testid">Styled Node</div>'
|
||||
].join("\n");
|
||||
|
||||
let test = asyncTest(function*() {
|
||||
|
@ -33,13 +28,9 @@ let test = asyncTest(function*() {
|
|||
info("Selecting the test element");
|
||||
yield selectNode("#testid", inspector);
|
||||
|
||||
yield testCreateNewEscape(view);
|
||||
});
|
||||
|
||||
function* testCreateNewEscape(view) {
|
||||
info("Test creating a new property and escaping");
|
||||
|
||||
let elementRuleEditor = getRuleViewRuleEditor(view, 0);
|
||||
let elementRuleEditor = getRuleViewRuleEditor(view, 1);
|
||||
|
||||
info("Focusing a new property name in the rule-view");
|
||||
let editor = yield focusEditableField(elementRuleEditor.closeBrace);
|
||||
|
@ -48,7 +39,9 @@ function* testCreateNewEscape(view) {
|
|||
let input = editor.input;
|
||||
|
||||
info("Entering a value in the property name editor");
|
||||
let onModifications = elementRuleEditor.rule._applyingModifications;
|
||||
input.value = "color";
|
||||
yield onModifications;
|
||||
|
||||
info("Pressing return to commit and focus the new value field");
|
||||
let onValueFocus = once(elementRuleEditor.element, "focus", true);
|
||||
|
@ -69,7 +62,9 @@ function* testCreateNewEscape(view) {
|
|||
editor.input.value = "red";
|
||||
|
||||
info("Escaping out of the field");
|
||||
let onModifications = elementRuleEditor.rule._applyingModifications;
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
|
||||
yield onModifications;
|
||||
|
||||
info("Checking that the previous field is focused");
|
||||
let focusedElement = inplaceEditor(elementRuleEditor.rule.textProps[0].editor.valueSpan).input;
|
||||
|
@ -81,4 +76,4 @@ function* testCreateNewEscape(view) {
|
|||
|
||||
is(elementRuleEditor.rule.textProps.length, 1, "Removed the new text property.");
|
||||
is(elementRuleEditor.propertyList.children.length, 1, "Removed the property editor.");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* Tests that SVG nodes and edges were created for the Graph View.
|
||||
*/
|
||||
|
||||
let connectCount = 0;
|
||||
|
||||
function spawnTest() {
|
||||
let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
|
||||
let { panelWin } = panel;
|
||||
|
@ -14,6 +16,8 @@ function spawnTest() {
|
|||
|
||||
reload(target);
|
||||
|
||||
panelWin.on(EVENTS.CONNECT_NODE, onConnectNode);
|
||||
|
||||
let [actors] = yield Promise.all([
|
||||
get3(gFront, "create-node"),
|
||||
waitForGraphRendered(panelWin, 3, 2)
|
||||
|
@ -27,7 +31,16 @@ function spawnTest() {
|
|||
is(findGraphEdge(panelWin, oscId, gainId).toString(), "[object SVGGElement]", "found edge for osc -> gain");
|
||||
is(findGraphEdge(panelWin, gainId, destId).toString(), "[object SVGGElement]", "found edge for gain -> dest");
|
||||
|
||||
yield wait(1000);
|
||||
|
||||
is(connectCount, 2, "Only two node connect events should be fired.");
|
||||
|
||||
panelWin.off(EVENTS.CONNECT_NODE, onConnectNode);
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
||||
function onConnectNode () {
|
||||
++connectCount;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,15 @@
|
|||
let osc = ctx.createOscillator();
|
||||
let gain = ctx.createGain();
|
||||
gain.gain.value = 0;
|
||||
|
||||
// Connect multiple times to test that it's disregarded.
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
gain.connect(ctx.destination);
|
||||
gain.connect(ctx.destination);
|
||||
gain.connect(ctx.destination);
|
||||
gain.connect(ctx.destination);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(0);
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -94,19 +94,24 @@ AudioNodeView.prototype.getType = Task.async(function* () {
|
|||
});
|
||||
|
||||
// Helper method to create connections in the AudioNodeConnections
|
||||
// WeakMap for rendering
|
||||
// WeakMap for rendering. Returns a boolean indicating
|
||||
// if the connection was successfully created. Will return `false`
|
||||
// when the connection was previously made.
|
||||
AudioNodeView.prototype.connect = function (destination) {
|
||||
let connections = AudioNodeConnections.get(this);
|
||||
if (!connections) {
|
||||
connections = [];
|
||||
AudioNodeConnections.set(this, connections);
|
||||
let connections = AudioNodeConnections.get(this) || new Set();
|
||||
AudioNodeConnections.set(this, connections);
|
||||
|
||||
// Don't duplicate add.
|
||||
if (!connections.has(destination)) {
|
||||
connections.add(destination);
|
||||
return true;
|
||||
}
|
||||
connections.push(destination);
|
||||
return false;
|
||||
};
|
||||
|
||||
// Helper method to remove audio connections from the current AudioNodeView
|
||||
AudioNodeView.prototype.disconnect = function () {
|
||||
AudioNodeConnections.set(this, []);
|
||||
AudioNodeConnections.set(this, new Set());
|
||||
};
|
||||
|
||||
// Returns a promise that resolves to an array of objects containing
|
||||
|
@ -293,8 +298,10 @@ let WebAudioEditorController = {
|
|||
// adding an edge.
|
||||
let [source, dest] = yield waitForNodeCreation(sourceActor, destActor);
|
||||
|
||||
source.connect(dest);
|
||||
window.emit(EVENTS.CONNECT_NODE, source.id, dest.id);
|
||||
// Connect nodes, and only emit if it's a new connection.
|
||||
if (source.connect(dest)) {
|
||||
window.emit(EVENTS.CONNECT_NODE, source.id, dest.id);
|
||||
}
|
||||
|
||||
function waitForNodeCreation (sourceActor, destActor) {
|
||||
let deferred = defer();
|
||||
|
|
|
@ -161,7 +161,7 @@ let WebAudioGraphView = {
|
|||
// Add all of the connections from this node to the edge array to be added
|
||||
// after all the nodes are added, otherwise edges will attempted to be created
|
||||
// for nodes that have not yet been added
|
||||
AudioNodeConnections.get(node, []).forEach(dest => edges.push([node, dest]));
|
||||
AudioNodeConnections.get(node, new Set()).forEach(dest => edges.push([node, dest]));
|
||||
});
|
||||
|
||||
edges.forEach(([node, dest]) => graph.addEdge(null, node.id, dest.id, {
|
||||
|
|
|
@ -2095,6 +2095,8 @@ public class GeckoAppShell
|
|||
|
||||
@WrapElementForJNI(allowMultithread = true)
|
||||
public static Context getContext() {
|
||||
if (sContextGetter == null)
|
||||
return null;
|
||||
return sContextGetter.getContext();
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,8 @@ public class GeckoApplication extends Application
|
|||
}
|
||||
|
||||
public void onActivityResume(GeckoActivityStatus activity) {
|
||||
GeckoAppShell.setContextGetter(this);
|
||||
|
||||
if (mPausedGecko) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createAppForegroundingEvent());
|
||||
mPausedGecko = false;
|
||||
|
|
|
@ -33,6 +33,9 @@ public class BrowserContract {
|
|||
public static final String READING_LIST_AUTHORITY = AppConstants.ANDROID_PACKAGE_NAME + ".db.readinglist";
|
||||
public static final Uri READING_LIST_AUTHORITY_URI = Uri.parse("content://" + READING_LIST_AUTHORITY);
|
||||
|
||||
public static final String SEARCH_HISTORY_AUTHORITY = AppConstants.ANDROID_PACKAGE_NAME + ".db.searchhistory";
|
||||
public static final Uri SEARCH_HISTORY_AUTHORITY_URI = Uri.parse("content://" + SEARCH_HISTORY_AUTHORITY);
|
||||
|
||||
public static final String PARAM_PROFILE = "profile";
|
||||
public static final String PARAM_PROFILE_PATH = "profilePath";
|
||||
public static final String PARAM_LIMIT = "limit";
|
||||
|
@ -433,6 +436,17 @@ public class BrowserContract {
|
|||
public static final String TYPE = "type";
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public static final class SearchHistory implements CommonColumns, HistoryColumns {
|
||||
private SearchHistory() {}
|
||||
|
||||
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/searchhistory";
|
||||
public static final String QUERY = "query";
|
||||
public static final String TABLE_NAME = "searchhistory";
|
||||
|
||||
public static final Uri CONTENT_URI = Uri.withAppendedPath(SEARCH_HISTORY_AUTHORITY_URI, "searchhistory");
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public static final class SuggestedSites implements CommonColumns, URLColumns {
|
||||
private SuggestedSites() {}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.mozilla.gecko.db.BrowserContract.Favicons;
|
|||
import org.mozilla.gecko.db.BrowserContract.History;
|
||||
import org.mozilla.gecko.db.BrowserContract.Obsolete;
|
||||
import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
|
||||
import org.mozilla.gecko.db.BrowserContract.SearchHistory;
|
||||
import org.mozilla.gecko.db.BrowserContract.Thumbnails;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
|
||||
|
@ -34,7 +35,7 @@ import android.util.Log;
|
|||
final class BrowserDatabaseHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final String LOGTAG = "GeckoBrowserDBHelper";
|
||||
public static final int DATABASE_VERSION = 18;
|
||||
public static final int DATABASE_VERSION = 19;
|
||||
public static final String DATABASE_NAME = "browser.db";
|
||||
|
||||
final protected Context mContext;
|
||||
|
@ -749,6 +750,20 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
|
|||
|
||||
createOrUpdateAllSpecialFolders(db);
|
||||
createReadingListTable(db);
|
||||
createSearchHistoryTable(db);
|
||||
}
|
||||
|
||||
private void createSearchHistoryTable(SQLiteDatabase db) {
|
||||
debug("Creating " + SearchHistory.TABLE_NAME + " table");
|
||||
|
||||
db.execSQL("CREATE TABLE " + SearchHistory.TABLE_NAME + "(" +
|
||||
SearchHistory._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||
SearchHistory.QUERY + " TEXT UNIQUE NOT NULL, " +
|
||||
SearchHistory.DATE_LAST_VISITED + " INTEGER, " +
|
||||
SearchHistory.VISITS + " INTEGER ) ");
|
||||
|
||||
db.execSQL("CREATE INDEX idx_search_history_last_visited ON " +
|
||||
SearchHistory.TABLE_NAME + "(" + SearchHistory.DATE_LAST_VISITED + ")");
|
||||
}
|
||||
|
||||
private void createReadingListTable(SQLiteDatabase db) {
|
||||
|
@ -1378,6 +1393,10 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
|
|||
}
|
||||
}
|
||||
|
||||
private void upgradeDatabaseFrom18to19(SQLiteDatabase db) {
|
||||
createSearchHistoryTable(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
debug("Upgrading browser.db: " + db.getPath() + " from " +
|
||||
|
@ -1454,6 +1473,10 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
|
|||
case 18:
|
||||
upgradeDatabaseFrom17to18(db);
|
||||
break;
|
||||
|
||||
case 19:
|
||||
upgradeDatabaseFrom18to19(db);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1566,4 +1589,4 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
|
|||
bookmark.remove("folder");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/* 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.db;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserContract.SearchHistory;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
public class SearchHistoryProvider extends SharedBrowserDatabaseProvider {
|
||||
|
||||
/**
|
||||
* Collapse whitespace.
|
||||
*/
|
||||
private String stripWhitespace(String query) {
|
||||
if (TextUtils.isEmpty(query)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Collapse whitespace
|
||||
return query.trim().replaceAll("\\s+", " ");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Uri insertInTransaction(Uri uri, ContentValues cv) {
|
||||
final String query = stripWhitespace(cv.getAsString(SearchHistory.QUERY));
|
||||
|
||||
// We don't support inserting empty search queries.
|
||||
if (TextUtils.isEmpty(query)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
|
||||
/*
|
||||
* FIRST: Try incrementing the VISITS counter and updating the DATE_LAST_VISITED.
|
||||
*/
|
||||
final String sql = "UPDATE " + SearchHistory.TABLE_NAME + " SET " +
|
||||
SearchHistory.VISITS + " = " + SearchHistory.VISITS + " + 1, " +
|
||||
SearchHistory.DATE_LAST_VISITED + " = " + System.currentTimeMillis() +
|
||||
" WHERE " + SearchHistory.QUERY + " = ?";
|
||||
final Cursor c = db.rawQuery(sql, new String[] { query });
|
||||
|
||||
try {
|
||||
if (c.getCount() > 1) {
|
||||
// There is a UNIQUE constraint on the QUERY column,
|
||||
// so there should only be one match.
|
||||
return null;
|
||||
}
|
||||
if (c.moveToFirst()) {
|
||||
return ContentUris
|
||||
.withAppendedId(uri, c.getInt(c.getColumnIndex(SearchHistory._ID)));
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* SECOND: If the update failed, then insert a new record.
|
||||
*/
|
||||
cv.put(SearchHistory.QUERY, query);
|
||||
cv.put(SearchHistory.VISITS, 1);
|
||||
cv.put(SearchHistory.DATE_LAST_VISITED, System.currentTimeMillis());
|
||||
|
||||
long id = db.insert(SearchHistory.TABLE_NAME, null, cv);
|
||||
|
||||
if (id < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ContentUris.withAppendedId(uri, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
|
||||
return getWritableDatabase(uri)
|
||||
.delete(SearchHistory.TABLE_NAME, selection, selectionArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since we are managing counts and the full-text db, an update
|
||||
* could mangle the internal state. So we disable it.
|
||||
*/
|
||||
@Override
|
||||
public int updateInTransaction(Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This content provider does not support updating items");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
String groupBy = null;
|
||||
String having = null;
|
||||
return getReadableDatabase(uri)
|
||||
.query(SearchHistory.TABLE_NAME, projection, selection, selectionArgs,
|
||||
groupBy, having, sortOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
return SearchHistory.CONTENT_TYPE;
|
||||
}
|
||||
}
|
|
@ -150,6 +150,7 @@ gbjar.sources += [
|
|||
'db/PerProfileDatabaseProvider.java',
|
||||
'db/PerProfileDatabases.java',
|
||||
'db/ReadingListProvider.java',
|
||||
'db/SearchHistoryProvider.java',
|
||||
'db/SharedBrowserDatabaseProvider.java',
|
||||
'db/SQLiteBridgeContentProvider.java',
|
||||
'db/SuggestedSites.java',
|
||||
|
|
|
@ -122,7 +122,9 @@ public class GeckoPreferenceFragment extends PreferenceFragment {
|
|||
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
final ActionBar actionBar = activity.getActionBar();
|
||||
actionBar.setTitle(newTitle);
|
||||
if (actionBar != null) {
|
||||
actionBar.setTitle(newTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,9 @@ OnSharedPreferenceChangeListener
|
|||
Log.v(LOGTAG, "Setting action bar title to " + newTitle);
|
||||
|
||||
final ActionBar actionBar = getActionBar();
|
||||
actionBar.setTitle(newTitle);
|
||||
if (actionBar != null) {
|
||||
actionBar.setTitle(newTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -360,7 +362,9 @@ OnSharedPreferenceChangeListener
|
|||
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
final ActionBar actionBar = getActionBar();
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
// N.B., if we ever need to redisplay the locale selection UI without
|
||||
|
@ -977,15 +981,15 @@ OnSharedPreferenceChangeListener
|
|||
} else if (PREFS_ANNOUNCEMENTS_ENABLED.equals(prefName)) {
|
||||
// Send a broadcast intent to the product announcements service, either to start or
|
||||
// to stop the repeated background checks.
|
||||
broadcastAnnouncementsPref(GeckoAppShell.getContext(), ((Boolean) newValue).booleanValue());
|
||||
broadcastAnnouncementsPref(this, ((Boolean) newValue).booleanValue());
|
||||
} else if (PREFS_UPDATER_AUTODOWNLOAD.equals(prefName)) {
|
||||
org.mozilla.gecko.updater.UpdateServiceHelper.registerForUpdates(GeckoAppShell.getContext(), (String) newValue);
|
||||
org.mozilla.gecko.updater.UpdateServiceHelper.registerForUpdates(this, (String) newValue);
|
||||
} else if (PREFS_HEALTHREPORT_UPLOAD_ENABLED.equals(prefName)) {
|
||||
// The healthreport pref only lives in Android, so we do not persist
|
||||
// to Gecko, but we do broadcast intent to the health report
|
||||
// background uploader service, which will start or stop the
|
||||
// repeated background upload attempts.
|
||||
broadcastHealthReportUploadPref(GeckoAppShell.getContext(), ((Boolean) newValue).booleanValue());
|
||||
broadcastHealthReportUploadPref(this, ((Boolean) newValue).booleanValue());
|
||||
} else if (PREFS_GEO_REPORTING.equals(prefName)) {
|
||||
// Translate boolean value to int for geo reporting pref.
|
||||
newValue = ((Boolean) newValue) ? 1 : 0;
|
||||
|
@ -1013,7 +1017,7 @@ OnSharedPreferenceChangeListener
|
|||
}
|
||||
|
||||
private EditText getTextBox(int aHintText) {
|
||||
EditText input = new FloatingHintEditText(GeckoAppShell.getContext());
|
||||
EditText input = new FloatingHintEditText(this);
|
||||
int inputtype = InputType.TYPE_CLASS_TEXT;
|
||||
inputtype |= InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
|
||||
input.setInputType(inputtype);
|
||||
|
|
|
@ -27,7 +27,7 @@ public final class WaitHelper {
|
|||
// assertion from waitFor)?
|
||||
private static final int DEFAULT_MAX_WAIT_MS = 5000;
|
||||
private static final int PAGE_LOAD_WAIT_MS = 10000;
|
||||
private static final int CHANGE_WAIT_MS = 10000;
|
||||
private static final int CHANGE_WAIT_MS = 15000;
|
||||
|
||||
// TODO: via lucasr - Add ThrobberVisibilityChangeVerifier?
|
||||
private static final ChangeVerifier[] PAGE_LOAD_VERIFIERS = new ChangeVerifier[] {
|
||||
|
|
|
@ -56,7 +56,7 @@ skip-if = android_version == "10" || processor == "x86"
|
|||
[testHomeProvider]
|
||||
[testLoad]
|
||||
[testMailToContextMenu]
|
||||
[testMasterPassword]
|
||||
# [testMasterPassword] disabled for being finicky, see bug 1033013
|
||||
# disabled on 2.3; bug 979603
|
||||
# disabled on 4.0; bug 1006242
|
||||
skip-if = android_version == "10" || android_version == "15"
|
||||
|
@ -80,6 +80,7 @@ skip-if = processor == "x86"
|
|||
skip-if = processor == "x86"
|
||||
# [testReaderMode] # see bug 913254, 936224
|
||||
[testReadingListProvider]
|
||||
[testSearchHistoryProvider]
|
||||
[testSearchSuggestions]
|
||||
# disabled on x86; bug 907768
|
||||
skip-if = processor == "x86"
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
/* 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.tests;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.db.BrowserContract.SearchHistory;
|
||||
import org.mozilla.gecko.db.SearchHistoryProvider;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
|
||||
public class testSearchHistoryProvider extends ContentProviderTest {
|
||||
|
||||
// Translations of "United Kingdom" in several different languages
|
||||
private static final String[] testStrings = {"An Ríocht Aontaithe", // Irish
|
||||
"Angli", // Albanian
|
||||
"Britanniarum Regnum", // Latin
|
||||
"Britio", // Esperanto
|
||||
"Büyük Britanya", // Turkish
|
||||
"Egyesült Királyság", // Hungarian
|
||||
"Erresuma Batua", // Basque
|
||||
"Inggris Raya", // Indonesian
|
||||
"Ir-Renju Unit", // Maltese
|
||||
"Iso-Britannia", // Finnish
|
||||
"Jungtinė Karalystė", // Lithuanian
|
||||
"Lielbritānija", // Latvian
|
||||
"Regatul Unit", // Romanian
|
||||
"Regne Unit", // Catalan, Valencian
|
||||
"Regno Unito", // Italian
|
||||
"Royaume-Uni", // French
|
||||
"Spojené království", // Czech
|
||||
"Spojené kráľovstvo", // Slovak
|
||||
"Storbritannia", // Norwegian
|
||||
"Storbritannien", // Danish
|
||||
"Suurbritannia", // Estonian
|
||||
"Ujedinjeno Kraljevstvo", // Bosnian
|
||||
"United Alaeze", // Igbo
|
||||
"United Kingdom", // English
|
||||
"Vereinigtes Königreich", // German
|
||||
"Verenigd Koninkrijk", // Dutch
|
||||
"Verenigde Koninkryk", // Afrikaans
|
||||
"Vương quốc Anh", // Vietnamese
|
||||
"Wayòm Ini", // Haitian, Haitian Creole
|
||||
"Y Deyrnas Unedig", // Welsh
|
||||
"Združeno kraljestvo", // Slovene
|
||||
"Zjednoczone Królestwo", // Polish
|
||||
"Ηνωμένο Βασίλειο", // Greek (modern)
|
||||
"Великобритания", // Russian
|
||||
"Нэгдсэн Вант Улс", // Mongolian
|
||||
"Обединетото Кралство", // Macedonian
|
||||
"Уједињено Краљевство", // Serbian
|
||||
"Միացյալ Թագավորություն", // Armenian
|
||||
"בריטניה", // Hebrew (modern)
|
||||
"פֿאַראייניקטע מלכות", // Yiddish
|
||||
"المملكة المتحدة", // Arabic
|
||||
"برطانیہ", // Urdu
|
||||
"پادشاهی متحده", // Persian (Farsi)
|
||||
"यूनाइटेड किंगडम", // Hindi
|
||||
"संयुक्त राज्य", // Nepali
|
||||
"যুক্তরাজ্য", // Bengali, Bangla
|
||||
"યુનાઇટેડ કિંગડમ", // Gujarati
|
||||
"ஐக்கிய ராஜ்யம்", // Tamil
|
||||
"สหราชอาณาจักร", // Thai
|
||||
"ສະຫະປະຊາຊະອານາຈັກ", // Lao
|
||||
"გაერთიანებული სამეფო", // Georgian
|
||||
"イギリス", // Japanese
|
||||
"联合王国" // Chinese
|
||||
};
|
||||
|
||||
|
||||
private static final String DB_NAME = "searchhistory.db";
|
||||
|
||||
/**
|
||||
* Boilerplate alert.
|
||||
* <p/>
|
||||
* Make sure this method is present and that it returns a new
|
||||
* instance of your class.
|
||||
*/
|
||||
private static Callable<ContentProvider> sProviderFactory =
|
||||
new Callable<ContentProvider>() {
|
||||
@Override
|
||||
public ContentProvider call() {
|
||||
return new SearchHistoryProvider();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp(sProviderFactory, BrowserContract.SEARCH_HISTORY_AUTHORITY, DB_NAME);
|
||||
mTests.add(new TestInsert());
|
||||
mTests.add(new TestUnicodeQuery());
|
||||
mTests.add(new TestTimestamp());
|
||||
mTests.add(new TestDelete());
|
||||
mTests.add(new TestIncrement());
|
||||
}
|
||||
|
||||
public void testSearchHistory() throws Exception {
|
||||
for (Runnable test : mTests) {
|
||||
String testName = test.getClass().getSimpleName();
|
||||
setTestName(testName);
|
||||
mAsserter.dumpLog(
|
||||
"testBrowserProvider: Database empty - Starting " + testName + ".");
|
||||
// Clear the db
|
||||
mProvider.delete(SearchHistory.CONTENT_URI, null, null);
|
||||
test.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that we can insert values into the DB, including unicode.
|
||||
*/
|
||||
private class TestInsert extends TestCase {
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
ContentValues cv;
|
||||
for (int i = 0; i < testStrings.length; i++) {
|
||||
cv = new ContentValues();
|
||||
cv.put(SearchHistory.QUERY, testStrings[i]);
|
||||
mProvider.insert(SearchHistory.CONTENT_URI, cv);
|
||||
}
|
||||
|
||||
Cursor c = mProvider.query(SearchHistory.CONTENT_URI, null, null, null, null);
|
||||
mAsserter.is(c.getCount(), testStrings.length,
|
||||
"Should have one row for each insert");
|
||||
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that we can insert values into the DB, including unicode.
|
||||
*/
|
||||
private class TestUnicodeQuery extends TestCase {
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
ContentValues cv;
|
||||
Cursor c = null;
|
||||
String selection = SearchHistory.QUERY + " = ?";
|
||||
|
||||
for (int i = 0; i < testStrings.length; i++) {
|
||||
cv = new ContentValues();
|
||||
cv.put(SearchHistory.QUERY, testStrings[i]);
|
||||
mProvider.insert(SearchHistory.CONTENT_URI, cv);
|
||||
|
||||
c = mProvider.query(SearchHistory.CONTENT_URI, null, selection,
|
||||
new String[]{ testStrings[i] }, null);
|
||||
mAsserter.is(c.getCount(), 1,
|
||||
"Should have one row for insert of " + testStrings[i]);
|
||||
}
|
||||
|
||||
if (c != null) {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that timestamps are updated on insert.
|
||||
*/
|
||||
private class TestTimestamp extends TestCase {
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
String insertedTerm = "Courtside Seats";
|
||||
long insertStart;
|
||||
long insertFinish;
|
||||
long t1Db;
|
||||
long t2Db;
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(SearchHistory.QUERY, insertedTerm);
|
||||
|
||||
// First check that the DB has a value that is close to the
|
||||
// system time.
|
||||
insertStart = System.currentTimeMillis();
|
||||
mProvider.insert(SearchHistory.CONTENT_URI, cv);
|
||||
Cursor c = mProvider.query(SearchHistory.CONTENT_URI, null, null, null, null);
|
||||
c.moveToFirst();
|
||||
t1Db = c.getLong(c.getColumnIndex(SearchHistory.DATE_LAST_VISITED));
|
||||
c.close();
|
||||
insertFinish = System.currentTimeMillis();
|
||||
mAsserter.ok(t1Db >= insertStart, "DATE_LAST_VISITED",
|
||||
"Date last visited should be set on insert.");
|
||||
mAsserter.ok(t1Db <= insertFinish, "DATE_LAST_VISITED",
|
||||
"Date last visited should be set on insert.");
|
||||
|
||||
cv = new ContentValues();
|
||||
cv.put(SearchHistory.QUERY, insertedTerm);
|
||||
|
||||
insertStart = System.currentTimeMillis();
|
||||
mProvider.insert(SearchHistory.CONTENT_URI, cv);
|
||||
c = mProvider.query(SearchHistory.CONTENT_URI, null, null, null, null);
|
||||
c.moveToFirst();
|
||||
t2Db = c.getLong(c.getColumnIndex(SearchHistory.DATE_LAST_VISITED));
|
||||
c.close();
|
||||
insertFinish = System.currentTimeMillis();
|
||||
|
||||
mAsserter.ok(t2Db >= insertStart, "DATE_LAST_VISITED",
|
||||
"Date last visited should be set on insert.");
|
||||
mAsserter.ok(t2Db <= insertFinish, "DATE_LAST_VISITED",
|
||||
"Date last visited should be set on insert.");
|
||||
mAsserter.ok(t2Db > t1Db, "DATE_LAST_VISITED",
|
||||
"Date last visited should be updated on key increment.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that sending a delete command empties the database.
|
||||
*/
|
||||
private class TestDelete extends TestCase {
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
String insertedTerm = "Courtside Seats";
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(SearchHistory.QUERY, insertedTerm);
|
||||
mProvider.insert(SearchHistory.CONTENT_URI, cv);
|
||||
|
||||
Cursor c = mProvider.query(SearchHistory.CONTENT_URI, null, null, null, null);
|
||||
mAsserter.is(c.getCount(), 1, "Should have one value");
|
||||
mProvider.delete(SearchHistory.CONTENT_URI, null, null);
|
||||
c.close();
|
||||
|
||||
c = mProvider.query(SearchHistory.CONTENT_URI, null, null, null, null);
|
||||
mAsserter.is(c.getCount(), 0, "Should be empty");
|
||||
mProvider.insert(SearchHistory.CONTENT_URI, cv);
|
||||
c.close();
|
||||
|
||||
c = mProvider.query(SearchHistory.CONTENT_URI, null, null, null, null);
|
||||
mAsserter.is(c.getCount(), 1, "Should have one value");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ensure that we only increment when the case matches.
|
||||
*/
|
||||
private class TestIncrement extends TestCase {
|
||||
@Override
|
||||
public void test() throws Exception {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(SearchHistory.QUERY, "omaha");
|
||||
mProvider.insert(SearchHistory.CONTENT_URI, cv);
|
||||
|
||||
cv = new ContentValues();
|
||||
cv.put(SearchHistory.QUERY, "omaha");
|
||||
mProvider.insert(SearchHistory.CONTENT_URI, cv);
|
||||
|
||||
Cursor c = mProvider.query(SearchHistory.CONTENT_URI, null, null, null, null);
|
||||
c.moveToFirst();
|
||||
mAsserter.is(c.getCount(), 1, "Should have one result");
|
||||
mAsserter.is(c.getInt(c.getColumnIndex(SearchHistory.VISITS)), 2,
|
||||
"Counter should be 2");
|
||||
c.close();
|
||||
|
||||
cv = new ContentValues();
|
||||
cv.put(SearchHistory.QUERY, "Omaha");
|
||||
mProvider.insert(SearchHistory.CONTENT_URI, cv);
|
||||
c = mProvider.query(SearchHistory.CONTENT_URI, null, null, null, null);
|
||||
mAsserter.is(c.getCount(), 2, "Should have two results");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
<Url type="text/html" method="GET" template="https://search.yahoo.com/search">
|
||||
<Param name="p" value="{searchTerms}" />
|
||||
<Param name="ei" value="UTF-8" />
|
||||
<Param name="fr" value="mozilla_mobile_search" />
|
||||
<Param name="fr" value="mozmob1" />
|
||||
</Url>
|
||||
<SearchForm>https://search.yahoo.com/</SearchForm>
|
||||
</SearchPlugin>
|
||||
|
|
|
@ -329,6 +329,9 @@ Discovery.prototype = {
|
|||
let remoteDevice = update.device;
|
||||
let remoteHost = update.from;
|
||||
|
||||
// Record the reply as received so it won't be purged as missing
|
||||
this._expectingReplies.from.delete(remoteDevice);
|
||||
|
||||
// First, loop over the known services
|
||||
for (let service in this.remoteServices) {
|
||||
let devicesWithService = this.remoteServices[service];
|
||||
|
|
|
@ -116,6 +116,9 @@ add_task(function*() {
|
|||
discovery.addService("penguins", { tux: false });
|
||||
yield scanForChange("penguins", "updated");
|
||||
|
||||
// Scan again, but nothing should be removed
|
||||
yield scanForNoChange("penguins", "removed");
|
||||
|
||||
// Split the scanning side from the service side to simulate the machine with
|
||||
// the service becoming unreachable
|
||||
gTestTransports = {};
|
||||
|
@ -137,3 +140,17 @@ function scanForChange(service, changeType) {
|
|||
discovery.scan();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function scanForNoChange(service, changeType) {
|
||||
let deferred = promise.defer();
|
||||
let timer = setTimeout(() => {
|
||||
deferred.resolve();
|
||||
}, discovery.replyTimeout + 500);
|
||||
discovery.on(service + "-device-" + changeType, function onChange() {
|
||||
discovery.off(service + "-device-" + changeType, onChange);
|
||||
clearTimeout(timer);
|
||||
deferred.reject(new Error("Unexpected change occurred"));
|
||||
});
|
||||
discovery.scan();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@ let FunctionCallActor = protocol.ActorClass({
|
|||
// If this argument matches the method's signature
|
||||
// and is an enum, change it to its constant name.
|
||||
if (enumArgs && enumArgs.indexOf(i) !== -1) {
|
||||
return getEnumsLookupTable(global, caller)[arg] || arg;
|
||||
return getBitToEnumValue(global, caller, arg);
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
|
@ -641,19 +641,48 @@ CallWatcherFront.ENUM_METHODS[CallWatcherFront.CANVAS_WEBGL_CONTEXT] = {
|
|||
var gEnumRegex = /^[A-Z_]+$/;
|
||||
var gEnumsLookupTable = {};
|
||||
|
||||
function getEnumsLookupTable(type, object) {
|
||||
let cachedEnum = gEnumsLookupTable[type];
|
||||
if (cachedEnum) {
|
||||
return cachedEnum;
|
||||
}
|
||||
// These values are returned from errors, or empty values,
|
||||
// and need to be ignored when checking arguments due to the bitwise math.
|
||||
var INVALID_ENUMS = [
|
||||
"INVALID_ENUM", "NO_ERROR", "INVALID_VALUE", "OUT_OF_MEMORY", "NONE"
|
||||
];
|
||||
|
||||
let table = gEnumsLookupTable[type] = {};
|
||||
function getBitToEnumValue(type, object, arg) {
|
||||
let table = gEnumsLookupTable[type];
|
||||
|
||||
for (let key in object) {
|
||||
if (key.match(gEnumRegex)) {
|
||||
table[object[key]] = key;
|
||||
// If mapping not yet created, do it on the first run.
|
||||
if (!table) {
|
||||
table = gEnumsLookupTable[type] = {};
|
||||
|
||||
for (let key in object) {
|
||||
if (key.match(gEnumRegex)) {
|
||||
// Maps `16384` to `"COLOR_BUFFER_BIT"`, etc.
|
||||
table[object[key]] = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
// If a single bit value, just return it.
|
||||
if (table[arg]) {
|
||||
return table[arg];
|
||||
}
|
||||
|
||||
// Otherwise, attempt to reduce it to the original bit flags:
|
||||
// `16640` -> "COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT"
|
||||
let flags = [];
|
||||
for (let flag in table) {
|
||||
if (INVALID_ENUMS.indexOf(table[flag]) !== -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cast to integer as all values are stored as strings
|
||||
// in `table`
|
||||
flag = flag | 0;
|
||||
if (flag && (arg & flag) === flag) {
|
||||
flags.push(table[flag]);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the combined bitmask value
|
||||
return table[arg] = flags.join(" | ") || arg;
|
||||
}
|
||||
|
|
|
@ -901,7 +901,7 @@ const TAB_CHARS = "\t";
|
|||
* @param string text The CSS source to prettify.
|
||||
* @return string Prettified CSS source
|
||||
*/
|
||||
CssLogic.prettifyCSS = function(text) {
|
||||
CssLogic.prettifyCSS = function(text, ruleCount) {
|
||||
if (CssLogic.LINE_SEPARATOR == null) {
|
||||
let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
|
||||
CssLogic.LINE_SEPARATOR = (os === "WINNT" ? "\r\n" : "\n");
|
||||
|
@ -910,6 +910,12 @@ CssLogic.prettifyCSS = function(text) {
|
|||
// remove initial and terminating HTML comments and surrounding whitespace
|
||||
text = text.replace(/(?:^\s*<!--[\r\n]*)|(?:\s*-->\s*$)/g, "");
|
||||
|
||||
// don't attempt to prettify if there's more than one line per rule.
|
||||
let lineCount = text.split("\n").length - 1;
|
||||
if (ruleCount !== null && lineCount >= ruleCount) {
|
||||
return text;
|
||||
}
|
||||
|
||||
let parts = []; // indented parts
|
||||
let partStart = 0; // start offset of currently parsed part
|
||||
let indent = "";
|
||||
|
|
|
@ -23,15 +23,19 @@ const TESTS = [ {
|
|||
pageid: PAGEID_ERROR_PATCHING,
|
||||
buttonClick: "next"
|
||||
}, {
|
||||
pageid: PAGEID_DOWNLOADING
|
||||
pageid: PAGEID_DOWNLOADING,
|
||||
extraStartFunction: createContinueFile
|
||||
}, {
|
||||
pageid: PAGEID_FINISHED,
|
||||
buttonClick: "extra1"
|
||||
buttonClick: "extra1",
|
||||
extraStartFunction: removeContinueFile
|
||||
} ];
|
||||
|
||||
function runTest() {
|
||||
debugDump("entering");
|
||||
|
||||
removeContinueFile();
|
||||
|
||||
// Specify the url to update.sjs with a slowDownloadMar param so the ui can
|
||||
// load before the download completes.
|
||||
let slowDownloadURL = URL_HTTP_UPDATE_XML + "?slowDownloadMar=1";
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<script type="application/javascript"
|
||||
src="utils.js"/>
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
|
@ -23,15 +24,19 @@ const TESTS = [ {
|
|||
pageid: PAGEID_ERROR_PATCHING,
|
||||
buttonClick: "next"
|
||||
}, {
|
||||
pageid: PAGEID_DOWNLOADING
|
||||
pageid: PAGEID_DOWNLOADING,
|
||||
extraStartFunction: createContinueFile
|
||||
}, {
|
||||
pageid: PAGEID_ERRORS,
|
||||
buttonClick: "finish"
|
||||
buttonClick: "finish",
|
||||
extraStartFunction: removeContinueFile
|
||||
} ];
|
||||
|
||||
function runTest() {
|
||||
debugDump("entering");
|
||||
|
||||
removeContinueFile();
|
||||
|
||||
// Specify the url to update.sjs with a slowDownloadMar param so the ui can
|
||||
// load before the download completes.
|
||||
let slowDownloadURL = URL_HTTP_UPDATE_XML + "?slowDownloadMar=1";
|
||||
|
|
|
@ -44,23 +44,37 @@ function handleRequest(aRequest, aResponse) {
|
|||
// mar will be downloaded asynchronously which will allow the ui to load
|
||||
// before the download completes.
|
||||
if (params.slowDownloadMar) {
|
||||
var i;
|
||||
aResponse.processAsync();
|
||||
aResponse.setHeader("Content-Type", "binary/octet-stream");
|
||||
aResponse.setHeader("Content-Length", SIZE_SIMPLE_MAR);
|
||||
var continueFile = AUS_Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(AUS_Ci.nsIProperties).
|
||||
get("CurWorkD", AUS_Ci.nsILocalFile);
|
||||
var continuePath = REL_PATH_DATA + "continue";
|
||||
var continuePathParts = continuePath.split("/");
|
||||
for (i = 0; i < continuePathParts.length; ++i) {
|
||||
continueFile.append(continuePathParts[i]);
|
||||
}
|
||||
|
||||
var marFile = AUS_Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(AUS_Ci.nsIProperties).
|
||||
get("CurWorkD", AUS_Ci.nsILocalFile);
|
||||
var path = REL_PATH_DATA + FILE_SIMPLE_MAR;
|
||||
var pathParts = path.split("/");
|
||||
for(var i = 0; i < pathParts.length; ++i)
|
||||
for (i = 0; i < pathParts.length; ++i) {
|
||||
marFile.append(pathParts[i]);
|
||||
}
|
||||
var contents = readFileBytes(marFile);
|
||||
gTimer = AUS_Cc["@mozilla.org/timer;1"].
|
||||
createInstance(AUS_Ci.nsITimer);
|
||||
gTimer.initWithCallback(function(aTimer) {
|
||||
aResponse.write(contents);
|
||||
aResponse.finish();
|
||||
}, SLOW_MAR_DOWNLOAD_INTERVAL, AUS_Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
if (continueFile.exists()) {
|
||||
gTimer.cancel();
|
||||
aResponse.write(contents);
|
||||
aResponse.finish();
|
||||
}
|
||||
}, SLOW_MAR_DOWNLOAD_INTERVAL, AUS_Ci.nsITimer.TYPE_REPEATING_SLACK);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -483,7 +483,7 @@ function delayedDefaultCallback() {
|
|||
|
||||
if (gTest.buttonClick) {
|
||||
debugDump("clicking " + gTest.buttonClick + " button");
|
||||
if(gTest.extraDelayedFinishFunction) {
|
||||
if (gTest.extraDelayedFinishFunction) {
|
||||
throw("Tests cannot have a buttonClick and an extraDelayedFinishFunction property");
|
||||
}
|
||||
gDocElem.getButton(gTest.buttonClick).click();
|
||||
|
@ -495,6 +495,45 @@ function delayedDefaultCallback() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the continue file used to signal the mock http server to continue
|
||||
* downloading for slow download mar file tests without creating it.
|
||||
*
|
||||
* @return nsILocalFile for the continue file.
|
||||
*/
|
||||
function getContinueFile() {
|
||||
let continueFile = AUS_Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(AUS_Ci.nsIProperties).
|
||||
get("CurWorkD", AUS_Ci.nsILocalFile);
|
||||
let continuePath = REL_PATH_DATA + "/continue";
|
||||
let continuePathParts = continuePath.split("/");
|
||||
for (let i = 0; i < continuePathParts.length; ++i) {
|
||||
continueFile.append(continuePathParts[i]);
|
||||
}
|
||||
return continueFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the continue file used to signal the mock http server to continue
|
||||
* downloading for slow download mar file tests.
|
||||
*/
|
||||
function createContinueFile() {
|
||||
debugDump("creating 'continue' file for slow mar downloads");
|
||||
writeFile(getContinueFile(), "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the continue file used to signal the mock http server to continue
|
||||
* downloading for slow download mar file tests.
|
||||
*/
|
||||
function removeContinueFile() {
|
||||
let continueFile = getContinueFile();
|
||||
if (continueFile.exists()) {
|
||||
debugDump("removing 'continue' file for slow mar downloads");
|
||||
continueFile.remove(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the wizard page buttons' disabled and hidden attributes values are
|
||||
* correct. If an expected button id is not specified then the expected disabled
|
||||
|
|
Загрузка…
Ссылка в новой задаче