зеркало из https://github.com/mozilla/gecko-dev.git
Merge the last PGO-green inbound changeset to m-c.
This commit is contained in:
Коммит
2d4e83283e
|
@ -2,7 +2,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/. */
|
||||
|
||||
#virtual-cursor-box {
|
||||
#virtual-cursor-box {
|
||||
position: fixed;
|
||||
border: 1px solid orange;
|
||||
pointer-events: none;
|
||||
|
@ -11,7 +11,7 @@
|
|||
box-shadow: 1px 1px 1px #444;
|
||||
}
|
||||
|
||||
#virtual-cursor-inset {
|
||||
#virtual-cursor-inset {
|
||||
border-radius: 1px;
|
||||
box-shadow: inset 1px 1px 1px #444;
|
||||
display: block;
|
||||
|
@ -19,4 +19,12 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
#accessfu-glass {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ Cu.import('resource://gre/modules/Services.jsm');
|
|||
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/Presenters.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/VirtualCursorController.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/TouchAdapter.jsm');
|
||||
|
||||
const ACCESSFU_DISABLE = 0;
|
||||
const ACCESSFU_ENABLE = 1;
|
||||
|
@ -44,8 +45,24 @@ var AccessFu = {
|
|||
this.prefsBranch.addObserver('activate', this, false);
|
||||
this.prefsBranch.addObserver('explorebytouch', this, false);
|
||||
|
||||
if (Utils.MozBuildApp == 'mobile/android')
|
||||
Services.obs.addObserver(this, 'Accessibility:Settings', false);
|
||||
this.touchAdapter = TouchAdapter;
|
||||
|
||||
switch(Utils.MozBuildApp) {
|
||||
case 'mobile/android':
|
||||
Services.obs.addObserver(this, 'Accessibility:Settings', false);
|
||||
this.touchAdapter = AndroidTouchAdapter;
|
||||
break;
|
||||
case 'b2g':
|
||||
aWindow.addEventListener(
|
||||
'ContentStart',
|
||||
(function(event) {
|
||||
let content = aWindow.shell.contentBrowser.contentWindow;
|
||||
content.addEventListener('mozContentEvent', this, false, true);
|
||||
}).bind(this), false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this._processPreferences();
|
||||
},
|
||||
|
@ -60,6 +77,13 @@ var AccessFu = {
|
|||
this._enabled = true;
|
||||
|
||||
Logger.info('enable');
|
||||
|
||||
// Add stylesheet
|
||||
let stylesheetURL = 'chrome://global/content/accessibility/AccessFu.css';
|
||||
this.stylesheet = this.chromeWin.document.createProcessingInstruction(
|
||||
'xml-stylesheet', 'href="' + stylesheetURL + '" type="text/css"');
|
||||
this.chromeWin.document.insertBefore(this.stylesheet, this.chromeWin.document.firstChild);
|
||||
|
||||
this.addPresenter(new VisualPresenter());
|
||||
|
||||
// Implicitly add the Android presenter on Android.
|
||||
|
@ -88,6 +112,8 @@ var AccessFu = {
|
|||
|
||||
Logger.info('disable');
|
||||
|
||||
this.chromeWin.document.removeChild(this.stylesheet);
|
||||
|
||||
this.presenters.forEach(function(p) { p.detach(); });
|
||||
this.presenters = [];
|
||||
|
||||
|
@ -130,8 +156,10 @@ var AccessFu = {
|
|||
else
|
||||
this._disable();
|
||||
|
||||
VirtualCursorController.exploreByTouch = ebtPref == ACCESSFU_ENABLE;
|
||||
Logger.info('Explore by touch:', VirtualCursorController.exploreByTouch);
|
||||
if (ebtPref == ACCESSFU_ENABLE)
|
||||
this.touchAdapter.attach(this.chromeWin);
|
||||
else
|
||||
this.touchAdapter.detach(this.chromeWin);
|
||||
},
|
||||
|
||||
addPresenter: function addPresenter(presenter) {
|
||||
|
@ -192,6 +220,14 @@ var AccessFu = {
|
|||
this.presenters.forEach(function(p) { p.viewportChanged(); });
|
||||
break;
|
||||
}
|
||||
case 'mozContentEvent':
|
||||
{
|
||||
if (aEvent.detail.type == 'accessibility-screenreader') {
|
||||
let pref = aEvent.detail.enabled + 0;
|
||||
this._processPreferences(pref, pref);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -117,12 +117,6 @@ VisualPresenter.prototype = {
|
|||
attach: function VisualPresenter_attach(aWindow) {
|
||||
this.chromeWin = aWindow;
|
||||
|
||||
// Add stylesheet
|
||||
let stylesheetURL = 'chrome://global/content/accessibility/AccessFu.css';
|
||||
this.stylesheet = aWindow.document.createProcessingInstruction(
|
||||
'xml-stylesheet', 'href="' + stylesheetURL + '" type="text/css"');
|
||||
aWindow.document.insertBefore(this.stylesheet, aWindow.document.firstChild);
|
||||
|
||||
// Add highlight box
|
||||
this.highlightBox = this.chromeWin.document.
|
||||
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
|
@ -138,7 +132,6 @@ VisualPresenter.prototype = {
|
|||
},
|
||||
|
||||
detach: function VisualPresenter_detach() {
|
||||
this.chromeWin.document.removeChild(this.stylesheet);
|
||||
this.highlightBox.parentNode.removeChild(this.highlightBox);
|
||||
this.highlightBox = this.stylesheet = null;
|
||||
},
|
||||
|
|
|
@ -0,0 +1,402 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
var EXPORTED_SYMBOLS = ['TouchAdapter', 'AndroidTouchAdapter'];
|
||||
|
||||
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
||||
|
||||
// We should not be emitting explore events more than 10 times a second.
|
||||
// It is granular enough to feel natural, and it does not hammer the CPU.
|
||||
const EXPLORE_THROTTLE = 100;
|
||||
|
||||
var TouchAdapter = {
|
||||
// minimal swipe distance in inches
|
||||
SWIPE_MIN_DISTANCE: 0.4,
|
||||
|
||||
// maximum duration of swipe
|
||||
SWIPE_MAX_DURATION: 400,
|
||||
|
||||
// how straight does a swipe need to be
|
||||
SWIPE_DIRECTNESS: 1.2,
|
||||
|
||||
// maximum consecutive
|
||||
MAX_CONSECUTIVE_GESTURE_DELAY: 400,
|
||||
|
||||
// delay before tap turns into dwell
|
||||
DWELL_THRESHOLD: 500,
|
||||
|
||||
// delay before distinct dwell events
|
||||
DWELL_REPEAT_DELAY: 300,
|
||||
|
||||
// maximum distance the mouse could move during a tap in inches
|
||||
TAP_MAX_RADIUS: 0.2,
|
||||
|
||||
attach: function TouchAdapter_attach(aWindow) {
|
||||
if (this.chromeWin)
|
||||
return;
|
||||
|
||||
Logger.info('TouchAdapter.attach');
|
||||
|
||||
this.chromeWin = aWindow;
|
||||
this._touchPoints = {};
|
||||
this._dwellTimeout = 0;
|
||||
this._prevGestures = {};
|
||||
this._lastExploreTime = 0;
|
||||
this._dpi = this.chromeWin.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils).displayDPI;
|
||||
|
||||
this.glass = this.chromeWin.document.
|
||||
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
this.glass.id = 'accessfu-glass';
|
||||
this.chromeWin.document.documentElement.appendChild(this.glass);
|
||||
|
||||
this.glass.addEventListener('touchend', this, true, true);
|
||||
this.glass.addEventListener('touchmove', this, true, true);
|
||||
this.glass.addEventListener('touchstart', this, true, true);
|
||||
|
||||
if (Utils.OS != 'Android')
|
||||
Mouse2Touch.attach(aWindow);
|
||||
},
|
||||
|
||||
detach: function TouchAdapter_detach(aWindow) {
|
||||
if (!this.chromeWin)
|
||||
return;
|
||||
|
||||
Logger.info('TouchAdapter.detach');
|
||||
|
||||
this.glass.removeEventListener('touchend', this, true, true);
|
||||
this.glass.removeEventListener('touchmove', this, true, true);
|
||||
this.glass.removeEventListener('touchstart', this, true, true);
|
||||
this.glass.parentNode.removeChild(this.glass);
|
||||
|
||||
if (Utils.OS != 'Android')
|
||||
Mouse2Touch.detach(aWindow);
|
||||
|
||||
delete this.chromeWin;
|
||||
},
|
||||
|
||||
handleEvent: function TouchAdapter_handleEvent(aEvent) {
|
||||
let touches = aEvent.changedTouches;
|
||||
switch (aEvent.type) {
|
||||
case 'touchstart':
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
let touch = touches[i];
|
||||
let touchPoint = new TouchPoint(touch, aEvent.timeStamp, this._dpi);
|
||||
this._touchPoints[touch.identifier] = touchPoint;
|
||||
this._lastExploreTime = aEvent.timeStamp + this.SWIPE_MAX_DURATION;
|
||||
}
|
||||
this._dwellTimeout = this.chromeWin.setTimeout(
|
||||
(function () {
|
||||
this.compileAndEmit(aEvent.timeStamp + this.DWELL_THRESHOLD);
|
||||
}).bind(this), this.DWELL_THRESHOLD);
|
||||
break;
|
||||
case 'touchmove':
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
let touch = touches[i];
|
||||
let touchPoint = this._touchPoints[touch.identifier];
|
||||
touchPoint.update(touch, aEvent.timeStamp);
|
||||
}
|
||||
if (aEvent.timeStamp - this._lastExploreTime >= EXPLORE_THROTTLE) {
|
||||
this.compileAndEmit(aEvent.timeStamp);
|
||||
this._lastExploreTime = aEvent.timeStamp;
|
||||
}
|
||||
break;
|
||||
case 'touchend':
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
let touch = touches[i];
|
||||
let touchPoint = this._touchPoints[touch.identifier];
|
||||
touchPoint.update(touch, aEvent.timeStamp);
|
||||
touchPoint.finish();
|
||||
}
|
||||
this.compileAndEmit(aEvent.timeStamp);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
cleanupTouches: function cleanupTouches() {
|
||||
for (var identifier in this._touchPoints) {
|
||||
if (!this._touchPoints[identifier].done)
|
||||
continue;
|
||||
|
||||
delete this._touchPoints[identifier];
|
||||
}
|
||||
},
|
||||
|
||||
compile: function TouchAdapter_compile(aTime) {
|
||||
let multiDetails = {};
|
||||
|
||||
// Compound multiple simultaneous touch gestures.
|
||||
for (let identifier in this._touchPoints) {
|
||||
let touchPoint = this._touchPoints[identifier];
|
||||
let details = touchPoint.compile(aTime);
|
||||
|
||||
if (!details)
|
||||
continue;
|
||||
|
||||
details.touches = [identifier];
|
||||
|
||||
let otherTouches = multiDetails[details.type];
|
||||
if (otherTouches) {
|
||||
otherTouches.touches.push(identifier);
|
||||
otherTouches.startTime =
|
||||
Math.min(otherTouches.startTime, touchPoint.startTime);
|
||||
} else {
|
||||
details.startTime = touchPoint.startTime;
|
||||
details.endTime = aTime;
|
||||
multiDetails[details.type] = details;
|
||||
}
|
||||
}
|
||||
|
||||
// Compound multiple consecutive touch gestures.
|
||||
for each (let details in multiDetails) {
|
||||
let idhash = details.touches.slice().sort().toString();
|
||||
let prevGesture = this._prevGestures[idhash];
|
||||
|
||||
if (prevGesture) {
|
||||
// The time delta is calculated as the period between the end of the
|
||||
// last gesture and the start of this one.
|
||||
let timeDelta = details.startTime - prevGesture.endTime;
|
||||
if (timeDelta > this.MAX_CONSECUTIVE_GESTURE_DELAY) {
|
||||
delete this._prevGestures[idhash];
|
||||
} else {
|
||||
if (details.type == 'tap' && prevGesture.type == 'tap')
|
||||
details.type = 'doubletap';
|
||||
if (details.type == 'tap' && prevGesture.type == 'doubletap')
|
||||
details.type = 'tripletap';
|
||||
if (details.type == 'dwell' && prevGesture.type == 'tap')
|
||||
details.type = 'taphold';
|
||||
}
|
||||
}
|
||||
|
||||
this._prevGestures[idhash] = details;
|
||||
}
|
||||
|
||||
this.chromeWin.clearTimeout(this._dwellTimeout);
|
||||
this.cleanupTouches();
|
||||
|
||||
return multiDetails;
|
||||
},
|
||||
|
||||
emitGesture: function TouchAdapter_emitGesture(aDetails) {
|
||||
let evt = this.chromeWin.document.createEvent('CustomEvent');
|
||||
evt.initCustomEvent('mozAccessFuGesture', true, true, aDetails);
|
||||
this.chromeWin.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
compileAndEmit: function TouchAdapter_compileAndEmit(aTime) {
|
||||
for each (let details in this.compile(aTime)) {
|
||||
this.emitGesture(details);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/***
|
||||
* A TouchPoint represents a single touch from the moment of contact until it is
|
||||
* lifted from the surface. It is capable of compiling gestures from the scope
|
||||
* of one single touch.
|
||||
*/
|
||||
function TouchPoint(aTouch, aTime, aDPI) {
|
||||
this.startX = aTouch.screenX;
|
||||
this.startY = aTouch.screenY;
|
||||
this.startTime = aTime;
|
||||
this.distanceTraveled = 0;
|
||||
this.dpi = aDPI;
|
||||
this.done = false;
|
||||
}
|
||||
|
||||
TouchPoint.prototype = {
|
||||
update: function TouchPoint_update(aTouch, aTime) {
|
||||
let lastX = this.x;
|
||||
let lastY = this.y;
|
||||
this.x = aTouch.screenX;
|
||||
this.y = aTouch.screenY;
|
||||
this.time = aTime;
|
||||
|
||||
if (lastX != undefined && lastY != undefined)
|
||||
this.distanceTraveled += this.getDistanceToCoord(lastX, lastY);
|
||||
},
|
||||
|
||||
getDistanceToCoord: function TouchPoint_getDistanceToCoord(aX, aY) {
|
||||
return Math.sqrt(Math.pow(this.x - aX, 2) + Math.pow(this.y - aY, 2));
|
||||
},
|
||||
|
||||
finish: function TouchPoint_finish() {
|
||||
this.done = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Compile a gesture from an individual touch point. This is used by the
|
||||
* TouchAdapter to compound multiple single gestures in to higher level
|
||||
* gestures.
|
||||
*/
|
||||
compile: function TouchPoint_compile(aTime) {
|
||||
let directDistance = this.directDistanceTraveled;
|
||||
let duration = aTime - this.startTime;
|
||||
|
||||
// To be considered a tap/dwell...
|
||||
if ((this.distanceTraveled / this.dpi) < TouchAdapter.TAP_MAX_RADIUS) { // Didn't travel
|
||||
if (duration < TouchAdapter.DWELL_THRESHOLD) {
|
||||
// Mark it as done so we don't use this touch for another gesture.
|
||||
this.finish();
|
||||
return {type: 'tap', x: this.startX, y: this.startY};
|
||||
} else if (!this.done && duration == TouchAdapter.DWELL_THRESHOLD) {
|
||||
return {type: 'dwell', x: this.startX, y: this.startY};
|
||||
}
|
||||
}
|
||||
|
||||
// To be considered a swipe...
|
||||
if (duration <= TouchAdapter.SWIPE_MAX_DURATION && // Quick enough
|
||||
(directDistance / this.dpi) >= TouchAdapter.SWIPE_MIN_DISTANCE && // Traveled far
|
||||
(directDistance * 1.2) >= this.distanceTraveled) { // Direct enough
|
||||
|
||||
let swipeGesture = {x1: this.startX, y1: this.startY,
|
||||
x2: this.x, y2: this.y};
|
||||
let deltaX = this.x - this.startX;
|
||||
let deltaY = this.y - this.startY;
|
||||
|
||||
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
||||
// Horizontal swipe.
|
||||
if (deltaX > 0)
|
||||
swipeGesture.type = 'swiperight';
|
||||
else
|
||||
swipeGesture.type = 'swipeleft';
|
||||
} else if (Math.abs(deltaX) < Math.abs(deltaY)) {
|
||||
// Vertical swipe.
|
||||
if (deltaY > 0)
|
||||
swipeGesture.type = 'swipedown';
|
||||
else
|
||||
swipeGesture.type = 'swipeup';
|
||||
} else {
|
||||
// A perfect 45 degree swipe?? Not in our book.
|
||||
return null;
|
||||
}
|
||||
|
||||
this.finish();
|
||||
|
||||
return swipeGesture;
|
||||
}
|
||||
|
||||
// To be considered an explore...
|
||||
if (!this.done &&
|
||||
duration > TouchAdapter.SWIPE_MAX_DURATION &&
|
||||
(this.distanceTraveled / this.dpi) > TouchAdapter.TAP_MAX_RADIUS) {
|
||||
return {type: 'explore', x: this.x, y: this.y};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
get directDistanceTraveled() {
|
||||
return this.getDistanceToCoord(this.startX, this.startY);
|
||||
}
|
||||
};
|
||||
|
||||
var Mouse2Touch = {
|
||||
_MouseToTouchMap: {
|
||||
mousedown: 'touchstart',
|
||||
mouseup: 'touchend',
|
||||
mousemove: 'touchmove'
|
||||
},
|
||||
|
||||
attach: function Mouse2Touch_attach(aWindow) {
|
||||
this.chromeWin = aWindow;
|
||||
this.chromeWin.addEventListener('mousedown', this, true, true);
|
||||
this.chromeWin.addEventListener('mouseup', this, true, true);
|
||||
this.chromeWin.addEventListener('mousemove', this, true, true);
|
||||
},
|
||||
|
||||
detach: function Mouse2Touch_detach(aWindow) {
|
||||
this.chromeWin.removeEventListener('mousedown', this, true, true);
|
||||
this.chromeWin.removeEventListener('mouseup', this, true, true);
|
||||
this.chromeWin.removeEventListener('mousemove', this, true, true);
|
||||
},
|
||||
|
||||
handleEvent: function Mouse2Touch_handleEvent(aEvent) {
|
||||
if (aEvent.buttons == 0)
|
||||
return;
|
||||
|
||||
let name = this._MouseToTouchMap[aEvent.type];
|
||||
let evt = this.chromeWin.document.createEvent("touchevent");
|
||||
let points = [this.chromeWin.document.createTouch(
|
||||
this.chromeWin, aEvent.target, 0,
|
||||
aEvent.pageX, aEvent.pageY, aEvent.screenX, aEvent.screenY,
|
||||
aEvent.clientX, aEvent.clientY, 1, 1, 0, 0)];
|
||||
|
||||
// Simulate another touch point at a 5px offset when ctrl is pressed.
|
||||
if (aEvent.ctrlKey)
|
||||
points.push(this.chromeWin.document.createTouch(
|
||||
this.chromeWin, aEvent.target, 1,
|
||||
aEvent.pageX + 5, aEvent.pageY + 5,
|
||||
aEvent.screenX + 5, aEvent.screenY + 5,
|
||||
aEvent.clientX + 5, aEvent.clientY + 5,
|
||||
1, 1, 0, 0));
|
||||
|
||||
// Simulate another touch point at a -5px offset when alt is pressed.
|
||||
if (aEvent.altKey)
|
||||
points.push(this.chromeWin.document.createTouch(
|
||||
this.chromeWin, aEvent.target, 2,
|
||||
aEvent.pageX - 5, aEvent.pageY - 5,
|
||||
aEvent.screenX - 5, aEvent.screenY - 5,
|
||||
aEvent.clientX - 5, aEvent.clientY - 5,
|
||||
1, 1, 0, 0));
|
||||
|
||||
let touches = this.chromeWin.document.createTouchList(points);
|
||||
if (name == "touchend") {
|
||||
let empty = this.chromeWin.document.createTouchList();
|
||||
evt.initTouchEvent(name, true, true, this.chromeWin, 0,
|
||||
false, false, false, false, empty, empty, touches);
|
||||
} else {
|
||||
evt.initTouchEvent(name, true, true, this.chromeWin, 0,
|
||||
false, false, false, false, touches, touches, touches);
|
||||
}
|
||||
aEvent.target.dispatchEvent(evt);
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
|
||||
var AndroidTouchAdapter = {
|
||||
attach: function AndroidTouchAdapter_attach(aWindow) {
|
||||
if (this.chromeWin)
|
||||
return;
|
||||
|
||||
Logger.info('AndroidTouchAdapter.attach');
|
||||
|
||||
this.chromeWin = aWindow;
|
||||
this.chromeWin.addEventListener('mousemove', this, true, true);
|
||||
this._lastExploreTime = 0;
|
||||
},
|
||||
|
||||
detach: function AndroidTouchAdapter_detach(aWindow) {
|
||||
if (!this.chromeWin)
|
||||
return;
|
||||
|
||||
Logger.info('AndroidTouchAdapter.detach');
|
||||
|
||||
this.chromeWin.removeEventListener('mousemove', this, true, true);
|
||||
delete this.chromeWin;
|
||||
},
|
||||
|
||||
handleEvent: function AndroidTouchAdapter_handleEvent(aEvent) {
|
||||
// On non-Android we use the shift key to simulate touch.
|
||||
if (Utils.MozBuildApp != 'mobile/android' && !aEvent.shiftKey)
|
||||
return;
|
||||
|
||||
if (aEvent.timeStamp - this._lastExploreTime >= EXPLORE_THROTTLE) {
|
||||
let evt = this.chromeWin.document.createEvent('CustomEvent');
|
||||
evt.initCustomEvent(
|
||||
'mozAccessFuGesture', true, true,
|
||||
{type: 'explore', x: aEvent.screenX, y: aEvent.screenY});
|
||||
this.chromeWin.dispatchEvent(evt);
|
||||
this._lastExploreTime = aEvent.timeStamp;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -71,6 +71,20 @@ var Utils = {
|
|||
return this.getBrowserApp(aWindow).selectedBrowser.contentDocument;
|
||||
},
|
||||
|
||||
getAllDocuments: function getAllDocuments(aWindow) {
|
||||
let doc = gAccRetrieval.
|
||||
getAccessibleFor(this.getCurrentContentDoc(aWindow)).
|
||||
QueryInterface(Ci.nsIAccessibleDocument);
|
||||
let docs = [];
|
||||
function getAllDocuments(aDocument) {
|
||||
docs.push(aDocument.DOMDocument);
|
||||
for (let i = 0; i < aDocument.childDocumentCount; i++)
|
||||
getAllDocuments(aDocument.getChildDocumentAt(i));
|
||||
}
|
||||
getAllDocuments(doc);
|
||||
return docs;
|
||||
},
|
||||
|
||||
getViewport: function getViewport(aWindow) {
|
||||
switch (this.MozBuildApp) {
|
||||
case 'mobile/android':
|
||||
|
@ -103,6 +117,83 @@ var Utils = {
|
|||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
scroll: function scroll(aWindow, aPage, aHorizontal) {
|
||||
for each (let doc in this.getAllDocuments(aWindow)) {
|
||||
// First see if we could scroll a window.
|
||||
let win = doc.defaultView;
|
||||
if (!aHorizontal && win.scrollMaxY &&
|
||||
((aPage > 0 && win.scrollY < win.scrollMaxY) ||
|
||||
(aPage < 0 && win.scrollY > 0))) {
|
||||
win.scroll(0, win.innerHeight);
|
||||
return true;
|
||||
} else if (aHorizontal && win.scrollMaxX &&
|
||||
((aPage > 0 && win.scrollX < win.scrollMaxX) ||
|
||||
(aPage < 0 && win.scrollX > 0))) {
|
||||
win.scroll(win.innerWidth, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Second, try to scroll main section or current target if there is no
|
||||
// main section.
|
||||
let main = doc.querySelector('[role=main]') ||
|
||||
doc.querySelector(':target');
|
||||
|
||||
if (main) {
|
||||
if ((!aHorizontal && main.clientHeight < main.scrollHeight) ||
|
||||
(aHorizontal && main.clientWidth < main.scrollWidth)) {
|
||||
let s = win.getComputedStyle(main);
|
||||
if (!aHorizontal) {
|
||||
if (s.overflowY == 'scroll' || s.overflowY == 'auto') {
|
||||
main.scrollTop += aPage * main.clientHeight;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (s.overflowX == 'scroll' || s.overflowX == 'auto') {
|
||||
main.scrollLeft += aPage * main.clientWidth;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
changePage: function changePage(aWindow, aPage) {
|
||||
for each (let doc in this.getAllDocuments(aWindow)) {
|
||||
// Get current main section or active target.
|
||||
let main = doc.querySelector('[role=main]') ||
|
||||
doc.querySelector(':target');
|
||||
if (!main)
|
||||
continue;
|
||||
|
||||
let mainAcc = gAccRetrieval.getAccessibleFor(main);
|
||||
if (!mainAcc)
|
||||
continue;
|
||||
|
||||
let controllers = mainAcc.
|
||||
getRelationByType(Ci.nsIAccessibleRelation.RELATION_CONTROLLED_BY);
|
||||
|
||||
for (var i=0; controllers.targetsCount > i; i++) {
|
||||
let controller = controllers.getTarget(i);
|
||||
// If the section has a controlling slider, it should be considered
|
||||
// the page-turner.
|
||||
if (controller.role == Ci.nsIAccessibleRole.ROLE_SLIDER) {
|
||||
// Sliders are controlled with ctrl+right/left. I just decided :)
|
||||
let evt = doc.createEvent("KeyboardEvent");
|
||||
evt.initKeyEvent('keypress', true, true, null,
|
||||
true, false, false, false,
|
||||
(aPage > 0) ? evt.DOM_VK_RIGHT : evt.DOM_VK_LEFT, 0);
|
||||
controller.DOMNode.dispatchEvent(evt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -197,12 +197,12 @@ var VirtualCursorController = {
|
|||
attach: function attach(aWindow) {
|
||||
this.chromeWin = aWindow;
|
||||
this.chromeWin.document.addEventListener('keypress', this, true);
|
||||
this.chromeWin.document.addEventListener('mousemove', this, true);
|
||||
this.chromeWin.addEventListener('mozAccessFuGesture', this, true);
|
||||
},
|
||||
|
||||
detach: function detach() {
|
||||
this.chromeWin.document.removeEventListener('keypress', this, true);
|
||||
this.chromeWin.document.removeEventListener('mousemove', this, true);
|
||||
this.chromeWin.removeEventListener('mozAccessFuGesture', this, true);
|
||||
},
|
||||
|
||||
handleEvent: function VirtualCursorController_handleEvent(aEvent) {
|
||||
|
@ -210,45 +210,68 @@ var VirtualCursorController = {
|
|||
case 'keypress':
|
||||
this._handleKeypress(aEvent);
|
||||
break;
|
||||
case 'mousemove':
|
||||
this._handleMousemove(aEvent);
|
||||
case 'mozAccessFuGesture':
|
||||
this._handleGesture(aEvent);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_handleMousemove: function _handleMousemove(aEvent) {
|
||||
// Explore by touch is disabled.
|
||||
if (!this.exploreByTouch)
|
||||
return;
|
||||
_handleGesture: function _handleGesture(aEvent) {
|
||||
let document = Utils.getCurrentContentDoc(this.chromeWin);
|
||||
let detail = aEvent.detail;
|
||||
Logger.info('Gesture', detail.type,
|
||||
'(fingers: ' + detail.touches.length + ')');
|
||||
|
||||
// On non-Android we use the shift key to simulate touch.
|
||||
if (Utils.OS != 'Android' && !aEvent.shiftKey)
|
||||
return;
|
||||
|
||||
// We should not be calling moveToPoint more than 10 times a second.
|
||||
// It is granular enough to feel natural, and it does not hammer the CPU.
|
||||
if (!this._handleMousemove._lastEventTime ||
|
||||
aEvent.timeStamp - this._handleMousemove._lastEventTime >= 100) {
|
||||
this.moveToPoint(Utils.getCurrentContentDoc(this.chromeWin),
|
||||
aEvent.screenX, aEvent.screenY);
|
||||
this._handleMousemove._lastEventTime = aEvent.timeStamp;
|
||||
if (detail.touches.length == 1) {
|
||||
switch (detail.type) {
|
||||
case 'swiperight':
|
||||
this.moveForward(document, aEvent.shiftKey);
|
||||
break;
|
||||
case 'swipeleft':
|
||||
this.moveBackward(document, aEvent.shiftKey);
|
||||
break;
|
||||
case 'doubletap':
|
||||
this.activateCurrent(document);
|
||||
break;
|
||||
case 'explore':
|
||||
this.moveToPoint(document, detail.x, detail.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopImmediatePropagation();
|
||||
if (detail.touches.length == 3) {
|
||||
switch (detail.type) {
|
||||
case 'swiperight':
|
||||
if (!Utils.scroll(this.chromeWin, -1, true))
|
||||
Utils.changePage(this.chromeWin, -1);
|
||||
break;
|
||||
case 'swipedown':
|
||||
Utils.scroll(this.chromeWin, -1);
|
||||
break;
|
||||
case 'swipeleft':
|
||||
if (!Utils.scroll(this.chromeWin, 1, true))
|
||||
Utils.changePage(this.chromeWin, 1);
|
||||
case 'swipeup':
|
||||
Utils.scroll(this.chromeWin, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_handleKeypress: function _handleKeypress(aEvent) {
|
||||
let document = Utils.getCurrentContentDoc(this.chromeWin);
|
||||
let target = aEvent.target;
|
||||
|
||||
// Ignore keys with modifiers so the content could take advantage of them.
|
||||
if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey)
|
||||
return;
|
||||
|
||||
switch (aEvent.keyCode) {
|
||||
case 0:
|
||||
// an alphanumeric key was pressed, handle it separately.
|
||||
// If it was pressed with either alt or ctrl, just pass through.
|
||||
// If it was pressed with meta, pass the key on without the meta.
|
||||
if (this.editableState ||
|
||||
aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey)
|
||||
if (this.editableState)
|
||||
return;
|
||||
|
||||
let key = String.fromCharCode(aEvent.charCode);
|
||||
|
|
|
@ -16,6 +16,7 @@ Cu.import('resource://gre/modules/SettingsChangeNotifier.jsm');
|
|||
Cu.import('resource://gre/modules/Webapps.jsm');
|
||||
Cu.import('resource://gre/modules/AlarmService.jsm');
|
||||
Cu.import('resource://gre/modules/ActivitiesService.jsm');
|
||||
Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(Services, 'env',
|
||||
'@mozilla.org/process/environment;1',
|
||||
|
|
|
@ -285,7 +285,7 @@ FeedConverter.prototype = {
|
|||
// getResponseHeader.
|
||||
if (!httpChannel.requestSucceeded) {
|
||||
// Just give up, but don't forget to cancel the channel first!
|
||||
request.cancel(0x804b0002); // NS_BINDING_ABORTED
|
||||
request.cancel(Cr.NS_BINDING_ABORTED);
|
||||
return;
|
||||
}
|
||||
var noSniff = httpChannel.getResponseHeader("X-Moz-Is-Feed");
|
||||
|
|
|
@ -45,6 +45,5 @@ DEPRECATED_OPERATION(InputEncoding)
|
|||
DEPRECATED_OPERATION(MozBeforePaint)
|
||||
DEPRECATED_OPERATION(MozBlobBuilder)
|
||||
DEPRECATED_OPERATION(DOMExceptionCode)
|
||||
DEPRECATED_OPERATION(NoExposedProps)
|
||||
DEPRECATED_OPERATION(MutationEvent)
|
||||
DEPRECATED_OPERATION(MozSlice)
|
||||
|
|
|
@ -515,8 +515,10 @@ CSPRep.prototype = {
|
|||
|
||||
for (var dir in CSPRep.SRC_DIRECTIVES) {
|
||||
var dirv = CSPRep.SRC_DIRECTIVES[dir];
|
||||
newRep._directives[dirv] = this._directives[dirv]
|
||||
.intersectWith(aCSPRep._directives[dirv]);
|
||||
if (this._directives.hasOwnProperty(dirv))
|
||||
newRep._directives[dirv] = this._directives[dirv].intersectWith(aCSPRep._directives[dirv]);
|
||||
else
|
||||
newRep._directives[dirv] = aCSPRep._directives[dirv];
|
||||
}
|
||||
|
||||
// REPORT_URI
|
||||
|
@ -534,12 +536,6 @@ CSPRep.prototype = {
|
|||
newRep._directives[reportURIDir] = aCSPRep._directives[reportURIDir].concat();
|
||||
}
|
||||
|
||||
for (var dir in CSPRep.SRC_DIRECTIVES) {
|
||||
var dirv = CSPRep.SRC_DIRECTIVES[dir];
|
||||
newRep._directives[dirv] = this._directives[dirv]
|
||||
.intersectWith(aCSPRep._directives[dirv]);
|
||||
}
|
||||
|
||||
newRep._allowEval = this.allowsEvalInScripts
|
||||
&& aCSPRep.allowsEvalInScripts;
|
||||
|
||||
|
@ -779,6 +775,8 @@ CSPSourceList.prototype = {
|
|||
|
||||
var newCSPSrcList = null;
|
||||
|
||||
if (!that) return this.clone();
|
||||
|
||||
if (this.isNone() || that.isNone())
|
||||
newCSPSrcList = CSPSourceList.fromString("'none'");
|
||||
|
||||
|
@ -1187,32 +1185,32 @@ CSPSource.prototype = {
|
|||
// when a scheme, host or port is undefined.)
|
||||
|
||||
// port
|
||||
if (!this._port)
|
||||
newSource._port = that._port;
|
||||
else if (!that._port)
|
||||
newSource._port = this._port;
|
||||
else if (this._port === "*")
|
||||
newSource._port = that._port;
|
||||
else if (that._port === "*")
|
||||
newSource._port = this._port;
|
||||
else if (that._port === this._port)
|
||||
newSource._port = this._port;
|
||||
if (!this.port)
|
||||
newSource._port = that.port;
|
||||
else if (!that.port)
|
||||
newSource._port = this.port;
|
||||
else if (this.port === "*")
|
||||
newSource._port = that.port;
|
||||
else if (that.port === "*")
|
||||
newSource._port = this.port;
|
||||
else if (that.port === this.port)
|
||||
newSource._port = this.port;
|
||||
else {
|
||||
CSPError(CSPLocalizer.getFormatStr("notIntersectPort", [this.toString(), that.toString()]));
|
||||
return null;
|
||||
}
|
||||
|
||||
// scheme
|
||||
if (!this._scheme)
|
||||
newSource._scheme = that._scheme;
|
||||
else if (!that._scheme)
|
||||
newSource._scheme = this._scheme;
|
||||
if (this._scheme === "*")
|
||||
newSource._scheme = that._scheme;
|
||||
else if (that._scheme === "*")
|
||||
newSource._scheme = this._scheme;
|
||||
else if (that._scheme === this._scheme)
|
||||
newSource._scheme = this._scheme;
|
||||
if (!this.scheme)
|
||||
newSource._scheme = that.scheme;
|
||||
else if (!that.scheme)
|
||||
newSource._scheme = this.scheme;
|
||||
if (this.scheme === "*")
|
||||
newSource._scheme = that.scheme;
|
||||
else if (that.scheme === "*")
|
||||
newSource._scheme = this.scheme;
|
||||
else if (that.scheme === this.scheme)
|
||||
newSource._scheme = this.scheme;
|
||||
else {
|
||||
CSPError(CSPLocalizer.getFormatStr("notIntersectScheme", [this.toString(), that.toString()]));
|
||||
return null;
|
||||
|
@ -1227,14 +1225,14 @@ CSPSource.prototype = {
|
|||
// error should still be reported.
|
||||
|
||||
// host
|
||||
if (this._host && that._host) {
|
||||
newSource._host = this._host.intersectWith(that._host);
|
||||
} else if (this._host) {
|
||||
if (this.host && that.host) {
|
||||
newSource._host = this.host.intersectWith(that.host);
|
||||
} else if (this.host) {
|
||||
CSPError(CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost", [that.toString()]));
|
||||
newSource._host = this._host.clone();
|
||||
} else if (that._host) {
|
||||
newSource._host = this.host.clone();
|
||||
} else if (that.host) {
|
||||
CSPError(CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost", [this.toString()]));
|
||||
newSource._host = that._host.clone();
|
||||
newSource._host = that.host.clone();
|
||||
} else {
|
||||
CSPError(CSPLocalizer.getFormatStr("intersectingSourcesWithUndefinedHosts", [this.toString(), that.toString()]));
|
||||
newSource._host = CSPHost.fromString("*");
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
#include "nsIDOMFormData.h"
|
||||
#include "DictionaryHelpers.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "nsStreamListenerWrapper.h"
|
||||
|
@ -572,9 +573,16 @@ nsXMLHttpRequest::InitParameters(bool aAnon, bool aSystem)
|
|||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
doc->NodePrincipal()->GetURI(getter_AddRefs(uri));
|
||||
if (!nsContentUtils::URIIsChromeOrInPref(uri, "dom.systemXHR.whitelist")) {
|
||||
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
if (!permMgr)
|
||||
return;
|
||||
|
||||
PRUint32 permission;
|
||||
nsresult rv =
|
||||
permMgr->TestPermissionFromPrincipal(principal, "systemXHR", &permission);
|
||||
if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,11 @@ function runTests() {
|
|||
authMgr.setAuthIdentity("http", "example.com", 80, "basic", "testrealm",
|
||||
"", "example.com", "user1", "password1");
|
||||
|
||||
SpecialPowers.setCharPref("dom.systemXHR.whitelist",
|
||||
"http://mochi.test:8888");
|
||||
SpecialPowers.addPermission("systemXHR", true, document);
|
||||
|
||||
return function tearDown() {
|
||||
authMgr.clearAll();
|
||||
SpecialPowers.clearUserPref("dom.systemXHR.whitelist");
|
||||
SpecialPowers.removePermission("systemXHR", document);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}());
|
||||
|
|
|
@ -84,11 +84,11 @@ function runTests() {
|
|||
|
||||
// ...and once with privileges.
|
||||
havePrivileges = true;
|
||||
SpecialPowers.setCharPref("dom.systemXHR.whitelist",
|
||||
"http://mochi.test:8888");
|
||||
SpecialPowers.addPermission("systemXHR", true, document);
|
||||
|
||||
validParameters.forEach(testValidParameter);
|
||||
invalidParameters.forEach(testInvalidParameter);
|
||||
SpecialPowers.clearUserPref("dom.systemXHR.whitelist");
|
||||
SpecialPowers.removePermission("systemXHR", document);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
<script class="testbody" type="application/javascript;version=1.8">
|
||||
|
||||
function runTests() {
|
||||
var comp = SpecialPowers.wrap(Components);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.setCharPref("dom.systemXHR.whitelist",
|
||||
"http://mochi.test:8888");
|
||||
SpecialPowers.addPermission("systemXHR", true, document);
|
||||
|
||||
function tearDown() {
|
||||
SpecialPowers.clearUserPref("dom.systemXHR.whitelist");
|
||||
SpecialPowers.removePermission("systemXHR", document);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
|
|
@ -635,6 +635,57 @@ test(
|
|||
do_check_false(p_none.permits("http://bar.com"));
|
||||
});
|
||||
|
||||
test(
|
||||
function test_bug783497_refinePolicyIssues() {
|
||||
|
||||
const firstPolicy = "allow 'self'; img-src 'self'; script-src 'self'; options 'bogus-option'";
|
||||
const secondPolicy = "default-src 'none'; script-src 'self'";
|
||||
var cspObj = Cc["@mozilla.org/contentsecuritypolicy;1"]
|
||||
.createInstance(Ci.nsIContentSecurityPolicy);
|
||||
var selfURI = URI("http://self.com/");
|
||||
|
||||
function testPermits(aUri, aContext) {
|
||||
return cspObj.shouldLoad(aContext, aUri, null, null, null, null)
|
||||
== Ci.nsIContentPolicy.ACCEPT;
|
||||
};
|
||||
|
||||
// everything is allowed by the default policy
|
||||
do_check_true(testPermits(URI("http://self.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_true(testPermits(URI("http://other.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_true(testPermits(URI("http://self.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
do_check_true(testPermits(URI("http://other.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
|
||||
// fold in the first policy
|
||||
cspObj.refinePolicy(firstPolicy, selfURI);
|
||||
|
||||
// script-src and img-src are limited to self after the first policy
|
||||
do_check_true(testPermits(URI("http://self.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_true(testPermits(URI("http://self.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
|
||||
// fold in the second policy
|
||||
cspObj.refinePolicy(secondPolicy, selfURI);
|
||||
|
||||
// script-src is self and img-src is none after the merge
|
||||
do_check_true(testPermits(URI("http://self.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_false(testPermits(URI("http://self.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
|
|
@ -124,8 +124,7 @@ ok(ctx.canvas === canvas, "ctx.canvas === canvas");
|
|||
function isPixel(ctx, x,y, r,g,b,a, d) {
|
||||
var pos = x + "," + y;
|
||||
var colour = r + "," + g + "," + b + "," + a;
|
||||
var pixel = SpecialPowers.unwrap(SpecialPowers.wrap(ctx)
|
||||
.getImageData(x, y, 1, 1));
|
||||
var pixel = ctx.getImageData(x, y, 1, 1);
|
||||
var pr = pixel.data[0],
|
||||
pg = pixel.data[1],
|
||||
pb = pixel.data[2],
|
||||
|
|
|
@ -18,7 +18,6 @@ const Cu = Components.utils;
|
|||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(Services, "DOMRequest", function() {
|
||||
return Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService);
|
||||
|
|
|
@ -23,6 +23,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={674720}
|
|||
|
||||
var comp = SpecialPowers.wrap(Components);
|
||||
comp.utils.import("resource://gre/modules/ContactService.jsm");
|
||||
comp.utils.import("resource://gre/modules/PermissionPromptHelper.jsm");
|
||||
SpecialPowers.setBoolPref("dom.mozContacts.enabled", true);
|
||||
SpecialPowers.addPermission("contacts", true, document);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={674720}
|
|||
|
||||
var comp = SpecialPowers.wrap(Components);
|
||||
comp.utils.import("resource://gre/modules/ContactService.jsm");
|
||||
comp.utils.import("resource://gre/modules/PermissionPromptHelper.jsm");
|
||||
SpecialPowers.setBoolPref("dom.mozContacts.enabled", true);
|
||||
SpecialPowers.addPermission("contacts", true, document);
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
class nsDOMDeviceStorage MOZ_FINAL
|
||||
: public nsIDOMDeviceStorage
|
||||
, public nsIFileUpdateListener
|
||||
, public nsDOMEventTargetHelper
|
||||
, public nsIObserver
|
||||
{
|
||||
|
@ -21,7 +20,6 @@ public:
|
|||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMDEVICESTORAGE
|
||||
|
||||
NS_DECL_NSIFILEUPDATELISTENER
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIDOMEVENTTARGET
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
|
||||
|
@ -58,11 +56,14 @@ private:
|
|||
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
bool mIsWatchingFile;
|
||||
bool mAllowedToWatchFile;
|
||||
|
||||
nsresult Notify(const char* aReason, nsIFile* aFile);
|
||||
|
||||
friend class WatchFileEvent;
|
||||
friend class DeviceStorageRequest;
|
||||
|
||||
bool mIsWatchingFile;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
void DispatchMountChangeEvent(nsAString& aType);
|
||||
#endif
|
||||
|
|
|
@ -274,7 +274,7 @@ DeviceStorageRequestParent::DeleteFileEvent::CancelableRun()
|
|||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
mFile->mFile->Remove(true);
|
||||
mFile->Remove();
|
||||
|
||||
nsRefPtr<nsRunnable> r;
|
||||
|
||||
|
|
|
@ -50,6 +50,32 @@ using namespace mozilla::dom::devicestorage;
|
|||
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
|
||||
class IOEventComplete : public nsRunnable
|
||||
{
|
||||
public:
|
||||
IOEventComplete(nsIFile *aFile, const char *aType)
|
||||
: mFile(aFile)
|
||||
, mType(aType)
|
||||
{
|
||||
}
|
||||
|
||||
~IOEventComplete() {}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
nsString data;
|
||||
CopyASCIItoUTF16(mType, data);
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->NotifyObservers(mFile, "file-watcher-update", data.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
nsCString mType;
|
||||
};
|
||||
|
||||
DeviceStorageFile::DeviceStorageFile(nsIFile* aFile, const nsAString& aPath)
|
||||
: mPath(aPath)
|
||||
, mEditable(false)
|
||||
|
@ -159,6 +185,9 @@ DeviceStorageFile::Write(nsIInputStream* aInputStream)
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(mFile, "created");
|
||||
NS_DispatchToMainThread(iocomplete);
|
||||
|
||||
PRUint64 bufSize = 0;
|
||||
aInputStream->Available(&bufSize);
|
||||
|
||||
|
@ -181,13 +210,18 @@ DeviceStorageFile::Write(nsIInputStream* aInputStream)
|
|||
rv = NS_OK;
|
||||
while (bufSize) {
|
||||
PRUint32 wrote;
|
||||
rv = bufferedOutputStream->WriteFrom(aInputStream, static_cast<PRUint32>(NS_MIN<PRUint64>(bufSize, PR_UINT32_MAX)), &wrote);
|
||||
rv = bufferedOutputStream->WriteFrom(aInputStream,
|
||||
static_cast<PRUint32>(NS_MIN<PRUint64>(bufSize, PR_UINT32_MAX)),
|
||||
&wrote);
|
||||
if (NS_FAILED(rv)) {
|
||||
break;
|
||||
}
|
||||
bufSize -= wrote;
|
||||
}
|
||||
|
||||
iocomplete = new IOEventComplete(mFile, "modified");
|
||||
NS_DispatchToMainThread(iocomplete);
|
||||
|
||||
bufferedOutputStream->Close();
|
||||
outputStream->Close();
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -204,6 +238,9 @@ DeviceStorageFile::Write(InfallibleTArray<PRUint8>& aBits) {
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(mFile, "created");
|
||||
NS_DispatchToMainThread(iocomplete);
|
||||
|
||||
nsCOMPtr<nsIOutputStream> outputStream;
|
||||
NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
|
||||
|
||||
|
@ -215,12 +252,24 @@ DeviceStorageFile::Write(InfallibleTArray<PRUint8>& aBits) {
|
|||
outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote);
|
||||
outputStream->Close();
|
||||
|
||||
iocomplete = new IOEventComplete(mFile, "modified");
|
||||
NS_DispatchToMainThread(iocomplete);
|
||||
|
||||
if (aBits.Length() != wrote) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageFile::Remove()
|
||||
{
|
||||
mFile->Remove(true);
|
||||
nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(mFile, "deleted");
|
||||
NS_DispatchToMainThread(iocomplete);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFile::CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
|
||||
PRUint64 aSince)
|
||||
|
@ -447,6 +496,9 @@ nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType)
|
|||
#ifdef MOZ_WIDGET_GONK
|
||||
RegisterForSDCardChanges(this);
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->AddObserver(this, "file-watcher-update", false);
|
||||
mFile = f;
|
||||
}
|
||||
|
||||
|
@ -897,7 +949,6 @@ private:
|
|||
nsRefPtr<DOMRequest> mRequest;
|
||||
};
|
||||
|
||||
|
||||
class PostResultEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -970,10 +1021,8 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<PostResultEvent> event = new PostResultEvent(mRequest,
|
||||
mFile->mPath);
|
||||
nsCOMPtr<PostResultEvent> event = new PostResultEvent(mRequest, mFile->mPath);
|
||||
NS_DispatchToMainThread(event);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -982,6 +1031,7 @@ private:
|
|||
nsRefPtr<DeviceStorageFile> mFile;
|
||||
nsRefPtr<DOMRequest> mRequest;
|
||||
};
|
||||
|
||||
class ReadFileEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -1034,8 +1084,7 @@ public:
|
|||
NS_IMETHOD Run()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
mFile->mFile->Remove(true);
|
||||
mFile->Remove();
|
||||
|
||||
nsRefPtr<nsRunnable> r;
|
||||
|
||||
|
@ -1283,22 +1332,8 @@ public:
|
|||
|
||||
case DEVICE_STORAGE_REQUEST_WATCH:
|
||||
{
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
nsString fullpath;
|
||||
mFile->mFile->GetPath(fullpath);
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->AddObserver(mDeviceStorage, "file-watcher-update", false);
|
||||
ContentChild::GetSingleton()->SendAddFileWatch(fullpath);
|
||||
} else {
|
||||
if (!mDeviceStorage->mIsWatchingFile) {
|
||||
|
||||
//TODO
|
||||
|
||||
mFile->mFile->Watch(mDeviceStorage);
|
||||
mDeviceStorage->mIsWatchingFile = true;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
mDeviceStorage->mAllowedToWatchFile = true;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1378,7 +1413,6 @@ DOMCI_DATA(DeviceStorage, nsDOMDeviceStorage)
|
|||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceStorage)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceStorage)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIFileUpdateListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DeviceStorage)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
||||
|
@ -1388,6 +1422,7 @@ NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
|
|||
|
||||
nsDOMDeviceStorage::nsDOMDeviceStorage()
|
||||
: mIsWatchingFile(false)
|
||||
, mAllowedToWatchFile(false)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
|
@ -1425,19 +1460,9 @@ nsDOMDeviceStorage::Shutdown()
|
|||
#ifdef MOZ_WIDGET_GONK
|
||||
UnregisterForSDCardChanges(this);
|
||||
#endif
|
||||
if (mIsWatchingFile) {
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->RemoveObserver(this, "file-watcher-update");
|
||||
|
||||
nsString fullpath;
|
||||
mFile->GetPath(fullpath);
|
||||
ContentChild::GetSingleton()->SendRemoveFileWatch(fullpath);
|
||||
}
|
||||
else {
|
||||
mFile->Unwatch(this);
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->RemoveObserver(this, "file-watcher-update");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1792,39 +1817,16 @@ nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRU
|
|||
{
|
||||
if (!strcmp(aTopic, "file-watcher-update")) {
|
||||
|
||||
// data strings will have the format of
|
||||
// reason:path
|
||||
nsDependentString data(aData);
|
||||
|
||||
nsAString::const_iterator start, end;
|
||||
nsAString::const_iterator colon;
|
||||
|
||||
data.BeginReading(start);
|
||||
data.EndReading(end);
|
||||
colon = end;
|
||||
|
||||
nsString reason;
|
||||
nsString filepath;
|
||||
if (!FindInReadable(NS_LITERAL_STRING(":"), start, colon)) {
|
||||
nsCOMPtr<nsIFile> file = do_QueryInterface(aSubject);
|
||||
if (!file) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
filepath = Substring(colon, end);
|
||||
data.BeginReading(start);
|
||||
reason = Substring(start, --colon);
|
||||
|
||||
nsCOMPtr<nsIFile> f;
|
||||
NS_NewLocalFile(filepath, false, getter_AddRefs(f));
|
||||
|
||||
nsCString creason;
|
||||
CopyUTF16toUTF8(reason, creason);
|
||||
|
||||
Update(creason.get(), f);
|
||||
Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
|
||||
else if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
|
||||
nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
|
||||
if (!vol) {
|
||||
return NS_OK;
|
||||
|
@ -1859,9 +1861,17 @@ nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRU
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMDeviceStorage::Update(const char* aReason, nsIFile* aFile)
|
||||
nsresult
|
||||
nsDOMDeviceStorage::Notify(const char* aReason, nsIFile* aFile)
|
||||
{
|
||||
if (!mAllowedToWatchFile) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mFile) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsString rootpath;
|
||||
nsresult rv = mFile->GetPath(rootpath);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -1926,6 +1936,12 @@ nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
|
|||
bool aWantsUntrusted,
|
||||
PRUint8 aArgc)
|
||||
{
|
||||
if (!mIsWatchingFile) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->AddObserver(this, "file-watcher-update", false);
|
||||
mIsWatchingFile = true;
|
||||
}
|
||||
|
||||
return nsDOMDeviceStorage::AddEventListener(aType,aListener,aUseCapture,aWantsUntrusted, aArgc);
|
||||
}
|
||||
|
||||
|
@ -1937,16 +1953,9 @@ nsDOMDeviceStorage::RemoveEventListener(const nsAString & aType,
|
|||
nsDOMEventTargetHelper::RemoveEventListener(aType, aListener, false);
|
||||
|
||||
if (mIsWatchingFile && !HasListenersFor(NS_LITERAL_STRING("change"))) {
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->RemoveObserver(this, "file-watcher-update");
|
||||
|
||||
nsString fullpath;
|
||||
mFile->GetPath(fullpath);
|
||||
ContentChild::GetSingleton()->SendRemoveFileWatch(fullpath);
|
||||
} else {
|
||||
mFile->Unwatch(this);
|
||||
}
|
||||
mIsWatchingFile = false;
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->RemoveObserver(this, "file-watcher-update");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
// outside of the type of storage the user asked for.
|
||||
bool IsSafePath();
|
||||
|
||||
nsresult Remove();
|
||||
nsresult Write(nsIInputStream* aInputStream);
|
||||
nsresult Write(InfallibleTArray<PRUint8>& bits);
|
||||
void CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRUint64 aSince = 0);
|
||||
|
|
|
@ -20,6 +20,7 @@ MOCHITEST_FILES = \
|
|||
test_enumerateOptions.html \
|
||||
test_lastModificationFilter.html \
|
||||
test_stat.html \
|
||||
test_watch.html \
|
||||
devicestorage_common.js \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html> <!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
-->
|
||||
<head>
|
||||
<title>Test for the device storage API </title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="devicestorage_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
|
||||
var gFileName = randomFilename(20);
|
||||
|
||||
function addSuccess(e) {
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
function onChange(e) {
|
||||
|
||||
dump("we saw: " + e.path + " " + e.reason + "\n");
|
||||
|
||||
if (e.path == gFileName) {
|
||||
ok(true, "we saw the file get created");
|
||||
storage.removeEventListener("change", onChange);
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
else {
|
||||
// we may see other file changes during the test, and
|
||||
// that is completely ok
|
||||
}
|
||||
}
|
||||
|
||||
function makeFile() {
|
||||
request = storage.addNamed(createRandomBlob(), gFileName);
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("testing");
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
storage.addEventListener("change", onChange);
|
||||
|
||||
// since addEventListener is async, we have to wait until
|
||||
// it's definitely done before we make the file.
|
||||
setTimeout(makeFile,100);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -99,7 +99,7 @@ ArchiveReaderEvent::ShareMainThread()
|
|||
|
||||
// Just to be sure, if something goes wrong, the mimetype is an empty string:
|
||||
nsCString type;
|
||||
if (GetType(ext, type) == NS_OK)
|
||||
if (NS_SUCCEEDED(GetType(ext, type)))
|
||||
item->SetType(type);
|
||||
}
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ nsresult
|
|||
ArchiveRequest::ReaderReady(nsTArray<nsCOMPtr<nsIDOMFile> >& aFileList,
|
||||
nsresult aStatus)
|
||||
{
|
||||
if (aStatus != NS_OK) {
|
||||
if (NS_FAILED(aStatus)) {
|
||||
FireError(aStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ ArchiveRequest::GetFilenamesResult(JSContext* aCx,
|
|||
|
||||
jsval item = STRING_TO_JSVAL(str);
|
||||
|
||||
if (rv != NS_OK || !JS_SetElement(aCx, array, i, &item)) {
|
||||
if (NS_FAILED(rv) || !JS_SetElement(aCx, array, i, &item)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ ArchiveReaderZipEvent::Exec()
|
|||
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
rv = mArchiveReader->GetInputStream(getter_AddRefs(inputStream));
|
||||
if (rv != NS_OK || !inputStream) {
|
||||
if (NS_FAILED(rv) || !inputStream) {
|
||||
return RunShare(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ ArchiveReaderZipEvent::Exec()
|
|||
|
||||
PRUint64 size;
|
||||
rv = mArchiveReader->GetSize(&size);
|
||||
if (rv != NS_OK) {
|
||||
if (NS_FAILED(rv)) {
|
||||
return RunShare(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ ArchiveReaderZipEvent::Exec()
|
|||
PRUint32 ret;
|
||||
|
||||
rv = inputStream->Read((char*)buffer, sizeof(buffer), &ret);
|
||||
if (rv != NS_OK || ret != sizeof(buffer)) {
|
||||
if (NS_FAILED(rv) || ret != sizeof(buffer)) {
|
||||
return RunShare(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ ArchiveReaderZipEvent::Exec()
|
|||
PRUint32 ret;
|
||||
|
||||
rv = inputStream->Read((char*)¢ralStruct, ZIPCENTRAL_SIZE, &ret);
|
||||
if (rv != NS_OK || ret != ZIPCENTRAL_SIZE) {
|
||||
if (NS_FAILED(rv) || ret != ZIPCENTRAL_SIZE) {
|
||||
return RunShare(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ ArchiveReaderZipEvent::Exec()
|
|||
// Read the name:
|
||||
char* filename = (char*)PR_Malloc(filenameLen + 1);
|
||||
rv = inputStream->Read(filename, filenameLen, &ret);
|
||||
if (rv != NS_OK || ret != filenameLen) {
|
||||
if (NS_FAILED(rv) || ret != filenameLen) {
|
||||
return RunShare(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,22 +17,29 @@ USING_FILE_NAMESPACE
|
|||
|
||||
// a internat input stream object
|
||||
|
||||
class ArchiveInputStream MOZ_FINAL : public nsIInputStream
|
||||
class ArchiveInputStream MOZ_FINAL : public nsIInputStream,
|
||||
public nsISeekableStream
|
||||
{
|
||||
public:
|
||||
ArchiveInputStream(ArchiveReader* aReader,
|
||||
ArchiveInputStream(PRUint64 aParentSize,
|
||||
nsIInputStream* aInputStream,
|
||||
nsString& aFilename,
|
||||
PRUint32 aStart,
|
||||
PRUint32 aLength,
|
||||
ZipCentral& aCentral)
|
||||
: mArchiveReader(aReader),
|
||||
mCentral(aCentral),
|
||||
: mCentral(aCentral),
|
||||
mFilename(aFilename),
|
||||
mStart(aStart),
|
||||
mLength(aLength),
|
||||
mStatus(NotStarted)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ArchiveInputStream);
|
||||
|
||||
// Reset the data:
|
||||
memset(&mData, 0, sizeof(mData));
|
||||
|
||||
mData.parentSize = aParentSize;
|
||||
mData.inputStream = aInputStream;
|
||||
}
|
||||
|
||||
virtual ~ArchiveInputStream()
|
||||
|
@ -43,12 +50,12 @@ public:
|
|||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
NS_DECL_NSISEEKABLESTREAM
|
||||
|
||||
private:
|
||||
nsresult Init();
|
||||
|
||||
private: // data
|
||||
nsRefPtr<ArchiveReader> mArchiveReader;
|
||||
ZipCentral mCentral;
|
||||
nsString mFilename;
|
||||
PRUint32 mStart;
|
||||
|
@ -63,14 +70,20 @@ private: // data
|
|||
} mStatus;
|
||||
|
||||
struct {
|
||||
PRUint64 parentSize;
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
|
||||
unsigned char input[ZIP_CHUNK];
|
||||
PRUint32 sizeToBeRead;
|
||||
PRUint32 cursor;
|
||||
|
||||
bool compressed; // a zip file can contain stored or compressed files
|
||||
} mData;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(ArchiveInputStream, nsIInputStream)
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(ArchiveInputStream,
|
||||
nsIInputStream,
|
||||
nsISeekableStream)
|
||||
|
||||
nsresult
|
||||
ArchiveInputStream::Init()
|
||||
|
@ -82,21 +95,12 @@ ArchiveInputStream::Init()
|
|||
if (zerr != Z_OK)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
// Reset the data:
|
||||
memset(&mData, 0, sizeof(mData));
|
||||
mData.sizeToBeRead = ArchiveZipItem::StrToInt32(mCentral.size);
|
||||
|
||||
PRUint32 offset = ArchiveZipItem::StrToInt32(mCentral.localhdr_offset);
|
||||
PRUint64 size;
|
||||
rv = mArchiveReader->GetSize(&size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// The file is corrupt
|
||||
if (offset + ZIPLOCAL_SIZE > size)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
mArchiveReader->GetInputStream(getter_AddRefs(mData.inputStream));
|
||||
if (rv != NS_OK || !mData.inputStream)
|
||||
// The file is corrupt
|
||||
if (offset + ZIPLOCAL_SIZE > mData.parentSize)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
// From the input stream to a seekable stream
|
||||
|
@ -111,7 +115,7 @@ ArchiveInputStream::Init()
|
|||
PRUint32 ret;
|
||||
|
||||
rv = mData.inputStream->Read((char*)buffer, ZIPLOCAL_SIZE, &ret);
|
||||
if (rv != NS_OK || ret != ZIPLOCAL_SIZE)
|
||||
if (NS_FAILED(rv) || ret != ZIPLOCAL_SIZE)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
// Signature check:
|
||||
|
@ -127,7 +131,7 @@ ArchiveInputStream::Init()
|
|||
ArchiveZipItem::StrToInt16(local.extrafield_len);
|
||||
|
||||
// The file is corrupt if there is not enough data
|
||||
if (offset + mData.sizeToBeRead > size)
|
||||
if (offset + mData.sizeToBeRead > mData.parentSize)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
// Data starts here:
|
||||
|
@ -138,20 +142,8 @@ ArchiveInputStream::Init()
|
|||
|
||||
// We have to skip the first mStart bytes:
|
||||
if (mStart != 0) {
|
||||
PRUint32 done(mStart);
|
||||
PRUint32 ret;
|
||||
char buffer[1024];
|
||||
|
||||
while (done > 0) {
|
||||
rv = Read(buffer, done > sizeof(buffer) ? sizeof(buffer) : done, &ret);
|
||||
if (rv != NS_OK)
|
||||
return rv;
|
||||
|
||||
if (ret == 0)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
done -= ret;
|
||||
}
|
||||
rv = Seek(NS_SEEK_SET, mStart);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -171,7 +163,7 @@ ArchiveInputStream::Close()
|
|||
NS_IMETHODIMP
|
||||
ArchiveInputStream::Available(PRUint64* _retval)
|
||||
{
|
||||
*_retval = mLength - mZs.total_out - mStart;
|
||||
*_retval = mLength - mData.cursor - mStart;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -190,7 +182,7 @@ ArchiveInputStream::Read(char* aBuffer,
|
|||
mStatus = Started;
|
||||
|
||||
rv = Init();
|
||||
if (rv != NS_OK)
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Let's set avail_out to -1 so we read something from the stream.
|
||||
|
@ -208,10 +200,11 @@ ArchiveInputStream::Read(char* aBuffer,
|
|||
{
|
||||
rv = mData.inputStream->Read(aBuffer,
|
||||
(mData.sizeToBeRead > aCount ?
|
||||
aCount : mData.sizeToBeRead),
|
||||
aCount : mData.sizeToBeRead),
|
||||
_retval);
|
||||
if (rv == NS_OK) {
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mData.sizeToBeRead -= *_retval;
|
||||
mData.cursor += *_retval;
|
||||
|
||||
if (mData.sizeToBeRead == 0)
|
||||
mStatus = Done;
|
||||
|
@ -228,7 +221,7 @@ ArchiveInputStream::Read(char* aBuffer,
|
|||
(mData.sizeToBeRead > sizeof(mData.input) ?
|
||||
sizeof(mData.input) : mData.sizeToBeRead),
|
||||
&ret);
|
||||
if (rv != NS_OK)
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Terminator:
|
||||
|
@ -253,6 +246,7 @@ ArchiveInputStream::Read(char* aBuffer,
|
|||
mStatus = Done;
|
||||
|
||||
*_retval = aCount - mZs.avail_out;
|
||||
mData.cursor += *_retval;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -275,6 +269,76 @@ ArchiveInputStream::IsNonBlocking(bool* _retval)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ArchiveInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
|
||||
{
|
||||
PRInt64 pos = aOffset;
|
||||
|
||||
switch (aWhence) {
|
||||
case NS_SEEK_SET:
|
||||
break;
|
||||
|
||||
case NS_SEEK_CUR:
|
||||
pos += mData.cursor;
|
||||
break;
|
||||
|
||||
case NS_SEEK_END:
|
||||
pos += mLength;
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_NOTREACHED("unexpected whence value");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (pos == PRInt64(mData.cursor))
|
||||
return NS_OK;
|
||||
|
||||
if (pos < 0 || pos >= mLength)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// We have to terminate the previous operation:
|
||||
nsresult rv;
|
||||
if (mStatus != NotStarted) {
|
||||
rv = Close();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Reset the cursor:
|
||||
mData.cursor = 0;
|
||||
|
||||
// Note: This code is heavy but inflate does not have any seek() support:
|
||||
PRUint32 ret;
|
||||
char buffer[1024];
|
||||
while (pos > 0) {
|
||||
rv = Read(buffer, pos > PRInt64(sizeof(buffer)) ? sizeof(buffer) : pos, &ret);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (ret == 0)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
pos -= ret;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ArchiveInputStream::Tell(PRInt64 *aResult)
|
||||
{
|
||||
if (NS_FAILED(mStatus))
|
||||
return mStatus;
|
||||
|
||||
LL_UI2L(*aResult, mData.cursor);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ArchiveInputStream::SetEOF()
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// ArchiveZipFile
|
||||
|
||||
|
@ -284,7 +348,17 @@ ArchiveZipFile::GetInternalStream(nsIInputStream** aStream)
|
|||
if (mLength > PR_INT32_MAX)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsRefPtr<ArchiveInputStream> stream = new ArchiveInputStream(mArchiveReader,
|
||||
PRUint64 size;
|
||||
nsresult rv = mArchiveReader->GetSize(&size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
rv = mArchiveReader->GetInputStream(getter_AddRefs(inputStream));
|
||||
if (NS_FAILED(rv) || !inputStream)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
nsRefPtr<ArchiveInputStream> stream = new ArchiveInputStream(size,
|
||||
inputStream,
|
||||
mFilename,
|
||||
mStart,
|
||||
mLength,
|
||||
|
|
|
@ -30,6 +30,7 @@ MOCHITEST_FILES = \
|
|||
test_write_read_data.html \
|
||||
test_workers.html \
|
||||
test_archivereader.html \
|
||||
test_archivereader_zip_in_zip.html \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Archive Reader Zip-In-Zip 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 createZipInZipData() {
|
||||
var Cc = SpecialPowers.wrap(Components).classes;
|
||||
var Ci = SpecialPowers.wrap(Components).interfaces;
|
||||
|
||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
var testFile = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
testFile.append("fileArchiveReader_42.zip");
|
||||
var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
|
||||
outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
|
||||
0666, 0);
|
||||
|
||||
var binaryData = "";
|
||||
for (var i = 0, len = binaryString.length / 2; i < len; ++i) {
|
||||
var hex = binaryString[i * 2] + binaryString[i * 2 + 1];
|
||||
binaryData += String.fromCharCode(parseInt(hex,16));
|
||||
}
|
||||
outStream.write(binaryData, binaryData.length);
|
||||
outStream.close();
|
||||
|
||||
var fileList = document.getElementById('fileList');
|
||||
SpecialPowers.wrap(fileList).value = testFile.path;
|
||||
|
||||
return fileList.files[0];
|
||||
}
|
||||
|
||||
function testSteps()
|
||||
{
|
||||
var binaryFile = createZipInZipData();
|
||||
|
||||
// The input is 4 nested zip archives:
|
||||
doLoop(binaryFile, 4);
|
||||
|
||||
yield;
|
||||
}
|
||||
|
||||
function doLoop(blob, loop)
|
||||
{
|
||||
var r = new ArchiveReader(blob);
|
||||
isnot(r, null, "ArchiveReader cannot be null");
|
||||
|
||||
// GetFilename
|
||||
var handle = r.getFilenames();
|
||||
isnot(handle, null, "ArchiveReader.getFilenames() cannot be null");
|
||||
handle.onsuccess = function() {
|
||||
ok(true, "ArchiveReader.getFilenames() should return a 'success'");
|
||||
is(this.result instanceof Array, true, "ArchiveReader.getFilenames() should return an array");
|
||||
is(this.result.length, 1, "ArchiveReader.getFilenames(): the array contains 1 item");
|
||||
ok(this.reader, r, "ArchiveRequest.reader should be == ArchiveReader");
|
||||
|
||||
dump('Content:\n');
|
||||
for (var i = 0; i < this.result.length; ++i)
|
||||
dump(' * ' + this.result[i] + '\n');
|
||||
|
||||
var handle = r.getFile(this.result[0]);
|
||||
handle.onerror = function() {
|
||||
ok(false, "ArchiveReader.getFile() should not return any 'error'");
|
||||
}
|
||||
handle.onsuccess = function() {
|
||||
ok(true, "ArchiveReader.getFilenames() should return a 'success'");
|
||||
ok(this.reader, r, "ArchiveRequest.reader should be == ArchiveReader");
|
||||
|
||||
if (loop > 0)
|
||||
doLoop(this.result, loop - 1);
|
||||
else
|
||||
doLastLoop(this.result);
|
||||
}
|
||||
}
|
||||
handle.onerror = function() {
|
||||
ok(false, "ArchiveReader.getFilenames() should not return any 'error'");
|
||||
}
|
||||
}
|
||||
|
||||
function doLastLoop(blob)
|
||||
{
|
||||
ok(blob.size == 262144, "The last file size is wrong");
|
||||
ok(blob.name == 'empty.dat', "The last filename is wrong");
|
||||
finishTest();
|
||||
}
|
||||
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();">
|
||||
<p id="display">
|
||||
<input id="fileList" type="file"></input>
|
||||
</p>
|
||||
<script type="text/javascript;version=1.7">
|
||||
var binaryString = '' +
|
||||
'504B03040A0000000000B0620E415F715F15970300009703000005001C00642E7A69705554090003AC262A50AC262A507578' +
|
||||
'0B000104E803000004E8030000504B03040A0000000000B0620E41CFE25F1EF7020000F702000005001C00632E7A69705554' +
|
||||
'090003AC262A50AC262A5075780B000104E803000004E8030000504B03040A0000000000B0620E4107D702A4570200005702' +
|
||||
'000005001C00622E7A69705554090003AC262A50AC262A5075780B000104E803000004E8030000504B03040A0000000000B0' +
|
||||
'620E417E45286DB7010000B701000005001C00612E7A69705554090003AC262A50AC262A5075780B000104E803000004E803' +
|
||||
'0000504B0304140000000800F7610E41784909B70F0100000000040009001C00656D7074792E646174555409000351252A50' +
|
||||
'57252A5075780B000104E803000004E8030000EDC13101000000C2A0FEA9E76D07A000000000000000000000000000000000' +
|
||||
'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +
|
||||
'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +
|
||||
'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +
|
||||
'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +
|
||||
'0000000000000000000000000000000000000000000000000000000000000000000000000000DE00504B01021E0314000000' +
|
||||
'0800F7610E41784909B70F01000000000400090018000000000001000000B48100000000656D7074792E6461745554050003' +
|
||||
'51252A5075780B000104E803000004E8030000504B050600000000010001004F000000520100000000504B01021E030A0000' +
|
||||
'000000B0620E417E45286DB7010000B7010000050018000000000000000000B48100000000612E7A69705554050003AC262A' +
|
||||
'5075780B000104E803000004E8030000504B050600000000010001004B000000F60100000000504B01021E030A0000000000' +
|
||||
'B0620E4107D702A45702000057020000050018000000000000000000B48100000000622E7A69705554050003AC262A507578' +
|
||||
'0B000104E803000004E8030000504B050600000000010001004B000000960200000000504B01021E030A0000000000B0620E' +
|
||||
'41CFE25F1EF7020000F7020000050018000000000000000000B48100000000632E7A69705554050003AC262A5075780B0001' +
|
||||
'04E803000004E8030000504B050600000000010001004B000000360300000000504B01021E030A0000000000B0620E415F71' +
|
||||
'5F159703000097030000050018000000000000000000B48100000000642E7A69705554050003AC262A5075780B000104E803' +
|
||||
'000004E8030000504B050600000000010001004B000000D60300000000';
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -947,15 +947,13 @@ ContentChild::RecvLastPrivateDocShellDestroyed()
|
|||
bool
|
||||
ContentChild::RecvFilePathUpdate(const nsString& path, const nsCString& aReason)
|
||||
{
|
||||
// data strings will have the format of
|
||||
// reason:path
|
||||
nsString data;
|
||||
CopyASCIItoUTF16(aReason, data);
|
||||
data.Append(NS_LITERAL_STRING(":"));
|
||||
data.Append(path);
|
||||
nsCOMPtr<nsIFile> file;
|
||||
NS_NewLocalFile(path, false, getter_AddRefs(file));
|
||||
|
||||
nsString reason;
|
||||
CopyASCIItoUTF16(aReason, reason);
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->NotifyObservers(nullptr, "file-watcher-update", data.get());
|
||||
obs->NotifyObservers(file, "file-watcher-update", reason.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -370,6 +370,7 @@ ContentParent::Init()
|
|||
obs->AddObserver(this, "child-gc-request", false);
|
||||
obs->AddObserver(this, "child-cc-request", false);
|
||||
obs->AddObserver(this, "last-pb-context-exited", false);
|
||||
obs->AddObserver(this, "file-watcher-update", false);
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false);
|
||||
#endif
|
||||
|
@ -543,6 +544,7 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
|
|||
obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-gc-request");
|
||||
obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-cc-request");
|
||||
obs->RemoveObserver(static_cast<nsIObserver*>(this), "last-pb-context-exited");
|
||||
obs->RemoveObserver(static_cast<nsIObserver*>(this), "file-watcher-update");
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
obs->RemoveObserver(static_cast<nsIObserver*>(this), NS_VOLUME_STATE_CHANGED);
|
||||
#endif
|
||||
|
@ -697,8 +699,6 @@ ContentParent::ContentParent(const nsAString& aAppManifestURL)
|
|||
//Sending all information to content process
|
||||
unused << SendAppInfo(version, buildID);
|
||||
}
|
||||
|
||||
mFileWatchers.Init();
|
||||
}
|
||||
|
||||
ContentParent::~ContentParent()
|
||||
|
@ -938,9 +938,6 @@ ContentParent::Observe(nsISupports* aSubject,
|
|||
const PRUnichar* aData)
|
||||
{
|
||||
if (!strcmp(aTopic, "xpcom-shutdown") && mSubprocess) {
|
||||
|
||||
mFileWatchers.Clear();
|
||||
|
||||
Close();
|
||||
NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
|
||||
}
|
||||
|
@ -995,6 +992,18 @@ ContentParent::Observe(nsISupports* aSubject,
|
|||
else if (!strcmp(aTopic, "last-pb-context-exited")) {
|
||||
unused << SendLastPrivateDocShellDestroyed();
|
||||
}
|
||||
else if (!strcmp(aTopic, "file-watcher-update")) {
|
||||
nsCString creason;
|
||||
CopyUTF16toUTF8(aData, creason);
|
||||
nsCOMPtr<nsIFile> file = do_QueryInterface(aSubject);
|
||||
if (!file) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString path;
|
||||
file->GetPath(path);
|
||||
unused << SendFilePathUpdate(path, creason);
|
||||
}
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
|
||||
nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
|
||||
|
@ -1739,64 +1748,5 @@ ContentParent::RecvPrivateDocShellsExist(const bool& aExist)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvAddFileWatch(const nsString& root)
|
||||
{
|
||||
nsRefPtr<WatchedFile> f;
|
||||
if (mFileWatchers.Get(root, getter_AddRefs(f))) {
|
||||
f->mUsageCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
f = new WatchedFile(this, root);
|
||||
mFileWatchers.Put(root, f);
|
||||
|
||||
f->Watch();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvRemoveFileWatch(const nsString& root)
|
||||
{
|
||||
nsRefPtr<WatchedFile> f;
|
||||
bool result = mFileWatchers.Get(root, getter_AddRefs(f));
|
||||
if (!result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!f)
|
||||
return true;
|
||||
|
||||
f->mUsageCount--;
|
||||
|
||||
if (f->mUsageCount > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
f->Unwatch();
|
||||
mFileWatchers.Remove(root);
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(ContentParent::WatchedFile, nsIFileUpdateListener)
|
||||
|
||||
nsresult
|
||||
ContentParent::WatchedFile::Update(const char* aReason, nsIFile* aFile)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsString path;
|
||||
aFile->GetPath(path);
|
||||
|
||||
unused << mParent->SendFilePathUpdate(path, nsDependentCString(aReason));
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCString cpath;
|
||||
aFile->GetNativePath(cpath);
|
||||
printf("ContentParent::WatchedFile::Update: %s -- %s\n", cpath.get(), aReason);
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "nsIMemoryReporter.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsInterfaceHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
class mozIApplication;
|
||||
|
@ -274,9 +273,6 @@ private:
|
|||
|
||||
virtual bool RecvPrivateDocShellsExist(const bool& aExist);
|
||||
|
||||
virtual bool RecvAddFileWatch(const nsString& root);
|
||||
virtual bool RecvRemoveFileWatch(const nsString& root);
|
||||
|
||||
virtual void ProcessingError(Result what) MOZ_OVERRIDE;
|
||||
|
||||
GeckoChildProcessHost* mSubprocess;
|
||||
|
@ -297,34 +293,6 @@ private:
|
|||
const nsString mAppManifestURL;
|
||||
nsRefPtr<nsFrameMessageManager> mMessageManager;
|
||||
|
||||
class WatchedFile MOZ_FINAL : public nsIFileUpdateListener {
|
||||
public:
|
||||
WatchedFile(ContentParent* aParent, const nsString& aPath)
|
||||
: mParent(aParent)
|
||||
, mUsageCount(1)
|
||||
{
|
||||
NS_NewLocalFile(aPath, false, getter_AddRefs(mFile));
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIFILEUPDATELISTENER
|
||||
|
||||
void Watch() {
|
||||
mFile->Watch(this);
|
||||
}
|
||||
|
||||
void Unwatch() {
|
||||
mFile->Watch(this);
|
||||
}
|
||||
|
||||
nsRefPtr<ContentParent> mParent;
|
||||
PRInt32 mUsageCount;
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
};
|
||||
|
||||
// This is a cache of all of the registered file watchers.
|
||||
nsInterfaceHashtable<nsStringHashKey, WatchedFile> mFileWatchers;
|
||||
|
||||
friend class CrashReporterParent;
|
||||
};
|
||||
|
||||
|
|
|
@ -298,9 +298,6 @@ parent:
|
|||
// Notify the parent of the presence or absence of private docshells
|
||||
PrivateDocShellsExist(bool aExist);
|
||||
|
||||
AddFileWatch(nsString filepath);
|
||||
RemoveFileWatch(nsString filepath);
|
||||
|
||||
both:
|
||||
AsyncMessage(nsString aMessage, ClonedMessageData aData);
|
||||
};
|
||||
|
|
|
@ -127,8 +127,6 @@ MediaLoadDecodeError=Media resource %S could not be decoded.
|
|||
MozBlobBuilderWarning=Use of MozBlobBuilder is deprecated. Use Blob constructor instead.
|
||||
# LOCALIZATION NOTE: Do not translate "DOMException", "code" and "name"
|
||||
DOMExceptionCodeWarning=Use of DOMException's code attribute is deprecated. Use name instead.
|
||||
# LOCALIZATION NOTE: Do not translate "__exposedProps__"
|
||||
NoExposedPropsWarning=Exposing chrome JS objects to content without __exposedProps__ is insecure and deprecated. See https://developer.mozilla.org/en/XPConnect_wrappers for more information.
|
||||
# LOCALIZATION NOTE: Do not translate "Mutation Event" and "MutationObserver"
|
||||
MutationEventWarning=Use of Mutation Events is deprecated. Use MutationObserver instead.
|
||||
# LOCALIZATION NOTE: Do not translate "Blob", "mozSlice", or "slice"
|
||||
|
|
|
@ -153,7 +153,9 @@ CompositorParent::CompositorParent(nsIWidget* aWidget,
|
|||
CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(&AddCompositor,
|
||||
this, &mCompositorID));
|
||||
|
||||
sCurrentCompositor = this;
|
||||
if (!sCurrentCompositor) {
|
||||
sCurrentCompositor = this;
|
||||
}
|
||||
}
|
||||
|
||||
PlatformThreadId
|
||||
|
|
|
@ -887,15 +887,20 @@ ClonedBlockDepth(BytecodeEmitter *bce)
|
|||
static uint16_t
|
||||
AliasedNameToSlot(JSScript *script, PropertyName *name)
|
||||
{
|
||||
/*
|
||||
* Beware: BindingIter may contain more than one Binding for a given name
|
||||
* (in the case of |function f(x,x) {}|) but only one will be aliased.
|
||||
*/
|
||||
unsigned slot = CallObject::RESERVED_SLOTS;
|
||||
BindingIter bi(script->bindings);
|
||||
for (; bi->name() != name; bi++) {
|
||||
if (bi->aliased())
|
||||
for (BindingIter bi(script->bindings); ; bi++) {
|
||||
if (bi->aliased()) {
|
||||
if (bi->name() == name)
|
||||
return slot;
|
||||
slot++;
|
||||
}
|
||||
}
|
||||
|
||||
JS_ASSERT(bi->aliased());
|
||||
return slot;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
|
@ -324,12 +324,25 @@ Statistics::gcDuration(int64_t *total, int64_t *maxPause)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Statistics::sccDurations(int64_t *total, int64_t *maxPause)
|
||||
{
|
||||
*total = *maxPause = 0;
|
||||
for (size_t i = 0; i < sccTimes.length(); i++) {
|
||||
*total += sccTimes[i];
|
||||
*maxPause = Max(*maxPause, sccTimes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Statistics::formatData(StatisticsSerializer &ss, uint64_t timestamp)
|
||||
{
|
||||
int64_t total, longest;
|
||||
gcDuration(&total, &longest);
|
||||
|
||||
int64_t sccTotal, sccLongest;
|
||||
sccDurations(&sccTotal, &sccLongest);
|
||||
|
||||
double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC);
|
||||
double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
|
||||
|
||||
|
@ -341,6 +354,8 @@ Statistics::formatData(StatisticsSerializer &ss, uint64_t timestamp)
|
|||
ss.appendNumber("Total Compartments", "%d", "", compartmentCount);
|
||||
ss.appendNumber("MMU (20ms)", "%d", "%", int(mmu20 * 100));
|
||||
ss.appendNumber("MMU (50ms)", "%d", "%", int(mmu50 * 100));
|
||||
ss.appendDecimal("SCC Sweep Total", "ms", t(sccTotal));
|
||||
ss.appendDecimal("SCC Sweep Max Pause", "ms", t(sccLongest));
|
||||
if (slices.length() > 1 || ss.isJSON())
|
||||
ss.appendDecimal("Max Pause", "ms", t(longest));
|
||||
else
|
||||
|
@ -488,6 +503,7 @@ Statistics::beginGC()
|
|||
PodArrayZero(phaseTimes);
|
||||
|
||||
slices.clearAndFree();
|
||||
sccTimes.clearAndFree();
|
||||
nonincrementalReason = NULL;
|
||||
|
||||
preBytes = runtime->gcBytes;
|
||||
|
@ -508,6 +524,9 @@ Statistics::endGC()
|
|||
int64_t total, longest;
|
||||
gcDuration(&total, &longest);
|
||||
|
||||
int64_t sccTotal, sccLongest;
|
||||
sccDurations(&sccTotal, &sccLongest);
|
||||
|
||||
(*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, collectedCount == compartmentCount ? 0 : 1);
|
||||
(*cb)(JS_TELEMETRY_GC_MS, t(total));
|
||||
(*cb)(JS_TELEMETRY_GC_MAX_PAUSE_MS, t(longest));
|
||||
|
@ -517,6 +536,8 @@ Statistics::endGC()
|
|||
(*cb)(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_MARK_GRAY]));
|
||||
(*cb)(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason);
|
||||
(*cb)(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gcIncrementalEnabled);
|
||||
(*cb)(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal));
|
||||
(*cb)(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest));
|
||||
|
||||
double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
|
||||
(*cb)(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
|
||||
|
@ -605,6 +626,21 @@ Statistics::endPhase(Phase phase)
|
|||
Probes::GCEndSweepPhase();
|
||||
}
|
||||
|
||||
int64_t
|
||||
Statistics::beginSCC()
|
||||
{
|
||||
return PRMJ_Now();
|
||||
}
|
||||
|
||||
void
|
||||
Statistics::endSCC(unsigned scc, int64_t start)
|
||||
{
|
||||
if (scc >= sccTimes.length() && !sccTimes.resize(scc + 1))
|
||||
return;
|
||||
|
||||
sccTimes[scc] += PRMJ_Now() - start;
|
||||
}
|
||||
|
||||
/*
|
||||
* MMU (minimum mutator utilization) is a measure of how much garbage collection
|
||||
* is affecting the responsiveness of the system. MMU measurements are given
|
||||
|
|
|
@ -80,6 +80,9 @@ struct Statistics {
|
|||
counts[s]++;
|
||||
}
|
||||
|
||||
int64_t beginSCC();
|
||||
void endSCC(unsigned scc, int64_t start);
|
||||
|
||||
jschar *formatMessage();
|
||||
jschar *formatJSON(uint64_t timestamp);
|
||||
|
||||
|
@ -134,10 +137,14 @@ struct Statistics {
|
|||
/* Allocated space before the GC started. */
|
||||
size_t preBytes;
|
||||
|
||||
/* Sweep times for SCCs of compartments. */
|
||||
Vector<int64_t, 0, SystemAllocPolicy> sccTimes;
|
||||
|
||||
void beginGC();
|
||||
void endGC();
|
||||
|
||||
void gcDuration(int64_t *total, int64_t *maxPause);
|
||||
void sccDurations(int64_t *total, int64_t *maxPause);
|
||||
void printStats();
|
||||
bool formatData(StatisticsSerializer &ss, uint64_t timestamp);
|
||||
|
||||
|
@ -168,6 +175,17 @@ struct AutoPhase {
|
|||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
struct AutoSCC {
|
||||
AutoSCC(Statistics &stats, unsigned scc JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: stats(stats), scc(scc) { JS_GUARD_OBJECT_NOTIFIER_INIT; start = stats.beginSCC(); }
|
||||
~AutoSCC() { stats.endSCC(scc, start); }
|
||||
|
||||
Statistics &stats;
|
||||
unsigned scc;
|
||||
int64_t start;
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
} /* namespace gcstats */
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
assertEq((function(x, y, x) { return (function() x+y)(); })(1,2,5), 7);
|
|
@ -0,0 +1,11 @@
|
|||
// |jit-test| error:ReferenceError
|
||||
|
||||
try {
|
||||
evaluate(" (function(c) { const x = 1; for (x in null); })();");
|
||||
var expect = "Passed";
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
}
|
||||
schedulegc(10);
|
||||
eval("var o = new MyObject(); var result = 0; for (var o in foo) { result += this[o]; } ")
|
||||
function MyObject() {}
|
|
@ -7117,16 +7117,16 @@ JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency)
|
|||
VerifyBarriers(rt, PostBarrierVerifier);
|
||||
}
|
||||
|
||||
bool schedule = zeal >= js::gc::ZealAllocValue;
|
||||
rt->gcZeal_ = zeal;
|
||||
rt->gcZealFrequency = frequency;
|
||||
rt->gcNextScheduled = schedule ? frequency : 0;
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
/* In case JSCompartment::compileBarriers() changed... */
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next())
|
||||
mjit::ClearAllFrames(c);
|
||||
#endif
|
||||
|
||||
bool schedule = zeal >= js::gc::ZealAllocValue;
|
||||
rt->gcZeal_ = zeal;
|
||||
rt->gcZealFrequency = frequency;
|
||||
rt->gcNextScheduled = schedule ? frequency : 0;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
|
|
@ -300,6 +300,9 @@ struct JSCompartment
|
|||
size_t gcMallocAndFreeBytes;
|
||||
size_t gcTriggerMallocAndFreeBytes;
|
||||
|
||||
/* During GC, stores the index of this compartment in rt->compartments. */
|
||||
unsigned index;
|
||||
|
||||
private:
|
||||
/*
|
||||
* Malloc counter to measure memory pressure for GC scheduling. It runs from
|
||||
|
|
|
@ -84,7 +84,9 @@ enum {
|
|||
JS_TELEMETRY_GC_MMU_50,
|
||||
JS_TELEMETRY_GC_RESET,
|
||||
JS_TELEMETRY_GC_INCREMENTAL_DISABLED,
|
||||
JS_TELEMETRY_GC_NON_INCREMENTAL
|
||||
JS_TELEMETRY_GC_NON_INCREMENTAL,
|
||||
JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS,
|
||||
JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS
|
||||
};
|
||||
|
||||
typedef void
|
||||
|
|
155
js/src/jsgc.cpp
155
js/src/jsgc.cpp
|
@ -3482,6 +3482,141 @@ ValidateIncrementalMarking(JSRuntime *rt)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If compartment A has an edge to an unmarked object in compartment B, then we
|
||||
* must not sweep A in a later slice than we sweep B. That's because a write
|
||||
* barrier in A that could lead to the unmarked object in B becoming
|
||||
* marked. However, if we had already swept that object, we would be in trouble.
|
||||
*
|
||||
* If we consider these dependencies as a graph, then all the compartments in
|
||||
* any strongly-connected component of this graph must be swept in the same
|
||||
* slice. This class is used to compute these strongly connected components via
|
||||
* Tarjan's algorithm.
|
||||
*/
|
||||
class PartitionCompartments
|
||||
{
|
||||
typedef unsigned Node;
|
||||
typedef Vector<Node, 0, SystemAllocPolicy> NodeVector;
|
||||
typedef Vector<bool, 0, SystemAllocPolicy> BoolVector;
|
||||
|
||||
static const Node Undefined = Node(-1);
|
||||
|
||||
JSRuntime *runtime;
|
||||
|
||||
/*
|
||||
* The value of clock ticks monotonically upward as each new compartment is
|
||||
* discovered by the algorithm. When a new SCC is found, it is assigned a
|
||||
* number from nextSCC.
|
||||
*/
|
||||
Node clock, nextSCC;
|
||||
|
||||
/*
|
||||
* Compartments have an index based on their order in rt->compartments. The
|
||||
* index is used as a subscript into the arrays below.
|
||||
*
|
||||
* discoveryTime[comp]: The |clock| value when comp was first explored.
|
||||
* lowLink[comp]: The minimal discovery time of any compartment reachable
|
||||
* from |comp|.
|
||||
* stack: List of explored compartments that haven't been assigned to an SCC.
|
||||
* scc[comp]: SCC number that |comp| is in.
|
||||
* onStack[comp]: Whether |comp| in in |stack|.
|
||||
*/
|
||||
NodeVector discoveryTime, lowLink, stack, scc;
|
||||
BoolVector onStack;
|
||||
|
||||
bool fail_;
|
||||
|
||||
void processNode(Node v);
|
||||
void fail() { fail_ = true; }
|
||||
bool failed() { return fail_; }
|
||||
|
||||
public:
|
||||
PartitionCompartments(JSRuntime *rt);
|
||||
void partition();
|
||||
unsigned getSCC(JSCompartment *comp) { return failed() ? 0 : scc[comp->index]; }
|
||||
};
|
||||
|
||||
const PartitionCompartments::Node PartitionCompartments::Undefined;
|
||||
|
||||
PartitionCompartments::PartitionCompartments(JSRuntime *rt)
|
||||
: runtime(rt), clock(0), nextSCC(0), fail_(false)
|
||||
{
|
||||
size_t n = runtime->compartments.length();
|
||||
if (!discoveryTime.reserve(n) ||
|
||||
!lowLink.reserve(n) ||
|
||||
!scc.reserve(n) ||
|
||||
!onStack.reserve(n) ||
|
||||
!stack.reserve(n))
|
||||
{
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
|
||||
for (Node v = 0; v < runtime->compartments.length(); v++) {
|
||||
runtime->compartments[v]->index = v;
|
||||
discoveryTime.infallibleAppend(Undefined);
|
||||
lowLink.infallibleAppend(Undefined);
|
||||
scc.infallibleAppend(Undefined);
|
||||
onStack.infallibleAppend(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* See the Wikipedia article "Tarjan's strongly connected components algorithm". */
|
||||
void
|
||||
PartitionCompartments::processNode(Node v)
|
||||
{
|
||||
int stackDummy;
|
||||
if (failed() || !JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(runtime), &stackDummy)) {
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
|
||||
discoveryTime[v] = clock;
|
||||
lowLink[v] = clock;
|
||||
clock++;
|
||||
stack.infallibleAppend(v);
|
||||
onStack[v] = true;
|
||||
|
||||
JSCompartment *comp = runtime->compartments[v];
|
||||
|
||||
for (WrapperMap::Enum e(comp->crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
if (e.front().key.kind == CrossCompartmentKey::StringWrapper)
|
||||
continue;
|
||||
|
||||
Cell *other = e.front().key.wrapped;
|
||||
if (other->isMarked(BLACK) && !other->isMarked(GRAY))
|
||||
continue;
|
||||
|
||||
Node w = other->compartment()->index;
|
||||
|
||||
if (discoveryTime[w] == Undefined) {
|
||||
processNode(w);
|
||||
lowLink[v] = Min(lowLink[v], lowLink[w]);
|
||||
} else if (onStack[w]) {
|
||||
lowLink[v] = Min(lowLink[v], discoveryTime[w]);
|
||||
}
|
||||
}
|
||||
|
||||
if (lowLink[v] == discoveryTime[v]) {
|
||||
Node w;
|
||||
do {
|
||||
w = stack.popCopy();
|
||||
onStack[w] = false;
|
||||
scc[w] = nextSCC;
|
||||
} while (w != v);
|
||||
nextSCC++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PartitionCompartments::partition()
|
||||
{
|
||||
for (Node n = 0; n < runtime->compartments.length(); n++) {
|
||||
if (discoveryTime[n] == Undefined)
|
||||
processNode(n);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
BeginSweepPhase(JSRuntime *rt)
|
||||
{
|
||||
|
@ -3542,6 +3677,9 @@ BeginSweepPhase(JSRuntime *rt)
|
|||
/* Detach unreachable debuggers and global objects from each other. */
|
||||
Debugger::sweepAll(&fop);
|
||||
|
||||
PartitionCompartments partition(rt);
|
||||
partition.partition();
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_COMPARTMENTS);
|
||||
|
||||
|
@ -3554,6 +3692,7 @@ BeginSweepPhase(JSRuntime *rt)
|
|||
|
||||
bool releaseTypes = ReleaseObservedTypes(rt);
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
|
||||
if (c->isCollecting())
|
||||
c->sweep(&fop, releaseTypes);
|
||||
else
|
||||
|
@ -3569,14 +3708,22 @@ BeginSweepPhase(JSRuntime *rt)
|
|||
*
|
||||
* Objects are finalized immediately but this may change in the future.
|
||||
*/
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
|
||||
c->arenas.queueObjectsForSweep(&fop);
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
||||
}
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
|
||||
c->arenas.queueStringsForSweep(&fop);
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
||||
}
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
|
||||
c->arenas.queueScriptsForSweep(&fop);
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
||||
}
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
|
||||
c->arenas.queueShapesForSweep(&fop);
|
||||
}
|
||||
|
||||
rt->gcSweepPhase = 0;
|
||||
rt->gcSweepCompartmentIndex = 0;
|
||||
|
|
|
@ -665,7 +665,7 @@ class CallCompiler : public BaseCompiler
|
|||
bool patchInlinePath(JSScript *script, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(ic.frameSize.isStatic());
|
||||
JITScript *jit = script->getJIT(callingNew, f.cx->compartment->needsBarrier());
|
||||
JITScript *jit = script->getJIT(callingNew, f.cx->compartment->compileBarriers());
|
||||
|
||||
/* Very fast path. */
|
||||
Repatcher repatch(f.chunk());
|
||||
|
|
|
@ -546,7 +546,7 @@ class SetPropCompiler : public PICStubCompiler
|
|||
* Since we're changing the object's shape, we need a write
|
||||
* barrier. Taking the slow path is the easiest way to get one.
|
||||
*/
|
||||
if (cx->compartment->needsBarrier())
|
||||
if (cx->compartment->compileBarriers())
|
||||
return disable("ADDPROP write barrier required");
|
||||
#endif
|
||||
|
||||
|
@ -2884,7 +2884,7 @@ SetElementIC::shouldUpdate(VMFrame &f)
|
|||
return false;
|
||||
}
|
||||
#ifdef JSGC_INCREMENTAL_MJ
|
||||
JS_ASSERT(!f.cx->compartment->needsBarrier());
|
||||
JS_ASSERT(!f.cx->compartment->compileBarriers());
|
||||
#endif
|
||||
JS_ASSERT(stubsGenerated < MAX_PIC_STUBS);
|
||||
return true;
|
||||
|
|
|
@ -214,7 +214,7 @@ intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp)
|
|||
uint32_t errorNumber = args[0].toInt32();
|
||||
|
||||
char *errorArgs[3] = {NULL, NULL, NULL};
|
||||
for (int i = 1; i < 3 && i < args.length(); i++) {
|
||||
for (unsigned i = 1; i < 3 && i < args.length(); i++) {
|
||||
RootedValue val(cx, args[i]);
|
||||
if (val.isInt32() || val.isString()) {
|
||||
errorArgs[i - 1] = JS_EncodeString(cx, ToString(cx, val));
|
||||
|
@ -226,7 +226,7 @@ intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp)
|
|||
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber,
|
||||
errorArgs[0], errorArgs[1], errorArgs[2]);
|
||||
for (uint32_t i = 0; i < 3; i++)
|
||||
for (unsigned i = 0; i < 3; i++)
|
||||
cx->free_(errorArgs[i]);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ interface ScheduledGCCallback : nsISupports
|
|||
/**
|
||||
* interface of Components.utils
|
||||
*/
|
||||
[scriptable, uuid(cc7ef3b0-339d-4317-927b-fdc8f0664927)]
|
||||
[scriptable, uuid(25442383-a1f5-440c-9e18-a2a1cdb8a638)]
|
||||
interface nsIXPCComponents_Utils : nsISupports
|
||||
{
|
||||
|
||||
|
@ -329,6 +329,14 @@ interface nsIXPCComponents_Utils : nsISupports
|
|||
[implicit_jscontext]
|
||||
void recomputeWrappers([optional] in jsval vobj);
|
||||
|
||||
/*
|
||||
* Dispatches a runnable to the current/main thread. If |scope| is passed,
|
||||
* the runnable will be dispatch in the compartment of |scope|, which
|
||||
* affects which error reporter gets called.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
void dispatch(in jsval runnable, [optional] in jsval scope);
|
||||
|
||||
/*
|
||||
* To be called from JS only.
|
||||
*
|
||||
|
|
|
@ -4347,6 +4347,33 @@ nsXPCComponents_Utils::RecomputeWrappers(const jsval &vobj, JSContext *cx)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::Dispatch(const jsval &runnable_, const jsval &scope,
|
||||
JSContext *cx)
|
||||
{
|
||||
// Enter the given compartment, if any, and rewrap runnable.
|
||||
JSAutoEnterCompartment ac;
|
||||
js::Value runnable = runnable_;
|
||||
if (scope.isObject()) {
|
||||
JSObject *scopeObj = js::UnwrapObject(&scope.toObject());
|
||||
if (!scopeObj || !ac.enter(cx, scopeObj) || !JS_WrapValue(cx, &runnable))
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Get an XPCWrappedJS for |runnable|.
|
||||
if (!runnable.isObject())
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
nsCOMPtr<nsIRunnable> run;
|
||||
nsresult rv = nsXPConnect::GetXPConnect()->WrapJS(cx, &runnable.toObject(),
|
||||
NS_GET_IID(nsIRunnable),
|
||||
getter_AddRefs(run));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MOZ_ASSERT(run);
|
||||
|
||||
// Dispatch.
|
||||
return NS_DispatchToMainThread(run);
|
||||
}
|
||||
|
||||
/* string canCreateWrapper (in nsIIDPtr iid); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::CanCreateWrapper(const nsIID * iid, char **_retval)
|
||||
|
|
|
@ -2073,6 +2073,12 @@ AccumulateTelemetryCallback(int id, uint32_t sample)
|
|||
case JS_TELEMETRY_GC_NON_INCREMENTAL:
|
||||
Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL, sample);
|
||||
break;
|
||||
case JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS:
|
||||
Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_TOTAL_MS, sample);
|
||||
break;
|
||||
case JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS:
|
||||
Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_MAX_PAUSE_MS, sample);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ MOCHITEST_CHROME_FILES = \
|
|||
test_bug706301.xul \
|
||||
test_bug726949.xul \
|
||||
test_bug743843.xul \
|
||||
test_bug758563.xul \
|
||||
test_bug760076.xul \
|
||||
test_bug760109.xul \
|
||||
test_bug763343.xul \
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=758563
|
||||
-->
|
||||
<window title="Mozilla Bug 758563"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=758563"
|
||||
target="_blank">Mozilla Bug 758563</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for deprecation warnings for non-__exposedProps__ COWs. **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Set up our console listener.
|
||||
var gWarnings = 0;
|
||||
function onWarning(consoleMessage) {
|
||||
if (/__exposedProps__/.test(consoleMessage.message))
|
||||
gWarnings++;
|
||||
}
|
||||
var gListener = {
|
||||
observe: onWarning,
|
||||
QueryInterface: function (iid) {
|
||||
if (!iid.equals(Components.interfaces.nsIConsoleListener) &&
|
||||
!iid.equals(Components.interfaces.nsISupports)) {
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var gConsoleService = Components.classes["@mozilla.org/consoleservice;1"]
|
||||
.getService(Components.interfaces.nsIConsoleService);
|
||||
gConsoleService.registerListener(gListener);
|
||||
|
||||
// Wait for both child frame to load.
|
||||
var gLoadCount = 0;
|
||||
function frameLoaded() {
|
||||
if (++gLoadCount == 2)
|
||||
go();
|
||||
}
|
||||
|
||||
function go() {
|
||||
testFor('frame1');
|
||||
testFor('frame2');
|
||||
|
||||
// Warnings are dispatched async, so stick ourselves at the end of the event
|
||||
// queue.
|
||||
setTimeout(done, 0);
|
||||
}
|
||||
|
||||
function testFor(id) {
|
||||
var win = document.getElementById(id).contentWindow.wrappedJSObject;
|
||||
win.chromeObj = {a: 42};
|
||||
win.ok = ok;
|
||||
win.is = is;
|
||||
win.doAccess();
|
||||
}
|
||||
|
||||
function done() {
|
||||
gConsoleService.unregisterListener(gListener);
|
||||
is(gWarnings, 2, "Got the right number of warnings");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
]]>
|
||||
|
||||
</script>
|
||||
<iframe id="frame1" onload="frameLoaded();" type="content" src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_bug758563.html" />
|
||||
<iframe id="frame2" onload="frameLoaded();" type="content" src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_bug758563.html" />
|
||||
</window>
|
|
@ -95,10 +95,12 @@ function COWTests() {
|
|||
const PROPS_TO_TEST = ['foo', 'bar', 'prototype'];
|
||||
|
||||
var empty = {};
|
||||
// Once we flip the default for __exposedProps__, this should behave
|
||||
// the same as for function objects below.
|
||||
var nonempty = {foo: 42, bar: 33};
|
||||
is(getCOW(empty).foo, undefined,
|
||||
"shouldn't throw when accessing exposed properties that doesn't exist");
|
||||
PROPS_TO_TEST.forEach(function(name) {
|
||||
isPropHidden(getCOW(nonempty), name, "object without exposedProps");
|
||||
});
|
||||
|
||||
// Test function objects without __exposedProps__
|
||||
var func = function(x) { return 42; };
|
||||
|
|
|
@ -61,7 +61,6 @@ MOCHITEST_FILES = bug500931_helper.html \
|
|||
test_bug691059.html \
|
||||
file_bug706301.html \
|
||||
test_bug745483.html \
|
||||
file_bug758563.html \
|
||||
file_bug760131.html \
|
||||
test_bug764389.html \
|
||||
test_bug772288.html \
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<script type="application/javascript">
|
||||
function doAccess() {
|
||||
// Access the variable twice.
|
||||
oneAccess();
|
||||
oneAccess();
|
||||
}
|
||||
|
||||
function oneAccess() {
|
||||
try {
|
||||
is(window.chromeObj.a, 42, "Successfully read chrome property");
|
||||
} catch (e) { ok(false, "Threw while trying to access chrome property"); };
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -427,24 +427,6 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper:
|
|||
if (!wrapperAC.enter(cx, wrapper))
|
||||
return false;
|
||||
|
||||
// For now, only do this on functions.
|
||||
if (!JS_ObjectIsFunction(cx, wrappedObject)) {
|
||||
|
||||
// This little loop hole will go away soon! See bug 553102.
|
||||
nsCOMPtr<nsPIDOMWindow> win =
|
||||
do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(cx, wrapper));
|
||||
if (win) {
|
||||
nsCOMPtr<nsIDocument> doc =
|
||||
do_QueryInterface(win->GetExtantDocument());
|
||||
if (doc) {
|
||||
doc->WarnOnceAbout(nsIDocument::eNoExposedProps,
|
||||
/* asError = */ true);
|
||||
}
|
||||
}
|
||||
|
||||
perm = PermitPropertyAccess;
|
||||
return true;
|
||||
}
|
||||
return PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
|
||||
}
|
||||
|
||||
|
|
|
@ -1114,12 +1114,11 @@ nsColumnSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
nsDisplayGeneric(aBuilder, this, ::PaintColumnRule, "ColumnRule",
|
||||
nsDisplayItem::TYPE_COLUMN_RULE));
|
||||
|
||||
nsIFrame* kid = mFrames.FirstChild();
|
||||
// Our children won't have backgrounds so it doesn't matter where we put them.
|
||||
while (kid) {
|
||||
nsresult rv = BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
|
||||
for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
|
||||
nsresult rv = BuildDisplayListForChild(aBuilder, e.get(),
|
||||
aDirtyRect, aLists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
kid = kid->GetNextSibling();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -3638,7 +3638,6 @@ pref("dom.sms.whitelist", "");
|
|||
|
||||
// WebContacts
|
||||
pref("dom.mozContacts.enabled", false);
|
||||
pref("dom.mozContacts.whitelist", "");
|
||||
|
||||
// WebAlarms
|
||||
pref("dom.mozAlarms.enabled", false);
|
||||
|
|
|
@ -12,8 +12,6 @@ const sinkContract = "@mozilla.org/network/unittest/channeleventsink;1";
|
|||
|
||||
const categoryName = "net-channel-event-sinks";
|
||||
|
||||
const NS_BINDING_ABORTED = 0x804b0002;
|
||||
|
||||
/**
|
||||
* This object is both a factory and an nsIChannelEventSink implementation (so, it
|
||||
* is de-facto a service). It's also an interface requestor that gives out
|
||||
|
@ -39,7 +37,7 @@ var eventsink = {
|
|||
asyncOnChannelRedirect: function eventsink_onredir(oldChan, newChan, flags, callback) {
|
||||
// veto
|
||||
this.called = true;
|
||||
throw NS_BINDING_ABORTED;
|
||||
throw Components.results.NS_BINDING_ABORTED;
|
||||
},
|
||||
|
||||
getInterface: function eventsink_gi(iid) {
|
||||
|
|
|
@ -8,8 +8,6 @@ const Cr = Components.results;
|
|||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
const NS_BINDING_ABORTED = 0x804b0002;
|
||||
|
||||
var observer = {
|
||||
QueryInterface: function eventsink_qi(iid) {
|
||||
if (iid.equals(Components.interfaces.nsISupports) ||
|
||||
|
@ -20,7 +18,7 @@ var observer = {
|
|||
|
||||
observe: function(subject, topic, data) {
|
||||
subject = subject.QueryInterface(Components.interfaces.nsIRequest);
|
||||
subject.cancel(NS_BINDING_ABORTED);
|
||||
subject.cancel(Components.results.NS_BINDING_ABORTED);
|
||||
|
||||
var obs = Components.classes["@mozilla.org/observer-service;1"].getService();
|
||||
obs = obs.QueryInterface(Components.interfaces.nsIObserverService);
|
||||
|
@ -30,7 +28,7 @@ var observer = {
|
|||
|
||||
var listener = {
|
||||
onStartRequest: function test_onStartR(request, ctx) {
|
||||
do_check_eq(request.status, NS_BINDING_ABORTED);
|
||||
do_check_eq(request.status, Components.results.NS_BINDING_ABORTED);
|
||||
},
|
||||
|
||||
onDataAvailable: function test_ODA() {
|
||||
|
|
|
@ -1 +1 @@
|
|||
http://hg.mozilla.org/projects/addon-sdk/archive/78041d0c5005.tar.bz2
|
||||
http://hg.mozilla.org/projects/addon-sdk/archive/075a2e92da06.tar.bz2
|
||||
|
|
|
@ -652,25 +652,8 @@ SimpleTest.waitForClipboard = function(aExpectedStringOrValidatorFn, aSetupFn,
|
|||
* working (or finish).
|
||||
*/
|
||||
SimpleTest.executeSoon = function(aFunc) {
|
||||
// Once SpecialPowers is available in chrome mochitests, we can replace the
|
||||
// body of this function with a call to SpecialPowers.executeSoon().
|
||||
if ("Components" in window && "classes" in window.Components) {
|
||||
try {
|
||||
netscape.security.PrivilegeManager
|
||||
.enablePrivilege("UniversalXPConnect");
|
||||
var tm = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService(Components.interfaces.nsIThreadManager);
|
||||
|
||||
tm.mainThread.dispatch({
|
||||
run: function() {
|
||||
aFunc();
|
||||
}
|
||||
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
|
||||
return;
|
||||
} catch (ex) {
|
||||
// If the above fails (most likely because of enablePrivilege
|
||||
// failing), fall through to the setTimeout path.
|
||||
}
|
||||
if ("SpecialPowers" in window) {
|
||||
return SpecialPowers.executeSoon(aFunc, window);
|
||||
}
|
||||
setTimeout(aFunc, 0);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
var Ci = Components.interfaces;
|
||||
var Cc = Components.classes;
|
||||
var Cu = Components.utils;
|
||||
|
||||
Components.utils.import("resource://mochikit/MockFilePicker.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
@ -882,13 +883,16 @@ SpecialPowersAPI.prototype = {
|
|||
return this._xpcomabi;
|
||||
},
|
||||
|
||||
executeSoon: function(aFunc) {
|
||||
var tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
|
||||
tm.mainThread.dispatch({
|
||||
run: function() {
|
||||
aFunc();
|
||||
}
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
// The optional aWin parameter allows the caller to specify a given window in
|
||||
// whose scope the runnable should be dispatched. If aFun throws, the
|
||||
// exception will be reported to aWin.
|
||||
executeSoon: function(aFun, aWin) {
|
||||
// Create the runnable in the scope of aWin to avoid running into COWs.
|
||||
var runnable = {};
|
||||
if (aWin)
|
||||
runnable = Cu.createObjectIn(aWin);
|
||||
runnable.run = aFun;
|
||||
Cu.dispatch(runnable, aWin);
|
||||
},
|
||||
|
||||
_os: null,
|
||||
|
|
|
@ -62,6 +62,8 @@ HISTOGRAM(GC_MMU_50, 1, 100, 20, LINEAR, "Minimum percentage of time spent outsi
|
|||
HISTOGRAM_BOOLEAN(GC_RESET, "Was an incremental GC canceled?")
|
||||
HISTOGRAM_BOOLEAN(GC_INCREMENTAL_DISABLED, "Is incremental GC permanently disabled?")
|
||||
HISTOGRAM_BOOLEAN(GC_NON_INCREMENTAL, "Was the GC non-incremental?")
|
||||
HISTOGRAM(GC_SCC_SWEEP_TOTAL_MS, 1, 500, 50, LINEAR, "Time spent sweeping compartment SCCs (ms)")
|
||||
HISTOGRAM(GC_SCC_SWEEP_MAX_PAUSE_MS, 1, 500, 50, LINEAR, "Time spent sweeping slowest compartment SCC (ms)")
|
||||
|
||||
HISTOGRAM(TELEMETRY_PING, 1, 3000, 10, EXPONENTIAL, "Time taken to submit telemetry info (ms)")
|
||||
HISTOGRAM_BOOLEAN(TELEMETRY_SUCCESS, "Successful telemetry submission")
|
||||
|
|
|
@ -162,8 +162,7 @@ nsUnknownContentTypeDialog.prototype = {
|
|||
} catch (ex) {
|
||||
// The containing window may have gone away. Break reference
|
||||
// cycles and stop doing the download.
|
||||
const NS_BINDING_ABORTED = 0x804b0002;
|
||||
this.mLauncher.cancel(NS_BINDING_ABORTED);
|
||||
this.mLauncher.cancel(Components.results.NS_BINDING_ABORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -916,8 +915,7 @@ nsUnknownContentTypeDialog.prototype = {
|
|||
|
||||
// Cancel app launcher.
|
||||
try {
|
||||
const NS_BINDING_ABORTED = 0x804b0002;
|
||||
this.mLauncher.cancel(NS_BINDING_ABORTED);
|
||||
this.mLauncher.cancel(Components.results.NS_BINDING_ABORTED);
|
||||
} catch(exception) {
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
[ptr] native FILE(FILE);
|
||||
|
||||
interface nsISimpleEnumerator;
|
||||
interface nsIFileUpdateListener;
|
||||
|
||||
/**
|
||||
* This is the only correct cross-platform way to specify a file.
|
||||
|
@ -31,7 +30,7 @@ interface nsIFileUpdateListener;
|
|||
* be safely passed to javascript via xpconnect. Therefore, the "native
|
||||
* methods" are not scriptable.
|
||||
*/
|
||||
[scriptable, uuid(9117c043-c01b-487a-a7ad-32cb350b0971), builtinclass]
|
||||
[scriptable, uuid(272a5020-64f5-485c-a8c4-44b2882ae0a2), builtinclass]
|
||||
interface nsIFile : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -448,60 +447,6 @@ interface nsIFile : nsISupports
|
|||
* the relative descriptor obtained from getRelativeDescriptor
|
||||
*/
|
||||
void setRelativeDescriptor(in nsIFile fromFile, in ACString relativeDesc);
|
||||
|
||||
/**
|
||||
* watch
|
||||
*
|
||||
* Watches this file for changes, or if this nsIFile is a
|
||||
* directory, watch for changes in its children recursively, not
|
||||
* dereferencing symlinks. Multiple listeners can be installed
|
||||
* at once, and all will be called when any appropriate changes
|
||||
* are made. If a child directory is created, that directory
|
||||
* will automatically be watched. If the file is a symlink to a
|
||||
* directory or another file, the target will be watched for
|
||||
* changes, not the link.
|
||||
*
|
||||
* @param listener
|
||||
* The listener to call out to when the file updates.
|
||||
* Updated will be recieved on the main thread.
|
||||
*
|
||||
* @return NS_NOT_AVAILABLE if there is an out-of-memory or other
|
||||
* resource failure, NS_OK otherwise.
|
||||
*/
|
||||
void watch(in nsIFileUpdateListener listener);
|
||||
|
||||
/**
|
||||
*
|
||||
* unwatch
|
||||
*
|
||||
* Removes the watch using the given listener from the file.
|
||||
* After this function terminates, no more requests will call the
|
||||
* given listener.
|
||||
*
|
||||
* @param listener
|
||||
* the listener to stop calling out to
|
||||
*
|
||||
* @return NS_NOT_AVAILABLE if the file is not being
|
||||
* watched with the given listener, NS_OK otherwise.
|
||||
*/
|
||||
void unwatch(in nsIFileUpdateListener listener);
|
||||
};
|
||||
|
||||
[scriptable, uuid(8968aaba-0f95-436c-8baf-7092ccaa814c), function]
|
||||
interface nsIFileUpdateListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* update
|
||||
*
|
||||
* This function will be called whenever there is an update to be
|
||||
* processed.
|
||||
*
|
||||
* @param type
|
||||
* The type of update that occured (one of "created" "deleted" or "modified").
|
||||
* @param file
|
||||
* The file which has updated
|
||||
*/
|
||||
void update(in string type, in nsIFile file);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
#include "nsCRT.h"
|
||||
#include "nsNativeCharsetUtils.h"
|
||||
#include "nsUTF8Utils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
|
||||
void NS_StartupLocalFile()
|
||||
{
|
||||
nsLocalFile::GlobalInit();
|
||||
|
@ -288,17 +288,3 @@ nsLocalFile::SetRelativeDescriptor(nsIFile *fromFile, const nsACString& relative
|
|||
|
||||
return InitWithFile(targetFile);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLocalFile::Watch(nsIFileUpdateListener *listener)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Watch must be called from main thread!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLocalFile::Unwatch(nsIFileUpdateListener *listener)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Unwatch must be called from main thread!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче