Merge m-c to autoland, a=merge

MozReview-Commit-ID: 1huLsbfIJEB
This commit is contained in:
Wes Kocher 2017-03-08 17:15:08 -08:00
Родитель 1b3d9372f6 3842d8fcef
Коммит c1d1ad8a06
3444 изменённых файлов: 41787 добавлений и 54170 удалений

Просмотреть файл

@ -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() {

Просмотреть файл

@ -1289,6 +1289,11 @@ pref("geo.provider.use_gpsd", true);
#endif
// We keep allowing non-HTTPS geo requests on all the release
// channels, for now.
// TODO: default to false (or remove altogether) for #1072859.
pref("geo.security.allowinsecure", true);
// Necko IPC security checks only needed for app isolation for cookies/cache/etc:
// currently irrelevant for desktop e10s
pref("network.disable.ipc.security", true);

Просмотреть файл

@ -6,7 +6,7 @@
// control inside the private browsing mode.
add_task(function* test() {
const testPageURL = "https://example.com/browser/" +
const testPageURL = "http://mochi.test:8888/browser/" +
"browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html";
function checkGeolocation(aPrivateMode, aWindow) {

Просмотреть файл

@ -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`);
}
}

Просмотреть файл

@ -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(&current);
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']

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше