mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 00:32:11 +00:00
Merge m-c to inbound
This commit is contained in:
commit
6fda513d49
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Intermittent Android startup crashes leading to test bustage.
|
||||
Bug 983185 requires a clobber. This may not affect all platforms.
|
||||
|
@ -860,6 +860,15 @@ pref("media.webspeech.synth.enabled", true);
|
||||
pref("dom.mozDownloads.enabled", true);
|
||||
pref("dom.downloads.max_retention_days", 7);
|
||||
|
||||
// External Helper Application Handling
|
||||
//
|
||||
// All external helper application handling can require the docshell to be
|
||||
// active before allowing the external helper app service to handle content.
|
||||
//
|
||||
// To prevent SD card DoS attacks via downloads we disable background handling.
|
||||
//
|
||||
pref("security.exthelperapp.disable_background_handling", true);
|
||||
|
||||
// Inactivity time in milliseconds after which we shut down the OS.File worker.
|
||||
pref("osfile.reset_worker_delay", 5000);
|
||||
|
||||
|
@ -19,12 +19,12 @@
|
||||
<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="1cef557f9e9a865d1bf49d99a8f1cca1f0f4f5c4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
|
@ -17,9 +17,9 @@
|
||||
</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="1cef557f9e9a865d1bf49d99a8f1cca1f0f4f5c4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
|
@ -15,10 +15,10 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1cef557f9e9a865d1bf49d99a8f1cca1f0f4f5c4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
|
@ -19,12 +19,12 @@
|
||||
<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="1cef557f9e9a865d1bf49d99a8f1cca1f0f4f5c4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"branch": "",
|
||||
"revision": ""
|
||||
},
|
||||
"revision": "37b9db0c5bc096893c78468b1a3cf3d02962e231",
|
||||
"revision": "a7c17d6cce9c60631e386b75885199a59b555a43",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,11 +17,11 @@
|
||||
<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="1cef557f9e9a865d1bf49d99a8f1cca1f0f4f5c4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
@ -15,11 +15,11 @@
|
||||
<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="1cef557f9e9a865d1bf49d99a8f1cca1f0f4f5c4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
@ -19,11 +19,11 @@
|
||||
<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="1cef557f9e9a865d1bf49d99a8f1cca1f0f4f5c4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
@ -17,11 +17,11 @@
|
||||
<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="1cef557f9e9a865d1bf49d99a8f1cca1f0f4f5c4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -17,9 +17,9 @@
|
||||
</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="1cef557f9e9a865d1bf49d99a8f1cca1f0f4f5c4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
|
@ -17,11 +17,11 @@
|
||||
<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="1cef557f9e9a865d1bf49d99a8f1cca1f0f4f5c4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -297,6 +297,7 @@
|
||||
@BINPATH@/components/storage.xpt
|
||||
@BINPATH@/components/telemetry.xpt
|
||||
@BINPATH@/components/toolkit_finalizationwitness.xpt
|
||||
@BINPATH@/components/toolkit_osfile.xpt
|
||||
@BINPATH@/components/toolkitprofile.xpt
|
||||
#ifdef MOZ_ENABLE_XREMOTE
|
||||
@BINPATH@/components/toolkitremote.xpt
|
||||
|
@ -460,6 +460,7 @@ const PanelUI = {
|
||||
* @return the selected locale or "en-US" if none is selected
|
||||
*/
|
||||
function getLocale() {
|
||||
const PREF_SELECTED_LOCALE = "general.useragent.locale";
|
||||
try {
|
||||
let locale = Services.prefs.getComplexValue(PREF_SELECTED_LOCALE,
|
||||
Ci.nsIPrefLocalizedString);
|
||||
|
@ -3681,7 +3681,11 @@ OverflowableToolbar.prototype = {
|
||||
if (!this._enabled)
|
||||
return;
|
||||
|
||||
this._moveItemsBackToTheirOrigin();
|
||||
if (this._target.scrollLeftMax > 0) {
|
||||
this.onOverflow();
|
||||
} else {
|
||||
this._moveItemsBackToTheirOrigin();
|
||||
}
|
||||
},
|
||||
|
||||
_disable: function() {
|
||||
|
@ -65,6 +65,7 @@ skip-if = os == "linux"
|
||||
[browser_948985_non_removable_defaultArea.js]
|
||||
[browser_952963_areaType_getter_no_area.js]
|
||||
[browser_956602_remove_special_widget.js]
|
||||
[browser_963639_customizing_attribute_non_customizable_toolbar.js]
|
||||
[browser_968447_bookmarks_toolbar_items_in_panel.js]
|
||||
[browser_968565_insert_before_hidden_items.js]
|
||||
[browser_969427_recreate_destroyed_widget_after_reset.js]
|
||||
|
@ -0,0 +1,34 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const kToolbar = "test-toolbar-963639-non-customizable-customizing-attribute";
|
||||
|
||||
add_task(function() {
|
||||
info("Test for Bug 963639 - CustomizeMode _onToolbarVisibilityChange sets @customizing on non-customizable toolbars");
|
||||
|
||||
let toolbar = document.createElement("toolbar");
|
||||
toolbar.id = kToolbar;
|
||||
gNavToolbox.appendChild(toolbar);
|
||||
|
||||
let testToolbar = document.getElementById(kToolbar)
|
||||
ok(testToolbar, "Toolbar was created.");
|
||||
is(gNavToolbox.getElementsByAttribute("id", kToolbar).length, 1,
|
||||
"Toolbar was added to the navigator toolbox");
|
||||
|
||||
toolbar.setAttribute("toolbarname", "NonCustomizableToolbarCustomizingAttribute");
|
||||
toolbar.setAttribute("collapsed", "true");
|
||||
|
||||
yield startCustomizing();
|
||||
window.setToolbarVisibility(toolbar, "true");
|
||||
isnot(toolbar.getAttribute("customizing"), "true",
|
||||
"Toolbar doesn't have the customizing attribute");
|
||||
|
||||
yield endCustomizing();
|
||||
gNavToolbox.removeChild(toolbar);
|
||||
|
||||
is(gNavToolbox.getElementsByAttribute("id", kToolbar).length, 0,
|
||||
"Toolbar was removed from the navigator toolbox");
|
||||
});
|
@ -23,7 +23,7 @@ function test() {
|
||||
Task.spawn(function* () {
|
||||
try {
|
||||
|
||||
yield waitForSourceShown(gPanel, CODE_URL);
|
||||
yield ensureSourceIs(gPanel, CODE_URL, true);
|
||||
|
||||
// Pause and set our breakpoints.
|
||||
yield doInterrupt();
|
||||
|
@ -10,6 +10,9 @@ function test() {
|
||||
initNetMonitor(FILTERING_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
// It seems that this test may be slow on Ubuntu builds running on ec2.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
let { $, NetMonitorView } = aMonitor.panelWin;
|
||||
let { RequestsMenu } = NetMonitorView;
|
||||
|
||||
|
@ -310,6 +310,7 @@
|
||||
@BINPATH@/components/spellchecker.xpt
|
||||
@BINPATH@/components/storage.xpt
|
||||
@BINPATH@/components/toolkit_finalizationwitness.xpt
|
||||
@BINPATH@/components/toolkit_osfile.xpt
|
||||
@BINPATH@/components/toolkitprofile.xpt
|
||||
#ifdef MOZ_ENABLE_XREMOTE
|
||||
@BINPATH@/components/toolkitremote.xpt
|
||||
|
@ -84,6 +84,10 @@ browser.jar:
|
||||
* skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
|
||||
skin/classic/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png)
|
||||
skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl.png (../shared/customizableui/subView-arrow-back-inverted-rtl.png)
|
||||
skin/classic/browser/customizableui/whimsy.png (../shared/customizableui/whimsy.png)
|
||||
skin/classic/browser/customizableui/whimsy@2x.png (../shared/customizableui/whimsy@2x.png)
|
||||
skin/classic/browser/customizableui/whimsy-bw.png (../shared/customizableui/whimsy-bw.png)
|
||||
skin/classic/browser/customizableui/whimsy-bw@2x.png (../shared/customizableui/whimsy-bw@2x.png)
|
||||
skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
|
@ -141,6 +141,10 @@ browser.jar:
|
||||
skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl.png (../shared/customizableui/subView-arrow-back-inverted-rtl.png)
|
||||
skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl@2x.png (../shared/customizableui/subView-arrow-back-inverted-rtl@2x.png)
|
||||
* skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
|
||||
skin/classic/browser/customizableui/whimsy.png (../shared/customizableui/whimsy.png)
|
||||
skin/classic/browser/customizableui/whimsy@2x.png (../shared/customizableui/whimsy@2x.png)
|
||||
skin/classic/browser/customizableui/whimsy-bw.png (../shared/customizableui/whimsy-bw.png)
|
||||
skin/classic/browser/customizableui/whimsy-bw@2x.png (../shared/customizableui/whimsy-bw@2x.png)
|
||||
skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/buttons@2x.png (downloads/buttons@2x.png)
|
||||
|
@ -19,6 +19,44 @@
|
||||
|
||||
%include ../browser.inc
|
||||
|
||||
#PanelUI-popup #PanelUI-contents:empty {
|
||||
height: 128px;
|
||||
}
|
||||
|
||||
#PanelUI-popup #PanelUI-contents:empty::before {
|
||||
content: "";
|
||||
background-image: url(chrome://browser/skin/customizableui/whimsy-bw.png);
|
||||
display: block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
position: absolute;
|
||||
animation: moveX 3.05s linear 0s infinite alternate,
|
||||
moveY 3.4s linear 0s infinite alternate;
|
||||
}
|
||||
|
||||
#PanelUI-popup #PanelUI-contents:empty:hover::before {
|
||||
background-image: url(chrome://browser/skin/customizableui/whimsy.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
#PanelUI-popup #PanelUI-contents:empty::before {
|
||||
background-image: url(chrome://browser/skin/customizableui/whimsy-bw@2x.png);
|
||||
background-size: 64px 64px;
|
||||
}
|
||||
#PanelUI-popup #PanelUI-contents:empty:hover::before {
|
||||
background-image: url(chrome://browser/skin/customizableui/whimsy@2x.png);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes moveX {
|
||||
/* These values are adjusted for the padding on the panel. */
|
||||
from { margin-left: -15px; } to { margin-left: calc(100% - 49px); }
|
||||
}
|
||||
@keyframes moveY {
|
||||
/* These values are adjusted for the padding and height of the panel. */
|
||||
from { margin-top: -6px; } to { margin-top: 58px; }
|
||||
}
|
||||
|
||||
#PanelUI-button {
|
||||
background-image: linear-gradient(to bottom, hsla(0,0%,100%,0), hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, hsla(0,0%,100%,0)),
|
||||
linear-gradient(to bottom, hsla(210,54%,20%,0), hsla(210,54%,20%,.3) 30%, hsla(210,54%,20%,.3) 70%, hsla(210,54%,20%,0)),
|
||||
|
BIN
browser/themes/shared/customizableui/whimsy-bw.png
Normal file
BIN
browser/themes/shared/customizableui/whimsy-bw.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
browser/themes/shared/customizableui/whimsy-bw@2x.png
Normal file
BIN
browser/themes/shared/customizableui/whimsy-bw@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
browser/themes/shared/customizableui/whimsy.png
Normal file
BIN
browser/themes/shared/customizableui/whimsy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
BIN
browser/themes/shared/customizableui/whimsy@2x.png
Normal file
BIN
browser/themes/shared/customizableui/whimsy@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
@ -103,6 +103,10 @@ browser.jar:
|
||||
* skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
|
||||
skin/classic/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png)
|
||||
skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl.png (../shared/customizableui/subView-arrow-back-inverted-rtl.png)
|
||||
skin/classic/browser/customizableui/whimsy.png (../shared/customizableui/whimsy.png)
|
||||
skin/classic/browser/customizableui/whimsy@2x.png (../shared/customizableui/whimsy@2x.png)
|
||||
skin/classic/browser/customizableui/whimsy-bw.png (../shared/customizableui/whimsy-bw.png)
|
||||
skin/classic/browser/customizableui/whimsy-bw@2x.png (../shared/customizableui/whimsy-bw@2x.png)
|
||||
* skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
@ -431,6 +435,10 @@ browser.jar:
|
||||
* skin/classic/aero/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay-aero.css)
|
||||
skin/classic/aero/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png)
|
||||
skin/classic/aero/browser/customizableui/subView-arrow-back-inverted-rtl.png (../shared/customizableui/subView-arrow-back-inverted-rtl.png)
|
||||
skin/classic/aero/browser/customizableui/whimsy.png (../shared/customizableui/whimsy.png)
|
||||
skin/classic/aero/browser/customizableui/whimsy@2x.png (../shared/customizableui/whimsy@2x.png)
|
||||
skin/classic/aero/browser/customizableui/whimsy-bw.png (../shared/customizableui/whimsy-bw.png)
|
||||
skin/classic/aero/browser/customizableui/whimsy-bw@2x.png (../shared/customizableui/whimsy-bw@2x.png)
|
||||
* skin/classic/aero/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay-aero.css)
|
||||
skin/classic/aero/browser/downloads/buttons.png (downloads/buttons-aero.png)
|
||||
skin/classic/aero/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
|
@ -230,6 +230,72 @@ ConvertPlanarYCbCrToNV12(const PlanarYCbCrData* aSource, uint8_t* aDestination)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert pixels in graphic buffer to NV12 format. aSource is the layer image
|
||||
// containing source graphic buffer, and aDestination is the destination of
|
||||
// conversion. Currently only 2 source format are supported:
|
||||
// - NV21/HAL_PIXEL_FORMAT_YCrCb_420_SP (from camera preview window).
|
||||
// - YV12/HAL_PIXEL_FORMAT_YV12 (from video decoder).
|
||||
static
|
||||
void
|
||||
ConvertGrallocImageToNV12(GrallocImage* aSource, uint8_t* aDestination)
|
||||
{
|
||||
// Get graphic buffer.
|
||||
SurfaceDescriptor handle = aSource->GetSurfaceDescriptor();
|
||||
SurfaceDescriptorGralloc gralloc = handle.get_SurfaceDescriptorGralloc();
|
||||
sp<GraphicBuffer> graphicBuffer = GrallocBufferActor::GetFrom(gralloc);
|
||||
|
||||
int pixelFormat = graphicBuffer->getPixelFormat();
|
||||
// Only support NV21 (from camera) or YV12 (from HW decoder output) for now.
|
||||
NS_ENSURE_TRUE_VOID(pixelFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
|
||||
pixelFormat == HAL_PIXEL_FORMAT_YV12);
|
||||
|
||||
void* imgPtr = nullptr;
|
||||
graphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_MASK, &imgPtr);
|
||||
// Build PlanarYCbCrData for NV21 or YV12 buffer.
|
||||
PlanarYCbCrData yuv;
|
||||
switch (pixelFormat) {
|
||||
case HAL_PIXEL_FORMAT_YCrCb_420_SP: // From camera.
|
||||
yuv.mYChannel = static_cast<uint8_t*>(imgPtr);
|
||||
yuv.mYSkip = 0;
|
||||
yuv.mYSize.width = graphicBuffer->getWidth();
|
||||
yuv.mYSize.height = graphicBuffer->getHeight();
|
||||
yuv.mYStride = graphicBuffer->getStride();
|
||||
// 4:2:0.
|
||||
yuv.mCbCrSize.width = yuv.mYSize.width / 2;
|
||||
yuv.mCbCrSize.height = yuv.mYSize.height / 2;
|
||||
// Interleaved VU plane.
|
||||
yuv.mCrChannel = yuv.mYChannel + (yuv.mYStride * yuv.mYSize.height);
|
||||
yuv.mCrSkip = 1;
|
||||
yuv.mCbChannel = yuv.mCrChannel + 1;
|
||||
yuv.mCbSkip = 1;
|
||||
yuv.mCbCrStride = yuv.mYStride;
|
||||
ConvertPlanarYCbCrToNV12(&yuv, aDestination);
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YV12: // From video decoder.
|
||||
// Android YV12 format is defined in system/core/include/system/graphics.h
|
||||
yuv.mYChannel = static_cast<uint8_t*>(imgPtr);
|
||||
yuv.mYSkip = 0;
|
||||
yuv.mYSize.width = graphicBuffer->getWidth();
|
||||
yuv.mYSize.height = graphicBuffer->getHeight();
|
||||
yuv.mYStride = graphicBuffer->getStride();
|
||||
// 4:2:0.
|
||||
yuv.mCbCrSize.width = yuv.mYSize.width / 2;
|
||||
yuv.mCbCrSize.height = yuv.mYSize.height / 2;
|
||||
yuv.mCrChannel = yuv.mYChannel + (yuv.mYStride * yuv.mYSize.height);
|
||||
// Aligned to 16 bytes boundary.
|
||||
yuv.mCbCrStride = (yuv.mYStride / 2 + 15) & ~0x0F;
|
||||
yuv.mCrSkip = 0;
|
||||
yuv.mCbChannel = yuv.mCrChannel + (yuv.mCbCrStride * yuv.mCbCrSize.height);
|
||||
yuv.mCbSkip = 0;
|
||||
ConvertPlanarYCbCrToNV12(&yuv, aDestination);
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Unsupported input gralloc image type. Should never be here.");
|
||||
}
|
||||
|
||||
graphicBuffer->unlock();
|
||||
}
|
||||
|
||||
nsresult
|
||||
OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight,
|
||||
int64_t aTimestamp, int aInputFlags)
|
||||
@ -252,7 +318,6 @@ OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight,
|
||||
|
||||
size_t yLen = aWidth * aHeight;
|
||||
size_t uvLen = yLen / 2;
|
||||
|
||||
// Buffer should be large enough to hold input image data.
|
||||
MOZ_ASSERT(dstSize >= yLen + uvLen);
|
||||
|
||||
@ -273,40 +338,7 @@ OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight,
|
||||
aHeight == img->GetSize().height);
|
||||
|
||||
if (format == ImageFormat::GRALLOC_PLANAR_YCBCR) {
|
||||
// Get graphic buffer pointer.
|
||||
void* imgPtr = nullptr;
|
||||
GrallocImage* nativeImage = static_cast<GrallocImage*>(img);
|
||||
SurfaceDescriptor handle = nativeImage->GetSurfaceDescriptor();
|
||||
SurfaceDescriptorGralloc gralloc = handle.get_SurfaceDescriptorGralloc();
|
||||
sp<GraphicBuffer> graphicBuffer = GrallocBufferActor::GetFrom(gralloc);
|
||||
graphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_MASK, &imgPtr);
|
||||
uint8_t* src = static_cast<uint8_t*>(imgPtr);
|
||||
|
||||
// Only support NV21 for now.
|
||||
MOZ_ASSERT(graphicBuffer->getPixelFormat() ==
|
||||
HAL_PIXEL_FORMAT_YCrCb_420_SP);
|
||||
|
||||
// Build PlanarYCbCrData for NV21 buffer.
|
||||
PlanarYCbCrData nv21;
|
||||
// Y plane.
|
||||
nv21.mYChannel = src;
|
||||
nv21.mYSize.width = aWidth;
|
||||
nv21.mYSize.height = aHeight;
|
||||
nv21.mYStride = aWidth;
|
||||
nv21.mYSkip = 0;
|
||||
// Interleaved VU plane.
|
||||
nv21.mCrChannel = src + yLen;
|
||||
nv21.mCrSkip = 1;
|
||||
nv21.mCbChannel = nv21.mCrChannel + 1;
|
||||
nv21.mCbSkip = 1;
|
||||
nv21.mCbCrStride = aWidth;
|
||||
// 4:2:0.
|
||||
nv21.mCbCrSize.width = aWidth / 2;
|
||||
nv21.mCbCrSize.height = aHeight / 2;
|
||||
|
||||
ConvertPlanarYCbCrToNV12(&nv21, dst);
|
||||
|
||||
graphicBuffer->unlock();
|
||||
ConvertGrallocImageToNV12(static_cast<GrallocImage*>(img), dst);
|
||||
} else if (format == ImageFormat::PLANAR_YCBCR) {
|
||||
ConvertPlanarYCbCrToNV12(static_cast<PlanarYCbCrImage*>(img)->GetData(),
|
||||
dst);
|
||||
|
@ -69,6 +69,14 @@ DOM4_MSG_DEF(QuotaExceededError, "The current transaction exceeded its quota lim
|
||||
|
||||
DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR, "The operation failed because the database was prevented from taking an action. The operation might be able to succeed if the application performs some recovery steps and retries the entire transaction. For example, there was not enough remaining storage space, or the storage quota was reached and the user declined to give more space to the database.")
|
||||
|
||||
/* FileSystem DOM errors. */
|
||||
DOM4_MSG_DEF(InvalidAccessError, "Invalid file system path.", NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR)
|
||||
DOM4_MSG_DEF(InvalidModificationError, "Failed to modify the file.", NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR)
|
||||
DOM4_MSG_DEF(NoModificationAllowedError, "Modifications are not allowed for this file", NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR)
|
||||
DOM4_MSG_DEF(AbortError, "File already exists.", NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR)
|
||||
DOM4_MSG_DEF(TypeMismatchError, "The type of the file is incompatible with the expected type.", NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR)
|
||||
DOM4_MSG_DEF(UnknownError, "The operation failed for reasons unrelated to the file system itself and not covered by any other error code.", NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR)
|
||||
|
||||
/* DOM error codes defined by us */
|
||||
|
||||
/* XXX string should be specified by norris */
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "mozilla/ipc/UnixSocket.h"
|
||||
#include "mozilla/LazyIdleThread.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsIObserverService.h"
|
||||
@ -71,7 +70,6 @@
|
||||
|
||||
#define PROP_BLUETOOTH_ENABLED "bluetooth.isEnabled"
|
||||
|
||||
#define DEFAULT_THREAD_TIMEOUT_MS 3000
|
||||
#define DEFAULT_SHUTDOWN_TIMER_MS 5000
|
||||
|
||||
bool gBluetoothDebugFlag = false;
|
||||
@ -133,9 +131,7 @@ GetAllBluetoothActors(InfallibleTArray<BluetoothParent*>& aActors)
|
||||
|
||||
BluetoothService::ToggleBtAck::ToggleBtAck(bool aEnabled)
|
||||
: mEnabled(aEnabled)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
}
|
||||
{ }
|
||||
|
||||
NS_METHOD
|
||||
BluetoothService::ToggleBtAck::Run()
|
||||
@ -179,60 +175,6 @@ BluetoothService::ToggleBtAck::Run()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class BluetoothService::ToggleBtTask : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ToggleBtTask(bool aEnabled, bool aIsStartup)
|
||||
: mEnabled(aEnabled)
|
||||
, mIsStartup(aIsStartup)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
/**
|
||||
* mEnabled: expected status of bluetooth
|
||||
* sBluetoothService->IsEnabled(): real status of bluetooth
|
||||
*
|
||||
* When two values are the same, we don't switch on/off bluetooth
|
||||
* but we still do ToggleBtAck task. One special case happens at startup
|
||||
* stage. At startup, the initialization of BluetoothService still has to
|
||||
* be done even if mEnabled is equal to the status of Bluetooth firmware.
|
||||
*
|
||||
* Please see bug 892392 for more information.
|
||||
*/
|
||||
if (!mIsStartup && mEnabled == sBluetoothService->IsEnabledInternal()) {
|
||||
BT_WARNING("Bluetooth has already been enabled/disabled before.");
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(mEnabled);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
} else {
|
||||
// Switch on/off bluetooth
|
||||
if (mEnabled) {
|
||||
if (NS_FAILED(sBluetoothService->StartInternal())) {
|
||||
BT_WARNING("Bluetooth service failed to start!");
|
||||
mEnabled = !mEnabled;
|
||||
}
|
||||
} else {
|
||||
if (NS_FAILED(sBluetoothService->StopInternal())) {
|
||||
BT_WARNING("Bluetooth service failed to stop!");
|
||||
mEnabled = !mEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mEnabled;
|
||||
bool mIsStartup;
|
||||
};
|
||||
|
||||
class BluetoothService::StartupTask : public nsISettingsServiceCallback
|
||||
{
|
||||
public:
|
||||
@ -451,73 +393,114 @@ BluetoothService::DistributeSignal(const BluetoothSignal& aSignal)
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup)
|
||||
BluetoothService::StartBluetooth(bool aIsStartup)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sInShutdown) {
|
||||
if (aStart) {
|
||||
// Don't try to start if we're already shutting down.
|
||||
MOZ_ASSERT(false, "Start called while in shutdown!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mBluetoothThread) {
|
||||
// Don't create a new thread after we've begun shutdown since bluetooth
|
||||
// can't be running.
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!aStart) {
|
||||
BluetoothProfileManagerBase* profile;
|
||||
profile = BluetoothHfpManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
} else {
|
||||
profile->Reset();
|
||||
}
|
||||
|
||||
profile = BluetoothOppManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
}
|
||||
|
||||
profile = BluetoothA2dpManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
} else {
|
||||
profile->Reset();
|
||||
}
|
||||
|
||||
profile = BluetoothHidManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
} else {
|
||||
profile->Reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!mBluetoothThread) {
|
||||
mBluetoothThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
|
||||
NS_LITERAL_CSTRING("Bluetooth"),
|
||||
LazyIdleThread::ManualShutdown);
|
||||
// Don't try to start if we're already shutting down.
|
||||
MOZ_ASSERT(false, "Start called while in shutdown!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mAdapterAddedReceived = false;
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new ToggleBtTask(aStart, aIsStartup);
|
||||
nsresult rv = mBluetoothThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
/* When IsEnabled() is true, we don't switch on Bluetooth but we still
|
||||
* send ToggleBtAck task. One special case happens at startup stage. At
|
||||
* startup, the initialization of BluetoothService still has to be done
|
||||
* even if Bluetooth is already enabled.
|
||||
*
|
||||
* Please see bug 892392 for more information.
|
||||
*/
|
||||
if (aIsStartup || !sBluetoothService->IsEnabled()) {
|
||||
// Switch Bluetooth on
|
||||
if (NS_FAILED(sBluetoothService->StartInternal())) {
|
||||
BT_WARNING("Bluetooth service failed to start!");
|
||||
}
|
||||
} else {
|
||||
BT_WARNING("Bluetooth has already been enabled before.");
|
||||
nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::StopBluetooth(bool aIsStartup)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BluetoothProfileManagerBase* profile;
|
||||
profile = BluetoothHfpManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
} else {
|
||||
profile->Reset();
|
||||
}
|
||||
|
||||
profile = BluetoothOppManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
}
|
||||
|
||||
profile = BluetoothA2dpManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
} else {
|
||||
profile->Reset();
|
||||
}
|
||||
|
||||
profile = BluetoothHidManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
} else {
|
||||
profile->Reset();
|
||||
}
|
||||
|
||||
mAdapterAddedReceived = false;
|
||||
|
||||
/* When IsEnabled() is false, we don't switch off Bluetooth but we still
|
||||
* send ToggleBtAck task. One special case happens at startup stage. At
|
||||
* startup, the initialization of BluetoothService still has to be done
|
||||
* even if Bluetooth is disabled.
|
||||
*
|
||||
* Please see bug 892392 for more information.
|
||||
*/
|
||||
if (aIsStartup || sBluetoothService->IsEnabled()) {
|
||||
// Switch Bluetooth off
|
||||
if (NS_FAILED(sBluetoothService->StopInternal())) {
|
||||
BT_WARNING("Bluetooth service failed to stop!");
|
||||
}
|
||||
} else {
|
||||
BT_WARNING("Bluetooth has already been enabled/disabled before.");
|
||||
nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup)
|
||||
{
|
||||
nsresult rv;
|
||||
if (aStart) {
|
||||
rv = StartBluetooth(aIsStartup);
|
||||
} else {
|
||||
rv = StopBluetooth(aIsStartup);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::SetEnabled(bool aEnabled)
|
||||
{
|
||||
@ -730,7 +713,7 @@ BluetoothService::HandleShutdown()
|
||||
}
|
||||
}
|
||||
|
||||
if (IsEnabled() && NS_FAILED(StartStopBluetooth(false, false))) {
|
||||
if (IsEnabled() && NS_FAILED(StopBluetooth(false))) {
|
||||
MOZ_ASSERT(false, "Failed to deliver stop message!");
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsTObserverArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
@ -333,6 +332,12 @@ protected:
|
||||
void
|
||||
Cleanup();
|
||||
|
||||
nsresult
|
||||
StartBluetooth(bool aIsStartup);
|
||||
|
||||
nsresult
|
||||
StopBluetooth(bool aIsStartup);
|
||||
|
||||
nsresult
|
||||
StartStopBluetooth(bool aStart, bool aIsStartup);
|
||||
|
||||
@ -354,15 +359,6 @@ protected:
|
||||
virtual nsresult
|
||||
StopInternal() = 0;
|
||||
|
||||
/**
|
||||
* Platform specific startup functions go here. Usually deals with member
|
||||
* variables, so not static. Guaranteed to be called outside of main thread.
|
||||
*
|
||||
* @return true if Bluetooth is enabled, false otherwise
|
||||
*/
|
||||
virtual bool
|
||||
IsEnabledInternal() = 0;
|
||||
|
||||
/**
|
||||
* Called when XPCOM first creates this service.
|
||||
*/
|
||||
@ -403,14 +399,6 @@ protected:
|
||||
bool mEnabled;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Due to the fact that the startup and shutdown of the Bluetooth system
|
||||
* can take an indefinite amount of time, a command thread is created
|
||||
* that can run blocking calls. The thread is not intended for regular
|
||||
* Bluetooth operations though.
|
||||
*/
|
||||
nsCOMPtr<nsIThread> mBluetoothThread;
|
||||
|
||||
bool mAdapterAddedReceived;
|
||||
};
|
||||
|
||||
|
@ -666,7 +666,7 @@ EnsureBluetoothHalLoad()
|
||||
static nsresult
|
||||
StartStopGonkBluetooth(bool aShouldEnable)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_TRUE(sBtInterface, NS_ERROR_FAILURE);
|
||||
|
||||
if (sIsBtEnabled == aShouldEnable) {
|
||||
@ -737,7 +737,7 @@ BluetoothServiceBluedroid::~BluetoothServiceBluedroid()
|
||||
nsresult
|
||||
BluetoothServiceBluedroid::StartInternal()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult ret = StartStopGonkBluetooth(true);
|
||||
if (NS_FAILED(ret)) {
|
||||
@ -755,7 +755,7 @@ BluetoothServiceBluedroid::StartInternal()
|
||||
nsresult
|
||||
BluetoothServiceBluedroid::StopInternal()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult ret = StartStopGonkBluetooth(false);
|
||||
if (NS_FAILED(ret)) {
|
||||
@ -770,14 +770,6 @@ BluetoothServiceBluedroid::StopInternal()
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothServiceBluedroid::IsEnabledInternal()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
return sIsBtEnabled;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothServiceBluedroid::GetDefaultAdapterPathInternal(
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
|
@ -24,7 +24,6 @@ public:
|
||||
|
||||
virtual nsresult StartInternal();
|
||||
virtual nsresult StopInternal();
|
||||
virtual bool IsEnabledInternal();
|
||||
|
||||
virtual nsresult GetDefaultAdapterPathInternal(
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "mozilla/ipc/UnixSocket.h"
|
||||
#include "mozilla/ipc/DBusUtils.h"
|
||||
#include "mozilla/ipc/RawDBusConnection.h"
|
||||
#include "mozilla/LazyIdleThread.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
@ -87,6 +88,8 @@ USING_BLUETOOTH_NAMESPACE
|
||||
*/
|
||||
#define TIMEOUT_FORCE_TO_DISABLE_BT 5
|
||||
|
||||
#define BT_LAZY_THREAD_TIMEOUT_MS 3000
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
class Bluedroid
|
||||
{
|
||||
@ -325,6 +328,18 @@ BluetoothDBusService::~BluetoothDBusService()
|
||||
sGetPropertyMonitor = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothDBusService::DispatchToBtThread(nsIRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mBluetoothThread) {
|
||||
mBluetoothThread = new LazyIdleThread(BT_LAZY_THREAD_TIMEOUT_MS,
|
||||
NS_LITERAL_CSTRING("BluetoothDBusService"),
|
||||
LazyIdleThread::ManualShutdown);
|
||||
}
|
||||
return mBluetoothThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetConnectedDevicesFilter(const BluetoothValue& aValue)
|
||||
{
|
||||
@ -1896,69 +1911,83 @@ private:
|
||||
bool mQueryDefaultAdapter;
|
||||
};
|
||||
|
||||
class StartBluetoothRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
// This could block. It should never be run on the main thread.
|
||||
MOZ_ASSERT(!NS_IsMainThread()); // BT thread
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (!sBluedroid.Enable()) {
|
||||
BT_WARNING("Bluetooth not available.");
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
RawDBusConnection* connection = new RawDBusConnection();
|
||||
nsresult rv = connection->EstablishDBusConnection();
|
||||
if (NS_FAILED(rv)) {
|
||||
BT_WARNING("Failed to establish connection to BlueZ daemon");
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
|
||||
// Set which messages will be processed by this dbus connection.
|
||||
// Since we are maintaining a single thread for all the DBus bluez
|
||||
// signals we want, register all of them in this thread at startup.
|
||||
// The event handler will sort the destinations out as needed.
|
||||
for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
|
||||
dbus_bus_add_match(connection->GetConnection(),
|
||||
sBluetoothDBusSignals[i],
|
||||
&err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
LOG_AND_FREE_DBUS_ERROR(&err);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a filter for all incoming messages_base
|
||||
if (!dbus_connection_add_filter(connection->GetConnection(),
|
||||
EventFilter, nullptr, nullptr)) {
|
||||
BT_WARNING("Cannot create DBus Event Filter for DBus Thread!");
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!sPairingReqTable) {
|
||||
sPairingReqTable = new nsDataHashtable<nsStringHashKey, DBusMessage* >;
|
||||
}
|
||||
|
||||
Task* task = new StartDBusConnectionTask(connection, sAdapterPath.IsEmpty());
|
||||
DispatchToDBusThread(task);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
nsresult
|
||||
BluetoothDBusService::StartInternal()
|
||||
{
|
||||
// This could block. It should never be run on the main thread.
|
||||
MOZ_ASSERT(!NS_IsMainThread()); // BT thread
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (!sBluedroid.Enable()) {
|
||||
BT_WARNING("Bluetooth not available.");
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
RawDBusConnection* connection = new RawDBusConnection();
|
||||
nsresult rv = connection->EstablishDBusConnection();
|
||||
nsRefPtr<nsRunnable> runnable = new StartBluetoothRunnable();
|
||||
nsresult rv = DispatchToBtThread(runnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
BT_WARNING("Failed to establish connection to BlueZ daemon");
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
BT_WARNING("Failed to dispatch to BT thread!");
|
||||
}
|
||||
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
|
||||
// Set which messages will be processed by this dbus connection.
|
||||
// Since we are maintaining a single thread for all the DBus bluez
|
||||
// signals we want, register all of them in this thread at startup.
|
||||
// The event handler will sort the destinations out as needed.
|
||||
for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
|
||||
dbus_bus_add_match(connection->GetConnection(),
|
||||
sBluetoothDBusSignals[i],
|
||||
&err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
LOG_AND_FREE_DBUS_ERROR(&err);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a filter for all incoming messages_base
|
||||
if (!dbus_connection_add_filter(connection->GetConnection(),
|
||||
EventFilter, nullptr, nullptr)) {
|
||||
BT_WARNING("Cannot create DBus Event Filter for DBus Thread!");
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!sPairingReqTable) {
|
||||
sPairingReqTable = new nsDataHashtable<nsStringHashKey, DBusMessage* >;
|
||||
}
|
||||
|
||||
Task* task = new StartDBusConnectionTask(connection, sAdapterPath.IsEmpty());
|
||||
DispatchToDBusThread(task);
|
||||
|
||||
return NS_OK;
|
||||
return rv;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
@ -1991,96 +2020,99 @@ private:
|
||||
RawDBusConnection* mConnection;
|
||||
};
|
||||
|
||||
nsresult
|
||||
BluetoothDBusService::StopInternal()
|
||||
class StopBluetoothRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
// This could block. It should never be run on the main thread.
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MonitorAutoLock lock(*sStopBluetoothMonitor);
|
||||
if (sConnectedDeviceCount > 0) {
|
||||
lock.Wait(PR_SecondsToInterval(TIMEOUT_FORCE_TO_DISABLE_BT));
|
||||
}
|
||||
}
|
||||
// This could block. It should never be run on the main thread.
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(*sStopBluetoothMonitor);
|
||||
if (sConnectedDeviceCount > 0) {
|
||||
lock.Wait(PR_SecondsToInterval(TIMEOUT_FORCE_TO_DISABLE_BT));
|
||||
}
|
||||
}
|
||||
|
||||
if (!sDBusConnection) {
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
|
||||
dbus_bus_remove_match(sDBusConnection->GetConnection(),
|
||||
sBluetoothDBusSignals[i],
|
||||
&err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
LOG_AND_FREE_DBUS_ERROR(&err);
|
||||
}
|
||||
}
|
||||
|
||||
dbus_connection_remove_filter(sDBusConnection->GetConnection(),
|
||||
EventFilter, nullptr);
|
||||
|
||||
if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(),
|
||||
KEY_LOCAL_AGENT)) {
|
||||
BT_WARNING("%s: Can't unregister object path %s for agent!",
|
||||
__FUNCTION__, KEY_LOCAL_AGENT);
|
||||
}
|
||||
|
||||
if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(),
|
||||
KEY_REMOTE_AGENT)) {
|
||||
BT_WARNING("%s: Can't unregister object path %s for agent!",
|
||||
__FUNCTION__, KEY_REMOTE_AGENT);
|
||||
}
|
||||
|
||||
// unref stored DBusMessages before clear the hashtable
|
||||
sPairingReqTable->EnumerateRead(UnrefDBusMessages, nullptr);
|
||||
sPairingReqTable->Clear();
|
||||
|
||||
sIsPairing = 0;
|
||||
sConnectedDeviceCount = 0;
|
||||
|
||||
sAuthorizedServiceClass.Clear();
|
||||
sControllerArray.Clear();
|
||||
|
||||
RawDBusConnection* connection = sDBusConnection;
|
||||
sDBusConnection = nullptr;
|
||||
|
||||
DispatchToDBusThread(new DeleteDBusConnectionTask(connection));
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
MOZ_ASSERT(sBluedroid.IsEnabled());
|
||||
if (!sBluedroid.Disable()) {
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(true);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!sDBusConnection) {
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
|
||||
dbus_bus_remove_match(sDBusConnection->GetConnection(),
|
||||
sBluetoothDBusSignals[i],
|
||||
&err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
LOG_AND_FREE_DBUS_ERROR(&err);
|
||||
}
|
||||
}
|
||||
|
||||
dbus_connection_remove_filter(sDBusConnection->GetConnection(),
|
||||
EventFilter, nullptr);
|
||||
|
||||
if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(),
|
||||
KEY_LOCAL_AGENT)) {
|
||||
BT_WARNING("%s: Can't unregister object path %s for agent!",
|
||||
__FUNCTION__, KEY_LOCAL_AGENT);
|
||||
}
|
||||
|
||||
if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(),
|
||||
KEY_REMOTE_AGENT)) {
|
||||
BT_WARNING("%s: Can't unregister object path %s for agent!",
|
||||
__FUNCTION__, KEY_REMOTE_AGENT);
|
||||
}
|
||||
|
||||
// unref stored DBusMessages before clear the hashtable
|
||||
sPairingReqTable->EnumerateRead(UnrefDBusMessages, nullptr);
|
||||
sPairingReqTable->Clear();
|
||||
|
||||
sIsPairing = 0;
|
||||
sConnectedDeviceCount = 0;
|
||||
|
||||
sAuthorizedServiceClass.Clear();
|
||||
sControllerArray.Clear();
|
||||
|
||||
RawDBusConnection* connection = sDBusConnection;
|
||||
sDBusConnection = nullptr;
|
||||
|
||||
DispatchToDBusThread(new DeleteDBusConnectionTask(connection));
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
MOZ_ASSERT(sBluedroid.IsEnabled());
|
||||
if (!sBluedroid.Disable()) {
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(true);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothDBusService::IsEnabledInternal()
|
||||
nsresult
|
||||
BluetoothDBusService::StopInternal()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread()); // BT thread
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
return sBluedroid.IsEnabled();
|
||||
#else
|
||||
return mEnabled;
|
||||
#endif
|
||||
nsRefPtr<nsRunnable> runnable = new StopBluetoothRunnable();
|
||||
nsresult rv = DispatchToBtThread(runnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
BT_WARNING("Failed to dispatch to BT thread!");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
class DefaultAdapterPathReplyHandler : public DBusReplyHandler
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "BluetoothCommon.h"
|
||||
#include "mozilla/ipc/RawDBusConnection.h"
|
||||
#include "BluetoothService.h"
|
||||
#include "nsIThread.h"
|
||||
|
||||
class DBusMessage;
|
||||
|
||||
@ -44,14 +45,14 @@ public:
|
||||
BluetoothDBusService();
|
||||
~BluetoothDBusService();
|
||||
|
||||
nsresult DispatchToBtThread(nsIRunnable* aRunnable);
|
||||
|
||||
bool IsReady();
|
||||
|
||||
virtual nsresult StartInternal() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult StopInternal() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool IsEnabledInternal() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult GetDefaultAdapterPathInternal(
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
@ -199,6 +200,14 @@ private:
|
||||
const char* aInterface,
|
||||
const nsAString& aMessage,
|
||||
mozilla::ipc::DBusReplyCallback aCallback);
|
||||
|
||||
/**
|
||||
* Due to the fact that the startup and shutdown of the Bluetooth system
|
||||
* can take an indefinite amount of time, a separate thread is used for
|
||||
* running blocking blocking calls. The thread is not intended for regular
|
||||
* Bluetooth operations though.
|
||||
*/
|
||||
nsCOMPtr<nsIThread> mBluetoothThread;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
@ -389,12 +389,6 @@ BluetoothServiceChildProcess::StopInternal()
|
||||
MOZ_CRASH("This should never be called!");
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothServiceChildProcess::IsEnabledInternal()
|
||||
{
|
||||
MOZ_CRASH("This should never be called!");
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothServiceChildProcess::IsConnected(uint16_t aServiceUuid)
|
||||
{
|
||||
|
@ -207,10 +207,6 @@ private:
|
||||
virtual nsresult
|
||||
StopInternal() MOZ_OVERRIDE;
|
||||
|
||||
// This method should never be called.
|
||||
virtual bool
|
||||
IsEnabledInternal() MOZ_OVERRIDE;
|
||||
|
||||
bool
|
||||
IsSignalRegistered(const nsAString& aNodeName) {
|
||||
return !!mBluetoothSignalObserverTable.Get(aNodeName);
|
||||
|
@ -31,6 +31,8 @@ namespace dom {
|
||||
class DeviceStorageEnumerationParameters;
|
||||
class DOMCursor;
|
||||
class DOMRequest;
|
||||
class Promise;
|
||||
class DeviceStorageFileSystem;
|
||||
} // namespace dom
|
||||
namespace ipc {
|
||||
class FileDescriptor;
|
||||
@ -157,6 +159,8 @@ class nsDOMDeviceStorage MOZ_FINAL
|
||||
EnumerationParameters;
|
||||
typedef mozilla::dom::DOMCursor DOMCursor;
|
||||
typedef mozilla::dom::DOMRequest DOMRequest;
|
||||
typedef mozilla::dom::Promise Promise;
|
||||
typedef mozilla::dom::DeviceStorageFileSystem DeviceStorageFileSystem;
|
||||
public:
|
||||
typedef nsTArray<nsString> VolumeNameArray;
|
||||
|
||||
@ -255,6 +259,9 @@ public:
|
||||
|
||||
// Uses XPCOM GetStorageName
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetRoot();
|
||||
|
||||
static void
|
||||
CreateDeviceStorageFor(nsPIDOMWindow* aWin,
|
||||
const nsAString& aType,
|
||||
@ -332,6 +339,8 @@ private:
|
||||
DEVICE_STORAGE_TYPE_SHARED,
|
||||
DEVICE_STORAGE_TYPE_EXTERNAL
|
||||
};
|
||||
|
||||
nsRefPtr<DeviceStorageFileSystem> mFileSystem;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -11,11 +11,15 @@
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/DeviceStorageBinding.h"
|
||||
#include "mozilla/dom/DeviceStorageFileSystem.h"
|
||||
#include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
|
||||
#include "mozilla/dom/Directory.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/ipc/Blob.h"
|
||||
#include "mozilla/dom/PBrowserChild.h"
|
||||
#include "mozilla/dom/PContentPermissionRequestChild.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/LazyIdleThread.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
@ -997,14 +1001,7 @@ DeviceStorageFile::IsSafePath(const nsAString& aPath)
|
||||
|
||||
void
|
||||
DeviceStorageFile::NormalizeFilePath() {
|
||||
#if defined(XP_WIN)
|
||||
char16_t* cur = mPath.BeginWriting();
|
||||
char16_t* end = mPath.EndWriting();
|
||||
for (; cur < end; ++cur) {
|
||||
if (char16_t('\\') == *cur)
|
||||
*cur = char16_t('/');
|
||||
}
|
||||
#endif
|
||||
FileSystemUtils::LocalPathToNormalizedPath(mPath, mPath);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1022,23 +1019,9 @@ DeviceStorageFile::AppendRelativePath(const nsAString& aPath) {
|
||||
NS_WARNING(NS_LossyConvertUTF16toASCII(aPath).get());
|
||||
return;
|
||||
}
|
||||
#if defined(XP_WIN)
|
||||
// replace forward slashes with backslashes,
|
||||
// since nsLocalFileWin chokes on them
|
||||
nsString temp;
|
||||
temp.Assign(aPath);
|
||||
|
||||
char16_t* cur = temp.BeginWriting();
|
||||
char16_t* end = temp.EndWriting();
|
||||
|
||||
for (; cur < end; ++cur) {
|
||||
if (char16_t('/') == *cur)
|
||||
*cur = char16_t('\\');
|
||||
}
|
||||
mFile->AppendRelativePath(temp);
|
||||
#else
|
||||
mFile->AppendRelativePath(aPath);
|
||||
#endif
|
||||
nsString localPath;
|
||||
FileSystemUtils::NormalizedPathToLocalPath(aPath, localPath);
|
||||
mFile->AppendRelativePath(localPath);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -3179,6 +3162,11 @@ nsDOMDeviceStorage::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mFileSystem) {
|
||||
mFileSystem->Shutdown();
|
||||
mFileSystem = nullptr;
|
||||
}
|
||||
|
||||
if (!mStorageName.IsEmpty()) {
|
||||
UnregisterForSDCardChanges(this);
|
||||
}
|
||||
@ -3917,6 +3905,16 @@ nsDOMDeviceStorage::Default()
|
||||
return mStorageName.Equals(defaultStorageName);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
nsDOMDeviceStorage::GetRoot()
|
||||
{
|
||||
if (!mFileSystem) {
|
||||
mFileSystem = new DeviceStorageFileSystem(mStorageType, mStorageName);
|
||||
mFileSystem->Init(this);
|
||||
}
|
||||
return mozilla::dom::Directory::GetRoot(mFileSystem);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMDeviceStorage::GetDefault(bool* aDefault)
|
||||
{
|
||||
|
@ -1,3 +1,4 @@
|
||||
[DEFAULT]
|
||||
|
||||
[test_app_permissions.html]
|
||||
[test_fs_app_permissions.html]
|
||||
|
@ -23,3 +23,8 @@ support-files = devicestorage_common.js
|
||||
[test_usedSpace.html]
|
||||
[test_watch.html]
|
||||
[test_watchOther.html]
|
||||
|
||||
# FileSystem API tests
|
||||
[test_fs_basic.html]
|
||||
[test_fs_createDirectory.html]
|
||||
[test_fs_get.html]
|
||||
|
428
dom/devicestorage/test/test_fs_app_permissions.html
Normal file
428
dom/devicestorage/test/test_fs_app_permissions.html
Normal file
@ -0,0 +1,428 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=910412
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Permission test of FileSystem API for Device Storage</title>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=910412">Mozilla Bug 910412</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
function randomFilename(l) {
|
||||
let set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ";
|
||||
let result = "";
|
||||
for (let i=0; i<l; i++) {
|
||||
let r = Math.floor(set.length * Math.random());
|
||||
result += set.substring(r, r + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
let MockPermissionPrompt = SpecialPowers.MockPermissionPrompt;
|
||||
MockPermissionPrompt.init();
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function TestCreateDirectory(iframe, data) {
|
||||
function cbError(e) {
|
||||
is(e.name, "SecurityError", "[TestCreateDirectory] Should fire a SecurityError for type " + data.type);
|
||||
is(data.shouldPass, false, "[TestCreateDirectory] Error callback was called for type " + data.type + '. Error: ' + e.name);
|
||||
testComplete(iframe, data);
|
||||
}
|
||||
|
||||
let storage = iframe.contentDocument.defaultView.navigator.getDeviceStorage(data.type);
|
||||
isnot(storage, null, "[TestCreateDirectory] Should be able to get storage object for " + data.type);
|
||||
|
||||
if (!storage) {
|
||||
testComplete(iframe, data);
|
||||
return;
|
||||
}
|
||||
|
||||
storage.getRoot().then(function(root) {
|
||||
is(data.shouldPass, true, "[TestCreateDirectory] Success callback was called for type " + data.type);
|
||||
let filename = randomFilename(100);
|
||||
root.createDirectory(filename).then(function(d) {
|
||||
let passed = d && (d.name === filename);
|
||||
is(data.shouldPass, passed, "[TestCreateDirectory] Success callback was called for type " + data.type);
|
||||
testComplete(iframe, data);
|
||||
}, cbError);
|
||||
}, cbError);
|
||||
}
|
||||
|
||||
function TestGet(iframe, data) {
|
||||
function cbError(e) {
|
||||
is(e.name, "SecurityError", "[TestGet] Should fire a SecurityError for type " + data.type);
|
||||
is(data.shouldPass, false, "[TestGet] Error callback was called for type " + data.type + '. Error: ' + e.name);
|
||||
testComplete(iframe, data);
|
||||
}
|
||||
|
||||
createTestFile(data.fileExtension);
|
||||
|
||||
let storage = iframe.contentDocument.defaultView.navigator.getDeviceStorage(data.type);
|
||||
isnot(storage, null, "[TestGet] Should be able to get storage object for " + data.type);
|
||||
|
||||
if (!storage) {
|
||||
testComplete(iframe, data);
|
||||
return;
|
||||
}
|
||||
|
||||
storage.getRoot().then(function(root) {
|
||||
ok(true, "[TestGet] Success callback of getRoot was called for type " + data.type);
|
||||
root.get("testfile" + data.fileExtension).then(function() {
|
||||
is(data.shouldPass, true, "[TestGet] Success callback was called for type " + data.type);
|
||||
testComplete(iframe, data);
|
||||
}, cbError);
|
||||
}, cbError);
|
||||
}
|
||||
|
||||
let gTestUri = "https://example.com/tests/dom/devicestorage/test/test_fs_app_permissions.html"
|
||||
|
||||
let gData = [
|
||||
|
||||
// Directory#get
|
||||
|
||||
// Web applications with no permissions
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: false,
|
||||
fileExtension: '.png',
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogv',
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
test: TestGet
|
||||
},
|
||||
|
||||
// Web applications with permission granted
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: true,
|
||||
fileExtension: '.png',
|
||||
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogv',
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: true,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
|
||||
// Certified application with permision granted
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: true,
|
||||
fileExtension: '.png',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogv',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: true,
|
||||
fileExtension: '.txt',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
|
||||
// Directory#createDirectory
|
||||
|
||||
// Web applications with no permissions
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: false,
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: false,
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: false,
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
|
||||
// Web applications with permission granted
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: true,
|
||||
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: true,
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: true,
|
||||
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
|
||||
// Certified application with permision granted
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: true,
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: true,
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: true,
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestCreateDirectory
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
function setupTest(iframe,data) {
|
||||
if (data.permissions) {
|
||||
for (let j in data.permissions) {
|
||||
SpecialPowers.addPermission(data.permissions[j], true, iframe.contentDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testComplete(iframe, data) {
|
||||
if (data.permissions) {
|
||||
for (let j in data.permissions) {
|
||||
SpecialPowers.removePermission(data.permissions[j], iframe.contentDocument);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('content').removeChild(iframe);
|
||||
|
||||
if (gData.length == 0) {
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
gTestRunner.next();
|
||||
}
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
while (gData.length > 0) {
|
||||
let iframe = document.createElement('iframe');
|
||||
let data = gData.shift();
|
||||
|
||||
iframe.setAttribute('mozbrowser', '');
|
||||
if (data.app) {
|
||||
iframe.setAttribute('mozapp', data.app);
|
||||
}
|
||||
|
||||
iframe.src = gTestUri;
|
||||
|
||||
iframe.addEventListener('load', function(e) {
|
||||
setupTest(iframe, data)
|
||||
data.test(iframe, data);
|
||||
});
|
||||
|
||||
document.getElementById('content').appendChild(iframe);
|
||||
yield undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function createTestFile(extension) {
|
||||
try {
|
||||
const Cc = SpecialPowers.Cc;
|
||||
const Ci = SpecialPowers.Ci;
|
||||
let directoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
let f = directoryService.get("TmpD", Ci.nsIFile);
|
||||
f.appendRelativePath("device-storage-testing");
|
||||
f.remove(true);
|
||||
f.appendRelativePath("testfile" + extension);
|
||||
f.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
let gTestRunner = runTest();
|
||||
SpecialPowers.addPermission("browser", true, gTestUri);
|
||||
|
||||
// We are more permissive with CSP in our testing environment....
|
||||
const DEFAULT_CSP_PRIV = "default-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'";
|
||||
const DEFAULT_CSP_CERT = "default-src *; script-src 'self'; style-src 'self'; object-src 'none'";
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [["dom.mozBrowserFramesEnabled", true],
|
||||
["device.storage.enabled", true],
|
||||
["device.storage.testing", true],
|
||||
["device.storage.prompt.testing", false],
|
||||
["security.apps.privileged.CSP.default", DEFAULT_CSP_PRIV],
|
||||
["security.apps.certified.CSP.default", DEFAULT_CSP_CERT]]},
|
||||
function() { gTestRunner.next(); });
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
70
dom/devicestorage/test/test_fs_basic.html
Normal file
70
dom/devicestorage/test/test_fs_basic.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html> <!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=910412
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the FileSystem API for device storage</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="devicestorage_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=910412">Mozilla Bug 910412</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
|
||||
var gFileName = randomFilename(12);
|
||||
|
||||
// The root directory object.
|
||||
var gRoot;
|
||||
|
||||
function getRootSuccess(r) {
|
||||
ok(r && r.name === storage.storageName, "Failed to get the root directory.");
|
||||
|
||||
gRoot = r;
|
||||
|
||||
// Create a new directory under the root.
|
||||
gRoot.createDirectory(gFileName).then(createDirectorySuccess, cbError);
|
||||
}
|
||||
|
||||
function createDirectorySuccess(d) {
|
||||
ok(d.name === gFileName, "Failed to create directory: name mismatch.");
|
||||
|
||||
// Get the new created directory from the root.
|
||||
gRoot.get(gFileName).then(getSuccess, cbError);
|
||||
}
|
||||
|
||||
function getSuccess(d) {
|
||||
ok(d.name === gFileName, "Should get directory - " + gFileName + ".");
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
function cbError(e) {
|
||||
ok(false, "Should not arrive here! Error: " + e.name);
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(storage, "Should have gotten a storage");
|
||||
|
||||
var promise = storage.getRoot();
|
||||
ok(promise, "Should have a non-null promise");
|
||||
|
||||
promise.then(getRootSuccess, cbError);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
105
dom/devicestorage/test/test_fs_createDirectory.html
Normal file
105
dom/devicestorage/test/test_fs_createDirectory.html
Normal file
@ -0,0 +1,105 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html> <!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=910412
|
||||
-->
|
||||
<head>
|
||||
<title>Test createDirectory of the FileSystem API for device storage</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="devicestorage_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=910412">Mozilla Bug 910412</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
|
||||
// The root directory object.
|
||||
var gRoot;
|
||||
var gTestCount = 0;
|
||||
var gPath = '';
|
||||
var gName = '';
|
||||
|
||||
function testCreateDirectory(rootDir, path) {
|
||||
rootDir.createDirectory(path).then(createDirectorySuccess, cbError);
|
||||
}
|
||||
|
||||
function createDirectorySuccess(d) {
|
||||
ok(d.name === gName, "Failed to create directory: name mismatch.");
|
||||
|
||||
// Get the new created directory from the root.
|
||||
gRoot.get(gPath).then(getSuccess, cbError);
|
||||
}
|
||||
|
||||
function getSuccess(d) {
|
||||
ok(d.name === gName, "Should get directory - " + (gPath || "[root]") + ".");
|
||||
switch (gTestCount) {
|
||||
case 0:
|
||||
gRoot = d;
|
||||
// Create a new directory under the root.
|
||||
gName = gPath = randomFilename(12);
|
||||
testCreateDirectory(gRoot, gName);
|
||||
break;
|
||||
case 1:
|
||||
// Create a sub-directory under current directory.
|
||||
gName = randomFilename(12);
|
||||
gPath = gPath + '/' + gName;
|
||||
testCreateDirectory(d, gName);
|
||||
break;
|
||||
case 2:
|
||||
// Create directory with an existing path.
|
||||
gRoot.createDirectory(gPath).then(function(what) {
|
||||
ok(false, "Should not overwrite an existing directory.");
|
||||
devicestorage_cleanup();
|
||||
}, function(e) {
|
||||
ok(true, "Creating directory should fail if it already exists.");
|
||||
|
||||
// Create a directory whose intermediate directory doesn't exit.
|
||||
gName = randomFilename(12);
|
||||
gPath = 'sub1/sub2/' + gName;
|
||||
testCreateDirectory(gRoot, gPath);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
// Create the parent directory.
|
||||
d.createDirectory('..').then(function(what) {
|
||||
ok(false, "Should not overwrite an existing directory.");
|
||||
devicestorage_cleanup();
|
||||
}, function(e) {
|
||||
ok(true, "Accessing parent directory with '..' is not allowed.");
|
||||
devicestorage_cleanup();
|
||||
});
|
||||
break;
|
||||
}
|
||||
gTestCount++;
|
||||
}
|
||||
|
||||
function cbError(e) {
|
||||
ok(false, e.name + " error should not arrive here!");
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage.");
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(storage, "Should have gotten a storage.");
|
||||
|
||||
var promise = storage.getRoot();
|
||||
ok(promise, "Should have a non-null promise for getRoot.");
|
||||
|
||||
gName = storage.storageName;
|
||||
promise.then(getSuccess, cbError);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
204
dom/devicestorage/test/test_fs_get.html
Normal file
204
dom/devicestorage/test/test_fs_get.html
Normal file
@ -0,0 +1,204 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html> <!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=910412
|
||||
-->
|
||||
<head>
|
||||
<title>Test Directory#get of the FileSystem API for device storage</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="devicestorage_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=910412">Mozilla Bug 910412</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
SimpleTest.requestCompleteLog();
|
||||
|
||||
// The root directory object.
|
||||
var gRoot = null;
|
||||
var gSub1 = null;
|
||||
var gSub2 = null;
|
||||
var gTestCount = 0;
|
||||
var gPath = "/";
|
||||
|
||||
function testGetSuccess(dir, path) {
|
||||
dir.get(path).then(getSuccess, cbError);
|
||||
}
|
||||
|
||||
function testGetFailure(dir, path) {
|
||||
dir.get(path).then(cbSuccess, getFailure);
|
||||
}
|
||||
|
||||
function getSuccess(r) {
|
||||
ok(r, "[" + gTestCount +"] Should get the file - " + gPath + ".");
|
||||
switch (gTestCount) {
|
||||
case 0:
|
||||
gRoot = r;
|
||||
// Get sub1/sub2/text.png from root.
|
||||
gPath = "sub1/sub2/test.png";
|
||||
testGetSuccess(gRoot, "sub1/sub2/test.png");
|
||||
break;
|
||||
case 1:
|
||||
// Get sub1 from root.
|
||||
gPath = "sub1";
|
||||
testGetSuccess(gRoot, "sub1");
|
||||
break;
|
||||
case 2:
|
||||
// Get sub1/sub2 from root.
|
||||
gSub1 = r;
|
||||
gPath = "sub1/sub2";
|
||||
testGetSuccess(gRoot, "sub1/sub2");
|
||||
break;
|
||||
case 3:
|
||||
// Get sub1/sub2 from sub2.
|
||||
gSub2 = r;
|
||||
gPath = "sub1/sub2";
|
||||
testGetSuccess(gSub1, "sub2");
|
||||
break;
|
||||
case 4:
|
||||
// Test path with leading and trailing white spaces.
|
||||
gPath = "sub1/sub2";
|
||||
testGetSuccess(gSub1, "\t sub2 ");
|
||||
break;
|
||||
case 5:
|
||||
// Get sub1 from sub1/sub2 with "..".
|
||||
gPath = "sub1/sub2/..";
|
||||
testGetFailure(gSub2, "..");
|
||||
break;
|
||||
default:
|
||||
ok(false, "Should not arrive at getSuccess!");
|
||||
devicestorage_cleanup();
|
||||
break;
|
||||
}
|
||||
gTestCount++;
|
||||
}
|
||||
|
||||
function getFailure(e) {
|
||||
ok(true, "[" + gTestCount +"] Should not get the file - " + gPath + ". Error: " + e.name);
|
||||
switch (gTestCount) {
|
||||
case 6:
|
||||
// Test special path "..".
|
||||
gPath = "sub1/sub2/../sub2";
|
||||
testGetFailure(gSub2, "../sub2");
|
||||
break;
|
||||
case 7:
|
||||
gPath = "sub1/sub2/../sub2";
|
||||
testGetFailure(gRoot, "sub1/sub2/../sub2");
|
||||
break;
|
||||
case 8:
|
||||
// Test special path ".".
|
||||
gPath = "sub1/./sub2";
|
||||
testGetFailure(gRoot, "sub1/./sub2");
|
||||
break;
|
||||
case 9:
|
||||
gPath = "./sub1/sub2";
|
||||
testGetFailure(gRoot, "./sub1/sub2");
|
||||
break;
|
||||
case 10:
|
||||
gPath = "././sub1/sub2";
|
||||
testGetFailure(gRoot, "././sub1/sub2");
|
||||
break;
|
||||
case 11:
|
||||
gPath = "sub1/sub2/.";
|
||||
testGetFailure(gRoot, "sub1/sub2/.");
|
||||
break;
|
||||
case 12:
|
||||
gPath = "sub1/.";
|
||||
testGetFailure(gSub1, "./");
|
||||
break;
|
||||
case 13:
|
||||
// Test path starting with "/".
|
||||
gPath = "sub1/";
|
||||
testGetFailure(gSub1, "/");
|
||||
break;
|
||||
case 14:
|
||||
// Test path ending with "/".
|
||||
gPath = "sub1/";
|
||||
testGetFailure(gSub1, "sub2/");
|
||||
break;
|
||||
case 15:
|
||||
// Test empty path.
|
||||
gPath = "sub2";
|
||||
testGetFailure(gSub2, "");
|
||||
break;
|
||||
case 16:
|
||||
// Test special path "//".
|
||||
gPath = "sub1//sub2";
|
||||
testGetFailure(gRoot, "sub1//sub2");
|
||||
break;
|
||||
case 17:
|
||||
devicestorage_cleanup();
|
||||
break;
|
||||
default:
|
||||
ok(false, "Should not arrive here!");
|
||||
devicestorage_cleanup();
|
||||
break;
|
||||
}
|
||||
gTestCount++;
|
||||
}
|
||||
|
||||
function cbError(e) {
|
||||
ok(false, "Should not arrive at cbError! Error: " + e.name);
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
function cbSuccess(e) {
|
||||
ok(false, "Should not arrive at cbSuccess!");
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage.");
|
||||
|
||||
var gStorage = navigator.getDeviceStorage("pictures");
|
||||
ok(gStorage, "Should have gotten a storage.");
|
||||
|
||||
function createTestFile(path, callback) {
|
||||
function addNamed() {
|
||||
var req = gStorage.addNamed(createRandomBlob("image/png"), path);
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(true, path + " was created.");
|
||||
callback();
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
ok(false, "Failed to create " + path + ": " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
};
|
||||
}
|
||||
|
||||
// Bug 980136. Check if the file exists before we create.
|
||||
var req = gStorage.get(path);
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(true, path + " exists. Do not need to create.");
|
||||
callback();
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
ok(true, path + " does not exists: " + e.target.error.name);
|
||||
addNamed();
|
||||
};
|
||||
}
|
||||
|
||||
createTestFile("sub1/sub2/test.png", function() {
|
||||
var promise = gStorage.getRoot();
|
||||
ok(promise, "Should have a non-null promise for getRoot.");
|
||||
promise.then(getSuccess, cbError);
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
141
dom/filesystem/CreateDirectoryTask.cpp
Normal file
141
dom/filesystem/CreateDirectoryTask.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "CreateDirectoryTask.h"
|
||||
|
||||
#include "DOMError.h"
|
||||
#include "mozilla/dom/Directory.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aPath)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetRealPath(aPath)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
}
|
||||
mPromise = new Promise(globalObject);
|
||||
}
|
||||
|
||||
CreateDirectoryTask::CreateDirectoryTask(
|
||||
FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent)
|
||||
: FileSystemTaskBase(aFileSystem, aParam, aParent)
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
mTargetRealPath = aParam.realPath();
|
||||
}
|
||||
|
||||
CreateDirectoryTask::~CreateDirectoryTask()
|
||||
{
|
||||
MOZ_ASSERT(!mPromise || NS_IsMainThread(),
|
||||
"mPromise should be released on main thread!");
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateDirectoryTask::GetPromise()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return nsRefPtr<Promise>(mPromise).forget();
|
||||
}
|
||||
|
||||
FileSystemParams
|
||||
CreateDirectoryTask::GetRequestParams(const nsString& aFileSystem) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return FileSystemCreateDirectoryParams(aFileSystem, mTargetRealPath);
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
CreateDirectoryTask::GetSuccessRequestResult() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return FileSystemDirectoryResponse(mTargetRealPath);
|
||||
}
|
||||
|
||||
void
|
||||
CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
FileSystemDirectoryResponse r = aValue;
|
||||
mTargetRealPath = r.realPath();
|
||||
}
|
||||
|
||||
nsresult
|
||||
CreateDirectoryTask::Work()
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
|
||||
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
|
||||
if (!file) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
|
||||
bool fileExists;
|
||||
nsresult rv = file->Exists(&fileExists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (fileExists) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
|
||||
}
|
||||
|
||||
rv = file->Create(nsIFile::DIRECTORY_TYPE, 0770);
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
CreateDirectoryTask::HandlerCallback()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasError()) {
|
||||
nsRefPtr<DOMError> domError = new DOMError(mFileSystem->GetWindow(),
|
||||
mErrorValue);
|
||||
mPromise->MaybeReject(domError);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
nsRefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
|
||||
mPromise->MaybeResolve(dir);
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
CreateDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral("create");
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
61
dom/filesystem/CreateDirectoryTask.h
Normal file
61
dom/filesystem/CreateDirectoryTask.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_CreateDirectoryTask_h
|
||||
#define mozilla_dom_CreateDirectoryTask_h
|
||||
|
||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
|
||||
class CreateDirectoryTask MOZ_FINAL
|
||||
: public FileSystemTaskBase
|
||||
{
|
||||
public:
|
||||
CreateDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aPath);
|
||||
CreateDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
|
||||
virtual
|
||||
~CreateDirectoryTask();
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetPromise();
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aFileSystem) const MOZ_OVERRIDE;
|
||||
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult() const MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult
|
||||
Work() MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
HandlerCallback() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
nsString mTargetRealPath;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_CreateDirectoryTask_h
|
146
dom/filesystem/DeviceStorageFileSystem.cpp
Normal file
146
dom/filesystem/DeviceStorageFileSystem.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/DeviceStorageFileSystem.h"
|
||||
|
||||
#include "DeviceStorage.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/Directory.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsDeviceStorage.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
DeviceStorageFileSystem::DeviceStorageFileSystem(
|
||||
const nsAString& aStorageType,
|
||||
const nsAString& aStorageName)
|
||||
: mDeviceStorage(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
|
||||
mStorageType = aStorageType;
|
||||
mStorageName = aStorageName;
|
||||
|
||||
// Generate the string representation of the file system.
|
||||
mString.AppendLiteral("devicestorage-");
|
||||
mString.Append(mStorageType);
|
||||
mString.AppendLiteral("-");
|
||||
mString.Append(mStorageName);
|
||||
|
||||
mIsTesting =
|
||||
mozilla::Preferences::GetBool("device.storage.prompt.testing", false);
|
||||
|
||||
// Get the permission name required to access the file system.
|
||||
nsresult rv =
|
||||
DeviceStorageTypeChecker::GetPermissionForType(mStorageType, mPermission);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
// Get the local path of the file system root.
|
||||
// Since the child process is not allowed to access the file system, we only
|
||||
// do this from the parent process.
|
||||
if (!FileSystemUtils::IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsIFile> rootFile;
|
||||
DeviceStorageFile::GetRootDirectoryForType(aStorageType,
|
||||
aStorageName,
|
||||
getter_AddRefs(rootFile));
|
||||
|
||||
NS_WARN_IF(!rootFile || NS_FAILED(rootFile->GetPath(mLocalRootPath)));
|
||||
FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath,
|
||||
mNormalizedLocalRootPath);
|
||||
|
||||
// DeviceStorageTypeChecker is a singleton object and must be initialized on
|
||||
// the main thread. We initialize it here so that we can use it on the worker
|
||||
// thread.
|
||||
DebugOnly<DeviceStorageTypeChecker*> typeChecker
|
||||
= DeviceStorageTypeChecker::CreateOrGet();
|
||||
MOZ_ASSERT(typeChecker);
|
||||
}
|
||||
|
||||
DeviceStorageFileSystem::~DeviceStorageFileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aDeviceStorage);
|
||||
mDeviceStorage = aDeviceStorage;
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFileSystem::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
mDeviceStorage = nullptr;
|
||||
mShutdown = true;
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
DeviceStorageFileSystem::GetWindow() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
if (!mDeviceStorage) {
|
||||
return nullptr;
|
||||
}
|
||||
return mDeviceStorage->GetOwner();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
DeviceStorageFileSystem::GetLocalFile(const nsAString& aRealPath) const
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Should be on parent process!");
|
||||
nsAutoString localPath;
|
||||
FileSystemUtils::NormalizedPathToLocalPath(aRealPath, localPath);
|
||||
localPath = mLocalRootPath + localPath;
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsresult rv = NS_NewLocalFile(localPath, false, getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
return file.forget();
|
||||
}
|
||||
|
||||
const nsAString&
|
||||
DeviceStorageFileSystem::GetRootName() const
|
||||
{
|
||||
return mStorageName;
|
||||
}
|
||||
|
||||
bool
|
||||
DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Should be on parent process!");
|
||||
MOZ_ASSERT(aFile);
|
||||
|
||||
// Check if this file belongs to this storage.
|
||||
nsAutoString path;
|
||||
if (NS_FAILED(aFile->GetPath(path))) {
|
||||
return false;
|
||||
}
|
||||
FileSystemUtils::LocalPathToNormalizedPath(path, path);
|
||||
if (!FileSystemUtils::IsDescendantPath(mNormalizedLocalRootPath, path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the file type is compatible with the storage type.
|
||||
DeviceStorageTypeChecker* typeChecker
|
||||
= DeviceStorageTypeChecker::CreateOrGet();
|
||||
MOZ_ASSERT(typeChecker);
|
||||
return typeChecker->Check(mStorageType, aFile);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
62
dom/filesystem/DeviceStorageFileSystem.h
Normal file
62
dom/filesystem/DeviceStorageFileSystem.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_DeviceStorageFileSystem_h
|
||||
#define mozilla_dom_DeviceStorageFileSystem_h
|
||||
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsDOMDeviceStorage;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DeviceStorageFileSystem
|
||||
: public FileSystemBase
|
||||
{
|
||||
public:
|
||||
DeviceStorageFileSystem(const nsAString& aStorageType,
|
||||
const nsAString& aStorageName);
|
||||
|
||||
void
|
||||
Init(nsDOMDeviceStorage* aDeviceStorage);
|
||||
|
||||
// Overrides FileSystemBase
|
||||
|
||||
virtual void
|
||||
Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsPIDOMWindow*
|
||||
GetWindow() const MOZ_OVERRIDE;
|
||||
|
||||
virtual already_AddRefed<nsIFile>
|
||||
GetLocalFile(const nsAString& aRealPath) const MOZ_OVERRIDE;
|
||||
|
||||
virtual const nsAString&
|
||||
GetRootName() const MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
IsSafeFile(nsIFile* aFile) const MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
virtual
|
||||
~DeviceStorageFileSystem();
|
||||
|
||||
nsString mStorageType;
|
||||
nsString mStorageName;
|
||||
|
||||
// The local path of the root. Only available in the parent process.
|
||||
// In the child process, we don't use it and its value should be empty.
|
||||
nsString mLocalRootPath;
|
||||
nsString mNormalizedLocalRootPath;
|
||||
nsDOMDeviceStorage* mDeviceStorage;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_DeviceStorageFileSystem_h
|
177
dom/filesystem/Directory.cpp
Normal file
177
dom/filesystem/Directory.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/Directory.h"
|
||||
|
||||
#include "CreateDirectoryTask.h"
|
||||
#include "FileSystemPermissionRequest.h"
|
||||
#include "GetFileOrDirectoryTask.h"
|
||||
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/dom/DirectoryBinding.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
|
||||
// Resolve the name collision of Microsoft's API name with macros defined in
|
||||
// Windows header files. Undefine the macro of CreateDirectory to avoid
|
||||
// Directory#CreateDirectory being replaced by Directory#CreateDirectoryW.
|
||||
#ifdef CreateDirectory
|
||||
#undef CreateDirectory
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Directory)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Directory)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Directory)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Directory)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
// static
|
||||
already_AddRefed<Promise>
|
||||
Directory::GetRoot(FileSystemBase* aFileSystem)
|
||||
{
|
||||
nsRefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
|
||||
aFileSystem, EmptyString(), true);
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
Directory::Directory(FileSystemBase* aFileSystem,
|
||||
const nsAString& aPath)
|
||||
: mFileSystem(aFileSystem)
|
||||
, mPath(aPath)
|
||||
{
|
||||
MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
|
||||
// Remove the trailing "/".
|
||||
mPath.Trim(FILESYSTEM_DOM_PATH_SEPARATOR, false, true);
|
||||
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
Directory::~Directory()
|
||||
{
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
Directory::GetParentObject() const
|
||||
{
|
||||
return mFileSystem->GetWindow();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
Directory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
||||
{
|
||||
return DirectoryBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
void
|
||||
Directory::GetName(nsString& aRetval) const
|
||||
{
|
||||
aRetval.Truncate();
|
||||
|
||||
if (mPath.IsEmpty()) {
|
||||
aRetval = mFileSystem->GetRootName();
|
||||
return;
|
||||
}
|
||||
|
||||
aRetval = Substring(mPath,
|
||||
mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::CreateDirectory(const nsAString& aPath)
|
||||
{
|
||||
nsresult error = NS_OK;
|
||||
nsString realPath;
|
||||
if (!DOMPathToRealPath(aPath, realPath)) {
|
||||
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
nsRefPtr<CreateDirectoryTask> task = new CreateDirectoryTask(
|
||||
mFileSystem, realPath);
|
||||
task->SetError(error);
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::Get(const nsAString& aPath)
|
||||
{
|
||||
nsresult error = NS_OK;
|
||||
nsString realPath;
|
||||
if (!DOMPathToRealPath(aPath, realPath)) {
|
||||
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
nsRefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
|
||||
mFileSystem, realPath, false);
|
||||
task->SetError(error);
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
bool
|
||||
Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
|
||||
{
|
||||
aRealPath.Truncate();
|
||||
|
||||
nsString relativePath;
|
||||
relativePath = aPath;
|
||||
|
||||
// Trim white spaces.
|
||||
static const char kWhitespace[] = "\b\t\r\n ";
|
||||
relativePath.Trim(kWhitespace);
|
||||
|
||||
if (!IsValidRelativePath(relativePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aRealPath = mPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR) +
|
||||
relativePath;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
Directory::IsValidRelativePath(const nsString& aPath)
|
||||
{
|
||||
// We don't allow empty relative path to access the root.
|
||||
if (aPath.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Leading and trailing "/" are not allowed.
|
||||
if (aPath.First() == FileSystemUtils::kSeparatorChar ||
|
||||
aPath.Last() == FileSystemUtils::kSeparatorChar) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_STRING(kCurrentDir, ".");
|
||||
NS_NAMED_LITERAL_STRING(kParentDir, "..");
|
||||
|
||||
// Split path and check each path component.
|
||||
nsCharSeparatedTokenizer tokenizer(aPath, FileSystemUtils::kSeparatorChar);
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
nsDependentSubstring pathComponent = tokenizer.nextToken();
|
||||
// The path containing empty components, such as "foo//bar", is invalid.
|
||||
// We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar",
|
||||
// to walk up the directory.
|
||||
if (pathComponent.IsEmpty() ||
|
||||
pathComponent.Equals(kCurrentDir) ||
|
||||
pathComponent.Equals(kParentDir)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
81
dom/filesystem/Directory.h
Normal file
81
dom/filesystem/Directory.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_Directory_h
|
||||
#define mozilla_dom_Directory_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
// Resolve the name collision of Microsoft's API name with macros defined in
|
||||
// Windows header files. Undefine the macro of CreateDirectory to avoid
|
||||
// Directory#CreateDirectory being replaced by Directory#CreateDirectoryW.
|
||||
#ifdef CreateDirectory
|
||||
#undef CreateDirectory
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FileSystemBase;
|
||||
class Promise;
|
||||
|
||||
class Directory MOZ_FINAL
|
||||
: public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory)
|
||||
|
||||
public:
|
||||
static already_AddRefed<Promise>
|
||||
GetRoot(FileSystemBase* aFileSystem);
|
||||
|
||||
Directory(FileSystemBase* aFileSystem, const nsAString& aPath);
|
||||
~Directory();
|
||||
|
||||
// ========= Begin WebIDL bindings. ===========
|
||||
|
||||
nsPIDOMWindow*
|
||||
GetParentObject() const;
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
||||
void
|
||||
GetName(nsString& aRetval) const;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateDirectory(const nsAString& aPath);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Get(const nsAString& aPath);
|
||||
|
||||
// =========== End WebIDL bindings.============
|
||||
private:
|
||||
static bool
|
||||
IsValidRelativePath(const nsString& aPath);
|
||||
|
||||
/*
|
||||
* Convert relative DOM path to the absolute real path.
|
||||
* @return true if succeed. false if the DOM path is invalid.
|
||||
*/
|
||||
bool
|
||||
DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const;
|
||||
|
||||
nsRefPtr<FileSystemBase> mFileSystem;
|
||||
nsString mPath;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Directory_h
|
72
dom/filesystem/FileSystemBase.cpp
Normal file
72
dom/filesystem/FileSystemBase.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
|
||||
#include "DeviceStorageFileSystem.h"
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// static
|
||||
already_AddRefed<FileSystemBase>
|
||||
FileSystemBase::FromString(const nsAString& aString)
|
||||
{
|
||||
if (StringBeginsWith(aString, NS_LITERAL_STRING("devicestorage-"))) {
|
||||
// The string representation of devicestorage file system is of the format:
|
||||
// devicestorage-StorageType-StorageName
|
||||
|
||||
nsCharSeparatedTokenizer tokenizer(aString, char16_t('-'));
|
||||
tokenizer.nextToken();
|
||||
|
||||
nsString storageType;
|
||||
if (tokenizer.hasMoreTokens()) {
|
||||
storageType = tokenizer.nextToken();
|
||||
}
|
||||
|
||||
nsString storageName;
|
||||
if (tokenizer.hasMoreTokens()) {
|
||||
storageName = tokenizer.nextToken();
|
||||
}
|
||||
|
||||
nsRefPtr<DeviceStorageFileSystem> f =
|
||||
new DeviceStorageFileSystem(storageType, storageName);
|
||||
return f.forget();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileSystemBase::FileSystemBase()
|
||||
: mShutdown(false)
|
||||
, mIsTesting(false)
|
||||
{
|
||||
}
|
||||
|
||||
FileSystemBase::~FileSystemBase()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemBase::Shutdown()
|
||||
{
|
||||
mShutdown = true;
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
FileSystemBase::GetWindow() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
FileSystemBase::IsSafeFile(nsIFile* aFile) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
95
dom/filesystem/FileSystemBase.h
Normal file
95
dom/filesystem/FileSystemBase.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_FileSystemBase_h
|
||||
#define mozilla_dom_FileSystemBase_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FileSystemBase
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemBase)
|
||||
public:
|
||||
|
||||
// Create file system object from its string representation.
|
||||
static already_AddRefed<FileSystemBase>
|
||||
FromString(const nsAString& aString);
|
||||
|
||||
FileSystemBase();
|
||||
|
||||
virtual void
|
||||
Shutdown();
|
||||
|
||||
// Get the string representation of the file system.
|
||||
const nsString&
|
||||
ToString() const
|
||||
{
|
||||
return mString;
|
||||
}
|
||||
|
||||
virtual nsPIDOMWindow*
|
||||
GetWindow() const;
|
||||
|
||||
/*
|
||||
* Create nsIFile object with the given real path (absolute DOM path).
|
||||
*/
|
||||
virtual already_AddRefed<nsIFile>
|
||||
GetLocalFile(const nsAString& aRealPath) const = 0;
|
||||
|
||||
/*
|
||||
* Get the virtual name of the root directory. This name will be exposed to
|
||||
* the content page.
|
||||
*/
|
||||
virtual const nsAString&
|
||||
GetRootName() const = 0;
|
||||
|
||||
bool
|
||||
IsShutdown() const
|
||||
{
|
||||
return mShutdown;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
IsSafeFile(nsIFile* aFile) const;
|
||||
|
||||
/*
|
||||
* Get the permission name required to access this file system.
|
||||
*/
|
||||
const nsCString&
|
||||
GetPermission() const
|
||||
{
|
||||
return mPermission;
|
||||
}
|
||||
|
||||
bool
|
||||
IsTesting() const
|
||||
{
|
||||
return mIsTesting;
|
||||
}
|
||||
protected:
|
||||
virtual ~FileSystemBase();
|
||||
|
||||
// The string representation of the file system.
|
||||
nsString mString;
|
||||
|
||||
bool mShutdown;
|
||||
|
||||
// The permission name required to access the file system.
|
||||
nsCString mPermission;
|
||||
|
||||
bool mIsTesting;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FileSystemBase_h
|
190
dom/filesystem/FileSystemPermissionRequest.cpp
Normal file
190
dom/filesystem/FileSystemPermissionRequest.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
#include "FileSystemPermissionRequest.h"
|
||||
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS2(FileSystemPermissionRequest, nsIRunnable, nsIContentPermissionRequest)
|
||||
|
||||
// static
|
||||
void
|
||||
FileSystemPermissionRequest::RequestForTask(FileSystemTaskBase* aTask)
|
||||
{
|
||||
MOZ_ASSERT(aTask, "aTask should not be null!");
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<FileSystemPermissionRequest> request =
|
||||
new FileSystemPermissionRequest(aTask);
|
||||
NS_DispatchToCurrentThread(request);
|
||||
}
|
||||
|
||||
FileSystemPermissionRequest::FileSystemPermissionRequest(
|
||||
FileSystemTaskBase* aTask)
|
||||
: mTask(aTask)
|
||||
{
|
||||
MOZ_ASSERT(mTask, "aTask should not be null!");
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mTask->GetPermissionAccessType(mPermissionAccess);
|
||||
|
||||
nsRefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
|
||||
if (!filesystem) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPermissionType = filesystem->GetPermission();
|
||||
|
||||
mWindow = filesystem->GetWindow();
|
||||
if (!mWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPrincipal = doc->NodePrincipal();
|
||||
}
|
||||
|
||||
FileSystemPermissionRequest::~FileSystemPermissionRequest()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
FileSystemPermissionRequest::Recv__delete__(const bool& aAllow,
|
||||
const InfallibleTArray<PermissionChoice>& aChoices)
|
||||
{
|
||||
MOZ_ASSERT(aChoices.IsEmpty(),
|
||||
"FileSystemPermissionRequest doesn't support permission choice");
|
||||
if (aAllow) {
|
||||
Allow(JS::UndefinedHandleValue);
|
||||
} else {
|
||||
Cancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemPermissionRequest::IPDLRelease()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
nsTArray<nsString> emptyOptions;
|
||||
return CreatePermissionArray(mPermissionType,
|
||||
mPermissionAccess,
|
||||
emptyOptions,
|
||||
aTypes);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
|
||||
{
|
||||
NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
|
||||
{
|
||||
NS_IF_ADDREF(*aRequestingWindow = mWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::GetElement(nsIDOMElement** aRequestingElement)
|
||||
{
|
||||
*aRequestingElement = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::Cancel()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mTask->SetError(NS_ERROR_DOM_SECURITY_ERR);
|
||||
mTask->Start();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::Allow(JS::HandleValue aChoices)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aChoices.isUndefined());
|
||||
mTask->Start();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsRefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
|
||||
if (!filesystem) {
|
||||
Cancel();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (filesystem->IsTesting()) {
|
||||
Allow(JS::UndefinedHandleValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (FileSystemUtils::IsParentProcess()) {
|
||||
nsCOMPtr<nsIContentPermissionPrompt> prompt
|
||||
= do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
|
||||
if (!prompt || NS_FAILED(prompt->Prompt(this))) {
|
||||
Cancel();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mWindow) {
|
||||
Cancel();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// because owner implements nsITabChild, we can assume that it is
|
||||
// the one and only TabChild.
|
||||
TabChild* child = TabChild::GetFrom(mWindow->GetDocShell());
|
||||
if (!child) {
|
||||
Cancel();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Retain a reference so the object isn't deleted without IPDL's
|
||||
// knowledge. Corresponding release occurs in
|
||||
// DeallocPContentPermissionRequest.
|
||||
AddRef();
|
||||
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
nsTArray<nsString> emptyOptions;
|
||||
permArray.AppendElement(PermissionRequest(mPermissionType,
|
||||
mPermissionAccess,
|
||||
emptyOptions));
|
||||
child->SendPContentPermissionRequestConstructor(
|
||||
this, permArray, IPC::Principal(mPrincipal));
|
||||
|
||||
Sendprompt();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} /* namespace dom */
|
||||
} /* namespace mozilla */
|
61
dom/filesystem/FileSystemPermissionRequest.h
Normal file
61
dom/filesystem/FileSystemPermissionRequest.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_FileSystemPermissionRequest_h
|
||||
#define mozilla_dom_FileSystemPermissionRequest_h
|
||||
|
||||
#include "PCOMContentPermissionRequestChild.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "nsIRunnable.h"
|
||||
|
||||
class nsCString;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FileSystemTaskBase;
|
||||
|
||||
class FileSystemPermissionRequest MOZ_FINAL
|
||||
: public nsIContentPermissionRequest
|
||||
, public nsIRunnable
|
||||
, public PCOMContentPermissionRequestChild
|
||||
{
|
||||
public:
|
||||
// Request permission for the given task.
|
||||
static void
|
||||
RequestForTask(FileSystemTaskBase* aTask);
|
||||
|
||||
// Overrides PCOMContentPermissionRequestChild
|
||||
|
||||
virtual void
|
||||
IPDLRelease() MOZ_OVERRIDE;
|
||||
|
||||
bool
|
||||
Recv__delete__(const bool& aAllow,
|
||||
const InfallibleTArray<PermissionChoice>& aChoices) MOZ_OVERRIDE;
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPERMISSIONREQUEST
|
||||
NS_DECL_NSIRUNNABLE
|
||||
private:
|
||||
FileSystemPermissionRequest(FileSystemTaskBase* aTask);
|
||||
|
||||
virtual
|
||||
~FileSystemPermissionRequest();
|
||||
|
||||
nsCString mPermissionType;
|
||||
nsCString mPermissionAccess;
|
||||
nsRefPtr<FileSystemTaskBase> mTask;
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FileSystemPermissionRequest_h
|
86
dom/filesystem/FileSystemRequestParent.cpp
Normal file
86
dom/filesystem/FileSystemRequestParent.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
#include "mozilla/dom/FileSystemRequestParent.h"
|
||||
|
||||
#include "CreateDirectoryTask.h"
|
||||
#include "GetFileOrDirectoryTask.h"
|
||||
|
||||
#include "mozilla/AppProcessChecker.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
FileSystemRequestParent::FileSystemRequestParent()
|
||||
{
|
||||
}
|
||||
|
||||
FileSystemRequestParent::~FileSystemRequestParent()
|
||||
{
|
||||
}
|
||||
|
||||
#define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name) \
|
||||
case FileSystemParams::TFileSystem##name##Params: { \
|
||||
const FileSystem##name##Params& p = aParams; \
|
||||
mFileSystem = FileSystemBase::FromString(p.filesystem()); \
|
||||
task = new name##Task(mFileSystem, p, this); \
|
||||
break; \
|
||||
}
|
||||
|
||||
bool
|
||||
FileSystemRequestParent::Dispatch(ContentParent* aParent,
|
||||
const FileSystemParams& aParams)
|
||||
{
|
||||
MOZ_ASSERT(aParent, "aParent should not be null.");
|
||||
nsRefPtr<FileSystemTaskBase> task;
|
||||
switch (aParams.type()) {
|
||||
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory)
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory)
|
||||
|
||||
default: {
|
||||
NS_RUNTIMEABORT("not reached");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!task || !mFileSystem)) {
|
||||
// Should never reach here.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mFileSystem->IsTesting()) {
|
||||
// Check the content process permission.
|
||||
|
||||
nsCString access;
|
||||
task->GetPermissionAccessType(access);
|
||||
|
||||
nsAutoCString permissionName;
|
||||
permissionName = mFileSystem->GetPermission();
|
||||
permissionName.AppendLiteral("-");
|
||||
permissionName.Append(access);
|
||||
|
||||
if (!AssertAppProcessPermission(aParent, permissionName.get())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
task->Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemRequestParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (!mFileSystem) {
|
||||
return;
|
||||
}
|
||||
mFileSystem->Shutdown();
|
||||
mFileSystem = nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
47
dom/filesystem/FileSystemRequestParent.h
Normal file
47
dom/filesystem/FileSystemRequestParent.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_FileSystemRequestParent_h
|
||||
#define mozilla_dom_FileSystemRequestParent_h
|
||||
|
||||
#include "mozilla/dom/PFileSystemRequestParent.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FileSystemBase;
|
||||
|
||||
class FileSystemRequestParent
|
||||
: public PFileSystemRequestParent
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemRequestParent)
|
||||
public:
|
||||
FileSystemRequestParent();
|
||||
|
||||
virtual
|
||||
~FileSystemRequestParent();
|
||||
|
||||
bool
|
||||
IsRunning()
|
||||
{
|
||||
return state() == PFileSystemRequest::__Start;
|
||||
}
|
||||
|
||||
bool
|
||||
Dispatch(ContentParent* aParent, const FileSystemParams& aParams);
|
||||
|
||||
virtual void
|
||||
ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
|
||||
private:
|
||||
nsRefPtr<FileSystemBase> mFileSystem;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FileSystemRequestParent_h
|
226
dom/filesystem/FileSystemTaskBase.cpp
Normal file
226
dom/filesystem/FileSystemTaskBase.cpp
Normal file
@ -0,0 +1,226 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||
|
||||
#include "nsNetUtil.h" // Stream transport service.
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemRequestParent.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PContent.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsDOMFile.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem)
|
||||
: mErrorValue(NS_OK)
|
||||
, mFileSystem(aFileSystem)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
|
||||
}
|
||||
|
||||
FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem,
|
||||
const FileSystemParams& aParam,
|
||||
FileSystemRequestParent* aParent)
|
||||
: mErrorValue(NS_OK)
|
||||
, mFileSystem(aFileSystem)
|
||||
, mRequestParent(aParent)
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
|
||||
}
|
||||
|
||||
FileSystemTaskBase::~FileSystemTaskBase()
|
||||
{
|
||||
}
|
||||
|
||||
FileSystemBase*
|
||||
FileSystemTaskBase::GetFileSystem() const
|
||||
{
|
||||
return mFileSystem.get();
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemTaskBase::Start()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
|
||||
if (HasError()) {
|
||||
NS_DispatchToMainThread(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (FileSystemUtils::IsParentProcess()) {
|
||||
// Run in parent process.
|
||||
// Start worker thread.
|
||||
nsCOMPtr<nsIEventTarget> target
|
||||
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
NS_ASSERTION(target, "Must have stream transport service.");
|
||||
target->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Run in child process.
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retain a reference so the task object isn't deleted without IPDL's
|
||||
// knowledge. The reference will be released by
|
||||
// mozilla::dom::ContentChild::DeallocPFileSystemRequestChild.
|
||||
NS_ADDREF_THIS();
|
||||
ContentChild::GetSingleton()->SendPFileSystemRequestConstructor(this,
|
||||
GetRequestParams(mFileSystem->ToString()));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemTaskBase::Run()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
// Run worker thread tasks
|
||||
nsresult rv = Work();
|
||||
if (NS_FAILED(rv)) {
|
||||
SetError(rv);
|
||||
}
|
||||
// Dispatch itself to main thread
|
||||
NS_DispatchToMainThread(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Run main thread tasks
|
||||
HandleResult();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemTaskBase::HandleResult()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
return;
|
||||
}
|
||||
if (mRequestParent && mRequestParent->IsRunning()) {
|
||||
unused << mRequestParent->Send__delete__(mRequestParent,
|
||||
GetRequestResult());
|
||||
} else {
|
||||
HandlerCallback();
|
||||
}
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
FileSystemTaskBase::GetRequestResult() const
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
if (HasError()) {
|
||||
return FileSystemErrorResponse(mErrorValue);
|
||||
} else {
|
||||
return GetSuccessRequestResult();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue)
|
||||
{
|
||||
MOZ_ASSERT(!FileSystemUtils::IsParentProcess(),
|
||||
"Only call from child process!");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) {
|
||||
FileSystemErrorResponse r = aValue;
|
||||
mErrorValue = r.error();
|
||||
} else {
|
||||
SetSuccessRequestResult(aValue);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
FileSystemTaskBase::Recv__delete__(const FileSystemResponseValue& aValue)
|
||||
{
|
||||
SetRequestResult(aValue);
|
||||
HandlerCallback();
|
||||
return true;
|
||||
}
|
||||
|
||||
BlobParent*
|
||||
FileSystemTaskBase::GetBlobParent(nsIDOMFile* aFile) const
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFile);
|
||||
|
||||
// Load the lazy dom file data from the parent before sending to the child.
|
||||
nsString mimeType;
|
||||
aFile->GetType(mimeType);
|
||||
uint64_t fileSize;
|
||||
aFile->GetSize(&fileSize);
|
||||
uint64_t lastModifiedDate;
|
||||
aFile->GetMozLastModifiedDate(&lastModifiedDate);
|
||||
|
||||
ContentParent* cp = static_cast<ContentParent*>(mRequestParent->Manager());
|
||||
return cp->GetOrCreateActorForBlob(aFile);
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemTaskBase::SetError(const nsresult& aErrorValue)
|
||||
{
|
||||
uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
|
||||
if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
|
||||
module == NS_ERROR_MODULE_DOM_FILE ||
|
||||
module == NS_ERROR_MODULE_DOM) {
|
||||
mErrorValue = aErrorValue;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aErrorValue) {
|
||||
case NS_OK:
|
||||
mErrorValue = NS_OK;
|
||||
return;
|
||||
|
||||
case NS_ERROR_FILE_INVALID_PATH:
|
||||
case NS_ERROR_FILE_UNRECOGNIZED_PATH:
|
||||
mErrorValue = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
return;
|
||||
|
||||
case NS_ERROR_FILE_DESTINATION_NOT_DIR:
|
||||
mErrorValue = NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR;
|
||||
return;
|
||||
|
||||
case NS_ERROR_FILE_ACCESS_DENIED:
|
||||
case NS_ERROR_FILE_DIR_NOT_EMPTY:
|
||||
mErrorValue = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
return;
|
||||
|
||||
case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST:
|
||||
case NS_ERROR_NOT_AVAILABLE:
|
||||
mErrorValue = NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
|
||||
return;
|
||||
|
||||
case NS_ERROR_FILE_ALREADY_EXISTS:
|
||||
mErrorValue = NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
|
||||
return;
|
||||
|
||||
case NS_ERROR_FILE_NOT_DIRECTORY:
|
||||
mErrorValue = NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
|
||||
return;
|
||||
|
||||
case NS_ERROR_UNEXPECTED:
|
||||
default:
|
||||
mErrorValue = NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
244
dom/filesystem/FileSystemTaskBase.h
Normal file
244
dom/filesystem/FileSystemTaskBase.h
Normal file
@ -0,0 +1,244 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_FileSystemTaskBase_h
|
||||
#define mozilla_dom_FileSystemTaskBase_h
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/FileSystemRequestParent.h"
|
||||
#include "mozilla/dom/PFileSystemRequestChild.h"
|
||||
#include "mozilla/dom/ipc/Blob.h"
|
||||
|
||||
class nsIDOMFile;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FileSystemBase;
|
||||
class FileSystemParams;
|
||||
class Promise;
|
||||
|
||||
/*
|
||||
* The base class to implement a Task class.
|
||||
* The task is used to handle the OOP (out of process) operations.
|
||||
* The file system operations can only be performed in the parent process. When
|
||||
* performing such a parent-process-only operation, a task will delivered the
|
||||
* operation to the parent process if needed.
|
||||
*
|
||||
* The following diagram illustrates the how a API call from the content page
|
||||
* starts a task and gets call back results.
|
||||
*
|
||||
* The left block is the call sequence inside the child process, while the
|
||||
* right block is the call sequence inside the parent process.
|
||||
*
|
||||
* There are two types of API call. One is from the content page of the child
|
||||
* process and we mark the steps as (1) to (8). The other is from the content
|
||||
* page of the parent process and we mark the steps as (1') to (4').
|
||||
*
|
||||
* Page Page
|
||||
* | |
|
||||
* | (1) | (1')
|
||||
* ______|________________ | _____________________|_____________
|
||||
* | | | | | | |
|
||||
* | | Task in | | | Task in | |
|
||||
* | | Child Process | | | Parent Process | |
|
||||
* | V | IPC | V |
|
||||
* [new FileSystemTaskBase()] | | [new FileSystemTaskBase()] |
|
||||
* | | | | | | |
|
||||
* | | (2) | | | (2') |
|
||||
* | V | (3) | | |
|
||||
* | [GetRequestParams]------------->[new FileSystemTaskBase(...)] |
|
||||
* | | | | | |
|
||||
* | | | | | (4) | |
|
||||
* | | | | | V |
|
||||
* | | | | -----------> [Work] |
|
||||
* | | IPC | | |
|
||||
* | | | | (5) | (3') |
|
||||
* | | | | V |
|
||||
* | | | | --------[HandleResult] |
|
||||
* | | | | | | |
|
||||
* | | | | (6) | |
|
||||
* | | (7) | V | |
|
||||
* | [SetRequestResult]<-------------[GetRequestResult] | |
|
||||
* | | | | | (4') |
|
||||
* | | (8) | | | | |
|
||||
* | V | | | V |
|
||||
* |[HandlerCallback] | IPC | [HandlerCallback] |
|
||||
* |_______|_______________| | |_________________________|_________|
|
||||
* | | |
|
||||
* V V
|
||||
* Page Page
|
||||
*
|
||||
* 1. From child process page
|
||||
* Child:
|
||||
* (1) Call FileSystem API from content page with JS. Create a task and run.
|
||||
* The base constructor [FileSystemTaskBase()] of the task should be called.
|
||||
* (2) Forward the task to the parent process through the IPC and call
|
||||
* [GetRequestParams] to prepare the parameters of the IPC.
|
||||
* Parent:
|
||||
* (3) The parent process receives IPC and handle it in
|
||||
* FileystemRequestParent.
|
||||
* Get the IPC parameters and create a task to run the IPC task. The base
|
||||
* constructor [FileSystemTaskBase(aParam, aParent)] of the task should be
|
||||
* called to set the task as an IPC task.
|
||||
* (4) The task operation will be performed in the member function of [Work].
|
||||
* A worker thread will be created to run that function. If error occurs
|
||||
* during the operation, call [SetError] to record the error and then abort.
|
||||
* (5) After finishing the task operation, call [HandleResult] to send the
|
||||
* result back to the child process though the IPC.
|
||||
* (6) Call [GetRequestResult] request result to prepare the parameters of the
|
||||
* IPC. Because the formats of the error result for different task are the
|
||||
* same, FileSystemTaskBase can handle the error message without interfering.
|
||||
* Each task only needs to implement its specific success result preparation
|
||||
* function -[GetSuccessRequestResult].
|
||||
* Child:
|
||||
* (7) The child process receives IPC and calls [SetRequestResult] to get the
|
||||
* task result. Each task needs to implement its specific success result
|
||||
* parsing function [SetSuccessRequestResult] to get the success result.
|
||||
* (8) Call [HandlerCallback] to send the task result to the content page.
|
||||
* 2. From parent process page
|
||||
* We don't need to send the task parameters and result to other process. So
|
||||
* there are less steps, but their functions are the same. The correspondence
|
||||
* between the two types of steps is:
|
||||
* (1') = (1),
|
||||
* (2') = (4),
|
||||
* (3') = (5),
|
||||
* (4') = (8).
|
||||
*/
|
||||
class FileSystemTaskBase
|
||||
: public nsRunnable
|
||||
, public PFileSystemRequestChild
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* Start the task. If the task is running the child process, it will be
|
||||
* forwarded to parent process by IPC, or else, creates a worker thread to
|
||||
* do the task work.
|
||||
*/
|
||||
void
|
||||
Start();
|
||||
|
||||
/*
|
||||
* The error codes are defined in xpcom/base/ErrorList.h and their
|
||||
* corresponding error name and message are defined in dom/base/domerr.msg.
|
||||
*/
|
||||
void
|
||||
SetError(const nsresult& aErrorCode);
|
||||
|
||||
FileSystemBase*
|
||||
GetFileSystem() const;
|
||||
|
||||
/*
|
||||
* Get the type of permission access required to perform this task.
|
||||
*/
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const = 0;
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
protected:
|
||||
/*
|
||||
* To create a task to handle the page content request.
|
||||
*/
|
||||
FileSystemTaskBase(FileSystemBase* aFileSystem);
|
||||
|
||||
/*
|
||||
* To create a parent process task delivered from the child process through
|
||||
* IPC.
|
||||
*/
|
||||
FileSystemTaskBase(FileSystemBase* aFileSystem,
|
||||
const FileSystemParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
|
||||
virtual
|
||||
~FileSystemTaskBase();
|
||||
|
||||
/*
|
||||
* The function to perform task operation. It will be run on the worker
|
||||
* thread of the parent process.
|
||||
* Overrides this function to define the task operation for individual task.
|
||||
*/
|
||||
virtual nsresult
|
||||
Work() = 0;
|
||||
|
||||
/*
|
||||
* After the task is completed, this function will be called to pass the task
|
||||
* result to the content page.
|
||||
* Override this function to handle the call back to the content page.
|
||||
*/
|
||||
virtual void
|
||||
HandlerCallback() = 0;
|
||||
|
||||
/*
|
||||
* Wrap the task parameter to FileSystemParams for sending it through IPC.
|
||||
* It will be called when we need to forward a task from the child process to
|
||||
* the prarent process.
|
||||
* @param filesystem The string representation of the file system.
|
||||
*/
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aFileSystem) const = 0;
|
||||
|
||||
/*
|
||||
* Wrap the task success result to FileSystemResponseValue for sending it
|
||||
* through IPC.
|
||||
* It will be called when the task is completed successfully and we need to
|
||||
* send the task success result back to the child process.
|
||||
*/
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult() const = 0;
|
||||
|
||||
/*
|
||||
* Unwrap the IPC message to get the task success result.
|
||||
* It will be called when the task is completed successfully and an IPC
|
||||
* message is received in the child process and we want to get the task
|
||||
* success result.
|
||||
*/
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue) = 0;
|
||||
|
||||
bool
|
||||
HasError() const { return mErrorValue != NS_OK; }
|
||||
|
||||
// Overrides PFileSystemRequestChild
|
||||
virtual bool
|
||||
Recv__delete__(const FileSystemResponseValue& value) MOZ_OVERRIDE;
|
||||
|
||||
BlobParent*
|
||||
GetBlobParent(nsIDOMFile* aFile) const;
|
||||
|
||||
nsresult mErrorValue;
|
||||
|
||||
nsRefPtr<FileSystemBase> mFileSystem;
|
||||
nsRefPtr<FileSystemRequestParent> mRequestParent;
|
||||
private:
|
||||
/*
|
||||
* After finishing the task operation, handle the task result.
|
||||
* If it is an IPC task, send back the IPC result. Or else, send the result
|
||||
* to the content page.
|
||||
*/
|
||||
void
|
||||
HandleResult();
|
||||
|
||||
/*
|
||||
* Wrap the task result to FileSystemResponseValue for sending it through IPC.
|
||||
* It will be called when the task is completed and we need to
|
||||
* send the task result back to the child process.
|
||||
*/
|
||||
FileSystemResponseValue
|
||||
GetRequestResult() const;
|
||||
|
||||
/*
|
||||
* Unwrap the IPC message to get the task result.
|
||||
* It will be called when the task is completed and an IPC message is received
|
||||
* in the child process and we want to get the task result.
|
||||
*/
|
||||
void
|
||||
SetRequestResult(const FileSystemResponseValue& aValue);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FileSystemTaskBase_h
|
76
dom/filesystem/FileSystemUtils.cpp
Normal file
76
dom/filesystem/FileSystemUtils.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// static
|
||||
void
|
||||
FileSystemUtils::LocalPathToNormalizedPath(const nsAString& aLocal,
|
||||
nsAString& aNorm)
|
||||
{
|
||||
nsString result;
|
||||
result = aLocal;
|
||||
#if defined(XP_WIN)
|
||||
char16_t* cur = result.BeginWriting();
|
||||
char16_t* end = result.EndWriting();
|
||||
for (; cur < end; ++cur) {
|
||||
if (char16_t('\\') == *cur)
|
||||
*cur = char16_t('/');
|
||||
}
|
||||
#endif
|
||||
aNorm = result;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
FileSystemUtils::NormalizedPathToLocalPath(const nsAString& aNorm,
|
||||
nsAString& aLocal)
|
||||
{
|
||||
nsString result;
|
||||
result = aNorm;
|
||||
#if defined(XP_WIN)
|
||||
char16_t* cur = result.BeginWriting();
|
||||
char16_t* end = result.EndWriting();
|
||||
for (; cur < end; ++cur) {
|
||||
if (char16_t('/') == *cur)
|
||||
*cur = char16_t('\\');
|
||||
}
|
||||
#endif
|
||||
aLocal = result;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
FileSystemUtils::IsDescendantPath(const nsAString& aPath,
|
||||
const nsAString& aDescendantPath)
|
||||
{
|
||||
// The descendant path should begin with its ancestor path.
|
||||
nsAutoString prefix;
|
||||
prefix = aPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR);
|
||||
|
||||
// Check the sub-directory path to see if it has the parent path as prefix.
|
||||
if (aDescendantPath.Length() < prefix.Length() ||
|
||||
!StringBeginsWith(aDescendantPath, prefix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
FileSystemUtils::IsParentProcess()
|
||||
{
|
||||
return XRE_GetProcessType() == GeckoProcessType_Default;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
53
dom/filesystem/FileSystemUtils.h
Normal file
53
dom/filesystem/FileSystemUtils.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_FileSystemUtils_h
|
||||
#define mozilla_dom_FileSystemUtils_h
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#define FILESYSTEM_DOM_PATH_SEPARATOR "/"
|
||||
|
||||
/*
|
||||
* This class is for error handling.
|
||||
* All methods in this class are static.
|
||||
*/
|
||||
class FileSystemUtils
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* Convert the path separator to "/".
|
||||
*/
|
||||
static void
|
||||
LocalPathToNormalizedPath(const nsAString& aLocal, nsAString& aNorm);
|
||||
|
||||
/*
|
||||
* Convert the normalized path separator "/" to the system dependent path
|
||||
* separator, which is "/" on Mac and Linux, and "\" on Windows.
|
||||
*/
|
||||
static void
|
||||
NormalizedPathToLocalPath(const nsAString& aNorm, nsAString& aLocal);
|
||||
|
||||
/*
|
||||
* Return true if aDescendantPath is a descendant of aPath. Both aPath and
|
||||
* aDescendantPath are absolute DOM path.
|
||||
*/
|
||||
static bool
|
||||
IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath);
|
||||
|
||||
static bool
|
||||
IsParentProcess();
|
||||
|
||||
static const char16_t kSeparatorChar = char16_t('/');
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FileSystemUtils_h
|
223
dom/filesystem/GetFileOrDirectoryTask.cpp
Normal file
223
dom/filesystem/GetFileOrDirectoryTask.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "GetFileOrDirectoryTask.h"
|
||||
|
||||
#include "js/Value.h"
|
||||
#include "mozilla/dom/Directory.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
GetFileOrDirectoryTask::GetFileOrDirectoryTask(
|
||||
FileSystemBase* aFileSystem,
|
||||
const nsAString& aTargetPath,
|
||||
bool aDirectoryOnly)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetRealPath(aTargetPath)
|
||||
, mIsDirectory(aDirectoryOnly)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
}
|
||||
mPromise = new Promise(globalObject);
|
||||
}
|
||||
|
||||
GetFileOrDirectoryTask::GetFileOrDirectoryTask(
|
||||
FileSystemBase* aFileSystem,
|
||||
const FileSystemGetFileOrDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent)
|
||||
: FileSystemTaskBase(aFileSystem, aParam, aParent)
|
||||
, mIsDirectory(false)
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
mTargetRealPath = aParam.realPath();
|
||||
}
|
||||
|
||||
GetFileOrDirectoryTask::~GetFileOrDirectoryTask()
|
||||
{
|
||||
MOZ_ASSERT(!mPromise || NS_IsMainThread(),
|
||||
"mPromise should be released on main thread!");
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetFileOrDirectoryTask::GetPromise()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return nsRefPtr<Promise>(mPromise).forget();
|
||||
}
|
||||
|
||||
FileSystemParams
|
||||
GetFileOrDirectoryTask::GetRequestParams(const nsString& aFileSystem) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return FileSystemGetFileOrDirectoryParams(aFileSystem, mTargetRealPath);
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
GetFileOrDirectoryTask::GetSuccessRequestResult() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
if (mIsDirectory) {
|
||||
return FileSystemDirectoryResponse(mTargetRealPath);
|
||||
}
|
||||
BlobParent* actor = GetBlobParent(mTargetFile);
|
||||
if (!actor) {
|
||||
return FileSystemErrorResponse(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR);
|
||||
}
|
||||
FileSystemFileResponse response;
|
||||
response.blobParent() = actor;
|
||||
return response;
|
||||
}
|
||||
|
||||
void
|
||||
GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
switch (aValue.type()) {
|
||||
case FileSystemResponseValue::TFileSystemFileResponse: {
|
||||
FileSystemFileResponse r = aValue;
|
||||
BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
|
||||
nsCOMPtr<nsIDOMBlob> blob = actor->GetBlob();
|
||||
mTargetFile = do_QueryInterface(blob);
|
||||
mIsDirectory = false;
|
||||
break;
|
||||
}
|
||||
case FileSystemResponseValue::TFileSystemDirectoryResponse: {
|
||||
FileSystemDirectoryResponse r = aValue;
|
||||
mTargetRealPath = r.realPath();
|
||||
mIsDirectory = true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
NS_RUNTIMEABORT("not reached");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetFileOrDirectoryTask::Work()
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
|
||||
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Whether we want to get the root directory.
|
||||
bool getRoot = mTargetRealPath.IsEmpty();
|
||||
|
||||
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
|
||||
if (!file) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
|
||||
bool exists;
|
||||
nsresult rv = file->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
if (!getRoot) {
|
||||
return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
|
||||
}
|
||||
|
||||
// If the root directory doesn't exit, create it.
|
||||
rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// Get isDirectory.
|
||||
rv = file->IsDirectory(&mIsDirectory);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mIsDirectory) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Check if the root is a directory.
|
||||
if (getRoot) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
|
||||
}
|
||||
|
||||
bool isFile;
|
||||
// Get isFile
|
||||
rv = file->IsFile(&isFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!isFile) {
|
||||
// Neither directory or file.
|
||||
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
|
||||
}
|
||||
|
||||
if (!mFileSystem->IsSafeFile(file)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
mTargetFile = new nsDOMFileFile(file);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GetFileOrDirectoryTask::HandlerCallback()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasError()) {
|
||||
nsRefPtr<DOMError> domError = new DOMError(mFileSystem->GetWindow(),
|
||||
mErrorValue);
|
||||
mPromise->MaybeReject(domError);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsDirectory) {
|
||||
nsRefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
|
||||
mPromise->MaybeResolve(dir);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
mPromise->MaybeResolve(mTargetFile);
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GetFileOrDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral("read");
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
63
dom/filesystem/GetFileOrDirectoryTask.h
Normal file
63
dom/filesystem/GetFileOrDirectoryTask.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_GetFileOrDirectory_h
|
||||
#define mozilla_dom_GetFileOrDirectory_h
|
||||
|
||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class GetFileOrDirectoryTask MOZ_FINAL
|
||||
: public FileSystemTaskBase
|
||||
{
|
||||
public:
|
||||
// If aDirectoryOnly is set, we should ensure that the target is a directory.
|
||||
GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aTargetPath,
|
||||
bool aDirectoryOnly);
|
||||
GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const FileSystemGetFileOrDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
|
||||
virtual
|
||||
~GetFileOrDirectoryTask();
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetPromise();
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const MOZ_OVERRIDE;
|
||||
protected:
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aFileSystem) const MOZ_OVERRIDE;
|
||||
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult() const MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult
|
||||
Work() MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
HandlerCallback() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
nsString mTargetRealPath;
|
||||
// Whether we get a directory.
|
||||
bool mIsDirectory;
|
||||
nsCOMPtr<nsIDOMFile> mTargetFile;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_GetFileOrDirectory_h
|
44
dom/filesystem/PFileSystemRequest.ipdl
Normal file
44
dom/filesystem/PFileSystemRequest.ipdl
Normal file
@ -0,0 +1,44 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
include protocol PBlob;
|
||||
include protocol PContent;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct FileSystemFileResponse
|
||||
{
|
||||
PBlob blob;
|
||||
};
|
||||
|
||||
struct FileSystemDirectoryResponse
|
||||
{
|
||||
nsString realPath;
|
||||
};
|
||||
|
||||
struct FileSystemErrorResponse
|
||||
{
|
||||
nsresult error;
|
||||
};
|
||||
|
||||
union FileSystemResponseValue
|
||||
{
|
||||
FileSystemDirectoryResponse;
|
||||
FileSystemFileResponse;
|
||||
FileSystemErrorResponse;
|
||||
};
|
||||
|
||||
sync protocol PFileSystemRequest
|
||||
{
|
||||
manager PContent;
|
||||
|
||||
child:
|
||||
__delete__(FileSystemResponseValue response);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
39
dom/filesystem/moz.build
Normal file
39
dom/filesystem/moz.build
Normal file
@ -0,0 +1,39 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'DeviceStorageFileSystem.h',
|
||||
'Directory.h',
|
||||
'FileSystemBase.h',
|
||||
'FileSystemRequestParent.h',
|
||||
'FileSystemTaskBase.h',
|
||||
'FileSystemUtils.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'CreateDirectoryTask.cpp',
|
||||
'DeviceStorageFileSystem.cpp',
|
||||
'Directory.cpp',
|
||||
'FileSystemBase.cpp',
|
||||
'FileSystemPermissionRequest.cpp',
|
||||
'FileSystemRequestParent.cpp',
|
||||
'FileSystemTaskBase.cpp',
|
||||
'FileSystemUtils.cpp',
|
||||
'GetFileOrDirectoryTask.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'gklayout'
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PFileSystemRequest.ipdl',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
]
|
||||
|
@ -5,6 +5,7 @@
|
||||
<script type="application/javascript;version=1.7">
|
||||
let input = document.getElementById('test-input');
|
||||
input.focus();
|
||||
dump('file_test_app.html was loaded.');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,5 +1,6 @@
|
||||
function inputmethod_setup(callback) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestCompleteLog();
|
||||
let appInfo = SpecialPowers.Cc['@mozilla.org/xre/app-info;1']
|
||||
.getService(SpecialPowers.Ci.nsIXULAppInfo);
|
||||
if (appInfo.name != 'B2G') {
|
||||
|
@ -23,6 +23,7 @@ inputmethod_setup(function() {
|
||||
function appFrameScript() {
|
||||
let input = content.document.getElementById('test-input');
|
||||
input.oninput = function() {
|
||||
dump('oninput was called in file_test_app.html.');
|
||||
sendAsyncMessage('test:InputMethod:oninput', {});
|
||||
};
|
||||
|
||||
@ -36,7 +37,7 @@ function appFrameScript() {
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
var timeoutId = null;
|
||||
let timeoutId = null;
|
||||
|
||||
// Create an app frame to recieve keyboard inputs.
|
||||
let app = document.createElement('iframe');
|
||||
@ -47,12 +48,8 @@ function runTest() {
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(app);
|
||||
mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
|
||||
mm.addMessageListener("test:InputMethod:oninput", function() {
|
||||
if (!timeoutId) {
|
||||
return;
|
||||
}
|
||||
ok(true, 'Keyboard input was received.');
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
inputmethod_cleanup();
|
||||
});
|
||||
});
|
||||
@ -88,10 +85,6 @@ function runTest() {
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(true, 'setInputMethodActive succeeded.');
|
||||
timeoutId = setTimeout(function() {
|
||||
inputmethod_cleanup();
|
||||
ok(false, 'Failed to generate keyboard input.');
|
||||
}, 20000);
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
@ -103,6 +96,10 @@ function runTest() {
|
||||
SpecialPowers.DOMWindowUtils.focus(app);
|
||||
setTimeout(function() {
|
||||
keyboard.src = imeUrl;
|
||||
timeoutId = setTimeout(function() {
|
||||
inputmethod_cleanup();
|
||||
ok(false, 'Failed to generate keyboard input.');
|
||||
}, 20000);
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
@ -117,6 +117,8 @@
|
||||
#include "mozilla/dom/indexedDB/PIndexedDBChild.h"
|
||||
#include "mozilla/dom/mobilemessage/SmsChild.h"
|
||||
#include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
|
||||
#include "mozilla/dom/PFileSystemRequestChild.h"
|
||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||
#include "mozilla/dom/bluetooth/PBluetoothChild.h"
|
||||
#include "mozilla/dom/PFMRadioChild.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
@ -1038,6 +1040,24 @@ ContentChild::DeallocPDeviceStorageRequestChild(PDeviceStorageRequestChild* aDev
|
||||
return true;
|
||||
}
|
||||
|
||||
PFileSystemRequestChild*
|
||||
ContentChild::AllocPFileSystemRequestChild(const FileSystemParams& aParams)
|
||||
{
|
||||
NS_NOTREACHED("Should never get here!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::DeallocPFileSystemRequestChild(PFileSystemRequestChild* aFileSystem)
|
||||
{
|
||||
mozilla::dom::FileSystemTaskBase* child =
|
||||
static_cast<mozilla::dom::FileSystemTaskBase*>(aFileSystem);
|
||||
// The reference is increased in FileSystemTaskBase::Start of
|
||||
// FileSystemTaskBase.cpp. We should decrease it after IPC.
|
||||
NS_RELEASE(child);
|
||||
return true;
|
||||
}
|
||||
|
||||
PNeckoChild*
|
||||
ContentChild::AllocPNeckoChild()
|
||||
{
|
||||
|
@ -99,6 +99,9 @@ public:
|
||||
virtual PDeviceStorageRequestChild* AllocPDeviceStorageRequestChild(const DeviceStorageParams&);
|
||||
virtual bool DeallocPDeviceStorageRequestChild(PDeviceStorageRequestChild*);
|
||||
|
||||
virtual PFileSystemRequestChild* AllocPFileSystemRequestChild(const FileSystemParams&);
|
||||
virtual bool DeallocPFileSystemRequestChild(PFileSystemRequestChild*);
|
||||
|
||||
virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams);
|
||||
virtual bool DeallocPBlobChild(PBlobChild*);
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "mozilla/dom/bluetooth/PBluetoothParent.h"
|
||||
#include "mozilla/dom/PFMRadioParent.h"
|
||||
#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
|
||||
#include "mozilla/dom/FileSystemRequestParent.h"
|
||||
#include "mozilla/dom/GeolocationBinding.h"
|
||||
#include "mozilla/dom/telephony/TelephonyParent.h"
|
||||
#include "mozilla/dom/time/DateCacheCleaner.h"
|
||||
@ -2228,6 +2229,24 @@ ContentParent::DeallocPDeviceStorageRequestParent(PDeviceStorageRequestParent* d
|
||||
return true;
|
||||
}
|
||||
|
||||
PFileSystemRequestParent*
|
||||
ContentParent::AllocPFileSystemRequestParent(const FileSystemParams& aParams)
|
||||
{
|
||||
nsRefPtr<FileSystemRequestParent> result = new FileSystemRequestParent();
|
||||
if (!result->Dispatch(this, aParams)) {
|
||||
return nullptr;
|
||||
}
|
||||
return result.forget().get();
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::DeallocPFileSystemRequestParent(PFileSystemRequestParent* doomed)
|
||||
{
|
||||
FileSystemRequestParent* parent = static_cast<FileSystemRequestParent*>(doomed);
|
||||
NS_RELEASE(parent);
|
||||
return true;
|
||||
}
|
||||
|
||||
PBlobParent*
|
||||
ContentParent::AllocPBlobParent(const BlobConstructorParams& aParams)
|
||||
{
|
||||
|
@ -341,6 +341,12 @@ private:
|
||||
AllocPDeviceStorageRequestParent(const DeviceStorageParams&) MOZ_OVERRIDE;
|
||||
virtual bool DeallocPDeviceStorageRequestParent(PDeviceStorageRequestParent*) MOZ_OVERRIDE;
|
||||
|
||||
virtual PFileSystemRequestParent*
|
||||
AllocPFileSystemRequestParent(const FileSystemParams&) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
DeallocPFileSystemRequestParent(PFileSystemRequestParent*) MOZ_OVERRIDE;
|
||||
|
||||
virtual PBlobParent* AllocPBlobParent(const BlobConstructorParams& aParams) MOZ_OVERRIDE;
|
||||
virtual bool DeallocPBlobParent(PBlobParent*) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -14,6 +14,7 @@ include protocol PCrashReporter;
|
||||
include protocol PExternalHelperApp;
|
||||
include protocol PDeviceStorageRequest;
|
||||
include protocol PFMRadio;
|
||||
include protocol PFileSystemRequest;
|
||||
include protocol PHal;
|
||||
include protocol PImageBridge;
|
||||
include protocol PIndexedDB;
|
||||
@ -193,6 +194,24 @@ union FMRadioRequestParams
|
||||
FMRadioRequestCancelSeekParams;
|
||||
};
|
||||
|
||||
struct FileSystemCreateDirectoryParams
|
||||
{
|
||||
nsString filesystem;
|
||||
nsString realPath;
|
||||
};
|
||||
|
||||
struct FileSystemGetFileOrDirectoryParams
|
||||
{
|
||||
nsString filesystem;
|
||||
nsString realPath;
|
||||
};
|
||||
|
||||
union FileSystemParams
|
||||
{
|
||||
FileSystemCreateDirectoryParams;
|
||||
FileSystemGetFileOrDirectoryParams;
|
||||
};
|
||||
|
||||
union PrefValue {
|
||||
nsCString;
|
||||
int32_t;
|
||||
@ -222,6 +241,7 @@ intr protocol PContent
|
||||
manages PBrowser;
|
||||
manages PCrashReporter;
|
||||
manages PDeviceStorageRequest;
|
||||
manages PFileSystemRequest;
|
||||
manages PExternalHelperApp;
|
||||
manages PFMRadio;
|
||||
manages PHal;
|
||||
@ -371,6 +391,8 @@ parent:
|
||||
|
||||
PDeviceStorageRequest(DeviceStorageParams params);
|
||||
|
||||
PFileSystemRequest(FileSystemParams params);
|
||||
|
||||
sync PCrashReporter(NativeThreadId tid, uint32_t processType);
|
||||
|
||||
sync GetRandomValues(uint32_t length)
|
||||
|
@ -99,6 +99,7 @@ LOCAL_INCLUDES += [
|
||||
'/dom/bluetooth/ipc',
|
||||
'/dom/devicestorage',
|
||||
'/dom/events',
|
||||
'/dom/filesystem',
|
||||
'/dom/fmradio/ipc',
|
||||
'/dom/indexedDB',
|
||||
'/dom/indexedDB/ipc',
|
||||
|
@ -50,6 +50,7 @@ PARALLEL_DIRS += [
|
||||
'encoding',
|
||||
'events',
|
||||
'file',
|
||||
'filesystem',
|
||||
'fmradio',
|
||||
'asmjscache',
|
||||
'media',
|
||||
|
@ -247,6 +247,22 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Accept objects that inherit from nsISupports but not nsWrapperCache (e.g.
|
||||
// nsIDOMFile).
|
||||
template <class T>
|
||||
typename EnableIf<!IsBaseOf<nsWrapperCache, T>::value &&
|
||||
IsBaseOf<nsISupports, T>::value, bool>::Type
|
||||
ArgumentToJSValue(T& aArgument,
|
||||
JSContext* aCx,
|
||||
JSObject* aScope,
|
||||
JS::MutableHandle<JS::Value> aValue)
|
||||
{
|
||||
JS::Rooted<JSObject*> scope(aCx, aScope);
|
||||
|
||||
nsresult rv = nsContentUtils::WrapNative(aCx, scope, &aArgument, aValue);
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
template <template <typename> class SmartPtr, typename T>
|
||||
bool
|
||||
ArgumentToJSValue(const SmartPtr<T>& aArgument,
|
||||
|
@ -709,6 +709,8 @@ static const dom::ConstantSpec gWinProperties[] =
|
||||
INT_CONSTANT(ERROR_FILE_NOT_FOUND),
|
||||
INT_CONSTANT(ERROR_NO_MORE_FILES),
|
||||
INT_CONSTANT(ERROR_PATH_NOT_FOUND),
|
||||
INT_CONSTANT(ERROR_BAD_ARGUMENTS),
|
||||
INT_CONSTANT(ERROR_NOT_SUPPORTED),
|
||||
|
||||
PROP_END
|
||||
};
|
||||
|
@ -55,4 +55,7 @@ interface DeviceStorage : EventTarget {
|
||||
// Determines if this storage area is the one which will be used by default
|
||||
// for storing new files.
|
||||
readonly attribute boolean default;
|
||||
|
||||
[NewObject]
|
||||
Promise getRoot();
|
||||
};
|
||||
|
49
dom/webidl/Directory.webidl
Normal file
49
dom/webidl/Directory.webidl
Normal file
@ -0,0 +1,49 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
interface File;
|
||||
|
||||
/*
|
||||
* All functions on Directory that accept DOMString arguments for file or
|
||||
* directory names only allow relative path to current directory itself. The
|
||||
* path should be a descendent path like "path/to/file.txt" and not contain a
|
||||
* segment of ".." or ".". So the paths aren't allowed to walk up the directory
|
||||
* tree. For example, paths like "../foo", "..", "/foo/bar" or "foo/../bar" are
|
||||
* not allowed.
|
||||
*/
|
||||
[NoInterfaceObject]
|
||||
interface Directory {
|
||||
/*
|
||||
* The leaf name of the directory.
|
||||
*/
|
||||
readonly attribute DOMString name;
|
||||
|
||||
/*
|
||||
* Creates a descendent directory. This method will create any intermediate
|
||||
* directories specified by the path segments.
|
||||
*
|
||||
* @param path The relative path of the new directory to current directory.
|
||||
* If path exists, createDirectory must fail.
|
||||
* @return If succeeds, the promise is resolved with the new created
|
||||
* Directory object. Otherwise, rejected with a DOM error.
|
||||
*/
|
||||
[NewObject]
|
||||
// Promise<Directory>
|
||||
Promise createDirectory(DOMString path);
|
||||
|
||||
/*
|
||||
* Gets a descendent file or directory with the given path.
|
||||
*
|
||||
* @param path The descendent entry's relative path to current directory.
|
||||
* @return If the path exists and no error occurs, the promise is resolved
|
||||
* with a File or Directory object, depending on the entry's type. Otherwise,
|
||||
* rejected with a DOM error.
|
||||
*/
|
||||
[NewObject]
|
||||
// Promise<(File or Directory)>
|
||||
Promise get(DOMString path);
|
||||
};
|
||||
|
21
dom/webidl/NativeOSFileInternals.webidl
Normal file
21
dom/webidl/NativeOSFileInternals.webidl
Normal file
@ -0,0 +1,21 @@
|
||||
/* 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 obtaone at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Options for nsINativeOSFileInternals::Read
|
||||
*/
|
||||
dictionary NativeOSFileReadOptions
|
||||
{
|
||||
/**
|
||||
* If specified, convert the raw bytes to a String
|
||||
* with the specified encoding. Otherwise, return
|
||||
* the raw bytes as a TypedArray.
|
||||
*/
|
||||
DOMString? encoding;
|
||||
|
||||
/**
|
||||
* If specified, limit the number of bytes to read.
|
||||
*/
|
||||
unsigned long long? bytes;
|
||||
};
|
@ -73,6 +73,7 @@ WEBIDL_FILES = [
|
||||
'DesktopNotification.webidl',
|
||||
'DeviceMotionEvent.webidl',
|
||||
'DeviceStorage.webidl',
|
||||
'Directory.webidl',
|
||||
'Document.webidl',
|
||||
'DocumentFragment.webidl',
|
||||
'DocumentType.webidl',
|
||||
@ -244,6 +245,7 @@ WEBIDL_FILES = [
|
||||
'MozWakeLock.webidl',
|
||||
'MutationEvent.webidl',
|
||||
'MutationObserver.webidl',
|
||||
'NativeOSFileInternals.webidl',
|
||||
'NetDashboard.webidl',
|
||||
'NetworkOptions.webidl',
|
||||
'Node.webidl',
|
||||
|
@ -249,6 +249,8 @@ public:
|
||||
|
||||
bool success = false;
|
||||
if (dstLocked) {
|
||||
if (DiscardingEnabled())
|
||||
dstFrame->SetDiscardable();
|
||||
success = NS_SUCCEEDED(dstFrame->UnlockImageData());
|
||||
|
||||
dstLocked = false;
|
||||
@ -2563,7 +2565,7 @@ RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
|
||||
gfxContext *aContext,
|
||||
GraphicsFilter aFilter,
|
||||
@ -2579,8 +2581,9 @@ RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
|
||||
imageSpaceToUserSpace.Invert();
|
||||
gfx::Size scale = ToSize(imageSpaceToUserSpace.ScaleFactors(true));
|
||||
nsIntRect subimage = aSubimage;
|
||||
nsRefPtr<gfxASurface> surf;
|
||||
|
||||
if (CanScale(aFilter, scale, aFlags)) {
|
||||
if (CanScale(aFilter, scale, aFlags) && !frame->IsSinglePixel()) {
|
||||
// If scale factor is still the same that we scaled for and
|
||||
// ScaleWorker isn't still working, then we can use pre-downscaled frame.
|
||||
// If scale factor has changed, order new request.
|
||||
@ -2589,21 +2592,35 @@ RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
|
||||
// pre-downscaled frame only for the latest requested scale.
|
||||
// The solution is to cache more than one scaled image frame
|
||||
// for each RasterImage.
|
||||
bool needScaleReq;
|
||||
if (mScaleResult.status == SCALE_DONE && mScaleResult.scale == scale) {
|
||||
frame = mScaleResult.frame;
|
||||
userSpaceToImageSpace.Multiply(gfxMatrix().Scale(scale.width, scale.height));
|
||||
// Grab and hold the surface to make sure the OS didn't destroy it
|
||||
mScaleResult.frame->GetSurface(getter_AddRefs(surf));
|
||||
needScaleReq = !surf;
|
||||
if (surf) {
|
||||
frame = mScaleResult.frame;
|
||||
userSpaceToImageSpace.Multiply(gfxMatrix().Scale(scale.width,
|
||||
scale.height));
|
||||
|
||||
// Since we're switching to a scaled image, we need to transform the
|
||||
// area of the subimage to draw accordingly, since imgFrame::Draw()
|
||||
// doesn't know about scaled frames.
|
||||
subimage.ScaleRoundOut(scale.width, scale.height);
|
||||
// Since we're switching to a scaled image, we need to transform the
|
||||
// area of the subimage to draw accordingly, since imgFrame::Draw()
|
||||
// doesn't know about scaled frames.
|
||||
subimage.ScaleRoundOut(scale.width, scale.height);
|
||||
}
|
||||
} else {
|
||||
needScaleReq = !(mScaleResult.status == SCALE_PENDING &&
|
||||
mScaleResult.scale == scale);
|
||||
}
|
||||
|
||||
// If we're not waiting for exactly this result, and there's only one
|
||||
// instance of this image on this page, ask for a scale.
|
||||
else if (!(mScaleResult.status == SCALE_PENDING && mScaleResult.scale == scale) &&
|
||||
mLockCount == 1) {
|
||||
// If we have an oustanding request, signal it to stop (if it can).
|
||||
if (needScaleReq && mLockCount == 1) {
|
||||
if (NS_FAILED(frame->LockImageData())) {
|
||||
frame->UnlockImageData();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have an outstanding request, signal it to stop (if it can).
|
||||
if (mScaleRequest) {
|
||||
mScaleRequest->stopped = true;
|
||||
}
|
||||
@ -2617,6 +2634,7 @@ RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
|
||||
|
||||
sScaleWorkerThread->Dispatch(runner, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
frame->UnlockImageData();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2625,8 +2643,8 @@ RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
|
||||
mSize.height - framerect.YMost(),
|
||||
framerect.x);
|
||||
|
||||
frame->Draw(aContext, aFilter, userSpaceToImageSpace, aFill, padding, subimage,
|
||||
aFlags);
|
||||
return frame->Draw(aContext, aFilter, userSpaceToImageSpace,
|
||||
aFill, padding, subimage, aFlags);
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
@ -2718,19 +2736,16 @@ RasterImage::Draw(gfxContext *aContext,
|
||||
return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
|
||||
}
|
||||
|
||||
nsRefPtr<gfxASurface> surf;
|
||||
if (!frame->IsSinglePixel()) {
|
||||
frame->GetSurface(getter_AddRefs(surf));
|
||||
if (!surf) {
|
||||
// The OS threw out some or all of our buffer. Start decoding again.
|
||||
ForceDiscard();
|
||||
WantDecodedFrames();
|
||||
return NS_OK;
|
||||
}
|
||||
bool drawn = DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter,
|
||||
aUserSpaceToImageSpace, aFill,
|
||||
aSubimage, aFlags);
|
||||
if (!drawn) {
|
||||
// The OS threw out some or all of our buffer. Start decoding again.
|
||||
ForceDiscard();
|
||||
WantDecodedFrames();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter, aUserSpaceToImageSpace, aFill, aSubimage, aFlags);
|
||||
|
||||
if (mDecoded && !mDrawStartTime.IsNull()) {
|
||||
TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, int32_t(drawLatency.ToMicroseconds()));
|
||||
|
@ -545,7 +545,7 @@ private:
|
||||
nsresult FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done,
|
||||
DecodeRequest* request = nullptr);
|
||||
|
||||
void DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
|
||||
bool DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
|
||||
gfxContext *aContext,
|
||||
GraphicsFilter aFilter,
|
||||
const gfxMatrix &aUserSpaceToImageSpace,
|
||||
|
@ -346,8 +346,33 @@ nsresult imgFrame::Optimize()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
gfxImageFormat optFormat =
|
||||
gfxPlatform::GetPlatform()->
|
||||
OptimalFormatForContent(gfxASurface::ContentFromFormat(mFormat));
|
||||
|
||||
if (optFormat == gfxImageFormat::RGB16_565) {
|
||||
RefPtr<VolatileBuffer> buf =
|
||||
LockedImageSurface::AllocateBuffer(mSize, optFormat);
|
||||
if (!buf)
|
||||
return NS_OK;
|
||||
|
||||
nsRefPtr<gfxImageSurface> surf =
|
||||
LockedImageSurface::CreateSurface(buf, mSize, optFormat);
|
||||
|
||||
gfxContext ctx(surf);
|
||||
ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx.SetSource(mImageSurface);
|
||||
ctx.Paint();
|
||||
|
||||
mImageSurface = surf;
|
||||
mVBuf = buf;
|
||||
mFormat = optFormat;
|
||||
}
|
||||
#else
|
||||
if (mOptSurface == nullptr)
|
||||
mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat);
|
||||
#endif
|
||||
|
||||
if (mOptSurface) {
|
||||
mVBuf = nullptr;
|
||||
@ -394,12 +419,13 @@ imgFrame::SurfaceForDrawing(bool aDoPadding,
|
||||
gfxRect& aFill,
|
||||
gfxRect& aSubimage,
|
||||
gfxRect& aSourceRect,
|
||||
gfxRect& aImageRect)
|
||||
gfxRect& aImageRect,
|
||||
gfxASurface* aSurface)
|
||||
{
|
||||
IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height()));
|
||||
if (!aDoPadding && !aDoPartialDecode) {
|
||||
NS_ASSERTION(!mSinglePixel, "This should already have been handled");
|
||||
return SurfaceWithFormat(new gfxSurfaceDrawable(ThebesSurface(), ThebesIntSize(size)), mFormat);
|
||||
return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, ThebesIntSize(size)), mFormat);
|
||||
}
|
||||
|
||||
gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height);
|
||||
@ -420,7 +446,7 @@ imgFrame::SurfaceForDrawing(bool aDoPadding,
|
||||
if (mSinglePixel) {
|
||||
tmpCtx.SetDeviceColor(mSinglePixelColor);
|
||||
} else {
|
||||
tmpCtx.SetSource(ThebesSurface(), gfxPoint(aPadding.left, aPadding.top));
|
||||
tmpCtx.SetSource(aSurface, gfxPoint(aPadding.left, aPadding.top));
|
||||
}
|
||||
tmpCtx.Rectangle(available);
|
||||
tmpCtx.Fill();
|
||||
@ -442,12 +468,11 @@ imgFrame::SurfaceForDrawing(bool aDoPadding,
|
||||
aImageRect = gfxRect(0, 0, mSize.width, mSize.height);
|
||||
|
||||
gfxIntSize availableSize(mDecoded.width, mDecoded.height);
|
||||
return SurfaceWithFormat(new gfxSurfaceDrawable(ThebesSurface(),
|
||||
availableSize),
|
||||
return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, availableSize),
|
||||
mFormat);
|
||||
}
|
||||
|
||||
void imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
|
||||
bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
|
||||
const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
|
||||
const nsIntMargin &aPadding, const nsIntRect &aSubimage,
|
||||
uint32_t aImageFlags)
|
||||
@ -462,7 +487,7 @@ void imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
|
||||
|
||||
if (mSinglePixel && !doPadding && !doPartialDecode) {
|
||||
DoSingleColorFastPath(aContext, mSinglePixelColor, aFill);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
|
||||
@ -475,12 +500,19 @@ void imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
|
||||
NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
|
||||
"We must be allowed to sample *some* source pixels!");
|
||||
|
||||
nsRefPtr<gfxASurface> surf;
|
||||
if (!mSinglePixel) {
|
||||
surf = ThebesSurface();
|
||||
if (!surf)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool doTile = !imageRect.Contains(sourceRect) &&
|
||||
!(aImageFlags & imgIContainer::FLAG_CLAMP);
|
||||
SurfaceWithFormat surfaceResult =
|
||||
SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
|
||||
userSpaceToImageSpace, fill, subimage, sourceRect,
|
||||
imageRect);
|
||||
imageRect, surf);
|
||||
|
||||
if (surfaceResult.IsValid()) {
|
||||
gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
|
||||
@ -488,6 +520,7 @@ void imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
|
||||
subimage, sourceRect, imageRect, fill,
|
||||
surfaceResult.mFormat, aFilter, aImageFlags);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This can be called from any thread, but not simultaneously.
|
||||
@ -632,25 +665,37 @@ nsresult imgFrame::LockImageData()
|
||||
mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
|
||||
if (!mImageSurface || mImageSurface->CairoStatus())
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
} else if (mOptSurface || mSinglePixel) {
|
||||
}
|
||||
if (mOptSurface || mSinglePixel || mFormat == gfxImageFormat::RGB16_565) {
|
||||
gfxImageFormat format = mFormat;
|
||||
if (mFormat == gfxImageFormat::RGB16_565)
|
||||
format = gfxImageFormat::ARGB32;
|
||||
|
||||
// Recover the pixels
|
||||
mVBuf = LockedImageSurface::AllocateBuffer(mSize, mFormat);
|
||||
if (!mVBuf) {
|
||||
RefPtr<VolatileBuffer> buf =
|
||||
LockedImageSurface::AllocateBuffer(mSize, format);
|
||||
if (!buf) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
|
||||
if (!mImageSurface || mImageSurface->CairoStatus())
|
||||
RefPtr<gfxImageSurface> surf =
|
||||
LockedImageSurface::CreateSurface(buf, mSize, mFormat);
|
||||
if (!surf || surf->CairoStatus())
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
gfxContext context(mImageSurface);
|
||||
gfxContext context(surf);
|
||||
context.SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
if (mSinglePixel)
|
||||
context.SetDeviceColor(mSinglePixelColor);
|
||||
else if (mFormat == gfxImageFormat::RGB16_565)
|
||||
context.SetSource(mImageSurface);
|
||||
else
|
||||
context.SetSource(mOptSurface);
|
||||
context.Paint();
|
||||
|
||||
mFormat = format;
|
||||
mVBuf = buf;
|
||||
mImageSurface = surf;
|
||||
mOptSurface = nullptr;
|
||||
#ifdef USE_WIN_SURFACE
|
||||
mWinSurface = nullptr;
|
||||
|
@ -49,7 +49,7 @@ public:
|
||||
nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, gfxImageFormat aFormat, uint8_t aPaletteDepth = 0);
|
||||
nsresult Optimize();
|
||||
|
||||
void Draw(gfxContext *aContext, GraphicsFilter aFilter,
|
||||
bool Draw(gfxContext *aContext, GraphicsFilter aFilter,
|
||||
const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
|
||||
const nsIntMargin &aPadding, const nsIntRect &aSubimage,
|
||||
uint32_t aImageFlags = imgIContainer::FLAG_NONE);
|
||||
@ -182,7 +182,8 @@ private: // methods
|
||||
gfxRect& aFill,
|
||||
gfxRect& aSubimage,
|
||||
gfxRect& aSourceRect,
|
||||
gfxRect& aImageRect);
|
||||
gfxRect& aImageRect,
|
||||
gfxASurface* aSurface);
|
||||
|
||||
private: // data
|
||||
nsRefPtr<gfxImageSurface> mImageSurface;
|
||||
|
@ -53,6 +53,7 @@ LOCAL_INCLUDES += [
|
||||
'/dom/camera',
|
||||
'/dom/events',
|
||||
'/dom/file',
|
||||
'/dom/filesystem',
|
||||
'/dom/media',
|
||||
'/dom/speakermanager',
|
||||
'/dom/src/geolocation',
|
||||
|
@ -20,6 +20,7 @@ import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator;
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.db.BrowserContract.Combined;
|
||||
import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.favicons.Favicons;
|
||||
import org.mozilla.gecko.favicons.LoadFaviconTask;
|
||||
@ -59,6 +60,7 @@ import org.mozilla.gecko.widget.GeckoActionProvider;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
@ -385,7 +387,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
});
|
||||
}
|
||||
|
||||
void handleReaderAdded(int result, final String title, final String url) {
|
||||
private void handleReaderAdded(int result, final ContentValues values) {
|
||||
if (result != READER_ADD_SUCCESS) {
|
||||
if (result == READER_ADD_FAILED) {
|
||||
showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT);
|
||||
@ -399,7 +401,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
BrowserDB.addReadingListItem(getContentResolver(), title, url);
|
||||
BrowserDB.addReadingListItem(getContentResolver(), values);
|
||||
showToast(R.string.reading_list_added, Toast.LENGTH_SHORT);
|
||||
|
||||
final int count = BrowserDB.getReadingListCount(getContentResolver());
|
||||
@ -408,6 +410,15 @@ abstract public class BrowserApp extends GeckoApp
|
||||
});
|
||||
}
|
||||
|
||||
private ContentValues messageToReadingListContentValues(JSONObject message) {
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(ReadingListItems.URL, message.optString("url"));
|
||||
values.put(ReadingListItems.TITLE, message.optString("title"));
|
||||
values.put(ReadingListItems.LENGTH, message.optInt("length"));
|
||||
values.put(ReadingListItems.EXCERPT, message.optString("excerpt"));
|
||||
return values;
|
||||
}
|
||||
|
||||
void handleReaderRemoved(final String url) {
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
@ -1127,9 +1138,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
handleReaderListStatusRequest(message.getString("url"));
|
||||
} else if (event.equals("Reader:Added")) {
|
||||
final int result = message.getInt("result");
|
||||
final String title = message.getString("title");
|
||||
final String url = message.getString("url");
|
||||
handleReaderAdded(result, title, url);
|
||||
handleReaderAdded(result, messageToReadingListContentValues(message));
|
||||
} else if (event.equals("Reader:Removed")) {
|
||||
final String url = message.getString("url");
|
||||
handleReaderRemoved(url);
|
||||
@ -1645,17 +1654,21 @@ abstract public class BrowserApp extends GeckoApp
|
||||
final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub);
|
||||
mHomePager = (HomePager) homePagerStub.inflate();
|
||||
|
||||
final HomeBanner homeBanner = (HomeBanner) findViewById(R.id.home_banner);
|
||||
mHomePager.setBanner(homeBanner);
|
||||
// Don't show the banner in guest mode.
|
||||
if (!getProfile().inGuestMode()) {
|
||||
final ViewStub homeBannerStub = (ViewStub) findViewById(R.id.home_banner_stub);
|
||||
final HomeBanner homeBanner = (HomeBanner) homeBannerStub.inflate();
|
||||
mHomePager.setBanner(homeBanner);
|
||||
|
||||
// Remove the banner from the view hierarchy if it is dismissed.
|
||||
homeBanner.setOnDismissListener(new HomeBanner.OnDismissListener() {
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
mHomePager.setBanner(null);
|
||||
mHomePagerContainer.removeView(homeBanner);
|
||||
}
|
||||
});
|
||||
// Remove the banner from the view hierarchy if it is dismissed.
|
||||
homeBanner.setOnDismissListener(new HomeBanner.OnDismissListener() {
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
mHomePager.setBanner(null);
|
||||
mHomePagerContainer.removeView(homeBanner);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mHomePagerContainer.setVisibility(View.VISIBLE);
|
||||
|
@ -106,7 +106,7 @@ public class DynamicToolbar {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean immediate = transition.equals(VisibilityTransition.ANIMATE);
|
||||
final boolean immediate = transition.equals(VisibilityTransition.IMMEDIATE);
|
||||
if (visible) {
|
||||
layerView.getLayerMarginsAnimator().showMargins(immediate);
|
||||
} else {
|
||||
|
@ -1197,10 +1197,10 @@ public class GeckoAppShell
|
||||
* @return an <code>Intent</code>, or <code>null</code> if none could be
|
||||
* produced.
|
||||
*/
|
||||
static Intent getShareIntent(final Context context,
|
||||
final String targetURI,
|
||||
final String mimeType,
|
||||
final String title) {
|
||||
public static Intent getShareIntent(final Context context,
|
||||
final String targetURI,
|
||||
final String mimeType,
|
||||
final String title) {
|
||||
Intent shareIntent = getIntentForActionString(Intent.ACTION_SEND);
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, targetURI);
|
||||
shareIntent.putExtra(Intent.EXTRA_SUBJECT, title);
|
||||
|
@ -396,6 +396,9 @@ public class BrowserContract {
|
||||
public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
|
||||
public static final String[] DEFAULT_PROJECTION = new String[] { _ID, URL, TITLE, EXCERPT, LENGTH };
|
||||
|
||||
// Minimum fields required to create a reading list item.
|
||||
public static final String[] REQUIRED_FIELDS = { Bookmarks.URL, Bookmarks.TITLE };
|
||||
|
||||
public static final String TABLE_NAME = "reading_list";
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWrapper;
|
||||
@ -98,7 +99,7 @@ public class BrowserDB {
|
||||
@RobocopTarget
|
||||
public void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword);
|
||||
|
||||
public void addReadingListItem(ContentResolver cr, String title, String uri);
|
||||
public void addReadingListItem(ContentResolver cr, ContentValues values);
|
||||
|
||||
public void removeReadingListItemWithURL(ContentResolver cr, String uri);
|
||||
|
||||
@ -271,8 +272,8 @@ public class BrowserDB {
|
||||
sDb.updateBookmark(cr, id, uri, title, keyword);
|
||||
}
|
||||
|
||||
public static void addReadingListItem(ContentResolver cr, String title, String uri) {
|
||||
sDb.addReadingListItem(cr, title, uri);
|
||||
public static void addReadingListItem(ContentResolver cr, ContentValues values) {
|
||||
sDb.addReadingListItem(cr, values);
|
||||
}
|
||||
|
||||
public static void removeReadingListItemWithURL(ContentResolver cr, String uri) {
|
||||
|
@ -699,11 +699,16 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addReadingListItem(ContentResolver cr, String title, String uri) {
|
||||
final ContentValues values = new ContentValues();
|
||||
public void addReadingListItem(ContentResolver cr, ContentValues values) {
|
||||
// Check that required fields are present.
|
||||
for (String field: ReadingListItems.REQUIRED_FIELDS) {
|
||||
if (!values.containsKey(field)) {
|
||||
throw new IllegalArgumentException("Missing required field for reading list item: " + field);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear delete flag if necessary
|
||||
values.put(ReadingListItems.IS_DELETED, 0);
|
||||
values.put(ReadingListItems.URL, uri);
|
||||
values.put(ReadingListItems.TITLE, title);
|
||||
|
||||
// Restore deleted record if possible
|
||||
final Uri insertUri = mReadingListUriWithProfile
|
||||
@ -714,7 +719,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
final int updated = cr.update(insertUri,
|
||||
values,
|
||||
ReadingListItems.URL + " = ? ",
|
||||
new String[] { uri });
|
||||
new String[] { values.getAsString(ReadingListItems.URL) });
|
||||
|
||||
debug("Updated " + updated + " rows to new modified time.");
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ public class HomeBanner extends LinearLayout
|
||||
public HomeBanner(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.home_banner, this);
|
||||
LayoutInflater.from(context).inflate(R.layout.home_banner_content, this);
|
||||
|
||||
mTextView = (TextView) findViewById(R.id.text);
|
||||
mIconView = (ImageView) findViewById(R.id.icon);
|
||||
|
@ -335,6 +335,10 @@ size. -->
|
||||
as alternate text for accessibility. It is not visible in the UI. -->
|
||||
<!ENTITY home_reading_list_hint_accessible "TIP: Save articles to your reading list by long pressing the reader mode button when it appears in the title bar.">
|
||||
|
||||
<!-- Localization note (home_default_empty): This string is used as the default text when there
|
||||
is no data to show in an about:home panel that was created by an add-on. -->
|
||||
<!ENTITY home_default_empty "No content could be found for this panel.">
|
||||
|
||||
<!ENTITY pin_site_dialog_hint "Enter a search keyword">
|
||||
|
||||
<!ENTITY filepicker_title "Choose File">
|
||||
|
@ -102,6 +102,25 @@ public class MenuItemActionView extends LinearLayout
|
||||
mMenuItem.setShowIcon(show);
|
||||
}
|
||||
|
||||
public void setIcon(Drawable icon) {
|
||||
mMenuItem.setIcon(icon);
|
||||
mMenuButton.setIcon(icon);
|
||||
}
|
||||
|
||||
public void setIcon(int icon) {
|
||||
mMenuItem.setIcon(icon);
|
||||
mMenuButton.setIcon(icon);
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
mMenuItem.setTitle(title);
|
||||
mMenuButton.setContentDescription(title);
|
||||
}
|
||||
|
||||
public void setSubMenuIndicator(boolean hasSubMenu) {
|
||||
mMenuItem.setSubMenuIndicator(hasSubMenu);
|
||||
}
|
||||
|
||||
public void addActionButton(Drawable drawable) {
|
||||
// If this is the first icon, retain the text.
|
||||
// If not, make the menu item an icon.
|
||||
|
@ -142,7 +142,7 @@ public class MenuItemDefault extends TextView
|
||||
}
|
||||
}
|
||||
|
||||
private void setSubMenuIndicator(boolean hasSubMenu) {
|
||||
void setSubMenuIndicator(boolean hasSubMenu) {
|
||||
if (mHasSubMenu != hasSubMenu) {
|
||||
mHasSubMenu = hasSubMenu;
|
||||
refreshDrawableState();
|
||||
|
@ -61,7 +61,7 @@ public class IntentChooserPrompt {
|
||||
|
||||
// If there's only one item in the intent list, just return it
|
||||
if (mItems.size() == 1) {
|
||||
handler.onIntentSelected(mItems.get(0).intent, 0);
|
||||
handler.onIntentSelected(mItems.get(0).getIntent(), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ public class IntentChooserPrompt {
|
||||
if (itemId == -1) {
|
||||
handler.onCancelled();
|
||||
} else {
|
||||
handler.onIntentSelected(mItems.get(itemId).intent, itemId);
|
||||
handler.onIntentSelected(mItems.get(itemId).getIntent(), itemId);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -128,12 +128,14 @@ public class IntentChooserPrompt {
|
||||
|
||||
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);
|
||||
item.setIcon(info.loadIcon(pm));
|
||||
|
||||
Intent i = new Intent(intent);
|
||||
// These intents should be implicit.
|
||||
item.intent.setComponent(new ComponentName(info.activityInfo.applicationInfo.packageName,
|
||||
info.activityInfo.name));
|
||||
i.setComponent(new ComponentName(info.activityInfo.applicationInfo.packageName,
|
||||
info.activityInfo.name));
|
||||
item.setIntent(new Intent(i));
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,17 @@
|
||||
package org.mozilla.gecko.prompts;
|
||||
|
||||
import org.mozilla.gecko.menu.MenuItemActionView;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.widget.GeckoActionProvider;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.Bitmap;
|
||||
@ -14,18 +19,20 @@ import android.graphics.drawable.BitmapDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AdapterView;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
private static final int VIEW_TYPE_ITEM = 0;
|
||||
private static final int VIEW_TYPE_GROUP = 1;
|
||||
private static final int VIEW_TYPE_COUNT = 2;
|
||||
private static final int VIEW_TYPE_ACTIONS = 2;
|
||||
private static final int VIEW_TYPE_COUNT = 3;
|
||||
|
||||
private static final String LOGTAG = "GeckoPromptListAdapter";
|
||||
|
||||
@ -38,6 +45,7 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
private static int mIconSize;
|
||||
private static int mMinRowSize;
|
||||
private static int mIconTextPadding;
|
||||
private static float mTextSize;
|
||||
private static boolean mInitialized = false;
|
||||
|
||||
PromptListAdapter(Context context, int textViewResourceId, PromptListItem[] objects) {
|
||||
@ -55,6 +63,8 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
mIconTextPadding = (int) (res.getDimension(R.dimen.prompt_service_icon_text_padding));
|
||||
mIconSize = (int) (res.getDimension(R.dimen.prompt_service_icon_size));
|
||||
mMinRowSize = (int) (res.getDimension(R.dimen.prompt_service_min_list_item_height));
|
||||
mTextSize = res.getDimension(R.dimen.menu_item_textsize);
|
||||
|
||||
mInitialized = true;
|
||||
}
|
||||
}
|
||||
@ -64,6 +74,8 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
PromptListItem item = getItem(position);
|
||||
if (item.isGroup) {
|
||||
return VIEW_TYPE_GROUP;
|
||||
} else if (item.showAsActions) {
|
||||
return VIEW_TYPE_ACTIONS;
|
||||
} else {
|
||||
return VIEW_TYPE_ITEM;
|
||||
}
|
||||
@ -90,11 +102,11 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
|
||||
public void toggleSelected(int position) {
|
||||
PromptListItem item = getItem(position);
|
||||
item.selected = !item.selected;
|
||||
item.setSelected(!item.getSelected());
|
||||
}
|
||||
|
||||
private void maybeUpdateIcon(PromptListItem item, TextView t) {
|
||||
if (item.icon == null && !item.inGroup && !item.isParent) {
|
||||
if (item.getIcon() == null && !item.inGroup && !item.isParent) {
|
||||
t.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||
return;
|
||||
}
|
||||
@ -103,10 +115,10 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
Resources res = getContext().getResources();
|
||||
// Set the padding between the icon and the text.
|
||||
t.setCompoundDrawablePadding(mIconTextPadding);
|
||||
if (item.icon != null) {
|
||||
if (item.getIcon() != null) {
|
||||
// We want the icon to be of a specific size. Some do not
|
||||
// follow this rule so we have to resize them.
|
||||
Bitmap bitmap = ((BitmapDrawable) item.icon).getBitmap();
|
||||
Bitmap bitmap = ((BitmapDrawable) item.getIcon()).getBitmap();
|
||||
d = new BitmapDrawable(res, Bitmap.createScaledBitmap(bitmap, mIconSize, mIconSize, true));
|
||||
} else if (item.inGroup) {
|
||||
// We don't currently support "indenting" items with icons
|
||||
@ -130,12 +142,12 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
// Apparently just using ct.setChecked(true) doesn't work, so this
|
||||
// is stolen from the android source code as a way to set the checked
|
||||
// state of these items
|
||||
list.setItemChecked(position, item.selected);
|
||||
list.setItemChecked(position, item.getSelected());
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSelected(int position){
|
||||
return getItem(position).selected;
|
||||
return getItem(position).getSelected();
|
||||
}
|
||||
|
||||
ArrayList<Integer> getSelected() {
|
||||
@ -161,6 +173,41 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private View getActionView(PromptListItem item) {
|
||||
GeckoActionProvider provider = GeckoActionProvider.getForType(item.getIntent().getType(), getContext());
|
||||
provider.setIntent(item.getIntent());
|
||||
return provider.onCreateActionView();
|
||||
}
|
||||
|
||||
private void updateActionView(final PromptListItem item, final MenuItemActionView view, final ListView list, final int position) {
|
||||
view.setTitle(item.label);
|
||||
view.setIcon(item.getIcon());
|
||||
view.setSubMenuIndicator(item.isParent);
|
||||
view.setMenuItemClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ListView.OnItemClickListener listener = list.getOnItemClickListener();
|
||||
if (listener != null) {
|
||||
listener.onItemClick(list, view, position, position);
|
||||
}
|
||||
|
||||
final GeckoActionProvider provider = GeckoActionProvider.getForType(item.getIntent().getType(), getContext());
|
||||
IntentChooserPrompt prompt = new IntentChooserPrompt(getContext(), provider);
|
||||
prompt.show(item.label, getContext(), new IntentHandler() {
|
||||
@Override
|
||||
public void onIntentSelected(final Intent intent, final int p) {
|
||||
provider.chooseActivity(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled() {
|
||||
// do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
PromptListItem item = getItem(position);
|
||||
@ -168,26 +215,36 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
ViewHolder viewHolder = null;
|
||||
|
||||
if (convertView == null) {
|
||||
int resourceId = mResourceId;
|
||||
if (item.isGroup) {
|
||||
resourceId = R.layout.list_item_header;
|
||||
if (type == VIEW_TYPE_ACTIONS) {
|
||||
convertView = getActionView(item);
|
||||
} else {
|
||||
int resourceId = mResourceId;
|
||||
if (item.isGroup) {
|
||||
resourceId = R.layout.list_item_header;
|
||||
}
|
||||
|
||||
LayoutInflater mInflater = LayoutInflater.from(getContext());
|
||||
convertView = mInflater.inflate(resourceId, null);
|
||||
convertView.setMinimumHeight(mMinRowSize);
|
||||
|
||||
TextView tv = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
|
||||
viewHolder = new ViewHolder(tv, tv.getPaddingLeft(), tv.getPaddingRight(),
|
||||
tv.getPaddingTop(), tv.getPaddingBottom());
|
||||
|
||||
convertView.setTag(viewHolder);
|
||||
}
|
||||
LayoutInflater mInflater = LayoutInflater.from(getContext());
|
||||
convertView = mInflater.inflate(resourceId, null);
|
||||
convertView.setMinimumHeight(mMinRowSize);
|
||||
|
||||
TextView tv = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
viewHolder = new ViewHolder(tv, tv.getPaddingLeft(), tv.getPaddingRight(),
|
||||
tv.getPaddingTop(), tv.getPaddingBottom());
|
||||
|
||||
convertView.setTag(viewHolder);
|
||||
} else {
|
||||
viewHolder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
viewHolder.textView.setText(item.label);
|
||||
maybeUpdateCheckedState((ListView) parent, position, item, viewHolder);
|
||||
maybeUpdateIcon(item, viewHolder.textView);
|
||||
if (type == VIEW_TYPE_ACTIONS) {
|
||||
updateActionView(item, (MenuItemActionView) convertView, (ListView) parent, position);
|
||||
} else {
|
||||
viewHolder.textView.setText(item.label);
|
||||
maybeUpdateCheckedState((ListView) parent, position, item, viewHolder);
|
||||
maybeUpdateIcon(item, viewHolder.textView);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user