Merge last PGO-green changeset from mozilla-inbound to mozilla-central

This commit is contained in:
Phil Ringnalda 2012-06-03 12:04:40 -07:00
Родитель 8f86773f20 513c660911
Коммит d203ecbd62
163 изменённых файлов: 9351 добавлений и 1451 удалений

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

@ -165,6 +165,7 @@
@BINPATH@/components/dom_css.xpt
@BINPATH@/components/dom_devicestorage.xpt
@BINPATH@/components/dom_events.xpt
@BINPATH@/components/dom_file.xpt
@BINPATH@/components/dom_geolocation.xpt
@BINPATH@/components/dom_media.xpt
@BINPATH@/components/dom_network.xpt

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

@ -162,9 +162,6 @@ exports.shutdown = function() {
* 'string' the most basic string type that doesn't need to convert
*/
function StringType(typeSpec) {
if (Object.keys(typeSpec).length > 0) {
throw new Error('StringType can not be customized');
}
}
StringType.prototype = Object.create(Type.prototype);
@ -311,9 +308,6 @@ exports.NumberType = NumberType;
* true/false values
*/
function BooleanType(typeSpec) {
if (Object.keys(typeSpec).length > 0) {
throw new Error('BooleanType can not be customized');
}
}
BooleanType.prototype = Object.create(SelectionType.prototype);
@ -334,6 +328,9 @@ BooleanType.prototype.parse = function(arg) {
};
BooleanType.prototype.stringify = function(value) {
if (value == null) {
return '';
}
return '' + value;
};
@ -404,9 +401,6 @@ exports.DeferredType = DeferredType;
* It should not be used anywhere else.
*/
function BlankType(typeSpec) {
if (Object.keys(typeSpec).length > 0) {
throw new Error('BlankType can not be customized');
}
}
BlankType.prototype = Object.create(Type.prototype);
@ -443,6 +437,9 @@ function ArrayType(typeSpec) {
ArrayType.prototype = Object.create(Type.prototype);
ArrayType.prototype.stringify = function(values) {
if (values == null) {
return '';
}
// BUG 664204: Check for strings with spaces and add quotes
return values.join(' ');
};
@ -1731,6 +1728,9 @@ SelectionType.prototype = Object.create(Type.prototype);
SelectionType.prototype.maxPredictions = 10;
SelectionType.prototype.stringify = function(value) {
if (value == null) {
return '';
}
if (this.stringifyProperty != null) {
return value[this.stringifyProperty];
}
@ -3219,9 +3219,6 @@ exports.unsetGlobalObject = function() {
* 'javascript' handles scripted input
*/
function JavascriptType(typeSpec) {
if (Object.keys(typeSpec).length > 0) {
throw new Error('JavascriptType can not be customized');
}
}
JavascriptType.prototype = Object.create(Type.prototype);
@ -3773,14 +3770,14 @@ exports.getDocument = function() {
* A CSS expression that refers to a single node
*/
function NodeType(typeSpec) {
if (Object.keys(typeSpec).length > 0) {
throw new Error('NodeType can not be customized');
}
}
NodeType.prototype = Object.create(Type.prototype);
NodeType.prototype.stringify = function(value) {
if (value == null) {
return '';
}
return value.__gcliQuery || 'Error';
};
@ -4207,10 +4204,6 @@ var lastSetting = null;
* A type for selecting a known setting
*/
function SettingType(typeSpec) {
if (Object.keys(typeSpec).length > 0) {
throw new Error('SettingType can not be customized');
}
settings.onChange.add(function(ev) {
this.clearCache();
}, this);
@ -4246,9 +4239,6 @@ SettingType.prototype.name = 'setting';
* A type for entering the value of a known setting
*/
function SettingValueType(typeSpec) {
if (Object.keys(typeSpec).length > 0) {
throw new Error('SettingType can not be customized');
}
}
SettingValueType.prototype = Object.create(DeferredType.prototype);

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

@ -3049,39 +3049,38 @@ exports.testPredictions = function(options) {
}
var resource1 = types.getType('resource');
var predictions1 = resource1.parseString('').getPredictions();
test.ok(predictions1.length > 1, 'have resources');
predictions1.forEach(function(prediction) {
var options1 = resource1.getLookup();
test.ok(options1.length > 1, 'have resources');
options1.forEach(function(prediction) {
checkPrediction(resource1, prediction);
});
var resource2 = types.getType({ name: 'resource', include: 'text/javascript' });
var predictions2 = resource2.parseString('').getPredictions();
test.ok(predictions2.length > 1, 'have resources');
predictions2.forEach(function(prediction) {
var options2 = resource2.getLookup();
test.ok(options2.length > 1, 'have resources');
options2.forEach(function(prediction) {
checkPrediction(resource2, prediction);
});
var resource3 = types.getType({ name: 'resource', include: 'text/css' });
var predictions3 = resource3.parseString('').getPredictions();
var options3 = resource3.getLookup();
// jsdom fails to support digging into stylesheets
if (!options.isNode) {
test.ok(predictions3.length >= 1, 'have resources');
test.ok(options3.length >= 1, 'have resources');
}
else {
test.log('Running under Node. ' +
'Skipping checks due to jsdom document.stylsheets support.');
}
predictions3.forEach(function(prediction) {
options3.forEach(function(prediction) {
checkPrediction(resource3, prediction);
});
var resource4 = types.getType({ name: 'resource' });
var predictions4 = resource4.parseString('').getPredictions();
var options4 = resource4.getLookup();
test.is(predictions1.length, predictions4.length, 'type spec');
// Bug 734045
// test.is(predictions2.length + predictions3.length, predictions4.length, 'split');
test.is(options1.length, options4.length, 'type spec');
test.is(options2.length + options3.length, options4.length, 'split');
};
function checkPrediction(res, prediction) {
@ -3813,6 +3812,28 @@ exports.setup = function() {
exports.shutdown = function() {
};
function forEachType(options, callback) {
types.getTypeNames().forEach(function(name) {
options.name = name;
// Provide some basic defaults to help selection/deferred/array work
if (name === 'selection') {
options.data = [ 'a', 'b' ];
}
else if (name === 'deferred') {
options.defer = function() {
return types.getType('string');
};
}
else if (name === 'array') {
options.subtype = 'string';
}
var type = types.getType(options);
callback(type);
});
}
exports.testDefault = function(options) {
if (options.isNode) {
test.log('Running under Node. ' +
@ -3820,27 +3841,29 @@ exports.testDefault = function(options) {
return;
}
types.getTypeNames().forEach(function(name) {
if (name === 'selection') {
name = { name: 'selection', data: [ 'a', 'b' ] };
forEachType({}, function(type) {
var blank = type.getBlank().value;
// boolean and array types are exempt from needing undefined blank values
if (type.name === 'boolean') {
test.is(blank, false, 'blank boolean is false');
}
if (name === 'deferred') {
name = {
name: 'deferred',
defer: function() { return types.getType('string'); }
};
else if (type.name === 'array') {
test.ok(Array.isArray(blank), 'blank array is array');
test.is(blank.length, 0, 'blank array is empty');
}
if (name === 'array') {
name = { name: 'array', subtype: 'string' };
}
var type = types.getType(name);
if (type.name !== 'boolean' && type.name !== 'array') {
test.ok(type.getBlank().value === undefined,
'default defined for ' + type.name);
else {
test.is(blank, undefined, 'default defined for ' + type.name);
}
});
};
exports.testNullDefault = function(options) {
forEachType({ defaultValue: null }, function(type) {
test.is(type.stringify(null), '', 'stringify(null) for ' + type.name);
});
};
});
/*
* Copyright 2009-2011 Mozilla Foundation and contributors

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

@ -247,8 +247,12 @@ DebuggerPane.prototype = {
/**
* Closes the debugger, removing child nodes and event listeners.
*
* @param function aCloseCallback
* Clients can pass a close callback to be notified when
* the panel successfully closes.
*/
close: function DP_close() {
close: function DP_close(aCloseCallback) {
if (!this._win) {
return;
}
@ -260,6 +264,16 @@ DebuggerPane.prototype = {
this._frame.removeEventListener("Debugger:Close", this.close, true);
this._frame.removeEventListener("unload", this.close, true);
// This method is also used as an event handler, so only
// use aCloseCallback if it's a function.
if (typeof(aCloseCallback) == "function") {
let frame = this._frame;
frame.addEventListener("unload", function onUnload() {
frame.removeEventListener("unload", onUnload, true);
aCloseCallback();
}, true)
}
this._nbox.removeChild(this._splitter);
this._nbox.removeChild(this._frame);

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

@ -11,6 +11,7 @@ const Cu = Components.utils;
const FRAME_STEP_CACHE_DURATION = 100; // ms
const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
const SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 1048576; // 1 MB in bytes
Cu.import("resource:///modules/source-editor.jsm");
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
@ -1011,7 +1012,9 @@ SourceScripts.prototype = {
_onShowScript: function SS__onShowScript(aScript, aOptions) {
aOptions = aOptions || {};
this._setEditorMode(aScript.url, aScript.contentType);
if (aScript.text.length < SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
this._setEditorMode(aScript.url, aScript.contentType);
}
let editor = DebuggerView.editor;
editor.setText(aScript.text);

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

@ -45,9 +45,7 @@ function testTab1(callback) {
is(DebuggerUI.getDebugger(), gPane1,
"getDebugger() should return the same pane as toggleDebugger().");
gPane1._frame.addEventListener("Debugger:Loaded", function dbgLoaded() {
gPane1._frame.removeEventListener("Debugger:Loaded", dbgLoaded, true);
wait_for_connect_and_resume(function dbgLoaded() {
info("First debugger has finished loading correctly.");
executeSoon(function() {
callback();
@ -180,7 +178,7 @@ function testTab4(callback) {
EventUtils.sendKey("SPACE");
info("The open button on the notification was pressed.");
executeSoon(function() {
wait_for_connect_and_resume(function() {
callback();
});
});
@ -211,18 +209,21 @@ function lastTest(callback) {
}
function cleanup(callback) {
removeTab(gTab1);
removeTab(gTab2);
removeTab(gTab3);
removeTab(gTab4);
gTab1 = null;
gTab2 = null;
gTab3 = null;
gTab4 = null;
gPane1 = null;
gPane2 = null;
gNbox = null;
callback();
closeDebuggerAndFinish(false, function() {
removeTab(gTab1);
removeTab(gTab2);
removeTab(gTab3);
removeTab(gTab4);
gTab1 = null;
gTab2 = null;
gTab3 = null;
gTab4 = null;
callback();
});
}

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

@ -55,14 +55,30 @@ function removeTab(aTab) {
gBrowser.removeTab(aTab);
}
function closeDebuggerAndFinish(aRemoteFlag) {
function closeDebuggerAndFinish(aRemoteFlag, aCallback) {
let debuggerClosed = false;
let debuggerDisconnected = false;
function _maybeFinish() {
if (debuggerClosed && debuggerDisconnected) {
if (!aCallback)
aCallback = finish;
aCallback();
}
}
DebuggerUI.chromeWindow.addEventListener("Debugger:Shutdown", function cleanup() {
DebuggerUI.chromeWindow.removeEventListener("Debugger:Shutdown", cleanup, false);
finish();
debuggerDisconnected = true;
_maybeFinish();
}, false);
if (!aRemoteFlag) {
DebuggerUI.getDebugger().close();
DebuggerUI.getDebugger().close(function() {
debuggerClosed = true;
_maybeFinish();
});
} else {
debuggerClosed = true;
DebuggerUI.getRemoteDebugger().close();
}
}
@ -116,6 +132,18 @@ function debug_tab_pane(aURL, aOnDebugging)
});
}
function wait_for_connect_and_resume(aOnDebugging)
{
window.document.addEventListener("Debugger:Connecting", function dbgConnected(aEvent) {
window.document.removeEventListener("Debugger:Connecting", dbgConnected, true);
// Wait for the initial resume...
aEvent.target.ownerDocument.defaultView.gClient.addOneTimeListener("resumed", function() {
aOnDebugging();
});
}, true);
}
function debug_remote(aURL, aOnDebugging, aBeforeTabAdded)
{
// Make any necessary preparations (start the debugger server etc.)

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

@ -44,6 +44,10 @@ const INSPECTOR_NOTIFICATIONS = {
const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
// Timer, in milliseconds, between change events fired by
// things like resize events.
const LAYOUT_CHANGE_TIMER = 250;
/**
* Represents an open instance of the Inspector for a tab.
* This is the object handed out to sidebars and other API consumers.
@ -61,7 +65,10 @@ function Inspector(aIUI)
{
this._IUI = aIUI;
this._winID = aIUI.winID;
this._browser = aIUI.browser;
this._listeners = {};
this._browser.addEventListener("resize", this, true);
}
Inspector.prototype = {
@ -106,18 +113,96 @@ Inspector.prototype = {
*/
change: function Inspector_change(aContext)
{
this._cancelLayoutChange();
this._IUI.nodeChanged(aContext);
},
/**
* Returns true if a given sidebar panel is currently visible.
* @param string aPanelName
* The panel name as registered with registerSidebar
*/
isPanelVisible: function Inspector_isPanelVisible(aPanelName)
{
return this._IUI.sidebar.visible &&
this._IUI.sidebar.activePanel === aPanelName;
},
/**
* Called by the InspectorUI when the inspector is being destroyed.
*/
_destroy: function Inspector__destroy()
{
this._cancelLayoutChange();
this._browser.removeEventListener("resize", this, true);
delete this._IUI;
delete this._listeners;
},
/**
* Event handler for DOM events.
*
* @param DOMEvent aEvent
*/
handleEvent: function Inspector_handleEvent(aEvent)
{
switch(aEvent.type) {
case "resize":
this._scheduleLayoutChange();
}
},
/**
* Schedule a low-priority change event for things like paint
* and resize.
*/
_scheduleLayoutChange: function Inspector_scheduleLayoutChange()
{
if (this._timer) {
return null;
}
this._timer = this._IUI.win.setTimeout(function() {
this.change("layout");
}.bind(this), LAYOUT_CHANGE_TIMER);
},
/**
* Cancel a pending low-priority change event if any is
* scheduled.
*/
_cancelLayoutChange: function Inspector_cancelLayoutChange()
{
if (this._timer) {
this._IUI.win.clearTimeout(this._timer);
delete this._timer;
}
},
/**
* Called by InspectorUI after a tab switch, when the
* inspector is no longer the active tab.
*/
_freeze: function Inspector__freeze()
{
this._cancelLayoutChange();
this._browser.removeEventListener("resize", this, true);
this._frozen = true;
},
/**
* Called by InspectorUI after a tab switch when the
* inspector is back to being the active tab.
*/
_thaw: function Inspector__thaw()
{
if (!this._frozen) {
return;
}
this._browser.addEventListener("resize", this, true);
delete this._frozen;
},
/// Event stuff. Would like to refactor this eventually.
/// Emulates the jetpack event source, which has a nice API.
@ -176,8 +261,21 @@ Inspector.prototype = {
{
if (!(aEvent in this._listeners))
return;
for each (let listener in this._listeners[aEvent]) {
listener.apply(null, arguments);
let originalListeners = this._listeners[aEvent];
for (let listener of this._listeners[aEvent]) {
// If the inspector was destroyed during event emission, stop
// emitting.
if (!this._listeners) {
break;
}
// If listeners were removed during emission, make sure the
// event handler we're going to fire wasn't removed.
if (originalListeners === this._listeners[aEvent] ||
this._listeners[aEvent].some(function(l) l === listener)) {
listener.apply(null, arguments);
}
}
}
}
@ -513,6 +611,7 @@ InspectorUI.prototype = {
// Has this windowID been inspected before?
if (this.store.hasID(this.winID)) {
this._currentInspector = this.store.getInspector(this.winID);
this._currentInspector._thaw();
let selectedNode = this.currentInspector._selectedNode;
if (selectedNode) {
this.inspectNode(selectedNode);
@ -646,9 +745,12 @@ InspectorUI.prototype = {
this.breadcrumbs = null;
}
delete this._currentInspector;
if (!aKeepInspector)
if (aKeepInspector) {
this._currentInspector._freeze();
} else {
this.store.deleteInspector(this.winID);
}
delete this._currentInspector;
this.inspectorUICommand.setAttribute("checked", "false");
@ -691,6 +793,7 @@ InspectorUI.prototype = {
_notifySelected: function IUI__notifySelected(aFrom)
{
this._currentInspector._cancelLayoutChange();
this._currentInspector._emit("select", aFrom);
},
@ -1477,9 +1580,6 @@ InspectorStyleSidebar.prototype = {
// wire up button to show the iframe
let onClick = function() {
this.activatePanel(aRegObj.id);
// Cheat a little bit and trigger a refresh
// when switching panels.
this._inspector.change("activatepanel-" + aRegObj.id);
}.bind(this);
btn.addEventListener("click", onClick, true);
@ -1636,7 +1736,13 @@ InspectorStyleSidebar.prototype = {
aTool.context = aTool.registration.load(this._inspector, aTool.frame);
this._inspector._emit("sidebaractivated", aTool.id);
this._inspector._emit("sidebaractivated-" + aTool.id);
// Send an event specific to the activation of this panel. For
// this initial event, include a "createpanel" argument
// to let panels watch sidebaractivated to refresh themselves
// but ignore the one immediately after their load.
// I don't really like this, we should find a better solution.
this._inspector._emit("sidebaractivated-" + aTool.id, "createpanel");
}.bind(this);
aTool.frame.addEventListener("load", aTool.onLoad, true);
aTool.frame.setAttribute("src", aTool.registration.contentURL);

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

@ -136,6 +136,17 @@ function ResponsiveUI(aWindow, aTab)
}
ResponsiveUI.prototype = {
_transitionsEnabled: true,
get transitionsEnabled() this._transitionsEnabled,
set transitionsEnabled(aValue) {
this._transitionsEnabled = aValue;
if (aValue && !this._resizing && this.stack.hasAttribute("responsivemode")) {
this.stack.removeAttribute("notransition");
} else if (!aValue) {
this.stack.setAttribute("notransition", "true");
}
},
/**
* Destroy the nodes. Remove listeners. Reset the style.
*/
@ -384,6 +395,7 @@ ResponsiveUI.prototype = {
this.mainWindow.addEventListener("mousemove", this.bound_onDrag, true);
this.container.style.pointerEvents = "none";
this._resizing = true;
this.stack.setAttribute("notransition", "true");
this.lastClientX = aEvent.clientX;
@ -431,7 +443,10 @@ ResponsiveUI.prototype = {
this.mainWindow.removeEventListener("mouseup", this.bound_stopResizing, true);
this.mainWindow.removeEventListener("mousemove", this.bound_onDrag, true);
this.stack.removeAttribute("notransition");
delete this._resizing;
if (this.transitionsEnabled) {
this.stack.removeAttribute("notransition");
}
this.ignoreY = false;
},
}

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

@ -46,6 +46,8 @@ include $(topsrcdir)/config/rules.mk
_BROWSER_FILES = \
browser_responsiveui.js \
browser_responsiveruleview.js \
browser_responsivecomputedview.js \
$(NULL)

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

@ -0,0 +1,111 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
let instance;
let computedView;
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
waitForFocus(startTest, content);
}, true);
content.location = "data:text/html,<html><style>" +
"div {" +
" width: 500px;" +
" height: 10px;" +
" background: purple;" +
"} " +
"@media screen and (max-width: 200px) {" +
" div { " +
" width: 100px;" +
" }" +
"};" +
"</style><div></div></html>"
function computedWidth() {
for (let prop of computedView.propertyViews) {
if (prop.name === "width") {
return prop.valueNode.textContent;
}
}
return null;
}
function startTest() {
document.getElementById("Tools:ResponsiveUI").doCommand();
executeSoon(onUIOpen);
}
function onUIOpen() {
instance = gBrowser.selectedTab.responsiveUI;
ok(instance, "instance of the module is attached to the tab.");
instance.stack.setAttribute("notransition", "true");
registerCleanupFunction(function() {
instance.stack.removeAttribute("notransition");
});
instance.setSize(500, 500);
Services.obs.addObserver(onInspectorUIOpen,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.openInspectorUI();
}
function onInspectorUIOpen() {
Services.obs.removeObserver(onInspectorUIOpen,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
let div = content.document.getElementsByTagName("div")[0];
InspectorUI.inspectNode(div);
InspectorUI.stopInspecting();
Services.obs.addObserver(testShrink, "StyleInspector-populated", false);
InspectorUI.sidebar.show();
InspectorUI.sidebar.activatePanel("computedview");
}
function testShrink() {
Services.obs.removeObserver(testShrink, "StyleInspector-populated", false);
computedView = InspectorUI.sidebar._toolContext("computedview").view;
is(computedWidth(), "500px", "Should show 500px initially.");
Services.obs.addObserver(function onShrink() {
Services.obs.removeObserver(onShrink, "StyleInspector-populated");
is(computedWidth(), "100px", "div should be 100px after shrinking.");
testGrow();
}, "StyleInspector-populated", false);
instance.setSize(100, 100);
}
function testGrow() {
Services.obs.addObserver(function onGrow() {
Services.obs.removeObserver(onGrow, "StyleInspector-populated");
is(computedWidth(), "500px", "Should be 500px after growing.");
finishUp();
}, "StyleInspector-populated", false);
instance.setSize(500, 500);
}
function finishUp() {
document.getElementById("Tools:ResponsiveUI").doCommand();
// Menus are correctly updated?
is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "false", "menu unchecked");
InspectorUI.closeInspectorUI();
gBrowser.removeCurrentTab();
finish();
}
}

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

@ -0,0 +1,103 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
let instance;
let ruleView;
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
waitForFocus(startTest, content);
}, true);
content.location = "data:text/html,<html><style>" +
"div {" +
" width: 500px;" +
" height: 10px;" +
" background: purple;" +
"} " +
"@media screen and (max-width: 200px) {" +
" div { " +
" width: 100px;" +
" }" +
"};" +
"</style><div></div></html>"
function numberOfRules() {
return ruleView.element.querySelectorAll(".ruleview-code").length;
}
function startTest() {
document.getElementById("Tools:ResponsiveUI").doCommand();
executeSoon(onUIOpen);
}
function onUIOpen() {
instance = gBrowser.selectedTab.responsiveUI;
ok(instance, "instance of the module is attached to the tab.");
instance.stack.setAttribute("notransition", "true");
registerCleanupFunction(function() {
instance.stack.removeAttribute("notransition");
});
instance.setSize(500, 500);
Services.obs.addObserver(onInspectorUIOpen,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.openInspectorUI();
}
function onInspectorUIOpen() {
Services.obs.removeObserver(onInspectorUIOpen,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
let div = content.document.getElementsByTagName("div")[0];
InspectorUI.inspectNode(div);
InspectorUI.stopInspecting();
InspectorUI.currentInspector.once("sidebaractivated-ruleview", testShrink);
InspectorUI.sidebar.show();
InspectorUI.sidebar.activatePanel("ruleview");
}
function testShrink() {
ruleView = InspectorUI.sidebar._toolContext("ruleview").view;
is(numberOfRules(), 2, "Should have two rules initially.");
ruleView.element.addEventListener("CssRuleViewRefreshed", function refresh() {
ruleView.element.removeEventListener("CssRuleViewRefreshed", refresh, false);
is(numberOfRules(), 3, "Should have three rules after shrinking.");
testGrow();
}, false);
instance.setSize(100, 100);
}
function testGrow() {
ruleView.element.addEventListener("CssRuleViewRefreshed", function refresh() {
ruleView.element.removeEventListener("CssRuleViewRefreshed", refresh, false);
is(numberOfRules(), 2, "Should have two rules after growing.");
finishUp();
}, false);
instance.setSize(500, 500);
}
function finishUp() {
document.getElementById("Tools:ResponsiveUI").doCommand();
// Menus are correctly updated?
is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "false", "menu unchecked");
InspectorUI.closeInspectorUI();
gBrowser.removeCurrentTab();
finish();
}
}

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

@ -31,6 +31,8 @@ function test() {
instance = gBrowser.selectedTab.responsiveUI;
ok(instance, "instance of the module is attached to the tab.");
instance.transitionsEnabled = false;
testPresets();
}
@ -41,13 +43,12 @@ function test() {
return;
}
instance.menulist.selectedIndex = c;
window.setTimeout(function() {
let item = instance.menulist.firstChild.childNodes[c];
let [width, height] = extractSizeFromString(item.getAttribute("label"));
is(content.innerWidth, width, "preset " + c + ": dimension valid (width)");
is(content.innerHeight, height, "preset " + c + ": dimension valid (height)");
testOnePreset(c - 1);
}, 500);
let item = instance.menulist.firstChild.childNodes[c];
let [width, height] = extractSizeFromString(item.getAttribute("label"));
is(content.innerWidth, width, "preset " + c + ": dimension valid (width)");
is(content.innerHeight, height, "preset " + c + ": dimension valid (height)");
testOnePreset(c - 1);
}
testOnePreset(instance.menulist.firstChild.childNodes.length - 1);
}
@ -71,19 +72,17 @@ function test() {
EventUtils.synthesizeMouse(instance.resizer, x, y, {type: "mousemove"}, window);
EventUtils.synthesizeMouse(instance.resizer, x, y, {type: "mouseup"}, window);
window.setTimeout(function() {
let expectedWidth = initialWidth + 20;
let expectedHeight = initialHeight + 10;
info("initial width: " + initialWidth);
info("initial height: " + initialHeight);
is(content.innerWidth, expectedWidth, "Size correcty updated (width).");
is(content.innerHeight, expectedHeight, "Size correcty updated (height).");
is(instance.menulist.selectedIndex, 0, "Custom menuitem selected");
let [width, height] = extractSizeFromString(instance.menulist.firstChild.firstChild.getAttribute("label"));
is(width, expectedWidth, "Label updated (width).");
is(height, expectedHeight, "Label updated (height).");
rotate();
}, 500);
let expectedWidth = initialWidth + 20;
let expectedHeight = initialHeight + 10;
info("initial width: " + initialWidth);
info("initial height: " + initialHeight);
is(content.innerWidth, expectedWidth, "Size correcty updated (width).");
is(content.innerHeight, expectedHeight, "Size correcty updated (height).");
is(instance.menulist.selectedIndex, 0, "Custom menuitem selected");
let [width, height] = extractSizeFromString(instance.menulist.firstChild.firstChild.getAttribute("label"));
is(width, expectedWidth, "Label updated (width).");
is(height, expectedHeight, "Label updated (height).");
rotate();
}
function rotate() {
@ -93,16 +92,14 @@ function test() {
info("rotate");
instance.rotate();
window.setTimeout(function() {
is(content.innerWidth, initialHeight, "The width is now the height.");
is(content.innerHeight, initialWidth, "The height is now the width.");
let [width, height] = extractSizeFromString(instance.menulist.firstChild.firstChild.getAttribute("label"));
is(width, initialHeight, "Label updated (width).");
is(height, initialWidth, "Label updated (height).");
is(content.innerWidth, initialHeight, "The width is now the height.");
is(content.innerHeight, initialWidth, "The height is now the width.");
let [width, height] = extractSizeFromString(instance.menulist.firstChild.firstChild.getAttribute("label"));
is(width, initialHeight, "Label updated (width).");
is(height, initialWidth, "Label updated (height).");
EventUtils.synthesizeKey("VK_ESCAPE", {});
executeSoon(finishUp);
}, 500);
EventUtils.synthesizeKey("VK_ESCAPE", {});
executeSoon(finishUp);
}
function finishUp() {

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

@ -880,6 +880,13 @@ CssRuleView.prototype = {
// The element that we're inspecting.
_viewedElement: null,
/**
* Returns true if the rule view currently has an input editor visible.
*/
get isEditing() {
return this.element.querySelectorAll(".styleinspector-propertyeditor").length > 0;
},
destroy: function CssRuleView_destroy()
{
this.clear();
@ -946,11 +953,21 @@ CssRuleView.prototype = {
*/
nodeChanged: function CssRuleView_nodeChanged()
{
// Ignore refreshes during editing.
if (this.isEditing) {
return;
}
// Repopulate the element style.
this._elementStyle.populate();
// Refresh the rule editors.
this._createEditors();
// Notify anyone that cares that we refreshed.
var evt = this.doc.createEvent("Events");
evt.initEvent("CssRuleViewRefreshed", true, false);
this.element.dispatchEvent(evt);
},
/**

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

@ -4,6 +4,7 @@
* 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/. */
const Cc = Components.classes;
const Cu = Components.utils;
const Ci = Components.interfaces;
@ -116,6 +117,7 @@ function RuleViewTool(aInspector, aFrame)
this._onChange = this.onChange.bind(this);
this.inspector.on("change", this._onChange);
this.inspector.on("sidebaractivated-ruleview", this._onChange);
this.onSelect();
}
@ -134,11 +136,11 @@ RuleViewTool.prototype = {
},
onChange: function RVT_onChange(aEvent, aFrom) {
if (aFrom == "ruleview") {
if (aFrom == "ruleview" || aFrom == "createpanel") {
return;
}
if (this.inspector.locked) {
if (this.inspector.locked && this.inspector.isPanelVisible("ruleview")) {
this.view.nodeChanged();
}
},
@ -146,6 +148,7 @@ RuleViewTool.prototype = {
destroy: function RVT_destroy() {
this.inspector.removeListener("select", this._onSelect);
this.inspector.removeListener("change", this._onChange);
this.inspector.removeListener("sidebaractivated-ruleview", this._onChange);
this.view.element.removeEventListener("CssRuleViewChanged",
this._changeHandler);
this.view.element.removeEventListener("CssRuleViewCSSLinkClicked",
@ -172,14 +175,13 @@ function ComputedViewTool(aInspector, aFrame)
this._onSelect = this.onSelect.bind(this);
this.inspector.on("select", this._onSelect);
this._onChange = this.onChange.bind(this);
this.inspector.on("change", this._onChange);
// Since refreshes of the computed view are non-destructive,
// refresh when the tab is changed so we can notice script-driven
// changes.
this.inspector.on("sidebaractivated", this._onChange);
this.inspector.on("sidebaractivated-computedview", this._onChange);
this.cssLogic.highlight(null);
this.view.highlight(null);
@ -199,19 +201,22 @@ ComputedViewTool.prototype = {
onChange: function CVT_change(aEvent, aFrom)
{
if (aFrom == "computedview" ||
aFrom == "createpanel" ||
this.inspector.selection != this.cssLogic.viewedElement) {
return;
}
this.cssLogic.highlight(this.inspector.selection);
this.view.refreshPanel();
if (this.inspector.locked && this.inspector.isPanelVisible("computedview")) {
this.cssLogic.highlight(this.inspector.selection);
this.view.refreshPanel();
}
},
destroy: function CVT_destroy(aContext)
{
this.inspector.removeListener("select", this._onSelect);
this.inspector.removeListener("change", this._onChange);
this.inspector.removeListener("sidebaractivated", this._onChange);
this.inspector.removeListener("sidebaractivated-computedview", this._onChange);
this.view.destroy();
delete this.view;

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

@ -499,9 +499,8 @@ let Manager = {
Manager = ConsoleAPIObserver = JSTerm = ConsoleListener = NetworkMonitor =
NetworkResponseListener = ConsoleProgressListener = null;
Cc = Ci = Cu = XPCOMUtils = Services = gConsoleStorage =
WebConsoleUtils = l10n = JSPropertyProvider = NetworkHelper =
NetUtil = activityDistributor = null;
XPCOMUtils = gConsoleStorage = WebConsoleUtils = l10n = JSPropertyProvider =
NetworkHelper = NetUtil = activityDistributor = null;
},
};
@ -1503,7 +1502,7 @@ NetworkResponseListener.prototype = {
*/
_findOpenResponse: function NRL__findOpenResponse()
{
if (this._foundOpenResponse) {
if (!_alive || this._foundOpenResponse) {
return;
}
@ -1611,7 +1610,9 @@ NetworkResponseListener.prototype = {
this.receivedData = "";
NetworkMonitor.sendActivity(this.httpActivity);
if (_alive) {
NetworkMonitor.sendActivity(this.httpActivity);
}
this.httpActivity.channel = null;
this.httpActivity = null;
@ -1745,7 +1746,7 @@ let NetworkMonitor = {
// NetworkResponseListener is responsible with updating the httpActivity
// object with the data from the new object in openResponses.
if (aTopic != "http-on-examine-response" ||
if (!_alive || aTopic != "http-on-examine-response" ||
!(aSubject instanceof Ci.nsIHttpChannel)) {
return;
}

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

@ -173,6 +173,14 @@ const GROUP_INDENT = 12;
// The pref prefix for webconsole filters
const PREFS_PREFIX = "devtools.webconsole.filter.";
// The number of messages to display in a single display update. If we display
// too many messages at once we slow the Firefox UI too much.
const MESSAGES_IN_INTERVAL = 30;
// The delay between display updates - tells how often we should push new
// messages to screen.
const OUTPUT_INTERVAL = 90; // milliseconds
///////////////////////////////////////////////////////////////////////////
//// Helper for creating the network panel.
@ -212,58 +220,21 @@ function createElement(aDocument, aTag, aAttributes)
* @param integer aCategory
* The category of message nodes to limit.
* @return number
* The current user-selected log limit.
* The number of removed nodes.
*/
function pruneConsoleOutputIfNecessary(aHUDId, aCategory)
{
// Get the log limit, either from the pref or from the constant.
let logLimit;
try {
let prefName = CATEGORY_CLASS_FRAGMENTS[aCategory];
logLimit = Services.prefs.getIntPref("devtools.hud.loglimit." + prefName);
} catch (e) {
logLimit = DEFAULT_LOG_LIMIT;
}
let hudRef = HUDService.getHudReferenceById(aHUDId);
let outputNode = hudRef.outputNode;
let logLimit = hudRef.logLimitForCategory(aCategory);
let scrollBox = outputNode.scrollBoxObject.element;
let oldScrollHeight = scrollBox.scrollHeight;
let scrolledToBottom = ConsoleUtils.isOutputScrolledToBottom(outputNode);
// Prune the nodes.
let messageNodes = outputNode.querySelectorAll(".webconsole-msg-" +
let messageNodes = outputNode.getElementsByClassName("webconsole-msg-" +
CATEGORY_CLASS_FRAGMENTS[aCategory]);
let removeNodes = messageNodes.length - logLimit;
for (let i = 0; i < removeNodes; i++) {
let node = messageNodes[i];
if (node._evalCacheId && !node._panelOpen) {
hudRef.jsterm.clearObjectCache(node._evalCacheId);
}
let n = Math.max(0, messageNodes.length - logLimit);
let toRemove = Array.prototype.slice.call(messageNodes, 0, n);
toRemove.forEach(hudRef.removeOutputMessage, hudRef);
if (node.classList.contains("webconsole-msg-cssparser")) {
let desc = messageNodes[i].childNodes[2].textContent;
let location = "";
if (node.childNodes[4]) {
location = node.childNodes[4].getAttribute("title");
}
delete hudRef.cssNodes[desc + location];
}
else if (node.classList.contains("webconsole-msg-inspector")) {
hudRef.pruneConsoleDirNode(node);
continue;
}
node.parentNode.removeChild(node);
}
if (!scrolledToBottom && removeNodes > 0 &&
oldScrollHeight != scrollBox.scrollHeight) {
scrollBox.scrollTop -= oldScrollHeight - scrollBox.scrollHeight;
}
return logLimit;
return n;
}
///////////////////////////////////////////////////////////////////////////
@ -470,11 +441,10 @@ HUD_SERVICE.prototype =
{
// Go through the nodes and adjust the placement of "webconsole-new-group"
// classes.
let nodes = aOutputNode.querySelectorAll(".hud-msg-node" +
":not(.hud-filtered-by-string):not(.hud-filtered-by-type)");
let lastTimestamp;
for (let i = 0; i < nodes.length; i++) {
for (let i = 0, n = nodes.length; i < n; i++) {
let thisTimestamp = nodes[i].timestamp;
if (lastTimestamp != null &&
thisTimestamp >= lastTimestamp + NEW_GROUP_DELAY) {
@ -565,9 +535,9 @@ HUD_SERVICE.prototype =
{
let outputNode = this.getHudReferenceById(aHUDId).outputNode;
let nodes = outputNode.querySelectorAll(".hud-msg-node");
let nodes = outputNode.getElementsByClassName("hud-msg-node");
for (let i = 0; i < nodes.length; ++i) {
for (let i = 0, n = nodes.length; i < n; ++i) {
let node = nodes[i];
// hide nodes that match the strings
@ -1054,6 +1024,9 @@ function HeadsUpDisplay(aTab)
// create a panel dynamically and attach to the parentNode
this.createHUD();
this._outputQueue = [];
this._pruneCategoriesQueue = {};
// create the JSTerm input element
this.jsterm = new JSTerm(this);
this.jsterm.inputNode.focus();
@ -1065,6 +1038,40 @@ function HeadsUpDisplay(aTab)
}
HeadsUpDisplay.prototype = {
/**
* Last time when we displayed any message in the output. Timestamp in
* milliseconds since the Unix epoch.
*
* @private
* @type number
*/
_lastOutputFlush: 0,
/**
* The number of messages displayed in the last interval. The interval is
* given by OUTPUT_INTERVAL.
*
* @private
* @type number
*/
_messagesDisplayedInInterval: 0,
/**
* Message nodes are stored here in a queue for later display.
*
* @private
* @type array
*/
_outputQueue: null,
/**
* Keep track of the categories we need to prune from time to time.
*
* @private
* @type array
*/
_pruneCategoriesQueue: null,
/**
* Message names that the HUD listens for. These messages come from the remote
* Web Console content script.
@ -1394,10 +1401,6 @@ HeadsUpDisplay.prototype = {
return;
}
// Turn off scrolling for the moment.
ConsoleUtils.scroll = false;
this.outputNode.hidden = true;
aRemoteMessages.forEach(function(aMessage) {
switch (aMessage._type) {
case "PageError":
@ -1408,17 +1411,6 @@ HeadsUpDisplay.prototype = {
break;
}
}, this);
this.outputNode.hidden = false;
ConsoleUtils.scroll = true;
// Scroll to bottom.
let numChildren = this.outputNode.childNodes.length;
if (numChildren && this.outputNode.clientHeight) {
// We also check the clientHeight to force a reflow, otherwise
// ensureIndexIsVisible() does not work after outputNode.hidden = false.
this.outputNode.ensureIndexIsVisible(numChildren - 1);
}
},
/**
@ -2485,12 +2477,273 @@ HeadsUpDisplay.prototype = {
}, false);
},
/**
* Output a message node. This filters a node appropriately, then sends it to
* the output, regrouping and pruning output as necessary.
*
* Note: this call is async - the given message node may not be displayed when
* you call this method.
*
* @param nsIDOMNode aNode
* The message node to send to the output.
* @param nsIDOMNode [aNodeAfter]
* Insert the node after the given aNodeAfter (optional).
*/
outputMessageNode: function HUD_outputMessageNode(aNode, aNodeAfter)
{
this._outputQueue.push([aNode, aNodeAfter]);
this._flushMessageQueue();
},
/**
* Try to flush the output message queue. This takes the messages in the
* output queue and displays them. Outputting stops at MESSAGES_IN_INTERVAL.
* Further output is queued to happen later - see OUTPUT_INTERVAL.
*
* @private
*/
_flushMessageQueue: function HUD__flushMessageQueue()
{
if ((Date.now() - this._lastOutputFlush) >= OUTPUT_INTERVAL) {
this._messagesDisplayedInInterval = 0;
}
// Determine how many messages we can display now.
let toDisplay = Math.min(this._outputQueue.length,
MESSAGES_IN_INTERVAL -
this._messagesDisplayedInInterval);
if (!toDisplay) {
if (!this._outputTimeout && this._outputQueue.length > 0) {
this._outputTimeout =
this.chromeWindow.setTimeout(function() {
delete this._outputTimeout;
this._flushMessageQueue();
}.bind(this), OUTPUT_INTERVAL);
}
return;
}
// Try to prune the message queue.
let shouldPrune = false;
if (this._outputQueue.length > toDisplay && this._pruneOutputQueue()) {
toDisplay = Math.min(this._outputQueue.length, toDisplay);
shouldPrune = true;
}
let batch = this._outputQueue.splice(0, toDisplay);
if (!batch.length) {
return;
}
let outputNode = this.outputNode;
let lastVisibleNode = null;
let scrolledToBottom = ConsoleUtils.isOutputScrolledToBottom(outputNode);
let scrollBox = outputNode.scrollBoxObject.element;
let hudIdSupportsString = WebConsoleUtils.supportsString(this.hudId);
// Output the current batch of messages.
for (let item of batch) {
if (this._outputMessageFromQueue(hudIdSupportsString, item)) {
lastVisibleNode = item[0];
}
}
// Keep track of how many messages we displayed, so we do not display too
// many at once.
this._messagesDisplayedInInterval += batch.length;
let oldScrollHeight = 0;
// Prune messages if needed. We do not do this for every flush call to
// improve performance.
let removedNodes = 0;
if (shouldPrune || !(this._outputQueue.length % 20)) {
oldScrollHeight = scrollBox.scrollHeight;
let categories = Object.keys(this._pruneCategoriesQueue);
categories.forEach(function _pruneOutput(aCategory) {
removedNodes += pruneConsoleOutputIfNecessary(this.hudId, aCategory);
}, this);
this._pruneCategoriesQueue = {};
}
// Regroup messages at the end of the queue.
if (!this._outputQueue.length) {
HUDService.regroupOutput(outputNode);
}
let isInputOutput = lastVisibleNode &&
(lastVisibleNode.classList.contains("webconsole-msg-input") ||
lastVisibleNode.classList.contains("webconsole-msg-output"));
// Scroll to the new node if it is not filtered, and if the output node is
// scrolled at the bottom or if the new node is a jsterm input/output
// message.
if (lastVisibleNode && (scrolledToBottom || isInputOutput)) {
ConsoleUtils.scrollToVisible(lastVisibleNode);
}
else if (!scrolledToBottom && removedNodes > 0 &&
oldScrollHeight != scrollBox.scrollHeight) {
// If there were pruned messages and if scroll is not at the bottom, then
// we need to adjust the scroll location.
scrollBox.scrollTop -= oldScrollHeight - scrollBox.scrollHeight;
}
// If the queue is not empty, schedule another flush.
if (!this._outputTimeout && this._outputQueue.length > 0) {
this._outputTimeout =
this.chromeWindow.setTimeout(function() {
delete this._outputTimeout;
this._flushMessageQueue();
}.bind(this), OUTPUT_INTERVAL);
}
this._lastOutputFlush = Date.now();
},
/**
* Output a message from the queue.
*
* @private
* @param nsISupportsString aHudIdSupportsString
* The HUD ID as an nsISupportsString.
* @param array aItem
* An item from the output queue - this item represents a message.
* @return boolean
* True if the message is visible, false otherwise.
*/
_outputMessageFromQueue:
function HUD__outputMessageFromQueue(aHudIdSupportsString, aItem)
{
let [node, afterNode] = aItem;
let isFiltered = ConsoleUtils.filterMessageNode(node, this.hudId);
let isRepeated = false;
if (node.classList.contains("webconsole-msg-cssparser")) {
isRepeated = ConsoleUtils.filterRepeatedCSS(node, this.outputNode,
this.hudId);
}
if (!isRepeated &&
(node.classList.contains("webconsole-msg-console") ||
node.classList.contains("webconsole-msg-exception") ||
node.classList.contains("webconsole-msg-error"))) {
isRepeated = ConsoleUtils.filterRepeatedConsole(node, this.outputNode);
}
if (!isRepeated) {
this.outputNode.insertBefore(node,
afterNode ? afterNode.nextSibling : null);
this._pruneCategoriesQueue[node.category] = true;
}
let nodeID = node.getAttribute("id");
Services.obs.notifyObservers(aHudIdSupportsString,
"web-console-message-created", nodeID);
return !isRepeated && !isFiltered;
},
/**
* Prune the queue of messages to display. This avoids displaying messages
* that will be removed at the end of the queue anyway.
* @private
*/
_pruneOutputQueue: function HUD__pruneOutputQueue()
{
let nodes = {};
// Group the messages per category.
this._outputQueue.forEach(function(aItem, aIndex) {
let [node] = aItem;
let category = node.category;
if (!(category in nodes)) {
nodes[category] = [];
}
nodes[category].push(aIndex);
}, this);
let pruned = 0;
// Loop through the categories we found and prune if needed.
for (let category in nodes) {
let limit = this.logLimitForCategory(category);
let indexes = nodes[category];
if (indexes.length > limit) {
let n = Math.max(0, indexes.length - limit);
pruned += n;
for (let i = n - 1; i >= 0; i--) {
let node = this._outputQueue[indexes[i]][0];
this._outputQueue.splice(indexes[i], 1);
}
}
}
return pruned;
},
/**
* Retrieve the limit of messages for a specific category.
*
* @param number aCategory
* The category of messages you want to retrieve the limit for. See the
* CATEGORY_* constants.
* @return number
* The number of messages allowed for the specific category.
*/
logLimitForCategory: function HUD_logLimitForCategory(aCategory)
{
let logLimit = DEFAULT_LOG_LIMIT;
try {
let prefName = CATEGORY_CLASS_FRAGMENTS[aCategory];
logLimit = Services.prefs.getIntPref("devtools.hud.loglimit." + prefName);
logLimit = Math.max(logLimit, 1);
}
catch (e) { }
return logLimit;
},
/**
* Remove a given message from the output.
*
* @param nsIDOMNode aNode
* The message node you want to remove.
*/
removeOutputMessage: function HUD_removeOutputMessage(aNode)
{
if (aNode._evalCacheId && !aNode._panelOpen) {
this.jsterm.clearObjectCache(aNode._evalCacheId);
}
if (aNode.classList.contains("webconsole-msg-cssparser")) {
let desc = aNode.childNodes[2].textContent;
let location = "";
if (aNode.childNodes[4]) {
location = aNode.childNodes[4].getAttribute("title");
}
delete this.cssNodes[desc + location];
}
else if (aNode.classList.contains("webconsole-msg-inspector")) {
this.pruneConsoleDirNode(aNode);
return;
}
aNode.parentNode.removeChild(aNode);
},
/**
* Destroy the HUD object. Call this method to avoid memory leaks when the Web
* Console is closed.
*/
destroy: function HUD_destroy()
{
this._outputQueue = [];
this.sendMessageToContent("WebConsole:Destroy", {});
this._messageListeners.forEach(function(aName) {
@ -2533,6 +2786,8 @@ HeadsUpDisplay.prototype = {
delete this.messageManager;
delete this.browser;
delete this.chromeDocument;
delete this.chromeWindow;
delete this.outputNode;
this.positionMenuitems.above.removeEventListener("command",
this._positionConsoleAbove, false);
@ -2592,7 +2847,7 @@ function JSTerm(aHud)
this.hudId = this.hud.hudId;
this.lastCompletion = {};
this.lastCompletion = { value: null };
this.history = [];
this.historyIndex = 0;
this.historyPlaceHolder = 0; // this.history.length;
@ -2890,17 +3145,7 @@ JSTerm.prototype = {
let outputNode = hud.outputNode;
let node;
while ((node = outputNode.firstChild)) {
if (node._evalCacheId && !node._panelOpen) {
this.clearObjectCache(node._evalCacheId);
}
if (node.classList &&
node.classList.contains("webconsole-msg-inspector")) {
hud.pruneConsoleDirNode(node);
}
else {
outputNode.removeChild(node);
}
hud.removeOutputMessage(node);
}
hud.HUDBox.lastTimestamp = 0;
@ -3244,7 +3489,11 @@ JSTerm.prototype = {
input: this.inputNode.value,
};
this.lastCompletion = {requestId: message.id, completionType: aType};
this.lastCompletion = {
requestId: message.id,
completionType: aType,
value: null,
};
let callback = this._receiveAutocompleteProperties.bind(this, aCallback);
this.hud.sendMessageToContent("JSTerm:Autocomplete", message, callback);
},
@ -3336,7 +3585,7 @@ JSTerm.prototype = {
clearCompletion: function JSTF_clearCompletion()
{
this.autocompletePopup.clearItems();
this.lastCompletion = {};
this.lastCompletion = { value: null };
this.updateCompleteNode("");
if (this.autocompletePopup.isOpen) {
this.autocompletePopup.hidePopup();
@ -3389,7 +3638,10 @@ JSTerm.prototype = {
*/
clearObjectCache: function JST_clearObjectCache(aCacheId)
{
this.hud.sendMessageToContent("JSTerm:ClearObjectCache", {cacheId: aCacheId});
if (this.hud) {
this.hud.sendMessageToContent("JSTerm:ClearObjectCache",
{ cacheId: aCacheId });
}
},
/**
@ -3887,13 +4139,18 @@ ConsoleUtils = {
* The newly-created message node.
* @param string aHUDId
* The ID of the HUD which this node is to be inserted into.
* @return boolean
* True if the message was filtered or false otherwise.
*/
filterMessageNode: function ConsoleUtils_filterMessageNode(aNode, aHUDId) {
let isFiltered = false;
// Filter by the message type.
let prefKey = MESSAGE_PREFERENCE_KEYS[aNode.category][aNode.severity];
if (prefKey && !HUDService.getFilterState(aHUDId, prefKey)) {
// The node is filtered by type.
aNode.classList.add("hud-filtered-by-type");
isFiltered = true;
}
// Filter on the search string.
@ -3903,7 +4160,10 @@ ConsoleUtils = {
// if string matches the filter text
if (!HUDService.stringMatchesFilters(text, search)) {
aNode.classList.add("hud-filtered-by-string");
isFiltered = true;
}
return isFiltered;
},
/**
@ -4010,50 +4270,8 @@ ConsoleUtils = {
*/
outputMessageNode:
function ConsoleUtils_outputMessageNode(aNode, aHUDId, aNodeAfter) {
ConsoleUtils.filterMessageNode(aNode, aHUDId);
let outputNode = HUDService.hudReferences[aHUDId].outputNode;
let scrolledToBottom = ConsoleUtils.isOutputScrolledToBottom(outputNode);
let isRepeated = false;
if (aNode.classList.contains("webconsole-msg-cssparser")) {
isRepeated = this.filterRepeatedCSS(aNode, outputNode, aHUDId);
}
if (!isRepeated &&
(aNode.classList.contains("webconsole-msg-console") ||
aNode.classList.contains("webconsole-msg-exception") ||
aNode.classList.contains("webconsole-msg-error"))) {
isRepeated = this.filterRepeatedConsole(aNode, outputNode);
}
if (!isRepeated) {
outputNode.insertBefore(aNode, aNodeAfter ? aNodeAfter.nextSibling : null);
}
HUDService.regroupOutput(outputNode);
if (pruneConsoleOutputIfNecessary(aHUDId, aNode.category) == 0) {
// We can't very well scroll to make the message node visible if the log
// limit is zero and the node was destroyed in the first place.
return;
}
let isInputOutput = aNode.classList.contains("webconsole-msg-input") ||
aNode.classList.contains("webconsole-msg-output");
let isFiltered = aNode.classList.contains("hud-filtered-by-string") ||
aNode.classList.contains("hud-filtered-by-type");
// Scroll to the new node if it is not filtered, and if the output node is
// scrolled at the bottom or if the new node is a jsterm input/output
// message.
if (!isFiltered && !isRepeated && (scrolledToBottom || isInputOutput)) {
ConsoleUtils.scrollToVisible(aNode);
}
let id = WebConsoleUtils.supportsString(aHUDId);
let nodeID = aNode.getAttribute("id");
Services.obs.notifyObservers(id, "web-console-message-created", nodeID);
let hud = HUDService.getHudReferenceById(aHUDId);
hud.outputMessageNode(aNode, aNodeAfter);
},
/**
@ -4083,6 +4301,10 @@ ConsoleUtils = {
HeadsUpDisplayUICommands = {
refreshCommand: function UIC_refreshCommand() {
var window = HUDService.currentContext();
if (!window) {
return;
}
let command = window.document.getElementById("Tools:WebConsole");
if (this.getOpenHUD() != null) {
command.setAttribute("checked", true);

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

@ -888,6 +888,9 @@ function JSPropertyProvider(aScope, aInputValue)
matchProp = properties.pop().trimLeft();
for (let i = 0; i < properties.length; i++) {
let prop = properties[i].trim();
if (!prop) {
return null;
}
// If obj is undefined or null, then there is no chance to run completion
// on it. Exit here.

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

@ -14,25 +14,32 @@ function test() {
}
function onLoad(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true);
openConsole();
browser.addEventListener("load", testBasicNetLogging, true);
content.location = TEST_NETWORK_URI;
}
function testBasicNetLogging(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true);
outputNode = HUDService.getHudByWindow(content).outputNode;
executeSoon(function() {
findLogEntry("test-network.html");
findLogEntry("testscript.js");
findLogEntry("test-image.png");
findLogEntry("network console");
finishTest();
browser.removeEventListener(aEvent.type, onLoad, true);
openConsole(null, function() {
browser.addEventListener("load", testBasicNetLogging, true);
content.location = TEST_NETWORK_URI;
});
}
function testBasicNetLogging(aEvent) {
browser.removeEventListener(aEvent.type, testBasicNetLogging, true);
outputNode = HUDService.getHudByWindow(content).outputNode;
waitForSuccess({
name: "network console message",
validatorFn: function()
{
return outputNode.textContent.indexOf("running network console") > -1;
},
successFn: function()
{
findLogEntry("test-network.html");
findLogEntry("testscript.js");
findLogEntry("test-image.png");
finishTest();
},
failureFn: finishTest,
});
}

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

@ -18,32 +18,61 @@ function test() {
function testGroups(HUD) {
let jsterm = HUD.jsterm;
let outputNode = HUD.outputNode;
jsterm.clearOutput();
// We test for one group by testing for zero "new" groups. The
// "webconsole-new-group" class creates a divider. Thus one group is
// indicated by zero new groups, two groups are indicated by one new group,
// and so on.
let waitForSecondMessage = {
name: "second console message",
validatorFn: function()
{
return outputNode.querySelectorAll(".webconsole-msg-output").length == 2;
},
successFn: function()
{
let timestamp1 = Date.now();
if (timestamp1 - timestamp0 < 5000) {
is(outputNode.querySelectorAll(".webconsole-new-group").length, 0,
"no group dividers exist after the second console message");
}
for (let i = 0; i < outputNode.itemCount; i++) {
outputNode.getItemAtIndex(i).timestamp = 0; // a "far past" value
}
jsterm.execute("2");
waitForSuccess(waitForThirdMessage);
},
failureFn: finishTest,
};
let waitForThirdMessage = {
name: "one group divider exists after the third console message",
validatorFn: function()
{
return outputNode.querySelectorAll(".webconsole-new-group").length == 1;
},
successFn: finishTest,
failureFn: finishTest,
};
let timestamp0 = Date.now();
jsterm.execute("0");
is(outputNode.querySelectorAll(".webconsole-new-group").length, 0,
"no group dividers exist after the first console message");
jsterm.execute("1");
let timestamp1 = Date.now();
if (timestamp1 - timestamp0 < 5000) {
is(outputNode.querySelectorAll(".webconsole-new-group").length, 0,
"no group dividers exist after the second console message");
}
for (let i = 0; i < outputNode.itemCount; i++) {
outputNode.getItemAtIndex(i).timestamp = 0; // a "far past" value
}
jsterm.execute("2");
is(outputNode.querySelectorAll(".webconsole-new-group").length, 1,
"one group divider exists after the third console message");
finishTest();
waitForSuccess({
name: "no group dividers exist after the first console message",
validatorFn: function()
{
return outputNode.querySelectorAll(".webconsole-new-group").length == 0;
},
successFn: function()
{
jsterm.execute("1");
waitForSuccess(waitForSecondMessage);
},
failureFn: finishTest,
});
}

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

@ -96,21 +96,6 @@ function testGen() {
is(countMessageNodes(), 30, "there are 30 message nodes in the output " +
"when the log limit is set to 30");
prefBranch.setIntPref("console", 0);
console.log("baz");
waitForSuccess({
name: "clear output",
validatorFn: function()
{
return countMessageNodes() == 0;
},
successFn: testNext,
failureFn: finishTest,
});
yield;
prefBranch.clearUserPref("console");
hud = testDriver = prefBranch = console = outputNode = null;
finishTest();

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

@ -89,12 +89,13 @@ const TESTS = [
category: "Image",
matchString: "corrupt",
},
{ // #15
file: "test-bug-595934-workers.html",
category: "Web Worker",
matchString: "fooBarWorker",
expectError: true,
},
// TODO: disabled due to Bug 760837 - intermittent failures.
//{ // #15
// file: "test-bug-595934-workers.html",
// category: "Web Worker",
// matchString: "fooBarWorker",
// expectError: true,
//},
];
let pos = -1;

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

@ -23,18 +23,36 @@ function consoleOpened(aHud) {
hud.filterBox.value = "test message";
HUDService.updateFilterText(hud.filterBox);
browser.addEventListener("load", tabReload, true);
let waitForNetwork = {
name: "network message",
validatorFn: function()
{
return hud.outputNode.querySelector(".webconsole-msg-network");
},
successFn: testScroll,
failureFn: finishTest,
};
executeSoon(function() {
content.location.reload();
waitForSuccess({
name: "console messages displayed",
validatorFn: function()
{
return hud.outputNode.textContent.indexOf("test message 199") > -1;
},
successFn: function()
{
browser.addEventListener("load", function onReload() {
browser.removeEventListener("load", onReload, true);
waitForSuccess(waitForNetwork);
}, true);
content.location.reload();
},
failureFn: finishTest,
});
}
function tabReload(aEvent) {
browser.removeEventListener(aEvent.type, tabReload, true);
function testScroll() {
let msgNode = hud.outputNode.querySelector(".webconsole-msg-network");
ok(msgNode, "found network message");
ok(msgNode.classList.contains("hud-filtered-by-type"),
"network message is filtered by type");
ok(msgNode.classList.contains("hud-filtered-by-string"),

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

@ -22,24 +22,28 @@ let TestObserver = {
};
function tabLoad(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true);
browser.removeEventListener(aEvent.type, tabLoad, true);
openConsole();
let hudId = HUDService.getHudIdByWindow(content);
hud = HUDService.hudReferences[hudId];
Services.obs.addObserver(TestObserver, "console-api-log-event", false);
content.location.reload();
openConsole(null, function(aHud) {
hud = aHud;
Services.obs.addObserver(TestObserver, "console-api-log-event", false);
content.location.reload();
});
}
function performTest() {
isnot(hud.outputNode.textContent.indexOf("foobarBug613013"), -1,
"console.log() message found");
Services.obs.removeObserver(TestObserver, "console-api-log-event");
TestObserver = null;
finishTest();
waitForSuccess({
name: "console.log() message",
validatorFn: function()
{
return hud.outputNode.textContent.indexOf("foobarBug613013") > -1;
},
successFn: finishTest,
failureFn: finishTest,
});
}
function test() {

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

@ -17,6 +17,8 @@ function consoleOpened(hud) {
content.console.log("test message " + i);
}
let oldScrollTop = -1;
waitForSuccess({
name: "console.log messages displayed",
validatorFn: function()
@ -25,11 +27,24 @@ function consoleOpened(hud) {
},
successFn: function()
{
let oldScrollTop = boxObject.scrollTop;
oldScrollTop = boxObject.scrollTop;
ok(oldScrollTop > 0, "scroll location is not at the top");
hud.jsterm.execute("'hello world'");
waitForSuccess(waitForExecute);
},
failureFn: finishTest,
});
let waitForExecute = {
name: "jsterm output displayed",
validatorFn: function()
{
return outputNode.querySelector(".webconsole-msg-output");
},
successFn: function()
{
isnot(boxObject.scrollTop, oldScrollTop, "scroll location updated");
oldScrollTop = boxObject.scrollTop;
@ -40,7 +55,7 @@ function consoleOpened(hud) {
finishTest();
},
failureFn: finishTest,
});
};
}
function test() {

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

@ -56,14 +56,15 @@ function testWebDevLimits2() {
}
waitForSuccess({
name: "11 console.log messages displayed",
name: "10 console.log messages displayed and one pruned",
validatorFn: function()
{
return outputNode.textContent.indexOf("test message 10") > -1;
let message0 = outputNode.textContent.indexOf("test message 0");
let message10 = outputNode.textContent.indexOf("test message 10");
return message0 == -1 && message10 > -1;
},
successFn: function()
{
testLogEntry(outputNode, "test message 0", "first message is pruned", false, true);
findLogEntry("test message 1");
// Check if the sentinel entry is still there.
findLogEntry("bar is not defined");
@ -161,14 +162,28 @@ function loadImage() {
gCounter++;
return;
}
is(gCounter, 11, "loaded 11 files");
testLogEntry(outputNode, "test-image.png?_fubar=0", "first message is pruned", false, true);
findLogEntry("test-image.png?_fubar=1");
// Check if the sentinel entry is still there.
findLogEntry("testing Net limits");
Services.prefs.setIntPref("devtools.hud.loglimit.network", gOldPref);
testCssLimits();
is(gCounter, 11, "loaded 11 files");
waitForSuccess({
name: "loaded 11 files, one message pruned",
validatorFn: function()
{
let message0 = outputNode.querySelector('*[value*="test-image.png?_fubar=0"]');
let message10 = outputNode.querySelector('*[value*="test-image.png?_fubar=10"]');
return !message0 && message10;
},
successFn: function()
{
findLogEntry("test-image.png?_fubar=1");
// Check if the sentinel entry is still there.
findLogEntry("testing Net limits");
Services.prefs.setIntPref("devtools.hud.loglimit.network", gOldPref);
testCssLimits();
},
failureFn: testCssLimits,
});
}
function testCssLimits() {

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

@ -16,29 +16,38 @@ function test() {
}
function onLoad(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true);
openConsole();
hudId = HUDService.getHudIdByWindow(content);
browser.addEventListener("load", testConsoleFileLocation, true);
content.location = TEST_URI;
}
function testConsoleFileLocation(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true);
outputNode = HUDService.hudReferences[hudId].outputNode;
executeSoon(function() {
findLogEntry("test-file-location.js");
findLogEntry("message for level");
findLogEntry("test-file-location.js:5");
findLogEntry("test-file-location.js:6");
findLogEntry("test-file-location.js:7");
findLogEntry("test-file-location.js:8");
findLogEntry("test-file-location.js:9");
finishTest();
browser.removeEventListener(aEvent.type, onLoad, true);
openConsole(null, function(aHud) {
hud = aHud;
browser.addEventListener("load", testConsoleFileLocation, true);
content.location = TEST_URI;
});
}
function testConsoleFileLocation(aEvent) {
browser.removeEventListener(aEvent.type, testConsoleFileLocation, true);
outputNode = hud.outputNode;
waitForSuccess({
name: "console API messages",
validatorFn: function()
{
return outputNode.textContent.indexOf("message for level debug") > -1;
},
successFn: function()
{
findLogEntry("test-file-location.js");
findLogEntry("message for level");
findLogEntry("test-file-location.js:5");
findLogEntry("test-file-location.js:6");
findLogEntry("test-file-location.js:7");
findLogEntry("test-file-location.js:8");
findLogEntry("test-file-location.js:9");
finishTest();
},
failureFn: finishTest,
});
}

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

@ -18,18 +18,25 @@ function test() {
function consoleOpened(hud) {
outputNode = hud.outputNode;
executeSoon(function() {
findLogEntry("aTimer: timer started");
findLogEntry("ms");
// The next test makes sure that timers with the same name but in separate
// tabs, do not contain the same value.
addTab("data:text/html;charset=utf-8,<script type='text/javascript'>" +
"console.timeEnd('bTimer');</script>");
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, testTimerIndependenceInTabs);
}, true);
waitForSuccess({
name: "aTimer started",
validatorFn: function()
{
return outputNode.textContent.indexOf("aTimer: timer started") > -1;
},
successFn: function()
{
findLogEntry("ms");
// The next test makes sure that timers with the same name but in separate
// tabs, do not contain the same value.
addTab("data:text/html;charset=utf-8,<script type='text/javascript'>" +
"console.timeEnd('bTimer');</script>");
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, testTimerIndependenceInTabs);
}, true);
},
failureFn: finishTest,
});
}
@ -56,22 +63,30 @@ function testTimerIndependenceInSameTab() {
let hud = HUDService.hudReferences[hudId];
outputNode = hud.outputNode;
executeSoon(function() {
findLogEntry("bTimer: timer started");
hud.jsterm.clearOutput();
waitForSuccess({
name: "bTimer started",
validatorFn: function()
{
return outputNode.textContent.indexOf("bTimer: timer started") > -1;
},
successFn: function() {
hud.jsterm.clearOutput();
// Now the following console.timeEnd() call shouldn't display anything,
// if the timers in different pages are not related.
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
executeSoon(testTimerIndependenceInSameTabAgain);
}, true);
content.location = "data:text/html;charset=utf-8,<script type='text/javascript'>" +
"console.timeEnd('bTimer');</script>";
// Now the following console.timeEnd() call shouldn't display anything,
// if the timers in different pages are not related.
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
executeSoon(testTimerIndependenceInSameTabAgain);
}, true);
content.location = "data:text/html;charset=utf-8," +
"<script type='text/javascript'>" +
"console.timeEnd('bTimer');</script>";
},
failureFn: finishTest,
});
}
function testTimerIndependenceInSameTabAgain(hud) {
function testTimerIndependenceInSameTabAgain() {
let hudId = HUDService.getHudIdByWindow(content);
let hud = HUDService.hudReferences[hudId];
outputNode = hud.outputNode;

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

@ -170,6 +170,7 @@
@BINPATH@/components/dom_css.xpt
@BINPATH@/components/dom_devicestorage.xpt
@BINPATH@/components/dom_events.xpt
@BINPATH@/components/dom_file.xpt
@BINPATH@/components/dom_geolocation.xpt
@BINPATH@/components/dom_media.xpt
@BINPATH@/components/dom_network.xpt

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

@ -550,8 +550,10 @@ public:
* @return boolean indicating whether a BOM was detected.
*/
static bool CheckForBOM(const unsigned char* aBuffer, PRUint32 aLength,
nsACString& aCharset, bool *bigEndian = nsnull);
nsACString& aCharset, bool *bigEndian = nsnull);
static nsresult GuessCharset(const char *aData, PRUint32 aDataLen,
nsACString &aCharset);
/**
* Determine whether aContent is in some way associated with aForm. If the

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

@ -43,6 +43,19 @@ class nsDOMFileBase : public nsIDOMFile,
public:
typedef mozilla::dom::indexedDB::FileInfo FileInfo;
virtual already_AddRefed<nsIDOMBlob>
CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType) = 0;
virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
GetSubBlobs() const { return nsnull; }
NS_DECL_NSIDOMBLOB
NS_DECL_NSIDOMFILE
NS_DECL_NSIXHRSENDABLE
NS_DECL_NSIMUTABLE
protected:
nsDOMFileBase(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength)
: mIsFile(true), mImmutable(false), mContentType(aContentType),
@ -60,8 +73,8 @@ public:
mContentType.SetIsVoid(false);
}
nsDOMFileBase(const nsAString& aContentType,
PRUint64 aStart, PRUint64 aLength)
nsDOMFileBase(const nsAString& aContentType, PRUint64 aStart,
PRUint64 aLength)
: mIsFile(false), mImmutable(false), mContentType(aContentType),
mStart(aStart), mLength(aLength)
{
@ -73,36 +86,35 @@ public:
virtual ~nsDOMFileBase() {}
virtual already_AddRefed<nsIDOMBlob>
CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType) = 0;
virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
GetSubBlobs() const { return nsnull; }
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMBLOB
NS_DECL_NSIDOMFILE
NS_DECL_NSIXHRSENDABLE
NS_DECL_NSIMUTABLE
protected:
bool IsSizeUnknown()
bool IsSizeUnknown() const
{
return mLength == UINT64_MAX;
}
virtual bool IsStoredFile()
virtual bool IsStoredFile() const
{
return false;
}
virtual bool IsWholeFile()
virtual bool IsWholeFile() const
{
NS_NOTREACHED("Should only be called on dom blobs backed by files!");
return false;
}
virtual bool IsSnapshot() const
{
return false;
}
FileInfo* GetFileInfo() const
{
NS_ASSERTION(IsStoredFile(), "Should only be called on stored files!");
NS_ASSERTION(!mFileInfos.IsEmpty(), "Must have at least one file info!");
return mFileInfos.ElementAt(0);
}
bool mIsFile;
bool mImmutable;
nsString mContentType;
@ -115,13 +127,53 @@ protected:
nsTArray<nsRefPtr<FileInfo> > mFileInfos;
};
class nsDOMFileFile : public nsDOMFileBase,
class nsDOMFile : public nsDOMFileBase
{
public:
nsDOMFile(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength)
: nsDOMFileBase(aName, aContentType, aLength)
{ }
nsDOMFile(const nsAString& aContentType, PRUint64 aLength)
: nsDOMFileBase(aContentType, aLength)
{ }
nsDOMFile(const nsAString& aContentType, PRUint64 aStart, PRUint64 aLength)
: nsDOMFileBase(aContentType, aStart, aLength)
{ }
NS_DECL_ISUPPORTS
};
class nsDOMFileCC : public nsDOMFileBase
{
public:
nsDOMFileCC(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength)
: nsDOMFileBase(aName, aContentType, aLength)
{ }
nsDOMFileCC(const nsAString& aContentType, PRUint64 aLength)
: nsDOMFileBase(aContentType, aLength)
{ }
nsDOMFileCC(const nsAString& aContentType, PRUint64 aStart, PRUint64 aLength)
: nsDOMFileBase(aContentType, aStart, aLength)
{ }
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMFileCC, nsIDOMFile)
};
class nsDOMFileFile : public nsDOMFile,
public nsIJSNativeInitializer
{
public:
// Create as a file
nsDOMFileFile(nsIFile *aFile)
: nsDOMFileBase(EmptyString(), EmptyString(), UINT64_MAX),
: nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX),
mFile(aFile), mWholeFile(true), mStoredFile(false)
{
NS_ASSERTION(mFile, "must have file");
@ -130,10 +182,19 @@ public:
mFile->GetLeafName(mName);
}
// Create as a file
nsDOMFileFile(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength, nsIFile *aFile)
: nsDOMFile(aName, aContentType, aLength),
mFile(aFile), mWholeFile(true), mStoredFile(false)
{
NS_ASSERTION(mFile, "must have file");
}
// Create as a blob
nsDOMFileFile(nsIFile *aFile, const nsAString& aContentType,
nsISupports *aCacheToken)
: nsDOMFileBase(aContentType, UINT64_MAX),
: nsDOMFile(aContentType, UINT64_MAX),
mFile(aFile), mWholeFile(true), mStoredFile(false),
mCacheToken(aCacheToken)
{
@ -142,20 +203,19 @@ public:
// Create as a file with custom name
nsDOMFileFile(nsIFile *aFile, const nsAString& aName)
: nsDOMFileBase(EmptyString(), EmptyString(), UINT64_MAX),
: nsDOMFile(aName, EmptyString(), UINT64_MAX),
mFile(aFile), mWholeFile(true), mStoredFile(false)
{
NS_ASSERTION(mFile, "must have file");
// Lazily get the content type and size
mContentType.SetIsVoid(true);
mName.Assign(aName);
}
// Create as a stored file
nsDOMFileFile(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength, nsIFile* aFile,
FileInfo* aFileInfo)
: nsDOMFileBase(aName, aContentType, aLength),
: nsDOMFile(aName, aContentType, aLength),
mFile(aFile), mWholeFile(true), mStoredFile(true)
{
NS_ASSERTION(mFile, "must have file");
@ -165,7 +225,7 @@ public:
// Create as a stored blob
nsDOMFileFile(const nsAString& aContentType, PRUint64 aLength,
nsIFile* aFile, FileInfo* aFileInfo)
: nsDOMFileBase(aContentType, aLength),
: nsDOMFile(aContentType, aLength),
mFile(aFile), mWholeFile(true), mStoredFile(true)
{
NS_ASSERTION(mFile, "must have file");
@ -174,7 +234,7 @@ public:
// Create as a file to be later initialized
nsDOMFileFile()
: nsDOMFileBase(EmptyString(), EmptyString(), UINT64_MAX),
: nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX),
mWholeFile(true), mStoredFile(false)
{
// Lazily get the content type and size
@ -206,7 +266,7 @@ protected:
// Create slice
nsDOMFileFile(const nsDOMFileFile* aOther, PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType)
: nsDOMFileBase(aContentType, aOther->mStart + aStart, aLength),
: nsDOMFile(aContentType, aOther->mStart + aStart, aLength),
mFile(aOther->mFile), mWholeFile(false),
mStoredFile(aOther->mStoredFile), mCacheToken(aOther->mCacheToken)
{
@ -216,32 +276,30 @@ protected:
if (mStoredFile) {
FileInfo* fileInfo;
if (!mozilla::dom::indexedDB::IndexedDatabaseManager::IsClosed()) {
mozilla::dom::indexedDB::IndexedDatabaseManager::FileMutex().Lock();
using mozilla::dom::indexedDB::IndexedDatabaseManager;
if (IndexedDatabaseManager::IsClosed()) {
fileInfo = aOther->GetFileInfo();
}
NS_ASSERTION(!aOther->mFileInfos.IsEmpty(),
"A stored file must have at least one file info!");
fileInfo = aOther->mFileInfos.ElementAt(0);
if (!mozilla::dom::indexedDB::IndexedDatabaseManager::IsClosed()) {
mozilla::dom::indexedDB::IndexedDatabaseManager::FileMutex().Unlock();
else {
mozilla::MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
fileInfo = aOther->GetFileInfo();
}
mFileInfos.AppendElement(fileInfo);
}
}
virtual already_AddRefed<nsIDOMBlob>
CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType);
virtual bool IsStoredFile()
virtual bool IsStoredFile() const
{
return mStoredFile;
}
virtual bool IsWholeFile()
virtual bool IsWholeFile() const
{
return mWholeFile;
}
@ -252,7 +310,7 @@ protected:
nsCOMPtr<nsISupports> mCacheToken;
};
class nsDOMMemoryFile : public nsDOMFileBase
class nsDOMMemoryFile : public nsDOMFile
{
public:
// Create as file
@ -260,7 +318,7 @@ public:
PRUint64 aLength,
const nsAString& aName,
const nsAString& aContentType)
: nsDOMFileBase(aName, aContentType, aLength),
: nsDOMFile(aName, aContentType, aLength),
mDataOwner(new DataOwner(aMemoryBuffer))
{
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
@ -270,7 +328,7 @@ public:
nsDOMMemoryFile(void *aMemoryBuffer,
PRUint64 aLength,
const nsAString& aContentType)
: nsDOMFileBase(aContentType, aLength),
: nsDOMFile(aContentType, aLength),
mDataOwner(new DataOwner(aMemoryBuffer))
{
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
@ -282,7 +340,7 @@ protected:
// Create slice
nsDOMMemoryFile(const nsDOMMemoryFile* aOther, PRUint64 aStart,
PRUint64 aLength, const nsAString& aContentType)
: nsDOMFileBase(aContentType, aOther->mStart + aStart, aLength),
: nsDOMFile(aContentType, aOther->mStart + aStart, aLength),
mDataOwner(aOther->mDataOwner)
{
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");

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

@ -178,6 +178,10 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
#include "nsIDOMDocumentType.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsICharsetDetector.h"
#include "nsICharsetDetectionObserver.h"
#include "nsIPlatformCharset.h"
extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
const char** next, PRUnichar* result);
extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
@ -190,21 +194,6 @@ using namespace mozilla;
const char kLoadAsData[] = "loadAsData";
/**
* Default values for the ViewportInfo structure.
*/
static const float kViewportMinScale = 0.0;
static const float kViewportMaxScale = 10.0;
static const PRUint32 kViewportMinWidth = 200;
static const PRUint32 kViewportMaxWidth = 10000;
static const PRUint32 kViewportMinHeight = 223;
static const PRUint32 kViewportMaxHeight = 10000;
static const PRInt32 kViewportDefaultScreenWidth = 980;
static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
nsIDOMScriptObjectFactory *nsContentUtils::sDOMScriptObjectFactory = nsnull;
nsIXPConnect *nsContentUtils::sXPConnect;
nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
@ -260,6 +249,23 @@ nsIParser* nsContentUtils::sXMLFragmentParser = nsnull;
nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nsnull;
bool nsContentUtils::sFragmentParsingActive = false;
namespace {
/**
* Default values for the ViewportInfo structure.
*/
static const float kViewportMinScale = 0.0;
static const float kViewportMaxScale = 10.0;
static const PRUint32 kViewportMinWidth = 200;
static const PRUint32 kViewportMaxWidth = 10000;
static const PRUint32 kViewportMinHeight = 223;
static const PRUint32 kViewportMaxHeight = 10000;
static const PRInt32 kViewportDefaultScreenWidth = 980;
static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
static PLDHashTable sEventListenerManagersHash;
class EventListenerManagerMapEntry : public PLDHashEntryHdr
@ -301,14 +307,36 @@ EventListenerManagerHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
lm->~EventListenerManagerMapEntry();
}
class nsSameOriginChecker : public nsIChannelEventSink,
public nsIInterfaceRequestor
class SameOriginChecker : public nsIChannelEventSink,
public nsIInterfaceRequestor
{
NS_DECL_ISUPPORTS
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
};
class CharsetDetectionObserver : public nsICharsetDetectionObserver
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf)
{
mCharset = aCharset;
return NS_OK;
}
const nsACString& GetResult() const
{
return mCharset;
}
private:
nsCString mCharset;
};
} // anonymous namespace
/* static */
TimeDuration
nsContentUtils::HandlingUserInputTimeout()
@ -3536,6 +3564,89 @@ nsContentUtils::CheckForBOM(const unsigned char* aBuffer, PRUint32 aLength,
return found;
}
NS_IMPL_ISUPPORTS1(CharsetDetectionObserver,
nsICharsetDetectionObserver)
/* static */
nsresult
nsContentUtils::GuessCharset(const char *aData, PRUint32 aDataLen,
nsACString &aCharset)
{
// First try the universal charset detector
nsCOMPtr<nsICharsetDetector> detector =
do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
"universal_charset_detector");
if (!detector) {
// No universal charset detector, try the default charset detector
const nsAdoptingCString& detectorName =
Preferences::GetLocalizedCString("intl.charset.detector");
if (!detectorName.IsEmpty()) {
nsCAutoString detectorContractID;
detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
detectorContractID += detectorName;
detector = do_CreateInstance(detectorContractID.get());
}
}
nsresult rv;
// The charset detector doesn't work for empty (null) aData. Testing
// aDataLen instead of aData so that we catch potential errors.
if (detector && aDataLen) {
nsRefPtr<CharsetDetectionObserver> observer =
new CharsetDetectionObserver();
rv = detector->Init(observer);
NS_ENSURE_SUCCESS(rv, rv);
bool dummy;
rv = detector->DoIt(aData, aDataLen, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
rv = detector->Done();
NS_ENSURE_SUCCESS(rv, rv);
aCharset = observer->GetResult();
} else {
// no charset detector available, check the BOM
unsigned char sniffBuf[3];
PRUint32 numRead =
(aDataLen >= sizeof(sniffBuf) ? sizeof(sniffBuf) : aDataLen);
memcpy(sniffBuf, aData, numRead);
bool bigEndian;
if (CheckForBOM(sniffBuf, numRead, aCharset, &bigEndian) &&
aCharset.EqualsLiteral("UTF-16")) {
if (bigEndian) {
aCharset.AppendLiteral("BE");
}
else {
aCharset.AppendLiteral("LE");
}
}
}
if (aCharset.IsEmpty()) {
// no charset detected, default to the system charset
nsCOMPtr<nsIPlatformCharset> platformCharset =
do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
rv = platformCharset->GetCharset(kPlatformCharsetSel_PlainTextInFile,
aCharset);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get the system charset!");
}
}
}
if (aCharset.IsEmpty()) {
// no sniffed or default charset, assume UTF-8
aCharset.AssignLiteral("UTF-8");
}
return NS_OK;
}
/* static */
void
nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver)
@ -5517,7 +5628,7 @@ nsIInterfaceRequestor*
nsContentUtils::GetSameOriginChecker()
{
if (!sSameOriginChecker) {
sSameOriginChecker = new nsSameOriginChecker();
sSameOriginChecker = new SameOriginChecker();
NS_IF_ADDREF(sSameOriginChecker);
}
return sSameOriginChecker;
@ -5549,15 +5660,15 @@ nsContentUtils::CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel
return rv;
}
NS_IMPL_ISUPPORTS2(nsSameOriginChecker,
NS_IMPL_ISUPPORTS2(SameOriginChecker,
nsIChannelEventSink,
nsIInterfaceRequestor)
NS_IMETHODIMP
nsSameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags,
nsIAsyncVerifyRedirectCallback *cb)
SameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags,
nsIAsyncVerifyRedirectCallback *cb)
{
NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
@ -5570,7 +5681,7 @@ nsSameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
}
NS_IMETHODIMP
nsSameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
SameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
{
return QueryInterface(aIID, aResult);
}

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

@ -17,7 +17,7 @@
using namespace mozilla;
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMMultipartFile, nsDOMFileBase,
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMMultipartFile, nsDOMFile,
nsIJSNativeInitializer)
NS_IMETHODIMP
@ -139,6 +139,15 @@ nsDOMMultipartFile::CreateSlice(PRUint64 aStart, PRUint64 aLength,
return blob.forget();
}
/* static */ nsresult
nsDOMMultipartFile::NewFile(const nsAString& aName, nsISupports* *aNewObject)
{
nsCOMPtr<nsISupports> file =
do_QueryObject(new nsDOMMultipartFile(aName));
file.forget(aNewObject);
return NS_OK;
}
/* static */ nsresult
nsDOMMultipartFile::NewBlob(nsISupports* *aNewObject)
{

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

@ -12,7 +12,7 @@
using namespace mozilla;
class nsDOMMultipartFile : public nsDOMFileBase,
class nsDOMMultipartFile : public nsDOMFile,
public nsIJSNativeInitializer
{
public:
@ -20,7 +20,7 @@ public:
nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
const nsAString& aName,
const nsAString& aContentType)
: nsDOMFileBase(aName, aContentType, UINT64_MAX),
: nsDOMFile(aName, aContentType, UINT64_MAX),
mBlobs(aBlobs)
{
}
@ -28,14 +28,20 @@ public:
// Create as a blob
nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
const nsAString& aContentType)
: nsDOMFileBase(aContentType, UINT64_MAX),
: nsDOMFile(aContentType, UINT64_MAX),
mBlobs(aBlobs)
{
}
// Create as a file to be later initialized
nsDOMMultipartFile(const nsAString& aName)
: nsDOMFile(aName, EmptyString(), UINT64_MAX)
{
}
// Create as a blob to be later initialized
nsDOMMultipartFile()
: nsDOMFileBase(EmptyString(), UINT64_MAX)
: nsDOMFile(EmptyString(), UINT64_MAX)
{
}
@ -60,6 +66,9 @@ public:
NS_IMETHOD GetSize(PRUint64*);
NS_IMETHOD GetInternalStream(nsIInputStream**);
static nsresult
NewFile(const nsAString& aName, nsISupports* *aNewObject);
// DOMClassInfo constructor (for Blob([b1, "foo"], { type: "image/png" }))
static nsresult
NewBlob(nsISupports* *aNewObject);

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

@ -103,23 +103,6 @@ nsresult DataOwnerAdapter::Create(DataOwner* aDataOwner,
////////////////////////////////////////////////////////////////////////////
// nsDOMFileBase implementation
DOMCI_DATA(File, nsDOMFileBase)
DOMCI_DATA(Blob, nsDOMFileBase)
NS_INTERFACE_MAP_BEGIN(nsDOMFileBase)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
NS_INTERFACE_MAP_ENTRY(nsIMutable)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
NS_INTERFACE_MAP_END
// Threadsafe when GetMutable() == false
NS_IMPL_THREADSAFE_ADDREF(nsDOMFileBase)
NS_IMPL_THREADSAFE_RELEASE(nsDOMFileBase)
NS_IMETHODIMP
nsDOMFileBase::GetName(nsAString &aFileName)
{
@ -302,7 +285,7 @@ nsDOMFileBase::GetFileId()
{
PRInt64 id = -1;
if (IsStoredFile() && IsWholeFile()) {
if (IsStoredFile() && IsWholeFile() && !IsSnapshot()) {
if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
indexedDB::IndexedDatabaseManager::FileMutex().Lock();
}
@ -353,7 +336,14 @@ nsDOMFileBase::GetFileInfo(indexedDB::FileManager* aFileManager)
// A slice created from a stored file must keep the file info alive.
// However, we don't support sharing of slices yet, so the slice must be
// copied again. That's why we have to ignore the first file info.
PRUint32 startIndex = IsStoredFile() && !IsWholeFile() ? 1 : 0;
// Snapshots are handled in a similar way (they have to be copied).
PRUint32 startIndex;
if (IsStoredFile() && (!IsWholeFile() || IsSnapshot())) {
startIndex = 1;
}
else {
startIndex = 0;
}
MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex());
@ -419,10 +409,48 @@ nsDOMFileBase::SetMutable(bool aMutable)
return rv;
}
////////////////////////////////////////////////////////////////////////////
// nsDOMFile implementation
DOMCI_DATA(File, nsDOMFile)
DOMCI_DATA(Blob, nsDOMFile)
NS_INTERFACE_MAP_BEGIN(nsDOMFile)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
NS_INTERFACE_MAP_ENTRY(nsIMutable)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
NS_INTERFACE_MAP_END
// Threadsafe when GetMutable() == false
NS_IMPL_THREADSAFE_ADDREF(nsDOMFile)
NS_IMPL_THREADSAFE_RELEASE(nsDOMFile)
////////////////////////////////////////////////////////////////////////////
// nsDOMFileCC implementation
NS_IMPL_CYCLE_COLLECTION_0(nsDOMFileCC)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMFileCC)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
NS_INTERFACE_MAP_ENTRY(nsIMutable)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMFileCC)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMFileCC)
////////////////////////////////////////////////////////////////////////////
// nsDOMFileFile implementation
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMFileFile, nsDOMFileBase,
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMFileFile, nsDOMFile,
nsIJSNativeInitializer)
already_AddRefed<nsIDOMBlob>

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

@ -11,14 +11,12 @@
#include "nsDOMFile.h"
#include "nsDOMError.h"
#include "nsCharsetAlias.h"
#include "nsICharsetDetector.h"
#include "nsICharsetConverterManager.h"
#include "nsIConverterInputStream.h"
#include "nsIFile.h"
#include "nsIFileStreams.h"
#include "nsIInputStream.h"
#include "nsIMIMEService.h"
#include "nsIPlatformCharset.h"
#include "nsIUnicodeDecoder.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
@ -97,7 +95,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMFileReader)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(FileReader)
NS_INTERFACE_MAP_END_INHERITING(FileIOObject)
@ -112,15 +109,6 @@ nsDOMFileReader::RootResultArrayBuffer()
static_cast<nsDOMEventTargetHelper*>(this)), this);
}
//nsICharsetDetectionObserver
NS_IMETHODIMP
nsDOMFileReader::Notify(const char *aCharset, nsDetectionConfident aConf)
{
mCharset = aCharset;
return NS_OK;
}
//nsDOMFileReader constructors/initializers
nsDOMFileReader::nsDOMFileReader()
@ -457,7 +445,7 @@ nsDOMFileReader::GetAsText(const nsACString &aCharset,
if (!aCharset.IsEmpty()) {
charsetGuess = aCharset;
} else {
rv = GuessCharset(aFileData, aDataLen, charsetGuess);
rv = nsContentUtils::GuessCharset(aFileData, aDataLen, charsetGuess);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -540,80 +528,3 @@ nsDOMFileReader::ConvertStream(const char *aFileData,
return rv;
}
nsresult
nsDOMFileReader::GuessCharset(const char *aFileData,
PRUint32 aDataLen,
nsACString &aCharset)
{
// First try the universal charset detector
nsCOMPtr<nsICharsetDetector> detector
= do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
"universal_charset_detector");
if (!detector) {
// No universal charset detector, try the default charset detector
const nsAdoptingCString& detectorName =
Preferences::GetLocalizedCString("intl.charset.detector");
if (!detectorName.IsEmpty()) {
nsCAutoString detectorContractID;
detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
detectorContractID += detectorName;
detector = do_CreateInstance(detectorContractID.get());
}
}
nsresult rv;
// The charset detector doesn't work for empty (null) aFileData. Testing
// aDataLen instead of aFileData so that we catch potential errors.
if (detector && aDataLen != 0) {
mCharset.Truncate();
detector->Init(this);
bool done;
rv = detector->DoIt(aFileData, aDataLen, &done);
NS_ENSURE_SUCCESS(rv, rv);
rv = detector->Done();
NS_ENSURE_SUCCESS(rv, rv);
aCharset = mCharset;
} else {
// no charset detector available, check the BOM
unsigned char sniffBuf[3];
PRUint32 numRead = (aDataLen >= sizeof(sniffBuf) ? sizeof(sniffBuf) : aDataLen);
memcpy(sniffBuf, aFileData, numRead);
if (numRead >= 2 &&
sniffBuf[0] == 0xfe &&
sniffBuf[1] == 0xff) {
aCharset = "UTF-16BE";
} else if (numRead >= 2 &&
sniffBuf[0] == 0xff &&
sniffBuf[1] == 0xfe) {
aCharset = "UTF-16LE";
} else if (numRead >= 3 &&
sniffBuf[0] == 0xef &&
sniffBuf[1] == 0xbb &&
sniffBuf[2] == 0xbf) {
aCharset = "UTF-8";
}
}
if (aCharset.IsEmpty()) {
// no charset detected, default to the system charset
nsCOMPtr<nsIPlatformCharset> platformCharset =
do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
rv = platformCharset->GetCharset(kPlatformCharsetSel_PlainTextInFile,
aCharset);
}
}
if (aCharset.IsEmpty()) {
// no sniffed or default charset, try UTF-8
aCharset.AssignLiteral("UTF-8");
}
return NS_OK;
}

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

@ -16,8 +16,6 @@
#include "nsIJSNativeInitializer.h"
#include "prtime.h"
#include "nsITimer.h"
#include "nsICharsetDetector.h"
#include "nsICharsetDetectionObserver.h"
#include "nsIDOMFile.h"
#include "nsIDOMFileReader.h"
@ -34,8 +32,7 @@ class nsDOMFileReader : public mozilla::dom::FileIOObject,
public nsIDOMFileReader,
public nsIInterfaceRequestor,
public nsSupportsWeakReference,
public nsIJSNativeInitializer,
public nsICharsetDetectionObserver
public nsIJSNativeInitializer
{
public:
nsDOMFileReader();
@ -54,13 +51,10 @@ public:
NS_DECL_EVENT_HANDLER(loadend)
NS_DECL_EVENT_HANDLER(loadstart)
// nsIJSNativeInitializer
NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
// nsIJSNativeInitializer
NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
PRUint32 argc, jsval* argv);
// nsICharsetDetectionObserver
NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf);
// FileIOObject overrides
NS_IMETHOD DoAbort(nsAString& aEvent);
NS_IMETHOD DoOnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
@ -88,7 +82,6 @@ protected:
nsresult GetAsText(const nsACString &aCharset,
const char *aFileData, PRUint32 aDataLen, nsAString &aResult);
nsresult GetAsDataURL(nsIDOMBlob *aFile, const char *aFileData, PRUint32 aDataLen, nsAString &aResult);
nsresult GuessCharset(const char *aFileData, PRUint32 aDataLen, nsACString &aCharset);
nsresult ConvertStream(const char *aFileData, PRUint32 aDataLen, const char *aCharset, nsAString &aResult);
void FreeFileData() {

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

@ -616,7 +616,7 @@ protected:
// Non-null only when we are able to get a os-file representation of the
// response, i.e. when loading from a file, or when the http-stream
// caches into a file or is reading from a cached file.
nsRefPtr<nsDOMFileBase> mDOMFile;
nsRefPtr<nsDOMFile> mDOMFile;
// We stream data to mBuilder when response type is "blob" or "moz-blob"
// and mDOMFile is null.
nsRefPtr<nsDOMBlobBuilder> mBuilder;

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

@ -193,4 +193,50 @@ private:
xpc_TryUnmarkWrappedGrayObject(tmp->mOn##_event##Listener->GetInner()); \
}
/* Use this macro to declare functions that forward the behavior of this
* interface to another object.
* This macro doesn't forward PreHandleEvent because sometimes subclasses
* want to override it.
*/
#define NS_FORWARD_NSIDOMEVENTTARGET_NOPREHANDLEEVENT(_to) \
NS_SCRIPTABLE NS_IMETHOD AddEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture, bool wantsUntrusted, PRUint8 _argc) { \
return _to AddEventListener(type, listener, useCapture, wantsUntrusted, _argc); \
} \
NS_IMETHOD AddSystemEventListener(const nsAString & type, nsIDOMEventListener *listener, bool aUseCapture, bool aWantsUntrusted, PRUint8 _argc) { \
return _to AddSystemEventListener(type, listener, aUseCapture, aWantsUntrusted, _argc); \
} \
NS_SCRIPTABLE NS_IMETHOD RemoveEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture) { \
return _to RemoveEventListener(type, listener, useCapture); \
} \
NS_IMETHOD RemoveSystemEventListener(const nsAString & type, nsIDOMEventListener *listener, bool aUseCapture) { \
return _to RemoveSystemEventListener(type, listener, aUseCapture); \
} \
NS_SCRIPTABLE NS_IMETHOD DispatchEvent(nsIDOMEvent *evt, bool *_retval NS_OUTPARAM) { \
return _to DispatchEvent(evt, _retval); \
} \
virtual nsIDOMEventTarget * GetTargetForDOMEvent(void) { \
return _to GetTargetForDOMEvent(); \
} \
virtual nsIDOMEventTarget * GetTargetForEventTargetChain(void) { \
return _to GetTargetForEventTargetChain(); \
} \
virtual nsresult WillHandleEvent(nsEventChainPostVisitor & aVisitor) { \
return _to WillHandleEvent(aVisitor); \
} \
virtual nsresult PostHandleEvent(nsEventChainPostVisitor & aVisitor) { \
return _to PostHandleEvent(aVisitor); \
} \
virtual nsresult DispatchDOMEvent(nsEvent *aEvent, nsIDOMEvent *aDOMEvent, nsPresContext *aPresContext, nsEventStatus *aEventStatus) { \
return _to DispatchDOMEvent(aEvent, aDOMEvent, aPresContext, aEventStatus); \
} \
virtual nsEventListenerManager * GetListenerManager(bool aMayCreate) { \
return _to GetListenerManager(aMayCreate); \
} \
virtual nsIScriptContext * GetContextForEventHandlers(nsresult *aRv NS_OUTPARAM) { \
return _to GetContextForEventHandlers(aRv); \
} \
virtual JSContext * GetJSContextForEventHandlers(void) { \
return _to GetJSContextForEventHandlers(); \
}
#endif // nsDOMEventTargetHelper_h_

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

@ -1042,7 +1042,7 @@ size_t sqlite3_quota_fread(
** the write if we exceed quota.
*/
size_t sqlite3_quota_fwrite(
void *pBuf, /* Take content to write from here */
const void *pBuf, /* Take content to write from here */
size_t size, /* Size of each element */
size_t nmemb, /* Number of elements */
quota_FILE *p /* Write to this quota_FILE objecct */
@ -1052,7 +1052,7 @@ size_t sqlite3_quota_fwrite(
sqlite3_int64 szNew;
quotaFile *pFile;
size_t rc;
iOfst = ftell(p->f);
iEnd = iOfst + size*nmemb;
pFile = p->pFile;
@ -1091,7 +1091,7 @@ size_t sqlite3_quota_fwrite(
pFile->iSize = iNewEnd;
quotaLeave();
}
return rc;
return rc;
}
/*
@ -1160,6 +1160,13 @@ long sqlite3_quota_ftell(quota_FILE *p){
return ftell(p->f);
}
/*
** Test the error indicator for the given file.
*/
int sqlite3_quota_ferror(quota_FILE *p){
return ferror(p->f);
}
/*
** Truncate a file to szNew bytes.
*/
@ -1237,6 +1244,25 @@ sqlite3_int64 sqlite3_quota_file_size(quota_FILE *p){
return p->pFile ? p->pFile->iSize : -1;
}
/*
** Determine the amount of data in bytes available for reading
** in the given file.
*/
long sqlite3_quota_file_available(quota_FILE *p){
FILE* f = p->f;
long pos1, pos2;
int rc;
pos1 = ftell(f);
if ( pos1 < 0 ) return -1;
rc = fseek(f, 0, SEEK_END);
if ( rc != 0 ) return -1;
pos2 = ftell(f);
if ( pos2 < 0 ) return -1;
rc = fseek(f, pos1, SEEK_SET);
if ( rc != 0 ) return -1;
return pos2 - pos1;
}
/*
** Remove a managed file. Update quotas accordingly.
*/

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

@ -162,7 +162,7 @@ quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
** the sum of sizes of all files from going over quota.
*/
size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*);
size_t sqlite3_quota_fwrite(const void*, size_t, size_t, quota_FILE*);
/*
** Flush all written content held in memory buffers out to disk.
@ -190,6 +190,13 @@ int sqlite3_quota_fseek(quota_FILE*, long, int);
void sqlite3_quota_rewind(quota_FILE*);
long sqlite3_quota_ftell(quota_FILE*);
/*
** Test the error indicator for the given file.
**
** Return non-zero if the error indicator is set.
*/
int sqlite3_quota_ferror(quota_FILE*);
/*
** Truncate a file previously opened by sqlite3_quota_fopen(). Return
** zero on success and non-zero on any kind of failure.
@ -198,7 +205,7 @@ long sqlite3_quota_ftell(quota_FILE*);
** Any attempt to "truncate" a file to a larger size results in
** undefined behavior.
*/
int sqlite3_quota_ftrunate(quota_FILE*, sqlite3_int64 newSize);
int sqlite3_quota_ftruncate(quota_FILE*, sqlite3_int64 newSize);
/*
** Return the last modification time of the opened file, in seconds
@ -232,6 +239,14 @@ sqlite3_int64 sqlite3_quota_file_size(quota_FILE*);
*/
sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE*);
/*
** Determine the amount of data in bytes available for reading
** in the given file.
**
** Return -1 if the amount cannot be determined for some reason.
*/
long sqlite3_quota_file_available(quota_FILE*);
/*
** Delete a file from the disk, if that file is under quota management.
** Adjust quotas accordingly.

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

@ -50,6 +50,7 @@ DIRS += \
battery \
contacts \
devicestorage \
file \
media \
power \
settings \

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

@ -7,13 +7,28 @@
let Cu = Components.utils;
let Ci = Components.interfaces;
let Cc = Components.classes;
// The maximum allowed number of concurrent timers per page.
const MAX_PAGE_TIMERS = 10000;
// The regular expression used to parse %s/%d and other placeholders for
// variables in strings that need to be interpolated.
const ARGUMENT_PATTERN = /%\d*\.?\d*([osdif])\b/g;
// The maximum stacktrace depth when populating the stacktrace array used for
// console.trace().
const DEFAULT_MAX_STACKTRACE_DEPTH = 200;
// The console API methods are async and their action is executed later. This
// delay tells how much later.
const CALL_DELAY = 30; // milliseconds
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm");
let nsITimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
function ConsoleAPI() {}
ConsoleAPI.prototype = {
@ -21,11 +36,17 @@ ConsoleAPI.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
_timerInitialized: false,
_queuedCalls: null,
_timerCallback: null,
_destroyedWindows: null,
// nsIDOMGlobalPropertyInitializer
init: function CA_init(aWindow) {
Services.obs.addObserver(this, "xpcom-shutdown", false);
Services.obs.addObserver(this, "inner-window-destroyed", false);
let outerID;
let innerID;
try {
@ -39,45 +60,50 @@ ConsoleAPI.prototype = {
Cu.reportError(ex);
}
let meta = {
outerID: outerID,
innerID: innerID,
};
let self = this;
let chromeObject = {
// window.console API
log: function CA_log() {
self.notifyObservers(outerID, innerID, "log", self.processArguments(arguments));
self.queueCall("log", arguments, meta);
},
info: function CA_info() {
self.notifyObservers(outerID, innerID, "info", self.processArguments(arguments));
self.queueCall("info", arguments, meta);
},
warn: function CA_warn() {
self.notifyObservers(outerID, innerID, "warn", self.processArguments(arguments));
self.queueCall("warn", arguments, meta);
},
error: function CA_error() {
self.notifyObservers(outerID, innerID, "error", self.processArguments(arguments));
self.queueCall("error", arguments, meta);
},
debug: function CA_debug() {
self.notifyObservers(outerID, innerID, "log", self.processArguments(arguments));
self.queueCall("debug", arguments, meta);
},
trace: function CA_trace() {
self.notifyObservers(outerID, innerID, "trace", self.getStackTrace());
self.queueCall("trace", arguments, meta);
},
// Displays an interactive listing of all the properties of an object.
dir: function CA_dir() {
self.notifyObservers(outerID, innerID, "dir", arguments);
self.queueCall("dir", arguments, meta);
},
group: function CA_group() {
self.notifyObservers(outerID, innerID, "group", self.beginGroup(arguments));
self.queueCall("group", arguments, meta);
},
groupCollapsed: function CA_groupCollapsed() {
self.notifyObservers(outerID, innerID, "groupCollapsed", self.beginGroup(arguments));
self.queueCall("groupCollapsed", arguments, meta);
},
groupEnd: function CA_groupEnd() {
self.notifyObservers(outerID, innerID, "groupEnd", arguments);
self.queueCall("groupEnd", arguments, meta);
},
time: function CA_time() {
self.notifyObservers(outerID, innerID, "time", self.startTimer(innerID, arguments[0]));
self.queueCall("time", arguments, meta);
},
timeEnd: function CA_timeEnd() {
self.notifyObservers(outerID, innerID, "timeEnd", self.stopTimer(innerID, arguments[0]));
self.queueCall("timeEnd", arguments, meta);
},
__exposedProps__: {
log: "r",
@ -123,6 +149,12 @@ ConsoleAPI.prototype = {
Object.defineProperties(contentObj, properties);
Cu.makeObjectPropsNormal(contentObj);
this._queuedCalls = [];
this._destroyedWindows = [];
this._timerCallback = {
notify: this._timerCallbackNotify.bind(this),
};
return contentObj;
},
@ -131,51 +163,144 @@ ConsoleAPI.prototype = {
if (aTopic == "xpcom-shutdown") {
Services.obs.removeObserver(this, "xpcom-shutdown");
Services.obs.removeObserver(this, "inner-window-destroyed");
this._destroyedWindows = [];
this._queuedCalls = [];
}
else if (aTopic == "inner-window-destroyed") {
let innerWindowID = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
delete this.timerRegistry[innerWindowID + ""];
this._destroyedWindows.push(innerWindowID);
}
},
/**
* Queue a call to a console method. See the CALL_DELAY constant.
*
* @param string aMethod
* The console method the code has invoked.
* @param object aArguments
* The arguments passed to the console method.
* @param object aMeta
* The associated call meta information. This needs to hold the inner
* and outer window IDs from where the console method was called.
*/
queueCall: function CA_queueCall(aMethod, aArguments, aMeta)
{
let metaForCall = {
outerID: aMeta.outerID,
innerID: aMeta.innerID,
timeStamp: Date.now(),
stack: this.getStackTrace(aMethod != "trace" ? 1 : null),
};
this._queuedCalls.push([aMethod, aArguments, metaForCall]);
if (!this._timerInitialized) {
nsITimer.initWithCallback(this._timerCallback, CALL_DELAY,
Ci.nsITimer.TYPE_ONE_SHOT);
this._timerInitialized = true;
}
},
/**
* Timer callback used to process each of the queued calls.
* @private
*/
_timerCallbackNotify: function CA__timerCallbackNotify()
{
this._timerInitialized = false;
this._queuedCalls.splice(0).forEach(this._processQueuedCall, this);
this._destroyedWindows = [];
},
/**
* Process a queued call to a console method.
*
* @private
* @param array aCall
* Array that holds information about the queued call.
*/
_processQueuedCall: function CA__processQueuedItem(aCall)
{
let [method, args, meta] = aCall;
let notifyMeta = {
outerID: meta.outerID,
innerID: meta.innerID,
timeStamp: meta.timeStamp,
frame: meta.stack[0],
};
let notifyArguments = null;
switch (method) {
case "log":
case "info":
case "warn":
case "error":
case "debug":
notifyArguments = this.processArguments(args);
break;
case "trace":
notifyArguments = meta.stack;
break;
case "group":
case "groupCollapsed":
notifyArguments = this.beginGroup(args);
break;
case "groupEnd":
case "dir":
notifyArguments = args;
break;
case "time":
notifyArguments = this.startTimer(meta.innerID, args[0], meta.timeStamp);
break;
case "timeEnd":
notifyArguments = this.stopTimer(meta.innerID, args[0], meta.timeStamp);
break;
default:
// unknown console API method!
return;
}
this.notifyObservers(method, notifyArguments, notifyMeta);
},
/**
* Notify all observers of any console API call.
*
* @param number aOuterWindowID
* The outer window ID from where the message came from.
* @param number aInnerWindowID
* The inner window ID from where the message came from.
* @param string aLevel
* The message level.
* @param mixed aArguments
* The arguments given to the console API call.
**/
notifyObservers:
function CA_notifyObservers(aOuterWindowID, aInnerWindowID, aLevel, aArguments) {
if (!aOuterWindowID) {
return;
}
let stack = this.getStackTrace();
// Skip the first frame since it contains an internal call.
let frame = stack[1];
* @param object aMeta
* Object that holds metadata about the console API call:
* - outerID - the outer ID of the window where the message came from.
* - innerID - the inner ID of the window where the message came from.
* - frame - the youngest content frame in the call stack.
* - timeStamp - when the console API call occurred.
*/
notifyObservers: function CA_notifyObservers(aLevel, aArguments, aMeta) {
let consoleEvent = {
ID: aOuterWindowID,
innerID: aInnerWindowID,
ID: aMeta.outerID,
innerID: aMeta.innerID,
level: aLevel,
filename: frame.filename,
lineNumber: frame.lineNumber,
functionName: frame.functionName,
filename: aMeta.frame.filename,
lineNumber: aMeta.frame.lineNumber,
functionName: aMeta.frame.functionName,
arguments: aArguments,
timeStamp: Date.now(),
timeStamp: aMeta.timeStamp,
};
consoleEvent.wrappedJSObject = consoleEvent;
ConsoleAPIStorage.recordEvent(aInnerWindowID, consoleEvent);
// Store messages for which the inner window was not destroyed.
if (this._destroyedWindows.indexOf(aMeta.innerID) == -1) {
ConsoleAPIStorage.recordEvent(aMeta.innerID, consoleEvent);
}
Services.obs.notifyObservers(consoleEvent,
"console-api-log-event", aOuterWindowID);
Services.obs.notifyObservers(consoleEvent, "console-api-log-event",
aMeta.outerID);
},
/**
@ -189,18 +314,14 @@ ConsoleAPI.prototype = {
* The arguments given to the console API call.
**/
processArguments: function CA_processArguments(aArguments) {
if (aArguments.length < 2) {
if (aArguments.length < 2 || typeof aArguments[0] != "string") {
return aArguments;
}
let args = Array.prototype.slice.call(aArguments);
let format = args.shift();
if (typeof format != "string") {
return aArguments;
}
// Format specification regular expression.
let pattern = /%(\d*).?(\d*)[a-zA-Z]/g;
let processed = format.replace(pattern, function CA_PA_substitute(spec) {
switch (spec[spec.length-1]) {
let processed = format.replace(ARGUMENT_PATTERN, function CA_PA_substitute(match, submatch) {
switch (submatch) {
case "o":
case "s":
return String(args.shift());
@ -210,7 +331,7 @@ ConsoleAPI.prototype = {
case "f":
return parseFloat(args.shift());
default:
return spec;
return submatch;
};
});
args.unshift(processed);
@ -220,13 +341,19 @@ ConsoleAPI.prototype = {
/**
* Build the stacktrace array for the console.trace() call.
*
* @param number [aMaxDepth=DEFAULT_MAX_STACKTRACE_DEPTH]
* Optional maximum stacktrace depth.
* @return array
* Each element is a stack frame that holds the following properties:
* filename, lineNumber, functionName and language.
**/
getStackTrace: function CA_getStackTrace() {
*/
getStackTrace: function CA_getStackTrace(aMaxDepth) {
if (!aMaxDepth) {
aMaxDepth = DEFAULT_MAX_STACKTRACE_DEPTH;
}
let stack = [];
let frame = Components.stack.caller;
let frame = Components.stack.caller.caller;
while (frame = frame.caller) {
if (frame.language == Ci.nsIProgrammingLanguage.JAVASCRIPT ||
frame.language == Ci.nsIProgrammingLanguage.JAVASCRIPT2) {
@ -236,6 +363,9 @@ ConsoleAPI.prototype = {
functionName: frame.name,
language: frame.language,
});
if (stack.length == aMaxDepth) {
break;
}
}
}
@ -265,13 +395,15 @@ ConsoleAPI.prototype = {
* The inner ID of the window.
* @param string aName
* The name of the timer.
* @param number [aTimestamp=Date.now()]
* Optional timestamp that tells when the timer was originally started.
* @return object
* The name property holds the timer name and the started property
* holds the time the timer was started. In case of error, it returns
* an object with the single property "error" that contains the key
* for retrieving the localized error message.
**/
startTimer: function CA_startTimer(aWindowId, aName) {
startTimer: function CA_startTimer(aWindowId, aName, aTimestamp) {
if (!aName) {
return;
}
@ -285,7 +417,7 @@ ConsoleAPI.prototype = {
}
let key = aWindowId + "-" + aName.toString();
if (!pageTimers[key]) {
pageTimers[key] = Date.now();
pageTimers[key] = aTimestamp || Date.now();
}
return { name: aName, started: pageTimers[key] };
},
@ -297,11 +429,13 @@ ConsoleAPI.prototype = {
* The inner ID of the window.
* @param string aName
* The name of the timer.
* @param number [aTimestamp=Date.now()]
* Optional timestamp that tells when the timer was originally stopped.
* @return object
* The name property holds the timer name and the duration property
* holds the number of milliseconds since the timer was started.
**/
stopTimer: function CA_stopTimer(aWindowId, aName) {
stopTimer: function CA_stopTimer(aWindowId, aName, aTimestamp) {
if (!aName) {
return;
}
@ -314,7 +448,7 @@ ConsoleAPI.prototype = {
if (!pageTimers[key]) {
return;
}
let duration = Date.now() - pageTimers[key];
let duration = (aTimestamp || Date.now()) - pageTimers[key];
delete pageTimers[key];
return { name: aName, duration: duration };
}

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

@ -17,8 +17,8 @@ using mozilla::dom::DOMRequest;
using mozilla::dom::DOMRequestService;
DOMRequest::DOMRequest(nsIDOMWindow* aWindow)
: mDone(false)
, mResult(JSVAL_VOID)
: mResult(JSVAL_VOID)
, mDone(false)
, mRooted(false)
{
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
@ -34,14 +34,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest,
nsDOMEventTargetHelper)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(success)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mError)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest,
nsDOMEventTargetHelper)
tmp->mResult = JSVAL_VOID;
tmp->UnrootResultVal();
if (tmp->mRooted) {
tmp->mResult = JSVAL_VOID;
tmp->UnrootResultVal();
}
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(success)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mError)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest,
@ -98,35 +102,54 @@ DOMRequest::GetError(nsIDOMDOMError** aError)
void
DOMRequest::FireSuccess(jsval aResult)
{
NS_ABORT_IF_FALSE(!mDone, "Already fired success/error");
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
NS_ASSERTION(!mError, "mError shouldn't have been set!");
NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
mDone = true;
RootResultVal();
if (JSVAL_IS_GCTHING(aResult)) {
RootResultVal();
}
mResult = aResult;
FireEvent(NS_LITERAL_STRING("success"));
FireEvent(NS_LITERAL_STRING("success"), false, false);
}
void
DOMRequest::FireError(const nsAString& aError)
{
NS_ABORT_IF_FALSE(!mDone, "Already fired success/error");
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
NS_ASSERTION(!mError, "mError shouldn't have been set!");
NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
mDone = true;
mError = DOMError::CreateWithName(aError);
FireEvent(NS_LITERAL_STRING("error"));
FireEvent(NS_LITERAL_STRING("error"), true, true);
}
void
DOMRequest::FireEvent(const nsAString& aType)
DOMRequest::FireError(nsresult aError)
{
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
NS_ASSERTION(!mError, "mError shouldn't have been set!");
NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
mDone = true;
mError = DOMError::CreateForNSResult(aError);
FireEvent(NS_LITERAL_STRING("error"), true, true);
}
void
DOMRequest::FireEvent(const nsAString& aType, bool aBubble, bool aCancelable)
{
if (NS_FAILED(CheckInnerWindowCorrectness())) {
return;
}
nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nsnull, nsnull);
nsresult rv = event->InitEvent(aType, false, false);
nsresult rv = event->InitEvent(aType, aBubble, aCancelable);
if (NS_FAILED(rv)) {
return;
}
@ -140,6 +163,22 @@ DOMRequest::FireEvent(const nsAString& aType)
DispatchEvent(event, &dummy);
}
void
DOMRequest::RootResultVal()
{
NS_ASSERTION(!mRooted, "Don't call me if already rooted!");
NS_HOLD_JS_OBJECTS(this, DOMRequest);
mRooted = true;
}
void
DOMRequest::UnrootResultVal()
{
NS_ASSERTION(mRooted, "Don't call me if not rooted!");
NS_DROP_JS_OBJECTS(this, DOMRequest);
mRooted = false;
}
NS_IMPL_ISUPPORTS1(DOMRequestService, nsIDOMRequestService)
NS_IMETHODIMP

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

@ -20,6 +20,12 @@ namespace dom {
class DOMRequest : public nsDOMEventTargetHelper,
public nsIDOMDOMRequest
{
protected:
jsval mResult;
nsCOMPtr<nsIDOMDOMError> mError;
bool mDone;
bool mRooted;
NS_DECL_EVENT_HANDLER(success)
NS_DECL_EVENT_HANDLER(error)
@ -33,37 +39,22 @@ public:
void FireSuccess(jsval aResult);
void FireError(const nsAString& aError);
void FireError(nsresult aError);
DOMRequest(nsIDOMWindow* aWindow);
virtual ~DOMRequest()
{
UnrootResultVal();
}
bool mDone;
jsval mResult;
nsCOMPtr<nsIDOMDOMError> mError;
bool mRooted;
private:
void FireEvent(const nsAString& aType);
void RootResultVal()
{
if (!mRooted) {
NS_HOLD_JS_OBJECTS(this, DOMRequest);
mRooted = true;
}
}
void UnrootResultVal()
{
if (mRooted) {
NS_DROP_JS_OBJECTS(this, DOMRequest);
mRooted = false;
UnrootResultVal();
}
}
protected:
void FireEvent(const nsAString& aType, bool aBubble, bool aCancelable);
virtual void RootResultVal();
virtual void UnrootResultVal();
};
class DOMRequestService : public nsIDOMRequestService

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

@ -17,6 +17,7 @@ enum StructuredCloneTags {
SCTAG_DOM_BLOB,
SCTAG_DOM_FILE,
SCTAG_DOM_FILELIST,
SCTAG_DOM_FILEHANDLE,
// These tags are used for both main thread and workers.
SCTAG_DOM_IMAGEDATA,

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

@ -107,3 +107,10 @@ DOM_MSG_DEF(NS_ERROR_FACTORY_NOT_REGISTERED , "Factory not registered")
DOM_MSG_DEF(NS_ERROR_FACTORY_NOT_LOADED , "Factory not loaded")
DOM_MSG_DEF(NS_ERROR_FACTORY_NO_SIGNATURE_SUPPORT , "Factory does not support signatures")
DOM_MSG_DEF(NS_ERROR_FACTORY_EXISTS , "Factory already exists")
DOM4_MSG_DEF(UnknownError, "The operation failed for reasons unrelated to the file storage itself and not covered by any other error code.", NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR)
DOM4_MSG_DEF(LockedFileInactiveError, "A request was placed against a locked file which is currently not active, or which is finished.", NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR)
DOM4_MSG_DEF(ReadOnlyError, "A mutation operation was attempted in a READ_ONLY locked file.", NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR)
DOM4_MSG_DEF(InvalidStateError, "A mutation operation was attempted on a file storage that did not allow mutations.", NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR)
DOM4_MSG_DEF(AbortError, "A request was aborted, for example through a call to LockedFile.abort.", NS_ERROR_DOM_FILEHANDLE_ABORT_ERR)

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

@ -469,6 +469,7 @@
#include "mozilla/dom/indexedDB/IDBWrapperCache.h"
#include "mozilla/dom/indexedDB/IDBFactory.h"
#include "mozilla/dom/indexedDB/IDBFileHandle.h"
#include "mozilla/dom/indexedDB/IDBRequest.h"
#include "mozilla/dom/indexedDB/IDBDatabase.h"
#include "mozilla/dom/indexedDB/IDBEvents.h"
@ -518,6 +519,10 @@ using mozilla::dom::indexedDB::IDBWrapperCache;
#include "DOMError.h"
#include "DOMRequest.h"
#include "DOMFileHandle.h"
#include "FileRequest.h"
#include "LockedFile.h"
#include "mozilla/Likely.h"
#undef None // something included above defines this preprocessor symbol, maybe Xlib headers
@ -1578,6 +1583,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(IDBFactory, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA_WITH_NAME(IDBFileHandle, FileHandle, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(IDBRequest, IDBEventTargetSH,
IDBEVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(IDBDatabase, IDBEventTargetSH,
@ -1643,6 +1650,13 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(DOMRequest, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA_WITH_NAME(DOMFileHandle, FileHandle, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(FileRequest, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(LockedFile, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
};
// Objects that should be constructable through |new Name();|
@ -4338,6 +4352,11 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIIDBFactory)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(IDBFileHandle, nsIDOMFileHandle)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileHandle)
DOM_CLASSINFO_MAP_ENTRY(nsIIDBFileHandle)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(IDBRequest, nsIIDBRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIIDBRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
@ -4467,6 +4486,20 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(DOMFileHandle, nsIDOMFileHandle)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileHandle)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(FileRequest, nsIDOMFileRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(LockedFile, nsIDOMLockedFile)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMLockedFile)
DOM_CLASSINFO_MAP_END
#ifdef NS_DEBUG
{
PRUint32 i = ArrayLength(sClassInfoData);

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

@ -487,6 +487,7 @@ DOMCI_CLASS(WebSocket)
DOMCI_CLASS(CloseEvent)
DOMCI_CLASS(IDBFactory)
DOMCI_CLASS(IDBFileHandle)
DOMCI_CLASS(IDBRequest)
DOMCI_CLASS(IDBDatabase)
DOMCI_CLASS(IDBObjectStore)
@ -526,3 +527,7 @@ DOMCI_CLASS(BluetoothAdapter)
DOMCI_CLASS(DOMError)
DOMCI_CLASS(DOMRequest)
DOMCI_CLASS(DOMFileHandle)
DOMCI_CLASS(FileRequest)
DOMCI_CLASS(LockedFile)

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

@ -89,4 +89,11 @@
#define NS_ERROR_DOM_FILE_NOT_READABLE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILE, 1)
#define NS_ERROR_DOM_FILE_ABORT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILE, 2)
#define NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,1)
#define NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,2)
#define NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR \
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,3)
#define NS_ERROR_DOM_FILEHANDLE_ABORT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,4)
#define NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,5)
#endif // nsDOMError_h__

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

@ -96,6 +96,9 @@ enum DOM4ErrorTypeCodeMap {
/* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */
NotReadableError = 0,
/* FileHandle API errors */
LockedFileInactiveError = 0,
};
#define DOM4_MSG_DEF(name, message, nsresult) {(nsresult), name, #name, message},

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

@ -55,8 +55,9 @@ nsDOMScriptObjectFactory::nsDOMScriptObjectFactory()
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_SVG);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_XPATH);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_INDEXEDDB);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_XPCONNECT);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_INDEXEDDB);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_FILEHANDLE);
}
NS_ASSERTION(!gExceptionProvider, "Registered twice?!");
@ -142,6 +143,10 @@ nsDOMScriptObjectFactory::Observe(nsISupports *aSubject,
NS_ERROR_MODULE_DOM_XPATH);
xs->UnregisterExceptionProvider(gExceptionProvider,
NS_ERROR_MODULE_XPCONNECT);
xs->UnregisterExceptionProvider(gExceptionProvider,
NS_ERROR_MODULE_DOM_INDEXEDDB);
xs->UnregisterExceptionProvider(gExceptionProvider,
NS_ERROR_MODULE_DOM_FILEHANDLE);
}
NS_RELEASE(gExceptionProvider);
@ -251,7 +256,8 @@ nsDOMExceptionProvider::GetException(nsresult result,
default:
MOZ_ASSERT(NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM ||
NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_FILE ||
NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_INDEXEDDB,
NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_INDEXEDDB ||
NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_FILEHANDLE,
"Trying to create an exception for the wrong error module.");
return NS_NewDOMException(result, aDefaultException, _retval);
}

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

@ -63,6 +63,8 @@
#include "mozilla/dom/indexedDB/FileInfo.h"
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
#include "sampler.h"
#include "nsDOMBlobBuilder.h"
#include "nsIDOMFileHandle.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -2270,14 +2272,99 @@ nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResu
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetFileId(nsIDOMBlob* aBlob, PRInt64* aResult)
static nsresult
GetFileOrBlob(const nsAString& aName, const jsval& aBlobParts,
const jsval& aParameters, JSContext* aCx,
PRUint8 aOptionalArgCount, nsISupports** aResult)
{
if (!IsUniversalXPConnectCapable()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
*aResult = aBlob->GetFileId();
nsresult rv;
nsCOMPtr<nsISupports> file;
if (aName.IsVoid()) {
rv = nsDOMMultipartFile::NewBlob(getter_AddRefs(file));
}
else {
rv = nsDOMMultipartFile::NewFile(aName, getter_AddRefs(file));
}
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(file);
NS_ASSERTION(initializer, "what?");
jsval args[2] = { aBlobParts, aParameters };
rv = initializer->Initialize(nsnull, aCx, nsnull, aOptionalArgCount, args);
NS_ENSURE_SUCCESS(rv, rv);
file.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetFile(const nsAString& aName, const jsval& aBlobParts,
const jsval& aParameters, JSContext* aCx,
PRUint8 aOptionalArgCount, nsIDOMFile** aResult)
{
nsCOMPtr<nsISupports> file;
nsresult rv = GetFileOrBlob(aName, aBlobParts, aParameters, aCx,
aOptionalArgCount, getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMFile> result = do_QueryInterface(file);
result.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetBlob(const jsval& aBlobParts, const jsval& aParameters,
JSContext* aCx, PRUint8 aOptionalArgCount,
nsIDOMBlob** aResult)
{
nsAutoString name;
name.SetIsVoid(true);
nsCOMPtr<nsISupports> blob;
nsresult rv = GetFileOrBlob(name, aBlobParts, aParameters, aCx,
aOptionalArgCount, getter_AddRefs(blob));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMBlob> result = do_QueryInterface(blob);
result.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetFileId(const jsval& aFile, JSContext* aCx,
PRInt64* aResult)
{
if (!JSVAL_IS_PRIMITIVE(aFile)) {
JSObject* obj = JSVAL_TO_OBJECT(aFile);
nsISupports* nativeObj =
nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(nativeObj);
if (blob) {
*aResult = blob->GetFileId();
return NS_OK;
}
nsCOMPtr<nsIDOMFileHandle> fileHandle = do_QueryInterface(nativeObj);
if (fileHandle) {
*aResult = fileHandle->GetFileId();
return NS_OK;
}
}
*aResult = -1;
return NS_OK;
}

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

@ -927,21 +927,16 @@ nsFocusManager::WindowHidden(nsIDOMWindow* aWindow)
nsIContent* oldFocusedContent = mFocusedContent;
mFocusedContent = nsnull;
nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
nsCOMPtr<nsIPresShell> presShell;
focusedDocShell->GetPresShell(getter_AddRefs(presShell));
if (oldFocusedContent && oldFocusedContent->IsInDoc()) {
NotifyFocusStateChange(oldFocusedContent,
mFocusedWindow->ShouldShowFocusRing(),
false);
window->UpdateCommands(NS_LITERAL_STRING("focus"));
SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell,
oldFocusedContent->GetCurrentDoc(),
oldFocusedContent, 1, false);
}
nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
nsCOMPtr<nsIPresShell> presShell;
focusedDocShell->GetPresShell(getter_AddRefs(presShell));
nsIMEStateManager::OnTextStateBlur(nsnull, nsnull);
if (presShell) {
nsIMEStateManager::OnChangeFocus(presShell->GetPresContext(), nsnull,

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

@ -17,8 +17,8 @@ interface nsIDOMDOMRequest : nsIDOMEventTarget
readonly attribute jsval result;
readonly attribute nsIDOMDOMError error;
attribute nsIDOMEventListener onsuccess;
attribute nsIDOMEventListener onerror;
attribute nsIDOMEventListener onsuccess;
attribute nsIDOMEventListener onerror;
};
[scriptable, builtinclass, uuid(eebcdf29-f8fa-4c36-bbc7-2146b1cbaf7b)]

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

@ -5,6 +5,7 @@
DOM_SRCDIRS = \
dom/base \
dom/battery \
dom/file \
dom/power \
dom/media \
dom/network/src \

128
dom/file/AsyncHelper.cpp Normal file
Просмотреть файл

@ -0,0 +1,128 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AsyncHelper.h"
#include "nsIRequestObserver.h"
#include "nsNetUtil.h"
#include "FileService.h"
USING_FILE_NAMESPACE
NS_IMPL_THREADSAFE_ISUPPORTS2(AsyncHelper, nsIRunnable, nsIRequest)
nsresult
AsyncHelper::AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt)
{
nsresult rv;
if (aObserver) {
// build proxy for observer events
rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), aObserver);
NS_ENSURE_SUCCESS(rv, rv);
}
mCtxt = aCtxt;
FileService* service = FileService::GetOrCreate();
NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
nsIEventTarget* target = service->StreamTransportTarget();
rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
AsyncHelper::Run()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mObserver) {
mObserver->OnStartRequest(this, mCtxt);
}
mStatus = DoStreamWork(mStream);
if (mObserver) {
mObserver->OnStopRequest(this, mCtxt, mStatus);
}
return NS_OK;
}
NS_IMETHODIMP
AsyncHelper::GetName(nsACString& aName)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::IsPending(bool* _retval)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::GetStatus(nsresult* aStatus)
{
*aStatus = mStatus;
return NS_OK;
}
NS_IMETHODIMP
AsyncHelper::Cancel(nsresult aStatus)
{
return NS_OK;
}
NS_IMETHODIMP
AsyncHelper::Suspend()
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::Resume()
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::GetLoadGroup(nsILoadGroup** aLoadGroup)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::GetLoadFlags(nsLoadFlags* aLoadFlags)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::SetLoadFlags(nsLoadFlags aLoadFlags)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}

57
dom/file/AsyncHelper.h Normal file
Просмотреть файл

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_asynchelper_h__
#define mozilla_dom_file_asynchelper_h__
#include "FileCommon.h"
#include "nsIRequest.h"
#include "nsIRunnable.h"
class nsIRequestObserver;
BEGIN_FILE_NAMESPACE
/**
* Must be subclassed. The subclass must implement DoStreamWork.
* Async operations that are not supported in necko (truncate, flush, etc.)
* should use this helper. Call AsyncWork to invoke the operation.
*/
class AsyncHelper : public nsIRunnable,
public nsIRequest
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSIREQUEST
nsresult
AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt);
protected:
AsyncHelper(nsISupports* aStream)
: mStream(aStream),
mStatus(NS_OK)
{ }
virtual ~AsyncHelper()
{ }
virtual nsresult
DoStreamWork(nsISupports* aStream) = 0;
private:
nsCOMPtr<nsISupports> mStream;
nsCOMPtr<nsIRequestObserver> mObserver;
nsCOMPtr<nsISupports> mCtxt;
nsresult mStatus;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_asynchelper_h__

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

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMFileHandle.h"
#include "nsIFileStreams.h"
#include "nsDOMClassInfoID.h"
#include "nsNetUtil.h"
#include "File.h"
#include "LockedFile.h"
USING_FILE_NAMESPACE
// static
already_AddRefed<DOMFileHandle>
DOMFileHandle::Create(nsPIDOMWindow* aWindow,
nsIFileStorage* aFileStorage,
nsIFile* aFile)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsRefPtr<DOMFileHandle> newFile(new DOMFileHandle);
newFile->BindToOwner(aWindow);
newFile->mFileStorage = aFileStorage;
nsresult rv = aFile->GetLeafName(newFile->mName);
NS_ENSURE_SUCCESS(rv, nsnull);
newFile->mFile = aFile;
newFile->mFileName = newFile->mName;
return newFile.forget();
}
already_AddRefed<nsISupports>
DOMFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
{
nsresult rv;
if (aReadOnly) {
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), aFile, -1, -1,
nsIFileInputStream::DEFER_OPEN);
NS_ENSURE_SUCCESS(rv, nsnull);
return stream.forget();
}
nsCOMPtr<nsIFileStream> stream;
rv = NS_NewLocalFileStream(getter_AddRefs(stream), aFile, -1, -1,
nsIFileStream::DEFER_OPEN);
NS_ENSURE_SUCCESS(rv, nsnull);
return stream.forget();
}
already_AddRefed<nsIDOMFile>
DOMFileHandle::CreateFileObject(LockedFile* aLockedFile, PRUint32 aFileSize)
{
nsCOMPtr<nsIDOMFile> file =
new File(mName, mType, aFileSize, mFile, aLockedFile);
return file.forget();
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMFileHandle)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMFileHandle)
NS_INTERFACE_MAP_END_INHERITING(FileHandle)
NS_IMPL_ADDREF_INHERITED(DOMFileHandle, FileHandle)
NS_IMPL_RELEASE_INHERITED(DOMFileHandle, FileHandle)
DOMCI_DATA(DOMFileHandle, DOMFileHandle)

42
dom/file/DOMFileHandle.h Normal file
Просмотреть файл

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_domfilehandle_h__
#define mozilla_dom_file_domfilehandle_h__
#include "FileCommon.h"
#include "FileHandle.h"
BEGIN_FILE_NAMESPACE
class DOMFileHandle : public FileHandle
{
public:
NS_DECL_ISUPPORTS_INHERITED
static already_AddRefed<DOMFileHandle>
Create(nsPIDOMWindow* aWindow,
nsIFileStorage* aFileStorage,
nsIFile* aFile);
virtual already_AddRefed<nsISupports>
CreateStream(nsIFile* aFile, bool aReadOnly);
virtual already_AddRefed<nsIDOMFile>
CreateFileObject(LockedFile* aLockedFile, PRUint32 aFileSize);
protected:
DOMFileHandle()
{ }
~DOMFileHandle()
{ }
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_domfilehandle_h__

86
dom/file/File.cpp Normal file
Просмотреть файл

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "File.h"
#include "LockedFile.h"
USING_FILE_NAMESPACE
using mozilla::dom::indexedDB::IndexedDatabaseManager;
// Create slice
File::File(const File* aOther, PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType)
: nsDOMFileCC(aContentType, aOther->mStart + aStart, aLength),
mFile(aOther->mFile), mLockedFile(aOther->mLockedFile),
mWholeFile(false), mStoredFile(aOther->mStoredFile)
{
NS_ASSERTION(mFile, "Null file!");
NS_ASSERTION(mLockedFile, "Null locked file!");
if (mStoredFile) {
FileInfo* fileInfo;
if (IndexedDatabaseManager::IsClosed()) {
fileInfo = aOther->GetFileInfo();
}
else {
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
fileInfo = aOther->GetFileInfo();
}
mFileInfos.AppendElement(fileInfo);
}
}
NS_IMPL_CYCLE_COLLECTION_CLASS(File)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(File, nsDOMFileCC)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mLockedFile,
nsIDOMLockedFile)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(File, nsDOMFileCC)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mLockedFile)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(File)
NS_INTERFACE_MAP_END_INHERITING(nsDOMFileCC)
NS_IMPL_ADDREF_INHERITED(File, nsDOMFileCC)
NS_IMPL_RELEASE_INHERITED(File, nsDOMFileCC)
NS_IMETHODIMP
File::GetInternalStream(nsIInputStream **aStream)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsresult rv = mLockedFile->OpenInputStream(mWholeFile, mStart, mLength,
aStream);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
already_AddRefed<nsIDOMBlob>
File::CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIDOMBlob> t =
new File(this, aStart, aLength, aContentType);
return t.forget();
}
NS_IMETHODIMP
File::GetMozFullPathInternal(nsAString &aFilename)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mIsFile, "Should only be called on files");
return mFile->GetPath(aFilename);
}

96
dom/file/File.h Normal file
Просмотреть файл

@ -0,0 +1,96 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_file_h__
#define mozilla_dom_file_file_h__
#include "FileCommon.h"
#include "nsDOMFile.h"
#include "LockedFile.h"
BEGIN_FILE_NAMESPACE
class File : public nsDOMFileCC
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(File, nsDOMFileCC)
// Create as a file
File(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength, nsIFile* aFile, LockedFile* aLockedFile)
: nsDOMFileCC(aName, aContentType, aLength),
mFile(aFile), mLockedFile(aLockedFile),
mWholeFile(true), mStoredFile(false)
{
NS_ASSERTION(mFile, "Null file!");
NS_ASSERTION(mLockedFile, "Null locked file!");
}
// Create as a stored file
File(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength, nsIFile* aFile, LockedFile* aLockedFile,
FileInfo* aFileInfo)
: nsDOMFileCC(aName, aContentType, aLength),
mFile(aFile), mLockedFile(aLockedFile),
mWholeFile(true), mStoredFile(true)
{
NS_ASSERTION(mFile, "Null file!");
NS_ASSERTION(mLockedFile, "Null locked file!");
mFileInfos.AppendElement(aFileInfo);
}
// Overrides
NS_IMETHOD
GetMozFullPathInternal(nsAString& aFullPath);
NS_IMETHOD
GetInternalStream(nsIInputStream** aStream);
protected:
// Create slice
File(const File* aOther, PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType);
virtual ~File()
{ }
virtual already_AddRefed<nsIDOMBlob>
CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType);
virtual bool
IsStoredFile() const
{
return mStoredFile;
}
virtual bool
IsWholeFile() const
{
return mWholeFile;
}
virtual bool
IsSnapshot() const
{
return true;
}
private:
nsCOMPtr<nsIFile> mFile;
nsRefPtr<LockedFile> mLockedFile;
bool mWholeFile;
bool mStoredFile;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_file_h__

25
dom/file/FileCommon.h Normal file
Просмотреть файл

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_filecommon_h__
#define mozilla_dom_file_filecommon_h__
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDOMEventTargetHelper.h"
#include "nsDebug.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
#define BEGIN_FILE_NAMESPACE \
namespace mozilla { namespace dom { namespace file {
#define END_FILE_NAMESPACE \
} /* namespace file */ } /* namespace dom */ } /* namespace mozilla */
#define USING_FILE_NAMESPACE \
using namespace mozilla::dom::file;
#endif // mozilla_dom_file_filecommon_h__

180
dom/file/FileHandle.cpp Normal file
Просмотреть файл

@ -0,0 +1,180 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileHandle.h"
#include "nsContentUtils.h"
#include "nsDOMClassInfoID.h"
#include "nsIDOMFile.h"
#include "nsIFileStorage.h"
#include "FileRequest.h"
#include "FileService.h"
#include "LockedFile.h"
#include "MetadataHelper.h"
USING_FILE_NAMESPACE
namespace {
class GetFileHelper : public MetadataHelper
{
public:
GetFileHelper(LockedFile* aLockedFile,
FileRequest* aFileRequest,
MetadataParameters* aParams,
FileHandle* aFileHandle)
: MetadataHelper(aLockedFile, aFileRequest, aParams),
mFileHandle(aFileHandle)
{ }
nsresult
GetSuccessResult(JSContext* aCx, jsval* aVal);
void
ReleaseObjects()
{
mFileHandle = nsnull;
MetadataHelper::ReleaseObjects();
}
private:
nsRefPtr<FileHandle> mFileHandle;
};
} // anonymous namespace
NS_IMPL_CYCLE_COLLECTION_CLASS(FileHandle)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileHandle,
nsDOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFileStorage)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileHandle,
nsDOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFileStorage)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileHandle)
NS_INTERFACE_MAP_ENTRY(nsIDOMFileHandle)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(FileHandle, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(FileHandle, nsDOMEventTargetHelper)
NS_IMPL_EVENT_HANDLER(FileHandle, abort);
NS_IMPL_EVENT_HANDLER(FileHandle, error);
NS_IMETHODIMP
FileHandle::GetName(nsAString& aName)
{
aName = mName;
return NS_OK;
}
NS_IMETHODIMP
FileHandle::GetType(nsAString& aType)
{
aType = mType;
return NS_OK;
}
NS_IMETHODIMP
FileHandle::Open(const nsAString& aMode,
PRUint8 aOptionalArgCount,
nsIDOMLockedFile** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (FileService::IsShuttingDown() || mFileStorage->IsStorageShuttingDown()) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
LockedFile::Mode mode;
if (aOptionalArgCount) {
if (aMode.EqualsLiteral("readwrite")) {
mode = LockedFile::READ_WRITE;
}
else if (aMode.EqualsLiteral("readonly")) {
mode = LockedFile::READ_ONLY;
}
else {
return NS_ERROR_TYPE_ERR;
}
}
else {
mode = LockedFile::READ_ONLY;
}
nsRefPtr<LockedFile> lockedFile = LockedFile::Create(this, mode);
NS_ENSURE_TRUE(lockedFile, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
lockedFile.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
FileHandle::GetFile(nsIDOMFileRequest** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Do nothing if the window is closed
if (!GetOwner()) {
return NS_OK;
}
nsRefPtr<LockedFile> lockedFile =
LockedFile::Create(this, LockedFile::READ_ONLY, LockedFile::PARALLEL);
NS_ENSURE_TRUE(lockedFile, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
nsRefPtr<FileRequest> fileRequest =
FileRequest::Create(GetOwner(), lockedFile);
nsRefPtr<MetadataParameters> params = new MetadataParameters();
params->Init(true, false);
nsRefPtr<GetFileHelper> helper =
new GetFileHelper(lockedFile, fileRequest, params, this);
nsresult rv = helper->Enqueue();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
fileRequest.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP_(PRInt64)
FileHandle::GetFileId()
{
return -1;
}
NS_IMETHODIMP_(mozilla::dom::indexedDB::FileInfo*)
FileHandle::GetFileInfo()
{
return nsnull;
}
nsresult
GetFileHelper::GetSuccessResult(JSContext* aCx, jsval* aVal)
{
nsCOMPtr<nsIDOMFile> domFile =
mFileHandle->CreateFileObject(mLockedFile, mParams->Size());
nsresult rv =
nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), domFile,
&NS_GET_IID(nsIDOMFile), aVal);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return NS_OK;
}

86
dom/file/FileHandle.h Normal file
Просмотреть файл

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_filehandle_h__
#define mozilla_dom_file_filehandle_h__
#include "FileCommon.h"
#include "nsIDOMFileHandle.h"
#include "nsIFile.h"
#include "nsDOMEventTargetHelper.h"
class nsIDOMFile;
class nsIFileStorage;
BEGIN_FILE_NAMESPACE
class FileService;
class LockedFile;
class FinishHelper;
class FileHelper;
/**
* Must be subclassed. The subclass must implement CreateStream and
* CreateFileObject. Basically, every file storage implementation provides its
* own FileHandle implementation (for example IDBFileHandle provides IndexedDB
* specific implementation).
*/
class FileHandle : public nsDOMEventTargetHelper,
public nsIDOMFileHandle
{
friend class FileService;
friend class LockedFile;
friend class FinishHelper;
friend class FileHelper;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMFILEHANDLE
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileHandle, nsDOMEventTargetHelper)
const nsAString&
Name() const
{
return mName;
}
const nsAString&
Type() const
{
return mType;
}
virtual already_AddRefed<nsISupports>
CreateStream(nsIFile* aFile, bool aReadOnly) = 0;
virtual already_AddRefed<nsIDOMFile>
CreateFileObject(LockedFile* aLockedFile, PRUint32 aFileSize) = 0;
protected:
FileHandle()
{ }
~FileHandle()
{ }
nsCOMPtr<nsIFileStorage> mFileStorage;
nsString mName;
nsString mType;
nsCOMPtr<nsIFile> mFile;
nsString mFileName;
NS_DECL_EVENT_HANDLER(abort)
NS_DECL_EVENT_HANDLER(error)
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_filehandle_h__

209
dom/file/FileHelper.cpp Normal file
Просмотреть файл

@ -0,0 +1,209 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileHelper.h"
#include "nsIFileStorage.h"
#include "nsNetError.h"
#include "nsProxyRelease.h"
#include "FileHandle.h"
#include "FileRequest.h"
#include "FileService.h"
USING_FILE_NAMESPACE
namespace {
LockedFile* gCurrentLockedFile = nsnull;
} // anonymous namespace
FileHelper::FileHelper(LockedFile* aLockedFile,
FileRequest* aFileRequest)
: mFileStorage(aLockedFile->mFileHandle->mFileStorage),
mLockedFile(aLockedFile),
mFileRequest(aFileRequest),
mResultCode(NS_OK),
mFinished(false)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
FileHelper::~FileHelper()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
NS_IMPL_THREADSAFE_ISUPPORTS1(FileHelper, nsIRequestObserver)
nsresult
FileHelper::Enqueue()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
FileService* service = FileService::GetOrCreate();
NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
nsresult rv = service->Enqueue(mLockedFile, this);
NS_ENSURE_SUCCESS(rv, rv);
if (mLockedFile) {
mLockedFile->OnNewRequest();
}
return NS_OK;
}
nsresult
FileHelper::AsyncRun(FileHelperListener* aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Assign the listener early, so we can use it synchronously if the code
// below fails.
mListener = aListener;
nsresult rv;
nsCOMPtr<nsISupports> stream;
if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
rv = mLockedFile->CreateParallelStream(getter_AddRefs(stream));
}
else {
rv = mLockedFile->GetOrCreateStream(getter_AddRefs(stream));
}
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION(stream, "This should never be null!");
rv = DoAsyncRun(stream);
}
if (NS_FAILED(rv)) {
mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
Finish();
}
return NS_OK;
}
NS_IMETHODIMP
FileHelper::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
return NS_OK;
}
NS_IMETHODIMP
FileHelper::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
nsresult aStatus)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (NS_FAILED(aStatus)) {
mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
Finish();
return NS_OK;
}
void
FileHelper::OnStreamProgress(PRUint64 aProgress, PRUint64 aProgressMax)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mLockedFile->IsAborted()) {
NS_ASSERTION(mRequest, "Should have a request!\n");
nsresult rv = mRequest->Cancel(NS_BINDING_ABORTED);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to cancel the request!");
}
return;
}
if (mFileRequest) {
mFileRequest->OnProgress(aProgress, aProgressMax);
}
}
// static
LockedFile*
FileHelper::GetCurrentLockedFile()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
return gCurrentLockedFile;
}
nsresult
FileHelper::GetSuccessResult(JSContext* aCx,
jsval* aVal)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
*aVal = JSVAL_VOID;
return NS_OK;
}
void
FileHelper::ReleaseObjects()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileStorage = nsnull;
mLockedFile = nsnull;
mFileRequest = nsnull;
mListener = nsnull;
mRequest = nsnull;
}
void
FileHelper::Finish()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mFinished) {
return;
}
mFinished = true;
if (mLockedFile->IsAborted()) {
// Always fire a "error" event with ABORT_ERR if the transaction was
// aborted, even if the request succeeded or failed with another error.
mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
}
LockedFile* oldLockedFile = gCurrentLockedFile;
gCurrentLockedFile = mLockedFile;
if (mFileRequest) {
nsresult rv = mFileRequest->NotifyHelperCompleted(this);
if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
mResultCode = rv;
}
}
NS_ASSERTION(gCurrentLockedFile == mLockedFile, "Should be unchanged!");
gCurrentLockedFile = oldLockedFile;
mLockedFile->OnRequestFinished();
mListener->OnFileHelperComplete(this);
ReleaseObjects();
NS_ASSERTION(!(mFileStorage || mLockedFile || mFileRequest || mListener ||
mRequest), "Subclass didn't call FileHelper::ReleaseObjects!");
}

107
dom/file/FileHelper.h Normal file
Просмотреть файл

@ -0,0 +1,107 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_filehelper_h__
#define mozilla_dom_file_filehelper_h__
#include "FileCommon.h"
#include "nsIRequestObserver.h"
class nsIFileStorage;
BEGIN_FILE_NAMESPACE
class FileHelper;
class FileRequest;
class FileOutputStreamWrapper;
class LockedFile;
class FileHelperListener
{
public:
NS_IMETHOD_(nsrefcnt)
AddRef() = 0;
NS_IMETHOD_(nsrefcnt)
Release() = 0;
virtual void
OnFileHelperComplete(FileHelper* aFileHelper) = 0;
};
/**
* Must be subclassed. The subclass must implement DoAsyncRun. It may then
* choose to implement GetSuccessResult to properly set the result of the
* success event. Call Enqueue to start the file operation.
*/
class FileHelper : public nsIRequestObserver
{
friend class FileRequest;
friend class FileOutputStreamWrapper;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
nsresult
Enqueue();
nsresult
AsyncRun(FileHelperListener* aListener);
void
OnStreamProgress(PRUint64 aProgress, PRUint64 aProgressMax);
void
OnStreamClose()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
Finish();
}
void
OnStreamDestroy()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
Finish();
}
static LockedFile*
GetCurrentLockedFile();
protected:
FileHelper(LockedFile* aLockedFile, FileRequest* aRequest);
virtual ~FileHelper();
virtual nsresult
DoAsyncRun(nsISupports* aStream) = 0;
virtual nsresult
GetSuccessResult(JSContext* aCx, jsval* aVal);
virtual void
ReleaseObjects();
void
Finish();
nsCOMPtr<nsIFileStorage> mFileStorage;
nsRefPtr<LockedFile> mLockedFile;
nsRefPtr<FileRequest> mFileRequest;
nsRefPtr<FileHelperListener> mListener;
nsCOMPtr<nsIRequest> mRequest;
private:
nsresult mResultCode;
bool mFinished;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_filehelper_h__

169
dom/file/FileRequest.cpp Normal file
Просмотреть файл

@ -0,0 +1,169 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileRequest.h"
#include "nsIJSContextStack.h"
#include "nsIPrivateDOMEvent.h"
#include "nsContentUtils.h"
#include "nsEventDispatcher.h"
#include "nsDOMProgressEvent.h"
#include "FileHelper.h"
#include "LockedFile.h"
USING_FILE_NAMESPACE
FileRequest::FileRequest(nsIDOMWindow* aWindow)
: DOMRequest(aWindow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
FileRequest::~FileRequest()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
// static
already_AddRefed<FileRequest>
FileRequest::Create(nsIDOMWindow* aOwner,
LockedFile* aLockedFile)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsRefPtr<FileRequest> request = new FileRequest(aOwner);
request->mLockedFile = aLockedFile;
return request.forget();
}
nsresult
FileRequest::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
aVisitor.mCanHandle = true;
aVisitor.mParentTarget = mLockedFile;
return NS_OK;
}
nsresult
FileRequest::NotifyHelperCompleted(FileHelper* aFileHelper)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsresult rv = aFileHelper->mResultCode;
// If the request failed then fire error event and return.
if (NS_FAILED(rv)) {
FireError(rv);
return NS_OK;
}
// Otherwise we need to get the result from the helper.
jsval result;
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
NS_ENSURE_STATE(sc);
JSContext* cx = sc->GetNativeContext();
NS_ASSERTION(cx, "Failed to get a context!");
JSObject* global = sc->GetNativeGlobal();
NS_ASSERTION(global, "Failed to get global object!");
JSAutoRequest ar(cx);
JSAutoEnterCompartment ac;
if (ac.enter(cx, global)) {
rv = aFileHelper->GetSuccessResult(cx, &result);
if (NS_FAILED(rv)) {
NS_WARNING("GetSuccessResult failed!");
}
}
else {
NS_WARNING("Failed to enter correct compartment!");
rv = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
if (NS_SUCCEEDED(rv)) {
FireSuccess(result);
}
else {
FireError(rv);
}
return NS_OK;
}
NS_IMETHODIMP
FileRequest::GetLockedFile(nsIDOMLockedFile** aLockedFile)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIDOMLockedFile> lockedFile(mLockedFile);
lockedFile.forget(aLockedFile);
return NS_OK;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(FileRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileRequest, DOMRequest)
// Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because
// nsDOMEventTargetHelper does it for us.
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(progress)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mLockedFile,
nsIDOMLockedFile)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileRequest, DOMRequest)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(progress)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mLockedFile)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileRequest)
NS_INTERFACE_MAP_ENTRY(nsIDOMFileRequest)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(FileRequest)
NS_INTERFACE_MAP_END_INHERITING(DOMRequest)
NS_IMPL_ADDREF_INHERITED(FileRequest, DOMRequest)
NS_IMPL_RELEASE_INHERITED(FileRequest, DOMRequest)
DOMCI_DATA(FileRequest, FileRequest)
NS_IMPL_EVENT_HANDLER(FileRequest, progress);
void
FileRequest::FireProgressEvent(PRUint64 aLoaded, PRUint64 aTotal)
{
if (NS_FAILED(CheckInnerWindowCorrectness())) {
return;
}
nsRefPtr<nsDOMProgressEvent> event = new nsDOMProgressEvent(nsnull, nsnull);
nsresult rv = event->InitProgressEvent(NS_LITERAL_STRING("progress"),
false, false, false, aLoaded, aTotal);
NS_ENSURE_SUCCESS(rv,);
rv = event->SetTrusted(true);
NS_ENSURE_SUCCESS(rv,);
bool dummy;
rv = DispatchEvent(static_cast<nsIDOMProgressEvent*>(event), &dummy);
NS_ENSURE_SUCCESS(rv,);
}
void
FileRequest::RootResultVal()
{
NS_ASSERTION(!mRooted, "Don't call me if already rooted!");
nsXPCOMCycleCollectionParticipant *participant;
CallQueryInterface(this, &participant);
nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(this, DOMRequest),
participant);
mRooted = true;
}

64
dom/file/FileRequest.h Normal file
Просмотреть файл

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_filerequest_h__
#define mozilla_dom_file_filerequest_h__
#include "FileCommon.h"
#include "nsIDOMFileRequest.h"
#include "DOMRequest.h"
BEGIN_FILE_NAMESPACE
class FileHelper;
class LockedFile;
class FileRequest : public mozilla::dom::DOMRequest,
public nsIDOMFileRequest
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMFILEREQUEST
NS_FORWARD_NSIDOMDOMREQUEST(DOMRequest::)
NS_FORWARD_NSIDOMEVENTTARGET_NOPREHANDLEEVENT(DOMRequest::)
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileRequest, DOMRequest)
static already_AddRefed<FileRequest>
Create(nsIDOMWindow* aOwner, LockedFile* aLockedFile);
// nsIDOMEventTarget
virtual nsresult
PreHandleEvent(nsEventChainPreVisitor& aVisitor);
void
OnProgress(PRUint64 aProgress, PRUint64 aProgressMax)
{
FireProgressEvent(aProgress, aProgressMax);
}
nsresult
NotifyHelperCompleted(FileHelper* aFileHelper);
private:
FileRequest(nsIDOMWindow* aWindow);
~FileRequest();
void
FireProgressEvent(PRUint64 aLoaded, PRUint64 aTotal);
virtual void
RootResultVal();
nsRefPtr<LockedFile> mLockedFile;
NS_DECL_EVENT_HANDLER(progress)
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_filerequest_h__

528
dom/file/FileService.cpp Normal file
Просмотреть файл

@ -0,0 +1,528 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileService.h"
#include "nsIFile.h"
#include "nsIFileStorage.h"
#include "nsIObserverService.h"
#include "nsIStreamTransportService.h"
#include "nsNetCID.h"
#include "FileHandle.h"
#include "FileRequest.h"
USING_FILE_NAMESPACE
namespace {
FileService* gInstance = nsnull;
bool gShutdown = false;
} // anonymous namespace
FileService::FileService()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gInstance, "More than one instance!");
}
FileService::~FileService()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gInstance, "More than one instance!");
}
nsresult
FileService::Init()
{
mFileStorageInfos.Init();
nsresult rv;
mStreamTransportTarget =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIStreamTransportService> sts =
do_QueryInterface(mStreamTransportTarget);
rv = sts->RaiseThreadLimit();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
FileService::Cleanup()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsIThread* thread = NS_GetCurrentThread();
while (mFileStorageInfos.Count()) {
if (!NS_ProcessNextEvent(thread)) {
NS_ERROR("Failed to process next event!");
break;
}
}
// Make sure the service is still accessible while any generated callbacks
// are processed.
nsresult rv = NS_ProcessPendingEvents(thread);
NS_ENSURE_SUCCESS(rv, rv);
if (!mCompleteCallbacks.IsEmpty()) {
// Run all callbacks manually now.
for (PRUint32 index = 0; index < mCompleteCallbacks.Length(); index++) {
mCompleteCallbacks[index].mCallback->Run();
}
mCompleteCallbacks.Clear();
// And make sure they get processed.
rv = NS_ProcessPendingEvents(thread);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
// static
FileService*
FileService::GetOrCreate()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (gShutdown) {
NS_WARNING("Calling GetOrCreate() after shutdown!");
return nsnull;
}
if (!gInstance) {
nsRefPtr<FileService> service(new FileService);
nsresult rv = service->Init();
NS_ENSURE_SUCCESS(rv, nsnull);
nsCOMPtr<nsIObserverService> obs =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, nsnull);
rv = obs->AddObserver(service, "profile-before-change", false);
NS_ENSURE_SUCCESS(rv, nsnull);
// The observer service now owns us.
gInstance = service;
}
return gInstance;
}
// static
FileService*
FileService::Get()
{
// Does not return an owning reference.
return gInstance;
}
// static
void
FileService::Shutdown()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
gShutdown = true;
if (gInstance) {
if (NS_FAILED(gInstance->Cleanup())) {
NS_WARNING("Failed to shutdown file service!");
}
gInstance = nsnull;
}
}
// static
bool
FileService::IsShuttingDown()
{
return gShutdown;
}
nsresult
FileService::Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aLockedFile, "Null pointer!");
FileHandle* fileHandle = aLockedFile->mFileHandle;
if (fileHandle->mFileStorage->IsStorageInvalidated()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsISupports* storageId = fileHandle->mFileStorage->StorageId();
const nsAString& fileName = fileHandle->mFileName;
bool modeIsWrite = aLockedFile->mMode == LockedFile::READ_WRITE;
FileStorageInfo* fileStorageInfo;
if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) {
nsAutoPtr<FileStorageInfo> newFileStorageInfo(new FileStorageInfo());
mFileStorageInfos.Put(storageId, newFileStorageInfo);
fileStorageInfo = newFileStorageInfo.forget();
}
LockedFileQueue* existingLockedFileQueue =
fileStorageInfo->GetLockedFileQueue(aLockedFile);
if (existingLockedFileQueue) {
existingLockedFileQueue->Enqueue(aFileHelper);
return NS_OK;
}
bool lockedForReading = fileStorageInfo->IsFileLockedForReading(fileName);
bool lockedForWriting = fileStorageInfo->IsFileLockedForWriting(fileName);
if (modeIsWrite) {
if (!lockedForWriting) {
fileStorageInfo->LockFileForWriting(fileName);
}
}
else {
if (!lockedForReading) {
fileStorageInfo->LockFileForReading(fileName);
}
}
if (lockedForWriting || (lockedForReading && modeIsWrite)) {
fileStorageInfo->CreateDelayedEnqueueInfo(aLockedFile, aFileHelper);
}
else {
LockedFileQueue* lockedFileQueue =
fileStorageInfo->CreateLockedFileQueue(aLockedFile);
if (aFileHelper) {
// Enqueue() will queue the file helper if there's already something
// running. That can't fail, so no need to eventually remove
// fileStorageInfo from the hash table.
//
// If the file helper is free to run then AsyncRun() is called on the
// file helper. AsyncRun() is responsible for calling all necessary
// callbacks when something fails. We're propagating the error here,
// however there's no need to eventually remove fileStorageInfo from
// the hash table. Code behind AsyncRun() will take care of it. The last
// item in the code path is NotifyLockedFileCompleted() which removes
// fileStorageInfo from the hash table if there are no locked files for
// the file storage.
nsresult rv = lockedFileQueue->Enqueue(aFileHelper);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
void
FileService::NotifyLockedFileCompleted(LockedFile* aLockedFile)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aLockedFile, "Null pointer!");
FileHandle* fileHandle = aLockedFile->mFileHandle;
nsISupports* storageId = fileHandle->mFileStorage->StorageId();
FileStorageInfo* fileStorageInfo;
if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) {
NS_ERROR("We don't know anyting about this locked file?!");
return;
}
fileStorageInfo->RemoveLockedFileQueue(aLockedFile);
if (!fileStorageInfo->HasRunningLockedFiles()) {
mFileStorageInfos.Remove(storageId);
#ifdef DEBUG
storageId = nsnull;
#endif
// See if we need to fire any complete callbacks.
PRUint32 index = 0;
while (index < mCompleteCallbacks.Length()) {
if (MaybeFireCallback(mCompleteCallbacks[index])) {
mCompleteCallbacks.RemoveElementAt(index);
}
else {
index++;
}
}
}
}
bool
FileService::WaitForAllStoragesToComplete(
nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
nsIRunnable* aCallback)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aStorages.IsEmpty(), "No databases to wait on!");
NS_ASSERTION(aCallback, "Null pointer!");
StoragesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
callback->mCallback = aCallback;
callback->mStorages.SwapElements(aStorages);
if (MaybeFireCallback(*callback)) {
mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
}
return true;
}
void
FileService::AbortLockedFilesForStorage(nsIFileStorage* aFileStorage)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aFileStorage, "Null pointer!");
FileStorageInfo* fileStorageInfo;
if (!mFileStorageInfos.Get(aFileStorage->StorageId(), &fileStorageInfo)) {
return;
}
nsAutoTArray<nsRefPtr<LockedFile>, 10> lockedFiles;
fileStorageInfo->CollectRunningAndDelayedLockedFiles(aFileStorage,
lockedFiles);
for (PRUint32 index = 0; index < lockedFiles.Length(); index++) {
lockedFiles[index]->Abort();
}
}
bool
FileService::HasLockedFilesForStorage(nsIFileStorage* aFileStorage)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aFileStorage, "Null pointer!");
FileStorageInfo* fileStorageInfo;
if (!mFileStorageInfos.Get(aFileStorage->StorageId(), &fileStorageInfo)) {
return false;
}
return fileStorageInfo->HasRunningLockedFiles(aFileStorage);
}
NS_IMPL_ISUPPORTS1(FileService, nsIObserver)
NS_IMETHODIMP
FileService::Observe(nsISupports* aSubject, const char* aTopic,
const PRUnichar* aData)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!strcmp(aTopic, "profile-before-change"), "Wrong topic!");
Shutdown();
return NS_OK;
}
bool
FileService::MaybeFireCallback(StoragesCompleteCallback& aCallback)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
for (PRUint32 index = 0; index < aCallback.mStorages.Length(); index++) {
if (mFileStorageInfos.Get(aCallback.mStorages[index]->StorageId(),
nsnull)) {
return false;
}
}
aCallback.mCallback->Run();
return true;
}
FileService::LockedFileQueue::LockedFileQueue(LockedFile* aLockedFile)
: mLockedFile(aLockedFile)
{
NS_ASSERTION(aLockedFile, "Null pointer!");
}
NS_IMPL_THREADSAFE_ADDREF(FileService::LockedFileQueue)
NS_IMPL_THREADSAFE_RELEASE(FileService::LockedFileQueue)
nsresult
FileService::LockedFileQueue::Enqueue(FileHelper* aFileHelper)
{
mQueue.AppendElement(aFileHelper);
nsresult rv;
if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
rv = aFileHelper->AsyncRun(this);
}
else {
rv = ProcessQueue();
}
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
FileService::LockedFileQueue::OnFileHelperComplete(FileHelper* aFileHelper)
{
if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
PRInt32 index = mQueue.IndexOf(aFileHelper);
NS_ASSERTION(index != -1, "We don't know anything about this helper!");
mQueue.RemoveElementAt(index);
}
else {
NS_ASSERTION(mCurrentHelper == aFileHelper, "How can this happen?!");
mCurrentHelper = nsnull;
nsresult rv = ProcessQueue();
NS_ENSURE_SUCCESS(rv,);
}
}
nsresult
FileService::LockedFileQueue::ProcessQueue()
{
if (mQueue.IsEmpty() || mCurrentHelper) {
return NS_OK;
}
mCurrentHelper = mQueue[0];
mQueue.RemoveElementAt(0);
nsresult rv = mCurrentHelper->AsyncRun(this);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
FileService::LockedFileQueue*
FileService::FileStorageInfo::CreateLockedFileQueue(LockedFile* aLockedFile)
{
nsRefPtr<LockedFileQueue>* lockedFileQueue =
mLockedFileQueues.AppendElement();
*lockedFileQueue = new LockedFileQueue(aLockedFile);
return lockedFileQueue->get();
}
FileService::LockedFileQueue*
FileService::FileStorageInfo::GetLockedFileQueue(LockedFile* aLockedFile)
{
PRUint32 count = mLockedFileQueues.Length();
for (PRUint32 index = 0; index < count; index++) {
nsRefPtr<LockedFileQueue>& lockedFileQueue = mLockedFileQueues[index];
if (lockedFileQueue->mLockedFile == aLockedFile) {
return lockedFileQueue;
}
}
return nsnull;
}
void
FileService::FileStorageInfo::RemoveLockedFileQueue(LockedFile* aLockedFile)
{
PRUint32 lockedFileCount = mLockedFileQueues.Length();
// We can't just remove entries from lock hash tables, we have to rebuild
// them instead. Multiple LockedFile objects may lock the same file
// (one entry can represent multiple locks).
mFilesReading.Clear();
mFilesWriting.Clear();
for (PRUint32 index = 0, count = lockedFileCount; index < count; index++) {
LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
if (lockedFile == aLockedFile) {
NS_ASSERTION(count == lockedFileCount, "More than one match?!");
mLockedFileQueues.RemoveElementAt(index);
index--;
count--;
continue;
}
const nsAString& fileName = lockedFile->mFileHandle->mFileName;
if (lockedFile->mMode == LockedFile::READ_WRITE) {
if (!IsFileLockedForWriting(fileName)) {
LockFileForWriting(fileName);
}
}
else {
if (!IsFileLockedForReading(fileName)) {
LockFileForReading(fileName);
}
}
}
NS_ASSERTION(mLockedFileQueues.Length() == lockedFileCount - 1,
"Didn't find the locked file we were looking for!");
nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos;
delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos);
for (PRUint32 index = 0; index < delayedEnqueueInfos.Length(); index++) {
DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index];
if (NS_FAILED(gInstance->Enqueue(delayedEnqueueInfo.mLockedFile,
delayedEnqueueInfo.mFileHelper))) {
NS_WARNING("Enqueue failed!");
}
}
}
bool
FileService::FileStorageInfo::HasRunningLockedFiles(
nsIFileStorage* aFileStorage)
{
for (PRUint32 index = 0; index < mLockedFileQueues.Length(); index++) {
LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
return true;
}
}
return false;
}
FileService::DelayedEnqueueInfo*
FileService::FileStorageInfo::CreateDelayedEnqueueInfo(LockedFile* aLockedFile,
FileHelper* aFileHelper)
{
DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement();
info->mLockedFile = aLockedFile;
info->mFileHelper = aFileHelper;
return info;
}
void
FileService::FileStorageInfo::CollectRunningAndDelayedLockedFiles(
nsIFileStorage* aFileStorage,
nsTArray<nsRefPtr<LockedFile> >& aLockedFiles)
{
for (PRUint32 index = 0; index < mLockedFileQueues.Length(); index++) {
LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
aLockedFiles.AppendElement(lockedFile);
}
}
for (PRUint32 index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
LockedFile* lockedFile = mDelayedEnqueueInfos[index].mLockedFile;
if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
aLockedFiles.AppendElement(lockedFile);
}
}
}

196
dom/file/FileService.h Normal file
Просмотреть файл

@ -0,0 +1,196 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_fileservice_h__
#define mozilla_dom_file_fileservice_h__
#include "FileCommon.h"
#include "nsIObserver.h"
#include "nsClassHashtable.h"
#include "mozilla/dom/file/FileHelper.h"
#include "mozilla/dom/file/LockedFile.h"
BEGIN_FILE_NAMESPACE
class FileService : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
// Returns a non-owning reference!
static FileService*
GetOrCreate();
// Returns a non-owning reference!
static FileService*
Get();
static void
Shutdown();
// Returns true if we've begun the shutdown process.
static bool
IsShuttingDown();
nsresult
Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper);
void
NotifyLockedFileCompleted(LockedFile* aLockedFile);
bool
WaitForAllStoragesToComplete(nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
nsIRunnable* aCallback);
void
AbortLockedFilesForStorage(nsIFileStorage* aFileStorage);
bool
HasLockedFilesForStorage(nsIFileStorage* aFileStorage);
nsIEventTarget*
StreamTransportTarget()
{
NS_ASSERTION(mStreamTransportTarget, "This should never be null!");
return mStreamTransportTarget;
}
private:
class LockedFileQueue : public FileHelperListener
{
friend class FileService;
public:
NS_IMETHOD_(nsrefcnt)
AddRef();
NS_IMETHOD_(nsrefcnt)
Release();
inline nsresult
Enqueue(FileHelper* aFileHelper);
virtual void
OnFileHelperComplete(FileHelper* aFileHelper);
private:
inline
LockedFileQueue(LockedFile* aLockedFile);
nsresult
ProcessQueue();
nsAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
nsRefPtr<LockedFile> mLockedFile;
nsTArray<nsRefPtr<FileHelper> > mQueue;
nsRefPtr<FileHelper> mCurrentHelper;
};
struct DelayedEnqueueInfo
{
nsRefPtr<LockedFile> mLockedFile;
nsRefPtr<FileHelper> mFileHelper;
};
class FileStorageInfo
{
friend class FileService;
public:
inline LockedFileQueue*
CreateLockedFileQueue(LockedFile* aLockedFile);
inline LockedFileQueue*
GetLockedFileQueue(LockedFile* aLockedFile);
void
RemoveLockedFileQueue(LockedFile* aLockedFile);
bool
HasRunningLockedFiles()
{
return !mLockedFileQueues.IsEmpty();
}
inline bool
HasRunningLockedFiles(nsIFileStorage* aFileStorage);
inline DelayedEnqueueInfo*
CreateDelayedEnqueueInfo(LockedFile* aLockedFile, FileHelper* aFileHelper);
inline void
CollectRunningAndDelayedLockedFiles(
nsIFileStorage* aFileStorage,
nsTArray<nsRefPtr<LockedFile> >& aLockedFiles);
void
LockFileForReading(const nsAString& aFileName)
{
mFilesReading.PutEntry(aFileName);
}
void
LockFileForWriting(const nsAString& aFileName)
{
mFilesWriting.PutEntry(aFileName);
}
bool
IsFileLockedForReading(const nsAString& aFileName)
{
return mFilesReading.Contains(aFileName);
}
bool
IsFileLockedForWriting(const nsAString& aFileName)
{
return mFilesWriting.Contains(aFileName);
}
private:
FileStorageInfo()
{
mFilesReading.Init();
mFilesWriting.Init();
}
nsTArray<nsRefPtr<LockedFileQueue> > mLockedFileQueues;
nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos;
nsTHashtable<nsStringHashKey> mFilesReading;
nsTHashtable<nsStringHashKey> mFilesWriting;
};
struct StoragesCompleteCallback
{
nsTArray<nsCOMPtr<nsIFileStorage> > mStorages;
nsCOMPtr<nsIRunnable> mCallback;
};
FileService();
~FileService();
nsresult
Init();
nsresult
Cleanup();
bool
MaybeFireCallback(StoragesCompleteCallback& aCallback);
nsCOMPtr<nsIEventTarget> mStreamTransportTarget;
nsClassHashtable<nsISupportsHashKey, FileStorageInfo> mFileStorageInfos;
nsTArray<StoragesCompleteCallback> mCompleteCallbacks;
};
END_FILE_NAMESPACE
#endif /* mozilla_dom_file_fileservice_h__ */

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

@ -0,0 +1,397 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileStreamWrappers.h"
#include "nsIFileStorage.h"
#include "nsISeekableStream.h"
#include "nsIStandardFileStream.h"
#include "FileHelper.h"
USING_FILE_NAMESPACE
namespace {
class ProgressRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
ProgressRunnable(FileHelper* aFileHelper,
PRUint64 aProgress,
PRUint64 aProgressMax)
: mFileHelper(aFileHelper),
mProgress(aProgress),
mProgressMax(aProgressMax)
{
}
private:
nsRefPtr<FileHelper> mFileHelper;
PRUint64 mProgress;
PRUint64 mProgressMax;
};
class CloseRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
CloseRunnable(FileHelper* aFileHelper)
: mFileHelper(aFileHelper)
{ }
private:
nsRefPtr<FileHelper> mFileHelper;
};
class DestroyRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
DestroyRunnable(FileHelper* aFileHelper)
: mFileHelper(aFileHelper)
{ }
private:
nsRefPtr<FileHelper> mFileHelper;
};
} // anonymous namespace
FileStreamWrapper::FileStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags)
: mFileStream(aFileStream),
mFileHelper(aFileHelper),
mOffset(aOffset),
mLimit(aLimit),
mFlags(aFlags),
mFirstTime(true)
{
NS_ASSERTION(mFileStream, "Must have a file stream!");
NS_ASSERTION(mFileHelper, "Must have a file helper!");
}
FileStreamWrapper::~FileStreamWrapper()
{
if (mFlags & NOTIFY_DESTROY) {
if (NS_IsMainThread()) {
mFileHelper->OnStreamDestroy();
}
else {
nsCOMPtr<nsIRunnable> runnable =
new DestroyRunnable(mFileHelper);
nsresult rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
}
}
NS_IMPL_THREADSAFE_ISUPPORTS0(FileStreamWrapper)
FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags)
: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
{
mInputStream = do_QueryInterface(mFileStream);
NS_ASSERTION(mInputStream, "This should always succeed!");
}
NS_IMPL_ISUPPORTS_INHERITED1(FileInputStreamWrapper,
FileStreamWrapper,
nsIInputStream)
NS_IMETHODIMP
FileInputStreamWrapper::Close()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mFlags & NOTIFY_CLOSE) {
nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
mOffset = 0;
mLimit = 0;
return NS_OK;
}
NS_IMETHODIMP
FileInputStreamWrapper::Available(PRUint32* _retval)
{
// Performing sync IO on the main thread is generally not allowed.
// However, the input stream wrapper is also used to track reads performed by
// other APIs like FileReader, XHR, etc.
// In that case nsInputStreamChannel::OpenContentStream() calls Available()
// before setting the content length property. This property is not important
// to perform reads properly, so we can just return NS_BASE_STREAM_CLOSED
// here. It causes OpenContentStream() to set the content length property to
// zero.
if (NS_IsMainThread()) {
return NS_BASE_STREAM_CLOSED;
}
return mInputStream->Available(_retval);
}
NS_IMETHODIMP
FileInputStreamWrapper::Read(char* aBuf, PRUint32 aCount, PRUint32* _retval)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsresult rv;
if (mFirstTime) {
mFirstTime = false;
if (mOffset != LL_MAXUINT) {
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
if (seekable) {
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
NS_ENSURE_SUCCESS(rv, rv);
}
}
mOffset = 0;
}
PRUint64 max = mLimit - mOffset;
if (max == 0) {
*_retval = 0;
return NS_OK;
}
if (aCount > max) {
aCount = max;
}
rv = mInputStream->Read(aBuf, aCount, _retval);
NS_ENSURE_SUCCESS(rv, rv);
mOffset += *_retval;
if (mFlags & NOTIFY_PROGRESS) {
nsCOMPtr<nsIRunnable> runnable =
new ProgressRunnable(mFileHelper, mOffset, mLimit);
rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
return NS_OK;
}
NS_IMETHODIMP
FileInputStreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
PRUint32 aCount, PRUint32* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileInputStreamWrapper::IsNonBlocking(bool* _retval)
{
*_retval = false;
return NS_OK;
}
FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags)
: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
#ifdef DEBUG
, mWriteThread(nsnull)
#endif
{
mOutputStream = do_QueryInterface(mFileStream);
NS_ASSERTION(mOutputStream, "This should always succeed!");
}
NS_IMPL_ISUPPORTS_INHERITED1(FileOutputStreamWrapper,
FileStreamWrapper,
nsIOutputStream)
NS_IMETHODIMP
FileOutputStreamWrapper::Close()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsresult rv = NS_OK;
if (!mFirstTime) {
// We must flush buffers of the stream on the same thread on which we wrote
// some data.
nsCOMPtr<nsIStandardFileStream> sstream = do_QueryInterface(mFileStream);
if (sstream) {
rv = sstream->FlushBuffers();
if (NS_FAILED(rv)) {
NS_WARNING("Failed to flush buffers of the stream!");
}
}
NS_ASSERTION(PR_GetCurrentThread() == mWriteThread,
"Unsetting thread locals on wrong thread!");
mFileHelper->mFileStorage->UnsetThreadLocals();
}
if (mFlags & NOTIFY_CLOSE) {
nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
mOffset = 0;
mLimit = 0;
return rv;
}
NS_IMETHODIMP
FileOutputStreamWrapper::Write(const char* aBuf, PRUint32 aCount,
PRUint32* _retval)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsresult rv;
if (mFirstTime) {
mFirstTime = false;
#ifdef DEBUG
mWriteThread = PR_GetCurrentThread();
#endif
mFileHelper->mFileStorage->SetThreadLocals();
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mOutputStream);
if (seekable) {
if (mOffset == LL_MAXUINT) {
rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
}
else {
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
}
NS_ENSURE_SUCCESS(rv, rv);
}
mOffset = 0;
}
PRUint64 max = mLimit - mOffset;
if (max == 0) {
*_retval = 0;
return NS_OK;
}
if (aCount > max) {
aCount = max;
}
rv = mOutputStream->Write(aBuf, aCount, _retval);
NS_ENSURE_SUCCESS(rv, rv);
mOffset += *_retval;
if (mFlags & NOTIFY_PROGRESS) {
nsCOMPtr<nsIRunnable> runnable =
new ProgressRunnable(mFileHelper, mOffset, mLimit);
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
}
return NS_OK;
}
NS_IMETHODIMP
FileOutputStreamWrapper::Flush()
{
return NS_OK;
}
NS_IMETHODIMP
FileOutputStreamWrapper::WriteFrom(nsIInputStream* aFromStream,
PRUint32 aCount, PRUint32* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileOutputStreamWrapper::WriteSegments(nsReadSegmentFun aReader,
void* aClosure, PRUint32 aCount,
PRUint32* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileOutputStreamWrapper::IsNonBlocking(bool* _retval)
{
*_retval = false;
return NS_OK;
}
NS_IMPL_THREADSAFE_ISUPPORTS1(ProgressRunnable, nsIRunnable)
NS_IMETHODIMP
ProgressRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileHelper->OnStreamProgress(mProgress, mProgressMax);
mFileHelper = nsnull;
return NS_OK;
}
NS_IMPL_THREADSAFE_ISUPPORTS1(CloseRunnable, nsIRunnable)
NS_IMETHODIMP
CloseRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileHelper->OnStreamClose();
mFileHelper = nsnull;
return NS_OK;
}
NS_IMPL_THREADSAFE_ISUPPORTS1(DestroyRunnable, nsIRunnable)
NS_IMETHODIMP
DestroyRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileHelper->OnStreamDestroy();
mFileHelper = nsnull;
return NS_OK;
}

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

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_filestreamwrappers_h__
#define mozilla_dom_file_filestreamwrappers_h__
#include "FileCommon.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
BEGIN_FILE_NAMESPACE
class FileHelper;
class FileStreamWrapper : public nsISupports
{
public:
NS_DECL_ISUPPORTS
FileStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags);
virtual ~FileStreamWrapper();
enum {
NOTIFY_PROGRESS = 1 << 0,
NOTIFY_CLOSE = 1 << 1,
NOTIFY_DESTROY = 1 << 2
};
protected:
nsCOMPtr<nsISupports> mFileStream;
nsRefPtr<FileHelper> mFileHelper;
PRUint64 mOffset;
PRUint64 mLimit;
PRUint32 mFlags;
bool mFirstTime;
};
class FileInputStreamWrapper : public FileStreamWrapper,
public nsIInputStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIINPUTSTREAM
FileInputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags);
protected:
virtual ~FileInputStreamWrapper()
{ }
private:
nsCOMPtr<nsIInputStream> mInputStream;
};
class FileOutputStreamWrapper : public FileStreamWrapper,
public nsIOutputStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIOUTPUTSTREAM
FileOutputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags);
protected:
virtual ~FileOutputStreamWrapper()
{ }
private:
nsCOMPtr<nsIOutputStream> mOutputStream;
#ifdef DEBUG
void* mWriteThread;
#endif
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_filestreamwrappers_h__

1119
dom/file/LockedFile.cpp Normal file

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

155
dom/file/LockedFile.h Normal file
Просмотреть файл

@ -0,0 +1,155 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_lockedfile_h__
#define mozilla_dom_file_lockedfile_h__
#include "FileCommon.h"
#include "nsIDOMLockedFile.h"
#include "nsIRunnable.h"
#include "nsDOMEventTargetHelper.h"
class nsIInputStream;
BEGIN_FILE_NAMESPACE
class FileHandle;
class FileRequest;
class MetadataHelper;
class LockedFile : public nsDOMEventTargetHelper,
public nsIDOMLockedFile,
public nsIRunnable
{
friend class FinishHelper;
friend class FileService;
friend class FileHelper;
friend class MetadataHelper;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMLOCKEDFILE
NS_DECL_NSIRUNNABLE
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LockedFile, nsDOMEventTargetHelper)
enum Mode
{
READ_ONLY = 0,
READ_WRITE
};
enum RequestMode
{
NORMAL = 0, // Sequential
PARALLEL
};
enum ReadyState
{
INITIAL = 0,
LOADING,
FINISHING,
DONE
};
static already_AddRefed<LockedFile>
Create(FileHandle* aFileHandle,
Mode aMode,
RequestMode aRequestMode = NORMAL);
// nsIDOMEventTarget
virtual nsresult
PreHandleEvent(nsEventChainPreVisitor& aVisitor);
nsresult
CreateParallelStream(nsISupports** aStream);
nsresult
GetOrCreateStream(nsISupports** aStream);
bool
IsOpen() const;
bool
IsAborted() const
{
return mAborted;
}
FileHandle*
Handle() const
{
return mFileHandle;
}
nsresult
OpenInputStream(bool aWholeFile, PRUint64 aStart, PRUint64 aLength,
nsIInputStream** aResult);
private:
LockedFile();
~LockedFile();
void
OnNewRequest();
void
OnRequestFinished();
inline already_AddRefed<FileRequest>
GenerateFileRequest();
nsresult
WriteOrAppend(const jsval& aValue, JSContext* aCx,
nsIDOMFileRequest** _retval, bool aAppend);
nsresult
Finish();
nsRefPtr<FileHandle> mFileHandle;
ReadyState mReadyState;
Mode mMode;
RequestMode mRequestMode;
PRUint64 mLocation;
PRUint32 mPendingRequests;
NS_DECL_EVENT_HANDLER(complete)
NS_DECL_EVENT_HANDLER(abort)
NS_DECL_EVENT_HANDLER(error)
nsTArray<nsCOMPtr<nsISupports> > mParallelStreams;
nsCOMPtr<nsISupports> mStream;
bool mAborted;
bool mCreating;
};
class FinishHelper : public nsIRunnable
{
friend class LockedFile;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
private:
FinishHelper(LockedFile* aLockedFile);
~FinishHelper()
{ }
nsRefPtr<LockedFile> mLockedFile;
nsTArray<nsCOMPtr<nsISupports> > mParallelStreams;
nsCOMPtr<nsISupports> mStream;
bool mAborted;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_lockedfile_h__

58
dom/file/Makefile.in Normal file
Просмотреть файл

@ -0,0 +1,58 @@
# 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/.
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = dom
LIBRARY_NAME = domfile_s
XPIDL_MODULE = dom_file
LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
include $(topsrcdir)/dom/dom-config.mk
EXPORTS_NAMESPACES = mozilla/dom/file
CPPSRCS = \
AsyncHelper.cpp \
DOMFileHandle.cpp \
File.cpp \
FileHandle.cpp \
FileHelper.cpp \
FileRequest.cpp \
FileService.cpp \
FileStreamWrappers.cpp \
LockedFile.cpp \
MemoryStreams.cpp \
MetadataHelper.cpp \
$(NULL)
EXPORTS = \
nsIFileStorage.h \
$(NULL)
EXPORTS_mozilla/dom/file = \
DOMFileHandle.h \
File.h \
FileCommon.h \
FileHandle.h \
FileHelper.h \
FileService.h \
LockedFile.h \
$(NULL)
XPIDLSRCS = \
nsIDOMFileHandle.idl \
nsIDOMFileRequest.idl \
nsIDOMLockedFile.idl \
$(NULL)
TEST_DIRS += test
include $(topsrcdir)/config/rules.mk

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

@ -0,0 +1,90 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MemoryStreams.h"
#include "nsStreamUtils.h"
USING_FILE_NAMESPACE
// static
already_AddRefed<MemoryOutputStream>
MemoryOutputStream::Create(PRUint64 aSize)
{
NS_ASSERTION(aSize, "Passed zero size!");
NS_ENSURE_TRUE(aSize <= PR_UINT32_MAX, nsnull);
nsRefPtr<MemoryOutputStream> stream = new MemoryOutputStream();
char* dummy;
PRUint32 length = stream->mData.GetMutableData(&dummy, aSize, fallible_t());
NS_ENSURE_TRUE(length == aSize, nsnull);
return stream.forget();
}
NS_IMPL_THREADSAFE_ISUPPORTS1(MemoryOutputStream, nsIOutputStream)
NS_IMETHODIMP
MemoryOutputStream::Close()
{
mData.Truncate(mOffset);
return NS_OK;
}
NS_IMETHODIMP
MemoryOutputStream::Write(const char* aBuf, PRUint32 aCount, PRUint32* _retval)
{
return WriteSegments(NS_CopySegmentToBuffer, (char*)aBuf, aCount, _retval);
}
NS_IMETHODIMP
MemoryOutputStream::Flush()
{
return NS_OK;
}
NS_IMETHODIMP
MemoryOutputStream::WriteFrom(nsIInputStream* aFromStream, PRUint32 aCount,
PRUint32* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
MemoryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
PRUint32 aCount, PRUint32* _retval)
{
NS_ASSERTION(mData.Length() >= mOffset, "Bad stream state!");
PRUint32 maxCount = mData.Length() - mOffset;
if (maxCount == 0) {
*_retval = 0;
return NS_OK;
}
if (aCount > maxCount) {
aCount = maxCount;
}
nsresult rv = aReader(this, aClosure, mData.BeginWriting() + mOffset, 0,
aCount, _retval);
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION(*_retval <= aCount,
"Reader should not read more than we asked it to read!");
mOffset += *_retval;
}
return NS_OK;
}
NS_IMETHODIMP
MemoryOutputStream::IsNonBlocking(bool* _retval)
{
*_retval = false;
return NS_OK;
}

46
dom/file/MemoryStreams.h Normal file
Просмотреть файл

@ -0,0 +1,46 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_memorystreams_h__
#define mozilla_dom_file_memorystreams_h__
#include "FileCommon.h"
#include "nsIOutputStream.h"
BEGIN_FILE_NAMESPACE
class MemoryOutputStream : public nsIOutputStream
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOUTPUTSTREAM
static already_AddRefed<MemoryOutputStream>
Create(PRUint64 aSize);
const nsCString&
Data() const
{
return mData;
}
private:
MemoryOutputStream()
: mOffset(0)
{ }
virtual ~MemoryOutputStream()
{ }
nsCString mData;
PRUint64 mOffset;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_memorystreams_h__

106
dom/file/MetadataHelper.cpp Normal file
Просмотреть файл

@ -0,0 +1,106 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MetadataHelper.h"
#include "LockedFile.h"
USING_FILE_NAMESPACE
nsresult
MetadataHelper::DoAsyncRun(nsISupports* aStream)
{
bool readWrite = mLockedFile->mMode == LockedFile::READ_WRITE;
nsRefPtr<AsyncMetadataGetter> getter =
new AsyncMetadataGetter(aStream, mParams, readWrite);
nsresult rv = getter->AsyncWork(this, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
MetadataHelper::GetSuccessResult(JSContext* aCx,
jsval* aVal)
{
JSObject* obj = JS_NewObject(aCx, nsnull, nsnull, nsnull);
NS_ENSURE_TRUE(obj, NS_ERROR_OUT_OF_MEMORY);
if (mParams->SizeRequested()) {
jsval val;
if (mParams->Size() <= JSVAL_INT_MAX) {
val = INT_TO_JSVAL(mParams->Size());
}
else {
double size = mParams->Size();
if (!JS_NewNumberValue(aCx, size, &val)) {
return NS_ERROR_FAILURE;
}
}
if (!JS_DefineProperty(aCx, obj, "size", val, nsnull, nsnull,
JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
}
if (mParams->LastModifiedRequested()) {
double msec = mParams->LastModified();
JSObject *date = JS_NewDateObjectMsec(aCx, msec);
NS_ENSURE_TRUE(date, NS_ERROR_OUT_OF_MEMORY);
if (!JS_DefineProperty(aCx, obj, "lastModified", OBJECT_TO_JSVAL(date),
nsnull, nsnull, JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
}
*aVal = OBJECT_TO_JSVAL(obj);
return NS_OK;
}
nsresult
MetadataHelper::AsyncMetadataGetter::DoStreamWork(nsISupports* aStream)
{
nsresult rv;
if (mReadWrite) {
// Force a flush (so all pending writes are flushed to the disk and file
// metadata is updated too).
nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream);
NS_ASSERTION(ostream, "This should always succeed!");
rv = ostream->Flush();
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIFileMetadata> metadata = do_QueryInterface(aStream);
if (mParams->SizeRequested()) {
PRInt64 size;
rv = metadata->GetSize(&size);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(size >= 0, NS_ERROR_FAILURE);
mParams->mSize = PRUint64(size);
}
if (mParams->LastModifiedRequested()) {
PRInt64 lastModified;
rv = metadata->GetLastModified(&lastModified);
NS_ENSURE_SUCCESS(rv, rv);
mParams->mLastModified = lastModified;
}
return NS_OK;
}

122
dom/file/MetadataHelper.h Normal file
Просмотреть файл

@ -0,0 +1,122 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_file_metadatahelper_h__
#define mozilla_dom_file_metadatahelper_h__
#include "FileCommon.h"
#include "nsIFileStreams.h"
#include "DictionaryHelpers.h"
#include "AsyncHelper.h"
#include "FileHelper.h"
class nsIFileStream;
BEGIN_FILE_NAMESPACE
class MetadataHelper;
class MetadataParameters
{
friend class MetadataHelper;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataParameters)
nsresult
Init(JSContext* aCx, const jsval* aVal)
{
return mConfig.Init(aCx, aVal);
}
void
Init(bool aRequestSize, bool aRequestLastModified)
{
mConfig.size = aRequestSize;
mConfig.lastModified = aRequestLastModified;
}
bool
IsConfigured() const
{
return mConfig.size || mConfig.lastModified;
}
bool
SizeRequested() const
{
return mConfig.size;
}
bool
LastModifiedRequested() const
{
return mConfig.lastModified;
}
PRUint64
Size() const
{
return mSize;
}
PRInt64
LastModified() const
{
return mLastModified;
}
private:
DOMFileMetadataParameters mConfig;
PRUint64 mSize;
PRInt64 mLastModified;
};
class MetadataHelper : public FileHelper
{
public:
MetadataHelper(LockedFile* aLockedFile,
FileRequest* aFileRequest,
MetadataParameters* aParams)
: FileHelper(aLockedFile, aFileRequest),
mParams(aParams)
{ }
nsresult
DoAsyncRun(nsISupports* aStream);
nsresult
GetSuccessResult(JSContext* aCx, jsval* aVal);
protected:
class AsyncMetadataGetter : public AsyncHelper
{
public:
AsyncMetadataGetter(nsISupports* aStream, MetadataParameters* aParams,
bool aReadWrite)
: AsyncHelper(aStream),
mParams(aParams), mReadWrite(aReadWrite)
{ }
protected:
nsresult
DoStreamWork(nsISupports* aStream);
private:
nsRefPtr<MetadataParameters> mParams;
bool mReadWrite;
};
nsRefPtr<MetadataParameters> mParams;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_metadatahelper_h__

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

@ -0,0 +1,51 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
%{C++
namespace mozilla {
namespace dom {
namespace indexedDB {
class FileInfo;
}
}
}
%}
[ptr] native FileInfo(mozilla::dom::indexedDB::FileInfo);
interface nsIDOMEventListener;
interface nsIDOMFileRequest;
interface nsIDOMLockedFile;
[scriptable, builtinclass, uuid(0dc9c73c-4e44-4430-8898-85f61a70b1d2)]
interface nsIDOMFileHandle : nsISupports
{
readonly attribute DOMString name;
readonly attribute DOMString type;
// mode can be either "readonly" or "readwrite"
[optional_argc]
nsIDOMLockedFile
open([optional /* "readonly" */] in DOMString mode);
nsIDOMFileRequest
getFile();
[notxpcom]
long long
getFileId();
[notxpcom]
FileInfo
getFileInfo();
attribute nsIDOMEventListener onabort;
attribute nsIDOMEventListener onerror;
};

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

@ -0,0 +1,18 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIDOMDOMRequest.idl"
interface nsIDOMEventListener;
interface nsIDOMLockedFile;
[scriptable, builtinclass, uuid(fe06b66e-fede-4d44-ab3a-403f60d6b593)]
interface nsIDOMFileRequest : nsIDOMDOMRequest
{
readonly attribute nsIDOMLockedFile lockedFile;
attribute nsIDOMEventListener onprogress;
};

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

@ -0,0 +1,66 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIDOMEventListener;
interface nsIDOMFileHandle;
interface nsIDOMFileRequest;
dictionary DOMFileMetadataParameters
{
boolean size;
boolean lastModified;
};
[scriptable, builtinclass, uuid(63055eeb-cc19-468b-bafa-7b7961796340)]
interface nsIDOMLockedFile : nsISupports
{
readonly attribute nsIDOMFileHandle fileHandle;
// "readonly" or "readwrite"
readonly attribute DOMString mode;
readonly attribute boolean active;
attribute unsigned long long location;
[implicit_jscontext]
nsIDOMFileRequest
getMetadata(/* DOMFileMetadataParameters */
[optional /* all */] in jsval parameters);
[implicit_jscontext]
nsIDOMFileRequest
readAsArrayBuffer(in unsigned long long size);
nsIDOMFileRequest
readAsText(in unsigned long long size,
[optional] in DOMString encoding);
[implicit_jscontext]
nsIDOMFileRequest
write(in jsval value);
[implicit_jscontext]
nsIDOMFileRequest
append(in jsval value);
nsIDOMFileRequest
truncate();
nsIDOMFileRequest
flush();
void
abort();
attribute nsIDOMEventListener oncomplete;
attribute nsIDOMEventListener onabort;
attribute nsIDOMEventListener onerror;
};

55
dom/file/nsIFileStorage.h Normal file
Просмотреть файл

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsIFileStorage_h__
#define nsIFileStorage_h__
#include "nsISupports.h"
#define NS_FILESTORAGE_IID \
{0xbba9c2ff, 0x85c9, 0x47c1, \
{ 0xaf, 0xce, 0x0a, 0x7e, 0x6f, 0x21, 0x50, 0x95 } }
class nsIFileStorage : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_FILESTORAGE_IID)
virtual nsISupports*
StorageId() = 0;
virtual bool
IsStorageInvalidated() = 0;
virtual bool
IsStorageShuttingDown() = 0;
virtual void
SetThreadLocals() = 0;
virtual void
UnsetThreadLocals() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIFileStorage, NS_FILESTORAGE_IID)
#define NS_DECL_NSIFILESTORAGE \
virtual nsISupports* \
StorageId(); \
\
virtual bool \
IsStorageInvalidated(); \
\
virtual bool \
IsStorageShuttingDown(); \
\
virtual void \
SetThreadLocals(); \
\
virtual void \
UnsetThreadLocals();
#endif // nsIFileStorage_h__

33
dom/file/test/Makefile.in Normal file
Просмотреть файл

@ -0,0 +1,33 @@
# 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/.
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = dom/file/test
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
TEST_FILES = \
helpers.js \
test_append_read_data.html \
test_getFileId.html \
test_lockedfile_lifetimes.html \
test_lockedfile_lifetimes_nested.html \
test_lockedfile_ordering.html \
test_overlapping_lockedfiles.html \
test_progress_events.html \
test_readonly_lockedfiles.html \
test_request_readyState.html \
test_stream_tracking.html \
test_success_events_after_abort.html \
test_truncate.html \
test_write_read_data.html \
$(NULL)
libs:: $(TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)

220
dom/file/test/helpers.js Normal file
Просмотреть файл

@ -0,0 +1,220 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const IndexedDatabaseKey = "IDB";
const DeviceStorageKey = "DS";
var fileStorages = [
{ key: IndexedDatabaseKey }
// { key: DeviceStorageKey }
];
var utils = SpecialPowers.getDOMWindowUtils(window);
var testGenerator = testSteps();
function runTest()
{
allowIndexedDB();
allowUnlimitedQuota();
SimpleTest.waitForExplicitFinish();
testGenerator.next();
}
function finishTest()
{
resetUnlimitedQuota();
resetIndexedDB();
SimpleTest.executeSoon(function() {
testGenerator.close();
SimpleTest.finish();
});
}
function grabEventAndContinueHandler(event)
{
testGenerator.send(event);
}
function continueToNextStep()
{
SimpleTest.executeSoon(function() {
testGenerator.next();
});
}
function errorHandler(event)
{
ok(false, "indexedDB error, code " + event.target.errorCode);
finishTest();
}
function unexpectedSuccessHandler()
{
ok(false, "Got success, but did not expect it!");
finishTest();
}
function ExpectError(name)
{
this._name = name;
}
ExpectError.prototype = {
handleEvent: function(event)
{
is(event.type, "error", "Got an error event");
is(event.target.error.name, this._name, "Expected error was thrown.");
event.preventDefault();
event.stopPropagation();
grabEventAndContinueHandler(event);
}
};
function addPermission(type, allow, url)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
let uri;
if (url) {
uri = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.newURI(url, null, null);
}
else {
uri = SpecialPowers.getDocumentURIObject(window.document);
}
let permission;
if (allow) {
permission = Components.interfaces.nsIPermissionManager.ALLOW_ACTION;
}
else {
permission = Components.interfaces.nsIPermissionManager.DENY_ACTION;
}
Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager)
.add(uri, type, permission);
}
function removePermission(permission, url)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
let uri;
if (url) {
uri = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.newURI(url, null, null);
}
else {
uri = SpecialPowers.getDocumentURIObject(window.document);
}
Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager)
.remove(uri.host, permission);
}
function allowIndexedDB(url)
{
addPermission("indexedDB", true, url);
}
function resetIndexedDB(url)
{
removePermission("indexedDB", url);
}
function allowUnlimitedQuota(url)
{
addPermission("indexedDB-unlimited", true, url);
}
function resetUnlimitedQuota(url)
{
removePermission("indexedDB-unlimited", url);
}
function getFileHandle(fileStorageKey, name)
{
var requestService = SpecialPowers.getDOMRequestService();
var request = requestService.createRequest(window);
switch (fileStorageKey) {
case IndexedDatabaseKey:
var dbname = window.location.pathname;
mozIndexedDB.open(dbname, 1).onsuccess = function(event) {
var db = event.target.result;
db.mozCreateFileHandle(name).onsuccess = function(event) {
var fileHandle = event.target.result;
requestService.fireSuccess(request, fileHandle);
}
}
break;
case DeviceStorageKey:
var dbname = window.location.pathname;
mozIndexedDB.open(dbname, 1).onsuccess = function(event) {
var db = event.target.result;
db.mozCreateFileHandle(name).onsuccess = function(event) {
var fileHandle = event.target.result;
requestService.fireSuccess(request, fileHandle);
}
}
break;
}
return request;
}
function getBuffer(size)
{
let buffer = new ArrayBuffer(size);
is(buffer.byteLength, size, "Correct byte length");
return buffer;
}
function getRandomBuffer(size)
{
let buffer = getBuffer(size);
let view = new Uint8Array(buffer);
for (let i = 0; i < size; i++) {
view[i] = parseInt(Math.random() * 255)
}
return buffer;
}
function compareBuffers(buffer1, buffer2)
{
if (buffer1.byteLength != buffer2.byteLength) {
return false;
}
let view1 = new Uint8Array(buffer1);
let view2 = new Uint8Array(buffer2);
for (let i = 0; i < buffer1.byteLength; i++) {
if (view1[i] != view2[i]) {
return false;
}
}
return true;
}
function getBlob(type, buffer)
{
return utils.getBlob([buffer], {type: type});
}
function getRandomBlob(size)
{
return getBlob("binary/random", getRandomBuffer(size));
}
function getFileId(blob)
{
return utils.getFileId(blob);
}

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

@ -0,0 +1,103 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const maxLocation = 18446744073709552000;
var testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis.";
for (let i = 0; i < 5; i++) {
testString += testString;
}
var testBuffer = getRandomBuffer(100000);
var testBlob = getBlob("binary/random", testBuffer);
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let location = 0;
let lockedFile = fileHandle.open("readwrite");
is(lockedFile.location, location, "Correct location");
request = lockedFile.append(testString);
is(lockedFile.location, maxLocation, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location = 0;
request = lockedFile.readAsText(testString.length);
location += testString.length
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let resultString = event.target.result;
ok(resultString == testString, "Correct string data");
request = lockedFile.append(testBuffer);
is(lockedFile.location, maxLocation, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location = location;
request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
location += testBuffer.byteLength;
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data");
request = lockedFile.append(testBlob);
is(lockedFile.location, maxLocation, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location = location;
request = lockedFile.readAsArrayBuffer(testBlob.size);
location += testBlob.size;
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data");
request = lockedFile.getMetadata({ size: true });
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let result = event.target.result;
is(result.size, location, "Correct size");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,31 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
let id = getFileId(null);
ok(id == -1, "Correct id");
id = getFileId(getRandomBlob(100));
ok(id == -1, "Correct id");
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,49 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open();
continueToNextStep();
yield;
try {
lockedFile.getMetadata({ size: true });
ok(false, "Should have thrown!");
}
catch (e) {
ok(e instanceof DOMException, "Got exception.");
is(e.name, "LockedFileInactiveError", "Good error.");
is(e.code, 0, "Good error code.");
}
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,61 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open();
let lockedFile2;
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
let thread = Components.classes["@mozilla.org/thread-manager;1"]
.getService()
.currentThread;
let eventHasRun;
thread.dispatch(function() {
eventHasRun = true;
lockedFile2 = fileHandle.open();
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
while (!eventHasRun) {
thread.processNextEvent(false);
}
ok(lockedFile2, "Non-null lockedFile2");
continueToNextStep();
yield;
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,54 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile1 = fileHandle.open("readwrite");
let lockedFile2 = fileHandle.open("readwrite");
let request1 = lockedFile2.write("2");
let request2 = lockedFile1.write("1");
lockedFile1.oncomplete = grabEventAndContinueHandler;
lockedFile2.oncomplete = grabEventAndContinueHandler;
yield;
yield;
let lockedFile3 = fileHandle.open("readonly");
let request3 = lockedFile3.readAsText(1);
request3.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result, "2", "Locked files were ordered properly.");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,65 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
for (let i = 0; i < 50; i++) {
let stepNumber = 0;
request = fileHandle.open("readwrite").append("string1");
request.onsuccess = function(event) {
is(stepNumber, 1, "This callback came first");
stepNumber++;
event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
}
request = fileHandle.open("readwrite").append("string2");
request.onsuccess = function(event) {
is(stepNumber, 2, "This callback came second");
stepNumber++;
event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
}
request = fileHandle.open("readwrite").append("string3");
request.onsuccess = function(event) {
is(stepNumber, 3, "This callback came third");
stepNumber++;
event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
}
stepNumber++;
yield; yield; yield;;
is(stepNumber, 4, "All callbacks received");
}
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,70 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
var testBuffer = getRandomBuffer(100000);
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open("readwrite");
let sum = 0;
request = lockedFile.write(testBuffer);
request.onprogress = function(event) {
let loaded = event.loaded;
let total = event.total;
ok(loaded >= 0 && loaded <= total, "Correct loaded progress");
is(total, testBuffer.byteLength, "Correct total progress");
sum += event.loaded - sum;
}
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(sum, testBuffer.byteLength, "Correct loaded progress sum");
sum = 0;
lockedFile.location = 0;
request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
request.onprogress = function(event) {
let loaded = event.loaded;
let total = event.total;
ok(loaded >= 0 && loaded <= total, "Correct loaded progress");
is(total, testBuffer.byteLength, "Correct total progress");
sum += event.loaded - sum;
}
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(sum, testBuffer.byteLength, "Correct loaded progress sum");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,73 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
request = fileHandle.open("readwrite").write({});
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.lockedFile.mode, "readwrite", "Correct mode");
try {
fileHandle.open().write({});
ok(false, "Writing to a readonly locked file should fail!");
}
catch (e) {
ok(true, "Writing to a readonly locked file failed");
}
try {
fileHandle.open().append({});
ok(false, "Appending to a readonly locked file should fail!");
}
catch (e) {
ok(true, "Appending to a readonly locked file failed");
}
try {
fileHandle.open().truncate({});
ok(false, "Truncating a readonly locked file should fail!");
}
catch (e) {
ok(true, "Truncating a readonly locked file failed");
}
try {
fileHandle.open().flush({});
ok(false, "Flushing a readonly locked file should fail!");
}
catch (e) {
ok(true, "Flushing a readonly locked file failed");
}
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,57 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
is(request.readyState, "pending", "Correct readyState");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
is(request.readyState, "done", "Correct readyState");
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open("readwrite");
request = lockedFile.write("string");
is(request.readyState, "pending", "Correct readyState");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(request.readyState, "done", "Correct readyState");
lockedFile.location = 0;
request = lockedFile.readAsText(6);
request.onsuccess = grabEventAndContinueHandler;
is(request.readyState, "pending", "Correct readyState");
event = yield;
ok(event.target.result, "Got something");
is(request.readyState, "done", "Correct readyState");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,86 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
var testBuffer = getRandomBuffer(100000);
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open("readwrite");
request = lockedFile.write(testBuffer);
request.onsuccess = grabEventAndContinueHandler;
event = yield;
request = fileHandle.getFile();
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let file = event.target.result;
let resultBuffer1;
let resultBuffer2;
let reader1 = new FileReader();
reader1.readAsArrayBuffer(file);
reader1.onerror = errorHandler;
reader1.onload = function(event)
{
resultBuffer1 = event.target.result;
let reader = new FileReader();
try {
reader.readAsArrayBuffer(file);
ok(false, "Should have thrown!");
}
catch (e) {
ok(e instanceof DOMException, "Got exception.");
is(e.name, "LockedFileInactiveError", "Good error.");
is(e.code, 0, "Good error code.");
}
}
let reader2 = new FileReader();
reader2.readAsArrayBuffer(file);
reader2.onerror = errorHandler;
reader2.onload = function(event)
{
resultBuffer2 = event.target.result;
}
lockedFile = event.target.lockedFile;
lockedFile.oncomplete = grabEventAndContinueHandler;
yield;
ok(compareBuffers(resultBuffer1, testBuffer), "Correct data");
ok(compareBuffers(resultBuffer2, testBuffer), "Correct data");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,66 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open();
lockedFile.oncomplete = unexpectedSuccessHandler;
lockedFile.onabort = grabEventAndContinueHandler;
let sawError = false;
request = lockedFile.getMetadata({ size: true });
request.onsuccess = unexpectedSuccessHandler;
request.onerror = function(event) {
is(event.target.error.name, "AbortError", "Good error");
sawError = true;
event.stopPropagation();
}
lockedFile.abort();
event = yield;
is(event.type, "abort", "Got abort event");
is(sawError, true, "Saw getMetadata() error");
// Make sure the success event isn't queued somehow.
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var thread = Components.classes["@mozilla.org/thread-manager;1"]
.getService(Components.interfaces.nsIThreadManager)
.currentThread;
while (thread.hasPendingEvents()) {
thread.processNextEvent(false);
}
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,61 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
var testBuffer = getRandomBuffer(100000);
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.bin");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open("readwrite");
request = lockedFile.write(testBuffer);
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(lockedFile.location, 100000, "Correct location");
for (let i = 0; i < 10; i++) {
let location = lockedFile.location - 10000
lockedFile.location = location;
request = lockedFile.truncate();
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(lockedFile.location, location, "Correct location");
request = lockedFile.getMetadata({ size: true });
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result.size, location, "Correct size");
}
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,101 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
var testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis.";
for (let i = 0; i < 5; i++) {
testString += testString;
}
var testBuffer = getRandomBuffer(100000);
var testBlob = getBlob("binary/random", testBuffer);
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let location = 0;
let lockedFile = fileHandle.open("readwrite");
is(lockedFile.location, location, "Correct location");
request = lockedFile.write(testString);
location += testString.length;
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location = 0;
request = lockedFile.readAsText(testString.length);
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let resultString = event.target.result;
ok(resultString == testString, "Correct string data");
request = lockedFile.write(testBuffer);
location += testBuffer.byteLength;
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location -= testBuffer.byteLength;
request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data");
request = lockedFile.write(testBlob);
location += testBlob.size;
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location -= testBlob.size;
request = lockedFile.readAsArrayBuffer(testBlob.size);
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data");
request = lockedFile.getMetadata({ size: true });
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let result = event.target.result;
is(result.size, location, "Correct size");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,321 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileStream.h"
#include "nsIFile.h"
#include "nsThreadUtils.h"
#include "test_quota.h"
USING_INDEXEDDB_NAMESPACE
NS_IMPL_THREADSAFE_ADDREF(FileStream)
NS_IMPL_THREADSAFE_RELEASE(FileStream)
NS_INTERFACE_MAP_BEGIN(FileStream)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardFileStream)
NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
NS_INTERFACE_MAP_ENTRY(nsIStandardFileStream)
NS_INTERFACE_MAP_ENTRY(nsIFileMetadata)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
FileStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
{
// TODO: Add support for 64 bit file sizes, bug 752431
NS_ENSURE_TRUE(aOffset <= PR_INT32_MAX, NS_ERROR_INVALID_ARG);
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
int whence;
switch (aWhence) {
case nsISeekableStream::NS_SEEK_SET:
whence = SEEK_SET;
break;
case nsISeekableStream::NS_SEEK_CUR:
whence = SEEK_CUR;
break;
case nsISeekableStream::NS_SEEK_END:
whence = SEEK_END;
break;
default:
return NS_ERROR_INVALID_ARG;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fseek(mQuotaFile, aOffset, whence);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
NS_IMETHODIMP
FileStream::Tell(PRInt64* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
long rc = sqlite3_quota_ftell(mQuotaFile);
NS_ENSURE_TRUE(rc >= 0, NS_BASE_STREAM_OSERROR);
*aResult = rc;
return NS_OK;
}
NS_IMETHODIMP
FileStream::SetEOF()
{
PRInt64 pos;
nsresult rv = Tell(&pos);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_ftruncate(mQuotaFile, pos);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
NS_IMETHODIMP
FileStream::Close()
{
CleanUpOpen();
if (mQuotaFile) {
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fclose(mQuotaFile);
mQuotaFile = nsnull;
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
}
return NS_OK;
}
NS_IMETHODIMP
FileStream::Available(PRUint32* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
long rc = sqlite3_quota_file_available(mQuotaFile);
NS_ENSURE_TRUE(rc >= 0, NS_BASE_STREAM_OSERROR);
*aResult = rc;
return NS_OK;
}
NS_IMETHODIMP
FileStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
size_t bytesRead = sqlite3_quota_fread(aBuf, 1, aCount, mQuotaFile);
if (bytesRead < aCount && sqlite3_quota_ferror(mQuotaFile)) {
return NS_BASE_STREAM_OSERROR;
}
*aResult = bytesRead;
return NS_OK;
}
NS_IMETHODIMP
FileStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
PRUint32 aCount, PRUint32* aResult)
{
NS_NOTREACHED("Don't call me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileStream::IsNonBlocking(bool *aNonBlocking)
{
*aNonBlocking = false;
return NS_OK;
}
NS_IMETHODIMP
FileStream::Write(const char* aBuf, PRUint32 aCount, PRUint32 *aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
size_t bytesWritten = sqlite3_quota_fwrite(aBuf, 1, aCount, mQuotaFile);
if (bytesWritten < aCount) {
return NS_BASE_STREAM_OSERROR;
}
*aResult = bytesWritten;
return NS_OK;
}
NS_IMETHODIMP
FileStream::Flush()
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fflush(mQuotaFile, 1);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
NS_IMETHODIMP
FileStream::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileStream::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
{
NS_NOTREACHED("Don't call me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileStream::Init(nsIFile* aFile, const nsAString& aMode, PRInt32 aFlags)
{
NS_ASSERTION(!mQuotaFile && !mDeferredOpen, "Already initialized!");
nsresult rv = aFile->GetPath(mFilePath);
NS_ENSURE_SUCCESS(rv, rv);
mMode = aMode;
mFlags = aFlags;
if (mFlags & nsIStandardFileStream::FLAGS_DEFER_OPEN) {
mDeferredOpen = true;
return NS_OK;
}
return DoOpen();
}
NS_IMETHODIMP
FileStream::GetSize(PRInt64* _retval)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
// TODO: Use sqlite3_quota_file_size() here, bug 760783
PRInt64 rc = sqlite3_quota_file_truesize(mQuotaFile);
NS_ASSERTION(rc >= 0, "The file is not under quota management!");
*_retval = rc;
return NS_OK;
}
NS_IMETHODIMP
FileStream::GetLastModified(PRInt64* _retval)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
time_t mtime;
int rc = sqlite3_quota_file_mtime(mQuotaFile, &mtime);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
*_retval = mtime * PR_MSEC_PER_SEC;
return NS_OK;
}
NS_IMETHODIMP
FileStream::FlushBuffers()
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fflush(mQuotaFile, 0);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
nsresult
FileStream::DoOpen()
{
NS_ASSERTION(!mFilePath.IsEmpty(), "Must have a file path");
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
quota_FILE* quotaFile =
sqlite3_quota_fopen(NS_ConvertUTF16toUTF8(mFilePath).get(),
NS_ConvertUTF16toUTF8(mMode).get());
CleanUpOpen();
if (!quotaFile) {
return NS_BASE_STREAM_OSERROR;
}
mQuotaFile = quotaFile;
return NS_OK;
}

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