mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-04 21:18:35 +00:00
Merge inbound to central, a=merge
MozReview-Commit-ID: 62e9XDudUlc
This commit is contained in:
commit
3842d8fcef
@ -68,16 +68,16 @@
|
||||
new ExpectedEditState({
|
||||
editing: true,
|
||||
multiline: true,
|
||||
atStart: false,
|
||||
atEnd: true
|
||||
atStart: true,
|
||||
atEnd: false
|
||||
}),
|
||||
new ExpectedCursorChange(
|
||||
['Please refrain from Mayoneggs during this salmonella scare.',
|
||||
{string: 'textarea'}]),
|
||||
new ExpectedTextSelectionChanged(59, 59)
|
||||
new ExpectedTextSelectionChanged(0, 0)
|
||||
],
|
||||
[ContentMessages.activateCurrent(10),
|
||||
new ExpectedTextCaretChanged(10, 59),
|
||||
new ExpectedTextCaretChanged(0, 10),
|
||||
new ExpectedEditState({ editing: true,
|
||||
multiline: true,
|
||||
atStart: false,
|
||||
|
@ -107,6 +107,7 @@
|
||||
// (per spec, min/maxlength validity is affected by interactive edits)
|
||||
var mininp = document.getElementById("minlength");
|
||||
mininp.focus();
|
||||
mininp.setSelectionRange(mininp.value.length, mininp.value.length);
|
||||
synthesizeKey("VK_BACK_SPACE", {});
|
||||
ok(!mininp.validity.valid,
|
||||
"input should be invalid after interactive edits");
|
||||
|
@ -89,38 +89,6 @@ var AdbController = {
|
||||
|
||||
updateState: function() {
|
||||
this.umsActive = false;
|
||||
this.storages = navigator.getDeviceStorages('sdcard');
|
||||
this.updateStorageState(0);
|
||||
},
|
||||
|
||||
updateStorageState: function(storageIndex) {
|
||||
if (storageIndex >= this.storages.length) {
|
||||
// We've iterated through all of the storage objects, now we can
|
||||
// really do updateStateInternal.
|
||||
this.updateStateInternal();
|
||||
return;
|
||||
}
|
||||
let storage = this.storages[storageIndex];
|
||||
DEBUG && debug("Checking availability of storage: '" + storage.storageName + "'");
|
||||
|
||||
let req = storage.available();
|
||||
req.onsuccess = function(e) {
|
||||
DEBUG && debug("Storage: '" + storage.storageName + "' is '" + e.target.result + "'");
|
||||
if (e.target.result == 'shared') {
|
||||
// We've found a storage area that's being shared with the PC.
|
||||
// We can stop looking now.
|
||||
this.umsActive = true;
|
||||
this.updateStateInternal();
|
||||
return;
|
||||
}
|
||||
this.updateStorageState(storageIndex + 1);
|
||||
}.bind(this);
|
||||
req.onerror = function(e) {
|
||||
|
||||
Cu.reportError("AdbController: error querying storage availability for '" +
|
||||
this.storages[storageIndex].storageName + "' (ignoring)\n");
|
||||
this.updateStorageState(storageIndex + 1);
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
updateStateInternal: function() {
|
||||
|
@ -98,13 +98,15 @@
|
||||
<div id="ruleview-command-toolbar">
|
||||
<button id="ruleview-add-rule-button" data-localization="title=inspector.addRule.tooltip" class="devtools-button"></button>
|
||||
<button id="pseudo-class-panel-toggle" data-localization="title=inspector.togglePseudo.tooltip" class="devtools-button"></button>
|
||||
<button id="class-panel-toggle" data-localization="title=inspector.classPanel.toggleClass.tooltip" class="devtools-button"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="pseudo-class-panel" hidden="true">
|
||||
<div id="pseudo-class-panel" class="ruleview-reveal-panel" hidden="true">
|
||||
<label><input id="pseudo-hover-toggle" type="checkbox" value=":hover" tabindex="-1" />:hover</label>
|
||||
<label><input id="pseudo-active-toggle" type="checkbox" value=":active" tabindex="-1" />:active</label>
|
||||
<label><input id="pseudo-focus-toggle" type="checkbox" value=":focus" tabindex="-1" />:focus</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ruleview-class-panel" class="ruleview-reveal-panel" hidden="true"></div>
|
||||
</div>
|
||||
|
||||
<div id="ruleview-container" class="ruleview">
|
||||
|
@ -17,6 +17,7 @@ const {PrefObserver} = require("devtools/client/shared/prefs");
|
||||
const ElementStyle = require("devtools/client/inspector/rules/models/element-style");
|
||||
const Rule = require("devtools/client/inspector/rules/models/rule");
|
||||
const RuleEditor = require("devtools/client/inspector/rules/views/rule-editor");
|
||||
const ClassListPreviewer = require("devtools/client/inspector/rules/views/class-list-previewer");
|
||||
const {gDevTools} = require("devtools/client/framework/devtools");
|
||||
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
|
||||
const {
|
||||
@ -120,6 +121,7 @@ function CssRuleView(inspector, document, store, pageStyle) {
|
||||
this._onClearSearch = this._onClearSearch.bind(this);
|
||||
this._onTogglePseudoClassPanel = this._onTogglePseudoClassPanel.bind(this);
|
||||
this._onTogglePseudoClass = this._onTogglePseudoClass.bind(this);
|
||||
this._onToggleClassPanel = this._onToggleClassPanel.bind(this);
|
||||
|
||||
let doc = this.styleDocument;
|
||||
this.element = doc.getElementById("ruleview-container-focusable");
|
||||
@ -128,6 +130,8 @@ function CssRuleView(inspector, document, store, pageStyle) {
|
||||
this.searchClearButton = doc.getElementById("ruleview-searchinput-clear");
|
||||
this.pseudoClassPanel = doc.getElementById("pseudo-class-panel");
|
||||
this.pseudoClassToggle = doc.getElementById("pseudo-class-panel-toggle");
|
||||
this.classPanel = doc.getElementById("ruleview-class-panel");
|
||||
this.classToggle = doc.getElementById("class-panel-toggle");
|
||||
this.hoverCheckbox = doc.getElementById("pseudo-hover-toggle");
|
||||
this.activeCheckbox = doc.getElementById("pseudo-active-toggle");
|
||||
this.focusCheckbox = doc.getElementById("pseudo-focus-toggle");
|
||||
@ -146,8 +150,8 @@ function CssRuleView(inspector, document, store, pageStyle) {
|
||||
this.searchField.addEventListener("input", this._onFilterStyles);
|
||||
this.searchField.addEventListener("contextmenu", this.inspector.onTextBoxContextMenu);
|
||||
this.searchClearButton.addEventListener("click", this._onClearSearch);
|
||||
this.pseudoClassToggle.addEventListener("click",
|
||||
this._onTogglePseudoClassPanel);
|
||||
this.pseudoClassToggle.addEventListener("click", this._onTogglePseudoClassPanel);
|
||||
this.classToggle.addEventListener("click", this._onToggleClassPanel);
|
||||
this.hoverCheckbox.addEventListener("click", this._onTogglePseudoClass);
|
||||
this.activeCheckbox.addEventListener("click", this._onTogglePseudoClass);
|
||||
this.focusCheckbox.addEventListener("click", this._onTogglePseudoClass);
|
||||
@ -181,6 +185,8 @@ function CssRuleView(inspector, document, store, pageStyle) {
|
||||
|
||||
this.highlighters.addToView(this);
|
||||
|
||||
this.classListPreviewer = new ClassListPreviewer(this.inspector, this.classPanel);
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
@ -673,6 +679,7 @@ CssRuleView.prototype = {
|
||||
|
||||
this.tooltips.destroy();
|
||||
this.highlighters.removeFromView(this);
|
||||
this.classListPreviewer.destroy();
|
||||
|
||||
// Remove bound listeners
|
||||
this.shortcuts.destroy();
|
||||
@ -683,8 +690,8 @@ CssRuleView.prototype = {
|
||||
this.searchField.removeEventListener("contextmenu",
|
||||
this.inspector.onTextBoxContextMenu);
|
||||
this.searchClearButton.removeEventListener("click", this._onClearSearch);
|
||||
this.pseudoClassToggle.removeEventListener("click",
|
||||
this._onTogglePseudoClassPanel);
|
||||
this.pseudoClassToggle.removeEventListener("click", this._onTogglePseudoClassPanel);
|
||||
this.classToggle.removeEventListener("click", this._onToggleClassPanel);
|
||||
this.hoverCheckbox.removeEventListener("click", this._onTogglePseudoClass);
|
||||
this.activeCheckbox.removeEventListener("click", this._onTogglePseudoClass);
|
||||
this.focusCheckbox.removeEventListener("click", this._onTogglePseudoClass);
|
||||
@ -693,6 +700,8 @@ CssRuleView.prototype = {
|
||||
this.searchClearButton = null;
|
||||
this.pseudoClassPanel = null;
|
||||
this.pseudoClassToggle = null;
|
||||
this.classPanel = null;
|
||||
this.classToggle = null;
|
||||
this.hoverCheckbox = null;
|
||||
this.activeCheckbox = null;
|
||||
this.focusCheckbox = null;
|
||||
@ -1372,18 +1381,30 @@ CssRuleView.prototype = {
|
||||
*/
|
||||
_onTogglePseudoClassPanel: function () {
|
||||
if (this.pseudoClassPanel.hidden) {
|
||||
this.pseudoClassToggle.classList.add("checked");
|
||||
this.hoverCheckbox.setAttribute("tabindex", "0");
|
||||
this.activeCheckbox.setAttribute("tabindex", "0");
|
||||
this.focusCheckbox.setAttribute("tabindex", "0");
|
||||
this.showPseudoClassPanel();
|
||||
} else {
|
||||
this.pseudoClassToggle.classList.remove("checked");
|
||||
this.hoverCheckbox.setAttribute("tabindex", "-1");
|
||||
this.activeCheckbox.setAttribute("tabindex", "-1");
|
||||
this.focusCheckbox.setAttribute("tabindex", "-1");
|
||||
this.hidePseudoClassPanel();
|
||||
}
|
||||
},
|
||||
|
||||
this.pseudoClassPanel.hidden = !this.pseudoClassPanel.hidden;
|
||||
showPseudoClassPanel: function () {
|
||||
this.hideClassPanel();
|
||||
|
||||
this.pseudoClassToggle.classList.add("checked");
|
||||
this.hoverCheckbox.setAttribute("tabindex", "0");
|
||||
this.activeCheckbox.setAttribute("tabindex", "0");
|
||||
this.focusCheckbox.setAttribute("tabindex", "0");
|
||||
|
||||
this.pseudoClassPanel.hidden = false;
|
||||
},
|
||||
|
||||
hidePseudoClassPanel: function () {
|
||||
this.pseudoClassToggle.classList.remove("checked");
|
||||
this.hoverCheckbox.setAttribute("tabindex", "-1");
|
||||
this.activeCheckbox.setAttribute("tabindex", "-1");
|
||||
this.focusCheckbox.setAttribute("tabindex", "-1");
|
||||
|
||||
this.pseudoClassPanel.hidden = true;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1395,6 +1416,32 @@ CssRuleView.prototype = {
|
||||
this.inspector.togglePseudoClass(target.value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the class panel button is clicked and toggles the display of the class
|
||||
* panel.
|
||||
*/
|
||||
_onToggleClassPanel: function () {
|
||||
if (this.classPanel.hidden) {
|
||||
this.showClassPanel();
|
||||
} else {
|
||||
this.hideClassPanel();
|
||||
}
|
||||
},
|
||||
|
||||
showClassPanel: function () {
|
||||
this.hidePseudoClassPanel();
|
||||
|
||||
this.classToggle.classList.add("checked");
|
||||
this.classPanel.hidden = false;
|
||||
|
||||
this.classListPreviewer.focusAddClassField();
|
||||
},
|
||||
|
||||
hideClassPanel: function () {
|
||||
this.classToggle.classList.remove("checked");
|
||||
this.classPanel.hidden = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the keypress event in the rule view.
|
||||
*/
|
||||
|
@ -61,6 +61,13 @@ support-files =
|
||||
[browser_rules_authored_color.js]
|
||||
[browser_rules_authored_override.js]
|
||||
[browser_rules_blob_stylesheet.js]
|
||||
[browser_rules_class_panel_add.js]
|
||||
[browser_rules_class_panel_content.js]
|
||||
[browser_rules_class_panel_edit.js]
|
||||
[browser_rules_class_panel_invalid_nodes.js]
|
||||
[browser_rules_class_panel_mutation.js]
|
||||
[browser_rules_class_panel_state_preserved.js]
|
||||
[browser_rules_class_panel_toggle.js]
|
||||
[browser_rules_colorpicker-and-image-tooltip_01.js]
|
||||
[browser_rules_colorpicker-and-image-tooltip_02.js]
|
||||
[browser_rules_colorpicker-appears-on-swatch-click.js]
|
||||
|
@ -0,0 +1,91 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that classes can be added in the class panel
|
||||
|
||||
// This array contains the list of test cases. Each test case contains these properties:
|
||||
// - {String} textEntered The text to be entered in the field
|
||||
// - {Boolean} expectNoMutation Set to true if we shouldn't wait for a DOM mutation
|
||||
// - {Array} expectedClasses The expected list of classes to be applied to the DOM and to
|
||||
// be found in the class panel
|
||||
const TEST_ARRAY = [{
|
||||
textEntered: "",
|
||||
expectNoMutation: true,
|
||||
expectedClasses: []
|
||||
}, {
|
||||
textEntered: "class",
|
||||
expectedClasses: ["class"]
|
||||
}, {
|
||||
textEntered: "class",
|
||||
expectNoMutation: true,
|
||||
expectedClasses: ["class"]
|
||||
}, {
|
||||
textEntered: "a a a a a a a a a a",
|
||||
expectedClasses: ["class", "a"]
|
||||
}, {
|
||||
textEntered: "class2 class3",
|
||||
expectedClasses: ["class", "a", "class2", "class3"]
|
||||
}, {
|
||||
textEntered: " ",
|
||||
expectNoMutation: true,
|
||||
expectedClasses: ["class", "a", "class2", "class3"]
|
||||
}, {
|
||||
textEntered: " class4",
|
||||
expectedClasses: ["class", "a", "class2", "class3", "class4"]
|
||||
}, {
|
||||
textEntered: " \t class5 \t \t\t ",
|
||||
expectedClasses: ["class", "a", "class2", "class3", "class4", "class5"]
|
||||
}];
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8,");
|
||||
let {testActor, inspector, view} = yield openRuleView();
|
||||
|
||||
info("Open the class panel");
|
||||
view.showClassPanel();
|
||||
|
||||
const textField = inspector.panelDoc.querySelector("#ruleview-class-panel .add-class");
|
||||
ok(textField, "The input field exists in the class panel");
|
||||
|
||||
textField.focus();
|
||||
|
||||
let onMutation;
|
||||
for (let {textEntered, expectNoMutation, expectedClasses} of TEST_ARRAY) {
|
||||
if (!expectNoMutation) {
|
||||
onMutation = inspector.once("markupmutation");
|
||||
}
|
||||
|
||||
info(`Enter the test string in the field: ${textEntered}`);
|
||||
for (let key of textEntered.split("")) {
|
||||
EventUtils.synthesizeKey(key, {}, view.styleWindow);
|
||||
}
|
||||
|
||||
info("Submit the change and wait for the textfield to become empty");
|
||||
let onEmpty = waitForFieldToBeEmpty(textField);
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
|
||||
|
||||
if (!expectNoMutation) {
|
||||
info("Wait for the DOM to change");
|
||||
yield onMutation;
|
||||
}
|
||||
|
||||
yield onEmpty;
|
||||
|
||||
info("Check the state of the DOM node");
|
||||
let className = yield testActor.getAttribute("body", "class");
|
||||
let expectedClassName = expectedClasses.length ? expectedClasses.join(" ") : null;
|
||||
is(className, expectedClassName, "The DOM node has the right className");
|
||||
|
||||
info("Check the content of the class panel");
|
||||
checkClassPanelContent(view, expectedClasses.map(name => {
|
||||
return {name, state: true};
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
function waitForFieldToBeEmpty(textField) {
|
||||
return waitForSuccess(() => !textField.value);
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that class panel shows the right content when selecting various nodes.
|
||||
|
||||
// This array contains the list of test cases. Each test case contains these properties:
|
||||
// - {String} inputClassName The className on a node
|
||||
// - {Array} expectedClasses The expected list of classes in the class panel
|
||||
const TEST_ARRAY = [{
|
||||
inputClassName: "",
|
||||
expectedClasses: []
|
||||
}, {
|
||||
inputClassName: " a a a a a a a a a",
|
||||
expectedClasses: ["a"]
|
||||
}, {
|
||||
inputClassName: "c1 c2 c3 c4 c5",
|
||||
expectedClasses: ["c1", "c2", "c3", "c4", "c5"]
|
||||
}, {
|
||||
inputClassName: "a a b b c c a a b b c c",
|
||||
expectedClasses: ["a", "b", "c"]
|
||||
}, {
|
||||
inputClassName: "ajdhfkasjhdkjashdkjghaskdgkauhkbdhvliashdlghaslidghasldgliashdglhasli",
|
||||
expectedClasses: [
|
||||
"ajdhfkasjhdkjashdkjghaskdgkauhkbdhvliashdlghaslidghasldgliashdglhasli"
|
||||
]
|
||||
}, {
|
||||
inputClassName: "c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 " +
|
||||
"c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 " +
|
||||
"c20 c21 c22 c23 c24 c25 c26 c27 c28 c29 " +
|
||||
"c30 c31 c32 c33 c34 c35 c36 c37 c38 c39 " +
|
||||
"c40 c41 c42 c43 c44 c45 c46 c47 c48 c49",
|
||||
expectedClasses: ["c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9",
|
||||
"c10", "c11", "c12", "c13", "c14", "c15", "c16", "c17", "c18", "c19",
|
||||
"c20", "c21", "c22", "c23", "c24", "c25", "c26", "c27", "c28", "c29",
|
||||
"c30", "c31", "c32", "c33", "c34", "c35", "c36", "c37", "c38", "c39",
|
||||
"c40", "c41", "c42", "c43", "c44", "c45", "c46", "c47", "c48", "c49"]
|
||||
}, {
|
||||
inputClassName: " \n \n class1 \t class2 \t\tclass3\t",
|
||||
expectedClasses: ["class1", "class2", "class3"]
|
||||
}];
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8,<div>");
|
||||
let {testActor, inspector, view} = yield openRuleView();
|
||||
|
||||
yield selectNode("div", inspector);
|
||||
|
||||
info("Open the class panel");
|
||||
view.showClassPanel();
|
||||
|
||||
for (let {inputClassName, expectedClasses} of TEST_ARRAY) {
|
||||
info(`Apply the '${inputClassName}' className to the node`);
|
||||
const onMutation = inspector.once("markupmutation");
|
||||
yield testActor.setAttribute("div", "class", inputClassName);
|
||||
yield onMutation;
|
||||
|
||||
info("Check the content of the class panel");
|
||||
checkClassPanelContent(view, expectedClasses.map(name => {
|
||||
return {name, state: true};
|
||||
}));
|
||||
}
|
||||
});
|
@ -0,0 +1,51 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that classes can be toggled in the class panel
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8,<body class='class1 class2'>");
|
||||
let {view, testActor} = yield openRuleView();
|
||||
|
||||
info("Open the class panel");
|
||||
view.showClassPanel();
|
||||
|
||||
info("Click on class1 and check that the checkbox is unchecked and the DOM is updated");
|
||||
yield toggleClassPanelCheckBox(view, "class1");
|
||||
checkClassPanelContent(view, [
|
||||
{name: "class1", state: false},
|
||||
{name: "class2", state: true}
|
||||
]);
|
||||
let newClassName = yield testActor.getAttribute("body", "class");
|
||||
is(newClassName, "class2", "The class attribute has been updated in the DOM");
|
||||
|
||||
info("Click on class2 and check the same thing");
|
||||
yield toggleClassPanelCheckBox(view, "class2");
|
||||
checkClassPanelContent(view, [
|
||||
{name: "class1", state: false},
|
||||
{name: "class2", state: false}
|
||||
]);
|
||||
newClassName = yield testActor.getAttribute("body", "class");
|
||||
is(newClassName, "", "The class attribute has been updated in the DOM");
|
||||
|
||||
info("Click on class2 and checks that the class is added again");
|
||||
yield toggleClassPanelCheckBox(view, "class2");
|
||||
checkClassPanelContent(view, [
|
||||
{name: "class1", state: false},
|
||||
{name: "class2", state: true}
|
||||
]);
|
||||
newClassName = yield testActor.getAttribute("body", "class");
|
||||
is(newClassName, "class2", "The class attribute has been updated in the DOM");
|
||||
|
||||
info("And finally, click on class1 again and checks it is added again");
|
||||
yield toggleClassPanelCheckBox(view, "class1");
|
||||
checkClassPanelContent(view, [
|
||||
{name: "class1", state: true},
|
||||
{name: "class2", state: true}
|
||||
]);
|
||||
newClassName = yield testActor.getAttribute("body", "class");
|
||||
is(newClassName, "class1 class2", "The class attribute has been updated in the DOM");
|
||||
});
|
@ -0,0 +1,50 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test the class panel shows a message when invalid nodes are selected.
|
||||
// text nodes, pseudo-elements, DOCTYPE, comment nodes.
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab(`data:text/html;charset=utf-8,
|
||||
<body>
|
||||
<style>div::after {content: "test";}</style>
|
||||
<!-- comment -->
|
||||
Some text
|
||||
<div></div>
|
||||
</body>`);
|
||||
|
||||
info("Open the class panel");
|
||||
let {inspector, view} = yield openRuleView();
|
||||
view.showClassPanel();
|
||||
|
||||
info("Selecting the DOCTYPE node");
|
||||
let {nodes} = yield inspector.walker.children(inspector.walker.rootNode);
|
||||
yield selectNode(nodes[0], inspector);
|
||||
checkMessageIsDisplayed(view);
|
||||
|
||||
info("Selecting the comment node");
|
||||
let styleNode = yield getNodeFront("style", inspector);
|
||||
let commentNode = yield inspector.walker.nextSibling(styleNode);
|
||||
yield selectNode(commentNode, inspector);
|
||||
checkMessageIsDisplayed(view);
|
||||
|
||||
info("Selecting the text node");
|
||||
let textNode = yield inspector.walker.nextSibling(commentNode);
|
||||
yield selectNode(textNode, inspector);
|
||||
checkMessageIsDisplayed(view);
|
||||
|
||||
info("Selecting the ::after pseudo-element");
|
||||
let divNode = yield getNodeFront("div", inspector);
|
||||
let pseudoElement = (yield inspector.walker.children(divNode)).nodes[0];
|
||||
yield selectNode(pseudoElement, inspector);
|
||||
checkMessageIsDisplayed(view);
|
||||
});
|
||||
|
||||
function checkMessageIsDisplayed(view) {
|
||||
ok(view.classListPreviewer.classesEl.querySelector(".no-classes"),
|
||||
"The message is displayed");
|
||||
checkClassPanelContent(view, []);
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that class panel updates on markup mutations
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8,<div class='c1 c2'>");
|
||||
let {inspector, view, testActor} = yield openRuleView();
|
||||
|
||||
yield selectNode("div", inspector);
|
||||
|
||||
info("Open the class panel");
|
||||
view.showClassPanel();
|
||||
|
||||
info("Trigger an unrelated mutation on the div (id attribute change)");
|
||||
let onMutation = view.inspector.once("markupmutation");
|
||||
yield testActor.setAttribute("div", "id", "test-id");
|
||||
yield onMutation;
|
||||
|
||||
info("Check that the panel still contains the right classes");
|
||||
checkClassPanelContent(view, [
|
||||
{name: "c1", state: true},
|
||||
{name: "c2", state: true}
|
||||
]);
|
||||
|
||||
info("Trigger a class mutation on a different, unknown, node");
|
||||
onMutation = view.inspector.once("markupmutation");
|
||||
yield testActor.setAttribute("body", "class", "test-class");
|
||||
yield onMutation;
|
||||
|
||||
info("Check that the panel still contains the right classes");
|
||||
checkClassPanelContent(view, [
|
||||
{name: "c1", state: true},
|
||||
{name: "c2", state: true}
|
||||
]);
|
||||
|
||||
info("Trigger a class mutation on the current node");
|
||||
onMutation = view.inspector.once("markupmutation");
|
||||
yield testActor.setAttribute("div", "class", "c3 c4");
|
||||
yield onMutation;
|
||||
|
||||
info("Check that the panel now contains the new classes");
|
||||
checkClassPanelContent(view, [
|
||||
{name: "c3", state: true},
|
||||
{name: "c4", state: true}
|
||||
]);
|
||||
|
||||
info("Change the state of one of the new classes");
|
||||
yield toggleClassPanelCheckBox(view, "c4");
|
||||
checkClassPanelContent(view, [
|
||||
{name: "c3", state: true},
|
||||
{name: "c4", state: false}
|
||||
]);
|
||||
|
||||
info("Select another node");
|
||||
yield selectNode("body", inspector);
|
||||
|
||||
info("Trigger a class mutation on the div");
|
||||
onMutation = view.inspector.once("markupmutation");
|
||||
yield testActor.setAttribute("div", "class", "c5 c6 c7");
|
||||
yield onMutation;
|
||||
|
||||
info("Go back to the previous node and check the content of the class panel." +
|
||||
"Even if hidden, it should have refreshed when we changed the DOM");
|
||||
yield selectNode("div", inspector);
|
||||
checkClassPanelContent(view, [
|
||||
{name: "c5", state: true},
|
||||
{name: "c6", state: true},
|
||||
{name: "c7", state: true}
|
||||
]);
|
||||
});
|
@ -0,0 +1,37 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that class states are preserved when switching to other nodes
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8,<body class='class1 class2 class3'><div>");
|
||||
let {inspector, view} = yield openRuleView();
|
||||
|
||||
info("Open the class panel");
|
||||
view.showClassPanel();
|
||||
|
||||
info("With the <body> selected, uncheck class2 and class3 in the panel");
|
||||
yield toggleClassPanelCheckBox(view, "class2");
|
||||
yield toggleClassPanelCheckBox(view, "class3");
|
||||
|
||||
info("Now select the <div> so the panel gets refreshed");
|
||||
yield selectNode("div", inspector);
|
||||
is(view.classPanel.querySelectorAll("[type=checkbox]").length, 0,
|
||||
"The panel content doesn't contain any checkboxes anymore");
|
||||
|
||||
info("Select the <body> again");
|
||||
yield selectNode("body", inspector);
|
||||
const checkBoxes = view.classPanel.querySelectorAll("[type=checkbox]");
|
||||
|
||||
is(checkBoxes[0].dataset.name, "class1", "The first checkbox is class1");
|
||||
is(checkBoxes[0].checked, true, "The first checkbox is still checked");
|
||||
|
||||
is(checkBoxes[1].dataset.name, "class2", "The second checkbox is class2");
|
||||
is(checkBoxes[1].checked, false, "The second checkbox is still unchecked");
|
||||
|
||||
is(checkBoxes[2].dataset.name, "class3", "The third checkbox is class3");
|
||||
is(checkBoxes[2].checked, false, "The third checkbox is still unchecked");
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that the class panel can be toggled.
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8,<body class='class1 class2'>");
|
||||
let {inspector, view} = yield openRuleView();
|
||||
|
||||
info("Check that the toggle button exists");
|
||||
const button = inspector.panelDoc.querySelector("#class-panel-toggle");
|
||||
ok(button, "The class panel toggle button exists");
|
||||
is(view.classToggle, button, "The rule-view refers to the right element");
|
||||
|
||||
info("Check that the panel exists and is hidden by default");
|
||||
const panel = inspector.panelDoc.querySelector("#ruleview-class-panel");
|
||||
ok(panel, "The class panel exists");
|
||||
is(view.classPanel, panel, "The rule-view refers to the right element");
|
||||
ok(panel.hasAttribute("hidden"), "The panel is hidden");
|
||||
|
||||
info("Click on the button to show the panel");
|
||||
button.click();
|
||||
ok(!panel.hasAttribute("hidden"), "The panel is shown");
|
||||
ok(button.classList.contains("checked"), "The button is checked");
|
||||
|
||||
info("Click again to hide the panel");
|
||||
button.click();
|
||||
ok(panel.hasAttribute("hidden"), "The panel is hidden");
|
||||
ok(!button.classList.contains("checked"), "The button is unchecked");
|
||||
|
||||
info("Open the pseudo-class panel first, then the class panel");
|
||||
view.pseudoClassToggle.click();
|
||||
ok(!view.pseudoClassPanel.hasAttribute("hidden"), "The pseudo-class panel is shown");
|
||||
button.click();
|
||||
ok(!panel.hasAttribute("hidden"), "The panel is shown");
|
||||
ok(view.pseudoClassPanel.hasAttribute("hidden"), "The pseudo-class panel is hidden");
|
||||
|
||||
info("Click again on the pseudo-class button");
|
||||
view.pseudoClassToggle.click();
|
||||
ok(panel.hasAttribute("hidden"), "The panel is hidden");
|
||||
ok(!view.pseudoClassPanel.hasAttribute("hidden"), "The pseudo-class panel is shown");
|
||||
});
|
@ -506,3 +506,40 @@ function focusAndSendKey(win, key) {
|
||||
win.document.documentElement.focus();
|
||||
EventUtils.sendKey(key, win);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle one of the checkboxes inside the class-panel. Resolved after the DOM mutation
|
||||
* has been recorded.
|
||||
* @param {CssRuleView} view The rule-view instance.
|
||||
* @param {String} name The class name to find the checkbox.
|
||||
*/
|
||||
function* toggleClassPanelCheckBox(view, name) {
|
||||
info(`Clicking on checkbox for class ${name}`);
|
||||
const checkBox = [...view.classPanel.querySelectorAll("[type=checkbox]")].find(box => {
|
||||
return box.dataset.name === name;
|
||||
});
|
||||
|
||||
const onMutation = view.inspector.once("markupmutation");
|
||||
checkBox.click();
|
||||
info("Waiting for a markupmutation as a result of toggling this class");
|
||||
yield onMutation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the content of the class-panel.
|
||||
* @param {CssRuleView} view The rule-view isntance
|
||||
* @param {Array} classes The list of expected classes. Each item in this array is an
|
||||
* object with the following properties: {name: {String}, state: {Boolean}}
|
||||
*/
|
||||
function checkClassPanelContent(view, classes) {
|
||||
const checkBoxNodeList = view.classPanel.querySelectorAll("[type=checkbox]");
|
||||
is(checkBoxNodeList.length, classes.length,
|
||||
"The panel contains the expected number of checkboxes");
|
||||
|
||||
for (let i = 0; i < classes.length; i++) {
|
||||
is(checkBoxNodeList[i].dataset.name, classes[i].name,
|
||||
`Checkbox ${i} has the right class name`);
|
||||
is(checkBoxNodeList[i].checked, classes[i].state,
|
||||
`Checkbox ${i} has the right state`);
|
||||
}
|
||||
}
|
||||
|
356
devtools/client/inspector/rules/views/class-list-previewer.js
Normal file
356
devtools/client/inspector/rules/views/class-list-previewer.js
Normal file
@ -0,0 +1,356 @@
|
||||
/* 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 EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
|
||||
const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");
|
||||
|
||||
// This serves as a local cache for the classes applied to each of the node we care about
|
||||
// here.
|
||||
// The map is indexed by NodeFront. Any time a new node is selected in the inspector, an
|
||||
// entry is added here, indexed by the corresponding NodeFront.
|
||||
// The value for each entry is an array of each of the class this node has. Items of this
|
||||
// array are objects like: { name, isApplied } where the name is the class itself, and
|
||||
// isApplied is a Boolean indicating if the class is applied on the node or not.
|
||||
const CLASSES = new WeakMap();
|
||||
|
||||
/**
|
||||
* Manages the list classes per DOM elements we care about.
|
||||
* The actual list is stored in the CLASSES const, indexed by NodeFront objects.
|
||||
* The responsibility of this class is to be the source of truth for anyone who wants to
|
||||
* know which classes a given NodeFront has, and which of these are enabled and which are
|
||||
* disabled.
|
||||
* It also reacts to DOM mutations so the list of classes is up to date with what is in
|
||||
* the DOM.
|
||||
* It can also be used to enable/disable a given class, or add classes.
|
||||
*
|
||||
* @param {Inspector} inspector
|
||||
* The current inspector instance.
|
||||
*/
|
||||
function ClassListPreviewerModel(inspector) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this.inspector = inspector;
|
||||
|
||||
this.onMutations = this.onMutations.bind(this);
|
||||
this.inspector.on("markupmutation", this.onMutations);
|
||||
|
||||
this.classListProxyNode = this.inspector.panelDoc.createElement("div");
|
||||
}
|
||||
|
||||
ClassListPreviewerModel.prototype = {
|
||||
destroy() {
|
||||
this.inspector.off("markupmutation", this.onMutations);
|
||||
this.inspector = null;
|
||||
this.classListProxyNode = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* The current node selection (which only returns if the node is an ELEMENT_NODE type
|
||||
* since that's the only type this model can work with.)
|
||||
*/
|
||||
get currentNode() {
|
||||
if (this.inspector.selection.isElementNode() &&
|
||||
!this.inspector.selection.isPseudoElementNode()) {
|
||||
return this.inspector.selection.nodeFront;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* The class states for the current node selection. See the documentation of the CLASSES
|
||||
* constant.
|
||||
*/
|
||||
get currentClasses() {
|
||||
if (!this.currentNode) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!CLASSES.has(this.currentNode)) {
|
||||
// Use the proxy node to get a clean list of classes.
|
||||
this.classListProxyNode.className = this.currentNode.className;
|
||||
let nodeClasses = [...new Set([...this.classListProxyNode.classList])].map(name => {
|
||||
return { name, isApplied: true };
|
||||
});
|
||||
|
||||
CLASSES.set(this.currentNode, nodeClasses);
|
||||
}
|
||||
|
||||
return CLASSES.get(this.currentNode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Same as currentClasses, but returns it in the form of a className string, where only
|
||||
* enabled classes are added.
|
||||
*/
|
||||
get currentClassesPreview() {
|
||||
return this.currentClasses.filter(({ isApplied }) => isApplied)
|
||||
.map(({ name }) => name)
|
||||
.join(" ");
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the state for a given class on the current node.
|
||||
*
|
||||
* @param {String} name
|
||||
* The class which state should be changed.
|
||||
* @param {Boolean} isApplied
|
||||
* True if the class should be enabled, false otherwise.
|
||||
* @return {Promise} Resolves when the change has been made in the DOM.
|
||||
*/
|
||||
setClassState(name, isApplied) {
|
||||
// Do the change in our local model.
|
||||
let nodeClasses = this.currentClasses;
|
||||
nodeClasses.find(({ name: cName }) => cName === name).isApplied = isApplied;
|
||||
|
||||
return this.applyClassState();
|
||||
},
|
||||
|
||||
/**
|
||||
* Add several classes to the current node at once.
|
||||
*
|
||||
* @param {String} classNameString
|
||||
* The string that contains all classes.
|
||||
* @return {Promise} Resolves when the change has been made in the DOM.
|
||||
*/
|
||||
addClassName(classNameString) {
|
||||
this.classListProxyNode.className = classNameString;
|
||||
return Promise.all([...new Set([...this.classListProxyNode.classList])].map(name => {
|
||||
return this.addClass(name);
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a class to the current node at once.
|
||||
*
|
||||
* @param {String} name
|
||||
* The class to be added.
|
||||
* @return {Promise} Resolves when the change has been made in the DOM.
|
||||
*/
|
||||
addClass(name) {
|
||||
// Avoid adding the same class again.
|
||||
if (this.currentClasses.some(({ name: cName }) => cName === name)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Change the local model, so we retain the state of the existing classes.
|
||||
this.currentClasses.push({ name, isApplied: true });
|
||||
|
||||
return this.applyClassState();
|
||||
},
|
||||
|
||||
/**
|
||||
* Used internally by other functions like addClass or setClassState. Actually applies
|
||||
* the class change to the DOM.
|
||||
*
|
||||
* @return {Promise} Resolves when the change has been made in the DOM.
|
||||
*/
|
||||
applyClassState() {
|
||||
// If there is no valid inspector selection, bail out silently. No need to report an
|
||||
// error here.
|
||||
if (!this.currentNode) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Remember which node we changed and the className we applied, so we can filter out
|
||||
// dom mutations that are caused by us in onMutations.
|
||||
this.lastStateChange = {
|
||||
node: this.currentNode,
|
||||
className: this.currentClassesPreview
|
||||
};
|
||||
|
||||
// Apply the change to the node.
|
||||
let mod = this.currentNode.startModifyingAttributes();
|
||||
mod.setAttribute("class", this.currentClassesPreview);
|
||||
return mod.apply();
|
||||
},
|
||||
|
||||
onMutations(e, mutations) {
|
||||
for (let {type, target, attributeName} of mutations) {
|
||||
// Only care if this mutation is for the class attribute.
|
||||
if (type !== "attributes" || attributeName !== "class") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let isMutationForOurChange = this.lastStateChange &&
|
||||
target === this.lastStateChange.node &&
|
||||
target.className === this.lastStateChange.className;
|
||||
|
||||
if (!isMutationForOurChange) {
|
||||
CLASSES.delete(target);
|
||||
if (target === this.currentNode) {
|
||||
this.emit("current-node-class-changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This UI widget shows a textfield and a series of checkboxes in the rule-view. It is
|
||||
* used to toggle classes on the current node selection, and add new classes.
|
||||
*
|
||||
* @param {Inspector} inspector
|
||||
* The current inspector instance.
|
||||
* @param {DomNode} containerEl
|
||||
* The element in the rule-view where the widget should go.
|
||||
*/
|
||||
function ClassListPreviewer(inspector, containerEl) {
|
||||
this.inspector = inspector;
|
||||
this.containerEl = containerEl;
|
||||
this.model = new ClassListPreviewerModel(inspector);
|
||||
|
||||
this.onNewSelection = this.onNewSelection.bind(this);
|
||||
this.onCheckBoxChanged = this.onCheckBoxChanged.bind(this);
|
||||
this.onKeyPress = this.onKeyPress.bind(this);
|
||||
this.onCurrentNodeClassChanged = this.onCurrentNodeClassChanged.bind(this);
|
||||
|
||||
// Create the add class text field.
|
||||
this.addEl = this.doc.createElement("input");
|
||||
this.addEl.classList.add("devtools-textinput");
|
||||
this.addEl.classList.add("add-class");
|
||||
this.addEl.setAttribute("placeholder",
|
||||
L10N.getStr("inspector.classPanel.newClass.placeholder"));
|
||||
this.addEl.addEventListener("keypress", this.onKeyPress);
|
||||
this.containerEl.appendChild(this.addEl);
|
||||
|
||||
// Create the class checkboxes container.
|
||||
this.classesEl = this.doc.createElement("div");
|
||||
this.classesEl.classList.add("classes");
|
||||
this.containerEl.appendChild(this.classesEl);
|
||||
|
||||
// Start listening for interesting events.
|
||||
this.inspector.selection.on("new-node-front", this.onNewSelection);
|
||||
this.containerEl.addEventListener("input", this.onCheckBoxChanged);
|
||||
this.model.on("current-node-class-changed", this.onCurrentNodeClassChanged);
|
||||
}
|
||||
|
||||
ClassListPreviewer.prototype = {
|
||||
destroy() {
|
||||
this.inspector.selection.off("new-node-front", this.onNewSelection);
|
||||
this.addEl.removeEventListener("keypress", this.onKeyPress);
|
||||
this.containerEl.removeEventListener("input", this.onCheckBoxChanged);
|
||||
|
||||
this.containerEl.innerHTML = "";
|
||||
|
||||
this.model.destroy();
|
||||
this.containerEl = null;
|
||||
this.inspector = null;
|
||||
this.addEl = null;
|
||||
this.classesEl = null;
|
||||
},
|
||||
|
||||
get doc() {
|
||||
return this.containerEl.ownerDocument;
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the content of the panel. You typically don't need to call this as the panel
|
||||
* renders itself on inspector selection changes.
|
||||
*/
|
||||
render() {
|
||||
this.classesEl.innerHTML = "";
|
||||
|
||||
for (let { name, isApplied } of this.model.currentClasses) {
|
||||
let checkBox = this.renderCheckBox(name, isApplied);
|
||||
this.classesEl.appendChild(checkBox);
|
||||
}
|
||||
|
||||
if (!this.model.currentClasses.length) {
|
||||
this.classesEl.appendChild(this.renderNoClassesMessage());
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Render a single checkbox for a given classname.
|
||||
*
|
||||
* @param {String} name
|
||||
* The name of this class.
|
||||
* @param {Boolean} isApplied
|
||||
* Is this class currently applied on the DOM node.
|
||||
* @return {DOMNode} The DOM element for this checkbox.
|
||||
*/
|
||||
renderCheckBox(name, isApplied) {
|
||||
let box = this.doc.createElement("input");
|
||||
box.setAttribute("type", "checkbox");
|
||||
if (isApplied) {
|
||||
box.setAttribute("checked", "checked");
|
||||
}
|
||||
box.dataset.name = name;
|
||||
|
||||
let labelWrapper = this.doc.createElement("label");
|
||||
labelWrapper.setAttribute("title", name);
|
||||
labelWrapper.appendChild(box);
|
||||
|
||||
// A child element is required to do the ellipsis.
|
||||
let label = this.doc.createElement("span");
|
||||
label.textContent = name;
|
||||
labelWrapper.appendChild(label);
|
||||
|
||||
return labelWrapper;
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the message displayed in the panel when the current element has no classes.
|
||||
*
|
||||
* @return {DOMNode} The DOM element for the message.
|
||||
*/
|
||||
renderNoClassesMessage() {
|
||||
let msg = this.doc.createElement("p");
|
||||
msg.classList.add("no-classes");
|
||||
msg.textContent = L10N.getStr("inspector.classPanel.noClasses");
|
||||
return msg;
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus the add-class text field.
|
||||
*/
|
||||
focusAddClassField() {
|
||||
if (this.addEl) {
|
||||
this.addEl.focus();
|
||||
}
|
||||
},
|
||||
|
||||
onCheckBoxChanged({ target }) {
|
||||
if (!target.dataset.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.model.setClassState(target.dataset.name, target.checked).catch(e => {
|
||||
// Only log the error if the panel wasn't destroyed in the meantime.
|
||||
if (this.containerEl) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onKeyPress(event) {
|
||||
if (event.key !== "Enter" || this.addEl.value === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
this.model.addClassName(this.addEl.value).then(() => {
|
||||
this.render();
|
||||
this.addEl.value = "";
|
||||
}).catch(e => {
|
||||
// Only log the error if the panel wasn't destroyed in the meantime.
|
||||
if (this.containerEl) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onNewSelection() {
|
||||
this.render();
|
||||
},
|
||||
|
||||
onCurrentNodeClassChanged() {
|
||||
this.render();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ClassListPreviewer;
|
@ -3,6 +3,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'class-list-previewer.js',
|
||||
'rule-editor.js',
|
||||
'text-property-editor.js',
|
||||
)
|
||||
|
@ -377,6 +377,19 @@ inspector.addRule.tooltip=Add new rule
|
||||
# rule view toolbar.
|
||||
inspector.togglePseudo.tooltip=Toggle pseudo-classes
|
||||
|
||||
# LOCALIZATION NOTE (inspector.classPanel.toggleClass.tooltip): This is the tooltip
|
||||
# shown when hovering over the `Toggle Class Panel` button in the
|
||||
# rule view toolbar.
|
||||
inspector.classPanel.toggleClass.tooltip=Toggle classes
|
||||
|
||||
# LOCALIZATION NOTE (inspector.classPanel.newClass.placeholder): This is the placeholder
|
||||
# shown inside the text field used to add a new class in the rule-view.
|
||||
inspector.classPanel.newClass.placeholder=Add new class
|
||||
|
||||
# LOCALIZATION NOTE (inspector.classPanel.noClasses): This is the text displayed in the
|
||||
# class panel when the current element has no classes applied.
|
||||
inspector.classPanel.noClasses=No classes on this element
|
||||
|
||||
# LOCALIZATION NOTE (inspector.noProperties): In the case where there are no CSS
|
||||
# properties to display e.g. due to search criteria this message is
|
||||
# displayed.
|
||||
|
@ -82,7 +82,7 @@ const getDisplayedRequestsSummary = createSelector(
|
||||
totals.contentSize += item.contentSize;
|
||||
}
|
||||
|
||||
if (typeof item.transferredSize == "number") {
|
||||
if (typeof item.transferredSize == "number" && !item.fromCache) {
|
||||
totals.transferredSize += item.transferredSize;
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,11 @@ function onLearnMoreClick(e, url) {
|
||||
e.preventDefault();
|
||||
|
||||
let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
|
||||
win.openUILinkIn(url, "tab");
|
||||
if (e.button === 1) {
|
||||
win.openUILinkIn(url, "tabshifted");
|
||||
} else {
|
||||
win.openUILinkIn(url, "tab");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MDNLink;
|
||||
|
@ -106,6 +106,7 @@ add_task(function* () {
|
||||
let queryFocus = once(query, "focus", false);
|
||||
// Bug 1195825: Due to some unexplained dark-matter with promise,
|
||||
// focus only works if delayed by one tick.
|
||||
query.setSelectionRange(query.value.length, query.value.length);
|
||||
executeSoon(() => query.focus());
|
||||
yield queryFocus;
|
||||
|
||||
@ -115,6 +116,7 @@ add_task(function* () {
|
||||
|
||||
let headers = document.getElementById("custom-headers-value");
|
||||
let headersFocus = once(headers, "focus", false);
|
||||
headers.setSelectionRange(headers.value.length, headers.value.length);
|
||||
headers.focus();
|
||||
yield headersFocus;
|
||||
|
||||
@ -129,6 +131,7 @@ add_task(function* () {
|
||||
|
||||
let postData = document.getElementById("custom-postdata-value");
|
||||
let postFocus = once(postData, "focus", false);
|
||||
postData.setSelectionRange(postData.value.length, postData.value.length);
|
||||
postData.focus();
|
||||
yield postFocus;
|
||||
|
||||
|
@ -85,6 +85,7 @@
|
||||
|
||||
/* Learn More link */
|
||||
.treeTable .treeValueCell .learn-more-link {
|
||||
-moz-user-select: none;
|
||||
color: var(--theme-highlight-blue);
|
||||
cursor: pointer;
|
||||
margin: 0 5px;
|
||||
|
@ -50,24 +50,71 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#pseudo-class-panel {
|
||||
.ruleview-reveal-panel {
|
||||
display: flex;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
transition: height 150ms ease;
|
||||
}
|
||||
|
||||
#pseudo-class-panel[hidden] {
|
||||
.ruleview-reveal-panel[hidden] {
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
#pseudo-class-panel > label {
|
||||
#pseudo-class-panel:not([hidden]) {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.ruleview-reveal-panel label {
|
||||
-moz-user-select: none;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Class toggle panel */
|
||||
#ruleview-class-panel:not([hidden]) {
|
||||
/* The class panel can contain 0 to N classes, so we can't hardcode a height here like
|
||||
we do for the pseudo-class panel. Unfortunately, that means we don't get the height
|
||||
transition when toggling the panel */
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#ruleview-class-panel .add-class {
|
||||
margin: 0;
|
||||
border-width: 0 0 1px 0;
|
||||
padding: 2px 6px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#ruleview-class-panel .classes {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#ruleview-class-panel .classes {
|
||||
max-height: 100px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#ruleview-class-panel .classes label {
|
||||
flex: 0 0;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
#ruleview-class-panel .classes label span {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#ruleview-class-panel .no-classes {
|
||||
flex: 1;
|
||||
color: var(--theme-body-color-inactive);
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Rule View Container */
|
||||
|
||||
#ruleview-container {
|
||||
@ -559,6 +606,12 @@
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#class-panel-toggle::before {
|
||||
content: ".cls";
|
||||
direction: ltr;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.ruleview-overridden-rule-filter {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "BatteryManager.h"
|
||||
#include "mozilla/dom/DeviceStorageAreaListener.h"
|
||||
#include "mozilla/dom/GamepadServiceTest.h"
|
||||
#include "mozilla/dom/PowerManager.h"
|
||||
#include "mozilla/dom/WakeLock.h"
|
||||
@ -68,7 +67,6 @@
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "TimeManager.h"
|
||||
#include "DeviceStorage.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "WidgetUtils.h"
|
||||
#include "nsIPresentationService.h"
|
||||
@ -219,7 +217,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageAreaListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDisplaysPromises)
|
||||
@ -281,15 +278,6 @@ Navigator::Invalidate()
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t len = mDeviceStorageStores.Length();
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
RefPtr<nsDOMDeviceStorage> ds = do_QueryReferent(mDeviceStorageStores[i]);
|
||||
if (ds) {
|
||||
ds->Shutdown();
|
||||
}
|
||||
}
|
||||
mDeviceStorageStores.Clear();
|
||||
|
||||
if (mTimeManager) {
|
||||
mTimeManager = nullptr;
|
||||
}
|
||||
@ -305,10 +293,6 @@ Navigator::Invalidate()
|
||||
mMediaKeySystemAccessManager = nullptr;
|
||||
}
|
||||
|
||||
if (mDeviceStorageAreaListener) {
|
||||
mDeviceStorageAreaListener = nullptr;
|
||||
}
|
||||
|
||||
if (mGamepadServiceTest) {
|
||||
mGamepadServiceTest->Shutdown();
|
||||
mGamepadServiceTest = nullptr;
|
||||
@ -1038,126 +1022,6 @@ Navigator::RegisterProtocolHandler(const nsAString& aProtocol,
|
||||
mWindow->GetOuterWindow());
|
||||
}
|
||||
|
||||
DeviceStorageAreaListener*
|
||||
Navigator::GetDeviceStorageAreaListener(ErrorResult& aRv)
|
||||
{
|
||||
if (!mDeviceStorageAreaListener) {
|
||||
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
mDeviceStorageAreaListener = new DeviceStorageAreaListener(mWindow);
|
||||
}
|
||||
|
||||
return mDeviceStorageAreaListener;
|
||||
}
|
||||
|
||||
already_AddRefed<nsDOMDeviceStorage>
|
||||
Navigator::FindDeviceStorage(const nsAString& aName, const nsAString& aType)
|
||||
{
|
||||
auto i = mDeviceStorageStores.Length();
|
||||
while (i > 0) {
|
||||
--i;
|
||||
RefPtr<nsDOMDeviceStorage> storage =
|
||||
do_QueryReferent(mDeviceStorageStores[i]);
|
||||
if (storage) {
|
||||
if (storage->Equals(mWindow, aName, aType)) {
|
||||
return storage.forget();
|
||||
}
|
||||
} else {
|
||||
mDeviceStorageStores.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsDOMDeviceStorage>
|
||||
Navigator::GetDeviceStorage(const nsAString& aType, ErrorResult& aRv)
|
||||
{
|
||||
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsString name;
|
||||
nsDOMDeviceStorage::GetDefaultStorageName(aType, name);
|
||||
RefPtr<nsDOMDeviceStorage> storage = FindDeviceStorage(name, aType);
|
||||
if (storage) {
|
||||
return storage.forget();
|
||||
}
|
||||
|
||||
nsDOMDeviceStorage::CreateDeviceStorageFor(mWindow, aType,
|
||||
getter_AddRefs(storage));
|
||||
|
||||
if (!storage) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mDeviceStorageStores.AppendElement(
|
||||
do_GetWeakReference(static_cast<DOMEventTargetHelper*>(storage)));
|
||||
return storage.forget();
|
||||
}
|
||||
|
||||
void
|
||||
Navigator::GetDeviceStorages(const nsAString& aType,
|
||||
nsTArray<RefPtr<nsDOMDeviceStorage> >& aStores,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsDOMDeviceStorage::VolumeNameArray volumes;
|
||||
nsDOMDeviceStorage::GetOrderedVolumeNames(aType, volumes);
|
||||
if (volumes.IsEmpty()) {
|
||||
RefPtr<nsDOMDeviceStorage> storage = GetDeviceStorage(aType, aRv);
|
||||
if (storage) {
|
||||
aStores.AppendElement(storage.forget());
|
||||
}
|
||||
} else {
|
||||
uint32_t len = volumes.Length();
|
||||
aStores.SetCapacity(len);
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
RefPtr<nsDOMDeviceStorage> storage =
|
||||
GetDeviceStorageByNameAndType(volumes[i], aType, aRv);
|
||||
if (aRv.Failed()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (storage) {
|
||||
aStores.AppendElement(storage.forget());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<nsDOMDeviceStorage>
|
||||
Navigator::GetDeviceStorageByNameAndType(const nsAString& aName,
|
||||
const nsAString& aType,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<nsDOMDeviceStorage> storage = FindDeviceStorage(aName, aType);
|
||||
if (storage) {
|
||||
return storage.forget();
|
||||
}
|
||||
nsDOMDeviceStorage::CreateDeviceStorageByNameAndType(mWindow, aName, aType,
|
||||
getter_AddRefs(storage));
|
||||
|
||||
if (!storage) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mDeviceStorageStores.AppendElement(
|
||||
do_GetWeakReference(static_cast<DOMEventTargetHelper*>(storage)));
|
||||
return storage.forget();
|
||||
}
|
||||
|
||||
Geolocation*
|
||||
Navigator::GetGeolocation(ErrorResult& aRv)
|
||||
{
|
||||
|
@ -26,7 +26,6 @@ class nsPluginArray;
|
||||
class nsMimeTypeArray;
|
||||
class nsPIDOMWindowInner;
|
||||
class nsIDOMNavigatorSystemMessages;
|
||||
class nsDOMDeviceStorage;
|
||||
class nsIPrincipal;
|
||||
class nsIURI;
|
||||
|
||||
@ -75,7 +74,6 @@ class Connection;
|
||||
} // namespace network
|
||||
|
||||
class PowerManager;
|
||||
class DeviceStorageAreaListener;
|
||||
class Presentation;
|
||||
class LegacyMozTCPSocket;
|
||||
class VRDisplay;
|
||||
@ -196,18 +194,6 @@ public:
|
||||
void RemoveIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
|
||||
already_AddRefed<WakeLock> RequestWakeLock(const nsAString &aTopic,
|
||||
ErrorResult& aRv);
|
||||
DeviceStorageAreaListener* GetDeviceStorageAreaListener(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<nsDOMDeviceStorage> GetDeviceStorage(const nsAString& aType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void GetDeviceStorages(const nsAString& aType,
|
||||
nsTArray<RefPtr<nsDOMDeviceStorage> >& aStores,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<nsDOMDeviceStorage>
|
||||
GetDeviceStorageByNameAndType(const nsAString& aName, const nsAString& aType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
DesktopNotificationCenter* GetMozNotification(ErrorResult& aRv);
|
||||
already_AddRefed<LegacyMozTCPSocket> MozTCPSocket();
|
||||
@ -293,9 +279,6 @@ private:
|
||||
bool CheckPermission(const char* type);
|
||||
static bool CheckPermission(nsPIDOMWindowInner* aWindow, const char* aType);
|
||||
|
||||
already_AddRefed<nsDOMDeviceStorage> FindDeviceStorage(const nsAString& aName,
|
||||
const nsAString& aType);
|
||||
|
||||
// This enum helps SendBeaconInternal to apply different behaviors to body
|
||||
// types.
|
||||
enum BeaconType {
|
||||
@ -323,11 +306,9 @@ private:
|
||||
RefPtr<system::AudioChannelManager> mAudioChannelManager;
|
||||
#endif
|
||||
RefPtr<MediaDevices> mMediaDevices;
|
||||
nsTArray<nsWeakPtr> mDeviceStorageStores;
|
||||
RefPtr<time::TimeManager> mTimeManager;
|
||||
RefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
RefPtr<DeviceStorageAreaListener> mDeviceStorageAreaListener;
|
||||
RefPtr<Presentation> mPresentation;
|
||||
RefPtr<GamepadServiceTest> mGamepadServiceTest;
|
||||
nsTArray<RefPtr<Promise> > mVRGetDisplaysPromises;
|
||||
|
@ -1833,12 +1833,9 @@ nsINode::Remove()
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
int32_t index = parent->IndexOf(this);
|
||||
if (index < 0) {
|
||||
NS_WARNING("Ignoring call to nsINode::Remove on anonymous child.");
|
||||
return;
|
||||
}
|
||||
parent->RemoveChildAt(uint32_t(index), true);
|
||||
|
||||
IgnoredErrorResult err;
|
||||
parent->RemoveChild(*this, err);
|
||||
}
|
||||
|
||||
Element*
|
||||
|
@ -307,11 +307,6 @@ DOMInterfaces = {
|
||||
'headerFile': 'mozilla/dom/DeviceMotionEvent.h',
|
||||
},
|
||||
|
||||
'DeviceStorage': {
|
||||
'nativeType': 'nsDOMDeviceStorage',
|
||||
'headerFile': 'DeviceStorage.h',
|
||||
},
|
||||
|
||||
'Document': {
|
||||
'nativeType': 'nsIDocument',
|
||||
},
|
||||
|
@ -1,382 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 DeviceStorage_h
|
||||
#define DeviceStorage_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/dom/DOMRequest.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
#define DEVICESTORAGE_PICTURES "pictures"
|
||||
#define DEVICESTORAGE_VIDEOS "videos"
|
||||
#define DEVICESTORAGE_MUSIC "music"
|
||||
#define DEVICESTORAGE_APPS "apps"
|
||||
#define DEVICESTORAGE_SDCARD "sdcard"
|
||||
#define DEVICESTORAGE_CRASHES "crashes"
|
||||
|
||||
class nsIInputStream;
|
||||
class nsIOutputStream;
|
||||
struct DeviceStorageFileDescriptor;
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
class nsIVolume;
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
class EventListenerManager;
|
||||
namespace dom {
|
||||
class Blob;
|
||||
struct DeviceStorageEnumerationParameters;
|
||||
class DOMCursor;
|
||||
class DOMRequest;
|
||||
class Promise;
|
||||
class DeviceStorageFileSystem;
|
||||
} // namespace dom
|
||||
namespace ipc {
|
||||
class FileDescriptor;
|
||||
class PrincipalInfo;
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
class DeviceStorageRequest;
|
||||
class DeviceStorageCursorRequest;
|
||||
class DeviceStorageRequestManager;
|
||||
class nsDOMDeviceStorageCursor;
|
||||
|
||||
class DeviceStorageFile final
|
||||
: public nsISupports {
|
||||
public:
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
nsString mStorageType;
|
||||
nsString mStorageName;
|
||||
nsString mRootDir;
|
||||
nsString mPath;
|
||||
bool mEditable;
|
||||
nsString mMimeType;
|
||||
uint64_t mLength;
|
||||
uint64_t mLastModifiedDate;
|
||||
|
||||
// Used when the path will be set later via SetPath.
|
||||
DeviceStorageFile(const nsAString& aStorageType,
|
||||
const nsAString& aStorageName);
|
||||
// Used for non-enumeration purposes.
|
||||
DeviceStorageFile(const nsAString& aStorageType,
|
||||
const nsAString& aStorageName,
|
||||
const nsAString& aPath);
|
||||
// Used for enumerations. When you call Enumerate, you can pass in a
|
||||
// directory to enumerate and the results that are returned are relative to
|
||||
// that directory, files related to an enumeration need to know the "root of
|
||||
// the enumeration" directory.
|
||||
DeviceStorageFile(const nsAString& aStorageType,
|
||||
const nsAString& aStorageName,
|
||||
const nsAString& aRootDir,
|
||||
const nsAString& aPath);
|
||||
|
||||
void SetPath(const nsAString& aPath);
|
||||
void SetEditable(bool aEditable);
|
||||
|
||||
static already_AddRefed<DeviceStorageFile>
|
||||
CreateUnique(nsAString& aFileName,
|
||||
uint32_t aFileType,
|
||||
uint32_t aFileAttributes);
|
||||
|
||||
static already_AddRefed<DeviceStorageFile>
|
||||
CreateUnique(const nsAString& aStorageType,
|
||||
const nsAString& aStorageName,
|
||||
nsAString& aFileName,
|
||||
uint32_t aFileType,
|
||||
uint32_t aFileAttributes);
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
bool IsAvailable();
|
||||
void GetFullPath(nsAString& aFullPath);
|
||||
|
||||
// we want to make sure that the names of file can't reach
|
||||
// outside of the type of storage the user asked for.
|
||||
bool IsSafePath() const;
|
||||
bool ValidateAndSplitPath(const nsAString& aPath,
|
||||
nsTArray<nsString>* aParts = nullptr) const;
|
||||
|
||||
void Dump(const char* label);
|
||||
|
||||
nsresult Remove();
|
||||
nsresult Write(nsIInputStream* aInputStream);
|
||||
nsresult Write(InfallibleTArray<uint8_t>& bits);
|
||||
nsresult Append(nsIInputStream* aInputStream);
|
||||
nsresult Append(nsIInputStream* aInputStream,
|
||||
nsIOutputStream* aOutputStream);
|
||||
void CollectFiles(nsTArray<RefPtr<DeviceStorageFile> >& aFiles,
|
||||
PRTime aSince = 0);
|
||||
void collectFilesInternal(nsTArray<RefPtr<DeviceStorageFile> >& aFiles,
|
||||
PRTime aSince, nsAString& aRootPath);
|
||||
|
||||
void AccumDiskUsage(uint64_t* aPicturesSoFar, uint64_t* aVideosSoFar,
|
||||
uint64_t* aMusicSoFar, uint64_t* aTotalSoFar);
|
||||
|
||||
void GetStorageFreeSpace(int64_t* aSoFar);
|
||||
void GetStatus(nsAString& aStatus);
|
||||
void GetStorageStatus(nsAString& aStatus);
|
||||
void DoFormat(nsAString& aStatus);
|
||||
void DoMount(nsAString& aStatus);
|
||||
void DoUnmount(nsAString& aStatus);
|
||||
static void GetRootDirectoryForType(const nsAString& aStorageType,
|
||||
const nsAString& aStorageName,
|
||||
nsIFile** aFile);
|
||||
|
||||
nsresult CalculateSizeAndModifiedDate();
|
||||
nsresult CalculateMimeType();
|
||||
nsresult CreateFileDescriptor(mozilla::ipc::FileDescriptor& aFileDescriptor);
|
||||
|
||||
private:
|
||||
~DeviceStorageFile() {}
|
||||
void Init();
|
||||
void AppendRelativePath(const nsAString& aPath);
|
||||
void AccumDirectoryUsage(nsIFile* aFile,
|
||||
uint64_t* aPicturesSoFar,
|
||||
uint64_t* aVideosSoFar,
|
||||
uint64_t* aMusicSoFar,
|
||||
uint64_t* aTotalSoFar);
|
||||
};
|
||||
|
||||
#define NS_DOM_DEVICE_STORAGE_CID \
|
||||
{ 0xe4a9b969, 0x81fe, 0x44f1, \
|
||||
{ 0xaa, 0x0c, 0x9e, 0x16, 0x64, 0x86, 0x2a, 0xd5 } }
|
||||
|
||||
class nsDOMDeviceStorage final
|
||||
: public mozilla::DOMEventTargetHelper
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
typedef mozilla::ErrorResult ErrorResult;
|
||||
typedef mozilla::dom::DeviceStorageEnumerationParameters
|
||||
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;
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_DEVICE_STORAGE_CID)
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
|
||||
|
||||
void EventListenerWasAdded(const nsAString& aType,
|
||||
ErrorResult& aRv,
|
||||
JSCompartment* aCompartment) override;
|
||||
|
||||
explicit nsDOMDeviceStorage(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
static int InstanceCount() { return sInstanceCount; }
|
||||
|
||||
static void InvalidateVolumeCaches();
|
||||
|
||||
nsresult Init(nsPIDOMWindowInner* aWindow, const nsAString& aType,
|
||||
const nsAString& aVolName);
|
||||
|
||||
bool IsAvailable();
|
||||
bool IsFullPath(const nsAString& aPath)
|
||||
{
|
||||
return aPath.Length() > 0 && aPath.CharAt(0) == '/';
|
||||
}
|
||||
|
||||
void SetRootDirectoryForType(const nsAString& aType,
|
||||
const nsAString& aVolName);
|
||||
|
||||
// WebIDL
|
||||
nsPIDOMWindowInner*
|
||||
GetParentObject() const
|
||||
{
|
||||
return GetOwner();
|
||||
}
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
IMPL_EVENT_HANDLER(change)
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
Add(mozilla::dom::Blob* aBlob, ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
AddNamed(mozilla::dom::Blob* aBlob, const nsAString& aPath, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
AppendNamed(mozilla::dom::Blob* aBlob, const nsAString& aPath,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
Get(const nsAString& aPath, ErrorResult& aRv)
|
||||
{
|
||||
return GetInternal(aPath, false, aRv);
|
||||
}
|
||||
already_AddRefed<DOMRequest>
|
||||
GetEditable(const nsAString& aPath, ErrorResult& aRv)
|
||||
{
|
||||
return GetInternal(aPath, true, aRv);
|
||||
}
|
||||
already_AddRefed<DOMRequest>
|
||||
Delete(const nsAString& aPath, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMCursor>
|
||||
Enumerate(const EnumerationParameters& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return Enumerate(NullString(), aOptions, aRv);
|
||||
}
|
||||
already_AddRefed<DOMCursor>
|
||||
Enumerate(const nsAString& aPath, const EnumerationParameters& aOptions,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<DOMCursor>
|
||||
EnumerateEditable(const EnumerationParameters& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
return EnumerateEditable(NullString(), aOptions, aRv);
|
||||
}
|
||||
already_AddRefed<DOMCursor>
|
||||
EnumerateEditable(const nsAString& aPath,
|
||||
const EnumerationParameters& aOptions, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest> FreeSpace(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> UsedSpace(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> Available(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> Format(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> StorageStatus(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> Mount(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> Unmount(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest> CreateFileDescriptor(const nsAString& aPath,
|
||||
DeviceStorageFileDescriptor* aDSFD,
|
||||
ErrorResult& aRv);
|
||||
|
||||
bool CanBeMounted();
|
||||
bool CanBeFormatted();
|
||||
bool CanBeShared();
|
||||
bool IsRemovable();
|
||||
bool LowDiskSpace();
|
||||
bool Default();
|
||||
void GetStorageName(nsAString& aStorageName);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetRoot(ErrorResult& aRv);
|
||||
|
||||
static void
|
||||
CreateDeviceStorageFor(nsPIDOMWindowInner* aWin,
|
||||
const nsAString& aType,
|
||||
nsDOMDeviceStorage** aStore);
|
||||
|
||||
static void
|
||||
CreateDeviceStorageByNameAndType(nsPIDOMWindowInner* aWin,
|
||||
const nsAString& aName,
|
||||
const nsAString& aType,
|
||||
nsDOMDeviceStorage** aStore);
|
||||
|
||||
bool Equals(nsPIDOMWindowInner* aWin,
|
||||
const nsAString& aName,
|
||||
const nsAString& aType);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
static void GetOrderedVolumeNames(const nsAString& aType,
|
||||
nsTArray<nsString>& aVolumeNames);
|
||||
|
||||
static void GetOrderedVolumeNames(nsTArray<nsString>& aVolumeNames);
|
||||
|
||||
static void GetDefaultStorageName(const nsAString& aStorageType,
|
||||
nsAString& aStorageName);
|
||||
|
||||
static bool ParseFullPath(const nsAString& aFullPath,
|
||||
nsAString& aOutStorageName,
|
||||
nsAString& aOutStoragePath);
|
||||
|
||||
// DeviceStorageStatics callbacks
|
||||
void OnFileWatcherUpdate(const nsCString& aData, DeviceStorageFile* aFile);
|
||||
void OnDiskSpaceWatcher(bool aLowDiskSpace);
|
||||
void OnWritableNameChanged();
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
void OnVolumeStateChanged(nsIVolume* aVolume);
|
||||
#endif
|
||||
|
||||
uint32_t CreateDOMRequest(DOMRequest** aRequest, ErrorResult& aRv);
|
||||
uint32_t CreateDOMCursor(DeviceStorageCursorRequest* aRequest,
|
||||
nsDOMDeviceStorageCursor** aCursor,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> CreateAndRejectDOMRequest(const char *aReason,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsresult CheckPermission(already_AddRefed<DeviceStorageRequest>&& aRequest);
|
||||
|
||||
bool IsOwningThread();
|
||||
nsresult DispatchToOwningThread(already_AddRefed<nsIRunnable>&& aRunnable);
|
||||
|
||||
private:
|
||||
~nsDOMDeviceStorage();
|
||||
|
||||
static nsresult CheckPrincipal(nsPIDOMWindowInner* aWindow,
|
||||
bool aIsAppsStorage,
|
||||
nsIPrincipal** aPrincipal);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
AddOrAppendNamed(mozilla::dom::Blob* aBlob, const nsAString& aPath,
|
||||
bool aCreate, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
GetInternal(const nsAString& aPath, bool aEditable, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
DeleteInternal(nsPIDOMWindowInner* aWin, const nsAString& aPath,
|
||||
DOMRequest* aRequest);
|
||||
|
||||
already_AddRefed<DOMCursor>
|
||||
EnumerateInternal(const nsAString& aName,
|
||||
const EnumerationParameters& aOptions, bool aEditable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static int sInstanceCount;
|
||||
|
||||
nsString mStorageType;
|
||||
nsCOMPtr<nsIFile> mRootDirectory;
|
||||
nsString mStorageName;
|
||||
bool mIsShareable;
|
||||
bool mIsRemovable;
|
||||
|
||||
already_AddRefed<nsDOMDeviceStorage> GetStorage(const nsAString& aFullPath,
|
||||
nsAString& aOutStoragePath);
|
||||
already_AddRefed<nsDOMDeviceStorage>
|
||||
GetStorageByName(const nsAString &aStorageName);
|
||||
|
||||
static already_AddRefed<nsDOMDeviceStorage>
|
||||
GetStorageByNameAndType(nsPIDOMWindowInner* aWin,
|
||||
const nsAString& aStorageName,
|
||||
const nsAString& aType);
|
||||
|
||||
bool mIsDefaultLocation;
|
||||
|
||||
nsresult Notify(const char* aReason, class DeviceStorageFile* aFile);
|
||||
|
||||
friend class WatchFileEvent;
|
||||
friend class DeviceStorageRequest;
|
||||
|
||||
static mozilla::StaticAutoPtr<nsTArray<nsString>> sVolumeNameCache;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsString mLastStatus;
|
||||
nsString mLastStorageStatus;
|
||||
void DispatchStatusChangeEvent(nsAString& aStatus);
|
||||
void DispatchStorageStatusChangeEvent(nsAString& aStorageStatus);
|
||||
#endif
|
||||
|
||||
uint64_t mInnerWindowID;
|
||||
RefPtr<DeviceStorageFileSystem> mFileSystem;
|
||||
RefPtr<DeviceStorageRequestManager> mManager;
|
||||
nsAutoPtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
|
||||
nsCOMPtr<nsIThread> mOwningThread;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMDeviceStorage, NS_DOM_DEVICE_STORAGE_CID)
|
||||
|
||||
#endif
|
@ -1,154 +0,0 @@
|
||||
/* -*- 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/DeviceStorageAreaListener.h"
|
||||
#include "mozilla/dom/DeviceStorageAreaListenerBinding.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "DeviceStorage.h"
|
||||
#include "nsIObserverService.h"
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsIVolume.h"
|
||||
#include "nsIVolumeService.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class VolumeStateObserver final : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
explicit VolumeStateObserver(DeviceStorageAreaListener* aListener)
|
||||
: mDeviceStorageAreaListener(aListener) {}
|
||||
void ForgetListener() { mDeviceStorageAreaListener = nullptr; }
|
||||
|
||||
private:
|
||||
~VolumeStateObserver() {};
|
||||
|
||||
// This reference is non-owning and it's cleared by
|
||||
// DeviceStorageAreaListener's destructor.
|
||||
DeviceStorageAreaListener* MOZ_NON_OWNING_REF mDeviceStorageAreaListener;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(VolumeStateObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
VolumeStateObserver::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
const char16_t *aData)
|
||||
{
|
||||
if (!mDeviceStorageAreaListener) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
|
||||
nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
|
||||
MOZ_ASSERT(vol);
|
||||
|
||||
int32_t state;
|
||||
nsresult rv = vol->GetState(&state);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsString volName;
|
||||
vol->GetName(volName);
|
||||
|
||||
switch (state) {
|
||||
case nsIVolume::STATE_MOUNTED:
|
||||
mDeviceStorageAreaListener->DispatchStorageAreaChangedEvent(
|
||||
volName,
|
||||
DeviceStorageAreaChangedEventOperation::Added);
|
||||
break;
|
||||
default:
|
||||
mDeviceStorageAreaListener->DispatchStorageAreaChangedEvent(
|
||||
volName,
|
||||
DeviceStorageAreaChangedEventOperation::Removed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(DeviceStorageAreaListener, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(DeviceStorageAreaListener, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(DeviceStorageAreaListener)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
DeviceStorageAreaListener::DeviceStorageAreaListener(nsPIDOMWindowInner* aWindow)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mVolumeStateObserver = new VolumeStateObserver(this);
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(mVolumeStateObserver, NS_VOLUME_STATE_CHANGED, false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DeviceStorageAreaListener::~DeviceStorageAreaListener()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(mVolumeStateObserver, NS_VOLUME_STATE_CHANGED);
|
||||
}
|
||||
#endif
|
||||
mVolumeStateObserver->ForgetListener();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
DeviceStorageAreaListener::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return DeviceStorageAreaListenerBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageAreaListener::DispatchStorageAreaChangedEvent(
|
||||
const nsString& aStorageName,
|
||||
DeviceStorageAreaChangedEventOperation aOperation)
|
||||
{
|
||||
StateMapType::const_iterator iter = mStorageAreaStateMap.find(aStorageName);
|
||||
if (iter == mStorageAreaStateMap.end() &&
|
||||
aOperation != DeviceStorageAreaChangedEventOperation::Added) {
|
||||
// The operation of the first event to dispatch should be "Added".
|
||||
return;
|
||||
}
|
||||
if (iter != mStorageAreaStateMap.end() &&
|
||||
iter->second == aOperation) {
|
||||
// No need to disptach the event if the state is unchanged.
|
||||
return;
|
||||
}
|
||||
|
||||
DeviceStorageAreaChangedEventInit init;
|
||||
init.mOperation = aOperation;
|
||||
init.mStorageName = aStorageName;
|
||||
|
||||
RefPtr<DeviceStorageAreaChangedEvent> event =
|
||||
DeviceStorageAreaChangedEvent::Constructor(this,
|
||||
NS_LITERAL_STRING("storageareachanged"),
|
||||
init);
|
||||
event->SetTrusted(true);
|
||||
|
||||
mStorageAreaStateMap[aStorageName] = aOperation;
|
||||
|
||||
nsDOMDeviceStorage::InvalidateVolumeCaches();
|
||||
|
||||
bool ignore;
|
||||
DOMEventTargetHelper::DispatchEvent(event, &ignore);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,47 +0,0 @@
|
||||
/* -*- 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_DeviceStorageAreaListener_h
|
||||
#define mozilla_dom_DeviceStorageAreaListener_h
|
||||
|
||||
#include <map>
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/DeviceStorageAreaChangedEvent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class VolumeStateObserver;
|
||||
|
||||
class DeviceStorageAreaListener final : public DOMEventTargetHelper
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
IMPL_EVENT_HANDLER(storageareachanged)
|
||||
|
||||
explicit DeviceStorageAreaListener(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
friend class VolumeStateObserver;
|
||||
|
||||
typedef std::map<nsString, DeviceStorageAreaChangedEventOperation> StateMapType;
|
||||
StateMapType mStorageAreaStateMap;
|
||||
|
||||
RefPtr<VolumeStateObserver> mVolumeStateObserver;
|
||||
|
||||
~DeviceStorageAreaListener();
|
||||
|
||||
void DispatchStorageAreaChangedEvent(
|
||||
const nsString& aStorageName,
|
||||
DeviceStorageAreaChangedEventOperation aOperation);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_DeviceStorageAreaListener_h
|
@ -1,21 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 DeviceStorageFileDescriptor_h
|
||||
#define DeviceStorageFileDescriptor_h
|
||||
|
||||
#include "mozilla/ipc/FileDescriptor.h"
|
||||
|
||||
struct DeviceStorageFileDescriptor final
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DeviceStorageFileDescriptor)
|
||||
RefPtr<DeviceStorageFile> mDSFile;
|
||||
mozilla::ipc::FileDescriptor mFileDescriptor;
|
||||
private:
|
||||
~DeviceStorageFileDescriptor() {}
|
||||
};
|
||||
|
||||
#endif
|
@ -1,151 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "DeviceStorageRequestChild.h"
|
||||
#include "DeviceStorageFileDescriptor.h"
|
||||
#include "nsDeviceStorage.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/ipc/BlobChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace devicestorage {
|
||||
|
||||
DeviceStorageRequestChild::DeviceStorageRequestChild()
|
||||
{
|
||||
MOZ_COUNT_CTOR(DeviceStorageRequestChild);
|
||||
}
|
||||
|
||||
DeviceStorageRequestChild::DeviceStorageRequestChild(DeviceStorageRequest* aRequest)
|
||||
: mRequest(aRequest)
|
||||
{
|
||||
MOZ_ASSERT(aRequest);
|
||||
MOZ_COUNT_CTOR(DeviceStorageRequestChild);
|
||||
}
|
||||
|
||||
DeviceStorageRequestChild::~DeviceStorageRequestChild() {
|
||||
MOZ_COUNT_DTOR(DeviceStorageRequestChild);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
DeviceStorageRequestChild::
|
||||
Recv__delete__(const DeviceStorageResponseValue& aValue)
|
||||
{
|
||||
switch (aValue.type()) {
|
||||
|
||||
case DeviceStorageResponseValue::TErrorResponse:
|
||||
{
|
||||
DS_LOG_INFO("error %u", mRequest->GetId());
|
||||
ErrorResponse r = aValue;
|
||||
mRequest->Reject(r.error());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TSuccessResponse:
|
||||
{
|
||||
DS_LOG_INFO("success %u", mRequest->GetId());
|
||||
nsString fullPath;
|
||||
mRequest->GetFile()->GetFullPath(fullPath);
|
||||
mRequest->Resolve(fullPath);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TFileDescriptorResponse:
|
||||
{
|
||||
DS_LOG_INFO("fd %u", mRequest->GetId());
|
||||
FileDescriptorResponse r = aValue;
|
||||
|
||||
DeviceStorageFile* file = mRequest->GetFile();
|
||||
DeviceStorageFileDescriptor* descriptor = mRequest->GetFileDescriptor();
|
||||
nsString fullPath;
|
||||
file->GetFullPath(fullPath);
|
||||
descriptor->mDSFile = file;
|
||||
descriptor->mFileDescriptor = r.fileDescriptor();
|
||||
mRequest->Resolve(fullPath);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TBlobResponse:
|
||||
{
|
||||
DS_LOG_INFO("blob %u", mRequest->GetId());
|
||||
BlobResponse r = aValue;
|
||||
BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
|
||||
RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
|
||||
mRequest->Resolve(blobImpl.get());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TFreeSpaceStorageResponse:
|
||||
{
|
||||
DS_LOG_INFO("free %u", mRequest->GetId());
|
||||
FreeSpaceStorageResponse r = aValue;
|
||||
mRequest->Resolve(r.freeBytes());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TUsedSpaceStorageResponse:
|
||||
{
|
||||
DS_LOG_INFO("used %u", mRequest->GetId());
|
||||
UsedSpaceStorageResponse r = aValue;
|
||||
mRequest->Resolve(r.usedBytes());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TFormatStorageResponse:
|
||||
{
|
||||
DS_LOG_INFO("format %u", mRequest->GetId());
|
||||
FormatStorageResponse r = aValue;
|
||||
mRequest->Resolve(r.mountState());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TMountStorageResponse:
|
||||
{
|
||||
DS_LOG_INFO("mount %u", mRequest->GetId());
|
||||
MountStorageResponse r = aValue;
|
||||
mRequest->Resolve(r.storageStatus());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TUnmountStorageResponse:
|
||||
{
|
||||
DS_LOG_INFO("unmount %u", mRequest->GetId());
|
||||
UnmountStorageResponse r = aValue;
|
||||
mRequest->Resolve(r.storageStatus());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TEnumerationResponse:
|
||||
{
|
||||
DS_LOG_INFO("enumerate %u", mRequest->GetId());
|
||||
EnumerationResponse r = aValue;
|
||||
auto request = static_cast<DeviceStorageCursorRequest*>(mRequest.get());
|
||||
uint32_t count = r.paths().Length();
|
||||
request->AddFiles(count);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
RefPtr<DeviceStorageFile> dsf
|
||||
= new DeviceStorageFile(r.type(), r.paths()[i].storageName(),
|
||||
r.rootdir(), r.paths()[i].name());
|
||||
request->AddFile(dsf.forget());
|
||||
}
|
||||
request->Continue();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
DS_LOG_ERROR("unknown %u", mRequest->GetId());
|
||||
MOZ_CRASH("not reached");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace devicestorage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,44 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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_devicestorage_DeviceStorageRequestChild_h
|
||||
#define mozilla_dom_devicestorage_DeviceStorageRequestChild_h
|
||||
|
||||
#include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
|
||||
|
||||
class DeviceStorageFile;
|
||||
class DeviceStorageRequest;
|
||||
struct DeviceStorageFileDescriptor;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace devicestorage {
|
||||
|
||||
class DeviceStorageRequestChildCallback
|
||||
{
|
||||
public:
|
||||
virtual void RequestComplete() = 0;
|
||||
};
|
||||
|
||||
class DeviceStorageRequestChild : public PDeviceStorageRequestChild
|
||||
{
|
||||
public:
|
||||
DeviceStorageRequestChild();
|
||||
explicit DeviceStorageRequestChild(DeviceStorageRequest* aRequest);
|
||||
~DeviceStorageRequestChild();
|
||||
|
||||
virtual mozilla::ipc::IPCResult Recv__delete__(const DeviceStorageResponseValue& value);
|
||||
|
||||
private:
|
||||
RefPtr<DeviceStorageRequest> mRequest;
|
||||
};
|
||||
|
||||
} // namespace devicestorage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -1,595 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "DeviceStorageRequestParent.h"
|
||||
#include "nsIMIMEService.h"
|
||||
#include "nsCExternalHandlerService.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/FileBlobImpl.h"
|
||||
#include "mozilla/dom/ipc/BlobParent.h"
|
||||
#include "ContentParent.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsNetCID.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace devicestorage {
|
||||
|
||||
DeviceStorageRequestParent::DeviceStorageRequestParent(
|
||||
const DeviceStorageParams& aParams)
|
||||
: mParams(aParams)
|
||||
, mMutex("DeviceStorageRequestParent::mMutex")
|
||||
, mActorDestroyed(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DeviceStorageRequestParent);
|
||||
|
||||
DebugOnly<DeviceStorageUsedSpaceCache*> usedSpaceCache
|
||||
= DeviceStorageUsedSpaceCache::CreateOrGet();
|
||||
MOZ_ASSERT(usedSpaceCache);
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageRequestParent::Dispatch()
|
||||
{
|
||||
RefPtr<CancelableRunnable> r;
|
||||
switch (mParams.type()) {
|
||||
case DeviceStorageParams::TDeviceStorageAddParams:
|
||||
{
|
||||
DeviceStorageAddParams p = mParams;
|
||||
|
||||
RefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName(), p.relpath());
|
||||
|
||||
BlobParent* bp = static_cast<BlobParent*>(p.blobParent());
|
||||
RefPtr<BlobImpl> blobImpl = bp->GetBlobImpl();
|
||||
|
||||
ErrorResult rv;
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
blobImpl->GetInternalStream(getter_AddRefs(stream), rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
r = new WriteFileEvent(this, dsf.forget(), stream,
|
||||
DEVICE_STORAGE_REQUEST_CREATE);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageAppendParams:
|
||||
{
|
||||
DeviceStorageAppendParams p = mParams;
|
||||
|
||||
RefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName(), p.relpath());
|
||||
|
||||
BlobParent* bp = static_cast<BlobParent*>(p.blobParent());
|
||||
RefPtr<BlobImpl> blobImpl = bp->GetBlobImpl();
|
||||
|
||||
ErrorResult rv;
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
blobImpl->GetInternalStream(getter_AddRefs(stream), rv);
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
r = new WriteFileEvent(this, dsf.forget(), stream,
|
||||
DEVICE_STORAGE_REQUEST_APPEND);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageCreateFdParams:
|
||||
{
|
||||
DeviceStorageCreateFdParams p = mParams;
|
||||
|
||||
RefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName(), p.relpath());
|
||||
|
||||
r = new CreateFdEvent(this, dsf.forget());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageGetParams:
|
||||
{
|
||||
DeviceStorageGetParams p = mParams;
|
||||
RefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName(),
|
||||
p.rootDir(), p.relpath());
|
||||
r = new ReadFileEvent(this, dsf.forget());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageDeleteParams:
|
||||
{
|
||||
DeviceStorageDeleteParams p = mParams;
|
||||
|
||||
RefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName(), p.relpath());
|
||||
r = new DeleteFileEvent(this, dsf.forget());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageFreeSpaceParams:
|
||||
{
|
||||
DeviceStorageFreeSpaceParams p = mParams;
|
||||
|
||||
RefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName());
|
||||
r = new FreeSpaceFileEvent(this, dsf.forget());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageUsedSpaceParams:
|
||||
{
|
||||
DeviceStorageUsedSpaceCache* usedSpaceCache
|
||||
= DeviceStorageUsedSpaceCache::CreateOrGet();
|
||||
MOZ_ASSERT(usedSpaceCache);
|
||||
|
||||
DeviceStorageUsedSpaceParams p = mParams;
|
||||
|
||||
RefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName());
|
||||
usedSpaceCache->Dispatch(
|
||||
MakeAndAddRef<UsedSpaceFileEvent>(this, dsf.forget()));
|
||||
return;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageFormatParams:
|
||||
{
|
||||
DeviceStorageFormatParams p = mParams;
|
||||
|
||||
RefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName());
|
||||
DebugOnly<nsresult> rv = NS_DispatchToMainThread(
|
||||
new PostFormatResultEvent(this, dsf.forget()));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
return;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageMountParams:
|
||||
{
|
||||
DeviceStorageMountParams p = mParams;
|
||||
|
||||
RefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName());
|
||||
DebugOnly<nsresult> rv = NS_DispatchToMainThread(
|
||||
new PostMountResultEvent(this, dsf.forget()));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
return;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageUnmountParams:
|
||||
{
|
||||
DeviceStorageUnmountParams p = mParams;
|
||||
|
||||
RefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName());
|
||||
DebugOnly<nsresult> rv = NS_DispatchToMainThread(
|
||||
new PostUnmountResultEvent(this, dsf.forget()));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
return;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageEnumerationParams:
|
||||
{
|
||||
DeviceStorageEnumerationParams p = mParams;
|
||||
RefPtr<DeviceStorageFile> dsf
|
||||
= new DeviceStorageFile(p.type(), p.storageName(),
|
||||
p.rootdir(), NS_LITERAL_STRING(""));
|
||||
r = new EnumerateFileEvent(this, dsf.forget(), p.since());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
MOZ_CRASH("not reached");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (r) {
|
||||
nsCOMPtr<nsIEventTarget> target =
|
||||
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
MOZ_ASSERT(target);
|
||||
target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceStorageRequestParent::~DeviceStorageRequestParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DeviceStorageRequestParent);
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF(DeviceStorageRequestParent)
|
||||
NS_IMPL_RELEASE(DeviceStorageRequestParent)
|
||||
|
||||
void
|
||||
DeviceStorageRequestParent::ActorDestroy(ActorDestroyReason)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mActorDestroyed = true;
|
||||
for (auto& runnable : mRunnables) {
|
||||
runnable->Cancel();
|
||||
}
|
||||
// Ensure we clear all references to the runnables so that there won't
|
||||
// be leak due to cyclic reference. Note that it is safe to release
|
||||
// the references here, since if a runnable is not cancelled yet, the
|
||||
// corresponding thread should still hold a reference to it, and thus
|
||||
// the runnable will end up being released in that thread, not here.
|
||||
mRunnables.Clear();
|
||||
}
|
||||
|
||||
DeviceStorageRequestParent::PostFreeSpaceResultEvent::PostFreeSpaceResultEvent(
|
||||
DeviceStorageRequestParent* aParent,
|
||||
uint64_t aFreeSpace)
|
||||
: CancelableRunnable(aParent)
|
||||
, mFreeSpace(aFreeSpace)
|
||||
{
|
||||
}
|
||||
|
||||
DeviceStorageRequestParent::PostFreeSpaceResultEvent::
|
||||
~PostFreeSpaceResultEvent() {}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostFreeSpaceResultEvent::CancelableRun() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
FreeSpaceStorageResponse response(mFreeSpace);
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostUsedSpaceResultEvent::CancelableRun() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
UsedSpaceStorageResponse response(mUsedSpace);
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DeviceStorageRequestParent::PostErrorEvent::
|
||||
PostErrorEvent(DeviceStorageRequestParent* aParent, const char* aError)
|
||||
: CancelableRunnable(aParent)
|
||||
{
|
||||
CopyASCIItoUTF16(aError, mError);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostErrorEvent::CancelableRun() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
ErrorResponse response(mError);
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostSuccessEvent::CancelableRun() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
SuccessResponse response;
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostBlobSuccessEvent::CancelableRun() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsString mime;
|
||||
CopyASCIItoUTF16(mMimeType, mime);
|
||||
|
||||
nsString fullPath;
|
||||
mFile->GetFullPath(fullPath);
|
||||
RefPtr<BlobImpl> blob =
|
||||
new FileBlobImpl(fullPath, mime, mLength, mFile->mFile,
|
||||
mLastModificationDate);
|
||||
|
||||
ContentParent* cp = static_cast<ContentParent*>(mParent->Manager());
|
||||
BlobParent* actor = cp->GetOrCreateActorForBlobImpl(blob);
|
||||
if (!actor) {
|
||||
ErrorResponse response(NS_LITERAL_STRING(POST_ERROR_EVENT_UNKNOWN));
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
BlobResponse response;
|
||||
response.blobParent() = actor;
|
||||
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostEnumerationSuccessEvent::CancelableRun() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
EnumerationResponse response(mStorageType, mRelPath, mPaths);
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::CreateFdEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (!mFile->mFile) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN));
|
||||
}
|
||||
bool check = false;
|
||||
mFile->mFile->Exists(&check);
|
||||
if (check) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> r;
|
||||
FileDescriptor fileDescriptor;
|
||||
nsresult rv = mFile->CreateFileDescriptor(fileDescriptor);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("CreateFileDescriptor failed");
|
||||
mFile->Dump("CreateFileDescriptor failed");
|
||||
r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
|
||||
}
|
||||
else {
|
||||
r = new PostFileDescriptorResultEvent(mParent, fileDescriptor);
|
||||
}
|
||||
|
||||
return NS_DispatchToMainThread(r.forget());
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::WriteFileEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (!mInputStream || !mFile->mFile) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN));
|
||||
}
|
||||
|
||||
bool check = false;
|
||||
nsresult rv;
|
||||
mFile->mFile->Exists(&check);
|
||||
|
||||
if (mRequestType == DEVICE_STORAGE_REQUEST_CREATE) {
|
||||
if (check) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS));
|
||||
}
|
||||
rv = mFile->Write(mInputStream);
|
||||
} else if (mRequestType == DEVICE_STORAGE_REQUEST_APPEND) {
|
||||
if (!check) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST));
|
||||
}
|
||||
rv = mFile->Append(mInputStream);
|
||||
} else {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> r;
|
||||
if (NS_FAILED(rv)) {
|
||||
r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
|
||||
}
|
||||
else {
|
||||
r = new PostPathResultEvent(mParent, mFile->mPath);
|
||||
}
|
||||
|
||||
return NS_DispatchToMainThread(r.forget());
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::DeleteFileEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
mFile->Remove();
|
||||
|
||||
if (!mFile->mFile) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN));
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> r;
|
||||
bool check = false;
|
||||
mFile->mFile->Exists(&check);
|
||||
if (check) {
|
||||
r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
|
||||
}
|
||||
else {
|
||||
r = new PostPathResultEvent(mParent, mFile->mPath);
|
||||
}
|
||||
|
||||
return NS_DispatchToMainThread(r.forget());
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::FreeSpaceFileEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
int64_t freeSpace = 0;
|
||||
if (mFile) {
|
||||
mFile->GetStorageFreeSpace(&freeSpace);
|
||||
}
|
||||
|
||||
return NS_DispatchToMainThread(
|
||||
new PostFreeSpaceResultEvent(mParent, static_cast<uint64_t>(freeSpace)));
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::UsedSpaceFileEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0;
|
||||
mFile->AccumDiskUsage(&picturesUsage, &videosUsage,
|
||||
&musicUsage, &totalUsage);
|
||||
nsCOMPtr<nsIRunnable> r;
|
||||
if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
|
||||
r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType,
|
||||
picturesUsage);
|
||||
}
|
||||
else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
|
||||
r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, videosUsage);
|
||||
}
|
||||
else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
|
||||
r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, musicUsage);
|
||||
} else {
|
||||
r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, totalUsage);
|
||||
}
|
||||
return NS_DispatchToMainThread(r.forget());
|
||||
}
|
||||
|
||||
DeviceStorageRequestParent::ReadFileEvent::
|
||||
ReadFileEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile)
|
||||
: CancelableFileEvent(aParent, Move(aFile))
|
||||
{
|
||||
nsCOMPtr<nsIMIMEService> mimeService
|
||||
= do_GetService(NS_MIMESERVICE_CONTRACTID);
|
||||
if (mimeService) {
|
||||
nsresult rv = mimeService->GetTypeFromFile(mFile->mFile, mMimeType);
|
||||
if (NS_FAILED(rv)) {
|
||||
mMimeType.Truncate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::ReadFileEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (!mFile->mFile) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN));
|
||||
}
|
||||
bool check = false;
|
||||
mFile->mFile->Exists(&check);
|
||||
|
||||
if (!check) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST));
|
||||
}
|
||||
|
||||
int64_t fileSize;
|
||||
nsresult rv = mFile->mFile->GetFileSize(&fileSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN));
|
||||
}
|
||||
|
||||
PRTime modDate;
|
||||
rv = mFile->mFile->GetLastModifiedTime(&modDate);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN));
|
||||
}
|
||||
|
||||
return NS_DispatchToMainThread(
|
||||
new PostBlobSuccessEvent(mParent, mFile.forget(),
|
||||
static_cast<uint64_t>(fileSize),
|
||||
mMimeType, modDate));
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::EnumerateFileEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (mFile->mFile) {
|
||||
bool check = false;
|
||||
mFile->mFile->Exists(&check);
|
||||
if (!check) {
|
||||
return NS_DispatchToMainThread(
|
||||
new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST));
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<DeviceStorageFile> > files;
|
||||
mFile->CollectFiles(files, mSince);
|
||||
|
||||
InfallibleTArray<DeviceStorageFileValue> values;
|
||||
|
||||
uint32_t count = files.Length();
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
DeviceStorageFileValue dsvf(files[i]->mStorageName, files[i]->mPath);
|
||||
values.AppendElement(dsvf);
|
||||
}
|
||||
|
||||
return NS_DispatchToMainThread(
|
||||
new PostEnumerationSuccessEvent(mParent, mFile->mStorageType,
|
||||
mFile->mRootDir, values));
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostPathResultEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
SuccessResponse response;
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostFileDescriptorResultEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
FileDescriptorResponse response(mFileDescriptor);
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostFormatResultEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsString state = NS_LITERAL_STRING("unavailable");
|
||||
if (mFile) {
|
||||
mFile->DoFormat(state);
|
||||
}
|
||||
|
||||
FormatStorageResponse response(state);
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostMountResultEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsString state = NS_LITERAL_STRING("unavailable");
|
||||
if (mFile) {
|
||||
mFile->DoMount(state);
|
||||
}
|
||||
|
||||
MountStorageResponse response(state);
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostUnmountResultEvent::CancelableRun()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsString state = NS_LITERAL_STRING("unavailable");
|
||||
if (mFile) {
|
||||
mFile->DoUnmount(state);
|
||||
}
|
||||
|
||||
UnmountStorageResponse response(state);
|
||||
Unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace devicestorage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,333 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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_devicestorage_DeviceStorageRequestParent_h
|
||||
#define mozilla_dom_devicestorage_DeviceStorageRequestParent_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/devicestorage/PDeviceStorageRequestParent.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsDeviceStorage.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace devicestorage {
|
||||
|
||||
class DeviceStorageRequestParent : public PDeviceStorageRequestParent
|
||||
{
|
||||
public:
|
||||
explicit DeviceStorageRequestParent(const DeviceStorageParams& aParams);
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef();
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release();
|
||||
|
||||
void Dispatch();
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason);
|
||||
|
||||
protected:
|
||||
~DeviceStorageRequestParent();
|
||||
|
||||
private:
|
||||
ThreadSafeAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
DeviceStorageParams mParams;
|
||||
|
||||
// XXXkhuey name collision :(
|
||||
class CancelableRunnable : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit CancelableRunnable(DeviceStorageRequestParent* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
mCanceled = !(mParent->AddRunnable(this));
|
||||
}
|
||||
|
||||
virtual ~CancelableRunnable() {
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override {
|
||||
nsresult rv = NS_OK;
|
||||
if (!mCanceled) {
|
||||
rv = CancelableRun();
|
||||
mParent->RemoveRunnable(this);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void Cancel() {
|
||||
mCanceled = true;
|
||||
}
|
||||
|
||||
virtual nsresult CancelableRun() = 0;
|
||||
|
||||
protected:
|
||||
RefPtr<DeviceStorageRequestParent> mParent;
|
||||
private:
|
||||
bool mCanceled;
|
||||
};
|
||||
|
||||
class CancelableFileEvent : public CancelableRunnable
|
||||
{
|
||||
protected:
|
||||
CancelableFileEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile)
|
||||
: CancelableRunnable(aParent)
|
||||
, mFile(Move(aFile)) {}
|
||||
|
||||
RefPtr<DeviceStorageFile> mFile;
|
||||
};
|
||||
|
||||
class PostErrorEvent : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
PostErrorEvent(DeviceStorageRequestParent* aParent, const char* aError);
|
||||
virtual ~PostErrorEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
nsString mError;
|
||||
};
|
||||
|
||||
class PostSuccessEvent : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit PostSuccessEvent(DeviceStorageRequestParent* aParent)
|
||||
: CancelableRunnable(aParent) {}
|
||||
virtual ~PostSuccessEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
};
|
||||
|
||||
class PostBlobSuccessEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
PostBlobSuccessEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile,
|
||||
uint32_t aLength, nsACString& aMimeType,
|
||||
uint64_t aLastModifiedDate)
|
||||
: CancelableFileEvent(aParent, Move(aFile))
|
||||
, mLength(aLength)
|
||||
, mLastModificationDate(aLastModifiedDate)
|
||||
, mMimeType(aMimeType) {}
|
||||
virtual ~PostBlobSuccessEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
uint32_t mLength;
|
||||
uint64_t mLastModificationDate;
|
||||
nsCString mMimeType;
|
||||
};
|
||||
|
||||
class PostEnumerationSuccessEvent : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent,
|
||||
const nsAString& aStorageType,
|
||||
const nsAString& aRelPath,
|
||||
InfallibleTArray<DeviceStorageFileValue>& aPaths)
|
||||
: CancelableRunnable(aParent)
|
||||
, mStorageType(aStorageType)
|
||||
, mRelPath(aRelPath)
|
||||
, mPaths(aPaths) {}
|
||||
virtual ~PostEnumerationSuccessEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
const nsString mStorageType;
|
||||
const nsString mRelPath;
|
||||
InfallibleTArray<DeviceStorageFileValue> mPaths;
|
||||
};
|
||||
|
||||
class CreateFdEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
CreateFdEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile)
|
||||
: CancelableFileEvent(aParent, Move(aFile)) {}
|
||||
virtual ~CreateFdEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
};
|
||||
|
||||
class WriteFileEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
WriteFileEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile,
|
||||
nsIInputStream* aInputStream, int32_t aRequestType)
|
||||
: CancelableFileEvent(aParent, Move(aFile))
|
||||
, mInputStream(aInputStream)
|
||||
, mRequestType(aRequestType) {}
|
||||
virtual ~WriteFileEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
nsCOMPtr<nsIInputStream> mInputStream;
|
||||
int32_t mRequestType;
|
||||
};
|
||||
|
||||
class DeleteFileEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
DeleteFileEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile)
|
||||
: CancelableFileEvent(aParent, Move(aFile)) {}
|
||||
virtual ~DeleteFileEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
};
|
||||
|
||||
class FreeSpaceFileEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
FreeSpaceFileEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile)
|
||||
: CancelableFileEvent(aParent, Move(aFile)) {}
|
||||
virtual ~FreeSpaceFileEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
};
|
||||
|
||||
class UsedSpaceFileEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
UsedSpaceFileEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile)
|
||||
: CancelableFileEvent(aParent, Move(aFile)) {}
|
||||
virtual ~UsedSpaceFileEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
};
|
||||
|
||||
class ReadFileEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
ReadFileEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile);
|
||||
virtual ~ReadFileEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
nsCString mMimeType;
|
||||
};
|
||||
|
||||
class EnumerateFileEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
EnumerateFileEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile,
|
||||
uint64_t aSince)
|
||||
: CancelableFileEvent(aParent, Move(aFile))
|
||||
, mSince(aSince) {}
|
||||
virtual ~EnumerateFileEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
uint64_t mSince;
|
||||
};
|
||||
|
||||
class PostPathResultEvent : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
PostPathResultEvent(DeviceStorageRequestParent* aParent,
|
||||
const nsAString& aPath)
|
||||
: CancelableRunnable(aParent)
|
||||
, mPath(aPath) {}
|
||||
virtual ~PostPathResultEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
nsString mPath;
|
||||
};
|
||||
|
||||
class PostFileDescriptorResultEvent : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
PostFileDescriptorResultEvent(DeviceStorageRequestParent* aParent,
|
||||
const FileDescriptor& aFileDescriptor)
|
||||
: CancelableRunnable(aParent)
|
||||
, mFileDescriptor(aFileDescriptor) {}
|
||||
virtual ~PostFileDescriptorResultEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
FileDescriptor mFileDescriptor;
|
||||
};
|
||||
|
||||
class PostFreeSpaceResultEvent : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
PostFreeSpaceResultEvent(DeviceStorageRequestParent* aParent,
|
||||
uint64_t aFreeSpace);
|
||||
virtual ~PostFreeSpaceResultEvent();
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
uint64_t mFreeSpace;
|
||||
};
|
||||
|
||||
class PostUsedSpaceResultEvent : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
PostUsedSpaceResultEvent(DeviceStorageRequestParent* aParent,
|
||||
const nsAString& aType,
|
||||
uint64_t aUsedSpace)
|
||||
: CancelableRunnable(aParent)
|
||||
, mType(aType)
|
||||
, mUsedSpace(aUsedSpace) {}
|
||||
virtual ~PostUsedSpaceResultEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
nsString mType;
|
||||
uint64_t mUsedSpace;
|
||||
};
|
||||
|
||||
class PostFormatResultEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
PostFormatResultEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile)
|
||||
: CancelableFileEvent(aParent, Move(aFile)) {}
|
||||
virtual ~PostFormatResultEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
};
|
||||
|
||||
class PostMountResultEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
PostMountResultEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile)
|
||||
: CancelableFileEvent(aParent, Move(aFile)) {}
|
||||
virtual ~PostMountResultEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
};
|
||||
|
||||
class PostUnmountResultEvent : public CancelableFileEvent
|
||||
{
|
||||
public:
|
||||
PostUnmountResultEvent(DeviceStorageRequestParent* aParent,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile)
|
||||
: CancelableFileEvent(aParent, Move(aFile)) {}
|
||||
virtual ~PostUnmountResultEvent() {}
|
||||
virtual nsresult CancelableRun();
|
||||
};
|
||||
|
||||
protected:
|
||||
bool AddRunnable(CancelableRunnable* aRunnable) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mActorDestroyed)
|
||||
return false;
|
||||
|
||||
mRunnables.AppendElement(aRunnable);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveRunnable(CancelableRunnable* aRunnable) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
mRunnables.RemoveElement(aRunnable);
|
||||
}
|
||||
|
||||
Mutex mMutex;
|
||||
bool mActorDestroyed;
|
||||
nsTArray<RefPtr<CancelableRunnable> > mRunnables;
|
||||
};
|
||||
|
||||
} // namespace devicestorage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -1,930 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "DeviceStorageStatics.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsDeviceStorage.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsIVolume.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include "AndroidBridge.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace devicestorage {
|
||||
|
||||
static const char* kPrefOverrideRootDir = "device.storage.overrideRootDir";
|
||||
static const char* kPrefTesting = "device.storage.testing";
|
||||
static const char* kPrefPromptTesting = "device.storage.prompt.testing";
|
||||
static const char* kPrefWritableName = "device.storage.writable.name";
|
||||
|
||||
// file-watcher-notify comes from some process (but not the MTP Server)
|
||||
// to indicate that a file has changed. It eventually winds up in the
|
||||
// parent process, and then gets broadcast out to all child listeners
|
||||
// as a file-watcher-update and mtp-watcher-update.
|
||||
//
|
||||
// mtp-watcher-notify comes from the MTP Server whenever it detects a change
|
||||
// and this gets rebroadcast as file-watcher-update to the device storage
|
||||
// listeners.
|
||||
//
|
||||
// download-watcher-notify is treated similarly to file-watcher-notify,
|
||||
// and gets converted into file-watcher-update and mtp-watcher-update.
|
||||
//
|
||||
// We need to make sure that the MTP server doesn't get notified about
|
||||
// files which it told us it added, otherwise it confuses some clients
|
||||
// (like the Android-File-Transfer program which runs under OS X).
|
||||
|
||||
static const char* kFileWatcherUpdate = "file-watcher-update";
|
||||
static const char* kMtpWatcherUpdate = "mtp-watcher-update";
|
||||
static const char* kDiskSpaceWatcher = "disk-space-watcher";
|
||||
static const char* kFileWatcherNotify = "file-watcher-notify";
|
||||
static const char* kMtpWatcherNotify = "mtp-watcher-notify";
|
||||
static const char* kDownloadWatcherNotify = "download-watcher-notify";
|
||||
|
||||
StaticRefPtr<DeviceStorageStatics> DeviceStorageStatics::sInstance;
|
||||
StaticMutex DeviceStorageStatics::sMutex;
|
||||
|
||||
NS_IMPL_ISUPPORTS(DeviceStorageStatics,
|
||||
nsIObserver)
|
||||
|
||||
/* static */ void
|
||||
DeviceStorageStatics::Initialize()
|
||||
{
|
||||
MOZ_ASSERT(!sInstance);
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
sInstance = new DeviceStorageStatics();
|
||||
sInstance->Init();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
DeviceStorageStatics::InitializeDirs()
|
||||
{
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* The actual initialization can only happen on the main thread. This will
|
||||
either happen when device storage is first used on the main thread, or
|
||||
(in the future) when a worker is created. */
|
||||
if (!sInstance->mInitialized && NS_IsMainThread()) {
|
||||
sInstance->InitDirs();
|
||||
sInstance->mInitialized = true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(sInstance->mInitialized);
|
||||
}
|
||||
|
||||
DeviceStorageStatics::DeviceStorageStatics()
|
||||
: mInitialized(false)
|
||||
, mPromptTesting(false)
|
||||
, mLowDiskSpace(false)
|
||||
{
|
||||
DS_LOG_INFO("");
|
||||
}
|
||||
|
||||
DeviceStorageStatics::~DeviceStorageStatics()
|
||||
{
|
||||
DS_LOG_INFO("");
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageStatics::Init()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sMutex.AssertCurrentThreadOwns();
|
||||
DS_LOG_INFO("");
|
||||
|
||||
Preferences::AddStrongObserver(this, kPrefTesting);
|
||||
Preferences::AddStrongObserver(this, kPrefPromptTesting);
|
||||
Preferences::AddStrongObserver(this, kPrefWritableName);
|
||||
|
||||
mWritableName = Preferences::GetString(kPrefWritableName);
|
||||
mPromptTesting = Preferences::GetBool(kPrefPromptTesting, false);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
obs->AddObserver(this, kFileWatcherNotify, false);
|
||||
obs->AddObserver(this, kMtpWatcherNotify, false);
|
||||
obs->AddObserver(this, kDownloadWatcherNotify, false);
|
||||
}
|
||||
DS_LOG_INFO("");
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageStatics::InitDirs()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sMutex.AssertCurrentThreadOwns();
|
||||
DS_LOG_INFO("");
|
||||
|
||||
#if !defined(MOZ_WIDGET_GONK)
|
||||
if (!XRE_IsParentProcess()) {
|
||||
// For gonk, we have the parent process forward the directory information
|
||||
// to the child using ContentParent::ForwardKnownInfo. On desktop, this
|
||||
// winds up slowing down the startup (in particular ts_paint), so rather
|
||||
// than penalize all e10s processes, we do a synchronous IPC call here,
|
||||
// which only penalizes child processes which actually use DeviceStorage.
|
||||
|
||||
dom::ContentChild* child = dom::ContentChild::GetSingleton();
|
||||
DeviceStorageLocationInfo locationInfo;
|
||||
child->SendGetDeviceStorageLocations(&locationInfo);
|
||||
|
||||
NS_NewLocalFile(locationInfo.apps(), true, getter_AddRefs(sInstance->mDirs[TYPE_APPS]));
|
||||
NS_NewLocalFile(locationInfo.crashes(), true, getter_AddRefs(sInstance->mDirs[TYPE_CRASHES]));
|
||||
NS_NewLocalFile(locationInfo.pictures(), true, getter_AddRefs(sInstance->mDirs[TYPE_PICTURES]));
|
||||
NS_NewLocalFile(locationInfo.videos(), true, getter_AddRefs(sInstance->mDirs[TYPE_VIDEOS]));
|
||||
NS_NewLocalFile(locationInfo.music(), true, getter_AddRefs(sInstance->mDirs[TYPE_MUSIC]));
|
||||
NS_NewLocalFile(locationInfo.sdcard(), true, getter_AddRefs(sInstance->mDirs[TYPE_SDCARD]));
|
||||
|
||||
sInstance->mInitialized = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIProperties> dirService
|
||||
= do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
|
||||
MOZ_ASSERT(dirService);
|
||||
|
||||
#if !defined(MOZ_WIDGET_GONK)
|
||||
|
||||
// Keep MOZ_WIDGET_COCOA above XP_UNIX,
|
||||
// because both are defined in Darwin builds.
|
||||
#if defined (MOZ_WIDGET_COCOA)
|
||||
dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_PICTURES]));
|
||||
dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_VIDEOS]));
|
||||
dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_MUSIC]));
|
||||
|
||||
// Keep MOZ_WIDGET_ANDROID above XP_UNIX,
|
||||
// because both are defined in Android builds.
|
||||
#elif defined (MOZ_WIDGET_ANDROID)
|
||||
nsAutoString path;
|
||||
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
|
||||
NS_LITERAL_STRING(DEVICESTORAGE_PICTURES), path))) {
|
||||
NS_NewLocalFile(path, /* aFollowLinks */ true,
|
||||
getter_AddRefs(mDirs[TYPE_PICTURES]));
|
||||
}
|
||||
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
|
||||
NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS), path))) {
|
||||
NS_NewLocalFile(path, /* aFollowLinks */ true,
|
||||
getter_AddRefs(mDirs[TYPE_VIDEOS]));
|
||||
}
|
||||
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
|
||||
NS_LITERAL_STRING(DEVICESTORAGE_MUSIC), path))) {
|
||||
NS_NewLocalFile(path, /* aFollowLinks */ true,
|
||||
getter_AddRefs(mDirs[TYPE_MUSIC]));
|
||||
}
|
||||
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
|
||||
NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), path))) {
|
||||
NS_NewLocalFile(path, /* aFollowLinks */ true,
|
||||
getter_AddRefs(mDirs[TYPE_SDCARD]));
|
||||
}
|
||||
|
||||
#elif defined (XP_UNIX)
|
||||
dirService->Get(NS_UNIX_XDG_PICTURES_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_PICTURES]));
|
||||
dirService->Get(NS_UNIX_XDG_VIDEOS_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_VIDEOS]));
|
||||
dirService->Get(NS_UNIX_XDG_MUSIC_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_MUSIC]));
|
||||
|
||||
#elif defined (XP_WIN)
|
||||
dirService->Get(NS_WIN_PICTURES_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_PICTURES]));
|
||||
dirService->Get(NS_WIN_VIDEOS_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_VIDEOS]));
|
||||
dirService->Get(NS_WIN_MUSIC_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_MUSIC]));
|
||||
#endif
|
||||
|
||||
#ifndef MOZ_WIDGET_ANDROID
|
||||
// Eventually, on desktop, we want to do something smarter -- for example,
|
||||
// detect when an sdcard is inserted, and use that instead of this.
|
||||
dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_SDCARD]));
|
||||
if (mDirs[TYPE_SDCARD]) {
|
||||
mDirs[TYPE_SDCARD]->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
|
||||
}
|
||||
#endif // !MOZ_WIDGET_ANDROID
|
||||
|
||||
dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(mDirs[TYPE_APPS]));
|
||||
|
||||
if (mDirs[TYPE_APPS]) {
|
||||
mDirs[TYPE_APPS]->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
|
||||
}
|
||||
#endif // !MOZ_WIDGET_GONK
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
NS_NewLocalFile(NS_LITERAL_STRING("/data"),
|
||||
false,
|
||||
getter_AddRefs(mDirs[TYPE_APPS]));
|
||||
#endif
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
NS_GetSpecialDirectory("UAppData", getter_AddRefs(mDirs[TYPE_CRASHES]));
|
||||
if (mDirs[TYPE_CRASHES]) {
|
||||
mDirs[TYPE_CRASHES]->Append(NS_LITERAL_STRING("Crash Reports"));
|
||||
}
|
||||
}
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// NS_GetSpecialDirectory("UAppData") fails in content processes because
|
||||
// gAppData from toolkit/xre/nsAppRunner.cpp is not initialized.
|
||||
else {
|
||||
NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla/Crash Reports"),
|
||||
false,
|
||||
getter_AddRefs(mDirs[TYPE_CRASHES]));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Directories which don't depend on a volume should be calculated once
|
||||
// here. Directories which depend on the root directory of a volume
|
||||
// should be calculated in DeviceStorageFile::GetRootDirectoryForType.
|
||||
Preferences::AddStrongObserver(this, kPrefOverrideRootDir);
|
||||
ResetOverrideRootDir();
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageStatics::DumpDirs()
|
||||
{
|
||||
#ifdef DS_LOGGING
|
||||
sMutex.AssertCurrentThreadOwns();
|
||||
|
||||
static const char* storageTypes[] = {
|
||||
"app",
|
||||
"crashes",
|
||||
"pictures",
|
||||
"videos",
|
||||
"music",
|
||||
"sdcard",
|
||||
"override",
|
||||
nullptr
|
||||
};
|
||||
|
||||
const char* ptStr;
|
||||
if (XRE_IsParentProcess()) {
|
||||
ptStr = "parent";
|
||||
} else {
|
||||
ptStr = "child";
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < TYPE_COUNT; ++i) {
|
||||
MOZ_ASSERT(storageTypes[i]);
|
||||
|
||||
nsString path;
|
||||
if (mDirs[i]) {
|
||||
mDirs[i]->GetPath(path);
|
||||
}
|
||||
DS_LOG_INFO("(%s) %s: '%s'",
|
||||
ptStr, storageTypes[i], NS_LossyConvertUTF16toASCII(path).get());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageStatics::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sMutex.AssertCurrentThreadOwns();
|
||||
DS_LOG_INFO("");
|
||||
|
||||
Preferences::RemoveObserver(this, kPrefOverrideRootDir);
|
||||
Preferences::RemoveObserver(this, kPrefTesting);
|
||||
Preferences::RemoveObserver(this, kPrefPromptTesting);
|
||||
Preferences::RemoveObserver(this, kPrefWritableName);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
DeviceStorageStatics::GetDeviceStorageLocationsForIPC(
|
||||
DeviceStorageLocationInfo* aLocationInfo)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
InitializeDirs();
|
||||
|
||||
GetDirPath(TYPE_APPS, aLocationInfo->apps());
|
||||
GetDirPath(TYPE_CRASHES, aLocationInfo->crashes());
|
||||
GetDirPath(TYPE_PICTURES, aLocationInfo->pictures());
|
||||
GetDirPath(TYPE_VIDEOS, aLocationInfo->videos());
|
||||
GetDirPath(TYPE_MUSIC, aLocationInfo->music());
|
||||
GetDirPath(TYPE_SDCARD, aLocationInfo->sdcard());
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIFile>
|
||||
DeviceStorageStatics::GetDir(DeviceStorageType aType)
|
||||
{
|
||||
MOZ_ASSERT(aType < TYPE_COUNT);
|
||||
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
switch (aType) {
|
||||
case TYPE_APPS:
|
||||
case TYPE_CRASHES:
|
||||
case TYPE_OVERRIDE:
|
||||
file = sInstance->mDirs[aType];
|
||||
return file.forget();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// In testing, we default all device storage types to a temp directory.
|
||||
// This is only initialized if the preference device.storage.testing
|
||||
// was set to true, or if device.storage.overrideRootDir is set.
|
||||
file = sInstance->mDirs[TYPE_OVERRIDE];
|
||||
if (!file) {
|
||||
file = sInstance->mDirs[aType];
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
/* We should use volume mount points on B2G. */
|
||||
MOZ_ASSERT(!file);
|
||||
#endif
|
||||
}
|
||||
return file.forget();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
DeviceStorageStatics::GetDirPath(DeviceStorageType aType, nsString& aDirPath)
|
||||
{
|
||||
aDirPath.Truncate();
|
||||
nsCOMPtr<nsIFile> file = GetDir(aType);
|
||||
if (file) {
|
||||
file->GetPath(aDirPath);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DeviceStorageStatics::HasOverrideRootDir()
|
||||
{
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return false;
|
||||
}
|
||||
return sInstance->mDirs[TYPE_OVERRIDE];
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIFile>
|
||||
DeviceStorageStatics::GetAppsDir()
|
||||
{
|
||||
return GetDir(TYPE_APPS);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIFile>
|
||||
DeviceStorageStatics::GetCrashesDir()
|
||||
{
|
||||
return GetDir(TYPE_CRASHES);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIFile>
|
||||
DeviceStorageStatics::GetPicturesDir()
|
||||
{
|
||||
return GetDir(TYPE_PICTURES);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIFile>
|
||||
DeviceStorageStatics::GetVideosDir()
|
||||
{
|
||||
return GetDir(TYPE_VIDEOS);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIFile>
|
||||
DeviceStorageStatics::GetMusicDir()
|
||||
{
|
||||
return GetDir(TYPE_MUSIC);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIFile>
|
||||
DeviceStorageStatics::GetSdcardDir()
|
||||
{
|
||||
return GetDir(TYPE_SDCARD);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DeviceStorageStatics::IsPromptTesting()
|
||||
{
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return false;
|
||||
}
|
||||
return sInstance->mPromptTesting;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DeviceStorageStatics::LowDiskSpace()
|
||||
{
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return false;
|
||||
}
|
||||
return sInstance->mLowDiskSpace;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
DeviceStorageStatics::GetWritableName(nsString& aName)
|
||||
{
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
aName.Truncate();
|
||||
return;
|
||||
}
|
||||
aName = sInstance->mWritableName;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
DeviceStorageStatics::SetWritableName(const nsAString& aName)
|
||||
{
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (!NS_WARN_IF(!sInstance)) {
|
||||
// Update inline although it will be updated again in case
|
||||
// another thread comes in checking it before the update takes
|
||||
sInstance->mWritableName = aName;
|
||||
}
|
||||
|
||||
nsString name;
|
||||
name.Assign(aName);
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction([name] () -> void {
|
||||
Preferences::SetString(kPrefWritableName, name);
|
||||
}));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
DeviceStorageStatics::AddListener(nsDOMDeviceStorage* aListener)
|
||||
{
|
||||
DS_LOG_DEBUG("%p", aListener);
|
||||
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(sInstance->mInitialized);
|
||||
if (sInstance->mListeners.IsEmpty()) {
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Register));
|
||||
}
|
||||
|
||||
RefPtr<ListenerWrapper> wrapper =
|
||||
new ListenerWrapper(aListener);
|
||||
sInstance->mListeners.AppendElement(wrapper.forget());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
DeviceStorageStatics::RemoveListener(nsDOMDeviceStorage* aListener)
|
||||
{
|
||||
DS_LOG_DEBUG("%p", aListener);
|
||||
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (!sInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool removed = false;
|
||||
uint32_t i = sInstance->mListeners.Length();
|
||||
while (i > 0) {
|
||||
--i;
|
||||
if (sInstance->mListeners[i]->Equals(aListener)) {
|
||||
sInstance->mListeners.RemoveElementAt(i);
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (removed && sInstance->mListeners.IsEmpty()) {
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Deregister));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageStatics::Register()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DS_LOG_INFO("");
|
||||
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(this, kFileWatcherUpdate, false);
|
||||
obs->AddObserver(this, kDiskSpaceWatcher, false);
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageStatics::Deregister()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DS_LOG_INFO("");
|
||||
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (!sInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, kFileWatcherUpdate);
|
||||
obs->RemoveObserver(this, kDiskSpaceWatcher);
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
obs->RemoveObserver(this, NS_VOLUME_STATE_CHANGED);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageStatics::ResetOverrideRootDir()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sMutex.AssertCurrentThreadOwns();
|
||||
nsCOMPtr<nsIFile> f;
|
||||
DS_LOG_INFO("");
|
||||
|
||||
// For users running on desktop, it's convenient to be able to override
|
||||
// all of the directories to point to a single tree, much like what happens
|
||||
// on a real device.
|
||||
const nsAdoptingString& overrideRootDir =
|
||||
mozilla::Preferences::GetString(kPrefOverrideRootDir);
|
||||
if (overrideRootDir && !overrideRootDir.IsEmpty()) {
|
||||
NS_NewLocalFile(overrideRootDir, false, getter_AddRefs(f));
|
||||
}
|
||||
|
||||
if (!f && Preferences::GetBool(kPrefTesting, false)) {
|
||||
DS_LOG_INFO("temp");
|
||||
nsCOMPtr<nsIProperties> dirService
|
||||
= do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
|
||||
MOZ_ASSERT(dirService);
|
||||
dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
|
||||
if (f) {
|
||||
f->AppendRelativeNativePath(
|
||||
NS_LITERAL_CSTRING("device-storage-testing"));
|
||||
}
|
||||
}
|
||||
|
||||
if (f) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
// Only the parent process can create directories. In testing, because
|
||||
// the preference is updated after startup, its entirely possible that
|
||||
// the preference updated notification will be received by a child
|
||||
// prior to the parent.
|
||||
nsresult rv = f->Create(nsIFile::DIRECTORY_TYPE, 0777);
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
|
||||
nsString path;
|
||||
f->GetPath(path);
|
||||
nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'",
|
||||
NS_LossyConvertUTF16toASCII(path).get());
|
||||
NS_WARNING(msg.get());
|
||||
}
|
||||
}
|
||||
f->Normalize();
|
||||
}
|
||||
|
||||
mDirs[TYPE_OVERRIDE] = f.forget();
|
||||
DumpDirs();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DeviceStorageStatics::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
||||
MOZ_ASSERT(aData);
|
||||
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsDependentString name(aData);
|
||||
if (name.EqualsASCII(kPrefTesting) ||
|
||||
name.EqualsASCII(kPrefOverrideRootDir)) {
|
||||
ResetOverrideRootDir();
|
||||
} else if(name.EqualsASCII(kPrefPromptTesting)) {
|
||||
mPromptTesting = Preferences::GetBool(kPrefPromptTesting, false);
|
||||
DS_LOG_INFO("prompt testing %d", mPromptTesting);
|
||||
} else if(name.EqualsASCII(kPrefWritableName)) {
|
||||
mWritableName = Preferences::GetString(kPrefWritableName);
|
||||
uint32_t i = mListeners.Length();
|
||||
DS_LOG_INFO("writable name '%s' (%u)",
|
||||
NS_LossyConvertUTF16toASCII(mWritableName).get(), i);
|
||||
while (i > 0) {
|
||||
--i;
|
||||
mListeners[i]->OnWritableNameChanged();
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
|
||||
nsCOMPtr<nsIVolume> volume = do_QueryInterface(aSubject);
|
||||
if (NS_WARN_IF(!volume)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t i = mListeners.Length();
|
||||
DS_LOG_INFO("volume updated (%u)", i);
|
||||
while (i > 0) {
|
||||
--i;
|
||||
mListeners[i]->OnVolumeStateChanged(volume);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!strcmp(aTopic, kFileWatcherUpdate)) {
|
||||
DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
|
||||
if (NS_WARN_IF(!file)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
auto data = NS_ConvertUTF16toUTF8(aData);
|
||||
uint32_t i = mListeners.Length();
|
||||
DS_LOG_INFO("file updated (%u)", i);
|
||||
while (i > 0) {
|
||||
--i;
|
||||
mListeners[i]->OnFileWatcherUpdate(data, file);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, kDiskSpaceWatcher)) {
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// 'disk-space-watcher' notifications are sent when there is a modification
|
||||
// of a file in a specific location while a low device storage situation
|
||||
// exists or after recovery of a low storage situation. For Firefox OS,
|
||||
// these notifications are specific for apps storage.
|
||||
if (!NS_strcmp(aData, u"full")) {
|
||||
sInstance->mLowDiskSpace = true;
|
||||
} else if (!NS_strcmp(aData, u"free")) {
|
||||
sInstance->mLowDiskSpace = false;
|
||||
} else {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
uint32_t i = mListeners.Length();
|
||||
DS_LOG_INFO("disk space %d (%u)", sInstance->mLowDiskSpace, i);
|
||||
while (i > 0) {
|
||||
--i;
|
||||
mListeners[i]->OnDiskSpaceWatcher(sInstance->mLowDiskSpace);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (NS_WARN_IF(!sInstance)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Shutdown();
|
||||
sInstance = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* Here we convert file-watcher-notify and download-watcher-notify observer
|
||||
events to file-watcher-update events. This is used to be able to
|
||||
broadcast events from one child to another child in B2G. (f.e., if one
|
||||
child decides to add a file, we want to be able to able to send a onchange
|
||||
notifications to every other child watching that device storage object).*/
|
||||
RefPtr<DeviceStorageFile> dsf;
|
||||
if (!strcmp(aTopic, kDownloadWatcherNotify)) {
|
||||
// aSubject will be an nsISupportsString with the native path to the file
|
||||
// in question.
|
||||
|
||||
nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(aSubject);
|
||||
if (!supportsString) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsString path;
|
||||
nsresult rv = supportsString->GetData(path);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The downloader uses the sdcard storage type.
|
||||
nsString volName;
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (DeviceStorageTypeChecker::IsVolumeBased(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD))) {
|
||||
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!vs)) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsIVolume> vol;
|
||||
rv = vs->GetVolumeByPath(path, getter_AddRefs(vol));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_OK;
|
||||
}
|
||||
rv = vol->GetName(volName);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsString mountPoint;
|
||||
rv = vol->GetMountPoint(mountPoint);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (!Substring(path, 0, mountPoint.Length()).Equals(mountPoint)) {
|
||||
return NS_OK;
|
||||
}
|
||||
path = Substring(path, mountPoint.Length() + 1);
|
||||
}
|
||||
#endif
|
||||
dsf = new DeviceStorageFile(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), volName, path);
|
||||
|
||||
} else if (!strcmp(aTopic, kFileWatcherNotify) ||
|
||||
!strcmp(aTopic, kMtpWatcherNotify)) {
|
||||
dsf = static_cast<DeviceStorageFile*>(aSubject);
|
||||
} else {
|
||||
DS_LOG_WARN("unhandled topic '%s'", aTopic);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!dsf || !dsf->mFile)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!XRE_IsParentProcess()) {
|
||||
// Child process. Forward the notification to the parent.
|
||||
ContentChild::GetSingleton()
|
||||
->SendFilePathUpdateNotify(dsf->mStorageType,
|
||||
dsf->mStorageName,
|
||||
dsf->mPath,
|
||||
NS_ConvertUTF16toUTF8(aData));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Multiple storage types may match the same files. So walk through each of
|
||||
// the storage types, and if the extension matches, tell them about it.
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (DeviceStorageTypeChecker::IsSharedMediaRoot(dsf->mStorageType)) {
|
||||
DeviceStorageTypeChecker* typeChecker
|
||||
= DeviceStorageTypeChecker::CreateOrGet();
|
||||
MOZ_ASSERT(typeChecker);
|
||||
|
||||
static const nsLiteralString kMediaTypes[] = {
|
||||
NS_LITERAL_STRING(DEVICESTORAGE_SDCARD),
|
||||
NS_LITERAL_STRING(DEVICESTORAGE_PICTURES),
|
||||
NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS),
|
||||
NS_LITERAL_STRING(DEVICESTORAGE_MUSIC),
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(kMediaTypes); i++) {
|
||||
RefPtr<DeviceStorageFile> dsf2;
|
||||
if (typeChecker->Check(kMediaTypes[i], dsf->mPath)) {
|
||||
if (dsf->mStorageType.Equals(kMediaTypes[i])) {
|
||||
dsf2 = dsf;
|
||||
} else {
|
||||
dsf2 = new DeviceStorageFile(kMediaTypes[i],
|
||||
dsf->mStorageName, dsf->mPath);
|
||||
}
|
||||
obs->NotifyObservers(dsf2, kFileWatcherUpdate, aData);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
obs->NotifyObservers(dsf, kFileWatcherUpdate, aData);
|
||||
}
|
||||
if (strcmp(aTopic, kMtpWatcherNotify)) {
|
||||
// Only send mtp-watcher-updates out if the MTP Server wasn't the one
|
||||
// telling us about the change.
|
||||
obs->NotifyObservers(dsf, kMtpWatcherUpdate, aData);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DeviceStorageStatics::ListenerWrapper::ListenerWrapper(nsDOMDeviceStorage* aListener)
|
||||
: mListener(do_GetWeakReference(static_cast<DOMEventTargetHelper*>(aListener)))
|
||||
, mOwningThread(NS_GetCurrentThread())
|
||||
{
|
||||
}
|
||||
|
||||
DeviceStorageStatics::ListenerWrapper::~ListenerWrapper()
|
||||
{
|
||||
// Even weak pointers are not thread safe
|
||||
NS_ProxyRelease(mOwningThread, mListener.forget());
|
||||
}
|
||||
|
||||
bool
|
||||
DeviceStorageStatics::ListenerWrapper::Equals(nsDOMDeviceStorage* aListener)
|
||||
{
|
||||
bool current = false;
|
||||
mOwningThread->IsOnCurrentThread(¤t);
|
||||
if (current) {
|
||||
// It is only safe to acquire the reference on the owning thread
|
||||
RefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(mListener);
|
||||
return listener.get() == aListener;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageStatics::ListenerWrapper::OnFileWatcherUpdate(const nsCString& aData,
|
||||
DeviceStorageFile* aFile)
|
||||
{
|
||||
RefPtr<ListenerWrapper> self = this;
|
||||
nsCString data = aData;
|
||||
RefPtr<DeviceStorageFile> file = aFile;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, data, file] () -> void {
|
||||
RefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
|
||||
if (listener) {
|
||||
listener->OnFileWatcherUpdate(data, file);
|
||||
}
|
||||
});
|
||||
mOwningThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageStatics::ListenerWrapper::OnDiskSpaceWatcher(bool aLowDiskSpace)
|
||||
{
|
||||
RefPtr<ListenerWrapper> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aLowDiskSpace] () -> void {
|
||||
RefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
|
||||
if (listener) {
|
||||
listener->OnDiskSpaceWatcher(aLowDiskSpace);
|
||||
}
|
||||
});
|
||||
mOwningThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageStatics::ListenerWrapper::OnWritableNameChanged()
|
||||
{
|
||||
RefPtr<ListenerWrapper> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void {
|
||||
RefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
|
||||
if (listener) {
|
||||
listener->OnWritableNameChanged();
|
||||
}
|
||||
});
|
||||
mOwningThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
void
|
||||
DeviceStorageStatics::ListenerWrapper::OnVolumeStateChanged(nsIVolume* aVolume)
|
||||
{
|
||||
RefPtr<ListenerWrapper> self = this;
|
||||
nsCOMPtr<nsIVolume> volume = aVolume;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, volume] () -> void {
|
||||
RefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
|
||||
if (listener) {
|
||||
listener->OnVolumeStateChanged(volume);
|
||||
}
|
||||
});
|
||||
mOwningThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace devicestorage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,115 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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_devicestorage_DeviceStorageStatics_h
|
||||
#define mozilla_dom_devicestorage_DeviceStorageStatics_h
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsArrayUtils.h"
|
||||
|
||||
class nsString;
|
||||
class nsDOMDeviceStorage;
|
||||
class DeviceStorageFile;
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
class nsIVolume;
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace devicestorage {
|
||||
|
||||
class DeviceStorageStatics final : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static void Initialize();
|
||||
static void InitializeDirs();
|
||||
static void AddListener(nsDOMDeviceStorage* aListener);
|
||||
static void RemoveListener(nsDOMDeviceStorage* aListener);
|
||||
|
||||
static bool LowDiskSpace();
|
||||
static bool IsPromptTesting();
|
||||
static void GetWritableName(nsString& aName);
|
||||
static void SetWritableName(const nsAString& aName);
|
||||
|
||||
static void GetDeviceStorageLocationsForIPC(DeviceStorageLocationInfo* aLocationInfo);
|
||||
|
||||
static bool HasOverrideRootDir();
|
||||
static already_AddRefed<nsIFile> GetAppsDir();
|
||||
static already_AddRefed<nsIFile> GetCrashesDir();
|
||||
static already_AddRefed<nsIFile> GetPicturesDir();
|
||||
static already_AddRefed<nsIFile> GetVideosDir();
|
||||
static already_AddRefed<nsIFile> GetMusicDir();
|
||||
static already_AddRefed<nsIFile> GetSdcardDir();
|
||||
|
||||
private:
|
||||
enum DeviceStorageType {
|
||||
TYPE_APPS,
|
||||
TYPE_CRASHES,
|
||||
TYPE_PICTURES,
|
||||
TYPE_VIDEOS,
|
||||
TYPE_MUSIC,
|
||||
TYPE_SDCARD,
|
||||
TYPE_OVERRIDE,
|
||||
TYPE_COUNT
|
||||
};
|
||||
|
||||
static already_AddRefed<nsIFile> GetDir(DeviceStorageType aType);
|
||||
static void GetDirPath(DeviceStorageType aType, nsString& aString);
|
||||
|
||||
DeviceStorageStatics();
|
||||
virtual ~DeviceStorageStatics();
|
||||
|
||||
void Init();
|
||||
void InitDirs();
|
||||
void DumpDirs();
|
||||
void Shutdown();
|
||||
void Register();
|
||||
void Deregister();
|
||||
void ResetOverrideRootDir();
|
||||
|
||||
class ListenerWrapper final {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ListenerWrapper)
|
||||
|
||||
explicit ListenerWrapper(nsDOMDeviceStorage* aListener);
|
||||
bool Equals(nsDOMDeviceStorage* aListener);
|
||||
void OnFileWatcherUpdate(const nsCString& aData, DeviceStorageFile* aFile);
|
||||
void OnDiskSpaceWatcher(bool aLowDiskSpace);
|
||||
void OnWritableNameChanged();
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
void OnVolumeStateChanged(nsIVolume* aVolume);
|
||||
#endif
|
||||
|
||||
private:
|
||||
virtual ~ListenerWrapper();
|
||||
|
||||
nsWeakPtr mListener;
|
||||
nsCOMPtr<nsIThread> mOwningThread;
|
||||
};
|
||||
|
||||
nsTArray<RefPtr<ListenerWrapper> > mListeners;
|
||||
nsCOMPtr<nsIFile> mDirs[TYPE_COUNT];
|
||||
|
||||
bool mInitialized;
|
||||
bool mPromptTesting;
|
||||
bool mLowDiskSpace;
|
||||
nsString mWritableName;
|
||||
|
||||
static StaticRefPtr<DeviceStorageStatics> sInstance;
|
||||
static StaticMutex sMutex;
|
||||
};
|
||||
|
||||
} // namespace devicestorage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -1,93 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; 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/. */
|
||||
|
||||
include protocol PBlob;
|
||||
include protocol PContent;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace devicestorage {
|
||||
|
||||
|
||||
struct ErrorResponse
|
||||
{
|
||||
nsString error;
|
||||
};
|
||||
|
||||
struct SuccessResponse
|
||||
{
|
||||
};
|
||||
|
||||
struct FileDescriptorResponse
|
||||
{
|
||||
FileDescriptor fileDescriptor;
|
||||
};
|
||||
|
||||
struct BlobResponse
|
||||
{
|
||||
PBlob blob;
|
||||
};
|
||||
|
||||
struct DeviceStorageFileValue
|
||||
{
|
||||
nsString storageName;
|
||||
nsString name;
|
||||
};
|
||||
|
||||
struct EnumerationResponse
|
||||
{
|
||||
nsString type;
|
||||
nsString rootdir;
|
||||
DeviceStorageFileValue[] paths;
|
||||
};
|
||||
|
||||
struct FreeSpaceStorageResponse
|
||||
{
|
||||
uint64_t freeBytes;
|
||||
};
|
||||
|
||||
struct UsedSpaceStorageResponse
|
||||
{
|
||||
uint64_t usedBytes;
|
||||
};
|
||||
|
||||
struct FormatStorageResponse
|
||||
{
|
||||
nsString mountState;
|
||||
};
|
||||
|
||||
struct MountStorageResponse
|
||||
{
|
||||
nsString storageStatus;
|
||||
};
|
||||
|
||||
struct UnmountStorageResponse
|
||||
{
|
||||
nsString storageStatus;
|
||||
};
|
||||
|
||||
union DeviceStorageResponseValue
|
||||
{
|
||||
ErrorResponse;
|
||||
SuccessResponse;
|
||||
FileDescriptorResponse;
|
||||
BlobResponse;
|
||||
EnumerationResponse;
|
||||
FreeSpaceStorageResponse;
|
||||
UsedSpaceStorageResponse;
|
||||
FormatStorageResponse;
|
||||
MountStorageResponse;
|
||||
UnmountStorageResponse;
|
||||
};
|
||||
|
||||
sync protocol PDeviceStorageRequest {
|
||||
manager PContent;
|
||||
child:
|
||||
async __delete__(DeviceStorageResponseValue response);
|
||||
};
|
||||
|
||||
} // namespace devicestorage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,50 +0,0 @@
|
||||
# -*- Mode: python; 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/.
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
|
||||
|
||||
|
||||
EXPORTS += [
|
||||
'DeviceStorage.h',
|
||||
'DeviceStorageFileDescriptor.h',
|
||||
'nsDeviceStorage.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'DeviceStorageAreaListener.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom.devicestorage += [
|
||||
'DeviceStorageRequestChild.h',
|
||||
'DeviceStorageRequestParent.h',
|
||||
'DeviceStorageStatics.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'DeviceStorageAreaListener.cpp',
|
||||
'DeviceStorageRequestChild.cpp',
|
||||
'DeviceStorageRequestParent.cpp',
|
||||
'DeviceStorageStatics.cpp',
|
||||
'nsDeviceStorage.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PDeviceStorageRequest.ipdl',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/dom/ipc',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'test/mochitest.ini',
|
||||
]
|
||||
MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
|
File diff suppressed because it is too large
Load Diff
@ -1,434 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 nsDeviceStorage_h
|
||||
#define nsDeviceStorage_h
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
|
||||
|
||||
#include "DOMRequest.h"
|
||||
#include "DOMCursor.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDOMClassInfoID.h"
|
||||
#include "nsIClassInfo.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWeakPtr.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "prtime.h"
|
||||
#include "DeviceStorage.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
class BlobImpl;
|
||||
class DeviceStorageParams;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
class nsDOMDeviceStorage;
|
||||
class DeviceStorageCursorRequest;
|
||||
|
||||
//#define DS_LOGGING 1
|
||||
|
||||
#ifdef DS_LOGGING
|
||||
// FIXME -- use MOZ_LOG and set to warn by default
|
||||
#define DS_LOG_DEBUG(msg, ...) printf_stderr("[%s:%d] " msg "\n", __func__, __LINE__, ##__VA_ARGS__)
|
||||
#define DS_LOG_INFO DS_LOG_DEBUG
|
||||
#define DS_LOG_WARN DS_LOG_DEBUG
|
||||
#define DS_LOG_ERROR DS_LOG_DEBUG
|
||||
#else
|
||||
#define DS_LOG_DEBUG(msg, ...)
|
||||
#define DS_LOG_INFO(msg, ...)
|
||||
#define DS_LOG_WARN(msg, ...)
|
||||
#define DS_LOG_ERROR(msg, ...)
|
||||
#endif
|
||||
|
||||
#define POST_ERROR_EVENT_FILE_EXISTS "NoModificationAllowedError"
|
||||
#define POST_ERROR_EVENT_FILE_DOES_NOT_EXIST "NotFoundError"
|
||||
#define POST_ERROR_EVENT_FILE_NOT_ENUMERABLE "TypeMismatchError"
|
||||
#define POST_ERROR_EVENT_PERMISSION_DENIED "SecurityError"
|
||||
#define POST_ERROR_EVENT_ILLEGAL_TYPE "TypeMismatchError"
|
||||
#define POST_ERROR_EVENT_UNKNOWN "Unknown"
|
||||
|
||||
enum DeviceStorageRequestType {
|
||||
DEVICE_STORAGE_REQUEST_READ,
|
||||
DEVICE_STORAGE_REQUEST_WRITE,
|
||||
DEVICE_STORAGE_REQUEST_APPEND,
|
||||
DEVICE_STORAGE_REQUEST_CREATE,
|
||||
DEVICE_STORAGE_REQUEST_DELETE,
|
||||
DEVICE_STORAGE_REQUEST_WATCH,
|
||||
DEVICE_STORAGE_REQUEST_FREE_SPACE,
|
||||
DEVICE_STORAGE_REQUEST_USED_SPACE,
|
||||
DEVICE_STORAGE_REQUEST_AVAILABLE,
|
||||
DEVICE_STORAGE_REQUEST_STATUS,
|
||||
DEVICE_STORAGE_REQUEST_FORMAT,
|
||||
DEVICE_STORAGE_REQUEST_MOUNT,
|
||||
DEVICE_STORAGE_REQUEST_UNMOUNT,
|
||||
DEVICE_STORAGE_REQUEST_CREATEFD,
|
||||
DEVICE_STORAGE_REQUEST_CURSOR
|
||||
};
|
||||
|
||||
enum DeviceStorageAccessType {
|
||||
DEVICE_STORAGE_ACCESS_READ,
|
||||
DEVICE_STORAGE_ACCESS_WRITE,
|
||||
DEVICE_STORAGE_ACCESS_CREATE,
|
||||
DEVICE_STORAGE_ACCESS_UNDEFINED,
|
||||
DEVICE_STORAGE_ACCESS_COUNT
|
||||
};
|
||||
|
||||
class DeviceStorageUsedSpaceCache final
|
||||
{
|
||||
public:
|
||||
static DeviceStorageUsedSpaceCache* CreateOrGet();
|
||||
|
||||
DeviceStorageUsedSpaceCache();
|
||||
~DeviceStorageUsedSpaceCache();
|
||||
|
||||
|
||||
class InvalidateRunnable final : public mozilla::Runnable
|
||||
{
|
||||
public:
|
||||
InvalidateRunnable(DeviceStorageUsedSpaceCache* aCache,
|
||||
const nsAString& aStorageName)
|
||||
: mCache(aCache)
|
||||
, mStorageName(aStorageName) {}
|
||||
|
||||
~InvalidateRunnable() {}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
RefPtr<DeviceStorageUsedSpaceCache::CacheEntry> cacheEntry;
|
||||
cacheEntry = mCache->GetCacheEntry(mStorageName);
|
||||
if (cacheEntry) {
|
||||
cacheEntry->mDirty = true;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
DeviceStorageUsedSpaceCache* mCache;
|
||||
nsString mStorageName;
|
||||
};
|
||||
|
||||
void Invalidate(const nsAString& aStorageName)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mIOThread);
|
||||
|
||||
mIOThread->Dispatch(new InvalidateRunnable(this, aStorageName),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void Dispatch(already_AddRefed<nsIRunnable>&& aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mIOThread);
|
||||
|
||||
mIOThread->Dispatch(mozilla::Move(aRunnable), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
nsresult AccumUsedSizes(const nsAString& aStorageName,
|
||||
uint64_t* aPictureSize, uint64_t* aVideosSize,
|
||||
uint64_t* aMusicSize, uint64_t* aTotalSize);
|
||||
|
||||
void SetUsedSizes(const nsAString& aStorageName,
|
||||
uint64_t aPictureSize, uint64_t aVideosSize,
|
||||
uint64_t aMusicSize, uint64_t aTotalSize);
|
||||
|
||||
private:
|
||||
friend class InvalidateRunnable;
|
||||
|
||||
struct CacheEntry
|
||||
{
|
||||
// Technically, this doesn't need to be threadsafe, but the implementation
|
||||
// of the non-thread safe one causes ASSERTS due to the underlying thread
|
||||
// associated with a LazyIdleThread changing from time to time.
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CacheEntry)
|
||||
|
||||
bool mDirty;
|
||||
nsString mStorageName;
|
||||
int64_t mFreeBytes;
|
||||
uint64_t mPicturesUsedSize;
|
||||
uint64_t mVideosUsedSize;
|
||||
uint64_t mMusicUsedSize;
|
||||
uint64_t mTotalUsedSize;
|
||||
|
||||
private:
|
||||
~CacheEntry() {}
|
||||
};
|
||||
already_AddRefed<CacheEntry> GetCacheEntry(const nsAString& aStorageName);
|
||||
|
||||
nsTArray<RefPtr<CacheEntry>> mCacheEntries;
|
||||
|
||||
nsCOMPtr<nsIThread> mIOThread;
|
||||
|
||||
static mozilla::StaticAutoPtr<DeviceStorageUsedSpaceCache> sDeviceStorageUsedSpaceCache;
|
||||
};
|
||||
|
||||
class DeviceStorageTypeChecker final
|
||||
{
|
||||
public:
|
||||
static DeviceStorageTypeChecker* CreateOrGet();
|
||||
|
||||
DeviceStorageTypeChecker();
|
||||
~DeviceStorageTypeChecker();
|
||||
|
||||
void InitFromBundle(nsIStringBundle* aBundle);
|
||||
|
||||
bool Check(const nsAString& aType, mozilla::dom::BlobImpl* aBlob);
|
||||
bool Check(const nsAString& aType, nsIFile* aFile);
|
||||
bool Check(const nsAString& aType, const nsString& aPath);
|
||||
void GetTypeFromFile(nsIFile* aFile, nsAString& aType);
|
||||
void GetTypeFromFileName(const nsAString& aFileName, nsAString& aType);
|
||||
static nsresult GetPermissionForType(const nsAString& aType,
|
||||
nsACString& aPermissionResult);
|
||||
static nsresult GetAccessForRequest(const DeviceStorageRequestType aRequestType,
|
||||
nsACString& aAccessResult);
|
||||
static nsresult GetAccessForIndex(size_t aAccessIndex, nsACString& aAccessResult);
|
||||
static size_t GetAccessIndexForRequest(const DeviceStorageRequestType aRequestType);
|
||||
static bool IsVolumeBased(const nsAString& aType);
|
||||
static bool IsSharedMediaRoot(const nsAString& aType);
|
||||
|
||||
private:
|
||||
nsString mPicturesExtensions;
|
||||
nsString mVideosExtensions;
|
||||
nsString mMusicExtensions;
|
||||
|
||||
static mozilla::StaticAutoPtr<DeviceStorageTypeChecker> sDeviceStorageTypeChecker;
|
||||
};
|
||||
|
||||
class nsDOMDeviceStorageCursor final
|
||||
: public mozilla::dom::DOMCursor
|
||||
{
|
||||
public:
|
||||
NS_FORWARD_NSIDOMDOMCURSOR(mozilla::dom::DOMCursor::)
|
||||
|
||||
// DOMCursor
|
||||
virtual void Continue(mozilla::ErrorResult& aRv) override;
|
||||
|
||||
nsDOMDeviceStorageCursor(nsIGlobalObject* aGlobal,
|
||||
DeviceStorageCursorRequest* aRequest);
|
||||
|
||||
void FireSuccess(JS::Handle<JS::Value> aResult);
|
||||
void FireError(const nsString& aReason);
|
||||
void FireDone();
|
||||
|
||||
private:
|
||||
virtual ~nsDOMDeviceStorageCursor();
|
||||
|
||||
bool mOkToCallContinue;
|
||||
RefPtr<DeviceStorageCursorRequest> mRequest;
|
||||
};
|
||||
|
||||
class DeviceStorageRequestManager final
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DeviceStorageRequestManager)
|
||||
|
||||
static const uint32_t INVALID_ID = 0;
|
||||
|
||||
DeviceStorageRequestManager();
|
||||
|
||||
bool IsOwningThread();
|
||||
nsresult DispatchToOwningThread(already_AddRefed<nsIRunnable>&& aRunnable);
|
||||
|
||||
void StorePermission(size_t aAccess, bool aAllow);
|
||||
uint32_t CheckPermission(size_t aAccess);
|
||||
|
||||
/* These must be called on the owning thread context of the device
|
||||
storage object. It will hold onto a device storage reference until
|
||||
all of the pending requests are completed or shutdown is called. */
|
||||
uint32_t Create(nsDOMDeviceStorage* aDeviceStorage,
|
||||
mozilla::dom::DOMRequest** aRequest);
|
||||
uint32_t Create(nsDOMDeviceStorage* aDeviceStorage,
|
||||
DeviceStorageCursorRequest* aRequest,
|
||||
nsDOMDeviceStorageCursor** aCursor);
|
||||
|
||||
/* These may be called from any thread context and post a request
|
||||
to the owning thread to resolve the underlying DOMRequest or
|
||||
DOMCursor. In order to trigger FireDone for a DOMCursor, one
|
||||
should call Resolve with only the request ID. */
|
||||
nsresult Resolve(uint32_t aId, bool aForceDispatch);
|
||||
nsresult Resolve(uint32_t aId, const nsString& aValue, bool aForceDispatch);
|
||||
nsresult Resolve(uint32_t aId, uint64_t aValue, bool aForceDispatch);
|
||||
nsresult Resolve(uint32_t aId, DeviceStorageFile* aValue, bool aForceDispatch);
|
||||
nsresult Resolve(uint32_t aId, mozilla::dom::BlobImpl* aValue, bool aForceDispatch);
|
||||
nsresult Reject(uint32_t aId, const nsString& aReason);
|
||||
nsresult Reject(uint32_t aId, const char* aReason);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
DeviceStorageRequestManager(const DeviceStorageRequestManager&) = delete;
|
||||
DeviceStorageRequestManager& operator=(const DeviceStorageRequestManager&) = delete;
|
||||
|
||||
struct ListEntry {
|
||||
RefPtr<mozilla::dom::DOMRequest> mRequest;
|
||||
uint32_t mId;
|
||||
bool mCursor;
|
||||
};
|
||||
|
||||
typedef nsTArray<ListEntry> ListType;
|
||||
typedef ListType::index_type ListIndex;
|
||||
|
||||
virtual ~DeviceStorageRequestManager();
|
||||
uint32_t CreateInternal(mozilla::dom::DOMRequest* aRequest, bool aCursor);
|
||||
nsresult ResolveInternal(ListIndex aIndex, JS::HandleValue aResult);
|
||||
nsresult RejectInternal(ListIndex aIndex, const nsString& aReason);
|
||||
nsresult DispatchOrAbandon(uint32_t aId,
|
||||
already_AddRefed<nsIRunnable>&& aRunnable);
|
||||
ListType::index_type Find(uint32_t aId);
|
||||
|
||||
nsCOMPtr<nsIThread> mOwningThread;
|
||||
ListType mPending; // owning thread or destructor only
|
||||
|
||||
mozilla::Mutex mMutex;
|
||||
uint32_t mPermissionCache[DEVICE_STORAGE_ACCESS_COUNT];
|
||||
bool mShutdown;
|
||||
|
||||
static mozilla::Atomic<uint32_t> sLastRequestId;
|
||||
};
|
||||
|
||||
class DeviceStorageRequest
|
||||
: public mozilla::Runnable
|
||||
{
|
||||
protected:
|
||||
DeviceStorageRequest();
|
||||
|
||||
public:
|
||||
virtual void Initialize(DeviceStorageRequestManager* aManager,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile,
|
||||
uint32_t aRequest);
|
||||
|
||||
virtual void Initialize(DeviceStorageRequestManager* aManager,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile,
|
||||
uint32_t aRequest,
|
||||
mozilla::dom::BlobImpl* aBlob);
|
||||
|
||||
virtual void Initialize(DeviceStorageRequestManager* aManager,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile,
|
||||
uint32_t aRequest,
|
||||
DeviceStorageFileDescriptor* aDSFileDescriptor);
|
||||
|
||||
DeviceStorageAccessType GetAccess() const;
|
||||
void GetStorageType(nsAString& aType) const;
|
||||
DeviceStorageFile* GetFile() const;
|
||||
DeviceStorageFileDescriptor* GetFileDescriptor() const;
|
||||
DeviceStorageRequestManager* GetManager() const;
|
||||
|
||||
uint32_t GetId() const
|
||||
{
|
||||
return mId;
|
||||
}
|
||||
|
||||
void PermissionCacheMissed()
|
||||
{
|
||||
mPermissionCached = false;
|
||||
}
|
||||
|
||||
nsresult Cancel();
|
||||
nsresult Allow();
|
||||
|
||||
nsresult Resolve()
|
||||
{
|
||||
/* Always dispatch an empty resolve because that signals a cursor end
|
||||
and should not be executed directly from the caller's context due
|
||||
to the object potentially getting freed before we return. */
|
||||
uint32_t id = mId;
|
||||
mId = DeviceStorageRequestManager::INVALID_ID;
|
||||
return mManager->Resolve(id, true);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
nsresult Resolve(T aValue)
|
||||
{
|
||||
uint32_t id = mId;
|
||||
if (!mMultipleResolve) {
|
||||
mId = DeviceStorageRequestManager::INVALID_ID;
|
||||
}
|
||||
return mManager->Resolve(id, aValue, ForceDispatch());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
nsresult Reject(T aReason)
|
||||
{
|
||||
uint32_t id = mId;
|
||||
mId = DeviceStorageRequestManager::INVALID_ID;
|
||||
return mManager->Reject(id, aReason);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool ForceDispatch() const
|
||||
{
|
||||
return !mSendToParent && mPermissionCached;
|
||||
}
|
||||
|
||||
virtual ~DeviceStorageRequest();
|
||||
virtual nsresult Prepare();
|
||||
virtual nsresult CreateSendParams(mozilla::dom::DeviceStorageParams& aParams);
|
||||
nsresult AllowInternal();
|
||||
nsresult SendToParentProcess();
|
||||
|
||||
RefPtr<DeviceStorageRequestManager> mManager;
|
||||
RefPtr<DeviceStorageFile> mFile;
|
||||
uint32_t mId;
|
||||
RefPtr<mozilla::dom::BlobImpl> mBlob;
|
||||
RefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
|
||||
DeviceStorageAccessType mAccess;
|
||||
bool mSendToParent;
|
||||
bool mUseMainThread;
|
||||
bool mUseStreamTransport;
|
||||
bool mCheckFile;
|
||||
bool mCheckBlob;
|
||||
bool mMultipleResolve;
|
||||
bool mPermissionCached;
|
||||
|
||||
private:
|
||||
DeviceStorageRequest(const DeviceStorageRequest&) = delete;
|
||||
DeviceStorageRequest& operator=(const DeviceStorageRequest&) = delete;
|
||||
};
|
||||
|
||||
class DeviceStorageCursorRequest final
|
||||
: public DeviceStorageRequest
|
||||
{
|
||||
public:
|
||||
DeviceStorageCursorRequest();
|
||||
|
||||
using DeviceStorageRequest::Initialize;
|
||||
|
||||
virtual void Initialize(DeviceStorageRequestManager* aManager,
|
||||
already_AddRefed<DeviceStorageFile>&& aFile,
|
||||
uint32_t aRequest,
|
||||
PRTime aSince);
|
||||
|
||||
void AddFiles(size_t aSize);
|
||||
void AddFile(already_AddRefed<DeviceStorageFile> aFile);
|
||||
nsresult Continue();
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
protected:
|
||||
virtual ~DeviceStorageCursorRequest()
|
||||
{ };
|
||||
|
||||
nsresult SendContinueToParentProcess();
|
||||
nsresult CreateSendParams(mozilla::dom::DeviceStorageParams& aParams) override;
|
||||
|
||||
size_t mIndex;
|
||||
PRTime mSince;
|
||||
nsString mStorageType;
|
||||
nsTArray<RefPtr<DeviceStorageFile> > mFiles;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,5 +0,0 @@
|
||||
[DEFAULT]
|
||||
skip-if = os == 'android'
|
||||
|
||||
[test_app_permissions.html]
|
||||
[test_fs_app_permissions.html]
|
@ -1,110 +0,0 @@
|
||||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var oldVal = false;
|
||||
|
||||
Object.defineProperty(Array.prototype, "remove", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
value: function(from, to) {
|
||||
// Array Remove - By John Resig (MIT Licensed)
|
||||
var rest = this.slice((to || from) + 1 || this.length);
|
||||
this.length = from < 0 ? this.length + from : from;
|
||||
return this.push.apply(this, rest);
|
||||
}
|
||||
});
|
||||
|
||||
function devicestorage_setup(callback) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const Cc = SpecialPowers.Cc;
|
||||
const Ci = SpecialPowers.Ci;
|
||||
var directoryService = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties);
|
||||
var f = directoryService.get("TmpD", Ci.nsIFile);
|
||||
f.appendRelativePath("device-storage-testing");
|
||||
|
||||
let script = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('remove_testing_directory.js'));
|
||||
|
||||
script.addMessageListener('directory-removed', function listener () {
|
||||
script.removeMessageListener('directory-removed', listener);
|
||||
var prefs = [["device.storage.enabled", true],
|
||||
["device.storage.testing", true],
|
||||
["device.storage.overrideRootDir", f.path],
|
||||
["device.storage.prompt.testing", true]];
|
||||
SpecialPowers.pushPrefEnv({"set": prefs}, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function getRandomBuffer() {
|
||||
var size = 1024;
|
||||
var buffer = new ArrayBuffer(size);
|
||||
var view = new Uint8Array(buffer);
|
||||
for (var i = 0; i < size; i++) {
|
||||
view[i] = parseInt(Math.random() * 255);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function createRandomBlob(mime) {
|
||||
return blob = new Blob([getRandomBuffer()], {type: mime});
|
||||
}
|
||||
|
||||
function randomFilename(l) {
|
||||
var set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ";
|
||||
var result = "";
|
||||
for (var i=0; i<l; i++) {
|
||||
var r = Math.floor(set.length * Math.random());
|
||||
result += set.substring(r, r + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function reportErrorAndQuit(e) {
|
||||
ok(false, "handleError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function createTestFiles(storage, paths) {
|
||||
function createTestFile(path) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
function addNamed() {
|
||||
var req = storage.addNamed(createRandomBlob("image/png"), path);
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(true, path + " was created.");
|
||||
resolve();
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
ok(false, "Failed to create " + path + ': ' + e.target.error.name);
|
||||
reject();
|
||||
};
|
||||
}
|
||||
|
||||
// Bug 980136. Check if the file exists before we create.
|
||||
var req = storage.get(path);
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(true, path + " exists. Do not need to create.");
|
||||
resolve();
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
ok(true, path + " does not exists: " + e.target.error.name);
|
||||
addNamed();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
var arr = [];
|
||||
|
||||
paths.forEach(function(path) {
|
||||
arr.push(createTestFile(path));
|
||||
});
|
||||
|
||||
return Promise.all(arr);
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
[DEFAULT]
|
||||
support-files = devicestorage_common.js
|
||||
remove_testing_directory.js
|
||||
|
||||
[test_823965.html]
|
||||
# [test_add.html]
|
||||
# man, our mime database sucks hard. followup bug # 788273
|
||||
[test_addCorrectType.html]
|
||||
[test_available.html]
|
||||
[test_basic.html]
|
||||
[test_dirs.html]
|
||||
# [test_diskSpace.html]
|
||||
# Possible race between the time we write a file, and the
|
||||
# time it takes to be reflected by statfs(). Bug # 791287
|
||||
[test_dotdot.html]
|
||||
[test_enumerate.html]
|
||||
[test_enumerateMultipleContinue.html]
|
||||
[test_enumerateOptions.html]
|
||||
[test_freeSpace.html]
|
||||
# FileSystem API tests start
|
||||
[test_fs_basic.html]
|
||||
[test_fs_createDirectory.html]
|
||||
[test_fs_get.html]
|
||||
[test_fs_getFilesAndDirectories.html]
|
||||
[test_fs_remove.html]
|
||||
[test_fs_createFile.html]
|
||||
[test_fs_appendFile.html]
|
||||
# FileSystem API tests end
|
||||
[test_lastModificationFilter.html]
|
||||
[test_overrideDir.html]
|
||||
[test_overwrite.html]
|
||||
[test_sanity.html]
|
||||
[test_usedSpace.html]
|
||||
[test_watch.html]
|
||||
[test_watchOther.html]
|
@ -1,11 +0,0 @@
|
||||
// ensure that the directory we are writing into is empty
|
||||
try {
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var directoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
var f = directoryService.get("TmpD", Ci.nsIFile);
|
||||
f.appendRelativePath("device-storage-testing");
|
||||
f.remove(true);
|
||||
} catch(e) {}
|
||||
|
||||
sendAsyncMessage('directory-removed', {});
|
@ -1,107 +0,0 @@
|
||||
<!--
|
||||
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=823965
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=823965">Mozilla Bug 823965</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var gFileName = "devicestorage/" + randomFilename(12) + "/hi.png";
|
||||
var gData = "My name is Doug Turner (?!?). My IRC nick is DougT. I like Maple cookies."
|
||||
var gDataBlob = new Blob([gData], {type: 'image/png'});
|
||||
|
||||
function getSuccess(e) {
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
ok(e.target.result.name == gFileName, "File name should match");
|
||||
ok(e.target.result.size > 0, "File size be greater than zero");
|
||||
ok(e.target.result.type, "File should have a mime type");
|
||||
ok(e.target.result.lastModifiedDate, "File should have a last modified date");
|
||||
|
||||
var mreq = storage.enumerate();
|
||||
mreq.onsuccess = function() {
|
||||
var storage2 = navigator.getDeviceStorage('music');
|
||||
var dreq = storage2.delete(mreq.result.name);
|
||||
dreq.onerror = function () {
|
||||
ok(true, "The bug has been fixed");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
dreq.onsuccess = function () {
|
||||
ok(false, "The bug has been fixed");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
};
|
||||
|
||||
mreq.onerror = getError;
|
||||
}
|
||||
|
||||
function getError(e) {
|
||||
ok(false, "getError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
|
||||
var filename = e.target.result;
|
||||
if (filename[0] == "/") {
|
||||
// We got /storageName/prefix/filename
|
||||
// Remove the storageName (this shows up on FirefoxOS)
|
||||
filename = filename.substring(1); // Remove leading slash
|
||||
var slashIndex = filename.indexOf("/");
|
||||
if (slashIndex >= 0) {
|
||||
filename = filename.substring(slashIndex + 1); // Remove storageName
|
||||
}
|
||||
}
|
||||
ok(filename == gFileName, "File name should match");
|
||||
// Since we now have the fully qualified name, change gFileName to that.
|
||||
gFileName = e.target.result;
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
request = storage.get(gFileName);
|
||||
request.onsuccess = getSuccess;
|
||||
request.onerror = getError;
|
||||
|
||||
ok(true, "addSuccess was called");
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(storage, "Should have gotten a storage");
|
||||
|
||||
request = storage.addNamed(gDataBlob, gFileName);
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,70 +0,0 @@
|
||||
<!--
|
||||
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=786922
|
||||
-->
|
||||
<head>
|
||||
<title>Test for basic sanity of the device storage API </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=786922">Mozilla Bug 786922</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function add(storage, mime) {
|
||||
dump("adding: " + mime + "\n");
|
||||
return navigator.getDeviceStorage(storage).add(createRandomBlob(mime));
|
||||
}
|
||||
|
||||
var tests = [
|
||||
function () { return add("pictures", "image/png")},
|
||||
function () { return add("videos", "video/webm")},
|
||||
function () { return add("music", "audio/wav")},
|
||||
function () { return add("sdcard", "maple/cookies")},
|
||||
];
|
||||
|
||||
function fail(e) {
|
||||
ok(false, "onerror was called");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function next(e) {
|
||||
|
||||
if (e != undefined)
|
||||
ok(true, "addError was called");
|
||||
|
||||
var f = tests.pop();
|
||||
|
||||
if (f == undefined) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
request = f();
|
||||
request.onsuccess = next;
|
||||
request.onerror = fail;
|
||||
}
|
||||
|
||||
next();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,75 +0,0 @@
|
||||
<!--
|
||||
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=786922
|
||||
-->
|
||||
<head>
|
||||
<title>Test for basic sanity of the device storage API </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=786922">Mozilla Bug 786922</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function addNamed(storage, mime, fileExtension) {
|
||||
dump("adding: " + mime + " " + fileExtension + "\n");
|
||||
return navigator.getDeviceStorage(storage).addNamed(createRandomBlob(mime), randomFilename(40) + "." + fileExtension);
|
||||
}
|
||||
|
||||
// These tests must all fail
|
||||
var tests = [
|
||||
function () { return addNamed("pictures", "kyle/smash", ".png")},
|
||||
function () { return addNamed("pictures", "image/png", ".poo")},
|
||||
function () { return addNamed("music", "kyle/smash", ".mp3")},
|
||||
function () { return addNamed("music", "music/mp3", ".poo")},
|
||||
function () { return addNamed("videos", "kyle/smash", ".ogv")},
|
||||
function () { return addNamed("videos", "video/ogv", ".poo")},
|
||||
];
|
||||
|
||||
function fail(e) {
|
||||
ok(false, "addSuccess was called");
|
||||
ok(e.target.error.name == "TypeMismatchError", "Error must be TypeMismatchError");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function next(e) {
|
||||
|
||||
if (e != undefined)
|
||||
ok(true, "addError was called");
|
||||
|
||||
var f = tests.pop();
|
||||
|
||||
if (f == undefined) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
request = f();
|
||||
request.onsuccess = fail;
|
||||
request.onerror = next;
|
||||
}
|
||||
|
||||
next();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,481 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=805322
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Permission test 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=805322">Mozilla Bug 805322</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
function randomFilename(l) {
|
||||
var set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ";
|
||||
var result = "";
|
||||
for (var i=0; i<l; i++) {
|
||||
var r = Math.floor(set.length * Math.random());
|
||||
result += set.substring(r, r + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
var MockPermissionPrompt = SpecialPowers.MockPermissionPrompt;
|
||||
MockPermissionPrompt.init();
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function TestAdd(iframe, data) {
|
||||
|
||||
var storage = iframe.contentDocument.defaultView.navigator.getDeviceStorage(data.type);
|
||||
isnot(storage, null, "Should be able to get storage object for " + data.type);
|
||||
|
||||
var blob = new Blob(["Kyle Huey is not a helicopter."], {type: data.mimeType});
|
||||
|
||||
request = storage.addNamed(blob, randomFilename(100) + "hi" + data.fileExtension);
|
||||
isnot(request, null, "Should be able to get request");
|
||||
|
||||
request.onsuccess = function() {
|
||||
is(data.shouldPass, true, "onsuccess was called for type " + data.type);
|
||||
testComplete(iframe, data);
|
||||
};
|
||||
|
||||
request.onerror = function(e) {
|
||||
isnot(data.shouldPass, true, "onfailure was called for type " + data.type + " Error: " + e.target.error.name);
|
||||
is(e.target.error.name, "SecurityError", "onerror should fire a SecurityError");
|
||||
testComplete(iframe, data);
|
||||
};
|
||||
}
|
||||
|
||||
function TestGet(iframe, data) {
|
||||
|
||||
createTestFile(data.fileExtension);
|
||||
|
||||
var storage = iframe.contentDocument.defaultView.navigator.getDeviceStorage(data.type);
|
||||
isnot(storage, null, "Should be able to get storage object for " + data.type);
|
||||
|
||||
request = storage.get("testfile" + data.fileExtension);
|
||||
isnot(request, null, "Should be able to get request");
|
||||
|
||||
request.onsuccess = function() {
|
||||
is(data.shouldPass, true, "onsuccess was called for type " + data.type);
|
||||
testComplete(iframe, data);
|
||||
};
|
||||
|
||||
request.onerror = function(e) {
|
||||
isnot(data.shouldPass, true, "onfailure was called for type " + data.type + " Error: " + e.target.error.name);
|
||||
testComplete(iframe, data);
|
||||
};
|
||||
}
|
||||
|
||||
function TestDelete(iframe, data) {
|
||||
|
||||
createTestFile(data.fileExtension);
|
||||
|
||||
var storage = iframe.contentDocument.defaultView.navigator.getDeviceStorage(data.type);
|
||||
isnot(storage, null, "Should be able to get storage object for " + data.type);
|
||||
|
||||
request = storage.delete("testfile" + data.fileExtension);
|
||||
isnot(request, null, "Should be able to get request");
|
||||
|
||||
request.onsuccess = function() {
|
||||
is(data.shouldPass, true, "onsuccess was called for type " + data.type);
|
||||
testComplete(iframe, data);
|
||||
};
|
||||
|
||||
request.onerror = function(e) {
|
||||
isnot(data.shouldPass, true, "onfailure was called for type " + data.type + " Error: " + e.target.error.name);
|
||||
is(e.target.error.name, "SecurityError", "onerror should fire a SecurityError");
|
||||
testComplete(iframe, data);
|
||||
};
|
||||
}
|
||||
|
||||
function TestEnumerate(iframe, data) {
|
||||
|
||||
createTestFile(data.fileExtension);
|
||||
|
||||
var storage = iframe.contentDocument.defaultView.navigator.getDeviceStorage(data.type);
|
||||
isnot(storage, null, "Should be able to get storage object for " + data.type);
|
||||
|
||||
request = storage.enumerate();
|
||||
isnot(request, null, "Should be able to get request");
|
||||
|
||||
request.onsuccess = function(e) {
|
||||
is(data.shouldPass, true, "onsuccess was called for type " + data.type);
|
||||
|
||||
if (e.target.result == null) {
|
||||
testComplete(iframe, data);
|
||||
return;
|
||||
}
|
||||
e.target.continue();
|
||||
};
|
||||
|
||||
request.onerror = function(e) {
|
||||
isnot(data.shouldPass, true, "onfailure was called for type " + data.type + " Error: " + e.target.error.name);
|
||||
is(e.target.error.name, "SecurityError", "onerror should fire a SecurityError");
|
||||
testComplete(iframe, data);
|
||||
};
|
||||
}
|
||||
|
||||
var gTestUri = "https://example.com/chrome/dom/devicestorage/test/test_app_permissions.html"
|
||||
|
||||
var gData = [
|
||||
|
||||
// Get
|
||||
// Web applications with no permissions
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: false,
|
||||
fileExtension: '.png',
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogv',
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
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: 'music',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: true,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestGet
|
||||
},
|
||||
|
||||
|
||||
// Add
|
||||
|
||||
|
||||
// Web applications with no permissions
|
||||
{
|
||||
type: 'pictures',
|
||||
mimeType: 'image/png',
|
||||
fileExtension: '.png',
|
||||
shouldPass: false,
|
||||
test: TestAdd
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
mimeType: 'video/ogv',
|
||||
fileExtension: '.ogv',
|
||||
shouldPass: false,
|
||||
test: TestAdd
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
mimeType: 'audio/ogg',
|
||||
fileExtension: '.ogg',
|
||||
shouldPass: false,
|
||||
test: TestAdd
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
mimeType: 'text/plain',
|
||||
fileExtension: '.txt',
|
||||
shouldPass: false,
|
||||
test: TestAdd
|
||||
},
|
||||
|
||||
// Web applications with permission granted
|
||||
{
|
||||
type: 'pictures',
|
||||
mimeType: 'image/png',
|
||||
fileExtension: '.png',
|
||||
shouldPass: true,
|
||||
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestAdd
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
mimeType: 'video/ogv',
|
||||
fileExtension: '.ogv',
|
||||
shouldPass: true,
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestAdd
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
mimeType: 'audio/ogg',
|
||||
fileExtension: '.ogg',
|
||||
shouldPass: true,
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestAdd
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
mimeType: 'text/plain',
|
||||
fileExtension: '.txt',
|
||||
shouldPass: true,
|
||||
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestAdd
|
||||
},
|
||||
|
||||
|
||||
// Delete
|
||||
|
||||
// Web applications with no permissions
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: false,
|
||||
fileExtension: '.png',
|
||||
test: TestDelete
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogv',
|
||||
test: TestDelete
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
test: TestDelete
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
test: TestDelete
|
||||
},
|
||||
|
||||
// Web applications with permission granted
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: true,
|
||||
fileExtension: '.png',
|
||||
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestDelete
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogv',
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestDelete
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestDelete
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: true,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestDelete
|
||||
},
|
||||
|
||||
// Enumeration
|
||||
|
||||
// Web applications with no permissions
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: false,
|
||||
fileExtension: '.png',
|
||||
test: TestEnumerate
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogv',
|
||||
test: TestEnumerate
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
test: TestEnumerate
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
test: TestEnumerate
|
||||
},
|
||||
|
||||
// Web applications with permission granted
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: true,
|
||||
fileExtension: '.png',
|
||||
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestEnumerate
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogv',
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestEnumerate
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestEnumerate
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: true,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestEnumerate
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
function setupTest(iframe,data) {
|
||||
if (data.permissions) {
|
||||
for (var j in data.permissions) {
|
||||
SpecialPowers.addPermission(data.permissions[j], true, iframe.contentDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testComplete(iframe, data) {
|
||||
if (data.permissions) {
|
||||
for (var 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) {
|
||||
var iframe = document.createElement('iframe');
|
||||
var data = gData.pop();
|
||||
|
||||
iframe.setAttribute('mozbrowser', '');
|
||||
|
||||
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;
|
||||
var directoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
var 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) {}
|
||||
}
|
||||
|
||||
createTestFile('.txt');
|
||||
var gTestRunner = runTest();
|
||||
SpecialPowers.addPermission("browser", true, gTestUri);
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [["dom.mozBrowserFramesEnabled", true],
|
||||
["network.disable.ipc.security", true],
|
||||
["device.storage.enabled", true],
|
||||
["device.storage.testing", true],
|
||||
["device.storage.prompt.testing", false]]},
|
||||
function() { gTestRunner.next(); });
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,50 +0,0 @@
|
||||
<!--
|
||||
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=834595
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=834595">Mozilla Bug 834595</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function availableSuccess(e) {
|
||||
isnot(e.target.result, null, "result should not be null");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function availableError(e) {
|
||||
ok(false, "availableError was called");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
|
||||
request = storage.available();
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = availableSuccess;
|
||||
request.onerror = availableError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,141 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function() {
|
||||
|
||||
var gFileName = "devicestorage/" + randomFilename(12) + "/hi.png";
|
||||
var gData = "My name is Doug Turner. My IRC nick is DougT. I like Maple cookies."
|
||||
var gDataBlob = new Blob([gData], {type: 'image/png'});
|
||||
var gFileReader = new FileReader();
|
||||
|
||||
function getAfterDeleteSuccess(e) {
|
||||
ok(false, "file was deleted not successfully");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getAfterDeleteError(e) {
|
||||
ok(true, "file was deleted successfully");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function deleteSuccess(e) {
|
||||
|
||||
ok(e.target.result == gFileName, "File name should match");
|
||||
dump(e.target.result + "\n")
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
request = storage.get(e.target.result);
|
||||
request.onsuccess = getAfterDeleteSuccess;
|
||||
request.onerror = getAfterDeleteError;
|
||||
|
||||
}
|
||||
|
||||
function deleteError(e) {
|
||||
ok(false, "deleteError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getSuccess(e) {
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
ok(e.target.result.name == gFileName, "File name should match");
|
||||
ok(e.target.result.size > 0, "File size be greater than zero");
|
||||
ok(e.target.result.type, "File should have a mime type");
|
||||
ok(e.target.result.lastModifiedDate, "File should have a last modified date");
|
||||
|
||||
var name = e.target.result.name;
|
||||
|
||||
gFileReader.readAsArrayBuffer(gDataBlob);
|
||||
gFileReader.onload = function(e) {
|
||||
readerCallback(e);
|
||||
|
||||
request = storage.delete(name)
|
||||
request.onsuccess = deleteSuccess;
|
||||
request.onerror = deleteError;
|
||||
}
|
||||
}
|
||||
|
||||
function readerCallback(e) {
|
||||
|
||||
ab = e.target.result;
|
||||
|
||||
is(ab.byteLength, gData.length, "wrong arraybuffer byteLength");
|
||||
var u8v = new Uint8Array(ab);
|
||||
is(String.fromCharCode.apply(String, u8v), gData, "wrong values");
|
||||
}
|
||||
|
||||
function getError(e) {
|
||||
ok(false, "getError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
|
||||
var filename = e.target.result;
|
||||
if (filename[0] == "/") {
|
||||
// We got /storageName/prefix/filename
|
||||
// Remove the storageName (this shows up on FirefoxOS)
|
||||
filename = filename.substring(1); // Remove leading slash
|
||||
var slashIndex = filename.indexOf("/");
|
||||
if (slashIndex >= 0) {
|
||||
filename = filename.substring(slashIndex + 1); // Remove storageName
|
||||
}
|
||||
}
|
||||
ok(filename == gFileName, "File name should match");
|
||||
|
||||
// Update gFileName to be the fully qualified name so that
|
||||
// further checks will pass.
|
||||
gFileName = e.target.result;
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
request = storage.get(gFileName);
|
||||
request.onsuccess = getSuccess;
|
||||
request.onerror = getError;
|
||||
|
||||
ok(true, "addSuccess was called");
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(storage, "Should have gotten a storage");
|
||||
|
||||
request = storage.addNamed(gDataBlob, gFileName);
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,80 +0,0 @@
|
||||
<!--
|
||||
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=886627
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API</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=886627">
|
||||
Mozilla Bug 886627
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/**
|
||||
* Test that common device storage directories are available.
|
||||
*
|
||||
* This test differs from other device storage tests in that other tests use a
|
||||
* "testing mode", which relocates the device storage directories to a testing
|
||||
* directory. On the other hand, this test turns off testing mode to makes sure
|
||||
* that the normal, non-testing directories also work properly.
|
||||
*/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv({
|
||||
'set': [
|
||||
["device.storage.enabled", true],
|
||||
["device.storage.testing", false],
|
||||
["device.storage.prompt.testing", true],
|
||||
]
|
||||
}, function() {
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
ok(!navigator.getDeviceStorage("nonexistent-type"), "Should not have nonexistent storage");
|
||||
|
||||
ok(navigator.getDeviceStorage("pictures"), "Should have pictures storage");
|
||||
ok(navigator.getDeviceStorage("videos"), "Should have videos storage");
|
||||
ok(navigator.getDeviceStorage("music"), "Should have music storage");
|
||||
|
||||
// Need special permission to access "apps". We always have the permission in B2G
|
||||
// mochitests, but on other platforms, we need to manually add the permission.
|
||||
var needAppsPermission = false;;
|
||||
if (!SpecialPowers.testPermission(
|
||||
"webapps-manage", SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION, document)) {
|
||||
ok(!navigator.getDeviceStorage("apps"), "Should not have apps storage without permission");
|
||||
needAppsPermission = true;
|
||||
}
|
||||
|
||||
var testFunction = function() {
|
||||
ok(navigator.getDeviceStorage("apps"), "Should have apps storage with permission");
|
||||
ok(navigator.getDeviceStorage("sdcard"), "Should have sdcard storage");
|
||||
ok(navigator.getDeviceStorage("crashes"), "Should have crashes storage");
|
||||
// The test harness reverts our pref changes automatically.
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (needAppsPermission) {
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ "type":"webapps-manage", "allow": true, "context": document }],
|
||||
testFunction);
|
||||
} else {
|
||||
testFunction();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,101 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var freeBytes = -1;
|
||||
var stats = 0;
|
||||
|
||||
function stat(s, file_list_length) {
|
||||
if (freeBytes == -1) {
|
||||
freeBytes = s.target.result.freeBytes;
|
||||
}
|
||||
|
||||
ok(freeBytes == s.target.result.freeBytes, "Free bytes should be the same");
|
||||
ok(file_list_length * 1024 == s.target.result.totalBytes, "space taken up by files should match")
|
||||
|
||||
stats = stats + 1;
|
||||
|
||||
if (stats == 2) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
added = added - 1;
|
||||
|
||||
if (added == 0) {
|
||||
request = pictures.stat();
|
||||
request.onsuccess = function(s) {stat(s, picture_files.length)};
|
||||
|
||||
request = videos.stat();
|
||||
request.onsuccess = function(s) {stat(s, video_files.length)};
|
||||
|
||||
request = music.stat();
|
||||
request.onsuccess = function(s) {stat(s, music_files.length)};
|
||||
}
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(true, "hi");
|
||||
|
||||
var pictures = navigator.getDeviceStorage("pictures");
|
||||
var picture_files = [ "a.png", "b.png", "c.png", "d.png", "e.png" ];
|
||||
|
||||
var videos = navigator.getDeviceStorage("videos");
|
||||
var video_files = [ "a.ogv", "b.ogv" ];
|
||||
|
||||
var music = navigator.getDeviceStorage("music");
|
||||
var music_files = [ "a.mp3", "b.mp3", "c.mp3" ];
|
||||
|
||||
var added = picture_files.length + video_files.length + music_files.length;
|
||||
|
||||
for (var i=0; i < picture_files.length; i++) {
|
||||
request = pictures.addNamed(createRandomBlob('image/png'), picture_files[i]);
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
for (var i=0; i < video_files.length; i++) {
|
||||
request = videos.addNamed(createRandomBlob('video/ogv'), video_files[i]);
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
for (var i=0; i < music_files.length; i++) {
|
||||
request = music.addNamed(createRandomBlob('audio/mp3'), music_files[i]);
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,72 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function testingStorage() {
|
||||
return navigator.getDeviceStorage("pictures");
|
||||
}
|
||||
|
||||
var tests = [
|
||||
function () { return testingStorage().addNamed(createRandomBlob('image/png'), gFileName); },
|
||||
function () { return testingStorage().delete(gFileName); },
|
||||
function () { return testingStorage().get(gFileName); },
|
||||
function () { var r = testingStorage().enumerate("../"); return r; }
|
||||
];
|
||||
|
||||
var gFileName = "../owned.png";
|
||||
|
||||
function fail(e) {
|
||||
ok(false, "addSuccess was called");
|
||||
dump(request);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function next(e) {
|
||||
|
||||
if (e != undefined) {
|
||||
ok(true, "addError was called");
|
||||
ok(e.target.error.name == "SecurityError", "Error must be SecurityError");
|
||||
}
|
||||
|
||||
var f = tests.pop();
|
||||
|
||||
if (f == undefined) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
request = f();
|
||||
request.onsuccess = fail;
|
||||
request.onerror = next;
|
||||
}
|
||||
|
||||
next();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,104 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function enumerateSuccess(e) {
|
||||
|
||||
if (e.target.result == null) {
|
||||
ok(files.length == 0, "when the enumeration is done, we shouldn't have any files in this array")
|
||||
dump("We still have length = " + files.length + "\n");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var filename = e.target.result.name;
|
||||
if (filename[0] == "/") {
|
||||
// We got /storageName/prefix/filename
|
||||
// Remove the storageName (this shows up on FirefoxOS)
|
||||
filename = filename.substring(1); // Remove leading slash
|
||||
var slashIndex = filename.indexOf("/");
|
||||
if (slashIndex >= 0) {
|
||||
filename = filename.substring(slashIndex + 1); // Remove storageName
|
||||
}
|
||||
}
|
||||
if (filename.startsWith(prefix)) {
|
||||
filename = filename.substring(prefix.length + 1); // Remove prefix
|
||||
}
|
||||
|
||||
var index = files.indexOf(filename);
|
||||
files.remove(index);
|
||||
|
||||
ok(index > -1, "filename should be in the enumeration : " + e.target.result.name);
|
||||
|
||||
// clean up
|
||||
var cleanup = storage.delete(e.target.result.name);
|
||||
cleanup.onsuccess = function(e) {} // todo - can i remove this?
|
||||
|
||||
e.target.continue();
|
||||
}
|
||||
|
||||
function handleError(e) {
|
||||
ok(false, "handleError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
addedSoFar = addedSoFar + 1;
|
||||
if (addedSoFar == files.length) {
|
||||
|
||||
var cursor = storage.enumerate(prefix);
|
||||
cursor.onsuccess = enumerateSuccess;
|
||||
cursor.onerror = handleError;
|
||||
}
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
var prefix = "devicestorage/" + randomFilename(12) + ".png"
|
||||
|
||||
var files = [ "a.PNG", "b.pnG", "c.png", "d/a.png", "d/b.png", "d/c.png", "d/d.png", "The/quick/brown/fox/jumps/over/the/lazy/dog.png"]
|
||||
var addedSoFar = 0;
|
||||
|
||||
|
||||
for (var i=0; i<files.length; i++) {
|
||||
|
||||
request = storage.addNamed(createRandomBlob('image/png'), prefix + '/' + files[i]);
|
||||
|
||||
ok(request, "Should have a non-null request");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,50 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function enumerateSuccess(e) {
|
||||
}
|
||||
|
||||
function enumerateFailure(e) {
|
||||
}
|
||||
|
||||
var cursor = navigator.getDeviceStorage("pictures").enumerate();
|
||||
cursor.onsuccess = enumerateSuccess;
|
||||
cursor.onerror = enumerateFailure;
|
||||
|
||||
try {
|
||||
cursor.continue();
|
||||
}
|
||||
catch (e) {
|
||||
ok(true, "Calling continue before enumerateSuccess fires should throw");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,109 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
// Array Remove - By John Resig (MIT Licensed)
|
||||
Array.prototype.remove = function(from, to) {
|
||||
var rest = this.slice((to || from) + 1 || this.length);
|
||||
this.length = from < 0 ? this.length + from : from;
|
||||
return this.push.apply(this, rest);
|
||||
};
|
||||
|
||||
devicestorage_setup();
|
||||
|
||||
function enumerateSuccess(e) {
|
||||
|
||||
if (e.target.result == null) {
|
||||
ok(files.length == 0, "when the enumeration is done, we shouldn't have any files in this array")
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var filename = e.target.result.name;
|
||||
if (filename[0] == "/") {
|
||||
// We got /storageName/prefix/filename
|
||||
// Remove the storageName (this shows up on FirefoxOS)
|
||||
filename = filename.substring(1); // Remove leading slash
|
||||
var slashIndex = filename.indexOf("/");
|
||||
if (slashIndex >= 0) {
|
||||
filename = filename.substring(slashIndex + 1); // Remove storageName
|
||||
}
|
||||
}
|
||||
if (filename.startsWith(prefix)) {
|
||||
filename = filename.substring(prefix.length + 1); // Remove prefix
|
||||
}
|
||||
|
||||
var index = files.indexOf(enumFilename);
|
||||
files.remove(index);
|
||||
|
||||
ok(index > -1, "filename should be in the enumeration : " + e.target.result.name);
|
||||
|
||||
// clean up
|
||||
var cleanup = storage.delete(e.target.result.name);
|
||||
cleanup.onsuccess = function(e) {} // todo - can i remove this?
|
||||
|
||||
e.target.continue();
|
||||
}
|
||||
|
||||
function handleError(e) {
|
||||
ok(false, "handleError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
addedSoFar = addedSoFar + 1;
|
||||
if (addedSoFar == files.length) {
|
||||
|
||||
var cursor = storage.enumerate();
|
||||
cursor.onsuccess = enumerateSuccess;
|
||||
cursor.onerror = handleError;
|
||||
}
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
var prefix = "devicestorage/" + randomFilename(12)
|
||||
|
||||
var files = [ "a.png", "b.png", "c.png" ]
|
||||
var addedSoFar = 0;
|
||||
|
||||
|
||||
for (var i=0; i<files.length; i++) {
|
||||
|
||||
request = storage.addNamed(createRandomBlob('image/png'), prefix + '/' + files[i]);
|
||||
|
||||
ok(request, "Should have a non-null request");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,80 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for basic sanity of the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
storage = navigator.getDeviceStorage("pictures");
|
||||
|
||||
|
||||
throws = false;
|
||||
try {
|
||||
var cursor = storage.enumerate();
|
||||
} catch(e) {throws = true}
|
||||
ok(!throws, "enumerate no parameter");
|
||||
|
||||
throws = false;
|
||||
try {
|
||||
var cursor = storage.enumerate("string");
|
||||
} catch(e) {throws = true}
|
||||
ok(!throws, "enumerate one string parameter");
|
||||
|
||||
throws = false;
|
||||
try {
|
||||
var cursor = storage.enumerate("string", "string2");
|
||||
} catch(e) {throws = true}
|
||||
ok(throws, "enumerate two string parameter");
|
||||
|
||||
throws = false;
|
||||
try {
|
||||
var cursor = storage.enumerate("string", {"since": new Date(1)});
|
||||
} catch(e) {throws = true}
|
||||
ok(!throws, "enumerate a string and object parameter");
|
||||
|
||||
throws = false;
|
||||
try {
|
||||
var cursor = storage.enumerate({"path": "a"});
|
||||
} catch(e) {throws = true}
|
||||
ok(!throws, "enumerate object parameter with path");
|
||||
|
||||
throws = false;
|
||||
try {
|
||||
var cursor = storage.enumerate({}, "string");
|
||||
} catch(e) {throws = true}
|
||||
ok(throws, "enumerate object then a string");
|
||||
|
||||
throws = false;
|
||||
try {
|
||||
var cursor = storage.enumerate({"path": "a", "since": new Date(0) });
|
||||
} catch(e) {throws = true}
|
||||
ok(!throws, "enumerate object parameter with path");
|
||||
|
||||
SimpleTest.finish()
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,62 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function freeSpaceSuccess(e) {
|
||||
ok(e.target.result > 0, "free bytes should exist and be greater than zero");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function freeSpaceError(e) {
|
||||
ok(false, "freeSpaceError was called");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
request = storage.freeSpace();
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = freeSpaceSuccess;
|
||||
request.onerror = freeSpaceError;
|
||||
}
|
||||
|
||||
var prefix = "devicestorage/" + randomFilename(12);
|
||||
request = storage.addNamed(createRandomBlob('image/png'), prefix + "/a/b.png");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,598 +0,0 @@
|
||||
<!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">
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function getRandomBuffer() {
|
||||
var size = 1024;
|
||||
var buffer = new ArrayBuffer(size);
|
||||
var view = new Uint8Array(buffer);
|
||||
for (var i = 0; i < size; i++) {
|
||||
view[i] = parseInt(Math.random() * 255);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function createRandomBlob(mime) {
|
||||
let size = 1024;
|
||||
let buffer = new ArrayBuffer(size);
|
||||
let view = new Uint8Array(buffer);
|
||||
for (let i = 0; i < size; i++) {
|
||||
view[i] = parseInt(Math.random() * 255);
|
||||
}
|
||||
return blob = new Blob([buffer], {type: mime});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
function TestCreateFile(iframe, data) {
|
||||
function cbError(e) {
|
||||
is(e.name, "SecurityError", "[TestCreateFile] Should fire a SecurityError for type " + data.type);
|
||||
is(data.shouldPass, false, "[TestCreateFile] 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, "[TestCreateFile] Should be able to get storage object for " + data.type);
|
||||
|
||||
if (!storage) {
|
||||
testComplete(iframe, data);
|
||||
return;
|
||||
}
|
||||
|
||||
storage.getRoot().then(function(root) {
|
||||
ok(true, "[TestCreateFile] Success callback of getRoot was called for type " + data.type);
|
||||
let filename = randomFilename(100) + data.fileExtension;
|
||||
root.createFile(filename, {
|
||||
data: createRandomBlob(data.mimeType),
|
||||
ifExists: "replace"
|
||||
}).then(function() {
|
||||
is(data.shouldPass, true, "[TestCreateFile] Success callback was called for type " + data.type);
|
||||
testComplete(iframe, data);
|
||||
}, cbError);
|
||||
}, cbError);
|
||||
}
|
||||
|
||||
function TestRemove(iframe, data) {
|
||||
function cbError(e) {
|
||||
is(e.name, "SecurityError", "[TestRemove] Should fire a SecurityError for type " + data.type);
|
||||
is(data.shouldPass, false, "[TestRemove] 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, "[TestRemove] Should be able to get storage object for " + data.type);
|
||||
|
||||
if (!storage) {
|
||||
testComplete(iframe, data);
|
||||
return;
|
||||
}
|
||||
|
||||
storage.getRoot().then(function(root) {
|
||||
ok(true, "[TestRemove] Success callback of getRoot was called for type " + data.type);
|
||||
root.remove("testfile" + data.fileExtension).then(function() {
|
||||
is(data.shouldPass, true, "[TestRemove] 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
|
||||
},
|
||||
|
||||
// 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
|
||||
},
|
||||
|
||||
// Directory#createFile
|
||||
|
||||
// Web applications with no permissions
|
||||
{
|
||||
type: 'pictures',
|
||||
mimeType: 'image/png',
|
||||
shouldPass: false,
|
||||
fileExtension: '.png',
|
||||
test: TestCreateFile
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
mimeType: 'video/ogv',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogv',
|
||||
test: TestCreateFile
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
mimeType: 'video/ogg',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
test: TestCreateFile
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
mimeType: 'audio/ogg',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
test: TestCreateFile
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
mimeType: 'audio/ogg',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
test: TestCreateFile
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
mimeType: 'text/plain',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
test: TestCreateFile
|
||||
},
|
||||
|
||||
// Web applications with permission granted
|
||||
{
|
||||
type: 'pictures',
|
||||
mimeType: 'image/png',
|
||||
shouldPass: true,
|
||||
fileExtension: '.png',
|
||||
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestCreateFile
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
mimeType: 'video/ogv',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogv',
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestCreateFile
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
mimeType: 'video/ogg',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestCreateFile
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
mimeType: 'audio/ogg',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestCreateFile
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
mimeType: 'audio/ogg',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestCreateFile
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
mimeType: 'text/plain',
|
||||
shouldPass: true,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestCreateFile
|
||||
},
|
||||
|
||||
// Directory#remove
|
||||
|
||||
// Web applications with no permissions
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: false,
|
||||
fileExtension: '.png',
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogv',
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
test: TestRemove
|
||||
},
|
||||
|
||||
// Web applications with permission granted
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: true,
|
||||
fileExtension: '.png',
|
||||
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogv',
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: true,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
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', '');
|
||||
|
||||
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);
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [["dom.mozBrowserFramesEnabled", true],
|
||||
["network.disable.ipc.security", true],
|
||||
["device.storage.enabled", true],
|
||||
["device.storage.testing", true],
|
||||
["device.storage.prompt.testing", false]]},
|
||||
function() { gTestRunner.next(); });
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,91 +0,0 @@
|
||||
<!--
|
||||
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=855952
|
||||
-->
|
||||
<head>
|
||||
<title>Test for basic sanity of the device storage API </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=855952">Mozilla Bug 855952</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var file = new Blob(["This is a text file."], {type: "text/plain"});
|
||||
var appendFile = new Blob([" Another text file."], {type: "text/plain"});
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function deleteSuccess(e) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function deleteError(e) {
|
||||
ok(false, "deleteError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function appendSuccess(e) {
|
||||
ok(true, "appendSuccess was called.");
|
||||
|
||||
request = gStorage.delete(e.target.result)
|
||||
request.onsuccess = deleteSuccess;
|
||||
request.onerror = deleteError;
|
||||
}
|
||||
|
||||
function appendError(e) {
|
||||
ok(false, "appendError was called.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
ok(true, "addSuccess was called");
|
||||
|
||||
request = gStorage.appendNamed(appendFile, e.target.result);
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = appendSuccess;
|
||||
request.onerror = appendError;
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
// test file is already exists. clean it up and try again..
|
||||
request = gStorage.delete(e.target.result)
|
||||
request.onsuccess = addFile;
|
||||
}
|
||||
|
||||
function addFile() {
|
||||
// Add a file first
|
||||
request = gStorage.addNamed(file, "devicestorage/append.asc");
|
||||
ok(request, "Should have a non-null request");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
function runtest() {
|
||||
addFile();
|
||||
}
|
||||
|
||||
var gStorage = navigator.getDeviceStorage("sdcard");
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
ok(gStorage, "Should get storage from sdcard");
|
||||
runtest();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,72 +0,0 @@
|
||||
<!--
|
||||
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(function () {
|
||||
|
||||
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 + ".");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function cbError(e) {
|
||||
ok(false, "Should not arrive here! Error: " + e.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
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>
|
@ -1,107 +0,0 @@
|
||||
<!--
|
||||
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(function () {
|
||||
|
||||
// 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) {
|
||||
is(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) {
|
||||
is(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.");
|
||||
SimpleTest.finish();
|
||||
}, 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.");
|
||||
SimpleTest.finish();
|
||||
}, function(e) {
|
||||
ok(true, "Accessing parent directory with '..' is not allowed.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
break;
|
||||
}
|
||||
gTestCount++;
|
||||
}
|
||||
|
||||
function cbError(e) {
|
||||
ok(false, e.name + " error should not arrive here!");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
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>
|
@ -1,133 +0,0 @@
|
||||
<!--
|
||||
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="application/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
let gTestCount = 0;
|
||||
let gFileReader = new FileReader();
|
||||
let gRoot;
|
||||
|
||||
function str2array(str) {
|
||||
let strlen = str.length;
|
||||
let buf = new ArrayBuffer(strlen);
|
||||
let bufView = new Uint8Array(buf);
|
||||
for (let i=0; i < strlen; i++) {
|
||||
bufView[i] = str.charCodeAt(i);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
function array2str(data) {
|
||||
return String.fromCharCode.apply(String, new Uint8Array(data));
|
||||
}
|
||||
|
||||
let gTestCases = [
|
||||
// Create with string data.
|
||||
{
|
||||
text: "My name is Yuan.",
|
||||
get data() { return this.text; },
|
||||
shouldPass: true,
|
||||
mode: "replace"
|
||||
},
|
||||
|
||||
// Create with array buffer data.
|
||||
{
|
||||
text: "I'm from Kunming.",
|
||||
get data() { return str2array(this.text); },
|
||||
shouldPass: true,
|
||||
mode: "replace"
|
||||
},
|
||||
|
||||
// Create with array buffer view data.
|
||||
{
|
||||
text: "Kunming is in Yunnan province of China.",
|
||||
get data() { return new Uint8Array(str2array(this.text)); },
|
||||
shouldPass: true,
|
||||
mode: "replace"
|
||||
},
|
||||
|
||||
// Create with blob data.
|
||||
{
|
||||
text: "Kunming is in Yunnan province of China.",
|
||||
get data() { return new Blob([this.text], {type: 'image/png'}); },
|
||||
shouldPass: true,
|
||||
mode: "replace"
|
||||
},
|
||||
|
||||
// Don't overwrite existing file.
|
||||
{
|
||||
data: null,
|
||||
shouldPass: false,
|
||||
mode: "fail"
|
||||
}
|
||||
];
|
||||
|
||||
function next() {
|
||||
if (gTestCount >= gTestCases.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
let c = gTestCases[gTestCount++];
|
||||
gRoot.createFile("text.png", {
|
||||
data: c.data,
|
||||
ifExists: c.mode
|
||||
}).then(function(file) {
|
||||
is(c.shouldPass, true, "[" + gTestCount + "] Success callback was called for createFile.");
|
||||
if (!c.shouldPass) {
|
||||
SimpleTest.executeSoon(next);
|
||||
return;
|
||||
}
|
||||
// Check the file content.
|
||||
gFileReader.readAsArrayBuffer(file);
|
||||
gFileReader.onload = function(e) {
|
||||
ab = e.target.result;
|
||||
is(array2str(e.target.result), c.text, "[" + gTestCount + "] Wrong values.");
|
||||
SimpleTest.executeSoon(next);
|
||||
};
|
||||
}, function(e) {
|
||||
is(c.shouldPass, false, "[" + gTestCount + "] Error callback was called for createFile.");
|
||||
SimpleTest.executeSoon(next);
|
||||
});
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage.");
|
||||
|
||||
let storage = navigator.getDeviceStorage("pictures");
|
||||
ok(storage, "Should have gotten a storage.");
|
||||
|
||||
// Get the root directory
|
||||
storage.getRoot().then(function(dir) {
|
||||
ok(dir, "Should have gotten the root directory.");
|
||||
gRoot = dir;
|
||||
next();
|
||||
}, function(e) {
|
||||
ok(false, e.name + " error should not arrive here!");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,206 +0,0 @@
|
||||
<!--
|
||||
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(function () {
|
||||
|
||||
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!");
|
||||
SimpleTest.finish();
|
||||
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:
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
default:
|
||||
ok(false, "Should not arrive here!");
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
}
|
||||
gTestCount++;
|
||||
}
|
||||
|
||||
function cbError(e) {
|
||||
ok(false, "Should not arrive at cbError! Error: " + e.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function cbSuccess(e) {
|
||||
ok(false, "Should not arrive at cbSuccess!");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
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);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}
|
||||
|
||||
// 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>
|
@ -1,98 +0,0 @@
|
||||
<!--
|
||||
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=XXX
|
||||
-->
|
||||
<head>
|
||||
<title>Test Directory#getFilesAndDirectories 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=XXX">Mozilla Bug XXX</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
SimpleTest.requestCompleteLog();
|
||||
|
||||
// The root directory object.
|
||||
var gRoot = null;
|
||||
|
||||
function checkContents1(contents) {
|
||||
var expected = {
|
||||
"sub2": "/sub",
|
||||
"sub3": "/sub",
|
||||
"a.png": "/sub",
|
||||
"b.png": "/sub",
|
||||
};
|
||||
|
||||
is(contents.length, Object.keys(expected).length,
|
||||
"The sub-directory should contain four children");
|
||||
|
||||
var sub2;
|
||||
|
||||
for (var child of contents) {
|
||||
if (child.name in expected) {
|
||||
ok(true, "Found '" + child.name + "' in /sub");
|
||||
if (child.name == "sub2") {
|
||||
sub2 = child;
|
||||
}
|
||||
} else {
|
||||
ok(false, "Did not expect '" + child.name + "' in /sub");
|
||||
}
|
||||
delete expected[child.name];
|
||||
}
|
||||
|
||||
// 'expected' should now be "empty"
|
||||
for (var missing in Object.keys(expected)) {
|
||||
ok(false, "Expected '" + missing.name + "' in /sub");
|
||||
}
|
||||
|
||||
sub2.getFilesAndDirectories().then(checkContents2, handleError);
|
||||
}
|
||||
|
||||
function checkContents2(contents) {
|
||||
is(contents[0].name, "c.png", "'sub2' should contain 'c.png'");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function handleError(e) {
|
||||
ok(false, "Should not arrive at handleError! Error: " + e.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var gStorage = navigator.getDeviceStorage("pictures");
|
||||
|
||||
ok(gStorage, "Should have gotten a storage.");
|
||||
|
||||
function runTests() {
|
||||
gStorage.getRoot().then(function(rootDir) {
|
||||
gRoot = rootDir;
|
||||
return rootDir.get("sub");
|
||||
}).then(function(subDir) {
|
||||
return subDir.getFilesAndDirectories();
|
||||
}).then(checkContents1).catch(handleError);
|
||||
}
|
||||
|
||||
createTestFiles(gStorage, ["sub/a.png", "sub/b.png", "sub/sub2/c.png", "sub/sub3/d.png"]).then(function() {
|
||||
runTests();
|
||||
}, function() {
|
||||
ok(false, "Failed to created test files.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,176 +0,0 @@
|
||||
<!--
|
||||
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=934368
|
||||
-->
|
||||
<head>
|
||||
<title>Test Directory#remove and #removeDeep 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=934368">Mozilla Bug 934368</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
let gStorage = null;
|
||||
let gTestCount = 0;
|
||||
let gFileMap = {};
|
||||
let gRemoveDeep = true;
|
||||
|
||||
let gTestCases = [
|
||||
// Remove a non-existent file should return false.
|
||||
{
|
||||
dir: "/",
|
||||
path: "non-existent.png",
|
||||
ret: false,
|
||||
shouldPass: true
|
||||
},
|
||||
|
||||
// Remove parent directory should fail.
|
||||
{
|
||||
dir: "sub1/sub2",
|
||||
target: "sub1",
|
||||
ret: true,
|
||||
shouldPass: false
|
||||
},
|
||||
|
||||
// Remove root directory should fail.
|
||||
{
|
||||
dir: "/",
|
||||
target: "/",
|
||||
ret: true,
|
||||
shouldPass: false
|
||||
},
|
||||
|
||||
// Remove non-descendant file should fail.
|
||||
{
|
||||
dir: "sub1",
|
||||
target: "sub/b.png",
|
||||
ret: true,
|
||||
shouldPass: false
|
||||
},
|
||||
|
||||
// Remove descendant file should return true.
|
||||
{
|
||||
dir: "sub1",
|
||||
target: "sub1/sub2/a.png",
|
||||
ret: true,
|
||||
shouldPass: true
|
||||
},
|
||||
|
||||
// Remove empty directory should return true.
|
||||
{
|
||||
dir: "sub1",
|
||||
path: "sub2",
|
||||
ret: true,
|
||||
shouldPass: true
|
||||
},
|
||||
|
||||
|
||||
// Remove non-empty directory should return true for "removeDeep" and fail
|
||||
// for "remove".
|
||||
{
|
||||
dir: "/",
|
||||
path: "sub",
|
||||
ret: true,
|
||||
get shouldPass() { return gRemoveDeep; }
|
||||
}
|
||||
];
|
||||
|
||||
function runNextTests() {
|
||||
gTestCount = 0;
|
||||
function runTests() {
|
||||
function cbError(e) {
|
||||
ok(false, "Should not arrive at cbError! Error: " + e.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function cbSuccess(r) {
|
||||
ok(r, "Should get the file - " + this);
|
||||
gFileMap[this] = r;
|
||||
}
|
||||
|
||||
// Get directory and file objects.
|
||||
gStorage.getRoot().then(function(root) {
|
||||
ok(root, "Should get root directory.");
|
||||
gFileMap["/"] = root;
|
||||
|
||||
let arr = [];
|
||||
|
||||
["sub1", "sub1/sub2", "sub1/sub2/a.png", "sub/b.png"].forEach(function(path) {
|
||||
arr.push(root.get(path).then(cbSuccess.bind(path), cbError));
|
||||
});
|
||||
|
||||
Promise.all(arr).then(function() {
|
||||
testNextRemove();
|
||||
}, function() {
|
||||
ok(false, "Failed to get test files.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}, cbError);
|
||||
};
|
||||
createTestFiles(gStorage, ["sub1/sub2/a.png", "sub/b.png"]).then(function() {
|
||||
runTests();
|
||||
}, function() {
|
||||
ok(false, "Failed to created test files.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
function testNextRemove() {
|
||||
if (gTestCount < gTestCases.length) {
|
||||
let data = gTestCases[gTestCount++];
|
||||
let dir = gFileMap[data.dir];
|
||||
let path = data.path || gFileMap[data.target];
|
||||
let targetPath = data.path || data.target;
|
||||
let promise = gRemoveDeep ? dir.removeDeep(path) : dir.remove(path);
|
||||
promise.then(function(result) {
|
||||
ok(data.shouldPass, "Success callback was called to remove " +
|
||||
targetPath + " from " + data.dir);
|
||||
is(result, data.ret, "Return value should match to remove " +
|
||||
targetPath + " from " + data.dir);
|
||||
SimpleTest.executeSoon(testNextRemove);
|
||||
}, function(err) {
|
||||
ok(!data.shouldPass, "Error callback was called to remove " +
|
||||
targetPath + " from " + data.dir + '. Error: ' + err.name);
|
||||
SimpleTest.executeSoon(testNextRemove);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (gRemoveDeep) {
|
||||
// Test "remove" after "removeDeep".
|
||||
gRemoveDeep = false;
|
||||
runNextTests();
|
||||
return;
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage.");
|
||||
|
||||
gStorage = navigator.getDeviceStorage("pictures");
|
||||
ok(gStorage, "Should have gotten a storage.");
|
||||
|
||||
// Test "removeDeep" first.
|
||||
gRemoveDeep = true;
|
||||
runNextTests();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,176 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
devicestorage_setup(function () {
|
||||
|
||||
// We put the old files in 2 levels deep. When you add a file to a directory
|
||||
// it will modify the parents last modification time, but not the parents
|
||||
// parents. So we want to make sure that even though x's timestamp is earlier
|
||||
// than the since parameter, we still pick up the later files.
|
||||
var oldFiles = ["x/y/aa.png", "x/y/ab.png", "x/y/ac.png"];
|
||||
var timeFile = "t.png";
|
||||
var newFiles = ["x/y/ad.png", "x/y/ae.png", "x/y/af.png", // new files in old dir
|
||||
"z/bd.png", "z/be.png", "z/bf.png"]; // new files in new dir
|
||||
|
||||
var storage = navigator.getDeviceStorage('pictures');
|
||||
var prefix = "devicestorage/" + randomFilename(12);
|
||||
var i;
|
||||
var timestamp;
|
||||
var lastFileAddedTimestamp;
|
||||
|
||||
function verifyAndDelete(prefix, files, e) {
|
||||
if (e.target.result == null) {
|
||||
ok(files.length == 0, "when the enumeration is done, we shouldn't have any files in this array")
|
||||
dump("We still have length = " + files.length + "\n");
|
||||
dump(files + "\n");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var filename = e.target.result.name;
|
||||
if (filename[0] == "/") {
|
||||
// We got /storageName/prefix/filename
|
||||
// Remove the storageName (this shows up on FirefoxOS)
|
||||
filename = filename.substring(1); // Remove leading slash
|
||||
var slashIndex = filename.indexOf("/");
|
||||
if (slashIndex >= 0) {
|
||||
filename = filename.substring(slashIndex + 1); // Remove storageName
|
||||
}
|
||||
}
|
||||
if (filename.startsWith(prefix)) {
|
||||
filename = filename.substring(prefix.length + 1); // Remove prefix
|
||||
}
|
||||
|
||||
var index = files.indexOf(filename);
|
||||
ok(index > -1, "filename should be in the enumeration : " + e.target.result.name);
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
files.splice(index, 1);
|
||||
|
||||
// clean up
|
||||
var cleanup = storage.delete(e.target.result.name);
|
||||
cleanup.onsuccess = function(e) {}
|
||||
}
|
||||
|
||||
function addFile(filename, callback) {
|
||||
var addReq = storage.addNamed(createRandomBlob('image/png'), prefix + '/' + filename);
|
||||
addReq.onsuccess = function(e) {
|
||||
// After adding the file, we go ahead and grab the timestamp of the file
|
||||
// that we just added
|
||||
var getReq = storage.get(prefix + '/' + filename);
|
||||
getReq.onsuccess = function(e) {
|
||||
lastFileAddedTimestamp = e.target.result.lastModifiedDate;
|
||||
callback();
|
||||
}
|
||||
getReq.onerror = function(e) {
|
||||
ok(false, "getError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}
|
||||
addReq.onerror = function(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function addFileArray(fileArray, callback) {
|
||||
var i = 0;
|
||||
function addNextFile() {
|
||||
i = i + 1;
|
||||
if (i == fileArray.length) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
addFile(fileArray[i], addNextFile);
|
||||
}
|
||||
addFile(fileArray[0], addNextFile);
|
||||
}
|
||||
|
||||
function delFile(filename, callback) {
|
||||
var req = storage.delete(prefix + '/' + filename);
|
||||
req.onsuccess = function(e) {
|
||||
callback();
|
||||
};
|
||||
req.onerror = function(e) {
|
||||
ok(false, "delError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}
|
||||
|
||||
function afterNewFiles() {
|
||||
var cursor = storage.enumerate(prefix, {"since": timestamp});
|
||||
cursor.onsuccess = function(e) {
|
||||
verifyAndDelete(prefix, newFiles, e);
|
||||
if (e.target.result) {
|
||||
e.target.continue();
|
||||
}
|
||||
};
|
||||
cursor.onerror = function (e) {
|
||||
ok(false, "handleError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}
|
||||
|
||||
function waitForTimestampChange() {
|
||||
// We've added a new file. See if the timestamp differs from
|
||||
// oldFileAddedTimestamp, and if it's the same wait for a bit
|
||||
// and try again.
|
||||
if (lastFileAddedTimestamp.valueOf() === timestamp.valueOf()) {
|
||||
delFile(timeFile, function() {
|
||||
setTimeout(function() {
|
||||
addFile(timeFile, waitForTimestampChange);
|
||||
}, 1000);
|
||||
});
|
||||
} else {
|
||||
timestamp = lastFileAddedTimestamp;
|
||||
// The timestamp has changed. Go ahead and add the rest of the new files
|
||||
delFile(timeFile, function() {
|
||||
addFileArray(newFiles, afterNewFiles);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function afterOldFiles() {
|
||||
timestamp = lastFileAddedTimestamp;
|
||||
setTimeout(function() {
|
||||
// We've added our old files and waited for a second.
|
||||
// Add a new file until the timestamp changes
|
||||
addFile(timeFile, waitForTimestampChange);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function addOldFiles() {
|
||||
addFileArray(oldFiles, afterOldFiles);
|
||||
}
|
||||
|
||||
addOldFiles();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,163 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var gFileName = "devicestorage/" + randomFilename(12) + "/hi.png";
|
||||
var gData = "My name is Doug Turner. My IRC nick is DougT. I like Maple cookies."
|
||||
var gDataBlob = new Blob([gData], {type: 'image/png'});
|
||||
var gFileReader = new FileReader();
|
||||
|
||||
function getAfterDeleteSuccess(e) {
|
||||
ok(false, "file was deleted not successfully");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getAfterDeleteError(e) {
|
||||
ok(true, "file was deleted successfully");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function deleteSuccess(e) {
|
||||
|
||||
ok(e.target.result == gFileName, "File name should match");
|
||||
dump(e.target.result + "\n")
|
||||
|
||||
// File was deleted using the sdcard stoage area. It should be gone
|
||||
// from the pictures as well.
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
request = storage.get(e.target.result);
|
||||
request.onsuccess = getAfterDeleteSuccess;
|
||||
request.onerror = getAfterDeleteError;
|
||||
}
|
||||
|
||||
function deleteError(e) {
|
||||
ok(false, "deleteError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getSuccess(e) {
|
||||
// We wrote the file out using pictures type. Since we've over-ridden the
|
||||
// root directory, we should be able to read it back using the sdcard
|
||||
// storage area.
|
||||
var storage = navigator.getDeviceStorage("sdcard");
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
ok(e.target.result.name == gFileName, "File name should match");
|
||||
ok(e.target.result.size > 0, "File size be greater than zero");
|
||||
ok(e.target.result.type, "File should have a mime type");
|
||||
ok(e.target.result.lastModifiedDate, "File should have a last modified date");
|
||||
|
||||
var name = e.target.result.name;
|
||||
|
||||
gFileReader.readAsArrayBuffer(gDataBlob);
|
||||
gFileReader.onload = function(e) {
|
||||
readerCallback(e);
|
||||
|
||||
request = storage.delete(name)
|
||||
request.onsuccess = deleteSuccess;
|
||||
request.onerror = deleteError;
|
||||
}
|
||||
}
|
||||
|
||||
function readerCallback(e) {
|
||||
|
||||
ab = e.target.result;
|
||||
|
||||
is(ab.byteLength, gData.length, "wrong arraybuffer byteLength");
|
||||
var u8v = new Uint8Array(ab);
|
||||
is(String.fromCharCode.apply(String, u8v), gData, "wrong values");
|
||||
}
|
||||
|
||||
function getError(e) {
|
||||
ok(false, "getError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
|
||||
var filename = e.target.result;
|
||||
if (filename[0] == "/") {
|
||||
// We got /storageName/prefix/filename
|
||||
// Remove the storageName (this shows up on FirefoxOS)
|
||||
filename = filename.substring(1); // Remove leading slash
|
||||
var slashIndex = filename.indexOf("/");
|
||||
if (slashIndex >= 0) {
|
||||
filename = filename.substring(slashIndex + 1); // Remove storageName
|
||||
}
|
||||
}
|
||||
ok(filename == gFileName, "File name should match");
|
||||
|
||||
// Update gFileName to be the fully qualified name so that
|
||||
// further checks will pass.
|
||||
gFileName = e.target.result;
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
request = storage.get(gFileName);
|
||||
request.onsuccess = getSuccess;
|
||||
request.onerror = getError;
|
||||
|
||||
ok(true, "addSuccess was called");
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(storage, "Should have gotten a storage");
|
||||
|
||||
request = storage.addNamed(gDataBlob, gFileName);
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
try {
|
||||
const Cc = SpecialPowers.Cc;
|
||||
const Ci = SpecialPowers.Ci;
|
||||
var directoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
var f = directoryService.get("TmpD", Ci.nsIFile);
|
||||
f.appendRelativePath("device-storage-sdcard");
|
||||
try {
|
||||
// The remove will fail if the directory doesn't exist, which is fine.
|
||||
f.remove(true);
|
||||
} catch (e) {}
|
||||
SpecialPowers.pushPrefEnv({'set': [["device.storage.overrideRootDir", f.path]]},
|
||||
function() {
|
||||
startTest();
|
||||
});
|
||||
} catch(e) {}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,93 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for basic sanity of the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var filename = "devicestorage/aaaa.png"
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
|
||||
function deleteSuccess(e) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function deleteError(e) {
|
||||
ok(false, "deleteError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addOverwritingSuccess(e) {
|
||||
ok(false, "addOverwritingSuccess was called.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addOverwritingError(e) {
|
||||
ok(true, "Adding to the same location should fail");
|
||||
ok(e.target.error.name == "NoModificationAllowedError", "Error must be NoModificationAllowedError");
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
request = storage.delete(filename)
|
||||
request.onsuccess = deleteSuccess;
|
||||
request.onerror = deleteError;
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
ok(true, "addSuccess was called");
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
request = storage.addNamed(createRandomBlob('image/png'), filename);
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = addOverwritingSuccess;
|
||||
request.onerror = addOverwritingError;
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
// test file is already exists. clean it up and try again..
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
request = storage.delete(filename)
|
||||
request.onsuccess = runtest;
|
||||
}
|
||||
|
||||
function runtest() {
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
request = storage.addNamed(createRandomBlob('image/png'), filename);
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
runtest();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,60 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for basic sanity of the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
var storage;
|
||||
|
||||
var throws = false;
|
||||
try {
|
||||
storage = navigator.getDeviceStorage();
|
||||
} catch(e) {throws = true}
|
||||
ok(throws, "getDeviceStorage takes one arg");
|
||||
|
||||
storage = navigator.getDeviceStorage("kilimanjaro");
|
||||
ok(!storage, "kilimanjaro - Should not have this type of storage");
|
||||
|
||||
storage = navigator.getDeviceStorage("pictures");
|
||||
ok(storage, "pictures - Should have getDeviceStorage");
|
||||
|
||||
storage = navigator.getDeviceStorage("music");
|
||||
ok(storage, "music - Should have getDeviceStorage");
|
||||
|
||||
storage = navigator.getDeviceStorage("videos");
|
||||
ok(storage, "videos - Should have getDeviceStorage");
|
||||
|
||||
var cursor = storage.enumerate();
|
||||
ok(cursor, "Should have a non-null cursor");
|
||||
|
||||
SimpleTest.finish();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,63 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function usedSpaceSuccess(e) {
|
||||
ok(e.target.result > 0, "total bytes should exist and be greater than zero");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function usedSpaceError(e) {
|
||||
ok(false, "usedSpaceError was called");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
request = storage.usedSpace();
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = usedSpaceSuccess;
|
||||
request.onerror = usedSpaceError;
|
||||
}
|
||||
|
||||
var prefix = "devicestorage/" + randomFilename(12);
|
||||
request = storage.addNamed(createRandomBlob('image/png'), prefix + "/a/b.png");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,77 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var gFileName = randomFilename(20) + ".png"
|
||||
|
||||
function addSuccess(e) {
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function onChange(e) {
|
||||
|
||||
dump("we saw: " + e.path + " " + e.reason + "\n");
|
||||
|
||||
var filename = e.path;
|
||||
if (filename[0] == "/") {
|
||||
// We got /storageName/prefix/filename
|
||||
// Remove the storageName (this shows up on FirefoxOS)
|
||||
filename = filename.substring(1); // Remove leading slash
|
||||
var slashIndex = filename.indexOf("/");
|
||||
if (slashIndex >= 0) {
|
||||
filename = filename.substring(slashIndex + 1); // Remove storageName
|
||||
}
|
||||
}
|
||||
if (filename == gFileName) {
|
||||
ok(true, "we saw the file get created");
|
||||
storage.removeEventListener("change", onChange);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
else {
|
||||
// we may see other file changes during the test, and
|
||||
// that is completely ok
|
||||
}
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(storage, "Should have storage");
|
||||
storage.addEventListener("change", onChange);
|
||||
|
||||
request = storage.addNamed(createRandomBlob('image/png'), gFileName);
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,86 +0,0 @@
|
||||
<!--
|
||||
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=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </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=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var gFileName = randomFilename(20) + ".png"
|
||||
|
||||
function addSuccess(e) {
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function onChange(e) {
|
||||
|
||||
dump("we saw: " + e.path + " " + e.reason + "\n");
|
||||
|
||||
var filename = e.path;
|
||||
if (filename[0] == "/") {
|
||||
// We got /storageName/prefix/filename
|
||||
// Remove the storageName (this shows up on FirefoxOS)
|
||||
filename = filename.substring(1); // Remove leading slash
|
||||
var slashIndex = filename.indexOf("/");
|
||||
if (slashIndex >= 0) {
|
||||
filename = filename.substring(slashIndex + 1); // Remove storageName
|
||||
}
|
||||
}
|
||||
if (filename == gFileName) {
|
||||
ok(true, "we saw the file get created");
|
||||
storage.removeEventListener("change", onChange);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
else {
|
||||
// we may see other file changes during the test, and
|
||||
// that is completely ok
|
||||
}
|
||||
}
|
||||
|
||||
function onChangeFail(e) {
|
||||
dump("onChangeFail: " + e.path + " " + e.reason + "\n");
|
||||
ok(false, "We should never see any changes");
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
ok(storage, "Should have storage");
|
||||
storage.addEventListener("change", onChange);
|
||||
|
||||
var storageOther = navigator.getDeviceStorage("music");
|
||||
ok(storageOther, "Should have storage");
|
||||
storageOther.addEventListener("change", onChangeFail);
|
||||
|
||||
request = storage.addNamed(createRandomBlob('image/png'), gFileName);
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -116,14 +116,6 @@ const kEventConstructors = {
|
||||
return new DeviceProximityEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
DeviceStorageAreaChangedEvent: { create: function (aName, aProps) {
|
||||
return new DeviceStorageAreaChangedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
DeviceStorageChangeEvent: { create: function (aName, aProps) {
|
||||
return new DeviceStorageChangeEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
DownloadEvent: { create: function (aName, aProps) {
|
||||
return new DownloadEvent(aName, aProps);
|
||||
},
|
||||
|
@ -62,6 +62,12 @@ function doTest() {
|
||||
is(subtreeModifiedCount, 1,
|
||||
"Removing a child node should have dispatched a DOMSubtreeModified event");
|
||||
|
||||
subtree.innerHTML = s;
|
||||
subtreeModifiedCount = 0;
|
||||
subtree.firstChild.firstChild.remove();
|
||||
is(subtreeModifiedCount, 1,
|
||||
"Removing a child node should have dispatched a DOMSubtreeModified event");
|
||||
|
||||
subtreeModifiedCount = 0;
|
||||
subtree.firstChild.setAttribute("foo", "bar");
|
||||
is(subtreeModifiedCount, 1,
|
||||
|
@ -1,220 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "mozilla/dom/Directory.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/PFileSystemParams.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace ipc;
|
||||
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* CreateDirectoryTaskChild
|
||||
*/
|
||||
|
||||
/* static */ already_AddRefed<CreateDirectoryTaskChild>
|
||||
CreateDirectoryTaskChild::Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
|
||||
RefPtr<CreateDirectoryTaskChild> task =
|
||||
new CreateDirectoryTaskChild(aFileSystem, aTargetPath);
|
||||
|
||||
// aTargetPath can be null. In this case SetError will be called.
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
if (NS_WARN_IF(!globalObject)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
task->mPromise = Promise::Create(globalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return task.forget();
|
||||
}
|
||||
|
||||
CreateDirectoryTaskChild::CreateDirectoryTaskChild(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath)
|
||||
: FileSystemTaskChildBase(aFileSystem)
|
||||
, mTargetPath(aTargetPath)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
}
|
||||
|
||||
CreateDirectoryTaskChild::~CreateDirectoryTaskChild()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateDirectoryTaskChild::GetPromise()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return RefPtr<Promise>(mPromise).forget();
|
||||
}
|
||||
|
||||
FileSystemParams
|
||||
CreateDirectoryTaskChild::GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
|
||||
nsAutoString path;
|
||||
aRv = mTargetPath->GetPath(path);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return FileSystemCreateDirectoryParams();
|
||||
}
|
||||
|
||||
return FileSystemCreateDirectoryParams(aSerializedDOMPath, path);
|
||||
}
|
||||
|
||||
void
|
||||
CreateDirectoryTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
|
||||
const FileSystemDirectoryResponse& r =
|
||||
aValue.get_FileSystemDirectoryResponse();
|
||||
|
||||
aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(mTargetPath));
|
||||
NS_WARNING_ASSERTION(!aRv.Failed(), "NS_NewLocalFile failed");
|
||||
}
|
||||
|
||||
void
|
||||
CreateDirectoryTaskChild::HandlerCallback()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasError()) {
|
||||
mPromise->MaybeReject(mErrorValue);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
|
||||
mTargetPath, mFileSystem);
|
||||
MOZ_ASSERT(dir);
|
||||
|
||||
mPromise->MaybeResolve(dir);
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
CreateDirectoryTaskChild::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral(DIRECTORY_CREATE_PERMISSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* CreateDirectoryTaskParent
|
||||
*/
|
||||
|
||||
/* static */ already_AddRefed<CreateDirectoryTaskParent>
|
||||
CreateDirectoryTaskParent::Create(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
|
||||
RefPtr<CreateDirectoryTaskParent> task =
|
||||
new CreateDirectoryTaskParent(aFileSystem, aParam, aParent);
|
||||
|
||||
aRv = NS_NewLocalFile(aParam.realPath(), true,
|
||||
getter_AddRefs(task->mTargetPath));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return task.forget();
|
||||
}
|
||||
|
||||
CreateDirectoryTaskParent::CreateDirectoryTaskParent(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent)
|
||||
: FileSystemTaskParentBase(aFileSystem, aParam, aParent)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
CreateDirectoryTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
nsAutoString path;
|
||||
aRv = mTargetPath->GetPath(path);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return FileSystemDirectoryResponse();
|
||||
}
|
||||
|
||||
return FileSystemDirectoryResponse(path);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CreateDirectoryTaskParent::IOWork()
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
|
||||
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool fileExists;
|
||||
nsresult rv = mTargetPath->Exists(&fileExists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (fileExists) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
|
||||
}
|
||||
|
||||
rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0770);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CreateDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral(DIRECTORY_CREATE_PERMISSION);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,87 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "mozilla/ErrorResult.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FileSystemCreateDirectoryParams;
|
||||
class Promise;
|
||||
|
||||
class CreateDirectoryTaskChild final : public FileSystemTaskChildBase
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<CreateDirectoryTaskChild>
|
||||
Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual
|
||||
~CreateDirectoryTaskChild();
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetPromise();
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
|
||||
virtual void
|
||||
HandlerCallback() override;
|
||||
|
||||
protected:
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const override;
|
||||
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
|
||||
private:
|
||||
CreateDirectoryTaskChild(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath);
|
||||
|
||||
RefPtr<Promise> mPromise;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
};
|
||||
|
||||
class CreateDirectoryTaskParent final : public FileSystemTaskParentBase
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<CreateDirectoryTaskParent>
|
||||
Create(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
|
||||
protected:
|
||||
virtual nsresult
|
||||
IOWork() override;
|
||||
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult(ErrorResult& aRv) const override;
|
||||
|
||||
private:
|
||||
CreateDirectoryTaskParent(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_CreateDirectoryTask_h
|
@ -1,399 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "CreateFileTask.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/FileBlobImpl.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/PFileSystemParams.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ipc/BlobChild.h"
|
||||
#include "mozilla/dom/ipc/BlobParent.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
#define GET_PERMISSION_ACCESS_TYPE(aAccess) \
|
||||
if (mReplace) { \
|
||||
aAccess.AssignLiteral(DIRECTORY_WRITE_PERMISSION); \
|
||||
return; \
|
||||
} \
|
||||
aAccess.AssignLiteral(DIRECTORY_CREATE_PERMISSION);
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
*CreateFileTaskChild
|
||||
*/
|
||||
|
||||
/* static */ already_AddRefed<CreateFileTaskChild>
|
||||
CreateFileTaskChild::Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
Blob* aBlobData,
|
||||
InfallibleTArray<uint8_t>& aArrayData,
|
||||
bool aReplace,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
|
||||
RefPtr<CreateFileTaskChild> task =
|
||||
new CreateFileTaskChild(aFileSystem, aTargetPath, aReplace);
|
||||
|
||||
// aTargetPath can be null. In this case SetError will be called.
|
||||
|
||||
if (aBlobData) {
|
||||
task->mBlobImpl = aBlobData->Impl();
|
||||
}
|
||||
|
||||
task->mArrayData.SwapElements(aArrayData);
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
if (NS_WARN_IF(!globalObject)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
task->mPromise = Promise::Create(globalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return task.forget();
|
||||
}
|
||||
|
||||
CreateFileTaskChild::CreateFileTaskChild(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
bool aReplace)
|
||||
: FileSystemTaskChildBase(aFileSystem)
|
||||
, mTargetPath(aTargetPath)
|
||||
, mReplace(aReplace)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
}
|
||||
|
||||
CreateFileTaskChild::~CreateFileTaskChild()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateFileTaskChild::GetPromise()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return RefPtr<Promise>(mPromise).forget();
|
||||
}
|
||||
|
||||
FileSystemParams
|
||||
CreateFileTaskChild::GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
FileSystemCreateFileParams param;
|
||||
param.filesystem() = aSerializedDOMPath;
|
||||
|
||||
aRv = mTargetPath->GetPath(param.realPath());
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return param;
|
||||
}
|
||||
|
||||
// If we are here, PBackground must be up and running: this method is called
|
||||
// when the task has been already started by FileSystemPermissionRequest
|
||||
// class and this happens only when PBackground actor has already been
|
||||
// created.
|
||||
PBackgroundChild* actor =
|
||||
mozilla::ipc::BackgroundChild::GetForCurrentThread();
|
||||
MOZ_ASSERT(actor);
|
||||
|
||||
param.replace() = mReplace;
|
||||
if (mBlobImpl) {
|
||||
PBlobChild* blobActor =
|
||||
mozilla::ipc::BackgroundChild::GetOrCreateActorForBlobImpl(actor,
|
||||
mBlobImpl);
|
||||
if (blobActor) {
|
||||
param.data() = blobActor;
|
||||
}
|
||||
} else {
|
||||
param.data() = mArrayData;
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
void
|
||||
CreateFileTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
|
||||
const FileSystemFileResponse& r = aValue.get_FileSystemFileResponse();
|
||||
|
||||
mBlobImpl = static_cast<BlobChild*>(r.blobChild())->GetBlobImpl();
|
||||
MOZ_ASSERT(mBlobImpl);
|
||||
}
|
||||
|
||||
void
|
||||
CreateFileTaskChild::HandlerCallback()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasError()) {
|
||||
mPromise->MaybeReject(mErrorValue);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<File> file = File::Create(mFileSystem->GetParentObject(), mBlobImpl);
|
||||
mPromise->MaybeResolve(file);
|
||||
|
||||
mBlobImpl = nullptr;
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
CreateFileTaskChild::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
GET_PERMISSION_ACCESS_TYPE(aAccess)
|
||||
}
|
||||
|
||||
/**
|
||||
* CreateFileTaskParent
|
||||
*/
|
||||
|
||||
uint32_t CreateFileTaskParent::sOutputBufferSize = 0;
|
||||
|
||||
/* static */ already_AddRefed<CreateFileTaskParent>
|
||||
CreateFileTaskParent::Create(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateFileParams& aParam,
|
||||
FileSystemRequestParent* aParent,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
|
||||
RefPtr<CreateFileTaskParent> task =
|
||||
new CreateFileTaskParent(aFileSystem, aParam, aParent);
|
||||
|
||||
aRv = NS_NewLocalFile(aParam.realPath(), true,
|
||||
getter_AddRefs(task->mTargetPath));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
task->mReplace = aParam.replace();
|
||||
|
||||
const FileSystemFileDataValue& data = aParam.data();
|
||||
|
||||
if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
|
||||
task->mArrayData = data;
|
||||
return task.forget();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(data.type() == FileSystemFileDataValue::TPBlobParent);
|
||||
|
||||
BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
|
||||
task->mBlobImpl = bp->GetBlobImpl();
|
||||
MOZ_ASSERT(task->mBlobImpl, "blobData should not be null.");
|
||||
|
||||
return task.forget();
|
||||
}
|
||||
|
||||
CreateFileTaskParent::CreateFileTaskParent(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateFileParams& aParam,
|
||||
FileSystemRequestParent* aParent)
|
||||
: FileSystemTaskParentBase(aFileSystem, aParam, aParent)
|
||||
, mReplace(false)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
CreateFileTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
RefPtr<BlobImpl> blobImpl = new FileBlobImpl(mTargetPath);
|
||||
BlobParent* blobParent =
|
||||
BlobParent::GetOrCreate(mRequestParent->Manager(), blobImpl);
|
||||
return FileSystemFileResponse(blobParent, nullptr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CreateFileTaskParent::IOWork()
|
||||
{
|
||||
class MOZ_RAII AutoClose final
|
||||
{
|
||||
public:
|
||||
explicit AutoClose(nsIOutputStream* aStream)
|
||||
: mStream(aStream)
|
||||
{
|
||||
MOZ_ASSERT(aStream);
|
||||
}
|
||||
|
||||
~AutoClose()
|
||||
{
|
||||
mStream->Close();
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIOutputStream> mStream;
|
||||
};
|
||||
|
||||
MOZ_ASSERT(XRE_IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
|
||||
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mFileSystem->IsSafeFile(mTargetPath)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
bool exists = false;
|
||||
nsresult rv = mTargetPath->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
bool isFile = false;
|
||||
rv = mTargetPath->IsFile(&isFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!isFile) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
|
||||
}
|
||||
|
||||
if (!mReplace) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
|
||||
}
|
||||
|
||||
// Remove the old file before creating.
|
||||
rv = mTargetPath->Remove(false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
rv = mTargetPath->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> outputStream;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mTargetPath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
AutoClose acOutputStream(outputStream);
|
||||
MOZ_ASSERT(sOutputBufferSize);
|
||||
|
||||
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
|
||||
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
|
||||
outputStream,
|
||||
sOutputBufferSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
AutoClose acBufferedOutputStream(bufferedOutputStream);
|
||||
|
||||
// Write the file content from blob data.
|
||||
if (mBlobImpl) {
|
||||
ErrorResult error;
|
||||
nsCOMPtr<nsIInputStream> blobStream;
|
||||
mBlobImpl->GetInternalStream(getter_AddRefs(blobStream), error);
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
return error.StealNSResult();
|
||||
}
|
||||
|
||||
uint64_t bufSize = 0;
|
||||
rv = blobStream->Available(&bufSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
while (bufSize && !mFileSystem->IsShutdown()) {
|
||||
uint32_t written = 0;
|
||||
uint32_t writeSize = bufSize < UINT32_MAX ? bufSize : UINT32_MAX;
|
||||
rv = bufferedOutputStream->WriteFrom(blobStream, writeSize, &written);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
bufSize -= written;
|
||||
}
|
||||
|
||||
blobStream->Close();
|
||||
|
||||
if (mFileSystem->IsShutdown()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Write file content from array data.
|
||||
|
||||
uint32_t written;
|
||||
rv = bufferedOutputStream->Write(
|
||||
reinterpret_cast<char*>(mArrayData.Elements()),
|
||||
mArrayData.Length(),
|
||||
&written);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mArrayData.Length() != written) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CreateFileTaskParent::MainThreadWork()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sOutputBufferSize) {
|
||||
sOutputBufferSize =
|
||||
mozilla::Preferences::GetUint("dom.filesystem.outputBufferSize", 4096 * 4);
|
||||
}
|
||||
|
||||
return FileSystemTaskParentBase::MainThreadWork();
|
||||
}
|
||||
|
||||
void
|
||||
CreateFileTaskParent::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
GET_PERMISSION_ACCESS_TYPE(aAccess)
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,119 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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_CreateFileTask_h
|
||||
#define mozilla_dom_CreateFileTask_h
|
||||
|
||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
class nsIInputStream;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Blob;
|
||||
class BlobImpl;
|
||||
class Promise;
|
||||
|
||||
class CreateFileTaskChild final : public FileSystemTaskChildBase
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<CreateFileTaskChild>
|
||||
Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aFile,
|
||||
Blob* aBlobData,
|
||||
InfallibleTArray<uint8_t>& aArrayData,
|
||||
bool replace,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual
|
||||
~CreateFileTaskChild();
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetPromise();
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
|
||||
protected:
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const override;
|
||||
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
virtual void
|
||||
HandlerCallback() override;
|
||||
|
||||
private:
|
||||
CreateFileTaskChild(FileSystemBase* aFileSystem,
|
||||
nsIFile* aFile,
|
||||
bool aReplace);
|
||||
|
||||
RefPtr<Promise> mPromise;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
|
||||
// This is the content of what we want to store. Then, when the File is
|
||||
// created, this will be used to store the new object.
|
||||
RefPtr<BlobImpl> mBlobImpl;
|
||||
|
||||
// This is going to be the content of the file, received by createFile()
|
||||
// params.
|
||||
InfallibleTArray<uint8_t> mArrayData;
|
||||
|
||||
bool mReplace;
|
||||
};
|
||||
|
||||
class CreateFileTaskParent final : public FileSystemTaskParentBase
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<CreateFileTaskParent>
|
||||
Create(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateFileParams& aParam,
|
||||
FileSystemRequestParent* aParent,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual bool
|
||||
NeedToGoToMainThread() const override { return true; }
|
||||
|
||||
virtual nsresult
|
||||
MainThreadWork() override;
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
|
||||
protected:
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult(ErrorResult& aRv) const override;
|
||||
|
||||
virtual nsresult
|
||||
IOWork() override;
|
||||
|
||||
private:
|
||||
CreateFileTaskParent(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateFileParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
|
||||
static uint32_t sOutputBufferSize;
|
||||
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
|
||||
RefPtr<BlobImpl> mBlobImpl;
|
||||
|
||||
// This is going to be the content of the file, received by createFile()
|
||||
// params.
|
||||
InfallibleTArray<uint8_t> mArrayData;
|
||||
|
||||
bool mReplace;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_CreateFileTask_h
|
@ -1,222 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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/File.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsDeviceStorage.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType,
|
||||
const nsAString& aStorageName)
|
||||
: mStorageType(aStorageType)
|
||||
, mStorageName(aStorageName)
|
||||
, mWindowId(0)
|
||||
{
|
||||
mPermissionCheckType = ePermissionCheckByTestingPref;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
|
||||
mPermissionCheckType = ePermissionCheckNotRequired;
|
||||
} else {
|
||||
mPermissionCheckType = ePermissionCheckRequired;
|
||||
}
|
||||
} else {
|
||||
AssertIsOnBackgroundThread();
|
||||
}
|
||||
|
||||
// Get the permission name required to access the file system.
|
||||
DebugOnly<nsresult> rv =
|
||||
DeviceStorageTypeChecker::GetPermissionForType(mStorageType, mPermission);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "GetPermissionForType failed");
|
||||
|
||||
// Get the local path of the file system root.
|
||||
nsCOMPtr<nsIFile> rootFile;
|
||||
DeviceStorageFile::GetRootDirectoryForType(aStorageType,
|
||||
aStorageName,
|
||||
getter_AddRefs(rootFile));
|
||||
|
||||
Unused <<
|
||||
NS_WARN_IF(!rootFile ||
|
||||
NS_FAILED(rootFile->GetPath(mLocalOrDeviceStorageRootPath)));
|
||||
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (NS_IsMainThread()) {
|
||||
DebugOnly<DeviceStorageTypeChecker*> typeChecker =
|
||||
DeviceStorageTypeChecker::CreateOrGet();
|
||||
MOZ_ASSERT(typeChecker);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceStorageFileSystem::~DeviceStorageFileSystem()
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
}
|
||||
|
||||
already_AddRefed<FileSystemBase>
|
||||
DeviceStorageFileSystem::Clone()
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
RefPtr<DeviceStorageFileSystem> fs =
|
||||
new DeviceStorageFileSystem(mStorageType, mStorageName);
|
||||
|
||||
fs->mWindowId = mWindowId;
|
||||
|
||||
return fs.forget();
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aDeviceStorage);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = aDeviceStorage->GetOwner();
|
||||
MOZ_ASSERT(window->IsInnerWindow());
|
||||
mWindowId = window->WindowID();
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFileSystem::Shutdown()
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
mShutdown = true;
|
||||
}
|
||||
|
||||
nsISupports*
|
||||
DeviceStorageFileSystem::GetParentObject() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
|
||||
MOZ_ASSERT_IF(!mShutdown, window);
|
||||
return window ? window->AsInner() : nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFileSystem::GetDirectoryName(nsIFile* aFile, nsAString& aRetval,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aFile);
|
||||
|
||||
nsCOMPtr<nsIFile> rootPath;
|
||||
aRv = NS_NewLocalFile(LocalOrDeviceStorageRootPath(), false,
|
||||
getter_AddRefs(rootPath));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool equal = false;
|
||||
aRv = aFile->Equals(rootPath, &equal);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (equal) {
|
||||
aRetval = mStorageName;
|
||||
return;
|
||||
}
|
||||
|
||||
FileSystemBase::GetDirectoryName(aFile, aRetval, aRv);
|
||||
NS_WARNING_ASSERTION(!aRv.Failed(), "GetDirectoryName failed");
|
||||
}
|
||||
|
||||
bool
|
||||
DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!");
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aFile);
|
||||
|
||||
nsCOMPtr<nsIFile> rootPath;
|
||||
nsresult rv = NS_NewLocalFile(LocalOrDeviceStorageRootPath(), false,
|
||||
getter_AddRefs(rootPath));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if this file belongs to this storage.
|
||||
if (NS_WARN_IF(!FileSystemUtils::IsDescendantPath(rootPath, aFile))) {
|
||||
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);
|
||||
}
|
||||
|
||||
bool
|
||||
DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aDir);
|
||||
|
||||
ErrorResult rv;
|
||||
RefPtr<FileSystemBase> fs = aDir->GetFileSystem(rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoString fsSerialization;
|
||||
fs->SerializeDOMPath(fsSerialization);
|
||||
|
||||
nsAutoString thisSerialization;
|
||||
SerializeDOMPath(thisSerialization);
|
||||
|
||||
// Check if the given directory is from this storage.
|
||||
return fsSerialization == thisSerialization;
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFileSystem::SerializeDOMPath(nsAString& aString) const
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
// Generate the string representation of the file system.
|
||||
aString.AssignLiteral("devicestorage-");
|
||||
aString.Append(mStorageType);
|
||||
aString.Append('-');
|
||||
aString.Append(mStorageName);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageFileSystem::MainThreadWork()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
DebugOnly<DeviceStorageTypeChecker*> typeChecker =
|
||||
DeviceStorageTypeChecker::CreateOrGet();
|
||||
MOZ_ASSERT(typeChecker);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,74 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 already_AddRefed<FileSystemBase>
|
||||
Clone() override;
|
||||
|
||||
virtual bool
|
||||
ShouldCreateDirectory() override { return true; }
|
||||
|
||||
virtual void
|
||||
Shutdown() override;
|
||||
|
||||
virtual nsISupports*
|
||||
GetParentObject() const override;
|
||||
|
||||
virtual void
|
||||
GetDirectoryName(nsIFile* aFile, nsAString& aRetval,
|
||||
ErrorResult& aRv) const override;
|
||||
|
||||
virtual bool
|
||||
IsSafeFile(nsIFile* aFile) const override;
|
||||
|
||||
virtual bool
|
||||
IsSafeDirectory(Directory* aDir) const override;
|
||||
|
||||
virtual void
|
||||
SerializeDOMPath(nsAString& aSerializedString) const override;
|
||||
|
||||
virtual bool
|
||||
NeedToGoToMainThread() const override { return true; }
|
||||
|
||||
virtual nsresult
|
||||
MainThreadWork() override;
|
||||
|
||||
private:
|
||||
virtual
|
||||
~DeviceStorageFileSystem();
|
||||
|
||||
nsString mStorageType;
|
||||
nsString mStorageName;
|
||||
|
||||
uint64_t mWindowId;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_DeviceStorageFileSystem_h
|
@ -6,13 +6,8 @@
|
||||
|
||||
#include "mozilla/dom/Directory.h"
|
||||
|
||||
#include "CreateDirectoryTask.h"
|
||||
#include "CreateFileTask.h"
|
||||
#include "FileSystemPermissionRequest.h"
|
||||
#include "GetDirectoryListingTask.h"
|
||||
#include "GetFileOrDirectoryTask.h"
|
||||
#include "GetFilesTask.h"
|
||||
#include "RemoveTask.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
@ -22,18 +17,6 @@
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/OSFileSystem.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
|
||||
// Undefine the macro of CreateFile to avoid Directory#CreateFile being replaced
|
||||
// by Directory#CreateFileW.
|
||||
#ifdef CreateFile
|
||||
#undef CreateFile
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -64,16 +47,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Directory)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
/* static */ bool
|
||||
Directory::DeviceStorageEnabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Preferences::GetBool("device.storage.enabled", false);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Directory::WebkitBlinkDirectoryPickerEnabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
@ -92,30 +65,6 @@ Directory::WebkitBlinkDirectoryPickerEnabled(JSContext* aCx, JSObject* aObj)
|
||||
return workerPrivate->WebkitBlinkDirectoryPickerEnabled();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<Promise>
|
||||
Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv)
|
||||
{
|
||||
// Only exposed for DeviceStorage.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
|
||||
nsCOMPtr<nsIFile> path;
|
||||
aRv = NS_NewLocalFile(aFileSystem->LocalOrDeviceStorageRootPath(),
|
||||
true, getter_AddRefs(path));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<GetFileOrDirectoryTaskChild> task =
|
||||
GetFileOrDirectoryTaskChild::Create(aFileSystem, path, true, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<Directory>
|
||||
Directory::Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aRealPath,
|
||||
@ -194,177 +143,6 @@ Directory::GetName(nsAString& aRetval, ErrorResult& aRv)
|
||||
fs->GetDirectoryName(mFile, aRetval, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// Only exposed for DeviceStorage.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<Blob> blobData;
|
||||
InfallibleTArray<uint8_t> arrayData;
|
||||
bool replace = (aOptions.mIfExists == CreateIfExistsMode::Replace);
|
||||
|
||||
// Get the file content.
|
||||
if (aOptions.mData.WasPassed()) {
|
||||
auto& data = aOptions.mData.Value();
|
||||
if (data.IsString()) {
|
||||
NS_ConvertUTF16toUTF8 str(data.GetAsString());
|
||||
arrayData.AppendElements(reinterpret_cast<const uint8_t *>(str.get()),
|
||||
str.Length());
|
||||
} else if (data.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = data.GetAsArrayBuffer();
|
||||
buffer.ComputeLengthAndData();
|
||||
arrayData.AppendElements(buffer.Data(), buffer.Length());
|
||||
} else if (data.IsArrayBufferView()){
|
||||
const ArrayBufferView& view = data.GetAsArrayBufferView();
|
||||
view.ComputeLengthAndData();
|
||||
arrayData.AppendElements(view.Data(), view.Length());
|
||||
} else {
|
||||
blobData = data.GetAsBlob();
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
|
||||
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<CreateFileTaskChild> task =
|
||||
CreateFileTaskChild::Create(fs, realPath, blobData, arrayData, replace,
|
||||
aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
task->SetError(error);
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv)
|
||||
{
|
||||
// Only exposed for DeviceStorage.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
|
||||
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<CreateDirectoryTaskChild> task =
|
||||
CreateDirectoryTaskChild::Create(fs, realPath, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
task->SetError(error);
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::Get(const nsAString& aPath, ErrorResult& aRv)
|
||||
{
|
||||
// Only exposed for DeviceStorage.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
|
||||
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<GetFileOrDirectoryTaskChild> task =
|
||||
GetFileOrDirectoryTaskChild::Create(fs, realPath, false, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
task->SetError(error);
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::Remove(const StringOrFileOrDirectory& aPath, ErrorResult& aRv)
|
||||
{
|
||||
// Only exposed for DeviceStorage.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return RemoveInternal(aPath, false, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::RemoveDeep(const StringOrFileOrDirectory& aPath, ErrorResult& aRv)
|
||||
{
|
||||
// Only exposed for DeviceStorage.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return RemoveInternal(aPath, true, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// Only exposed for DeviceStorage.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult error = NS_OK;
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
|
||||
// Check and get the target path.
|
||||
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If this is a File
|
||||
if (aPath.IsFile()) {
|
||||
if (!fs->GetRealPath(aPath.GetAsFile().Impl(),
|
||||
getter_AddRefs(realPath))) {
|
||||
error = NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// If this is a string
|
||||
} else if (aPath.IsString()) {
|
||||
error = DOMPathToRealPath(aPath.GetAsString(), getter_AddRefs(realPath));
|
||||
|
||||
// Directory
|
||||
} else {
|
||||
MOZ_ASSERT(aPath.IsDirectory());
|
||||
if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) {
|
||||
error = NS_ERROR_DOM_SECURITY_ERR;
|
||||
} else {
|
||||
realPath = aPath.GetAsDirectory().mFile;
|
||||
}
|
||||
}
|
||||
|
||||
// The target must be a descendant of this directory.
|
||||
if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) {
|
||||
error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
RefPtr<RemoveTaskChild> task =
|
||||
RemoveTaskChild::Create(fs, mFile, realPath, aRecursive, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
task->SetError(error);
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
void
|
||||
Directory::GetPath(nsAString& aRetval, ErrorResult& aRv)
|
||||
{
|
||||
@ -409,7 +187,8 @@ Directory::GetFilesAndDirectories(ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
task->Start();
|
||||
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
@ -430,7 +209,8 @@ Directory::GetFiles(bool aRecursiveFlag, ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
task->Start();
|
||||
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
@ -459,37 +239,6 @@ Directory::GetFileSystem(ErrorResult& aRv)
|
||||
return mFileSystem;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Directory::DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const
|
||||
{
|
||||
nsString relativePath;
|
||||
relativePath = aPath;
|
||||
|
||||
// Trim white spaces.
|
||||
static const char kWhitespace[] = "\b\t\r\n ";
|
||||
relativePath.Trim(kWhitespace);
|
||||
|
||||
nsTArray<nsString> parts;
|
||||
if (!FileSystemUtils::IsValidRelativeDOMPath(relativePath, parts)) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsresult rv = mFile->Clone(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < parts.Length(); ++i) {
|
||||
rv = file->AppendRelativePath(parts[i]);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
file.forget(aFile);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
Directory::ClonableToDifferentThreadOrProcess() const
|
||||
|
@ -14,22 +14,9 @@
|
||||
#include "nsCycleCollectionParticipant.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
|
||||
// Undefine the macro of CreateFile to avoid Directory#CreateFile being replaced
|
||||
// by Directory#CreateFileW.
|
||||
#ifdef CreateFile
|
||||
#undef CreateFile
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct CreateFileOptions;
|
||||
class FileSystemBase;
|
||||
class Promise;
|
||||
class StringOrFileOrDirectory;
|
||||
@ -42,15 +29,9 @@ public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory)
|
||||
|
||||
static bool
|
||||
DeviceStorageEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
static bool
|
||||
WebkitBlinkDirectoryPickerEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
static already_AddRefed<Promise>
|
||||
GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<Directory>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aRealPath,
|
||||
@ -71,22 +52,6 @@ public:
|
||||
void
|
||||
GetName(nsAString& aRetval, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateDirectory(const nsAString& aPath, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Get(const nsAString& aPath, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Remove(const StringOrFileOrDirectory& aPath, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
RemoveDeep(const StringOrFileOrDirectory& aPath, ErrorResult& aRv);
|
||||
|
||||
// From https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface :
|
||||
|
||||
void
|
||||
@ -152,10 +117,6 @@ private:
|
||||
nsresult
|
||||
DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
RefPtr<FileSystemBase> mFileSystem;
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
|
@ -6,48 +6,14 @@
|
||||
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
|
||||
#include "DeviceStorageFileSystem.h"
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "OSFileSystem.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// static
|
||||
already_AddRefed<FileSystemBase>
|
||||
FileSystemBase::DeserializeDOMPath(const nsAString& aString)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
RefPtr<DeviceStorageFileSystem> f =
|
||||
new DeviceStorageFileSystem(storageType, storageName);
|
||||
return f.forget();
|
||||
}
|
||||
|
||||
return RefPtr<OSFileSystemParent>(new OSFileSystemParent(aString)).forget();
|
||||
}
|
||||
|
||||
FileSystemBase::FileSystemBase()
|
||||
: mShutdown(false)
|
||||
, mPermissionCheckType(eNotSet)
|
||||
#ifdef DEBUG
|
||||
, mOwningThread(PR_GetCurrentThread())
|
||||
#endif
|
||||
@ -133,8 +99,7 @@ FileSystemBase::GetDOMPath(nsIFile* aFile,
|
||||
aRetval.Truncate();
|
||||
|
||||
nsCOMPtr<nsIFile> fileSystemPath;
|
||||
aRv = NS_NewLocalFile(LocalOrDeviceStorageRootPath(),
|
||||
true, getter_AddRefs(fileSystemPath));
|
||||
aRv = NS_NewLocalFile(LocalRootPath(), true, getter_AddRefs(fileSystemPath));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
@ -20,10 +20,6 @@ class FileSystemBase
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(FileSystemBase)
|
||||
|
||||
// Create file system object from its string representation.
|
||||
static already_AddRefed<FileSystemBase>
|
||||
DeserializeDOMPath(const nsAString& aString);
|
||||
|
||||
FileSystemBase();
|
||||
|
||||
virtual void
|
||||
@ -56,9 +52,9 @@ public:
|
||||
* directory of the exposed root Directory (per type).
|
||||
*/
|
||||
const nsAString&
|
||||
LocalOrDeviceStorageRootPath() const
|
||||
LocalRootPath() const
|
||||
{
|
||||
return mLocalOrDeviceStorageRootPath;
|
||||
return mLocalRootPath;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -76,46 +72,6 @@ public:
|
||||
bool
|
||||
GetRealPath(BlobImpl* aFile, nsIFile** aPath) const;
|
||||
|
||||
/*
|
||||
* Get the permission name required to access this file system.
|
||||
*/
|
||||
const nsCString&
|
||||
GetPermission() const
|
||||
{
|
||||
return mPermission;
|
||||
}
|
||||
|
||||
// The decision about doing or not doing the permission check cannot be done
|
||||
// everywhere because, for some FileSystemBase implementation, this depends on
|
||||
// a preference.
|
||||
// This enum describes all the possible decisions. The implementation will do
|
||||
// the check on the main-thread in the child and in the parent process when
|
||||
// needed.
|
||||
// Note: the permission check should not fail in PBackground because that
|
||||
// means that the child has been compromised. If this happens the child
|
||||
// process is killed.
|
||||
enum ePermissionCheckType {
|
||||
// When on the main-thread, we must check if we have
|
||||
// device.storage.prompt.testing set to true.
|
||||
ePermissionCheckByTestingPref,
|
||||
|
||||
// No permission check must be done.
|
||||
ePermissionCheckNotRequired,
|
||||
|
||||
// Permission check is required.
|
||||
ePermissionCheckRequired,
|
||||
|
||||
// This is the default value. We crash if this is let like this.
|
||||
eNotSet
|
||||
};
|
||||
|
||||
ePermissionCheckType
|
||||
PermissionCheckType() const
|
||||
{
|
||||
MOZ_ASSERT(mPermissionCheckType != eNotSet);
|
||||
return mPermissionCheckType;
|
||||
}
|
||||
|
||||
// IPC initialization
|
||||
// See how these 2 methods are used in FileSystemTaskChildBase.
|
||||
|
||||
@ -147,18 +103,10 @@ protected:
|
||||
// Directory object can have different OS 'root' path.
|
||||
// To be more clear, any path managed by this FileSystem implementation must
|
||||
// be discendant of this local root path.
|
||||
// The reason why it's not just called 'localRootPath' is because for
|
||||
// DeviceStorage this contains the path of the device storage SDCard, that is
|
||||
// the parent directory of the exposed root path.
|
||||
nsString mLocalOrDeviceStorageRootPath;
|
||||
nsString mLocalRootPath;
|
||||
|
||||
bool mShutdown;
|
||||
|
||||
// The permission name required to access the file system.
|
||||
nsCString mPermission;
|
||||
|
||||
ePermissionCheckType mPermissionCheckType;
|
||||
|
||||
#ifdef DEBUG
|
||||
PRThread* mOwningThread;
|
||||
#endif
|
||||
|
@ -1,262 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "FileSystemPermissionRequest.h"
|
||||
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
// This class takes care of the PBackground initialization and, once this step
|
||||
// is completed, it starts the task.
|
||||
class PBackgroundInitializer final : public nsIIPCBackgroundChildCreateCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
|
||||
|
||||
static void
|
||||
ScheduleTask(FileSystemTaskChildBase* aTask)
|
||||
{
|
||||
MOZ_ASSERT(aTask);
|
||||
RefPtr<PBackgroundInitializer> pb = new PBackgroundInitializer(aTask);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit PBackgroundInitializer(FileSystemTaskChildBase* aTask)
|
||||
: mTask(aTask)
|
||||
{
|
||||
MOZ_ASSERT(aTask);
|
||||
|
||||
PBackgroundChild* actor =
|
||||
mozilla::ipc::BackgroundChild::GetForCurrentThread();
|
||||
if (actor) {
|
||||
ActorCreated(actor);
|
||||
} else {
|
||||
if (NS_WARN_IF(
|
||||
!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~PBackgroundInitializer()
|
||||
{}
|
||||
|
||||
RefPtr<FileSystemTaskChildBase> mTask;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(PBackgroundInitializer,
|
||||
nsIIPCBackgroundChildCreateCallback)
|
||||
|
||||
void
|
||||
PBackgroundInitializer::ActorFailed()
|
||||
{
|
||||
MOZ_CRASH("Failed to create a PBackgroundChild actor!");
|
||||
}
|
||||
|
||||
void
|
||||
PBackgroundInitializer::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
|
||||
{
|
||||
mTask->Start();
|
||||
}
|
||||
|
||||
// This must be a CancelableRunnable because it can be dispatched to a worker
|
||||
// thread. But we don't care about the Cancel() because in that case, Run() is
|
||||
// not called and the task is deleted by the DTOR.
|
||||
class AsyncStartRunnable final : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit AsyncStartRunnable(FileSystemTaskChildBase* aTask)
|
||||
: mTask(aTask)
|
||||
{
|
||||
MOZ_ASSERT(aTask);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
PBackgroundInitializer::ScheduleTask(mTask);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<FileSystemTaskChildBase> mTask;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable,
|
||||
nsIContentPermissionRequest)
|
||||
|
||||
/* static */ void
|
||||
FileSystemPermissionRequest::RequestForTask(FileSystemTaskChildBase* aTask)
|
||||
{
|
||||
MOZ_ASSERT(aTask);
|
||||
|
||||
RefPtr<FileSystemBase> filesystem = aTask->GetFileSystem();
|
||||
if (!filesystem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (filesystem->PermissionCheckType() == FileSystemBase::ePermissionCheckNotRequired) {
|
||||
// Let's make the scheduling of this task asynchronous.
|
||||
RefPtr<AsyncStartRunnable> runnable = new AsyncStartRunnable(aTask);
|
||||
NS_DispatchToCurrentThread(runnable);
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't need any permission check for the FileSystem API. If we are here
|
||||
// it's because we are dealing with a DeviceStorage API that is main-thread
|
||||
// only.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<FileSystemPermissionRequest> request =
|
||||
new FileSystemPermissionRequest(aTask);
|
||||
NS_DispatchToCurrentThread(request);
|
||||
}
|
||||
|
||||
FileSystemPermissionRequest::FileSystemPermissionRequest(FileSystemTaskChildBase* aTask)
|
||||
: mTask(aTask)
|
||||
{
|
||||
MOZ_ASSERT(mTask, "aTask should not be null!");
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mTask->GetPermissionAccessType(mPermissionAccess);
|
||||
|
||||
RefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
|
||||
if (!filesystem) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPermissionType = filesystem->GetPermission();
|
||||
|
||||
mWindow = do_QueryInterface(filesystem->GetParentObject());
|
||||
if (NS_WARN_IF(!mWindow)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPrincipal = doc->NodePrincipal();
|
||||
mRequester = new nsContentPermissionRequester(mWindow);
|
||||
}
|
||||
|
||||
FileSystemPermissionRequest::~FileSystemPermissionRequest()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
nsTArray<nsString> emptyOptions;
|
||||
return nsContentPermissionUtils::CreatePermissionArray(mPermissionType,
|
||||
mPermissionAccess,
|
||||
emptyOptions,
|
||||
aTypes);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
|
||||
{
|
||||
NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::GetWindow(mozIDOMWindow** 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);
|
||||
ScheduleTask();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::Allow(JS::HandleValue aChoices)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aChoices.isUndefined());
|
||||
ScheduleTask();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
|
||||
if (!filesystem) {
|
||||
Cancel();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (filesystem->PermissionCheckType() == FileSystemBase::ePermissionCheckNotRequired) {
|
||||
Allow(JS::UndefinedHandleValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (filesystem->PermissionCheckType() == FileSystemBase::ePermissionCheckByTestingPref &&
|
||||
mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
|
||||
Allow(JS::UndefinedHandleValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mWindow) {
|
||||
Cancel();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsContentPermissionUtils::AskPermission(this, mWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileSystemPermissionRequest::GetRequester(nsIContentPermissionRequester** aRequester)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRequester);
|
||||
|
||||
nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
|
||||
requester.forget(aRequester);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemPermissionRequest::ScheduleTask()
|
||||
{
|
||||
PBackgroundInitializer::ScheduleTask(mTask);
|
||||
}
|
||||
|
||||
} /* namespace dom */
|
||||
} /* namespace mozilla */
|
@ -1,57 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "nsIRunnable.h"
|
||||
#include "nsIContentPermissionPrompt.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FileSystemTaskChildBase;
|
||||
|
||||
class FileSystemPermissionRequest final
|
||||
: public nsIContentPermissionRequest
|
||||
, public nsIRunnable
|
||||
{
|
||||
public:
|
||||
// Request permission for the given task.
|
||||
static void
|
||||
RequestForTask(FileSystemTaskChildBase* aTask);
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPERMISSIONREQUEST
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
explicit FileSystemPermissionRequest(FileSystemTaskChildBase* aTask);
|
||||
|
||||
~FileSystemPermissionRequest();
|
||||
|
||||
// Once the permission check has been done, we must run the task using IPC and
|
||||
// PBackground. This method checks if the PBackground thread is ready to
|
||||
// receive the task and in case waits for ActorCreated() to be called using
|
||||
// the PBackgroundInitializer class (see FileSystemPermissionRequest.cpp).
|
||||
void
|
||||
ScheduleTask();
|
||||
|
||||
nsCString mPermissionType;
|
||||
nsCString mPermissionAccess;
|
||||
RefPtr<FileSystemTaskChildBase> mTask;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsIContentPermissionRequester> mRequester;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FileSystemPermissionRequest_h
|
@ -3,15 +3,17 @@
|
||||
/* 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 "CreateFileTask.h"
|
||||
#include "mozilla/dom/FileSystemRequestParent.h"
|
||||
#include "mozilla/dom/PFileSystemParams.h"
|
||||
|
||||
#include "GetDirectoryListingTask.h"
|
||||
#include "GetFileOrDirectoryTask.h"
|
||||
#include "RemoveTask.h"
|
||||
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -30,7 +32,7 @@ FileSystemRequestParent::~FileSystemRequestParent()
|
||||
#define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name) \
|
||||
case FileSystemParams::TFileSystem##name##Params: { \
|
||||
const FileSystem##name##Params& p = aParams; \
|
||||
mFileSystem = FileSystemBase::DeserializeDOMPath(p.filesystem()); \
|
||||
mFileSystem = new OSFileSystemParent(p.filesystem()); \
|
||||
MOZ_ASSERT(mFileSystem); \
|
||||
mTask = name##TaskParent::Create(mFileSystem, p, this, rv); \
|
||||
if (NS_WARN_IF(rv.Failed())) { \
|
||||
@ -49,12 +51,9 @@ FileSystemRequestParent::Initialize(const FileSystemParams& aParams)
|
||||
|
||||
switch (aParams.type()) {
|
||||
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory)
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateFile)
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetDirectoryListing)
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory)
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFiles)
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(Remove)
|
||||
|
||||
default: {
|
||||
MOZ_CRASH("not reached");
|
||||
@ -67,15 +66,6 @@ FileSystemRequestParent::Initialize(const FileSystemParams& aParams)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mFileSystem->PermissionCheckType() != FileSystemBase::ePermissionCheckNotRequired) {
|
||||
nsAutoCString access;
|
||||
mTask->GetPermissionAccessType(access);
|
||||
|
||||
mPermissionName = mFileSystem->GetPermission();
|
||||
mPermissionName.Append('-');
|
||||
mPermissionName.Append(access);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -23,19 +23,6 @@ class FileSystemRequestParent final : public PFileSystemRequestParent
|
||||
public:
|
||||
FileSystemRequestParent();
|
||||
|
||||
const nsCString&
|
||||
PermissionName() const
|
||||
{
|
||||
return mPermissionName;
|
||||
}
|
||||
|
||||
FileSystemBase::ePermissionCheckType
|
||||
PermissionCheckType() const
|
||||
{
|
||||
return mFileSystem ? mFileSystem->PermissionCheckType()
|
||||
: FileSystemBase::eNotSet;
|
||||
}
|
||||
|
||||
bool
|
||||
Initialize(const FileSystemParams& aParams);
|
||||
|
||||
@ -56,8 +43,6 @@ private:
|
||||
RefPtr<FileSystemBase> mFileSystem;
|
||||
RefPtr<FileSystemTaskParentBase> mTask;
|
||||
|
||||
nsCString mPermissionName;
|
||||
|
||||
bool mDestroyed;
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,9 @@
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ipc/BlobParent.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
@ -103,6 +105,8 @@ private:
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMPL_ISUPPORTS(FileSystemTaskChildBase, nsIIPCBackgroundChildCreateCallback)
|
||||
|
||||
/**
|
||||
* FileSystemTaskBase class
|
||||
*/
|
||||
@ -132,6 +136,27 @@ FileSystemTaskChildBase::Start()
|
||||
{
|
||||
mFileSystem->AssertIsOnOwningThread();
|
||||
|
||||
mozilla::ipc::PBackgroundChild* actor =
|
||||
mozilla::ipc::BackgroundChild::GetForCurrentThread();
|
||||
if (actor) {
|
||||
ActorCreated(actor);
|
||||
} else {
|
||||
if (NS_WARN_IF(
|
||||
!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemTaskChildBase::ActorFailed()
|
||||
{
|
||||
MOZ_CRASH("Failed to create a PBackgroundChild actor!");
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemTaskChildBase::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
|
||||
{
|
||||
if (HasError()) {
|
||||
// In this case we don't want to use IPC at all.
|
||||
RefPtr<ErrorRunnable> runnable = new ErrorRunnable(this);
|
||||
@ -159,10 +184,7 @@ FileSystemTaskChildBase::Start()
|
||||
// mozilla::ipc::BackgroundChildImpl::DeallocPFileSystemRequestChild.
|
||||
NS_ADDREF_THIS();
|
||||
|
||||
// If we are here, PBackground must be up and running, because Start() is
|
||||
// called only by FileSystemPermissionRequest, and that class takes care of
|
||||
// PBackground initialization.
|
||||
PBackgroundChild* actor =
|
||||
mozilla::ipc::PBackgroundChild* actor =
|
||||
mozilla::ipc::BackgroundChild::GetForCurrentThread();
|
||||
MOZ_ASSERT(actor);
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/FileSystemRequestParent.h"
|
||||
#include "mozilla/dom/PFileSystemRequestChild.h"
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -20,10 +21,6 @@ class FileSystemBase;
|
||||
class FileSystemParams;
|
||||
class PBlobParent;
|
||||
|
||||
#define DIRECTORY_READ_PERMISSION "read"
|
||||
#define DIRECTORY_WRITE_PERMISSION "write"
|
||||
#define DIRECTORY_CREATE_PERMISSION "create"
|
||||
|
||||
/*
|
||||
* The base class to implement a Task class.
|
||||
* The file system operations can only be performed in the parent process. In
|
||||
@ -39,39 +36,34 @@ class PBlobParent;
|
||||
* Page
|
||||
* |
|
||||
* | (1)
|
||||
* ______|_______________________ | __________________________________
|
||||
* | | | | | |
|
||||
* ______|_________________________ | _________________________________
|
||||
* | | | | | |
|
||||
* | | | | | |
|
||||
* | V | IPC | PBackground thread on |
|
||||
* | [new FileSystemTaskChildBase()] | | | the parent process |
|
||||
* | | | | | |
|
||||
* | | (2) | | | |
|
||||
* | V | | | |
|
||||
* | [FileSystemPermissionRequest------------------\ |
|
||||
* | ::RequestForTask()] <------------------------/ |
|
||||
* | | | | | |
|
||||
* | | (3) | | |
|
||||
* | V | (4) | |
|
||||
* | | | | | |
|
||||
* | | (2) | | |
|
||||
* | V | (3) | |
|
||||
* | [GetRequestParams]------------------->[new FileSystemTaskParentBase()] |
|
||||
* | | | | |
|
||||
* | | | | | (5) _____________ |
|
||||
* | | | | | (4) _____________ |
|
||||
* | | | | | | | |
|
||||
* | | | | | | I/O Thread | |
|
||||
* | | | | | | | |
|
||||
* | | | | ---------> [IOWork] | |
|
||||
* | | IPC | | | | |
|
||||
* | | | | | | (6) | |
|
||||
* | | | | | | (5) | |
|
||||
* | | | | -------------- | |
|
||||
* | | | | | |_____________| |
|
||||
* | | | | | |
|
||||
* | | | | V |
|
||||
* | | | | [HandleResult] |
|
||||
* | | | | | |
|
||||
* | | | | (7) |
|
||||
* | | (8) | V |
|
||||
* | | | | (6) |
|
||||
* | | (7) | V |
|
||||
* | [SetRequestResult]<---------------------[GetRequestResult] |
|
||||
* | | | | |
|
||||
* | | (9) | | | |
|
||||
* | | (8) | | | |
|
||||
* | V | | | |
|
||||
* |[HandlerCallback] | IPC | |
|
||||
* |_______|_________________________| | |_________________________________|
|
||||
@ -84,44 +76,35 @@ class PBlobParent;
|
||||
* (1) Call FileSystem API from content page with JS. Create a task and run.
|
||||
* The base constructor [FileSystemTaskChildBase()] of the task should be
|
||||
* called.
|
||||
* (2) The FileSystemTaskChildBase object is given to
|
||||
* [FileSystemPermissionRequest::RequestForTask()] that will perform a
|
||||
* permission check step if needed (See ePermissionCheckType enum). The real
|
||||
* operation is done on the parent process but it's hidden by
|
||||
* [nsContentPermissionUtils::AskPermission()]. If the permission check is not
|
||||
* needed or if the page has the right permission, the
|
||||
* FileSystemPermissionRequest will start the task (only once PBackground
|
||||
* actor is fully initialized).
|
||||
* (3) Forward the task to the parent process through the IPC and call
|
||||
* (2) Forward the task to the parent process through the IPC and call
|
||||
* [GetRequestParams] to prepare the parameters of the IPC.
|
||||
* Parent:
|
||||
* (4) The parent process receives IPC and handle it in
|
||||
* (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 [FileSystemTaskParentBase(aParam, aParent)]
|
||||
* For security reasons, we do an additional permission check if needed. In
|
||||
* the check fails, the child process will be killed.
|
||||
* of the task should be called to set the task as an IPC task.
|
||||
* (5) The task operation will be performed in the member function of [IOWork].
|
||||
* IPC task.
|
||||
* (4) The task operation will be performed in the member function of [IOWork].
|
||||
* A I/O thread will be created to run that function. If error occurs
|
||||
* during the operation, call [SetError] to record the error and then abort.
|
||||
* (6) After finishing the task operation, call [HandleResult] to send the
|
||||
* (5) After finishing the task operation, call [HandleResult] to send the
|
||||
* result back to the child process though the IPC.
|
||||
* (7) Call [GetRequestResult] request result to prepare the parameters of the
|
||||
* (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, FileSystemTaskChildBase can handle the error message without
|
||||
* interfering.
|
||||
* Each task only needs to implement its specific success result preparation
|
||||
* function -[GetSuccessRequestResult].
|
||||
* Child/Parent:
|
||||
* (8) The process receives IPC and calls [SetRequestResult] to get the
|
||||
* (7) The 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.
|
||||
* (9) Call [HandlerCallback] to send the task result to the content page.
|
||||
* (8) Call [HandlerCallback] to send the task result to the content page.
|
||||
*/
|
||||
class FileSystemTaskChildBase : public PFileSystemRequestChild
|
||||
, public nsIIPCBackgroundChildCreateCallback
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(FileSystemTaskChildBase)
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
|
||||
|
||||
/*
|
||||
* Start the task. It will dispatch all the information to the parent process,
|
||||
@ -140,12 +123,6 @@ public:
|
||||
FileSystemBase*
|
||||
GetFileSystem() const;
|
||||
|
||||
/*
|
||||
* Get the type of permission access required to perform this task.
|
||||
*/
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const = 0;
|
||||
|
||||
/*
|
||||
* After the task is completed, this function will be called to pass the task
|
||||
* result to the content page. This method is called in the owning thread.
|
||||
@ -259,12 +236,6 @@ public:
|
||||
virtual nsresult
|
||||
MainThreadWork();
|
||||
|
||||
/*
|
||||
* Get the type of permission access required to perform this task.
|
||||
*/
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const = 0;
|
||||
|
||||
bool
|
||||
HasError() const { return NS_FAILED(mErrorValue); }
|
||||
|
||||
|
@ -13,9 +13,11 @@
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/PFileSystemParams.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "mozilla/dom/ipc/BlobChild.h"
|
||||
#include "mozilla/dom/ipc/BlobParent.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -176,12 +178,6 @@ GetDirectoryListingTaskChild::HandlerCallback()
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GetDirectoryListingTaskChild::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* GetDirectoryListingTaskParent
|
||||
*/
|
||||
@ -384,11 +380,5 @@ GetDirectoryListingTaskParent::IOWork()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GetDirectoryListingTaskParent::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -15,6 +15,8 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class BlobImpl;
|
||||
class FileSystemGetDirectoryListingParams;
|
||||
class OwningFileOrDirectory;
|
||||
|
||||
class GetDirectoryListingTaskChild final : public FileSystemTaskChildBase
|
||||
{
|
||||
@ -32,9 +34,6 @@ public:
|
||||
already_AddRefed<Promise>
|
||||
GetPromise();
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
|
||||
private:
|
||||
// If aDirectoryOnly is set, we should ensure that the target is a directory.
|
||||
GetDirectoryListingTaskChild(FileSystemBase* aFileSystem,
|
||||
@ -70,9 +69,6 @@ public:
|
||||
FileSystemRequestParent* aParent,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
|
||||
private:
|
||||
GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
|
||||
const FileSystemGetDirectoryListingParams& aParam,
|
||||
|
@ -27,14 +27,13 @@ namespace dom {
|
||||
/* static */ already_AddRefed<GetFileOrDirectoryTaskChild>
|
||||
GetFileOrDirectoryTaskChild::Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
bool aDirectoryOnly,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
|
||||
RefPtr<GetFileOrDirectoryTaskChild> task =
|
||||
new GetFileOrDirectoryTaskChild(aFileSystem, aTargetPath, aDirectoryOnly);
|
||||
new GetFileOrDirectoryTaskChild(aFileSystem, aTargetPath);
|
||||
|
||||
// aTargetPath can be null. In this case SetError will be called.
|
||||
|
||||
@ -54,8 +53,7 @@ GetFileOrDirectoryTaskChild::Create(FileSystemBase* aFileSystem,
|
||||
}
|
||||
|
||||
GetFileOrDirectoryTaskChild::GetFileOrDirectoryTaskChild(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
bool aDirectoryOnly)
|
||||
nsIFile* aTargetPath)
|
||||
: FileSystemTaskChildBase(aFileSystem)
|
||||
, mTargetPath(aTargetPath)
|
||||
{
|
||||
@ -156,12 +154,6 @@ GetFileOrDirectoryTaskChild::HandlerCallback()
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GetFileOrDirectoryTaskChild::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* GetFileOrDirectoryTaskParent
|
||||
*/
|
||||
@ -278,11 +270,5 @@ GetFileOrDirectoryTaskParent::IOWork()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GetFileOrDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -15,6 +15,7 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class BlobImpl;
|
||||
class FileSystemGetFileOrDirectoryParams;
|
||||
|
||||
class GetFileOrDirectoryTaskChild final : public FileSystemTaskChildBase
|
||||
{
|
||||
@ -22,7 +23,6 @@ public:
|
||||
static already_AddRefed<GetFileOrDirectoryTaskChild>
|
||||
Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
bool aDirectoryOnly,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual
|
||||
@ -31,9 +31,6 @@ public:
|
||||
already_AddRefed<Promise>
|
||||
GetPromise();
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
|
||||
protected:
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
@ -46,10 +43,8 @@ protected:
|
||||
HandlerCallback() override;
|
||||
|
||||
private:
|
||||
// If aDirectoryOnly is set, we should ensure that the target is a directory.
|
||||
GetFileOrDirectoryTaskChild(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
bool aDirectoryOnly);
|
||||
nsIFile* aTargetPath);
|
||||
|
||||
RefPtr<Promise> mPromise;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
@ -67,9 +62,6 @@ public:
|
||||
FileSystemRequestParent* aParent,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
|
||||
protected:
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult(ErrorResult& aRv) const override;
|
||||
|
@ -149,12 +149,6 @@ GetFilesTaskChild::HandlerCallback()
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GetFilesTaskChild::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral("read");
|
||||
}
|
||||
|
||||
/**
|
||||
* GetFilesTaskParent
|
||||
*/
|
||||
@ -259,11 +253,5 @@ GetFilesTaskParent::IOWork()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GetFilesTaskParent::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -16,6 +16,7 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class BlobImpl;
|
||||
class FileSystemGetFilesParams;
|
||||
|
||||
class GetFilesTaskChild final : public FileSystemTaskChildBase
|
||||
{
|
||||
@ -33,9 +34,6 @@ public:
|
||||
already_AddRefed<Promise>
|
||||
GetPromise();
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
|
||||
private:
|
||||
// If aDirectoryOnly is set, we should ensure that the target is a directory.
|
||||
GetFilesTaskChild(FileSystemBase* aFileSystem,
|
||||
@ -73,9 +71,6 @@ public:
|
||||
FileSystemRequestParent* aParent,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
|
||||
private:
|
||||
GetFilesTaskParent(FileSystemBase* aFileSystem,
|
||||
const FileSystemGetFilesParams& aParam,
|
||||
|
@ -19,12 +19,7 @@ namespace dom {
|
||||
|
||||
OSFileSystem::OSFileSystem(const nsAString& aRootDir)
|
||||
{
|
||||
mLocalOrDeviceStorageRootPath = aRootDir;
|
||||
mPermissionCheckType = ePermissionCheckNotRequired;
|
||||
|
||||
#ifdef DEBUG
|
||||
mPermission.AssignLiteral("never-used");
|
||||
#endif
|
||||
mLocalRootPath = aRootDir;
|
||||
}
|
||||
|
||||
already_AddRefed<FileSystemBase>
|
||||
@ -32,7 +27,7 @@ OSFileSystem::Clone()
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
RefPtr<OSFileSystem> fs = new OSFileSystem(mLocalOrDeviceStorageRootPath);
|
||||
RefPtr<OSFileSystem> fs = new OSFileSystem(mLocalRootPath);
|
||||
if (mParent) {
|
||||
fs->Init(mParent);
|
||||
}
|
||||
@ -102,7 +97,7 @@ void
|
||||
OSFileSystem::SerializeDOMPath(nsAString& aOutput) const
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
aOutput = mLocalOrDeviceStorageRootPath;
|
||||
aOutput = mLocalRootPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,12 +106,7 @@ OSFileSystem::SerializeDOMPath(nsAString& aOutput) const
|
||||
|
||||
OSFileSystemParent::OSFileSystemParent(const nsAString& aRootDir)
|
||||
{
|
||||
mLocalOrDeviceStorageRootPath = aRootDir;
|
||||
mPermissionCheckType = ePermissionCheckNotRequired;
|
||||
|
||||
#ifdef DEBUG
|
||||
mPermission.AssignLiteral("never-used");
|
||||
#endif
|
||||
mLocalRootPath = aRootDir;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -7,26 +7,6 @@ include protocol PBlob;
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct FileSystemCreateDirectoryParams
|
||||
{
|
||||
nsString filesystem;
|
||||
nsString realPath;
|
||||
};
|
||||
|
||||
union FileSystemFileDataValue
|
||||
{
|
||||
uint8_t[];
|
||||
PBlob;
|
||||
};
|
||||
|
||||
struct FileSystemCreateFileParams
|
||||
{
|
||||
nsString filesystem;
|
||||
nsString realPath;
|
||||
FileSystemFileDataValue data;
|
||||
bool replace;
|
||||
};
|
||||
|
||||
struct FileSystemGetDirectoryListingParams
|
||||
{
|
||||
nsString filesystem;
|
||||
@ -59,22 +39,11 @@ struct FileSystemGetFileOrDirectoryParams
|
||||
nsString realPath;
|
||||
};
|
||||
|
||||
struct FileSystemRemoveParams
|
||||
{
|
||||
nsString filesystem;
|
||||
nsString directory;
|
||||
nsString targetDirectory;
|
||||
bool recursive;
|
||||
};
|
||||
|
||||
union FileSystemParams
|
||||
{
|
||||
FileSystemCreateDirectoryParams;
|
||||
FileSystemCreateFileParams;
|
||||
FileSystemGetDirectoryListingParams;
|
||||
FileSystemGetFilesParams;
|
||||
FileSystemGetFileOrDirectoryParams;
|
||||
FileSystemRemoveParams;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
|
@ -52,14 +52,8 @@ struct FileSystemErrorResponse
|
||||
nsresult error;
|
||||
};
|
||||
|
||||
struct FileSystemBooleanResponse
|
||||
{
|
||||
bool success;
|
||||
};
|
||||
|
||||
union FileSystemResponseValue
|
||||
{
|
||||
FileSystemBooleanResponse;
|
||||
FileSystemDirectoryResponse;
|
||||
FileSystemDirectoryListingResponse;
|
||||
FileSystemFileResponse;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user