mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 12:37:37 +00:00
Merge m-c to inbound
This commit is contained in:
commit
cd6c2201e2
@ -12,7 +12,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="8e21d0a5cdac94f05e9c3623fa3b9ad579ba2967"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80d6405725788327102cab36e8d8c017cf25fb23"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
|
@ -11,7 +11,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="8e21d0a5cdac94f05e9c3623fa3b9ad579ba2967"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80d6405725788327102cab36e8d8c017cf25fb23"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8c449b53328059e9b55bb34baec9b27a15055a7e"/>
|
||||
|
@ -12,7 +12,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="8e21d0a5cdac94f05e9c3623fa3b9ad579ba2967"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80d6405725788327102cab36e8d8c017cf25fb23"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "823616f0af83eca32bceb0367a7a221f8b187110",
|
||||
"revision": "b2b0a8234336f7004812bf27e4053957cecad492",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -11,7 +11,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="8e21d0a5cdac94f05e9c3623fa3b9ad579ba2967"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80d6405725788327102cab36e8d8c017cf25fb23"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
@ -10,7 +10,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="8e21d0a5cdac94f05e9c3623fa3b9ad579ba2967"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80d6405725788327102cab36e8d8c017cf25fb23"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
@ -12,7 +12,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="8e21d0a5cdac94f05e9c3623fa3b9ad579ba2967"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80d6405725788327102cab36e8d8c017cf25fb23"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
@ -11,7 +11,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="8e21d0a5cdac94f05e9c3623fa3b9ad579ba2967"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80d6405725788327102cab36e8d8c017cf25fb23"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
@ -11,7 +11,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="8e21d0a5cdac94f05e9c3623fa3b9ad579ba2967"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80d6405725788327102cab36e8d8c017cf25fb23"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8c449b53328059e9b55bb34baec9b27a15055a7e"/>
|
||||
|
@ -11,7 +11,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="8e21d0a5cdac94f05e9c3623fa3b9ad579ba2967"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80d6405725788327102cab36e8d8c017cf25fb23"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
@ -592,6 +592,9 @@
|
||||
cui-areatype="toolbar"
|
||||
tooltiptext="&closeTab.label;"/>
|
||||
|
||||
#ifdef XP_WIN
|
||||
<hbox id="private-browsing-indicator" skipintoolbarset="true" ordinal="1000"/>
|
||||
#endif
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
<hbox class="titlebar-placeholder" type="caption-buttons"
|
||||
id="titlebar-placeholder-on-TabsToolbar-for-captions-buttons" persist="width"
|
||||
|
@ -191,6 +191,19 @@ const CustomizableWidgets = [{
|
||||
}
|
||||
recentlyClosedWindows.appendChild(windowsFragment);
|
||||
},
|
||||
onCreated: function(aNode) {
|
||||
// Middle clicking recently closed items won't close the panel - cope:
|
||||
let onRecentlyClosedClick = function(aEvent) {
|
||||
if (aEvent.button == 1) {
|
||||
CustomizableUI.hidePanelForNode(this);
|
||||
}
|
||||
};
|
||||
let doc = aNode.ownerDocument;
|
||||
let recentlyClosedTabs = doc.getElementById("PanelUI-recentlyClosedTabs");
|
||||
let recentlyClosedWindows = doc.getElementById("PanelUI-recentlyClosedWindows");
|
||||
recentlyClosedTabs.addEventListener("click", onRecentlyClosedClick);
|
||||
recentlyClosedWindows.addEventListener("click", onRecentlyClosedClick);
|
||||
},
|
||||
onViewHiding: function(aEvent) {
|
||||
LOG("History view is being hidden!");
|
||||
}
|
||||
|
@ -107,6 +107,8 @@ support-files =
|
||||
test-bug_923281_test2.js
|
||||
test-bug_939783_console_trace_duplicates.html
|
||||
test-bug-952277-highlight-nodes-in-vview.html
|
||||
test-bug-609872-cd-iframe-parent.html
|
||||
test-bug-609872-cd-iframe-child.html
|
||||
|
||||
[browser_bug664688_sandbox_update_after_navigation.js]
|
||||
[browser_bug_638949_copy_link_location.js]
|
||||
@ -267,3 +269,4 @@ run-if = os == "mac"
|
||||
[browser_webconsole_output_events.js]
|
||||
[browser_console_variables_view_highlighter.js]
|
||||
[browser_webconsole_console_trace_duplicates.js]
|
||||
[browser_webconsole_cd_iframe.js]
|
||||
|
110
browser/devtools/webconsole/test/browser_webconsole_cd_iframe.js
Normal file
110
browser/devtools/webconsole/test/browser_webconsole_cd_iframe.js
Normal file
@ -0,0 +1,110 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the cd() jsterm helper function works as expected. See bug 609872.
|
||||
|
||||
function test() {
|
||||
let hud;
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-609872-cd-iframe-parent.html";
|
||||
|
||||
const parentMessages = [{
|
||||
name: "document.title in parent iframe",
|
||||
text: "bug 609872 - iframe parent",
|
||||
category: CATEGORY_OUTPUT,
|
||||
}, {
|
||||
name: "paragraph content",
|
||||
text: "p: test for bug 609872 - iframe parent",
|
||||
category: CATEGORY_OUTPUT,
|
||||
}, {
|
||||
name: "object content",
|
||||
text: "obj: parent!",
|
||||
category: CATEGORY_OUTPUT,
|
||||
}];
|
||||
|
||||
const childMessages = [{
|
||||
name: "document.title in child iframe",
|
||||
text: "bug 609872 - iframe child",
|
||||
category: CATEGORY_OUTPUT,
|
||||
}, {
|
||||
name: "paragraph content",
|
||||
text: "p: test for bug 609872 - iframe child",
|
||||
category: CATEGORY_OUTPUT,
|
||||
}, {
|
||||
name: "object content",
|
||||
text: "obj: child!",
|
||||
category: CATEGORY_OUTPUT,
|
||||
}];
|
||||
|
||||
Task.spawn(runner).then(finishTest);
|
||||
|
||||
function* runner() {
|
||||
const {tab} = yield loadTab(TEST_URI);
|
||||
hud = yield openConsole(tab);
|
||||
|
||||
executeWindowTest();
|
||||
|
||||
yield waitForMessages({ webconsole: hud, messages: parentMessages });
|
||||
|
||||
info("cd() into the iframe using a selector");
|
||||
hud.jsterm.clearOutput();
|
||||
hud.jsterm.execute("cd('iframe')");
|
||||
executeWindowTest();
|
||||
|
||||
yield waitForMessages({ webconsole: hud, messages: childMessages });
|
||||
|
||||
info("cd() out of the iframe, reset to default window");
|
||||
hud.jsterm.clearOutput();
|
||||
hud.jsterm.execute("cd()");
|
||||
executeWindowTest();
|
||||
|
||||
yield waitForMessages({ webconsole: hud, messages: parentMessages });
|
||||
|
||||
info("call cd() with unexpected arguments");
|
||||
hud.jsterm.clearOutput();
|
||||
hud.jsterm.execute("cd(document)");
|
||||
|
||||
yield waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [{
|
||||
text: "Cannot cd()",
|
||||
category: CATEGORY_OUTPUT,
|
||||
severity: SEVERITY_ERROR,
|
||||
}],
|
||||
});
|
||||
|
||||
hud.jsterm.clearOutput();
|
||||
hud.jsterm.execute("cd('p')");
|
||||
|
||||
yield waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [{
|
||||
text: "Cannot cd()",
|
||||
category: CATEGORY_OUTPUT,
|
||||
severity: SEVERITY_ERROR,
|
||||
}],
|
||||
});
|
||||
|
||||
info("cd() into the iframe using an iframe DOM element");
|
||||
hud.jsterm.clearOutput();
|
||||
hud.jsterm.execute("cd($('iframe'))");
|
||||
executeWindowTest();
|
||||
|
||||
yield waitForMessages({ webconsole: hud, messages: childMessages });
|
||||
|
||||
info("cd(window.parent)");
|
||||
hud.jsterm.clearOutput();
|
||||
hud.jsterm.execute("cd(window.parent)");
|
||||
executeWindowTest();
|
||||
|
||||
yield waitForMessages({ webconsole: hud, messages: parentMessages });
|
||||
|
||||
yield closeConsole(tab);
|
||||
}
|
||||
|
||||
function executeWindowTest() {
|
||||
hud.jsterm.execute("document.title");
|
||||
hud.jsterm.execute("'p: ' + document.querySelector('p').textContent");
|
||||
hud.jsterm.execute("'obj: ' + window.foobarBug609872");
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>test for bug 609872 - iframe child</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<p>test for bug 609872 - iframe child</p>
|
||||
<script>window.foobarBug609872 = 'child!';</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>test for bug 609872 - iframe parent</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<p>test for bug 609872 - iframe parent</p>
|
||||
<script>window.foobarBug609872 = 'parent!';</script>
|
||||
<iframe src="test-bug-609872-cd-iframe-child.html"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -213,3 +213,7 @@ emptyPropertiesList=No properties to display
|
||||
# #1 number of message repeats
|
||||
# example: 3 repeats
|
||||
messageRepeats.tooltip2=#1 repeat;#1 repeats
|
||||
|
||||
# LOCALIZATION NOTE (cdFunctionInvalidArgument): the text that is displayed when
|
||||
# cd() is invoked with an invalid argument.
|
||||
cdFunctionInvalidArgument=Cannot cd() to the given window. Invalid argument.
|
||||
|
@ -64,6 +64,7 @@ var Browser = {
|
||||
messageManager.loadFrameScript("chrome://browser/content/contenthandlers/SelectionHandler.js", true);
|
||||
messageManager.loadFrameScript("chrome://browser/content/contenthandlers/ContextMenuHandler.js", true);
|
||||
messageManager.loadFrameScript("chrome://browser/content/contenthandlers/ConsoleAPIObserver.js", true);
|
||||
messageManager.loadFrameScript("chrome://browser/content/contenthandlers/PluginHelper.js", true);
|
||||
} catch (e) {
|
||||
// XXX whatever is calling startup needs to dump errors!
|
||||
dump("###########" + e + "\n");
|
||||
|
@ -1120,6 +1120,9 @@ Desktop browser's sync prefs.
|
||||
<setting pref="signon.rememberSignons"
|
||||
title="&optionsHeader.privacy.passwords.label;"
|
||||
type="bool"/>
|
||||
<setting pref="browser.display.overlaynavbuttons"
|
||||
title="&optionsHeader.displayOverlayButtons.label;"
|
||||
type="bool"/>
|
||||
<settings id="prefs-reporting"
|
||||
label="&optionsHeader.reporting.title;">
|
||||
<setting pref="app.crashreporter.autosubmit"
|
||||
|
95
browser/metro/base/content/contenthandlers/PluginHelper.js
Normal file
95
browser/metro/base/content/contenthandlers/PluginHelper.js
Normal file
@ -0,0 +1,95 @@
|
||||
/* 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";
|
||||
|
||||
dump("### PluginHelper.js loaded\n");
|
||||
|
||||
/**
|
||||
* Handle events generated by plugin click-to-play code.
|
||||
*
|
||||
* This "PluginBindingAttached" fires when a "pluginProblem" XBL binding is
|
||||
* created. This binding overlays plugin content when the plugin is missing,
|
||||
* blocked, click-to-play, or replaced by a "preview" extension plugin like
|
||||
* Shumway or PDF.js.
|
||||
*/
|
||||
var PluginHelper = {
|
||||
init: function () {
|
||||
addEventListener("PluginBindingAttached", this, true, true);
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "PluginBindingAttached":
|
||||
this.handlePluginBindingAttached(aEvent);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
getPluginMimeType: function (plugin) {
|
||||
var tagMimetype = plugin.actualType;
|
||||
|
||||
if (tagMimetype == "") {
|
||||
tagMimetype = plugin.type;
|
||||
}
|
||||
return tagMimetype;
|
||||
},
|
||||
|
||||
handlePluginBindingAttached: function (aEvent) {
|
||||
let plugin = aEvent.target;
|
||||
let doc = plugin.ownerDocument;
|
||||
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
|
||||
if (!overlay || overlay._bindingHandled) {
|
||||
return;
|
||||
}
|
||||
overlay._bindingHandled = true;
|
||||
|
||||
let eventType = PluginHelper._getBindingType(plugin);
|
||||
if (!eventType) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case "PluginPlayPreview": {
|
||||
// Load the "preview" handler (an extension plugin like Shumway or PDF.js).
|
||||
let previewContent = doc.getAnonymousElementByAttribute(plugin, "class", "previewPluginContent");
|
||||
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
|
||||
let mimeType = PluginHelper.getPluginMimeType(plugin);
|
||||
let playPreviewInfo = pluginHost.getPlayPreviewInfo(mimeType);
|
||||
|
||||
let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0];
|
||||
if (!iframe) {
|
||||
// lazy initialization of the iframe
|
||||
iframe = doc.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
|
||||
iframe.className = "previewPluginContentFrame";
|
||||
previewContent.appendChild(iframe);
|
||||
}
|
||||
iframe.src = playPreviewInfo.redirectURL;
|
||||
break;
|
||||
}
|
||||
|
||||
case "PluginNotFound": {
|
||||
// TODO: Display a message about missing plugins (bug 936907)
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Helper to get the binding handler type from a plugin object
|
||||
_getBindingType: function(plugin) {
|
||||
if (!(plugin instanceof Ci.nsIObjectLoadingContent))
|
||||
return null;
|
||||
|
||||
switch (plugin.pluginFallbackType) {
|
||||
case Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED:
|
||||
return "PluginNotFound";
|
||||
case Ci.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW:
|
||||
return "PluginPlayPreview";
|
||||
default:
|
||||
// Metro Firefox does not yet support other fallback types.
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
PluginHelper.init();
|
@ -59,6 +59,7 @@ chrome.jar:
|
||||
content/contenthandlers/FormHelper.js (content/contenthandlers/FormHelper.js)
|
||||
content/contenthandlers/ConsoleAPIObserver.js (content/contenthandlers/ConsoleAPIObserver.js)
|
||||
content/contenthandlers/Content.js (content/contenthandlers/Content.js)
|
||||
content/contenthandlers/PluginHelper.js (content/contenthandlers/PluginHelper.js)
|
||||
|
||||
content/library/SelectionPrototype.js (content/library/SelectionPrototype.js)
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
<!ENTITY clearPrivateData.logins "Active logins">
|
||||
|
||||
<!ENTITY optionsHeader.privacy.passwords.label "Remember Passwords">
|
||||
<!ENTITY optionsHeader.displayOverlayButtons.label "Show Navigation Buttons">
|
||||
<!ENTITY doNotTrack.title "Do Not Track">
|
||||
<!ENTITY doNotTrack.options.doNotTrack "Tell websites that I do not want to be tracked">
|
||||
<!ENTITY doNotTrack.options.doTrack "Tell websites that I want to be tracked">
|
||||
|
@ -573,7 +573,12 @@ pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false);
|
||||
#endif
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
// Shumay is currently experimental. Toggle this pref to enable Shumway for
|
||||
// testing and development.
|
||||
pref("shumway.disabled", true);
|
||||
// When Shumway is enabled, use it all the time, not only when Flash is set to
|
||||
// click-to-play (because Metro doesn't even load the native Flash plugin).
|
||||
pref("shumway.ignoreCTP", true);
|
||||
#endif
|
||||
|
||||
// The maximum amount of decoded image data we'll willingly keep around (we
|
||||
|
@ -2509,9 +2509,7 @@ chatbox {
|
||||
|
||||
/* End customization mode */
|
||||
|
||||
#main-window[privatebrowsingmode=temporary] #TabsToolbar::after {
|
||||
content: "";
|
||||
display: -moz-box;
|
||||
#main-window[privatebrowsingmode=temporary] #private-browsing-indicator {
|
||||
width: 40px;
|
||||
background: url("chrome://browser/skin/privatebrowsing-indicator.png") no-repeat center center;
|
||||
}
|
||||
|
@ -952,12 +952,6 @@ nsDOMCameraControl::Shutdown()
|
||||
mCameraControl->Shutdown();
|
||||
}
|
||||
|
||||
nsRefPtr<ICameraControl>
|
||||
nsDOMCameraControl::GetNativeCameraControl()
|
||||
{
|
||||
return mCameraControl;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg)
|
||||
{
|
||||
@ -1139,21 +1133,31 @@ void
|
||||
nsDOMCameraControl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ErrorResult ignored;
|
||||
|
||||
nsCOMPtr<CameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb.forget();
|
||||
mAutoFocusOnErrorCb = nullptr;
|
||||
cb->Call(aAutoFocusSucceeded, ignored);
|
||||
if (cb) {
|
||||
ErrorResult ignored;
|
||||
cb->Call(aAutoFocusSucceeded, ignored);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ErrorResult ignored;
|
||||
|
||||
nsCOMPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb.forget();
|
||||
mTakePictureOnErrorCb = nullptr;
|
||||
if (!cb) {
|
||||
// Warn because it shouldn't be possible to get here without
|
||||
// having passed a success callback into takePicture(), even
|
||||
// though we guard against a nullptr dereference.
|
||||
NS_WARNING("DOM Null success callback in OnTakePictureComplete()");
|
||||
return;
|
||||
}
|
||||
|
||||
ErrorResult ignored;
|
||||
cb->Call(aPicture, ignored);
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,6 @@ public:
|
||||
dom::GetCameraCallback* aOnSuccess,
|
||||
dom::CameraErrorCallback* aOnError,
|
||||
nsPIDOMWindow* aWindow);
|
||||
nsRefPtr<ICameraControl> GetNativeCameraControl();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
|
@ -272,53 +272,86 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This utility is replicated from RepoUtils, which is managed by android-sync.
|
||||
*/
|
||||
private static String computeSQLInClause(int items, String field) {
|
||||
final StringBuilder builder = new StringBuilder(field);
|
||||
builder.append(" IN (");
|
||||
int i = 0;
|
||||
for (; i < items - 1; ++i) {
|
||||
builder.append("?, ");
|
||||
}
|
||||
if (i < items) {
|
||||
builder.append("?");
|
||||
}
|
||||
builder.append(")");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a single-column cursor of longs into a single SQL "IN" clause.
|
||||
* We can do this without using selection arguments because Long isn't
|
||||
* vulnerable to injection.
|
||||
*/
|
||||
private static String computeSQLInClauseFromLongs(final Cursor cursor, String field) {
|
||||
final StringBuilder builder = new StringBuilder(field);
|
||||
builder.append(" IN (");
|
||||
final int commaLimit = cursor.getCount() - 1;
|
||||
int i = 0;
|
||||
while (cursor.moveToNext()) {
|
||||
builder.append(cursor.getLong(0));
|
||||
if (i++ < commaLimit) {
|
||||
builder.append(", ");
|
||||
}
|
||||
}
|
||||
builder.append(")");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up some deleted records from the specified table.
|
||||
*
|
||||
* If called in an existing transaction, it is the caller's responsibility
|
||||
* to ensure that the transaction is already upgraded to a writer, because
|
||||
* this method issues a read followed by a write, and thus is potentially
|
||||
* vulnerable to an unhandled SQLITE_BUSY failure during the upgrade.
|
||||
*
|
||||
* If not called in an existing transaction, no new explicit transaction
|
||||
* will be begun.
|
||||
*/
|
||||
private void cleanupSomeDeletedRecords(Uri fromUri, Uri targetUri, String tableName) {
|
||||
Log.d(LOGTAG, "Cleaning up deleted records from " + tableName);
|
||||
|
||||
// we cleanup records marked as deleted that are older than a
|
||||
// We clean up records marked as deleted that are older than a
|
||||
// predefined max age. It's important not be too greedy here and
|
||||
// remove only a few old deleted records at a time.
|
||||
|
||||
// The PARAM_SHOW_DELETED argument is necessary to return the records
|
||||
// that were marked as deleted. We use PARAM_IS_SYNC here to ensure
|
||||
// that we'll be actually deleting records instead of flagging them.
|
||||
Uri.Builder uriBuilder = targetUri.buildUpon()
|
||||
.appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(DELETED_RECORDS_PURGE_LIMIT))
|
||||
.appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1")
|
||||
.appendQueryParameter(BrowserContract.PARAM_IS_SYNC, "1");
|
||||
|
||||
String profile = fromUri.getQueryParameter(BrowserContract.PARAM_PROFILE);
|
||||
if (!TextUtils.isEmpty(profile))
|
||||
uriBuilder = uriBuilder.appendQueryParameter(BrowserContract.PARAM_PROFILE, profile);
|
||||
|
||||
if (isTest(fromUri))
|
||||
uriBuilder = uriBuilder.appendQueryParameter(BrowserContract.PARAM_IS_TEST, "1");
|
||||
|
||||
Uri uriWithArgs = uriBuilder.build();
|
||||
|
||||
Cursor cursor = null;
|
||||
// Android SQLite doesn't have LIMIT on DELETE. Instead, query for the
|
||||
// IDs of matching rows, then delete them in one go.
|
||||
final long now = System.currentTimeMillis();
|
||||
final String selection = SyncColumns.IS_DELETED + " = 1 AND " +
|
||||
SyncColumns.DATE_MODIFIED + " <= " +
|
||||
(now - MAX_AGE_OF_DELETED_RECORDS);
|
||||
|
||||
final String profile = fromUri.getQueryParameter(BrowserContract.PARAM_PROFILE);
|
||||
final SQLiteDatabase db = getWritableDatabaseForProfile(profile, isTest(fromUri));
|
||||
final String[] ids;
|
||||
final String limit = Long.toString(DELETED_RECORDS_PURGE_LIMIT, 10);
|
||||
final Cursor cursor = db.query(tableName, new String[] { CommonColumns._ID }, selection, null, null, null, null, limit);
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
String selection = SyncColumns.IS_DELETED + " = 1 AND " +
|
||||
SyncColumns.DATE_MODIFIED + " <= " + (now - MAX_AGE_OF_DELETED_RECORDS);
|
||||
|
||||
cursor = query(uriWithArgs,
|
||||
new String[] { CommonColumns._ID },
|
||||
selection,
|
||||
null,
|
||||
null);
|
||||
|
||||
ids = new String[cursor.getCount()];
|
||||
int i = 0;
|
||||
while (cursor.moveToNext()) {
|
||||
Uri uriWithId = ContentUris.withAppendedId(uriWithArgs, cursor.getLong(0));
|
||||
delete(uriWithId, null, null);
|
||||
|
||||
debug("Removed old deleted item with URI: " + uriWithId);
|
||||
ids[i++] = Long.toString(cursor.getLong(0), 10);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
final String inClause = computeSQLInClause(ids.length,
|
||||
CommonColumns._ID);
|
||||
db.delete(tableName, inClause, ids);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -328,8 +361,6 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
* Provide <code>keepAfter</code> less than or equal to zero to skip that check.
|
||||
*
|
||||
* Items will be removed according to an approximate frecency calculation.
|
||||
*
|
||||
* Call this method within a transaction.
|
||||
*/
|
||||
private void expireHistory(final SQLiteDatabase db, final int retain, final long keepAfter) {
|
||||
Log.d(LOGTAG, "Expiring history.");
|
||||
@ -358,6 +389,8 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
"ORDER BY " + sortOrder + " LIMIT " + toRemove + ")";
|
||||
}
|
||||
trace("Deleting using query: " + sql);
|
||||
|
||||
beginWrite(db);
|
||||
db.execSQL(sql);
|
||||
}
|
||||
|
||||
@ -442,6 +475,7 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
public int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
|
||||
trace("Calling delete in transaction on URI: " + uri);
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
|
||||
final int match = URI_MATCHER.match(uri);
|
||||
int deleted = 0;
|
||||
|
||||
@ -469,6 +503,7 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
// fall through
|
||||
case HISTORY: {
|
||||
trace("Deleting history: " + uri);
|
||||
beginWrite(db);
|
||||
deleted = deleteHistory(uri, selection, selectionArgs);
|
||||
deleteUnusedImages(uri);
|
||||
break;
|
||||
@ -498,6 +533,7 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
// fall through
|
||||
case FAVICONS: {
|
||||
trace("Deleting favicons: " + uri);
|
||||
beginWrite(db);
|
||||
deleted = deleteFavicons(uri, selection, selectionArgs);
|
||||
break;
|
||||
}
|
||||
@ -511,6 +547,7 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
// fall through
|
||||
case THUMBNAILS: {
|
||||
trace("Deleting thumbnails: " + uri);
|
||||
beginWrite(db);
|
||||
deleted = deleteThumbnails(uri, selection, selectionArgs);
|
||||
break;
|
||||
}
|
||||
@ -577,6 +614,7 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
int match = URI_MATCHER.match(uri);
|
||||
int updated = 0;
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
switch (match) {
|
||||
// We provide a dedicated (hacky) API for callers to bulk-update the positions of
|
||||
// folder children by passing an array of GUID strings as `selectionArgs`.
|
||||
@ -589,13 +627,16 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
// `values` and `selection` are ignored.
|
||||
case BOOKMARKS_POSITIONS: {
|
||||
debug("Update on BOOKMARKS_POSITIONS: " + uri);
|
||||
|
||||
// This already starts and finishes its own transaction.
|
||||
updated = updateBookmarkPositions(uri, selectionArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case BOOKMARKS_PARENT: {
|
||||
debug("Update on BOOKMARKS_PARENT: " + uri);
|
||||
updated = updateBookmarkParents(uri, values, selection, selectionArgs);
|
||||
beginWrite(db);
|
||||
updated = updateBookmarkParents(db, values, selection, selectionArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -608,10 +649,11 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
// fall through
|
||||
case BOOKMARKS: {
|
||||
debug("Updating bookmark: " + uri);
|
||||
if (shouldUpdateOrInsert(uri))
|
||||
if (shouldUpdateOrInsert(uri)) {
|
||||
updated = updateOrInsertBookmark(uri, values, selection, selectionArgs);
|
||||
else
|
||||
} else {
|
||||
updated = updateBookmarks(uri, values, selection, selectionArgs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -624,10 +666,11 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
// fall through
|
||||
case HISTORY: {
|
||||
debug("Updating history: " + uri);
|
||||
if (shouldUpdateOrInsert(uri))
|
||||
if (shouldUpdateOrInsert(uri)) {
|
||||
updated = updateOrInsertHistory(uri, values, selection, selectionArgs);
|
||||
else
|
||||
} else {
|
||||
updated = updateHistory(uri, values, selection, selectionArgs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -643,11 +686,11 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
faviconSelectionArgs = new String[] { url };
|
||||
}
|
||||
|
||||
if (shouldUpdateOrInsert(uri))
|
||||
if (shouldUpdateOrInsert(uri)) {
|
||||
updated = updateOrInsertFavicon(uri, values, faviconSelection, faviconSelectionArgs);
|
||||
else
|
||||
} else {
|
||||
updated = updateExistingFavicon(uri, values, faviconSelection, faviconSelectionArgs);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -657,15 +700,15 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
String url = values.getAsString(Thumbnails.URL);
|
||||
|
||||
// if no URL is provided, update all of the entries
|
||||
if (TextUtils.isEmpty(values.getAsString(Thumbnails.URL)))
|
||||
if (TextUtils.isEmpty(values.getAsString(Thumbnails.URL))) {
|
||||
updated = updateExistingThumbnail(uri, values, null, null);
|
||||
else if (shouldUpdateOrInsert(uri))
|
||||
} else if (shouldUpdateOrInsert(uri)) {
|
||||
updated = updateOrInsertThumbnail(uri, values, Thumbnails.URL + " = ?",
|
||||
new String[] { url });
|
||||
else
|
||||
} else {
|
||||
updated = updateExistingThumbnail(uri, values, Thumbnails.URL + " = ?",
|
||||
new String[] { url });
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -674,7 +717,6 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
}
|
||||
|
||||
debug("Updated " + updated + " rows for URI: " + uri);
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
@ -876,40 +918,42 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
return cursor;
|
||||
}
|
||||
|
||||
int getUrlCount(SQLiteDatabase db, String table, String url) {
|
||||
Cursor c = db.query(table, new String[] { "COUNT(*)" },
|
||||
URLColumns.URL + " = ?", new String[] { url }, null, null,
|
||||
null);
|
||||
|
||||
int count = 0;
|
||||
|
||||
private static int getUrlCount(SQLiteDatabase db, String table, String url) {
|
||||
final Cursor c = db.query(table, new String[] { "COUNT(*)" },
|
||||
URLColumns.URL + " = ?", new String[] { url },
|
||||
null, null, null);
|
||||
try {
|
||||
if (c.moveToFirst())
|
||||
count = c.getInt(0);
|
||||
if (c.moveToFirst()) {
|
||||
return c.getInt(0);
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the positions of bookmarks in batches.
|
||||
*
|
||||
* Begins and ends its own transactions.
|
||||
*
|
||||
* @see #updateBookmarkPositionsInTransaction(SQLiteDatabase, String[], int, int)
|
||||
*/
|
||||
int updateBookmarkPositions(Uri uri, String[] guids) {
|
||||
if (guids == null)
|
||||
if (guids == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int guidsCount = guids.length;
|
||||
if (guidsCount == 0)
|
||||
if (guidsCount == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
int offset = 0;
|
||||
int updated = 0;
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
db.beginTransaction();
|
||||
|
||||
while (offset < guidsCount) {
|
||||
@ -942,8 +986,8 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
* Construct and execute an update expression that will modify the positions
|
||||
* of records in-place.
|
||||
*/
|
||||
int updateBookmarkPositionsInTransaction(final SQLiteDatabase db, final String[] guids,
|
||||
final int offset, final int max) {
|
||||
private static int updateBookmarkPositionsInTransaction(final SQLiteDatabase db, final String[] guids,
|
||||
final int offset, final int max) {
|
||||
int guidsCount = guids.length;
|
||||
int processCount = Math.min(max, guidsCount - offset);
|
||||
|
||||
@ -969,6 +1013,7 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
b.append(" WHEN ? THEN " + i);
|
||||
}
|
||||
|
||||
// TODO: use computeSQLInClause
|
||||
b.append(" END WHERE " + Bookmarks.GUID + " IN (");
|
||||
i = 1;
|
||||
while (i++ < processCount) {
|
||||
@ -985,13 +1030,13 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
* Construct an update expression that will modify the parents of any records
|
||||
* that match.
|
||||
*/
|
||||
int updateBookmarkParents(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
private int updateBookmarkParents(SQLiteDatabase db, ContentValues values, String selection, String[] selectionArgs) {
|
||||
trace("Updating bookmark parents of " + selection + " (" + selectionArgs[0] + ")");
|
||||
String where = Bookmarks._ID + " IN (" +
|
||||
" SELECT DISTINCT " + Bookmarks.PARENT +
|
||||
" FROM " + TABLE_BOOKMARKS +
|
||||
" WHERE " + selection + " )";
|
||||
return getWritableDatabase(uri).update(TABLE_BOOKMARKS, values, where, selectionArgs);
|
||||
return db.update(TABLE_BOOKMARKS, values, where, selectionArgs);
|
||||
}
|
||||
|
||||
long insertBookmark(Uri uri, ContentValues values) {
|
||||
@ -1017,10 +1062,10 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
}
|
||||
|
||||
String url = values.getAsString(Bookmarks.URL);
|
||||
Integer type = values.getAsInteger(Bookmarks.TYPE);
|
||||
|
||||
debug("Inserting bookmark in database with URL: " + url);
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
beginWrite(db);
|
||||
return db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.TITLE, values);
|
||||
}
|
||||
|
||||
@ -1028,9 +1073,11 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
int updateOrInsertBookmark(Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs) {
|
||||
int updated = updateBookmarks(uri, values, selection, selectionArgs);
|
||||
if (updated > 0)
|
||||
if (updated > 0) {
|
||||
return updated;
|
||||
}
|
||||
|
||||
// Transaction already begun by updateBookmarks.
|
||||
if (0 <= insertBookmark(uri, values)) {
|
||||
// We 'updated' one row.
|
||||
return 1;
|
||||
@ -1044,49 +1091,35 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
String[] selectionArgs) {
|
||||
trace("Updating bookmarks on URI: " + uri);
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
int updated = 0;
|
||||
|
||||
final String[] bookmarksProjection = new String[] {
|
||||
Bookmarks._ID, // 0
|
||||
Bookmarks.URL, // 1
|
||||
};
|
||||
|
||||
trace("Quering bookmarks to update on URI: " + uri);
|
||||
|
||||
Cursor cursor = db.query(TABLE_BOOKMARKS, bookmarksProjection,
|
||||
selection, selectionArgs, null, null, null);
|
||||
|
||||
try {
|
||||
if (!values.containsKey(Bookmarks.DATE_MODIFIED))
|
||||
values.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis());
|
||||
|
||||
boolean updatingUrl = values.containsKey(Bookmarks.URL);
|
||||
String url = null;
|
||||
|
||||
if (updatingUrl)
|
||||
url = values.getAsString(Bookmarks.URL);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
long id = cursor.getLong(0);
|
||||
|
||||
trace("Updating bookmark with ID: " + id);
|
||||
|
||||
updated += db.update(TABLE_BOOKMARKS, values, "_id = ?",
|
||||
new String[] { Long.toString(id) });
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
if (!values.containsKey(Bookmarks.DATE_MODIFIED)) {
|
||||
values.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
return updated;
|
||||
trace("Querying bookmarks to update on URI: " + uri);
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
|
||||
// Compute matching IDs.
|
||||
final Cursor cursor = db.query(TABLE_BOOKMARKS, bookmarksProjection,
|
||||
selection, selectionArgs, null, null, null);
|
||||
|
||||
// Now that we're done reading, open a transaction.
|
||||
final String inClause;
|
||||
try {
|
||||
inClause = computeSQLInClauseFromLongs(cursor, Bookmarks._ID);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
beginWrite(db);
|
||||
return db.update(TABLE_BOOKMARKS, values, inClause, null);
|
||||
}
|
||||
|
||||
long insertHistory(Uri uri, ContentValues values) {
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
final long now = System.currentTimeMillis();
|
||||
values.put(History.DATE_CREATED, now);
|
||||
values.put(History.DATE_MODIFIED, now);
|
||||
|
||||
@ -1098,20 +1131,25 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
String url = values.getAsString(History.URL);
|
||||
|
||||
debug("Inserting history in database with URL: " + url);
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
beginWrite(db);
|
||||
return db.insertOrThrow(TABLE_HISTORY, History.VISITS, values);
|
||||
}
|
||||
|
||||
int updateOrInsertHistory(Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs) {
|
||||
int updated = updateHistory(uri, values, selection, selectionArgs);
|
||||
if (updated > 0)
|
||||
final int updated = updateHistory(uri, values, selection, selectionArgs);
|
||||
if (updated > 0) {
|
||||
return updated;
|
||||
}
|
||||
|
||||
// Insert a new entry if necessary
|
||||
if (!values.containsKey(History.VISITS))
|
||||
if (!values.containsKey(History.VISITS)) {
|
||||
values.put(History.VISITS, 1);
|
||||
if (!values.containsKey(History.TITLE))
|
||||
}
|
||||
if (!values.containsKey(History.TITLE)) {
|
||||
values.put(History.TITLE, values.getAsString(History.URL));
|
||||
}
|
||||
|
||||
if (0 <= insertHistory(uri, values)) {
|
||||
return 1;
|
||||
@ -1124,7 +1162,6 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
String[] selectionArgs) {
|
||||
trace("Updating history on URI: " + uri);
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
int updated = 0;
|
||||
|
||||
final String[] historyProjection = new String[] {
|
||||
@ -1133,20 +1170,15 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
History.VISITS // 2
|
||||
};
|
||||
|
||||
Cursor cursor = db.query(TABLE_HISTORY, historyProjection, selection,
|
||||
selectionArgs, null, null, null);
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
final Cursor cursor = db.query(TABLE_HISTORY, historyProjection, selection,
|
||||
selectionArgs, null, null, null);
|
||||
|
||||
try {
|
||||
if (!values.containsKey(Bookmarks.DATE_MODIFIED)) {
|
||||
values.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
boolean updatingUrl = values.containsKey(History.URL);
|
||||
String url = null;
|
||||
|
||||
if (updatingUrl)
|
||||
url = values.getAsString(History.URL);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
long id = cursor.getLong(0);
|
||||
|
||||
@ -1161,11 +1193,10 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
}
|
||||
|
||||
updated += db.update(TABLE_HISTORY, values, "_id = ?",
|
||||
new String[] { Long.toString(id) });
|
||||
new String[] { Long.toString(id) });
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return updated;
|
||||
@ -1193,7 +1224,6 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
// If changes are needed, please update both
|
||||
String faviconUrl = values.getAsString(Favicons.URL);
|
||||
String pageUrl = null;
|
||||
long faviconId;
|
||||
|
||||
trace("Inserting favicon for URL: " + faviconUrl);
|
||||
|
||||
@ -1210,15 +1240,16 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
values.put(Favicons.URL, org.mozilla.gecko.favicons.Favicons.guessDefaultFaviconURL(pageUrl));
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
final long now = System.currentTimeMillis();
|
||||
values.put(Favicons.DATE_CREATED, now);
|
||||
values.put(Favicons.DATE_MODIFIED, now);
|
||||
faviconId = db.insertOrThrow(TABLE_FAVICONS, null, values);
|
||||
|
||||
beginWrite(db);
|
||||
final long faviconId = db.insertOrThrow(TABLE_FAVICONS, null, values);
|
||||
|
||||
if (pageUrl != null) {
|
||||
updateFaviconIdsForUrl(db, pageUrl, faviconId);
|
||||
}
|
||||
|
||||
return faviconId;
|
||||
}
|
||||
|
||||
@ -1239,8 +1270,6 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
String faviconUrl = values.getAsString(Favicons.URL);
|
||||
String pageUrl = null;
|
||||
int updated = 0;
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
Cursor cursor = null;
|
||||
Long faviconId = null;
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
@ -1256,33 +1285,38 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
|
||||
values.put(Favicons.DATE_MODIFIED, now);
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
|
||||
// If there's no favicon URL given and we're inserting if needed, skip
|
||||
// the update and only do an insert (otherwise all rows would be
|
||||
// updated)
|
||||
// updated).
|
||||
if (!(insertIfNeeded && (faviconUrl == null))) {
|
||||
updated = db.update(TABLE_FAVICONS, values, selection, selectionArgs);
|
||||
}
|
||||
|
||||
if (updated > 0) {
|
||||
if ((faviconUrl != null) && (pageUrl != null)) {
|
||||
final Cursor cursor = db.query(TABLE_FAVICONS,
|
||||
new String[] { Favicons._ID },
|
||||
Favicons.URL + " = ?",
|
||||
new String[] { faviconUrl },
|
||||
null, null, null);
|
||||
try {
|
||||
cursor = db.query(TABLE_FAVICONS,
|
||||
new String[] { Favicons._ID },
|
||||
Favicons.URL + " = ?",
|
||||
new String[] { faviconUrl },
|
||||
null, null, null);
|
||||
if (cursor.moveToFirst()) {
|
||||
faviconId = cursor.getLong(cursor.getColumnIndexOrThrow(Favicons._ID));
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
if (pageUrl != null) {
|
||||
beginWrite(db);
|
||||
}
|
||||
} else if (insertIfNeeded) {
|
||||
values.put(Favicons.DATE_CREATED, now);
|
||||
|
||||
trace("No update, inserting favicon for URL: " + faviconUrl);
|
||||
beginWrite(db);
|
||||
faviconId = db.insert(TABLE_FAVICONS, null, values);
|
||||
updated = 1;
|
||||
}
|
||||
@ -1294,40 +1328,40 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
return updated;
|
||||
}
|
||||
|
||||
long insertThumbnail(Uri uri, ContentValues values) {
|
||||
String url = values.getAsString(Thumbnails.URL);
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
private long insertThumbnail(Uri uri, ContentValues values) {
|
||||
final String url = values.getAsString(Thumbnails.URL);
|
||||
|
||||
trace("Inserting thumbnail for URL: " + url);
|
||||
|
||||
DBUtils.stripEmptyByteArray(values, Thumbnails.DATA);
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
beginWrite(db);
|
||||
return db.insertOrThrow(TABLE_THUMBNAILS, null, values);
|
||||
}
|
||||
|
||||
int updateOrInsertThumbnail(Uri uri, ContentValues values, String selection,
|
||||
private int updateOrInsertThumbnail(Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs) {
|
||||
return updateThumbnail(uri, values, selection, selectionArgs,
|
||||
true /* insert if needed */);
|
||||
}
|
||||
|
||||
int updateExistingThumbnail(Uri uri, ContentValues values, String selection,
|
||||
private int updateExistingThumbnail(Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs) {
|
||||
return updateThumbnail(uri, values, selection, selectionArgs,
|
||||
false /* only update, no insert */);
|
||||
}
|
||||
|
||||
int updateThumbnail(Uri uri, ContentValues values, String selection,
|
||||
private int updateThumbnail(Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs, boolean insertIfNeeded) {
|
||||
String url = values.getAsString(Thumbnails.URL);
|
||||
int updated = 0;
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
|
||||
final String url = values.getAsString(Thumbnails.URL);
|
||||
DBUtils.stripEmptyByteArray(values, Thumbnails.DATA);
|
||||
|
||||
trace("Updating thumbnail for URL: " + url);
|
||||
|
||||
updated = db.update(TABLE_THUMBNAILS, values, selection, selectionArgs);
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
beginWrite(db);
|
||||
int updated = db.update(TABLE_THUMBNAILS, values, selection, selectionArgs);
|
||||
|
||||
if (updated == 0 && insertIfNeeded) {
|
||||
trace("No update, inserting thumbnail for URL: " + url);
|
||||
@ -1338,6 +1372,12 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method does not create a new transaction. Its first operation is
|
||||
* guaranteed to be a write, which in the case of a new enclosing
|
||||
* transaction will guarantee that a read does not need to be upgraded to
|
||||
* a write.
|
||||
*/
|
||||
int deleteHistory(Uri uri, String selection, String[] selectionArgs) {
|
||||
debug("Deleting history entry for URI: " + uri);
|
||||
|
||||
@ -1360,8 +1400,20 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
values.put(History.VISITS, 0);
|
||||
values.put(History.DATE_MODIFIED, System.currentTimeMillis());
|
||||
|
||||
cleanupSomeDeletedRecords(uri, History.CONTENT_URI, TABLE_HISTORY);
|
||||
return db.update(TABLE_HISTORY, values, selection, selectionArgs);
|
||||
// Doing this UPDATE (or the DELETE above) first ensures that the
|
||||
// first operation within a new enclosing transaction is a write.
|
||||
// The cleanup call below will do a SELECT first, and thus would
|
||||
// require the transaction to be upgraded from a reader to a writer.
|
||||
// In some cases that upgrade can fail (SQLITE_BUSY), so we avoid
|
||||
// it if we can.
|
||||
final int updated = db.update(TABLE_HISTORY, values, selection, selectionArgs);
|
||||
try {
|
||||
cleanupSomeDeletedRecords(uri, History.CONTENT_URI, TABLE_HISTORY);
|
||||
} catch (Exception e) {
|
||||
// We don't care.
|
||||
Log.e(LOGTAG, "Unable to clean up deleted history records: ", e);
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
int deleteBookmarks(Uri uri, String selection, String[] selectionArgs) {
|
||||
@ -1370,6 +1422,7 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
|
||||
if (isCallerSync(uri)) {
|
||||
beginWrite(db);
|
||||
return db.delete(TABLE_BOOKMARKS, selection, selectionArgs);
|
||||
}
|
||||
|
||||
@ -1378,8 +1431,18 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Bookmarks.IS_DELETED, 1);
|
||||
|
||||
cleanupSomeDeletedRecords(uri, Bookmarks.CONTENT_URI, TABLE_BOOKMARKS);
|
||||
return updateBookmarks(uri, values, selection, selectionArgs);
|
||||
// Doing this UPDATE (or the DELETE above) first ensures that the
|
||||
// first operation within this transaction is a write.
|
||||
// The cleanup call below will do a SELECT first, and thus would
|
||||
// require the transaction to be upgraded from a reader to a writer.
|
||||
final int updated = updateBookmarks(uri, values, selection, selectionArgs);
|
||||
try {
|
||||
cleanupSomeDeletedRecords(uri, Bookmarks.CONTENT_URI, TABLE_BOOKMARKS);
|
||||
} catch (Exception e) {
|
||||
// We don't care.
|
||||
Log.e(LOGTAG, "Unable to clean up deleted bookmark records: ", e);
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
int deleteFavicons(Uri uri, String selection, String[] selectionArgs) {
|
||||
@ -1430,29 +1493,36 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
throws OperationApplicationException {
|
||||
final int numOperations = operations.size();
|
||||
final ContentProviderResult[] results = new ContentProviderResult[numOperations];
|
||||
boolean failures = false;
|
||||
SQLiteDatabase db = null;
|
||||
|
||||
if (numOperations >= 1) {
|
||||
// We only have 1 database for all Uri's that we can get
|
||||
db = getWritableDatabase(operations.get(0).getUri());
|
||||
} else {
|
||||
if (numOperations < 1) {
|
||||
debug("applyBatch: no operations; returning immediately.");
|
||||
// The original Android implementation returns a zero-length
|
||||
// array in this case, we do the same.
|
||||
// array in this case. We do the same.
|
||||
return results;
|
||||
}
|
||||
|
||||
boolean failures = false;
|
||||
|
||||
// We only have 1 database for all Uris that we can get.
|
||||
SQLiteDatabase db = getWritableDatabase(operations.get(0).getUri());
|
||||
|
||||
// Note that the apply() call may cause us to generate
|
||||
// additional transactions for the invidual operations.
|
||||
// additional transactions for the individual operations.
|
||||
// But Android's wrapper for SQLite supports nested transactions,
|
||||
// so this will do the right thing.
|
||||
db.beginTransaction();
|
||||
//
|
||||
// Note further that in some circumstances this can result in
|
||||
// exceptions: if this transaction is first involved in reading,
|
||||
// and then (naturally) tries to perform writes, SQLITE_BUSY can
|
||||
// be raised. See Bug 947939 and friends.
|
||||
beginBatch(db);
|
||||
|
||||
for (int i = 0; i < numOperations; i++) {
|
||||
try {
|
||||
results[i] = operations.get(i).apply(this, results, i);
|
||||
final ContentProviderOperation operation = operations.get(i);
|
||||
results[i] = operation.apply(this, results, i);
|
||||
} catch (SQLException e) {
|
||||
Log.w(LOGTAG, "SQLite Exception during applyBatch: ", e);
|
||||
Log.w(LOGTAG, "SQLite Exception during applyBatch.", e);
|
||||
// The Android API makes it implementation-defined whether
|
||||
// the failure of a single operation makes all others abort
|
||||
// or not. For our use cases, best-effort operation makes
|
||||
@ -1485,8 +1555,8 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
|
||||
}
|
||||
|
||||
trace("Flushing DB applyBatch...");
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
markBatchSuccessful(db);
|
||||
endBatch(db);
|
||||
|
||||
if (failures) {
|
||||
throw new OperationApplicationException();
|
||||
|
@ -57,21 +57,23 @@ public abstract class TransactionalProvider<T extends SQLiteOpenHelper> extends
|
||||
/*
|
||||
* Deletes items from the database within a DB transaction.
|
||||
*
|
||||
* @param uri query URI
|
||||
* @param selection An optional filter to match rows to update.
|
||||
* @param selectionArgs arguments for the selection
|
||||
* @return number of rows impacted by the deletion
|
||||
* @param uri Query URI.
|
||||
* @param selection An optional filter to match rows to delete.
|
||||
* @param selectionArgs An array of arguments to substitute into the selection.
|
||||
*
|
||||
* @return number of rows impacted by the deletion.
|
||||
*/
|
||||
abstract protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs);
|
||||
|
||||
/*
|
||||
* Updates the database within a DB transaction.
|
||||
*
|
||||
* @param uri Query URI
|
||||
* @param uri Query URI.
|
||||
* @param values A set of column_name/value pairs to add to the database.
|
||||
* @param selection An optional filter to match rows to update.
|
||||
* @param selectionArgs Arguments for the selection
|
||||
* @return number of rows impacted by the update
|
||||
* @param selectionArgs An array of arguments to substitute into the selection.
|
||||
*
|
||||
* @return number of rows impacted by the update.
|
||||
*/
|
||||
abstract protected int updateInTransaction(Uri uri, ContentValues values, String selection, String[] selectionArgs);
|
||||
|
||||
@ -109,6 +111,10 @@ public abstract class TransactionalProvider<T extends SQLiteOpenHelper> extends
|
||||
return mDatabases.getDatabaseHelperForProfile(profile, isTest(uri)).getWritableDatabase();
|
||||
}
|
||||
|
||||
protected SQLiteDatabase getWritableDatabaseForProfile(String profile, boolean isTest) {
|
||||
return mDatabases.getDatabaseHelperForProfile(profile, isTest).getWritableDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
synchronized (this) {
|
||||
@ -125,25 +131,133 @@ public abstract class TransactionalProvider<T extends SQLiteOpenHelper> extends
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if OS version and database parallelism support indicates
|
||||
* that this provider should bundle writes into transactions.
|
||||
*/
|
||||
@SuppressWarnings("static-method")
|
||||
protected boolean shouldUseTransactions() {
|
||||
return Build.VERSION.SDK_INT >= 11;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track whether we're in a batch operation.
|
||||
*
|
||||
* When we're in a batch operation, individual write steps won't even try
|
||||
* to start a transaction... and neither will they attempt to finish one.
|
||||
*
|
||||
* Set this to <code>Boolean.TRUE</code> when you're entering a batch --
|
||||
* a section of code in which {@link ContentProvider} methods will be
|
||||
* called, but nested transactions should not be started. Callers are
|
||||
* responsible for beginning and ending the enclosing transaction, and
|
||||
* for setting this to <code>Boolean.FALSE</code> when done.
|
||||
*
|
||||
* This is a ThreadLocal separate from `db.inTransaction` because batched
|
||||
* operations start transactions independent of individual ContentProvider
|
||||
* operations. This doesn't work well with the entire concept of this
|
||||
* abstract class -- that is, automatically beginning and ending transactions
|
||||
* for each insert/delete/update operation -- and doing so without
|
||||
* causing arbitrary nesting requires external tracking.
|
||||
*
|
||||
* Note that beginWrite takes a DB argument, but we don't differentiate
|
||||
* between databases in this tracking flag. If your ContentProvider manages
|
||||
* multiple database transactions within the same thread, you'll need to
|
||||
* amend this scheme -- but then, you're already doing some serious wizardry,
|
||||
* so rock on.
|
||||
*/
|
||||
final ThreadLocal<Boolean> isInBatchOperation = new ThreadLocal<Boolean>();
|
||||
|
||||
private boolean isInBatch() {
|
||||
final Boolean isInBatch = isInBatchOperation.get();
|
||||
if (isInBatch == null) {
|
||||
return false;
|
||||
}
|
||||
return isInBatch.booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* If we're not currently in a transaction, and we should be, start one.
|
||||
*/
|
||||
protected void beginWrite(final SQLiteDatabase db) {
|
||||
if (isInBatch()) {
|
||||
trace("Not bothering with an intermediate write transaction: inside batch operation.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldUseTransactions() && !db.inTransaction()) {
|
||||
trace("beginWrite: beginning transaction.");
|
||||
db.beginTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we're not in a batch, but we are in a write transaction, mark it as
|
||||
* successful.
|
||||
*/
|
||||
protected void markWriteSuccessful(final SQLiteDatabase db) {
|
||||
if (isInBatch()) {
|
||||
trace("Not marking write successful: inside batch operation.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldUseTransactions() && db.inTransaction()) {
|
||||
trace("Marking write transaction successful.");
|
||||
db.setTransactionSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we're not in a batch, but we are in a write transaction,
|
||||
* end it.
|
||||
*
|
||||
* @see TransactionalProvider#markWriteSuccessful(SQLiteDatabase)
|
||||
*/
|
||||
protected void endWrite(final SQLiteDatabase db) {
|
||||
if (isInBatch()) {
|
||||
trace("Not ending write: inside batch operation.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldUseTransactions() && db.inTransaction()) {
|
||||
trace("endWrite: ending transaction.");
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
protected void beginBatch(final SQLiteDatabase db) {
|
||||
trace("Beginning batch.");
|
||||
isInBatchOperation.set(Boolean.TRUE);
|
||||
db.beginTransaction();
|
||||
}
|
||||
|
||||
protected void markBatchSuccessful(final SQLiteDatabase db) {
|
||||
if (isInBatch()) {
|
||||
trace("Marking batch successful.");
|
||||
db.setTransactionSuccessful();
|
||||
return;
|
||||
}
|
||||
Log.w(LOGTAG, "Unexpectedly asked to mark batch successful, but not in batch!");
|
||||
throw new IllegalStateException("Not in batch.");
|
||||
}
|
||||
|
||||
protected void endBatch(final SQLiteDatabase db) {
|
||||
trace("Ending batch.");
|
||||
db.endTransaction();
|
||||
isInBatchOperation.set(Boolean.FALSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
trace("Calling delete on URI: " + uri);
|
||||
trace("Calling delete on URI: " + uri + ", " + selection + ", " + selectionArgs);
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
int deleted = 0;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
trace("Beginning delete transaction: " + uri);
|
||||
db.beginTransaction();
|
||||
try {
|
||||
deleted = deleteInTransaction(uri, selection, selectionArgs);
|
||||
db.setTransactionSuccessful();
|
||||
trace("Successful delete transaction: " + uri);
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
deleted = deleteInTransaction(uri, selection, selectionArgs);
|
||||
markWriteSuccessful(db);
|
||||
} finally {
|
||||
endWrite(db);
|
||||
}
|
||||
|
||||
if (deleted > 0) {
|
||||
@ -160,23 +274,14 @@ public abstract class TransactionalProvider<T extends SQLiteOpenHelper> extends
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
Uri result = null;
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
trace("Beginning insert transaction: " + uri);
|
||||
db.beginTransaction();
|
||||
try {
|
||||
result = insertInTransaction(uri, values);
|
||||
db.setTransactionSuccessful();
|
||||
trace("Successful insert transaction: " + uri);
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
} else {
|
||||
result = insertInTransaction(uri, values);
|
||||
}
|
||||
result = insertInTransaction(uri, values);
|
||||
markWriteSuccessful(db);
|
||||
} catch (SQLException sqle) {
|
||||
Log.e(LOGTAG, "exception in DB operation", sqle);
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
Log.e(LOGTAG, "don't know how to perform that insert", uoe);
|
||||
} finally {
|
||||
endWrite(db);
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
@ -190,23 +295,17 @@ public abstract class TransactionalProvider<T extends SQLiteOpenHelper> extends
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs) {
|
||||
trace("Calling update on URI: " + uri);
|
||||
trace("Calling update on URI: " + uri + ", " + selection + ", " + selectionArgs);
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
int updated = 0;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
trace("Beginning update transaction: " + uri);
|
||||
db.beginTransaction();
|
||||
try {
|
||||
updated = updateInTransaction(uri, values, selection, selectionArgs);
|
||||
db.setTransactionSuccessful();
|
||||
trace("Successful update transaction: " + uri);
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
} else {
|
||||
updated = updateInTransaction(uri, values, selection, selectionArgs);
|
||||
try {
|
||||
updated = updateInTransaction(uri, values, selection,
|
||||
selectionArgs);
|
||||
markWriteSuccessful(db);
|
||||
} finally {
|
||||
endWrite(db);
|
||||
}
|
||||
|
||||
if (updated > 0) {
|
||||
@ -227,7 +326,8 @@ public abstract class TransactionalProvider<T extends SQLiteOpenHelper> extends
|
||||
|
||||
final SQLiteDatabase db = getWritableDatabase(uri);
|
||||
|
||||
db.beginTransaction();
|
||||
debug("bulkInsert: explicitly starting transaction.");
|
||||
beginBatch(db);
|
||||
|
||||
try {
|
||||
for (int i = 0; i < numValues; i++) {
|
||||
@ -235,9 +335,10 @@ public abstract class TransactionalProvider<T extends SQLiteOpenHelper> extends
|
||||
successes++;
|
||||
}
|
||||
trace("Flushing DB bulkinsert...");
|
||||
db.setTransactionSuccessful();
|
||||
markBatchSuccessful(db);
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
debug("bulkInsert: explicitly ending transaction.");
|
||||
endBatch(db);
|
||||
}
|
||||
|
||||
if (successes > 0) {
|
||||
|
@ -297,11 +297,14 @@ gbjar.sources += [
|
||||
'PrivateTab.java',
|
||||
'prompts/ColorPickerInput.java',
|
||||
'prompts/IconGridInput.java',
|
||||
'prompts/IntentChooserPrompt.java',
|
||||
'prompts/IntentHandler.java',
|
||||
'prompts/Prompt.java',
|
||||
'prompts/PromptInput.java',
|
||||
'prompts/PromptListAdapter.java',
|
||||
'prompts/PromptListItem.java',
|
||||
'prompts/PromptService.java',
|
||||
'prompts/TabInput.java',
|
||||
'ReaderModeUtils.java',
|
||||
'ReferrerReceiver.java',
|
||||
'RemoteTabs.java',
|
||||
|
157
mobile/android/base/prompts/IntentChooserPrompt.java
Normal file
157
mobile/android/base/prompts/IntentChooserPrompt.java
Normal file
@ -0,0 +1,157 @@
|
||||
/* 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.prompts;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.widget.GeckoActionProvider;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.widget.ListView;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Shows a prompt letting the user pick from a list of intent handlers for a set of Intents or
|
||||
* for a GeckoActionProvider. Basic usage:
|
||||
* IntentChooserPrompt prompt = new IntentChooserPrompt(context, new Intent[] {
|
||||
* ... // some intents
|
||||
* });
|
||||
* prompt.show("Title", context, new IntentHandler() {
|
||||
* public void onIntentSelected(Intent intent, int position) { }
|
||||
* public void onCancelled() { }
|
||||
* });
|
||||
**/
|
||||
public class IntentChooserPrompt {
|
||||
private static final String LOGTAG = "GeckoIntentChooser";
|
||||
|
||||
private final ArrayList<PromptListItem> mItems;
|
||||
|
||||
public IntentChooserPrompt(Context context, Intent[] intents) {
|
||||
mItems = getItems(context, intents);
|
||||
}
|
||||
|
||||
public IntentChooserPrompt(Context context, GeckoActionProvider provider) {
|
||||
mItems = getItems(context, provider);
|
||||
}
|
||||
|
||||
/* If an IntentHandler is passed in, will asynchronously call the handler when the dialog is closed
|
||||
* Otherwise, will return the Intent that was chosen by the user. Must be called on the UI thread.
|
||||
*/
|
||||
public void show(final String title, final Context context, final IntentHandler handler) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
|
||||
if (mItems.isEmpty()) {
|
||||
Log.i(LOGTAG, "No activities for the intent chooser!");
|
||||
handler.onCancelled();
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's only one item in the intent list, just return it
|
||||
if (mItems.size() == 1) {
|
||||
handler.onIntentSelected(mItems.get(0).intent, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
final Prompt prompt = new Prompt(context, new Prompt.PromptCallback() {
|
||||
@Override
|
||||
public void onPromptFinished(String promptServiceResult) {
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int itemId = -1;
|
||||
try {
|
||||
itemId = new JSONObject(promptServiceResult).getInt("button");
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "result from promptservice was invalid: ", e);
|
||||
}
|
||||
|
||||
if (itemId == -1) {
|
||||
handler.onCancelled();
|
||||
} else {
|
||||
handler.onIntentSelected(mItems.get(itemId).intent, itemId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
PromptListItem[] arrays = new PromptListItem[mItems.size()];
|
||||
mItems.toArray(arrays);
|
||||
prompt.show(title, "", arrays, ListView.CHOICE_MODE_NONE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Whether or not any activities were found. Useful for checking if you should try a different Intent set
|
||||
public boolean hasActivities(Context context) {
|
||||
return mItems.isEmpty();
|
||||
}
|
||||
|
||||
// Gets a list of PromptListItems for an Intent array
|
||||
private ArrayList<PromptListItem> getItems(final Context context, Intent[] intents) {
|
||||
final ArrayList<PromptListItem> items = new ArrayList<PromptListItem>();
|
||||
|
||||
// If we have intents, use them to build the initial list
|
||||
for (final Intent intent : intents) {
|
||||
items.addAll(getItemsForIntent(context, intent));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
// Gets a list of PromptListItems for a GeckoActionProvider
|
||||
private ArrayList<PromptListItem> getItems(final Context context, final GeckoActionProvider provider) {
|
||||
final ArrayList<PromptListItem> items = new ArrayList<PromptListItem>();
|
||||
|
||||
// Add any intents from the provider.
|
||||
final PackageManager packageManager = context.getPackageManager();
|
||||
final ArrayList<ResolveInfo> infos = provider.getSortedActivites();
|
||||
|
||||
for (final ResolveInfo info : infos) {
|
||||
items.add(getItemForResolveInfo(info, packageManager, provider.getIntent()));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private PromptListItem getItemForResolveInfo(ResolveInfo info, PackageManager pm, Intent intent) {
|
||||
PromptListItem item = new PromptListItem(info.loadLabel(pm).toString());
|
||||
item.icon = info.loadIcon(pm);
|
||||
item.intent = new Intent(intent);
|
||||
|
||||
// These intents should be implicit.
|
||||
item.intent.setComponent(new ComponentName(info.activityInfo.applicationInfo.packageName,
|
||||
info.activityInfo.name));
|
||||
return item;
|
||||
}
|
||||
|
||||
private ArrayList<PromptListItem> getItemsForIntent(Context context, Intent intent) {
|
||||
ArrayList<PromptListItem> items = new ArrayList<PromptListItem>();
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> lri = pm.queryIntentActivityOptions(GeckoAppShell.getGeckoInterface().getActivity().getComponentName(), null, intent, 0);
|
||||
|
||||
// If we didn't find any activities, just return the empty list
|
||||
if (lri == null) {
|
||||
return items;
|
||||
}
|
||||
|
||||
// Otherwise, convert the ResolveInfo. Note we don't currently check for duplicates here.
|
||||
for (ResolveInfo ri : lri) {
|
||||
items.add(getItemForResolveInfo(ri, pm, intent));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
12
mobile/android/base/prompts/IntentHandler.java
Normal file
12
mobile/android/base/prompts/IntentHandler.java
Normal file
@ -0,0 +1,12 @@
|
||||
/* 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.prompts;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
public interface IntentHandler {
|
||||
public void onIntentSelected(Intent intent, int position);
|
||||
public void onCancelled();
|
||||
}
|
@ -76,6 +76,7 @@ public class PromptInput {
|
||||
mView = (View)input;
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
EditText edit = (EditText)mView;
|
||||
@ -111,6 +112,7 @@ public class PromptInput {
|
||||
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
EditText edit = (EditText)mView;
|
||||
@ -135,6 +137,7 @@ public class PromptInput {
|
||||
mView = (View)checkbox;
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
CheckBox checkbox = (CheckBox)mView;
|
||||
@ -213,6 +216,7 @@ public class PromptInput {
|
||||
private static String formatDateString(String dateFormat, Calendar calendar) {
|
||||
return new SimpleDateFormat(dateFormat).format(calendar.getTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
if (Build.VERSION.SDK_INT < 11 && mType.equals("date")) {
|
||||
@ -293,6 +297,7 @@ public class PromptInput {
|
||||
|
||||
return spinner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return new Integer(spinner.getSelectedItemPosition());
|
||||
@ -312,10 +317,6 @@ public class PromptInput {
|
||||
mView = view;
|
||||
return mView;
|
||||
}
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public PromptInput(JSONObject obj) {
|
||||
@ -344,6 +345,8 @@ public class PromptInput {
|
||||
return new IconGridInput(obj);
|
||||
} else if (ColorPickerInput.INPUT_TYPE.equals(type)) {
|
||||
return new ColorPickerInput(obj);
|
||||
} else if (TabInput.INPUT_TYPE.equals(type)) {
|
||||
return new TabInput(obj);
|
||||
} else {
|
||||
for (String dtType : DateTimeInput.INPUT_TYPES) {
|
||||
if (dtType.equals(type)) {
|
||||
@ -363,7 +366,7 @@ public class PromptInput {
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean getScrollable() {
|
||||
@ -371,6 +374,6 @@ public class PromptInput {
|
||||
}
|
||||
|
||||
public boolean canApplyInputStyle() {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@ -17,6 +19,7 @@ public class PromptListItem {
|
||||
public final boolean disabled;
|
||||
public final int id;
|
||||
public boolean selected;
|
||||
public Intent intent;
|
||||
|
||||
public boolean isParent;
|
||||
public Drawable icon;
|
||||
|
109
mobile/android/base/prompts/TabInput.java
Normal file
109
mobile/android/base/prompts/TabInput.java
Normal file
@ -0,0 +1,109 @@
|
||||
/* -*- 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.prompts;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.TabWidget;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
import java.util.LinkedHashMap;
|
||||
import android.util.Log;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
public class TabInput extends PromptInput implements AdapterView.OnItemClickListener {
|
||||
public static final String INPUT_TYPE = "tabs";
|
||||
public static final String LOGTAG = "GeckoTabInput";
|
||||
|
||||
/* Keeping the order of this in sync with the JSON is important. */
|
||||
final private LinkedHashMap<String, PromptListItem[]> mTabs;
|
||||
|
||||
private TabHost mHost;
|
||||
private int mPosition;
|
||||
|
||||
public TabInput(JSONObject obj) {
|
||||
super(obj);
|
||||
mTabs = new LinkedHashMap<String, PromptListItem[]>();
|
||||
try {
|
||||
JSONArray tabs = obj.getJSONArray("items");
|
||||
for (int i = 0; i < tabs.length(); i++) {
|
||||
JSONObject tab = tabs.getJSONObject(i);
|
||||
String title = tab.getString("label");
|
||||
JSONArray items = tab.getJSONArray("items");
|
||||
mTabs.put(title, PromptListItem.getArray(items));
|
||||
}
|
||||
} catch(JSONException ex) {
|
||||
Log.e(LOGTAG, "Exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Context context) throws UnsupportedOperationException {
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
mHost = (TabHost) inflater.inflate(R.layout.tab_prompt_input, null);
|
||||
mHost.setup();
|
||||
|
||||
for (String title : mTabs.keySet()) {
|
||||
final TabHost.TabSpec spec = mHost.newTabSpec(title);
|
||||
spec.setContent(new TabHost.TabContentFactory() {
|
||||
@Override
|
||||
public View createTabContent(final String tag) {
|
||||
PromptListAdapter adapter = new PromptListAdapter(context, android.R.layout.simple_list_item_1, mTabs.get(tag));
|
||||
ListView listView = new ListView(context);
|
||||
listView.setOnItemClickListener(TabInput.this);
|
||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
listView.setAdapter(adapter);
|
||||
return listView;
|
||||
}
|
||||
});
|
||||
|
||||
spec.setIndicator(title);
|
||||
mHost.addTab(spec);
|
||||
}
|
||||
mView = mHost;
|
||||
return mHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("tab", mHost.getCurrentTab());
|
||||
obj.put("item", mPosition);
|
||||
} catch(JSONException ex) { }
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScrollable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canApplyInputStyle() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
mPosition = position;
|
||||
}
|
||||
|
||||
}
|
31
mobile/android/base/resources/layout/tab_prompt_input.xml
Normal file
31
mobile/android/base/resources/layout/tab_prompt_input.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/tabhost"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:minHeight="100dp">
|
||||
|
||||
<TabWidget
|
||||
android:id="@android:id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
</TabWidget>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@android:id/tabcontent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</TabHost>
|
@ -65,17 +65,41 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID });
|
||||
|
||||
c = mProvider.query(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null);
|
||||
mAsserter.is(c.getCount(), 7, "All non-special bookmarks and folders were deleted");
|
||||
assertCountIsAndClose(c, 7, "All non-special bookmarks and folders were deleted");
|
||||
|
||||
mProvider.delete(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
|
||||
c = mProvider.query(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null);
|
||||
mAsserter.is(c.getCount(), 0, "All history entries were deleted");
|
||||
assertCountIsAndClose(c, 0, "All history entries were deleted");
|
||||
|
||||
mProvider.delete(BrowserContract.Favicons.CONTENT_URI, null, null);
|
||||
mAsserter.is(c.getCount(), 0, "All favicons were deleted");
|
||||
/**
|
||||
* There's no reason why the following two parts should fail.
|
||||
* But sometimes they do, and I'm not going to spend the time
|
||||
* to figure out why in an unrelated bug.
|
||||
*/
|
||||
|
||||
mProvider.delete(BrowserContract.Thumbnails.CONTENT_URI, null, null);
|
||||
mAsserter.is(c.getCount(), 0, "All thumbnails were deleted");
|
||||
mProvider.delete(appendUriParam(BrowserContract.Favicons.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
|
||||
c = mProvider.query(appendUriParam(BrowserContract.Favicons.CONTENT_URI,
|
||||
BrowserContract.PARAM_SHOW_DELETED, "1"),
|
||||
null, null, null, null);
|
||||
|
||||
if (c.getCount() > 0) {
|
||||
mAsserter.dumpLog("Unexpected favicons in ensureEmptyDatabase.");
|
||||
}
|
||||
c.close();
|
||||
|
||||
// assertCountIsAndClose(c, 0, "All favicons were deleted");
|
||||
|
||||
mProvider.delete(appendUriParam(BrowserContract.Thumbnails.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
|
||||
c = mProvider.query(appendUriParam(BrowserContract.Thumbnails.CONTENT_URI,
|
||||
BrowserContract.PARAM_SHOW_DELETED, "1"),
|
||||
null, null, null, null);
|
||||
|
||||
if (c.getCount() > 0) {
|
||||
mAsserter.dumpLog("Unexpected thumbnails in ensureEmptyDatabase.");
|
||||
}
|
||||
c.close();
|
||||
|
||||
// assertCountIsAndClose(c, 0, "All thumbnails were deleted");
|
||||
}
|
||||
|
||||
private ContentValues createBookmark(String title, String url, long parentId,
|
||||
@ -326,7 +350,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
mAsserter.is(seenException, false, "Batch updating succeded");
|
||||
mOperations.clear();
|
||||
|
||||
// Delte all visits
|
||||
// Delete all visits
|
||||
for (int i = 0; i < TESTCOUNT; i++) {
|
||||
builder = ContentProviderOperation.newDelete(BrowserContract.History.CONTENT_URI);
|
||||
builder.withSelection(BrowserContract.History.URL + " = ?",
|
||||
@ -340,7 +364,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
} catch (OperationApplicationException ex) {
|
||||
seenException = true;
|
||||
}
|
||||
mAsserter.is(seenException, false, "Batch deletion succeded");
|
||||
mAsserter.is(seenException, false, "Batch deletion succeeded");
|
||||
}
|
||||
|
||||
// Force a Constraint error, see if later operations still apply correctly
|
||||
@ -475,6 +499,8 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
mAsserter.is(new Integer(parentId), new Integer(rootId),
|
||||
"The PARENT of the " + guid + " special folder is correct");
|
||||
}
|
||||
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -549,6 +575,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
|
||||
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), String.valueOf(BrowserContract.Bookmarks.TYPE_BOOKMARK),
|
||||
"Inserted bookmark has correct default type");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -571,12 +598,14 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
|
||||
mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Bookmarks.FAVICON)), "UTF8"),
|
||||
favicon, "Inserted bookmark has corresponding favicon image");
|
||||
c.close();
|
||||
|
||||
c = getFaviconsByUrl(pageUrl);
|
||||
mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
|
||||
|
||||
mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
|
||||
favicon, "Inserted favicon has corresponding favicon image");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -587,6 +616,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
|
||||
Cursor c = getBookmarkById(id);
|
||||
mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
|
||||
c.close();
|
||||
|
||||
return id;
|
||||
}
|
||||
@ -603,6 +633,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
|
||||
Cursor c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
|
||||
mAsserter.is(c.moveToFirst(), true, "Deleted bookmark was only marked as deleted");
|
||||
c.close();
|
||||
|
||||
deleted = mProvider.delete(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"),
|
||||
BrowserContract.Bookmarks._ID + " = ?",
|
||||
@ -612,6 +643,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
|
||||
c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
|
||||
mAsserter.is(c.moveToFirst(), false, "Inserted bookmark is now actually deleted");
|
||||
c.close();
|
||||
|
||||
id = insertOneBookmark();
|
||||
|
||||
@ -622,6 +654,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
c = getBookmarkById(id);
|
||||
mAsserter.is(c.moveToFirst(), false,
|
||||
"Inserted bookmark can't be found after deletion using URI with ID");
|
||||
c.close();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 8 &&
|
||||
Build.VERSION.SDK_INT < 16) {
|
||||
@ -631,6 +664,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
long parentId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
|
||||
c = getBookmarkById(parentId);
|
||||
mAsserter.is(c.moveToFirst(), true, "Inserted bookmarks folder found");
|
||||
c.close();
|
||||
|
||||
b = createBookmark("Example", "http://example.com", parentId,
|
||||
BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
|
||||
@ -638,6 +672,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
|
||||
c = getBookmarkById(id);
|
||||
mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
|
||||
c.close();
|
||||
|
||||
deleted = 0;
|
||||
try {
|
||||
@ -664,11 +699,13 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
|
||||
Cursor c = getFaviconsByUrl(pageUrl);
|
||||
mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
|
||||
c.close();
|
||||
|
||||
mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null);
|
||||
|
||||
c = getFaviconsByUrl(pageUrl);
|
||||
mAsserter.is(c.moveToFirst(), false, "Favicon is deleted with last reference to it");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -713,6 +750,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
new String[] { String.valueOf(id) });
|
||||
|
||||
mAsserter.is((updated == 1), true, "Inserted bookmark was updated");
|
||||
c.close();
|
||||
|
||||
c = getBookmarkById(id);
|
||||
mAsserter.is(c.moveToFirst(), true, "Updated bookmark found");
|
||||
@ -752,12 +790,14 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
u.put(BrowserContract.Bookmarks.URL, "http://examples2.com");
|
||||
|
||||
updated = mProvider.update(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), u, null, null);
|
||||
c.close();
|
||||
|
||||
c = getBookmarkById(id);
|
||||
mAsserter.is(c.moveToFirst(), true, "Updated bookmark found");
|
||||
|
||||
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), u.getAsString(BrowserContract.Bookmarks.URL),
|
||||
"Updated bookmark has correct URL using URI with id");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -784,12 +824,14 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
|
||||
ContentValues u = createFaviconEntry(pageUrl, newFavicon);
|
||||
mProvider.update(BrowserContract.Favicons.CONTENT_URI, u, null, null);
|
||||
c.close();
|
||||
|
||||
c = getFaviconsByUrl(pageUrl);
|
||||
mAsserter.is(c.moveToFirst(), true, "Updated favicon found");
|
||||
|
||||
mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
|
||||
newFavicon, "Updated favicon has corresponding favicon image");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -825,6 +867,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
}
|
||||
|
||||
mAsserter.is(i, count, "Folder has the right number of children.");
|
||||
c.close();
|
||||
}
|
||||
|
||||
public static final int NUMBER_OF_CHILDREN = 1001;
|
||||
@ -921,6 +964,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
id = insertWithNullCol(BrowserContract.History.VISITS);
|
||||
mAsserter.is(new Long(id), new Long(-1),
|
||||
"Should not be able to insert history with null number of visits");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -943,12 +987,14 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
|
||||
mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.History.FAVICON)), "UTF8"),
|
||||
favicon, "Inserted history entry has corresponding favicon image");
|
||||
c.close();
|
||||
|
||||
c = getFaviconsByUrl(pageUrl);
|
||||
mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
|
||||
|
||||
mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
|
||||
favicon, "Inserted favicon has corresponding favicon image");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -959,6 +1005,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
|
||||
Cursor c = getHistoryEntryById(id);
|
||||
mAsserter.is(c.moveToFirst(), true, "Inserted history entry found");
|
||||
c.close();
|
||||
|
||||
return id;
|
||||
}
|
||||
@ -981,6 +1028,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
new String[] { String.valueOf(id) });
|
||||
|
||||
mAsserter.is((deleted == 1), true, "Inserted history entry was deleted");
|
||||
c.close();
|
||||
|
||||
c = getHistoryEntryById(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
|
||||
mAsserter.is(c.moveToFirst(), false, "Inserted history is now actually deleted");
|
||||
@ -990,10 +1038,12 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
deleted = mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
|
||||
mAsserter.is((deleted == 1), true,
|
||||
"Inserted history entry was deleted using URI with id");
|
||||
c.close();
|
||||
|
||||
c = getHistoryEntryById(id);
|
||||
mAsserter.is(c.moveToFirst(), false,
|
||||
"Inserted history entry can't be found after deletion using URI with ID");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1012,9 +1062,11 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
|
||||
|
||||
mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
|
||||
c.close();
|
||||
|
||||
c = getFaviconsByUrl(pageUrl);
|
||||
mAsserter.is(c.moveToFirst(), false, "Favicon is deleted with last reference to it");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1056,6 +1108,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
new String[] { String.valueOf(id) });
|
||||
|
||||
mAsserter.is((updated == 1), true, "Inserted history entry was updated");
|
||||
c.close();
|
||||
|
||||
c = getHistoryEntryById(id);
|
||||
mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
|
||||
@ -1089,12 +1142,14 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
u.put(BrowserContract.History.URL, "http://examples2.com");
|
||||
|
||||
updated = mProvider.update(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), u, null, null);
|
||||
c.close();
|
||||
|
||||
c = getHistoryEntryById(id);
|
||||
mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
|
||||
|
||||
mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), u.getAsString(BrowserContract.History.URL),
|
||||
"Updated history entry has correct URL using URI with id");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1121,12 +1176,14 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
ContentValues u = createFaviconEntry(pageUrl, newFavicon);
|
||||
|
||||
mProvider.update(BrowserContract.Favicons.CONTENT_URI, u, null, null);
|
||||
c.close();
|
||||
|
||||
c = getFaviconsByUrl(pageUrl);
|
||||
mAsserter.is(c.moveToFirst(), true, "Updated favicon found");
|
||||
|
||||
mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
|
||||
newFavicon, "Updated favicon has corresponding favicon image");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1195,6 +1252,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
BrowserContract.History._ID + " = ?",
|
||||
new String[] { String.valueOf(id) });
|
||||
mAsserter.is((updated == 1), true, "Inserted history entry was updated");
|
||||
c.close();
|
||||
|
||||
c = getHistoryEntryById(id);
|
||||
mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
|
||||
@ -1220,6 +1278,8 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
mAsserter.is((updated == 1), true, "History entry was inserted");
|
||||
|
||||
id = getHistoryEntryIdByUrl(TEST_URL_2);
|
||||
c.close();
|
||||
|
||||
c = getHistoryEntryById(id);
|
||||
mAsserter.is(c.moveToFirst(), true, "History entry was inserted");
|
||||
|
||||
@ -1239,6 +1299,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
BrowserContract.History._ID + " = ?",
|
||||
new String[] { String.valueOf(id) });
|
||||
mAsserter.is((updated == 1), true, "Inserted history entry was updated");
|
||||
c.close();
|
||||
|
||||
c = getHistoryEntryById(id);
|
||||
mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
|
||||
@ -1253,6 +1314,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
"Updated history entry has same creation date");
|
||||
mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED))), new Long(dateModified),
|
||||
"Updated history entry has new modification date");
|
||||
c.close();
|
||||
|
||||
}
|
||||
}
|
||||
@ -1275,6 +1337,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
|
||||
mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
|
||||
thumbnail, "Inserted thumbnail has corresponding thumbnail image");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1301,12 +1364,14 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
ContentValues u = createThumbnailEntry(pageUrl, newThumbnail);
|
||||
|
||||
mProvider.update(BrowserContract.Thumbnails.CONTENT_URI, u, null, null);
|
||||
c.close();
|
||||
|
||||
c = getThumbnailByUrl(pageUrl);
|
||||
mAsserter.is(c.moveToFirst(), true, "Updated thumbnail found");
|
||||
|
||||
mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
|
||||
newThumbnail, "Updated thumbnail has corresponding thumbnail image");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1325,9 +1390,11 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found");
|
||||
|
||||
mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
|
||||
c.close();
|
||||
|
||||
c = getThumbnailByUrl(pageUrl);
|
||||
mAsserter.is(c.moveToFirst(), false, "Thumbnail is deleted with last reference to it");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1435,6 +1502,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
"Combined entry has correct number of visits");
|
||||
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED))), new Long(LAST_VISITED),
|
||||
"Combined entry has correct last visit time");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1495,6 +1563,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
mAsserter.is(new Integer(display), new Integer(expectedDisplay),
|
||||
"Combined display column should always be DISPLAY_READER for the reading list item");
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1527,6 +1596,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
new String[] { String.valueOf(combinedBookmarkId) });
|
||||
|
||||
mAsserter.is((deleted == 1), true, "Inserted combined bookmark was deleted");
|
||||
c.close();
|
||||
|
||||
c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
|
||||
mAsserter.is(c.getCount(), 1, "1 combined entry found");
|
||||
@ -1534,6 +1604,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id");
|
||||
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
|
||||
"Bookmark id should not be set to removed bookmark id");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1569,6 +1640,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
new String[] { String.valueOf(combinedReadingListItemId) });
|
||||
|
||||
mAsserter.is((deleted == 1), true, "Inserted combined reading list item was deleted");
|
||||
c.close();
|
||||
|
||||
c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
|
||||
mAsserter.is(c.getCount(), 1, "1 combined entry found");
|
||||
@ -1578,6 +1650,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
"Bookmark id should not be set to removed bookmark id");
|
||||
mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DISPLAY))), new Long(BrowserContract.Combined.DISPLAY_NORMAL),
|
||||
"Combined entry should have reader display type");
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1608,7 +1681,8 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
}
|
||||
|
||||
Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
|
||||
mAsserter.is(c.getCount(), count, count + " history entries found");
|
||||
|
||||
assertCountIsAndClose(c, count, count + " history entries found");
|
||||
|
||||
// add thumbnails for each entry
|
||||
allVals = new ContentValues[count];
|
||||
@ -1622,7 +1696,7 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
mAsserter.is(inserts, count, "Expected number of inserts matches");
|
||||
|
||||
c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
|
||||
mAsserter.is(c.getCount(), count, count + " thumbnails entries found");
|
||||
assertCountIsAndClose(c, count, count + " thumbnails entries found");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1637,57 +1711,61 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
Uri url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "NORMAL");
|
||||
mProvider.delete(url, null, null);
|
||||
Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
|
||||
mAsserter.is(c.getCount(), count, count + " history entries found");
|
||||
assertCountIsAndClose(c, count, count + " history entries found");
|
||||
|
||||
// expiring with a normal priority should delete all but 10 thumbnails
|
||||
c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
|
||||
mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
|
||||
assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
|
||||
|
||||
ensureEmptyDatabase();
|
||||
// insert a bunch of new entries
|
||||
|
||||
// Insert a bunch of new entries.
|
||||
createFakeHistory(0, count);
|
||||
|
||||
// expiring with a aggressive priority should leave 500 entries
|
||||
// Expiring with a aggressive priority should leave 500 entries.
|
||||
url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "AGGRESSIVE");
|
||||
mProvider.delete(url, null, null);
|
||||
c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
|
||||
mAsserter.is(c.getCount(), 500, "500 history entries found");
|
||||
|
||||
// expiring with a aggressive priority should delete all but 10 thumbnails
|
||||
c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
|
||||
assertCountIsAndClose(c, 500, "500 history entries found");
|
||||
|
||||
// Expiring with a aggressive priority should delete all but 10 thumbnails.
|
||||
c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
|
||||
mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
|
||||
assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
|
||||
|
||||
ensureEmptyDatabase();
|
||||
// insert a bunch of entries with an old time created/modified
|
||||
|
||||
// Insert a bunch of entries with an old time created/modified.
|
||||
long time = 1000L * 60L * 60L * 24L * 30L * 3L;
|
||||
createFakeHistory(time, count);
|
||||
|
||||
// expiring with an normal priority should remove at most 1000 entries
|
||||
// entries leaving at least 2000
|
||||
// Expiring with an normal priority should remove at most 1000 entries,
|
||||
// entries leaving at least 2000.
|
||||
url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "NORMAL");
|
||||
mProvider.delete(url, null, null);
|
||||
c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
|
||||
mAsserter.is(c.getCount(), 2000, "2000 history entries found");
|
||||
|
||||
// expiring with a normal priority should delete all but 10 thumbnails
|
||||
c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
|
||||
assertCountIsAndClose(c, 2000, "2000 history entries found");
|
||||
|
||||
// Expiring with a normal priority should delete all but 10 thumbnails.
|
||||
c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
|
||||
mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
|
||||
assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
|
||||
|
||||
ensureEmptyDatabase();
|
||||
// insert a bunch of entries with an old time created/modified
|
||||
time = 1000L * 60L * 60L * 24L * 30L * 3L;
|
||||
createFakeHistory(time, count);
|
||||
|
||||
// expiring with an agressive priority should remove old
|
||||
// entries leaving at least 500
|
||||
// Expiring with an aggressive priority should remove old
|
||||
// entries, leaving at least 500.
|
||||
url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "AGGRESSIVE");
|
||||
mProvider.delete(url, null, null);
|
||||
c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
|
||||
mAsserter.is(c.getCount(), 500, "500 history entries found");
|
||||
assertCountIsAndClose(c, 500, "500 history entries found");
|
||||
|
||||
// expiring with an aggressive priority should delete all but 10 thumbnails
|
||||
c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
|
||||
mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
|
||||
assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1776,4 +1854,16 @@ public class testBrowserProvider extends ContentProviderTest {
|
||||
ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "bulkInsert");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the provided cursor has the expected number of rows,
|
||||
* closing the cursor afterwards.
|
||||
*/
|
||||
private void assertCountIsAndClose(Cursor c, int expectedCount, String message) {
|
||||
try {
|
||||
mAsserter.is(c.getCount(), expectedCount, message);
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GeckoActionProvider extends ActionProvider {
|
||||
private static int MAX_HISTORY_SIZE = 2;
|
||||
|
||||
@ -125,6 +127,20 @@ public class GeckoActionProvider extends ActionProvider {
|
||||
mOnTargetListener = listener;
|
||||
}
|
||||
|
||||
public ArrayList<ResolveInfo> getSortedActivites() {
|
||||
ArrayList<ResolveInfo> infos = new ArrayList<ResolveInfo>();
|
||||
|
||||
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName);
|
||||
PackageManager packageManager = mContext.getPackageManager();
|
||||
|
||||
// Populate the sub-menu with a sub set of the activities.
|
||||
final int count = dataModel.getActivityCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
infos.add(dataModel.getActivity(i));
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for handling default activity / menu item clicks.
|
||||
*/
|
||||
|
@ -1045,6 +1045,8 @@ BrowserAddonActor.prototype = {
|
||||
function DebuggerProgressListener(aBrowserTabActor) {
|
||||
this._tabActor = aBrowserTabActor;
|
||||
this._tabActor._tabbrowser.addProgressListener(this);
|
||||
let EventEmitter = devtools.require("devtools/shared/event-emitter");
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
DebuggerProgressListener.prototype = {
|
||||
@ -1072,28 +1074,32 @@ DebuggerProgressListener.prototype = {
|
||||
this._tabActor._pendingNavigation = aRequest;
|
||||
}
|
||||
|
||||
this._tabActor.threadActor.disableAllBreakpoints();
|
||||
this._tabActor.conn.send({
|
||||
let packet = {
|
||||
from: this._tabActor.actorID,
|
||||
type: "tabNavigated",
|
||||
url: aRequest.URI.spec,
|
||||
nativeConsoleAPI: true,
|
||||
state: "start"
|
||||
});
|
||||
};
|
||||
this._tabActor.threadActor.disableAllBreakpoints();
|
||||
this._tabActor.conn.send(packet);
|
||||
this.emit("will-navigate", packet);
|
||||
} else if (isStop) {
|
||||
if (this._tabActor.threadActor.state == "running") {
|
||||
this._tabActor.threadActor.dbg.enabled = true;
|
||||
}
|
||||
|
||||
let window = this._tabActor.window;
|
||||
this._tabActor.conn.send({
|
||||
let packet = {
|
||||
from: this._tabActor.actorID,
|
||||
type: "tabNavigated",
|
||||
url: this._tabActor.url,
|
||||
title: this._tabActor.title,
|
||||
nativeConsoleAPI: this._tabActor.hasNativeConsoleAPI(window),
|
||||
state: "stop"
|
||||
});
|
||||
};
|
||||
this._tabActor.conn.send(packet);
|
||||
this.emit("navigate", packet);
|
||||
}
|
||||
}, "DebuggerProgressListener.prototype.onStateChange"),
|
||||
|
||||
|
@ -59,10 +59,10 @@ function WebConsoleActor(aConnection, aParentActor)
|
||||
|
||||
this.dbg = new Debugger();
|
||||
|
||||
this._protoChains = new Map();
|
||||
this._netEvents = new Map();
|
||||
this._gripDepth = 0;
|
||||
|
||||
this._onWillNavigate = this._onWillNavigate.bind(this);
|
||||
this._onObserverNotification = this._onObserverNotification.bind(this);
|
||||
if (this.parentActor.isRootActor) {
|
||||
Services.obs.addObserver(this._onObserverNotification,
|
||||
@ -112,16 +112,6 @@ WebConsoleActor.prototype =
|
||||
*/
|
||||
_netEvents: null,
|
||||
|
||||
/**
|
||||
* A cache of prototype chains for objects that have received a
|
||||
* prototypeAndProperties request.
|
||||
*
|
||||
* @private
|
||||
* @type Map
|
||||
* @see dbg-script-actors.js, ThreadActor._protoChains
|
||||
*/
|
||||
_protoChains: null,
|
||||
|
||||
/**
|
||||
* The debugger server connection instance.
|
||||
* @type object
|
||||
@ -209,6 +199,31 @@ WebConsoleActor.prototype =
|
||||
*/
|
||||
_lastChromeWindow: null,
|
||||
|
||||
// The evalWindow is used at the scope for JS evaluation.
|
||||
_evalWindow: null,
|
||||
get evalWindow() {
|
||||
return this._evalWindow || this.window;
|
||||
},
|
||||
|
||||
set evalWindow(aWindow) {
|
||||
this._evalWindow = aWindow;
|
||||
|
||||
if (!this._progressListenerActive && this.parentActor._progressListener) {
|
||||
this.parentActor._progressListener.once("will-navigate", this._onWillNavigate);
|
||||
this._progressListenerActive = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Flag used to track if we are listening for events from the progress
|
||||
* listener of the tab actor. We use the progress listener to clear
|
||||
* this.evalWindow on page navigation.
|
||||
*
|
||||
* @private
|
||||
* @type boolean
|
||||
*/
|
||||
_progressListenerActive: false,
|
||||
|
||||
/**
|
||||
* The ConsoleServiceListener instance.
|
||||
* @type object
|
||||
@ -295,8 +310,9 @@ WebConsoleActor.prototype =
|
||||
}
|
||||
this._actorPool = null;
|
||||
|
||||
this._jstermHelpersCache = null;
|
||||
this._evalWindow = null;
|
||||
this._netEvents.clear();
|
||||
this._protoChains.clear();
|
||||
this.dbg.enabled = false;
|
||||
this.dbg = null;
|
||||
this.conn = null;
|
||||
@ -725,7 +741,7 @@ WebConsoleActor.prototype =
|
||||
}
|
||||
// This is the general case (non-paused debugger)
|
||||
else {
|
||||
dbgObject = this.dbg.makeGlobalObjectReference(this.window);
|
||||
dbgObject = this.dbg.makeGlobalObjectReference(this.evalWindow);
|
||||
}
|
||||
|
||||
let result = JSPropertyProvider(dbgObject, environment, aRequest.text,
|
||||
@ -822,12 +838,13 @@ WebConsoleActor.prototype =
|
||||
_getJSTermHelpers: function WCA__getJSTermHelpers(aDebuggerGlobal)
|
||||
{
|
||||
let helpers = {
|
||||
window: this.window,
|
||||
window: this.evalWindow,
|
||||
chromeWindow: this.chromeWindow.bind(this),
|
||||
makeDebuggeeValue: aDebuggerGlobal.makeDebuggeeValue.bind(aDebuggerGlobal),
|
||||
createValueGrip: this.createValueGrip.bind(this),
|
||||
sandbox: Object.create(null),
|
||||
helperResult: null,
|
||||
consoleActor: this,
|
||||
};
|
||||
JSTermHelpers(helpers);
|
||||
|
||||
@ -924,12 +941,12 @@ WebConsoleActor.prototype =
|
||||
// as ordinary objects, not as references to be followed, so mixing
|
||||
// debuggers causes strange behaviors.)
|
||||
let dbg = frame ? frameActor.threadActor.dbg : this.dbg;
|
||||
let dbgWindow = dbg.makeGlobalObjectReference(this.window);
|
||||
let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow);
|
||||
|
||||
// If we have an object to bind to |_self|, create a Debugger.Object
|
||||
// referring to that object, belonging to dbg.
|
||||
let bindSelf = null;
|
||||
let dbgWindow = dbg.makeGlobalObjectReference(this.window);
|
||||
let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow);
|
||||
if (aOptions.bindObjectActor) {
|
||||
let objActor = this.getActorByID(aOptions.bindObjectActor);
|
||||
if (objActor) {
|
||||
@ -1289,7 +1306,17 @@ WebConsoleActor.prototype =
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "will-navigate" progress listener. This is used to clear the current
|
||||
* eval scope.
|
||||
*/
|
||||
_onWillNavigate: function WCA__onWillNavigate()
|
||||
{
|
||||
this._evalWindow = null;
|
||||
this._progressListenerActive = false;
|
||||
},
|
||||
};
|
||||
|
||||
WebConsoleActor.prototype.requestTypes =
|
||||
|
@ -21,3 +21,4 @@ support-files =
|
||||
[test_object_actor_native_getters_lenient_this.html]
|
||||
[test_page_errors.html]
|
||||
[test_throw.html]
|
||||
[test_jsterm_cd_iframe.html]
|
||||
|
154
toolkit/devtools/webconsole/test/test_jsterm_cd_iframe.html
Normal file
154
toolkit/devtools/webconsole/test/test_jsterm_cd_iframe.html
Normal file
@ -0,0 +1,154 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<title>Test for the cd() function</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="common.js"></script>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<p>Test for the cd() function</p>
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.8">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let gState;
|
||||
|
||||
function startTest()
|
||||
{
|
||||
removeEventListener("load", startTest);
|
||||
|
||||
attachConsole([], onAttach, true);
|
||||
}
|
||||
|
||||
function onAttach(aState, aResponse)
|
||||
{
|
||||
top.foobarObject = Object.create(null);
|
||||
top.foobarObject.bug609872 = "parent";
|
||||
|
||||
window.foobarObject = Object.create(null);
|
||||
window.foobarObject.bug609872 = "child";
|
||||
|
||||
gState = aState;
|
||||
|
||||
let tests = [doCheckParent, doCdIframe, doCheckIframe, doCdParent,
|
||||
doCheckParent2];
|
||||
runTests(tests, testEnd);
|
||||
}
|
||||
|
||||
function doCheckParent()
|
||||
{
|
||||
info("check parent window");
|
||||
gState.client.evaluateJS("window.foobarObject.bug609872",
|
||||
onFooObjectFromParent);
|
||||
}
|
||||
|
||||
function onFooObjectFromParent(aResponse)
|
||||
{
|
||||
checkObject(aResponse, {
|
||||
from: gState.actor,
|
||||
input: "window.foobarObject.bug609872",
|
||||
result: "parent",
|
||||
});
|
||||
|
||||
ok(!aResponse.exception, "no eval exception");
|
||||
ok(!aResponse.helperResult, "no helper result");
|
||||
|
||||
nextTest();
|
||||
}
|
||||
|
||||
function doCdIframe()
|
||||
{
|
||||
info("test cd('iframe')");
|
||||
gState.client.evaluateJS("cd('iframe')", onCdIframe);
|
||||
}
|
||||
|
||||
function onCdIframe(aResponse)
|
||||
{
|
||||
checkObject(aResponse, {
|
||||
from: gState.actor,
|
||||
input: "cd('iframe')",
|
||||
result: { type: "undefined" },
|
||||
helperResult: { type: "cd" },
|
||||
});
|
||||
|
||||
ok(!aResponse.exception, "no eval exception");
|
||||
|
||||
nextTest();
|
||||
}
|
||||
|
||||
function doCheckIframe()
|
||||
{
|
||||
info("check foobarObject from the iframe");
|
||||
gState.client.evaluateJS("window.foobarObject.bug609872",
|
||||
onFooObjectFromIframe);
|
||||
}
|
||||
|
||||
function onFooObjectFromIframe(aResponse)
|
||||
{
|
||||
checkObject(aResponse, {
|
||||
from: gState.actor,
|
||||
input: "window.foobarObject.bug609872",
|
||||
result: "child",
|
||||
});
|
||||
|
||||
ok(!aResponse.exception, "no js eval exception");
|
||||
ok(!aResponse.helperResult, "no helper result");
|
||||
|
||||
nextTest();
|
||||
}
|
||||
|
||||
function doCdParent()
|
||||
{
|
||||
info("test cd() back to parent");
|
||||
gState.client.evaluateJS("cd()", onCdParent);
|
||||
}
|
||||
|
||||
function onCdParent(aResponse)
|
||||
{
|
||||
checkObject(aResponse, {
|
||||
from: gState.actor,
|
||||
input: "cd()",
|
||||
result: { type: "undefined" },
|
||||
helperResult: { type: "cd" },
|
||||
});
|
||||
|
||||
ok(!aResponse.exception, "no eval exception");
|
||||
|
||||
nextTest();
|
||||
}
|
||||
|
||||
function doCheckParent2()
|
||||
{
|
||||
gState.client.evaluateJS("window.foobarObject.bug609872",
|
||||
onFooObjectFromParent2);
|
||||
}
|
||||
|
||||
function onFooObjectFromParent2(aResponse)
|
||||
{
|
||||
checkObject(aResponse, {
|
||||
from: gState.actor,
|
||||
input: "window.foobarObject.bug609872",
|
||||
result: "parent",
|
||||
});
|
||||
|
||||
ok(!aResponse.exception, "no eval exception");
|
||||
ok(!aResponse.helperResult, "no helper result");
|
||||
|
||||
nextTest();
|
||||
}
|
||||
|
||||
function testEnd()
|
||||
{
|
||||
closeDebugger(gState, function() {
|
||||
gState = null;
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
addEventListener("load", startTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1512,6 +1512,40 @@ function JSTermHelpers(aOwner)
|
||||
aOwner.helperResult = { type: "help" };
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the JS evaluation scope.
|
||||
*
|
||||
* @param DOMElement|string|window aWindow
|
||||
* The window object to use for eval scope. This can be a string that
|
||||
* is used to perform document.querySelector(), to find the iframe that
|
||||
* you want to cd() to. A DOMElement can be given as well, the
|
||||
* .contentWindow property is used. Lastly, you can directly pass
|
||||
* a window object. If you call cd() with no arguments, the current
|
||||
* eval scope is cleared back to its default (the top window).
|
||||
*/
|
||||
aOwner.sandbox.cd = function JSTH_cd(aWindow)
|
||||
{
|
||||
if (!aWindow) {
|
||||
aOwner.consoleActor.evalWindow = null;
|
||||
aOwner.helperResult = { type: "cd" };
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof aWindow == "string") {
|
||||
aWindow = aOwner.window.document.querySelector(aWindow);
|
||||
}
|
||||
if (aWindow instanceof Ci.nsIDOMElement && aWindow.contentWindow) {
|
||||
aWindow = aWindow.contentWindow;
|
||||
}
|
||||
if (!(aWindow instanceof Ci.nsIDOMWindow)) {
|
||||
aOwner.helperResult = { type: "error", message: "cdFunctionInvalidArgument" };
|
||||
return;
|
||||
}
|
||||
|
||||
aOwner.consoleActor.evalWindow = aWindow;
|
||||
aOwner.helperResult = { type: "cd" };
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspects the passed aObject. This is done by opening the PropertyPanel.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user