Merge the last PGO-green inbound changeset to m-c.

This commit is contained in:
Ryan VanderMeulen 2012-08-18 07:22:18 -04:00
commit 450658219d
69 changed files with 1448 additions and 580 deletions

View File

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

View File

@ -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;
}
}
},

View File

@ -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;
},

View File

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

View File

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

View File

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

View File

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

View File

@ -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");

View File

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

View File

@ -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("*");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -274,7 +274,7 @@ DeviceStorageRequestParent::DeleteFileEvent::CancelableRun()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
mFile->mFile->Remove(true);
mFile->Remove();
nsRefPtr<nsRunnable> r;

View File

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

View File

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

View File

@ -20,6 +20,7 @@ MOCHITEST_FILES = \
test_enumerateOptions.html \
test_lastModificationFilter.html \
test_stat.html \
test_watch.html \
devicestorage_common.js \
$(NULL)

View File

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

View File

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

View File

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

View File

@ -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*)&centralStruct, 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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -153,7 +153,9 @@ CompositorParent::CompositorParent(nsIWidget* aWidget,
CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(&AddCompositor,
this, &mCompositorID));
sCurrentCompositor = this;
if (!sCurrentCompositor) {
sCurrentCompositor = this;
}
}
PlatformThreadId

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
assertEq((function(x, y, x) { return (function() x+y)(); })(1,2,5), 7);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3638,7 +3638,6 @@ pref("dom.sms.whitelist", "");
// WebContacts
pref("dom.mozContacts.enabled", false);
pref("dom.mozContacts.whitelist", "");
// WebAlarms
pref("dom.mozAlarms.enabled", false);

View File

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

View File

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

View File

@ -1 +1 @@
http://hg.mozilla.org/projects/addon-sdk/archive/78041d0c5005.tar.bz2
http://hg.mozilla.org/projects/addon-sdk/archive/075a2e92da06.tar.bz2

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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