Merge m-c to b2g-inbound. a=merge
Merge m-c to b2g-inbound. a=merge
CLOSED TREE
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -52,17 +52,17 @@ nsCoreUtils::HasClickListener(nsIContent
(listenerManager->HasListenersFor(nsGkAtoms::onclick) ||
listenerManager->HasListenersFor(nsGkAtoms::onmousedown) ||
listenerManager->HasListenersFor(nsGkAtoms::onmouseup));
}
void
nsCoreUtils::DispatchClickEvent(nsITreeBoxObject *aTreeBoxObj,
int32_t aRowIndex, nsITreeColumn *aColumn,
- const nsCString& aPseudoElt)
+ const nsAString& aPseudoElt)
{
nsCOMPtr<nsIDOMElement> tcElm;
aTreeBoxObj->GetTreeBody(getter_AddRefs(tcElm));
if (!tcElm)
return;
nsCOMPtr<nsIContent> tcContent(do_QueryInterface(tcElm));
nsIDocument *document = tcContent->GetCurrentDoc();
--- a/accessible/base/nsCoreUtils.h
+++ b/accessible/base/nsCoreUtils.h
@@ -39,17 +39,17 @@ public:
* @param aTreeBoxObj [in] tree box object
* @param aRowIndex [in] row index
* @param aColumn [in] column object
* @param aPseudoElm [in] pseudo elemenet inside the cell, see
* nsITreeBoxObject for available values
*/
static void DispatchClickEvent(nsITreeBoxObject *aTreeBoxObj,
int32_t aRowIndex, nsITreeColumn *aColumn,
- const nsCString& aPseudoElt = EmptyCString());
+ const nsAString& aPseudoElt = EmptyString());
/**
* Send mouse event to the given element.
*
* @param aEventType [in] an event type (see BasicEvents.h for constants)
* @param aX [in] x coordinate in dev pixels
* @param aY [in] y coordinate in dev pixels
* @param aContent [in] the element
@@ -309,9 +309,8 @@ public:
static bool IsWhitespace(char16_t aChar)
{
return aChar == ' ' || aChar == '\n' ||
aChar == '\r' || aChar == '\t' || aChar == 0xa0;
}
};
#endif
-
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -15,16 +15,18 @@ this.EXPORTED_SYMBOLS = ['AccessFu']; //
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
const ACCESSFU_DISABLE = 0; // jshint ignore:line
const ACCESSFU_ENABLE = 1;
const ACCESSFU_AUTO = 2;
const SCREENREADER_SETTING = 'accessibility.screenreader';
+const QUICKNAV_MODES_PREF = 'accessibility.accessfu.quicknav_modes';
+const QUICKNAV_INDEX_PREF = 'accessibility.accessfu.quicknav_index';
this.AccessFu = { // jshint ignore:line
/**
* Initialize chrome-layer accessibility functionality.
* If accessibility is enabled on the platform, then a special accessibility
* mode is started.
*/
attach: function attach(aWindow) {
@@ -98,21 +100,28 @@ this.AccessFu = { // jshint ignore:line
let stylesheet = Utils.win.document.createProcessingInstruction(
'xml-stylesheet', 'href="' + stylesheetURL + '" type="text/css"');
Utils.win.document.insertBefore(stylesheet, Utils.win.document.firstChild);
this.stylesheet = Cu.getWeakReference(stylesheet);
// Populate quicknav modes
this._quicknavModesPref =
- new PrefCache(
- 'accessibility.accessfu.quicknav_modes',
- (aName, aValue) => {
- this.Input.quickNavMode.updateModes(aValue);
- }, true);
+ new PrefCache(QUICKNAV_MODES_PREF, (aName, aValue, aFirstRun) => {
+ this.Input.quickNavMode.updateModes(aValue);
+ if (!aFirstRun) {
+ // If the modes change, reset the current mode index to 0.
+ Services.prefs.setIntPref(QUICKNAV_INDEX_PREF, 0);
+ }
+ }, true);
+
+ this._quicknavCurrentModePref =
+ new PrefCache(QUICKNAV_INDEX_PREF, (aName, aValue) => {
+ this.Input.quickNavMode.updateCurrentMode(Number(aValue));
+ }, true);
// Check for output notification
this._notifyOutputPref =
new PrefCache('accessibility.accessfu.notify_output');
this.Input.start();
Output.start();
@@ -662,27 +671,31 @@ var Input = {
aGesture.touches[0].y);
break;
case 'doubletap1':
this.activateCurrent();
break;
case 'taphold1':
this.sendContextMenuMessage();
break;
+ case 'doubletaphold1':
+ Utils.dispatchChromeEvent('accessibility-control', 'quicknav-menu');
+ break;
case 'swiperight1':
this.moveCursor('moveNext', 'Simple', 'gestures');
break;
case 'swipeleft1':
this.moveCursor('movePrevious', 'Simple', 'gesture');
break;
case 'swipeup1':
- this.contextAction('backward');
+ this.moveCursor(
+ 'movePrevious', this.quickNavMode.current, 'gesture', true);
break;
case 'swipedown1':
- this.contextAction('forward');
+ this.moveCursor('moveNext', this.quickNavMode.current, 'gesture', true);
break;
case 'exploreend1':
case 'dwellend1':
this.activateCurrent(null, true);
break;
case 'swiperight2':
if (aGesture.edge) {
Utils.dispatchChromeEvent('accessibility-control',
@@ -824,27 +837,22 @@ var Input = {
{rule: aRule, x: aX, y: aY, origin: 'top'});
} else {
let win = Utils.win;
Utils.winUtils.sendMouseEvent('mousemove',
aX - win.mozInnerScreenX, aY - win.mozInnerScreenY, 0, 0, 0);
}
},
- moveCursor: function moveCursor(aAction, aRule, aInputType) {
+ moveCursor: function moveCursor(aAction, aRule, aInputType, aAdjustRange) {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:MoveCursor',
- {action: aAction, rule: aRule,
- origin: 'top', inputType: aInputType});
- },
-
- contextAction: function contextAction(aDirection) {
- // XXX: For now, the only supported context action is adjusting a range.
- let mm = Utils.getMessageManager(Utils.CurrentBrowser);
- mm.sendAsyncMessage('AccessFu:AdjustRange', {direction: aDirection});
+ { action: aAction, rule: aRule,
+ origin: 'top', inputType: aInputType,
+ adjustRange: aAdjustRange });
},
moveByGranularity: function moveByGranularity(aDetails) {
const MOVEMENT_GRANULARITY_PARAGRAPH = 8;
if (!this.editState.editing) {
if (aDetails.granularity === MOVEMENT_GRANULARITY_PARAGRAPH) {
this.moveCursor('move' + aDetails.direction, 'Paragraph', 'gesture');
@@ -952,31 +960,34 @@ var Input = {
},
quickNavMode: {
get current() {
return this.modes[this._currentIndex];
},
previous: function quickNavMode_previous() {
- if (--this._currentIndex < 0) {
- this._currentIndex = this.modes.length - 1;
- }
+ Services.prefs.setIntPref(QUICKNAV_INDEX_PREF,
+ this._currentIndex > 0 ?
+ this._currentIndex - 1 : this.modes.length - 1);
},
next: function quickNavMode_next() {
- if (++this._currentIndex >= this.modes.length) {
- this._currentIndex = 0;
- }
+ Services.prefs.setIntPref(QUICKNAV_INDEX_PREF,
+ this._currentIndex + 1 >= this.modes.length ?
+ 0 : this._currentIndex + 1);
},
updateModes: function updateModes(aModes) {
if (aModes) {
this.modes = aModes.split(',');
} else {
this.modes = [];
}
},
- _currentIndex: -1
+ updateCurrentMode: function updateCurrentMode(aModeIndex) {
+ Logger.debug('Quicknav mode:', this.modes[aModeIndex]);
+ this._currentIndex = aModeIndex;
+ }
}
};
AccessFu.Input = Input;
--- a/accessible/jsat/ContentControl.jsm
+++ b/accessible/jsat/ContentControl.jsm
@@ -89,23 +89,28 @@ this.ContentControl.prototype = {
Logger.logException(
x, 'Error handling message: ' + JSON.stringify(aMessage.json));
}
},
handleMoveCursor: function cc_handleMoveCursor(aMessage) {
let origin = aMessage.json.origin;
let action = aMessage.json.action;
+ let adjustRange = aMessage.json.adjustRange;
let vc = this.vc;
if (origin != 'child' && this.sendToChild(vc, aMessage)) {
// Forwarded succesfully to child cursor.
return;
}
+ if (adjustRange && this.adjustRange(vc.position, action === 'moveNext')) {
+ return;
+ }
+
let moved = vc[action](TraversalRules[aMessage.json.rule]);
if (moved) {
if (origin === 'child') {
// We just stepped out of a child, clear child cursor.
Utils.getMessageManager(aMessage.target).sendAsyncMessage(
'AccessFu:ClearCursor', {});
} else {
@@ -117,20 +122,24 @@ this.ContentControl.prototype = {
} else if (action === 'movePrevious') {
childAction = 'moveLast';
}
// Attempt to forward move to a potential child cursor in our
// new position.
this.sendToChild(vc, aMessage, { action: childAction }, true);
}
- } else if (!this._childMessageSenders.has(aMessage.target)) {
- // We failed to move, and the message is not from a child, so forward
- // to parent.
+ } else if (!this._childMessageSenders.has(aMessage.target) &&
+ origin !== 'top') {
+ // We failed to move, and the message is not from a parent, so forward
+ // to it.
this.sendToParent(aMessage);
+ } else {
+ this._contentScope.get().sendAsyncMessage('AccessFu:Present',
+ Presentation.noMove(action));
}
},
handleEvent: function cc_handleEvent(aEvent) {
if (aEvent.type === 'mousemove') {
this.handleMoveToPoint(
{ json: { x: aEvent.screenX, y: aEvent.screenY, rule: 'Simple' } });
}
@@ -164,17 +173,17 @@ this.ContentControl.prototype = {
handleActivate: function cc_handleActivate(aMessage) {
let activateAccessible = (aAccessible) => {
Logger.debug(() => {
return ['activateAccessible', Logger.accessibleToString(aAccessible)];
});
try {
if (aMessage.json.activateIfKey &&
- aAccessible.role != Roles.KEY) {
+ !Utils.isActivatableOnFingerUp(aAccessible)) {
// Only activate keys, don't do anything on other objects.
return;
}
} catch (e) {
// accessible is invalid. Silently fail.
return;
}
@@ -205,17 +214,17 @@ this.ContentControl.prototype = {
for (let eventType of ['mousedown', 'mouseup']) {
let evt = this.document.createEvent('MouseEvents');
evt.initMouseEvent(eventType, true, true, this.window,
x, y, 0, 0, 0, false, false, false, false, 0, null);
node.dispatchEvent(evt);
}
}
- if (aAccessible.role !== Roles.KEY) {
+ if (!Utils.isActivatableOnFingerUp(aAccessible)) {
// Keys will typically have a sound of their own.
this._contentScope.get().sendAsyncMessage('AccessFu:Present',
Presentation.actionInvoked(aAccessible, 'click'));
}
};
let focusedAcc = Utils.AccRetrieval.getAccessibleFor(
this.document.activeElement);
@@ -252,16 +261,47 @@ this.ContentControl.prototype = {
let vc = this.vc;
if (!this.sendToChild(vc, aMessage, null, true)) {
let position = vc.position;
activateAccessible(getActivatableDescendant(position) || position);
}
},
+ adjustRange: function cc_adjustRange(aAccessible, aStepUp) {
+ let acc = Utils.getEmbeddedControl(aAccessible) || aAccessible;
+ try {
+ acc.QueryInterface(Ci.nsIAccessibleValue);
+ } catch (x) {
+ // This is not an adjustable, return false.
+ return false;
+ }
+
+ let elem = acc.DOMNode;
+ if (!elem) {
+ return false;
+ }
+
+ if (elem.tagName === 'INPUT' && elem.type === 'range') {
+ elem[aStepUp ? 'stepDown' : 'stepUp']();
+ let evt = this.document.createEvent('UIEvent');
+ evt.initEvent('change', true, true);
+ elem.dispatchEvent(evt);
+ } else {
+ let evt = this.document.createEvent('KeyboardEvent');
+ let keycode = aStepUp ? content.KeyEvent.DOM_VK_DOWN :
+ content.KeyEvent.DOM_VK_UP;
+ evt.initKeyEvent(
+ "keypress", false, true, null, false, false, false, false, keycode, 0);
+ elem.dispatchEvent(evt);
+ }
+
+ return true;
+ },
+
handleMoveByGranularity: function cc_handleMoveByGranularity(aMessage) {
// XXX: Add sendToChild. Right now this is only used in Android, so no need.
let direction = aMessage.json.direction;
let granularity;
switch(aMessage.json.granularity) {
case MOVEMENT_GRANULARITY_CHARACTER:
granularity = Ci.nsIAccessiblePivot.CHAR_BOUNDARY;
--- a/accessible/jsat/Presentation.jsm
+++ b/accessible/jsat/Presentation.jsm
@@ -112,16 +112,21 @@ Presenter.prototype = {
editingModeChanged: function editingModeChanged(aIsEditing) {}, // jshint ignore:line
/**
* Announce something. Typically an app state change.
*/
announce: function announce(aAnnouncement) {}, // jshint ignore:line
+ /**
+ * User tried to move cursor forward or backward with no success.
+ * @param {string} aMoveMethod move method that was used (eg. 'moveNext').
+ */
+ noMove: function noMove(aMoveMethod) {},
/**
* Announce a live region.
* @param {PivotContext} aContext context object for an accessible.
* @param {boolean} aIsPolite A politeness level for a live region.
* @param {boolean} aIsHide An indicator of hide/remove event.
* @param {string} aModifiedText Optional modified text.
*/
@@ -477,17 +482,17 @@ B2GPresenter.prototype.pivotChanged =
return {
type: this.type,
details: {
eventType: 'vc-change',
data: UtteranceGenerator.genForContext(aContext),
options: {
pattern: this.PIVOT_CHANGE_HAPTIC_PATTERN,
- isKey: aContext.accessible.role === Roles.KEY,
+ isKey: Utils.isActivatableOnFingerUp(aContext.accessible),
reason: this.pivotChangedReasons[aReason],
isUserInput: aIsUserInput
}
}
};
};
B2GPresenter.prototype.valueChanged =
@@ -531,16 +536,27 @@ B2GPresenter.prototype.announce =
type: this.type,
details: {
eventType: 'announcement',
data: aAnnouncement
}
};
};
+B2GPresenter.prototype.noMove =
+ function B2GPresenter_noMove(aMoveMethod) {
+ return {
+ type: this.type,
+ details: {
+ eventType: 'no-move',
+ data: aMoveMethod
+ }
+ };
+ };
+
/**
* A braille presenter
*/
function BraillePresenter() {}
BraillePresenter.prototype = Object.create(Presenter.prototype);
BraillePresenter.prototype.type = 'Braille';
@@ -631,16 +647,20 @@ this.Presentation = { // jshint ignore:l
announce: function Presentation_announce(aAnnouncement) {
// XXX: Typically each presenter uses the UtteranceGenerator,
// but there really isn't a point here.
return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement)) // jshint ignore:line
for each (p in this.presenters)]; // jshint ignore:line
},
+ noMove: function Presentation_noMove(aMoveMethod) {
+ return [p.noMove(aMoveMethod) for each (p in this.presenters)]; // jshint ignore:line
+ },
+
liveRegion: function Presentation_liveRegion(aAccessible, aIsPolite, aIsHide,
aModifiedText) {
let context;
if (!aModifiedText) {
context = new PivotContext(aAccessible, null, -1, -1, true,
aIsHide ? true : false);
}
return [p.liveRegion(context, aIsPolite, aIsHide, aModifiedText) // jshint ignore:line
--- a/accessible/jsat/TraversalRules.jsm
+++ b/accessible/jsat/TraversalRules.jsm
@@ -29,16 +29,20 @@ XPCOMUtils.defineLazyModuleGetter(this,
let gSkipEmptyImages = new PrefCache('accessibility.accessfu.skip_empty_images');
function BaseTraversalRule(aRoles, aMatchFunc, aPreFilter) {
this._explicitMatchRoles = new Set(aRoles);
this._matchRoles = aRoles;
if (aRoles.indexOf(Roles.LABEL) < 0) {
this._matchRoles.push(Roles.LABEL);
}
+ if (aRoles.indexOf(Roles.INTERNAL_FRAME) < 0) {
+ // Used for traversing in to child OOP frames.
+ this._matchRoles.push(Roles.INTERNAL_FRAME);
+ }
this._matchFunc = aMatchFunc || function() { return Filters.MATCH; };
this.preFilter = aPreFilter || gSimplePreFilter;
}
BaseTraversalRule.prototype = {
getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
aRules.value = this._matchRoles;
return aRules.value.length;
@@ -93,19 +97,17 @@ var gSimpleTraversalRoles =
Roles.HEADER,
Roles.HEADING,
Roles.SLIDER,
Roles.SPINBUTTON,
Roles.OPTION,
Roles.LISTITEM,
Roles.GRID_CELL,
Roles.COLUMNHEADER,
- Roles.ROWHEADER,
- // Used for traversing in to child OOP frames.
- Roles.INTERNAL_FRAME];
+ Roles.ROWHEADER];
var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
// An object is simple, if it either has a single child lineage,
// or has a flat subtree.
function isSingleLineage(acc) {
for (let child = acc; child; child = child.firstChild) {
if (Utils.visibleChildCount(child) > 1) {
return false;
--- a/accessible/jsat/Utils.jsm
+++ b/accessible/jsat/Utils.jsm
@@ -465,16 +465,24 @@ this.Utils = { // jshint ignore:line
// emulator add-on.
window.dispatchEvent(new window.CustomEvent(aType, {
bubbles: true,
cancelable: true,
detail: details
}));
}
+ },
+
+ isActivatableOnFingerUp: function isActivatableOnFingerUp(aAccessible) {
+ if (aAccessible.role === Roles.KEY) {
+ return true;
+ }
+ let quick_activate = this.getAttributes(aAccessible)['moz-quick-activate'];
+ return quick_activate && JSON.parse(quick_activate);
}
};
/**
* State object used internally to process accessible's states.
* @param {Number} aBase Base state.
* @param {Number} aExtended Extended state.
*/
@@ -944,17 +952,17 @@ this.PrefCache = function PrefCache(aNam
this.name = aName;
this.callback = aCallback;
let branch = Services.prefs;
this.value = this._getValue(branch);
if (this.callback && aRunCallbackNow) {
try {
- this.callback(this.name, this.value);
+ this.callback(this.name, this.value, true);
} catch (x) {
Logger.logException(x);
}
}
branch.addObserver(aName, this, true);
};
@@ -977,19 +985,20 @@ PrefCache.prototype = {
} catch (x) {
// Pref does not exist.
return null;
}
},
observe: function observe(aSubject) {
this.value = this._getValue(aSubject.QueryInterface(Ci.nsIPrefBranch));
+ Logger.info('pref changed', this.name, this.value);
if (this.callback) {
try {
- this.callback(this.name, this.value);
+ this.callback(this.name, this.value, false);
} catch (x) {
Logger.logException(x);
}
}
},
QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference])
--- a/accessible/jsat/content-script.js
+++ b/accessible/jsat/content-script.js
@@ -87,56 +87,29 @@ function scroll(aMessage) {
if (!forwardToChild(aMessage, scroll, position)) {
sendAsyncMessage('AccessFu:DoScroll',
{ bounds: Utils.getBounds(position, true),
page: aMessage.json.page,
horizontal: aMessage.json.horizontal });
}
}
-function adjustRange(aMessage) {
- function sendUpDownKey(aAccessible) {
- let acc = Utils.getEmbeddedControl(aAccessible) || aAccessible;
- let elem = acc.DOMNode;
- if (elem) {
- if (elem.tagName === 'INPUT' && elem.type === 'range') {
- elem[aMessage.json.direction === 'forward' ? 'stepDown' : 'stepUp']();
- let changeEvent = content.document.createEvent('UIEvent');
- changeEvent.initEvent('change', true, true);
- elem.dispatchEvent(changeEvent);
- } else {
- let evt = content.document.createEvent('KeyboardEvent');
- let keycode = aMessage.json.direction == 'forward' ?
- content.KeyEvent.DOM_VK_DOWN : content.KeyEvent.DOM_VK_UP;
- evt.initKeyEvent(
- "keypress", false, true, null, false, false, false, false, keycode, 0);
- elem.dispatchEvent(evt);
- }
- }
- }
-
- let position = Utils.getVirtualCursor(content.document).position;
- if (!forwardToChild(aMessage, adjustRange, position)) {
- sendUpDownKey(position);
- }
-}
addMessageListener(
'AccessFu:Start',
function(m) {
if (m.json.logLevel) {
Logger.logLevel = Logger[m.json.logLevel];
}
Logger.debug('AccessFu:Start');
if (m.json.buildApp)
Utils.MozBuildApp = m.json.buildApp;
addMessageListener('AccessFu:ContextMenu', activateContextMenu);
addMessageListener('AccessFu:Scroll', scroll);
- addMessageListener('AccessFu:AdjustRange', adjustRange);
if (!contentControl) {
contentControl = new ContentControl(this);
}
contentControl.start();
if (!eventManager) {
eventManager = new EventManager(this, contentControl);
--- a/accessible/tests/mochitest/actions/test_treegrid.xul
+++ b/accessible/tests/mochitest/actions/test_treegrid.xul
@@ -150,18 +150,17 @@
{
var treeNode = getNode("tabletree");
waitForEvent(EVENT_REORDER, treeNode, doTestActions);
treeNode.view = new nsTreeTreeView();
}
function test1()
{
- var boxObj = getNode("tabletree").treeBoxObject;
- boxObj.view.setCellValue(0, boxObj.columns.firstColumn, "false");
+ getNode("tabletree").view.setCellValue(0, boxObj.columns.firstColumn, "false");
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
]]>
</script>
<hbox flex="1" style="overflow: auto;">
@@ -191,9 +190,8 @@
</tree>
<vbox id="debug"/>
<button oncommand="test1();" label="uncheck"/>
</vbox>
</hbox>
</window>
-
--- a/accessible/tests/mochitest/hittest/test_zoom_tree.xul
+++ b/accessible/tests/mochitest/hittest/test_zoom_tree.xul
@@ -35,26 +35,24 @@
var treecol1 = tabDocument.getElementById("treecol1");
// tree columns
hitTest(tree, treecols, treecol1);
// tree rows and cells
var treeBoxObject = tree.treeBoxObject;
var treeBodyBoxObj = tree.treeBoxObject.treeBody.boxObject;
- var xObj = {}, yObj = {}, widthObj = {}, heightObj = {};
- treeBoxObject.getCoordsForCellItem(1, tree.columns[0], "cell",
- xObj, yObj, widthObj, heightObj);
+ var rect = treeBoxObject.getCoordsForCellItem(1, tree.columns[0], "cell");
var treeAcc = getAccessible(tree, [nsIAccessibleTable]);
var cellAcc = treeAcc.getCellAt(1, 0);
var rowAcc = cellAcc.parent;
- var cssX = xObj.value + treeBodyBoxObj.x;
- var cssY = yObj.value + treeBodyBoxObj.y;
+ var cssX = rect.x + treeBodyBoxObj.x;
+ var cssY = rect.y + treeBodyBoxObj.y;
var [x, y] = CSSToDevicePixels(tabWindow, cssX, cssY);
testChildAtPoint(treeAcc, x, y, rowAcc, cellAcc);
testChildAtPoint(rowAcc, x, y, cellAcc, cellAcc);
// do zoom
zoomDocument(tabDocument, 1.5);
@@ -95,9 +93,8 @@
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</hbox>
</window>
-
--- a/accessible/tests/mochitest/jsat/a11y.ini
+++ b/accessible/tests/mochitest/jsat/a11y.ini
@@ -13,11 +13,12 @@ support-files =
skip-if = buildapp == 'mulet'
[test_content_text.html]
skip-if = buildapp == 'mulet'
[test_explicit_names.html]
[test_gesture_tracker.html]
[test_landmarks.html]
[test_live_regions.html]
[test_output.html]
+[test_quicknav_modes.html]
[test_tables.html]
[test_pointer_relay.html]
[test_traversal.html]
--- a/accessible/tests/mochitest/jsat/jsatcommon.js
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -130,17 +130,17 @@ var AccessFuTest = {
} catch (ex) {
// StopIteration exception.
this.finish();
return;
}
testFunc();
},
- runTests: function AccessFuTest_runTests() {
+ runTests: function AccessFuTest_runTests(aAdditionalPrefs) {
if (gTestFuncs.length === 0) {
ok(false, "No tests specified!");
SimpleTest.finish();
return;
}
// Create an Iterator for gTestFuncs array.
gIterator = Iterator(gTestFuncs); // jshint ignore:line
@@ -151,20 +151,21 @@ var AccessFuTest = {
AccessFu.attach(getMainChromeWindow(window));
AccessFu.readyCallback = function readyCallback() {
// Enable logging to the console service.
Logger.test = true;
Logger.logLevel = Logger.DEBUG;
};
- SpecialPowers.pushPrefEnv({
- 'set': [['accessibility.accessfu.notify_output', 1],
- ['dom.mozSettings.enabled', true]]
- }, function () {
+ var prefs = [['accessibility.accessfu.notify_output', 1],
+ ['dom.mozSettings.enabled', true]];
+ prefs.push.apply(prefs, aAdditionalPrefs);
+
+ SpecialPowers.pushPrefEnv({ 'set': prefs }, function () {
if (AccessFuTest._waitForExplicitFinish) {
// Run all test functions asynchronously.
AccessFuTest.nextTest();
} else {
// Run all test functions synchronously.
[testFunc() for (testFunc of gTestFuncs)]; // jshint ignore:line
AccessFuTest.finish();
}
@@ -359,29 +360,39 @@ var ContentMessages = {
clearCursor: {
name: 'AccessFu:ClearCursor',
json: {
origin: 'top'
}
},
- adjustRangeUp: {
- name: 'AccessFu:AdjustRange',
- json: {
- origin: 'top',
- direction: 'backward'
+ moveOrAdjustUp: function moveOrAdjustUp(aRule) {
+ return {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ origin: 'top',
+ action: 'movePrevious',
+ inputType: 'gesture',
+ rule: (aRule || 'Simple'),
+ adjustRange: true
+ }
}
},
- adjustRangeDown: {
- name: 'AccessFu:AdjustRange',
- json: {
- origin: 'top',
- direction: 'forward'
+ moveOrAdjustDown: function moveOrAdjustUp(aRule) {
+ return {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ origin: 'top',
+ action: 'moveNext',
+ inputType: 'gesture',
+ rule: (aRule || 'Simple'),
+ adjustRange: true
+ }
}
},
focusSelector: function focusSelector(aSelector, aBlur) {
return {
name: 'AccessFuTest:Focus',
json: {
selector: aSelector,
@@ -649,16 +660,22 @@ function ExpectedAnnouncement(aAnnouncem
eventType: AndroidEvent.ANNOUNCEMENT,
text: [ aAnnouncement],
addedCount: aAnnouncement.length
}], aOptions);
}
ExpectedAnnouncement.prototype = Object.create(ExpectedPresent.prototype);
+function ExpectedNoMove(aOptions) {
+ ExpectedPresent.call(this, {eventType: 'no-move' }, null, aOptions);
+}
+
+ExpectedNoMove.prototype = Object.create(ExpectedPresent.prototype);
+
var AndroidEvent = {
VIEW_CLICKED: 0x01,
VIEW_LONG_CLICKED: 0x02,
VIEW_SELECTED: 0x04,
VIEW_FOCUSED: 0x08,
VIEW_TEXT_CHANGED: 0x10,
WINDOW_STATE_CHANGED: 0x20,
VIEW_HOVER_ENTER: 0x80,
--- a/accessible/tests/mochitest/jsat/test_content_integration.html
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -29,16 +29,17 @@
iframe.addEventListener('mozbrowserloadend', function () {
var contentTest = new AccessFuContentTest(
[
// Simple traversal forward
[ContentMessages.simpleMoveNext,
new ExpectedCursorChange(
['Phone status bar', 'Traversal Rule test document'],
{ focused: 'body' })],
+ [ContentMessages.simpleMovePrevious, new ExpectedNoMove()],
[ContentMessages.simpleMoveNext,
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
[ContentMessages.simpleMoveNext, new ExpectedCursorChange(
['wow', {'string': 'headingLevel', 'args': [1]} ,'such app'],
{ focused: 'iframe' })],
[ContentMessages.simpleMoveNext,
new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
{'string': 'checkbutton'}, {'string': 'listStart'},
@@ -47,24 +48,24 @@
// check checkbox
[ContentMessages.activateCurrent(),
new ExpectedClickAction({ no_android: true }),
new ExpectedCheckAction(true, { android_todo: true })],
[ContentMessages.simpleMoveNext,
new ExpectedCursorChange(['much range', {'string': 'label'}])],
[ContentMessages.simpleMoveNext,
new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
- [ContentMessages.adjustRangeUp, new ExpectedValueChange('6')],
+ [ContentMessages.moveOrAdjustUp(), new ExpectedValueChange('6')],
[ContentMessages.simpleMoveNext,
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
// Simple traversal backward
[ContentMessages.simpleMovePrevious,
new ExpectedCursorChange(['much range', '6', {'string': 'slider'}, 'such app'])],
- [ContentMessages.adjustRangeDown, new ExpectedValueChange('5')],
+ [ContentMessages.moveOrAdjustDown(), new ExpectedValueChange('5')],
[ContentMessages.simpleMovePrevious,
new ExpectedCursorChange(['much range', {'string': 'label'}])],
[ContentMessages.simpleMovePrevious,
new ExpectedCursorChange(['many option', {'string': 'stateChecked'},
{'string': 'checkbutton'}, {'string': 'listStart'},
{'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
// uncheck checkbox
[ContentMessages.activateCurrent(),
@@ -86,16 +87,42 @@
['wow', {'string': 'headingLevel', 'args': [1]}, 'such app'])],
// Move from an inner frame to the last element in the parent doc
[ContentMessages.simpleMoveLast,
new ExpectedCursorChange(
['Home', {'string': 'pushbutton'}], { b2g_todo: true })],
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedCursorChange(['Back', {"string": "pushbutton"}])],
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}, 'such app'])],
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
+ // Calling AdjustOrMove should adjust the range.
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedValueChange('4')],
+ [ContentMessages.moveOrAdjustUp('FormElement'),
+ new ExpectedValueChange('5')],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['much range', {'string': 'label'}])],
+ [ContentMessages.moveOrAdjustUp('FormElement'),
+ new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+ [ContentMessages.moveOrAdjustUp('FormElement'),
+ new ExpectedCursorChange(['Back', {"string": "pushbutton"}])],
+
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
// Moving to the absolute first item from an embedded document
// fails. Bug 972035.
[ContentMessages.simpleMoveNext,
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
[ContentMessages.simpleMoveNext,
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
[ContentMessages.simpleMoveNext,
new ExpectedCursorChange(['wow', {'string': 'headingLevel', 'args': [1]}, 'such app'])],
@@ -220,17 +247,18 @@
[doc.defaultView.showAlert,
new ExpectedCursorChange(['This is an alert!',
{'string': 'headingLevel', 'args': [1]}, {'string': 'dialog'}])],
[function hideAlertAndFocusHomeButton() {
doc.defaultView.hideAlert();
doc.querySelector('button#home').focus();
}, new ExpectedCursorChange(['Home', {'string': 'pushbutton'},
- 'Traversal Rule test document'])]
+ 'Traversal Rule test document'])],
+ [ContentMessages.simpleMoveNext, new ExpectedNoMove()]
]);
addA11yLoadEvent(function() {
contentTest.start(function () {
closeBrowserWindow();
SimpleTest.finish();
});
}, doc.defaultView)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_quicknav_modes.html
@@ -0,0 +1,103 @@
+<html>
+
+<head>
+ <title>AccessFu test for enabling</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="./jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function prefStart() {
+ // Start AccessFu via pref.
+ SpecialPowers.setIntPref("accessibility.accessfu.activate", 1);
+ AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
+ }
+
+ function nextMode(aCurrentMode, aNextMode) {
+ return function() {
+ is(AccessFu.Input.quickNavMode.current, aCurrentMode,
+ 'initial current mode is correct');
+ AccessFu.Input.quickNavMode.next();
+ _expectMode(aNextMode, AccessFuTest.nextTest);
+ }
+ }
+
+ function prevMode(aCurrentMode, aNextMode) {
+ return function() {
+ is(AccessFu.Input.quickNavMode.current, aCurrentMode,
+ 'initial current mode is correct');
+ AccessFu.Input.quickNavMode.previous();
+ _expectMode(aNextMode, AccessFuTest.nextTest);
+ }
+ }
+
+ function setMode(aModeIndex, aExpectedMode) {
+ return function() {
+ SpecialPowers.setIntPref(
+ 'accessibility.accessfu.quicknav_index', aModeIndex);
+ _expectMode(aExpectedMode, AccessFuTest.nextTest);
+ }
+ }
+
+ function reconfigureModes() {
+ SpecialPowers.setCharPref('accessibility.accessfu.quicknav_modes',
+ 'Landmark,Button,Entry,Graphic');
+ // When the modes are reconfigured, the current mode should
+ // be set to the first in the new list.
+ _expectMode('Landmark', AccessFuTest.nextTest);
+ }
+
+ function _expectMode(aExpectedMode, aCallback) {
+ if (AccessFu.Input.quickNavMode.current === aExpectedMode) {
+ ok(true, 'correct mode');
+ aCallback();
+ } else {
+ AccessFuTest.once_log('Quicknav mode: ' + aExpectedMode, function() {
+ ok(true, 'correct mode');
+ aCallback();
+ });
+ }
+ }
+
+ // Listen for initial 'EventManager.start' and disable AccessFu.
+ function prefStop() {
+ ok(AccessFu._enabled, "AccessFu was started via preference.");
+ AccessFuTest.once_log("EventManager.stop", AccessFuTest.finish);
+ SpecialPowers.setIntPref("accessibility.accessfu.activate", 0);
+ }
+
+ function doTest() {
+ AccessFuTest.addFunc(prefStart);
+ AccessFuTest.addFunc(nextMode('Link', 'Heading'));
+ AccessFuTest.addFunc(nextMode('Heading', 'FormElement'));
+ AccessFuTest.addFunc(nextMode('FormElement', 'Link'));
+ AccessFuTest.addFunc(nextMode('Link', 'Heading'));
+ AccessFuTest.addFunc(prevMode('Heading', 'Link'));
+ AccessFuTest.addFunc(prevMode('Link', 'FormElement'));
+ AccessFuTest.addFunc(setMode(1, 'Heading'));
+ AccessFuTest.addFunc(reconfigureModes);
+ AccessFuTest.addFunc(prefStop);
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests([ // Will call SimpleTest.finish();
+ ['accessibility.accessfu.quicknav_modes', 'Link,Heading,FormElement']]);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=811307"
+ title="[AccessFu] Add mochitest for enabling">
+ Mozilla Bug 811307
+ </a>
+</body>
+</html>
\ No newline at end of file
--- a/accessible/tests/mochitest/name/test_tree.xul
+++ b/accessible/tests/mochitest/name/test_tree.xul
@@ -27,17 +27,17 @@
<script type="application/javascript">
<![CDATA[
function treeTester(aID)
{
this.DOMNode = getNode(aID);
this.invoke = function treeTester_invoke()
{
- this.DOMNode.treeBoxObject.view = new nsTreeTreeView();
+ this.DOMNode.view = new nsTreeTreeView();
}
this.check = function treeTester_check(aEvent)
{
var tree = {
role: ROLE_OUTLINE,
children: [
{
@@ -85,17 +85,17 @@
}
function tableTester(aID, aIsTable, aCol1ID, aCol2ID)
{
this.DOMNode = getNode(aID);
this.invoke = function tableTester_invoke()
{
- this.DOMNode.treeBoxObject.view = new nsTableTreeView(2);
+ this.DOMNode.view = new nsTableTreeView(2);
}
this.check = function tableTester_check(aEvent)
{
var tree = {
role: aIsTable ? ROLE_TABLE : ROLE_TREE_TABLE,
children: [
{
@@ -204,9 +204,8 @@
<treecol id="tt_col2" flex="1" label="column 2"/>
</treecols>
<treechildren/>
</tree>
</vbox> <!-- close tests area -->
</hbox> <!-- close main area -->
</window>
-
--- a/accessible/tests/mochitest/selectable/test_tree.xul
+++ b/accessible/tests/mochitest/selectable/test_tree.xul
@@ -37,17 +37,17 @@
* accessible.
*/
function statesChecker(aTreeID, aView)
{
this.DOMNode = getNode(aTreeID);
this.invoke = function invoke()
{
- this.DOMNode.treeBoxObject.view = aView;
+ this.DOMNode.view = aView;
}
this.check = function check()
{
var tree = getAccessible(this.DOMNode);
var isTreeMultiSelectable = false;
var seltype = this.DOMNode.getAttribute("seltype");
if (seltype != "single" && seltype != "cell" && seltype != "text")
@@ -181,9 +181,8 @@
<treechildren/>
</tree>
<vbox id="debug"/>
</vbox>
</hbox>
</window>
-
--- a/accessible/tests/mochitest/states/test_tree.xul
+++ b/accessible/tests/mochitest/states/test_tree.xul
@@ -33,17 +33,17 @@
* accessible.
*/
function statesChecker(aTreeID, aView)
{
this.DOMNode = getNode(aTreeID);
this.invoke = function statesChecker_invoke()
{
- this.DOMNode.treeBoxObject.view = aView;
+ this.DOMNode.view = aView;
}
this.check = function statesChecker_check()
{
var tree = getAccessible(this.DOMNode);
// tree states
testStates(tree, STATE_READONLY);
@@ -145,9 +145,8 @@
<treechildren/>
</tree>
<vbox id="debug"/>
</vbox>
</hbox>
</window>
-
--- a/accessible/tests/mochitest/tree/test_tree.xul
+++ b/accessible/tests/mochitest/tree/test_tree.xul
@@ -67,17 +67,17 @@
var accTreeForTree = {
role: aRole,
children: [
accTreeForColumns
]
};
var treeBoxObject = aTree.treeBoxObject;
- var view = treeBoxObject.view;
+ var view = aTree.view;
var columnCount = treeBoxObject.columns.count;
for (var idx = 0; idx < columnCount; idx++)
accTreeForColumns.children.push({ COLUMNHEADER: [ ] });
if (!aTree.hasAttribute("hidecolumnpicker"))
accTreeForColumns.children.push({ PUSHBUTTON: [ { MENUPOPUP: [] } ] });
for (var idx = 0; idx < view.rowCount; idx++)
@@ -90,17 +90,17 @@
* Event queue invoker object to test accessible tree for XUL tree element.
*/
function treeChecker(aID, aView, aRole)
{
this.DOMNode = getNode(aID);
this.invoke = function invoke()
{
- this.DOMNode.treeBoxObject.view = aView;
+ this.DOMNode.view = aView;
}
this.check = function check(aEvent)
{
testAccessibleTreeFor(this.DOMNode, aRole);
}
this.getID = function getID()
{
return "Tree testing of " + aID;
@@ -175,9 +175,8 @@
<treechildren/>
</tree>
<vbox id="debug"/>
</vbox>
</hbox>
</window>
-
--- a/accessible/tests/mochitest/treeview.js
+++ b/accessible/tests/mochitest/treeview.js
@@ -12,17 +12,17 @@ function loadXULTreeAndDoTest(aDoTestFun
this.treeNode = getNode(aTreeID);
this.eventSeq = [
new invokerChecker(EVENT_REORDER, this.treeNode)
];
this.invoke = function loadXULTree_invoke()
{
- this.treeNode.treeBoxObject.view = aTreeView;
+ this.treeNode.view = aTreeView;
}
this.getID = function loadXULTree_getID()
{
return "Load XUL tree " + prettyName(aTreeID);
}
}
--- a/accessible/xul/XULTreeAccessible.cpp
+++ b/accessible/xul/XULTreeAccessible.cpp
@@ -194,17 +194,17 @@ XULTreeAccessible::ChildAtPoint(int32_t
nsIntRect rootRect = rootFrame->GetScreenRect();
int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.x;
int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.y;
int32_t row = -1;
nsCOMPtr<nsITreeColumn> column;
- nsAutoCString childEltUnused;
+ nsAutoString childEltUnused;
mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column),
childEltUnused);
// If we failed to find tree cell for the given point then it might be
// tree columns.
if (row == -1 || !column)
return AccessibleWrap::ChildAtPoint(aX, aY, aWhichChild);
@@ -741,17 +741,17 @@ XULTreeItemAccessibleBase::Bounds() cons
nsCOMPtr<nsIBoxObject> boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree);
if (!boxObj)
return nsIntRect();
nsCOMPtr<nsITreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree);
int32_t x = 0, y = 0, width = 0, height = 0;
- nsresult rv = mTree->GetCoordsForCellItem(mRow, column, EmptyCString(),
+ nsresult rv = mTree->GetCoordsForCellItem(mRow, column, EmptyString(),
&x, &y, &width, &height);
if (NS_FAILED(rv))
return nsIntRect();
boxObj->GetWidth(&width);
int32_t tcX = 0, tcY = 0;
boxObj->GetScreenX(&tcX);
@@ -991,25 +991,25 @@ XULTreeItemAccessibleBase::DispatchClick
nsCOMPtr<nsITreeColumns> columns;
mTree->GetColumns(getter_AddRefs(columns));
if (!columns)
return;
// Get column and pseudo element.
nsCOMPtr<nsITreeColumn> column;
- nsAutoCString pseudoElm;
+ nsAutoString pseudoElm;
if (aActionIndex == eAction_Click) {
// Key column is visible and clickable.
columns->GetKeyColumn(getter_AddRefs(column));
} else {
// Primary column contains a twisty we should click on.
columns->GetPrimaryColumn(getter_AddRefs(column));
- pseudoElm = NS_LITERAL_CSTRING("twisty");
+ pseudoElm = NS_LITERAL_STRING("twisty");
}
if (column)
nsCoreUtils::DispatchClickEvent(mTree, mRow, column, pseudoElm);
}
Accessible*
XULTreeItemAccessibleBase::GetSiblingAtOffset(int32_t aOffset,
@@ -1191,9 +1191,8 @@ XULTreeColumAccessible::GetSiblingAtOffs
if (treeAcc)
return treeAcc->GetTreeItemAccessible(aOffset - 1);
}
}
}
return nullptr;
}
-
--- a/accessible/xul/XULTreeGridAccessible.cpp
+++ b/accessible/xul/XULTreeGridAccessible.cpp
@@ -336,17 +336,17 @@ XULTreeGridRowAccessible::ChildAtPoint(i
nsIntRect rootRect = rootFrame->GetScreenRect();
int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.x;
int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.y;
int32_t row = -1;
nsCOMPtr<nsITreeColumn> column;
- nsAutoCString childEltUnused;
+ nsAutoString childEltUnused;
mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column),
childEltUnused);
// Return if we failed to find tree cell in the row for the given point.
if (row != mRow || !column)
return nullptr;
return GetCellAccessible(column);
@@ -517,17 +517,17 @@ XULTreeGridCellAccessible::Bounds() cons
// Get bounds for tree cell and add x and y of treechildren element to
// x and y of the cell.
nsCOMPtr<nsIBoxObject> boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree);
if (!boxObj)
return nsIntRect();
int32_t x = 0, y = 0, width = 0, height = 0;
nsresult rv = mTree->GetCoordsForCellItem(mRow, mColumn,
- NS_LITERAL_CSTRING("cell"),
+ NS_LITERAL_STRING("cell"),
&x, &y, &width, &height);
if (NS_FAILED(rv))
return nsIntRect();
int32_t tcX = 0, tcY = 0;
boxObj->GetScreenX(&tcX);
boxObj->GetScreenY(&tcY);
x += tcX;
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -3,17 +3,16 @@ dnl Local autoconf macros used with mozi
dnl The contents of this file are under the Public Domain.
dnl
builtin(include, build/autoconf/hotfixes.m4)dnl
builtin(include, build/autoconf/acwinpaths.m4)dnl
builtin(include, build/autoconf/hooks.m4)dnl
builtin(include, build/autoconf/config.status.m4)dnl
builtin(include, build/autoconf/toolchain.m4)dnl
-builtin(include, build/autoconf/ccache.m4)dnl
builtin(include, build/autoconf/wrapper.m4)dnl
builtin(include, build/autoconf/nspr.m4)dnl
builtin(include, build/autoconf/nspr-build.m4)dnl
builtin(include, build/autoconf/nss.m4)dnl
builtin(include, build/autoconf/pkg.m4)dnl
builtin(include, build/autoconf/codeset.m4)dnl
builtin(include, build/autoconf/altoptions.m4)dnl
builtin(include, build/autoconf/mozprog.m4)dnl
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -789,16 +789,18 @@ pref("dom.ipc.systemMessageCPULockTimeou
pref("dom.disable_window_open_dialog_feature", true);
// Enable before keyboard events and after keyboard events.
pref("dom.beforeAfterKeyboardEvent.enabled", true);
// Screen reader support
pref("accessibility.accessfu.activate", 2);
pref("accessibility.accessfu.quicknav_modes", "Link,Heading,FormElement,Landmark,ListItem");
+// Active quicknav mode, index value of list from quicknav_modes
+pref("accessibility.accessfu.quicknav_index", 0);
// Setting for an utterance order (0 - description first, 1 - description last).
pref("accessibility.accessfu.utterance", 1);
// Whether to skip images with empty alt text
pref("accessibility.accessfu.skip_empty_images", true);
// Enable hit-target fluffing
pref("ui.touch.radius.enabled", true);
pref("ui.touch.radius.leftmm", 3);
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -452,16 +452,26 @@ SettingsListener.observe("theme.selected
Services.prefs.setCharPref('dom.mozApps.selected_theme', newTheme);
Services.prefs.savePrefFile(null);
Services.obs.notifyObservers(null, 'app-theme-changed', newTheme);
}
});
// =================== Various simple mapping ======================
let settingsToObserve = {
+ 'accessibility.screenreader_quicknav_modes': {
+ prefName: 'accessibility.accessfu.quicknav_modes',
+ resetToPref: true,
+ defaultValue: ''
+ },
+ 'accessibility.screenreader_quicknav_index': {
+ prefName: 'accessibility.accessfu.quicknav_index',
+ resetToPref: true,
+ defaultValue: 0
+ },
'app.update.channel': {
resetToPref: true
},
'app.update.interval': 86400,
'app.update.url': {
resetToPref: true
},
'apz.force-enable': {
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -703,23 +703,22 @@ var BookmarksEventHandler = {
fillInBHTooltip: function BEH_fillInBHTooltip(aDocument, aEvent) {
var node;
var cropped = false;
var targetURI;
if (aDocument.tooltipNode.localName == "treechildren") {
var tree = aDocument.tooltipNode.parentNode;
- var row = {}, column = {};
var tbo = tree.treeBoxObject;
- tbo.getCellAt(aEvent.clientX, aEvent.clientY, row, column, {});
- if (row.value == -1)
+ var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);
+ if (cell.row == -1)
return false;
- node = tree.view.nodeForTreeIndex(row.value);
- cropped = tbo.isCellCropped(row.value, column.value);
+ node = tree.view.nodeForTreeIndex(cell.row);
+ cropped = tbo.isCellCropped(cell.row, cell.col);
}
else {
// Check whether the tooltipNode is a Places node.
// In such a case use it, otherwise check for targetURI attribute.
var tooltipNode = aDocument.tooltipNode;
if (tooltipNode._placesNode)
node = tooltipNode._placesNode;
else {
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -246,23 +246,29 @@ var gPluginHandler = {
pluginData = new Map();
}
for (var pluginInfo of plugins) {
if (pluginData.has(pluginInfo.permissionString)) {
continue;
}
- let url;
- // TODO: allow the blocklist to specify a better link, bug 873093
+ // If a block contains an infoURL, we should always prefer that to the default
+ // URL that we construct in-product, even for other blocklist types.
+ let url = Services.blocklist.getPluginInfoURL(pluginInfo.pluginTag);
+
if (pluginInfo.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
- url = Services.urlFormatter.formatURLPref("plugins.update.url");
+ if (!url) {
+ url = Services.urlFormatter.formatURLPref("plugins.update.url");
+ }
}
else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
- url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
+ if (!url) {
+ url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
+ }
}
else {
url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
}
pluginInfo.detailsLink = url;
pluginData.set(pluginInfo.permissionString, pluginInfo);
}
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -503,17 +503,17 @@ function makeGeneralTab()
metaGroup.collapsed = true;
else {
var metaTagsCaption = document.getElementById("metaTagsCaption");
if (length == 1)
metaTagsCaption.label = gBundle.getString("generalMetaTag");
else
metaTagsCaption.label = gBundle.getFormattedString("generalMetaTags", [length]);
var metaTree = document.getElementById("metatree");
- metaTree.treeBoxObject.view = gMetaView;
+ metaTree.view = gMetaView;
for (var i = 0; i < length; i++)
gMetaView.addRow([metaNodes[i].name || metaNodes[i].httpEquiv, metaNodes[i].content]);
metaGroup.collapsed = false;
}
// get the date of last modification
--- a/browser/base/content/sync/quota.js
+++ b/browser/base/content/sync/quota.js
@@ -156,20 +156,19 @@ let gUsageTreeView = {
/*
* Handle click events on the tree.
*/
onTreeClick: function onTreeClick(event) {
if (event.button == 2)
return;
- let row = {}, col = {};
- this.treeBox.getCellAt(event.clientX, event.clientY, row, col, {});
- if (col.value && col.value.id == "enabled")
- this.toggle(row.value);
+ let cell = this.treeBox.getCellAt(event.clientX, event.clientY);
+ if (cell.col && cell.col.id == "enabled")
+ this.toggle(cell.row);
},
/*
* Toggle enabled state of an engine.
*/
toggle: function toggle(row) {
// Update the tree
let collection = this._collections[row];
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1175,41 +1175,43 @@
oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
if (this.isFindBarInitialized(oldTab)) {
let findBar = this.getFindBar(oldTab);
oldTab._findBarFocused = (!findBar.hidden &&
findBar._findField.getAttribute("focused") == "true");
}
+ // If focus is in the tab bar, retain it there.
+ if (document.activeElement == oldTab) {
+ // We need to explicitly focus the new tab, because
+ // tabbox.xml does this only in some cases.
+ this.mCurrentTab.focus();
+ }
+
if (!gMultiProcessBrowser)
- this._adjustFocusAfterTabSwitch(this.mCurrentTab, oldTab);
+ this._adjustFocusAfterTabSwitch(this.mCurrentTab);
}
this.tabContainer._setPositionalAttributes();
if (!aForceUpdate)
TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
]]>
</body>
</method>
<method name="_adjustFocusAfterTabSwitch">
<parameter name="newTab"/>
- <parameter name="oldTab"/>
<body><![CDATA[
let newBrowser = this.getBrowserForTab(newTab);
- // When focus is in the tab bar, retain it there.
- if (document.activeElement == oldTab) {
- // We need to explicitly focus the new tab, because
- // tabbox.xml does this only in some cases.
- this.mCurrentTab.focus();
+ // Don't steal focus from the tab bar.
+ if (document.activeElement == newTab)
return;
- }
// If there's a tabmodal prompt showing, focus it.
if (newBrowser.hasAttribute("tabmodalPromptShowing")) {
let XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let prompts = newBrowser.parentNode.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
let prompt = prompts[prompts.length - 1];
prompt.Dialog.setDefaultFocus();
return;
@@ -3314,17 +3316,17 @@
}
]]></body>
</method>
<method name="_finalizeTabSwitch">
<parameter name="toTab"/>
<parameter name="fromTab"/>
<body><![CDATA[
- this._adjustFocusAfterTabSwitch(toTab, fromTab);
+ this._adjustFocusAfterTabSwitch(toTab);
this._deactivateContent(fromTab);
let toBrowser = this.getBrowserForTab(toTab);
toBrowser.setAttribute("type", "content-primary");
let fromBrowser = this.getBrowserForTab(fromTab);
// It's possible that the tab we're switching from closed
// before we were able to finalize, in which case, fromBrowser
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugins/blockPluginInfoURL.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1336406310000">
+ <emItems>
+ </emItems>
+ <pluginItems>
+ <pluginItem blockID="p9999">
+ <match name="filename" exp="libnptest\.so|nptest\.dll|Test\.plugin" />
+ <versionRange severity="2"></versionRange>
+ <infoURL>http://test.url.com/</infoURL>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -4,16 +4,17 @@
# * Bug 921916 - no plugin events
# * Bug XXXXX - no plugins in content processes ("Error: You cannot use the AddonManager in child processes!")
# * Bug 866413 - PageInfo doesn't work in e10s [browser_pageInfo_plugins.js]
# * Bug 921957 - remote webprogress doesn't supply originalURI attribute on the request object [browser_clearplugindata.js]
skip-if = buildapp == "mulet" || e10s
support-files =
blockNoPlugins.xml
blockPluginHard.xml
+ blockPluginInfoURL.xml
blockPluginVulnerableNoUpdate.xml
blockPluginVulnerableUpdatable.xml
browser_clearplugindata.html
browser_clearplugindata_noage.html
head.js
plugin_add_dynamically.html
plugin_alternate_content.html
plugin_big.html
--- a/browser/base/content/test/plugins/browser_pluginnotification.js
+++ b/browser/base/content/test/plugins/browser_pluginnotification.js
@@ -56,16 +56,19 @@ TabOpenListener.prototype = {
function test() {
waitForExplicitFinish();
SimpleTest.requestCompleteLog();
requestLongerTimeout(2);
registerCleanupFunction(function() {
clearAllPluginPermissions();
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
+ return new Promise(resolve => {
+ setAndUpdateBlocklist(gHttpTestRoot + "blockNoPlugins.xml", resolve);
+ });
});
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
var newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
@@ -816,10 +819,51 @@ function test24d() {
}, "Test 24d, plugin should be activated");
}
function test25() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 25: There should be a plugin notification even if the plugin was immediately removed");
ok(notification.dismissed, "Test 25: The notification should be dismissed by default");
+ prepareTest26();
+}
+
+function prepareTest26() {
+ info("prepareTest26");
+ let plugin = getTestPlugin();
+ plugin.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ setAndUpdateBlocklist(gHttpTestRoot + "blockPluginInfoURL.xml",
+ function() {
+ info("prepareTest26 callback");
+ prepareTest(runAfterPluginBindingAttached(test26), gTestRoot + "plugin_test.html");
+ });
+}
+
+// Tests a page with a blocked plugin in it and make sure the
+// infoURL property from the blocklist file gets used.
+function test26() {
+ info("test26 - Test infoURL");
+ let notification = PopupNotifications.getNotification("click-to-play-plugins");
+
+ // Since the plugin notification is dismissed by default, reshow it.
+ notification.reshow();
+
+ let pluginNode = gTestBrowser.contentDocument.getElementById("test");
+ ok(pluginNode, "Test 26, Found plugin in page");
+ let objLoadingContent = pluginNode.QueryInterface(Ci.nsIObjectLoadingContent);
+ is(objLoadingContent.pluginFallbackType,
+ Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED,
+ "Test 26, plugin fallback type should be PLUGIN_BLOCKLISTED");
+
+ const testUrl = "http://test.url.com/";
+
+ let doc = gTestBrowser.contentDocument;
+ let firstPanelChild = PopupNotifications.panel.firstChild;
+
+ let infoLink = doc.getAnonymousElementByAttribute(
+ firstPanelChild, "anonid", "click-to-play-plugins-notification-link");
+
+ is(infoLink.href, testUrl,
+ "Test 26, the notification URL needs to match the infoURL from the blocklist file.");
+
finishTest();
}
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -1900,18 +1900,24 @@ CustomizeMode.prototype = {
if (!aEvent.dataTransfer.mozTypesAt(0)) {
return;
}
let draggedItemId =
aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
- draggedWrapper.hidden = false;
- draggedWrapper.removeAttribute("mousedown");
+
+ // DraggedWrapper might no longer available if a widget node is
+ // destroyed after starting (but before stopping) a drag.
+ if (draggedWrapper) {
+ draggedWrapper.hidden = false;
+ draggedWrapper.removeAttribute("mousedown");
+ }
+
if (this._dragOverItem) {
this._cancelDragActive(this._dragOverItem);
this._dragOverItem = null;
}
this._updateToolbarCustomizationOutline(this.window);
this._showPanelCustomizationPlaceholders();
DragPositionManager.stop();
},
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -62,16 +62,18 @@ XPCOMUtils.defineLazyModuleGetter(this,
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
"resource:///modules/RecentWindow.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsLogger",
"resource:///modules/DownloadsLogger.jsm");
const nsIDM = Ci.nsIDownloadManager;
const kDownloadsStringBundleUrl =
"chrome://browser/locale/downloads/downloads.properties";
@@ -138,16 +140,23 @@ PrefObserver.register({
////////////////////////////////////////////////////////////////////////////////
//// DownloadsCommon
/**
* This object is exposed directly to the consumers of this JavaScript module,
* and provides shared methods for all the instances of the user interface.
*/
this.DownloadsCommon = {
+ /**
+ * Constants with the different types of unblock messages.
+ */
+ BLOCK_VERDICT_MALWARE: "Malware",
+ BLOCK_VERDICT_POTENTIALLY_UNWANTED: "PotentiallyUnwanted",
+ BLOCK_VERDICT_UNCOMMON: "Uncommon",
+
log: function DC_log(...aMessageArgs) {
delete this.log;
this.log = function DC_log(...aMessageArgs) {
if (!PrefObserver.debug) {
return;
}
DownloadsLogger.log.apply(DownloadsLogger, aMessageArgs);
}
@@ -506,17 +515,79 @@ this.DownloadsCommon = {
// If launch also fails (probably because it's not implemented), let
// the OS handler try to open the parent.
Cc["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Ci.nsIExternalProtocolService)
.loadUrl(NetUtil.newURI(parent));
}
}
}
- }
+ },
+
+ /**
+ * Displays an alert message box which asks the user if they want to
+ * unblock the downloaded file or not.
+ *
+ * @param aType
+ * The type of malware the downloaded file contains.
+ * @param aOwnerWindow
+ * The window with which this action is associated.
+ *
+ * @return True to unblock the file, false to keep the user safe and
+ * cancel the operation.
+ */
+ confirmUnblockDownload: Task.async(function* DP_confirmUnblockDownload(aType, aOwnerWindow) {
+ let s = DownloadsCommon.strings;
+ let title = s.unblockHeader;
+ let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
+ (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1);
+ let type = "";
+ let message = s.unblockTip;
+ let okButton = s.unblockButtonContinue;
+ let cancelButton = s.unblockButtonCancel;
+
+ switch (aType) {
+ case this.BLOCK_VERDICT_MALWARE:
+ type = s.unblockTypeMalware;
+ break;
+ case this.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
+ type = s.unblockTypePotentiallyUnwanted;
+ break;
+ case this.BLOCK_VERDICT_UNCOMMON:
+ type = s.unblockTypeUncommon;
+ break;
+ }
+
+ if (type) {
+ message = type + "\n\n" + message;
+ }
+
+ Services.ww.registerNotification(function onOpen(subj, topic) {
+ if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
+ // Make sure to listen for "DOMContentLoaded" because it is fired
+ // before the "load" event.
+ subj.addEventListener("DOMContentLoaded", function onLoad() {
+ subj.removeEventListener("DOMContentLoaded", onLoad);
+ if (subj.document.documentURI ==
+ "chrome://global/content/commonDialog.xul") {
+ Services.ww.unregisterNotification(onOpen);
+ let dialog = subj.document.getElementById("commonDialog");
+ if (dialog) {
+ // Change the dialog to use a warning icon.
+ dialog.classList.add("alert-dialog");
+ }
+ }
+ });
+ }
+ });
+
+ let rv = Services.prompt.confirmEx(aOwnerWindow, title, message, buttonFlags,
+ cancelButton, okButton, null, null, {});
+ return (rv == 1);
+ }),
};
/**
* Returns true if we are executing on Windows Vista or a later version.
*/
XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function () {
let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
if (os != "WINNT") {
--- a/browser/components/downloads/test/browser/browser.ini
+++ b/browser/components/downloads/test/browser/browser.ini
@@ -2,8 +2,9 @@
support-files = head.js
[browser_basic_functionality.js]
skip-if = buildapp == "mulet" || e10s
[browser_first_download_panel.js]
skip-if = os == "linux" # Bug 949434
[browser_overflow_anchor.js]
skip-if = os == "linux" # Bug 952422
+[browser_confirm_unblock_download.js]
--- a/browser/components/downloads/test/browser/browser_basic_functionality.js
+++ b/browser/components/downloads/test/browser/browser_basic_functionality.js
@@ -1,57 +1,55 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
+registerCleanupFunction(function*() {
+ yield task_resetState();
+});
+
/**
* Make sure the downloads panel can display items in the right order and
* contains the expected data.
*/
-function test_task()
-{
+add_task(function* test_basic_functionality() {
// Display one of each download state.
const DownloadData = [
{ state: nsIDM.DOWNLOAD_NOTSTARTED },
{ state: nsIDM.DOWNLOAD_PAUSED },
{ state: nsIDM.DOWNLOAD_FINISHED },
{ state: nsIDM.DOWNLOAD_FAILED },
{ state: nsIDM.DOWNLOAD_CANCELED },
];
- try {
- // Wait for focus first
- yield promiseFocus();
+ // Wait for focus first
+ yield promiseFocus();
- // Ensure that state is reset in case previous tests didn't finish.
- yield task_resetState();
+ // Ensure that state is reset in case previous tests didn't finish.
+ yield task_resetState();
- // For testing purposes, show all the download items at once.
- var originalCountLimit = DownloadsView.kItemCountLimit;
- DownloadsView.kItemCountLimit = DownloadData.length;
- registerCleanupFunction(function () {
- DownloadsView.kItemCountLimit = originalCountLimit;
- });
+ // For testing purposes, show all the download items at once.
+ var originalCountLimit = DownloadsView.kItemCountLimit;
+ DownloadsView.kItemCountLimit = DownloadData.length;
+ registerCleanupFunction(function () {
+ DownloadsView.kItemCountLimit = originalCountLimit;
+ });
- // Populate the downloads database with the data required by this test.
- yield task_addDownloads(DownloadData);
+ // Populate the downloads database with the data required by this test.
+ yield task_addDownloads(DownloadData);
- // Open the user interface and wait for data to be fully loaded.
- yield task_openPanel();
+ // Open the user interface and wait for data to be fully loaded.
+ yield task_openPanel();
- // Test item data and count. This also tests the ordering of the display.
- let richlistbox = document.getElementById("downloadsListBox");
-/* disabled for failing intermittently (bug 767828)
+ // Test item data and count. This also tests the ordering of the display.
+ let richlistbox = document.getElementById("downloadsListBox");
+ /* disabled for failing intermittently (bug 767828)
is(richlistbox.children.length, DownloadData.length,
"There is the correct number of richlistitems");
-*/
- let itemCount = richlistbox.children.length;
- for (let i = 0; i < itemCount; i++) {
- let element = richlistbox.children[itemCount - i - 1];
- let dataItem = new DownloadsViewItemController(element).dataItem;
- is(dataItem.state, DownloadData[i].state, "Download states match up");
- }
- } finally {
- // Clean up when the test finishes.
- yield task_resetState();
+ */
+ let itemCount = richlistbox.children.length;
+ for (let i = 0; i < itemCount; i++) {
+ let element = richlistbox.children[itemCount - i - 1];
+ let dataItem = new DownloadsViewItemController(element).dataItem;
+ is(dataItem.state, DownloadData[i].state, "Download states match up");
}
-}
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/browser/browser_confirm_unblock_download.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the dialog which allows the user to unblock a downloaded file.
+
+registerCleanupFunction(() => {});
+
+function addDialogOpenObserver(buttonAction) {
+ Services.ww.registerNotification(function onOpen(subj, topic, data) {
+ if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
+ // The test listens for the "load" event which guarantees that the alert
+ // class has already been added (it is added when "DOMContentLoaded" is
+ // fired).
+ subj.addEventListener("load", function onLoad() {
+ subj.removeEventListener("load", onLoad);
+ if (subj.document.documentURI ==
+ "chrome://global/content/commonDialog.xul") {
+ Services.ww.unregisterNotification(onOpen);
+
+ let dialog = subj.document.getElementById("commonDialog");
+ ok(dialog.classList.contains("alert-dialog"),
+ "The dialog element should contain an alert class.");
+
+ let doc = subj.document.documentElement;
+ doc.getButton(buttonAction).click();
+ }
+ });
+ }
+ });
+}
+
+add_task(function* test_confirm_unblock_dialog_unblock() {
+ addDialogOpenObserver("cancel");
+ let result = yield DownloadsCommon.confirmUnblockDownload(DownloadsCommon.UNBLOCK_MALWARE,
+ window);
+ ok(result, "Should return true when the user clicks on `Unblock` button.");
+});
+
+add_task(function* test_confirm_unblock_dialog_keep_safe() {
+ addDialogOpenObserver("accept");
+ let result = yield DownloadsCommon.confirmUnblockDownload(DownloadsCommon.UNBLOCK_MALWARE,
+ window);
+ ok(!result, "Should return false when the user clicks on `Keep me safe` button.");
+});
--- a/browser/components/downloads/test/browser/browser_first_download_panel.js
+++ b/browser/components/downloads/test/browser/browser_first_download_panel.js
@@ -3,63 +3,55 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Make sure the downloads panel only opens automatically on the first
* download it notices. All subsequent downloads, even across sessions, should
* not open the panel automatically.
*/
-function test_task()
-{
+add_task(function* test_first_download_panel() {
// Clear the download panel has shown preference first as this test is used to
// verify this preference's behaviour.
- let oldPrefValue = true;
- try {
- oldPrefValue = Services.prefs.getBoolPref("browser.download.panel.shown");
- } catch(ex) { }
+ let oldPrefValue = Services.prefs.getBoolPref("browser.download.panel.shown");
Services.prefs.setBoolPref("browser.download.panel.shown", false);
- try {
- // Ensure that state is reset in case previous tests didn't finish.
+ registerCleanupFunction(function*() {
+ // Clean up when the test finishes.
yield task_resetState();
- // With this set to false, we should automatically open the panel the first
- // time a download is started.
- DownloadsCommon.getData(window).panelHasShownBefore = false;
-
- let promise = promisePanelOpened();
- DownloadsCommon.getData(window)._notifyDownloadEvent("start");
- yield promise;
-
- // If we got here, that means the panel opened.
- DownloadsPanel.hidePanel();
-
- ok(DownloadsCommon.getData(window).panelHasShownBefore,
- "Should have recorded that the panel was opened on a download.")
-
- // Next, make sure that if we start another download, we don't open the
- // panel automatically.
- let originalOnPopupShown = DownloadsPanel.onPopupShown;
- DownloadsPanel.onPopupShown = function () {
- originalOnPopupShown.apply(this, arguments);
- ok(false, "Should not have opened the downloads panel.");
- };
-
- try {
- DownloadsCommon.getData(window)._notifyDownloadEvent("start");
-
- // Wait 2 seconds to ensure that the panel does not open.
- let deferTimeout = Promise.defer();
- setTimeout(deferTimeout.resolve, 2000);
- yield deferTimeout.promise;
- } finally {
- DownloadsPanel.onPopupShown = originalOnPopupShown;
- }
- } finally {
- // Clean up when the test finishes.
- yield task_resetState();
// Set the preference instead of clearing it afterwards to ensure the
// right value is used no matter what the default was. This ensures the
// panel doesn't appear and affect other tests.
Services.prefs.setBoolPref("browser.download.panel.shown", oldPrefValue);
- }
-}
+ });
+
+ // Ensure that state is reset in case previous tests didn't finish.
+ yield task_resetState();
+
+ // With this set to false, we should automatically open the panel the first
+ // time a download is started.
+ DownloadsCommon.getData(window).panelHasShownBefore = false;
+
+ let promise = promisePanelOpened();
+ DownloadsCommon.getData(window)._notifyDownloadEvent("start");
+ yield promise;
+
+ // If we got here, that means the panel opened.
+ DownloadsPanel.hidePanel();
+
+ ok(DownloadsCommon.getData(window).panelHasShownBefore,
+ "Should have recorded that the panel was opened on a download.")
+
+ // Next, make sure that if we start another download, we don't open the
+ // panel automatically.
+ let originalOnPopupShown = DownloadsPanel.onPopupShown;
+ DownloadsPanel.onPopupShown = function () {
+ originalOnPopupShown.apply(this, arguments);
+ ok(false, "Should not have opened the downloads panel.");
+ };
+
+ DownloadsCommon.getData(window)._notifyDownloadEvent("start");
+
+ // Wait 2 seconds to ensure that the panel does not open.
+ yield new Promise(resolve => setTimeout(resolve, 2000));
+ DownloadsPanel.onPopupShown = originalOnPopupShown;
+});
--- a/browser/components/downloads/test/browser/browser_overflow_anchor.js
+++ b/browser/components/downloads/test/browser/browser_overflow_anchor.js
@@ -1,74 +1,74 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
+registerCleanupFunction(function*() {
+ // Clean up when the test finishes.
+ yield task_resetState();
+});
+
/**
* Make sure the downloads button and indicator overflows into the nav-bar
* chevron properly, and then when those buttons are clicked in the overflow
* panel that the downloads panel anchors to the chevron.
*/
-function test_task() {
- try {
- // Ensure that state is reset in case previous tests didn't finish.
- yield task_resetState();
+add_task(function* test_overflow_anchor() {
+ // Ensure that state is reset in case previous tests didn't finish.
+ yield task_resetState();
- // Record the original width of the window so we can put it back when
- // this test finishes.
- let oldWidth = window.outerWidth;
+ // Record the original width of the window so we can put it back when
+ // this test finishes.
+ let oldWidth = window.outerWidth;
- // The downloads button should not be overflowed to begin with.
- let button = CustomizableUI.getWidget("downloads-button")
- .forWindow(window);
- ok(!button.overflowed, "Downloads button should not be overflowed.");
+ // The downloads button should not be overflowed to begin with.
+ let button = CustomizableUI.getWidget("downloads-button")
+ .forWindow(window);
+ ok(!button.overflowed, "Downloads button should not be overflowed.");
- // Hack - we lock the size of the default flex-y items in the nav-bar,
- // namely, the URL and search inputs. That way we can resize the
- // window without worrying about them flexing.
- const kFlexyItems = ["urlbar-container", "search-container"];
- registerCleanupFunction(() => unlockWidth(kFlexyItems));
- lockWidth(kFlexyItems);
+ // Hack - we lock the size of the default flex-y items in the nav-bar,
+ // namely, the URL and search inputs. That way we can resize the
+ // window without worrying about them flexing.
+ const kFlexyItems = ["urlbar-container", "search-container"];
+ registerCleanupFunction(() => unlockWidth(kFlexyItems));
+ lockWidth(kFlexyItems);
- // Resize the window to half of its original size. That should
- // be enough to overflow the downloads button.
- window.resizeTo(oldWidth / 2, window.outerHeight);
- yield waitForOverflowed(button, true);
+ // Resize the window to half of its original size. That should
+ // be enough to overflow the downloads button.
+ window.resizeTo(oldWidth / 2, window.outerHeight);
+ yield waitForOverflowed(button, true);
- let promise = promisePanelOpened();
- button.node.doCommand();
- yield promise;
-
- let panel = DownloadsPanel.panel;
- let chevron = document.getElementById("nav-bar-overflow-button");
- is(panel.anchorNode, chevron, "Panel should be anchored to the chevron.");
+ let promise = promisePanelOpened();
+ button.node.doCommand();
+ yield promise;
- DownloadsPanel.hidePanel();
+ let panel = DownloadsPanel.panel;
+ let chevron = document.getElementById("nav-bar-overflow-button");
+ is(panel.anchorNode, chevron, "Panel should be anchored to the chevron.");
- // Unlock the widths on the flex-y items.
- unlockWidth(kFlexyItems);
+ DownloadsPanel.hidePanel();
- // Put the window back to its original dimensions.
- window.resizeTo(oldWidth, window.outerHeight);
+ // Unlock the widths on the flex-y items.
+ unlockWidth(kFlexyItems);
- // The downloads button should eventually be un-overflowed.
- yield waitForOverflowed(button, false);
+ // Put the window back to its original dimensions.
+ window.resizeTo(oldWidth, window.outerHeight);
- // Now try opening the panel again.
- promise = promisePanelOpened();
- button.node.doCommand();
- yield promise;
+ // The downloads button should eventually be un-overflowed.
+ yield waitForOverflowed(button, false);
- is(panel.anchorNode.id, "downloads-indicator-anchor");
+ // Now try opening the panel again.
+ promise = promisePanelOpened();
+ button.node.doCommand();
+ yield promise;
- DownloadsPanel.hidePanel();
- } finally {
- // Clean up when the test finishes.
- yield task_resetState();
- }
-}
+ is(panel.anchorNode.id, "downloads-indicator-anchor");
+
+ DownloadsPanel.hidePanel();
+});
/**
* For some node IDs, finds the nodes and sets their min-width's to their
* current width, preventing them from flex-shrinking.
*
* @param aItemIDs an array of item IDs to set min-width on.
*/
function lockWidth(aItemIDs) {
--- a/browser/components/downloads/test/browser/head.js
+++ b/browser/components/downloads/test/browser/head.js
@@ -24,25 +24,16 @@ const nsIDM = Ci.nsIDownloadManager;
let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
registerCleanupFunction(function () {
gTestTargetFile.remove(false);
});
////////////////////////////////////////////////////////////////////////////////
-//// Infrastructure
-
-function test()
-{
- waitForExplicitFinish();
- Task.spawn(test_task).then(null, ex => ok(false, ex)).then(finish);
-}
-
-////////////////////////////////////////////////////////////////////////////////
//// Asynchronous support subroutines
function promiseFocus()
{
let deferred = Promise.defer();
waitForFocus(deferred.resolve);
return deferred.promise;
}
--- a/browser/components/feeds/FeedWriter.js
+++ b/browser/components/feeds/FeedWriter.js
@@ -853,17 +853,16 @@ FeedWriter.prototype = {
* Application" item is being selected with the keyboard. We do this
* by ignoring command events while the dropdown is closed (user
* arrowing through the combobox), but handling them while the
* combobox dropdown is open (user pressed enter when an item was
* selected). If we don't show the filepicker here, it will be shown
* when clicking "Subscribe Now".
*/
var popupbox = this._handlersMenuList.firstChild.boxObject;
- popupbox.QueryInterface(Components.interfaces.nsIPopupBoxObject);
if (popupbox.popupState == "hiding") {
this._chooseClientApp(function(aResult) {
if (!aResult) {
// Select the (per-prefs) selected handler if no application
// was selected
this._setSelectedHandler(this._getFeedType());
}
}.bind(this));
--- a/browser/components/loop/GoogleImporter.jsm
+++ b/browser/components/loop/GoogleImporter.jsm
@@ -446,17 +446,17 @@ this.GoogleImporter.prototype = {
// Process telephone numbers.
let phoneNodes = entry.getElementsByTagNameNS(kNS_GD, "phoneNumber");
if (phoneNodes.length) {
contact.tel = [];
for (let [,phoneNode] of Iterator(phoneNodes)) {
contact.tel.push({
pref: (phoneNode.getAttribute("primary") == "true"),
type: [getFieldType(phoneNode)],
- value: phoneNode.firstChild.nodeValue
+ value: phoneNode.getAttribute("uri").replace("tel:", "")
});
}
}
let orgNodes = entry.getElementsByTagNameNS(kNS_GD, "organization");
if (orgNodes.length) {
contact.org = [];
contact.jobTitle = [];
@@ -496,17 +496,17 @@ this.GoogleImporter.prototype = {
try {
email = getPreferred(contact);
} catch (ex) {}
if (email) {
contact.name = [email.value];
} else {
let tel;
try {
- tel = getPreferred(contact, "phone");
+ tel = getPreferred(contact, "tel");
} catch (ex) {}
if (tel) {
contact.name = [tel.value];
}
}
}
}
}
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -5,18 +5,21 @@
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://services-common/utils.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/loop/MozLoopService.jsm");
-Cu.import("resource:///modules/loop/LoopContacts.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "LoopContacts",
+ "resource:///modules/loop/LoopContacts.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
+ "resource:///modules/loop/LoopStorage.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "hookWindowCloseForPanelClose",
"resource://gre/modules/MozSocialAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
XPCOMUtils.defineLazyGetter(this, "appInfo", function() {
return Cc["@mozilla.org/xre/app-info;1"]
.getService(Ci.nsIXULAppInfo)
.QueryInterface(Ci.nsIXULRuntime);
@@ -226,16 +229,22 @@ function injectLoopAPI(targetWindow) {
* @returns {Object} The contacts API object
*/
contacts: {
enumerable: true,
get: function() {
if (contactsAPI) {
return contactsAPI;
}
+
+ // Make a database switch when a userProfile is active already.
+ let profile = MozLoopService.userProfile;
+ if (profile) {
+ LoopStorage.switchDatabase(profile.uid);
+ }
return contactsAPI = injectObjectAPI(LoopContacts, targetWindow);
}
},
/**
* Import a list of (new) contacts from an external data source.
*
* @param {Object} options Property bag of options for the importer
--- a/browser/components/loop/content/conversation.html
+++ b/browser/components/loop/content/conversation.html
@@ -30,15 +30,17 @@
<script type="text/javascript" src="loop/shared/js/mixins.js"></script>
<script type="text/javascript" src="loop/shared/js/views.js"></script>
<script type="text/javascript" src="loop/shared/js/feedbackApiClient.js"></script>
<script type="text/javascript" src="loop/shared/js/actions.js"></script>
<script type="text/javascript" src="loop/shared/js/validate.js"></script>
<script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
<script type="text/javascript" src="loop/shared/js/otSdkDriver.js"></script>
<script type="text/javascript" src="loop/shared/js/conversationStore.js"></script>
+ <script type="text/javascript" src="loop/shared/js/localRoomStore.js"></script>
<script type="text/javascript" src="loop/js/conversationViews.js"></script>
<script type="text/javascript" src="loop/shared/js/websocket.js"></script>
<script type="text/javascript" src="loop/js/client.js"></script>
<script type="text/javascript" src="loop/js/conversationViews.js"></script>
+ <script type="text/javascript" src="loop/js/roomViews.js"></script>
<script type="text/javascript" src="loop/js/conversation.js"></script>
</body>
</html>
--- a/browser/components/loop/content/js/conversation.js
+++ b/browser/components/loop/content/js/conversation.js
@@ -9,18 +9,21 @@
var loop = loop || {};
loop.conversation = (function(mozL10n) {
"use strict";
var sharedViews = loop.shared.views;
var sharedMixins = loop.shared.mixins;
var sharedModels = loop.shared.models;
+ var sharedActions = loop.shared.actions;
+
var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
var CallIdentifierView = loop.conversationViews.CallIdentifierView;
+ var EmptyRoomView = loop.roomViews.EmptyRoomView;
var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
mixins: [sharedMixins.DropdownMenuMixin],
propTypes: {
model: React.PropTypes.object.isRequired,
video: React.PropTypes.bool.isRequired
},
@@ -475,40 +478,52 @@ loop.conversation = (function(mozL10n) {
console.error("Failed initiating the call session.");
},
});
/**
* Master controller view for handling if incoming or outgoing calls are
* in progress, and hence, which view to display.
*/
- var ConversationControllerView = React.createClass({displayName: 'ConversationControllerView',
+ var AppControllerView = React.createClass({displayName: 'AppControllerView',
propTypes: {
// XXX Old types required for incoming call view.
client: React.PropTypes.instanceOf(loop.Client).isRequired,
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
.isRequired,
sdk: React.PropTypes.object.isRequired,
// XXX New types for OutgoingConversationView
store: React.PropTypes.instanceOf(loop.store.ConversationStore).isRequired,
- dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
+ dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+
+ // if not passed, this is not a room view
+ localRoomStore: React.PropTypes.instanceOf(loop.store.LocalRoomStore)
},
getInitialState: function() {
return this.props.store.attributes;
},
componentWillMount: function() {
this.props.store.on("change:outgoing", function() {
this.setState(this.props.store.attributes);
}, this);
},
render: function() {
+ if (this.props.localRoomStore) {
+ return (
+ EmptyRoomView({
+ mozLoop: navigator.mozLoop,
+ localRoomStore: this.props.localRoomStore}
+ )
+ );
+ }
+
// Don't display anything, until we know what type of call we are.
if (this.state.outgoing === undefined) {
return null;
}
if (this.state.outgoing) {
return (OutgoingConversationView({
store: this.props.store,
@@ -564,53 +579,72 @@ loop.conversation = (function(mozL10n) {
{sdk: window.OT} // Model dependencies
);
// Obtain the callId and pass it through
var helper = new loop.shared.utils.Helper();
var locationHash = helper.locationHash();
var callId;
var outgoing;
+ var localRoomStore;
- var hash = locationHash.match(/\#incoming\/(.*)/);
+ // XXX removeMe, along with noisy comment at the beginning of
+ // conversation_test.js "when locationHash begins with #room".
+ if (navigator.mozLoop.getLoopBoolPref("test.alwaysUseRooms")) {
+ locationHash = "#room/32";
+ }
+
+ var hash = locationHash.match(/#incoming\/(.*)/);
if (hash) {
callId = hash[1];
outgoing = false;
+ } else if (hash = locationHash.match(/#room\/(.*)/)) {
+ localRoomStore = new loop.store.LocalRoomStore({
+ dispatcher: dispatcher,
+ mozLoop: navigator.mozLoop
+ });
} else {
- hash = locationHash.match(/\#outgoing\/(.*)/);
+ hash = locationHash.match(/#outgoing\/(.*)/);
if (hash) {
callId = hash[1];
outgoing = true;
}
}
conversation.set({callId: callId});
window.addEventListener("unload", function(event) {
// Handle direct close of dialog box via [x] control.
navigator.mozLoop.releaseCallData(callId);
});
document.body.classList.add(loop.shared.utils.getTargetPlatform());
- React.renderComponent(ConversationControllerView({
+ React.renderComponent(AppControllerView({
+ localRoomStore: localRoomStore,
store: conversationStore,
client: client,
conversation: conversation,
dispatcher: dispatcher,
sdk: window.OT}
), document.querySelector('#main'));
+ if (localRoomStore) {
+ dispatcher.dispatch(
+ new sharedActions.SetupEmptyRoom({localRoomId: hash[1]}));
+ return;
+ }
+
dispatcher.dispatch(new loop.shared.actions.GatherCallData({
callId: callId,
outgoing: outgoing
}));
}
return {
- ConversationControllerView: ConversationControllerView,
+ AppControllerView: AppControllerView,
IncomingConversationView: IncomingConversationView,
IncomingCallView: IncomingCallView,
init: init
};
})(document.mozL10n);
document.addEventListener('DOMContentLoaded', loop.conversation.init);
--- a/browser/components/loop/content/js/conversation.jsx
+++ b/browser/components/loop/content/js/conversation.jsx
@@ -9,18 +9,21 @@
var loop = loop || {};
loop.conversation = (function(mozL10n) {
"use strict";
var sharedViews = loop.shared.views;
var sharedMixins = loop.shared.mixins;
var sharedModels = loop.shared.models;
+ var sharedActions = loop.shared.actions;
+
var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
var CallIdentifierView = loop.conversationViews.CallIdentifierView;
+ var EmptyRoomView = loop.roomViews.EmptyRoomView;
var IncomingCallView = React.createClass({
mixins: [sharedMixins.DropdownMenuMixin],
propTypes: {
model: React.PropTypes.object.isRequired,
video: React.PropTypes.bool.isRequired
},
@@ -475,40 +478,52 @@ loop.conversation = (function(mozL10n) {
console.error("Failed initiating the call session.");
},
});
/**
* Master controller view for handling if incoming or outgoing calls are
* in progress, and hence, which view to display.
*/
- var ConversationControllerView = React.createClass({
+ var AppControllerView = React.createClass({
propTypes: {
// XXX Old types required for incoming call view.
client: React.PropTypes.instanceOf(loop.Client).isRequired,
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
.isRequired,
sdk: React.PropTypes.object.isRequired,
// XXX New types for OutgoingConversationView
store: React.PropTypes.instanceOf(loop.store.ConversationStore).isRequired,
- dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
+ dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+
+ // if not passed, this is not a room view
+ localRoomStore: React.PropTypes.instanceOf(loop.store.LocalRoomStore)
},
getInitialState: function() {
return this.props.store.attributes;
},
componentWillMount: function() {
this.props.store.on("change:outgoing", function() {
this.setState(this.props.store.attributes);
}, this);
},
render: function() {
+ if (this.props.localRoomStore) {
+ return (
+ <EmptyRoomView
+ mozLoop={navigator.mozLoop}
+ localRoomStore={this.props.localRoomStore}
+ />
+ );
+ }
+
// Don't display anything, until we know what type of call we are.
if (this.state.outgoing === undefined) {
return null;
}
if (this.state.outgoing) {
return (<OutgoingConversationView
store={this.props.store}
@@ -564,53 +579,72 @@ loop.conversation = (function(mozL10n) {
{sdk: window.OT} // Model dependencies
);
// Obtain the callId and pass it through
var helper = new loop.shared.utils.Helper();
var locationHash = helper.locationHash();
var callId;
var outgoing;
+ var localRoomStore;
- var hash = locationHash.match(/\#incoming\/(.*)/);
+ // XXX removeMe, along with noisy comment at the beginning of
+ // conversation_test.js "when locationHash begins with #room".
+ if (navigator.mozLoop.getLoopBoolPref("test.alwaysUseRooms")) {
+ locationHash = "#room/32";
+ }
+
+ var hash = locationHash.match(/#incoming\/(.*)/);
if (hash) {
callId = hash[1];
outgoing = false;
+ } else if (hash = locationHash.match(/#room\/(.*)/)) {
+ localRoomStore = new loop.store.LocalRoomStore({
+ dispatcher: dispatcher,
+ mozLoop: navigator.mozLoop
+ });
} else {
- hash = locationHash.match(/\#outgoing\/(.*)/);
+ hash = locationHash.match(/#outgoing\/(.*)/);
if (hash) {
callId = hash[1];
outgoing = true;
}
}
conversation.set({callId: callId});
window.addEventListener("unload", function(event) {
// Handle direct close of dialog box via [x] control.
navigator.mozLoop.releaseCallData(callId);
});
document.body.classList.add(loop.shared.utils.getTargetPlatform());
- React.renderComponent(<ConversationControllerView
+ React.renderComponent(<AppControllerView
+ localRoomStore={localRoomStore}
store={conversationStore}
client={client}
conversation={conversation}
dispatcher={dispatcher}
sdk={window.OT}
/>, document.querySelector('#main'));
+ if (localRoomStore) {
+ dispatcher.dispatch(
+ new sharedActions.SetupEmptyRoom({localRoomId: hash[1]}));
+ return;
+ }
+
dispatcher.dispatch(new loop.shared.actions.GatherCallData({
callId: callId,
outgoing: outgoing
}));
}
return {
- ConversationControllerView: ConversationControllerView,
+ AppControllerView: AppControllerView,
IncomingConversationView: IncomingConversationView,
IncomingCallView: IncomingCallView,
init: init
};
})(document.mozL10n);
document.addEventListener('DOMContentLoaded', loop.conversation.init);
--- a/browser/components/loop/content/js/conversationViews.js
+++ b/browser/components/loop/content/js/conversationViews.js
@@ -279,19 +279,21 @@ loop.conversationViews = (function(mozL1
// height set to 100%" to fix video layout on Google Chrome
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=1020445
return {
insertMode: "append",
width: "100%",
height: "100%",
publishVideo: this.props.video.enabled,
style: {
+ audioLevelDisplayMode: "off",
bugDisplayMode: "off",
buttonDisplayMode: "off",
- nameDisplayMode: "off"
+ nameDisplayMode: "off",
+ videoDisabledDisplayMode: "off"
}
}
},
/**
* Used to update the video container whenever the orientation or size of the
* display area changes.
*/
--- a/browser/components/loop/content/js/conversationViews.jsx
+++ b/browser/components/loop/content/js/conversationViews.jsx
@@ -279,19 +279,21 @@ loop.conversationViews = (function(mozL1
// height set to 100%" to fix video layout on Google Chrome
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=1020445
return {
insertMode: "append",
width: "100%",
height: "100%",
publishVideo: this.props.video.enabled,
style: {
+ audioLevelDisplayMode: "off",
bugDisplayMode: "off",
buttonDisplayMode: "off",
- nameDisplayMode: "off"
+ nameDisplayMode: "off",
+ videoDisabledDisplayMode: "off"
}
}
},
/**
* Used to update the video container whenever the orientation or size of the
* display area changes.
*/
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/content/js/roomViews.js
@@ -0,0 +1,109 @@
+/** @jsx React.DOM */
+
+/* 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/. */
+
+/* global loop:true, React */
+
+var loop = loop || {};
+loop.roomViews = (function(mozL10n) {
+ "use strict";
+
+ /**
+ * Root object, by default set to window.
+ * @type {DOMWindow|Object}
+ */
+ var rootObject = window;
+
+ /**
+ * Sets a new root object. This is useful for testing native DOM events so we
+ * can fake them.
+ *
+ * @param {Object}
+ */
+ function setRootObject(obj) {
+ rootObject = obj;
+ }
+
+ var EmptyRoomView = React.createClass({displayName: 'EmptyRoomView',
+ mixins: [Backbone.Events],
+
+ propTypes: {
+ mozLoop:
+ React.PropTypes.object.isRequired,
+ localRoomStore:
+ React.PropTypes.instanceOf(loop.store.LocalRoomStore).isRequired,
+ },
+
+ getInitialState: function() {
+ return this.props.localRoomStore.getStoreState();
+ },
+
+ componentWillMount: function() {
+ this.listenTo(this.props.localRoomStore, "change",
+ this._onLocalRoomStoreChanged);
+ },
+
+ componentDidMount: function() {
+ // XXXremoveMe (just the conditional itself) in patch 2 for bug 1074686,
+ // once the addCallback stuff lands
+ if (this.props.mozLoop.rooms && this.props.mozLoop.rooms.addCallback) {
+ this.props.mozLoop.rooms.addCallback(
+ this.state.localRoomId,
+ "RoomCreationError", this.onCreationError);
+ }
+ },
+
+ /**
+ * Attached to the "RoomCreationError" with mozLoop.rooms.addCallback,
+ * which is fired mozLoop.rooms.createRoom from the panel encounters an
+ * error while attempting to create the room for this view.
+ *
+ * @param {Error} err - JS Error object with info about the problem
+ */
+ onCreationError: function(err) {
+ // XXX put up a user friendly error instead of this
+ rootObject.console.error("EmptyRoomView creation error: ", err);
+ },
+
+ /**
+ * Handles a "change" event on the localRoomStore, and updates this.state
+ * to match the store.
+ *
+ * @private
+ */
+ _onLocalRoomStoreChanged: function() {
+ this.setState(this.props.localRoomStore.getStoreState());
+ },
+
+ componentWillUnmount: function() {
+ this.stopListening(this.props.localRoomStore);
+
+ // XXXremoveMe (just the conditional itself) in patch 2 for bug 1074686,
+ // once the addCallback stuff lands
+ if (this.props.mozLoop.rooms && this.props.mozLoop.rooms.removeCallback) {
+ this.props.mozLoop.rooms.removeCallback(
+ this.state.localRoomId,
+ "RoomCreationError", this.onCreationError);
+ }
+ },
+
+ render: function() {
+ // XXX switch this to use the document title mixin once bug 1081079 lands
+ if (this.state.serverData && this.state.serverData.roomName) {
+ rootObject.document.title = this.state.serverData.roomName;
+ }
+
+ return (
+ React.DOM.div({className: "goat"})
+ );
+ }
+ });
+
+ return {
+ setRootObject: setRootObject,
+ EmptyRoomView: EmptyRoomView
+ };
+
+})(document.mozL10n || navigator.mozL10n);;
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/content/js/roomViews.jsx
@@ -0,0 +1,109 @@
+/** @jsx React.DOM */
+
+/* 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/. */
+
+/* global loop:true, React */
+
+var loop = loop || {};
+loop.roomViews = (function(mozL10n) {
+ "use strict";
+
+ /**
+ * Root object, by default set to window.
+ * @type {DOMWindow|Object}
+ */
+ var rootObject = window;
+
+ /**
+ * Sets a new root object. This is useful for testing native DOM events so we
+ * can fake them.
+ *
+ * @param {Object}
+ */
+ function setRootObject(obj) {
+ rootObject = obj;
+ }
+
+ var EmptyRoomView = React.createClass({
+ mixins: [Backbone.Events],
+
+ propTypes: {
+ mozLoop:
+ React.PropTypes.object.isRequired,
+ localRoomStore:
+ React.PropTypes.instanceOf(loop.store.LocalRoomStore).isRequired,
+ },
+
+ getInitialState: function() {
+ return this.props.localRoomStore.getStoreState();
+ },
+
+ componentWillMount: function() {
+ this.listenTo(this.props.localRoomStore, "change",
+ this._onLocalRoomStoreChanged);
+ },
+
+ componentDidMount: function() {
+ // XXXremoveMe (just the conditional itself) in patch 2 for bug 1074686,
+ // once the addCallback stuff lands
+ if (this.props.mozLoop.rooms && this.props.mozLoop.rooms.addCallback) {
+ this.props.mozLoop.rooms.addCallback(
+ this.state.localRoomId,
+ "RoomCreationError", this.onCreationError);
+ }
+ },
+
+ /**
+ * Attached to the "RoomCreationError" with mozLoop.rooms.addCallback,
+ * which is fired mozLoop.rooms.createRoom from the panel encounters an
+ * error while attempting to create the room for this view.
+ *
+ * @param {Error} err - JS Error object with info about the problem
+ */
+ onCreationError: function(err) {
+ // XXX put up a user friendly error instead of this
+ rootObject.console.error("EmptyRoomView creation error: ", err);
+ },
+
+ /**
+ * Handles a "change" event on the localRoomStore, and updates this.state
+ * to match the store.
+ *
+ * @private
+ */
+ _onLocalRoomStoreChanged: function() {
+ this.setState(this.props.localRoomStore.getStoreState());
+ },
+
+ componentWillUnmount: function() {
+ this.stopListening(this.props.localRoomStore);
+
+ // XXXremoveMe (just the conditional itself) in patch 2 for bug 1074686,
+ // once the addCallback stuff lands
+ if (this.props.mozLoop.rooms && this.props.mozLoop.rooms.removeCallback) {
+ this.props.mozLoop.rooms.removeCallback(
+ this.state.localRoomId,
+ "RoomCreationError", this.onCreationError);
+ }
+ },
+
+ render: function() {
+ // XXX switch this to use the document title mixin once bug 1081079 lands
+ if (this.state.serverData && this.state.serverData.roomName) {
+ rootObject.document.title = this.state.serverData.roomName;
+ }
+
+ return (
+ <div className="goat"/>
+ );
+ }
+ });
+
+ return {
+ setRootObject: setRootObject,
+ EmptyRoomView: EmptyRoomView
+ };
+
+})(document.mozL10n || navigator.mozL10n);;
--- a/browser/components/loop/content/shared/js/actions.js
+++ b/browser/components/loop/content/shared/js/actions.js
@@ -120,11 +120,21 @@ loop.shared.actions = (function() {
enabled: Boolean
}),
/**
* Retrieves room list.
* XXX: should move to some roomActions module - refs bug 1079284
*/
GetAllRooms: Action.define("getAllRooms", {
- })
+ }),
+
+ /**
+ * Primes localRoomStore with roomLocalId, which triggers the EmptyRoomView
+ * to do any necessary setup.
+ *
+ * XXX should move to localRoomActions module
+ */
+ SetupEmptyRoom: Action.define("setupEmptyRoom", {
+ localRoomId: String
+ }),
};
})();
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/content/shared/js/localRoomStore.js
@@ -0,0 +1,113 @@
+/* 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/. */
+
+/* global loop:true */
+
+var loop = loop || {};
+loop.store = loop.store || {};
+loop.store.LocalRoomStore = (function() {
+ "use strict";
+
+ var sharedActions = loop.shared.actions;
+
+ /**
+ * Store for things that are local to this instance (in this profile, on
+ * this machine) of this roomRoom store, in addition to a mirror of some
+ * remote-state.
+ *
+ * @extends {Backbone.Events}
+ *
+ * @param {Object} options - Options object
+ * @param {loop.Dispatcher} options.dispatch - The dispatcher for dispatching
+ * actions and registering to consume them.
+ * @param {MozLoop} options.mozLoop - MozLoop API provider object
+ */
+ function LocalRoomStore(options) {
+ options = options || {};
+
+ if (!options.dispatcher) {
+ throw new Error("Missing option dispatcher");
+ }
+ this.dispatcher = options.dispatcher;
+
+ if (!options.mozLoop) {
+ throw new Error("Missing option mozLoop");
+ }
+ this.mozLoop = options.mozLoop;
+
+ this.dispatcher.register(this, ["setupEmptyRoom"]);
+ }
+
+ LocalRoomStore.prototype = _.extend({
+
+ /**
+ * Stored data reflecting the local state of a given room, used to drive
+ * the room's views.
+ *
+ * @property {Object} serverData - local cache of the data returned by
+ * MozLoop.getRoomData for this room.
+ * @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
+ *
+ * @property {Error=} error - if the room is an error state, this will be
+ * set to an Error object reflecting the problem;
+ * otherwise it will be unset.
+ *
+ * @property {String} localRoomId - profile-local identifier used with
+ * the MozLoop API.
+ */
+ _storeState: {
+ },
+
+ getStoreState: function() {
+ return this._storeState;
+ },
+
+ setStoreState: function(state) {
+ this._storeState = state;
+ this.trigger("change");
+ },
+
+ /**
+ * Proxy to mozLoop.rooms.getRoomData for setupEmptyRoom action.
+ *
+ * XXXremoveMe Can probably be removed when bug 1074664 lands.
+ *
+ * @param {sharedActions.setupEmptyRoom} actionData
+ * @param {Function} cb Callback(error, roomData)
+ */
+ _fetchRoomData: function(actionData, cb) {
+ if (this.mozLoop.rooms && this.mozLoop.rooms.getRoomData) {
+ this.mozLoop.rooms.getRoomData(actionData.localRoomId, cb);
+ } else {
+ cb(null, {roomName: "Donkeys"});
+ }
+ },
+
+ /**
+ * Execute setupEmptyRoom event action from the dispatcher. This primes
+ * the store with the localRoomId, and calls MozLoop.getRoomData on that
+ * ID. This will return either a reflection of state on the server, or,
+ * if the createRoom call hasn't yet returned, it will have at least the
+ * roomName as specified to the createRoom method.
+ *
+ * When the room name gets set, that will trigger the view to display
+ * that name.
+ *
+ * @param {sharedActions.setupEmptyRoom} actionData
+ */
+ setupEmptyRoom: function(actionData) {
+ this._fetchRoomData(actionData, function(error, roomData) {
+ this.setStoreState({
+ error: error,
+ localRoomId: actionData.localRoomId,
+ serverData: roomData
+ });
+ }.bind(this));
+ }
+
+ }, Backbone.Events);
+
+ return LocalRoomStore;
+
+})();
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/components/loop/content/shared/js/views.js
@@ -146,19 +146,21 @@ loop.shared.views = (function(_, OT, l10
// height set to 100%" to fix video layout on Google Chrome
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=1020445
publisherConfig: {
insertMode: "append",
width: "100%",
height: "100%",
style: {
+ audioLevelDisplayMode: "off",
bugDisplayMode: "off",
buttonDisplayMode: "off",
- nameDisplayMode: "off"
+ nameDisplayMode: "off",
+ videoDisabledDisplayMode: "off"
}
},
getDefaultProps: function() {
return {
initiate: true,
video: {enabled: true, visible: true},
audio: {enabled: true, visible: true}
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/components/loop/content/shared/js/views.jsx
@@ -146,19 +146,21 @@ loop.shared.views = (function(_, OT, l10
// height set to 100%" to fix video layout on Google Chrome
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=1020445
publisherConfig: {
insertMode: "append",
width: "100%",
height: "100%",
style: {
+ audioLevelDisplayMode: "off",
bugDisplayMode: "off",
buttonDisplayMode: "off",
- nameDisplayMode: "off"
+ nameDisplayMode: "off",
+ videoDisabledDisplayMode: "off"
}
},
getDefaultProps: function() {
return {
initiate: true,
video: {enabled: true, visible: true},
audio: {enabled: true, visible: true}
--- a/browser/components/loop/jar.mn
+++ b/browser/components/loop/jar.mn
@@ -12,16 +12,17 @@ browser.jar:
# Desktop script
content/browser/loop/js/client.js (content/js/client.js)
content/browser/loop/js/conversation.js (content/js/conversation.js)
content/browser/loop/js/otconfig.js (content/js/otconfig.js)
content/browser/loop/js/panel.js (content/js/panel.js)
content/browser/loop/js/contacts.js (content/js/contacts.js)
content/browser/loop/js/conversationViews.js (content/js/conversationViews.js)
+ content/browser/loop/js/roomViews.js (content/js/roomViews.js)
# Shared styles
content/browser/loop/shared/css/reset.css (content/shared/css/reset.css)
content/browser/loop/shared/css/common.css (content/shared/css/common.css)
content/browser/loop/shared/css/panel.css (content/shared/css/panel.css)
content/browser/loop/shared/css/conversation.css (content/shared/css/conversation.css)
content/browser/loop/shared/css/contacts.css (content/shared/css/contacts.css)
@@ -52,16 +53,17 @@ browser.jar:
content/browser/loop/shared/img/icons-10x10.svg (content/shared/img/icons-10x10.svg)
content/browser/loop/shared/img/icons-14x14.svg (content/shared/img/icons-14x14.svg)
content/browser/loop/shared/img/icons-16x16.svg (content/shared/img/icons-16x16.svg)
# Shared scripts
content/browser/loop/shared/js/actions.js (content/shared/js/actions.js)
content/browser/loop/shared/js/conversationStore.js (content/shared/js/conversationStore.js)
content/browser/loop/shared/js/roomListStore.js (content/shared/js/roomListStore.js)
+ content/browser/loop/shared/js/localRoomStore.js (content/shared/js/localRoomStore.js)
content/browser/loop/shared/js/dispatcher.js (content/shared/js/dispatcher.js)
content/browser/loop/shared/js/feedbackApiClient.js (content/shared/js/feedbackApiClient.js)
content/browser/loop/shared/js/models.js (content/shared/js/models.js)
content/browser/loop/shared/js/mixins.js (content/shared/js/mixins.js)
content/browser/loop/shared/js/otSdkDriver.js (content/shared/js/otSdkDriver.js)
content/browser/loop/shared/js/views.js (content/shared/js/views.js)
content/browser/loop/shared/js/utils.js (content/shared/js/utils.js)
content/browser/loop/shared/js/validate.js (content/shared/js/validate.js)
--- a/browser/components/loop/test/desktop-local/conversationViews_test.js
+++ b/browser/components/loop/test/desktop-local/conversationViews_test.js
@@ -1,14 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
var expect = chai.expect;
describe("loop.conversationViews", function () {
+ "use strict";
+
var sandbox, oldTitle, view, dispatcher, contact;
var CALL_STATES = loop.store.CALL_STATES;
beforeEach(function() {
sandbox = sinon.sandbox.create();
oldTitle = document.title;
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -85,35 +85,72 @@ describe("loop.conversation", function()
overrideGuidStorage: sinon.stub()
};
});
afterEach(function() {
delete window.OT;
});
- it("should initalize L10n", function() {
+ it("should initialize L10n", function() {
loop.conversation.init();
sinon.assert.calledOnce(document.mozL10n.initialize);
sinon.assert.calledWithExactly(document.mozL10n.initialize,
navigator.mozLoop);
});
- it("should create the ConversationControllerView", function() {
+ it("should create the AppControllerView", function() {
loop.conversation.init();
sinon.assert.calledOnce(React.renderComponent);
sinon.assert.calledWith(React.renderComponent,
sinon.match(function(value) {
return TestUtils.isDescriptorOfType(value,
- loop.conversation.ConversationControllerView);
+ loop.conversation.AppControllerView);
}));
});
+ describe("when locationHash begins with #room", function () {
+ // XXX must stay in sync with "test.alwaysUseRooms" pref check
+ // in conversation.jsx:init until we remove that code, which should
+ // happen in the second patch in bug 1074686, at which time this comment
+ // can go away as well.
+ var fakeRoomID = "32";
+
+ beforeEach(function() {
+ loop.shared.utils.Helper.prototype.locationHash
+ .returns("#room/" + fakeRoomID);
+
+ sandbox.stub(loop.store, "LocalRoomStore");
+ });
+
+ it("should create a localRoomStore", function() {
+ loop.conversation.init();
+
+ sinon.assert.calledOnce(loop.store.LocalRoomStore);
+ sinon.assert.calledWithNew(loop.store.LocalRoomStore);
+ sinon.assert.calledWithExactly(loop.store.LocalRoomStore,
+ sinon.match({
+ dispatcher: sinon.match.instanceOf(loop.Dispatcher),
+ mozLoop: sinon.match.same(navigator.mozLoop)
+ }));
+ });
+
+ it("should dispatch SetupEmptyRoom with localRoomId from locationHash",
+ function() {
+
+ loop.conversation.init();
+
+ sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
+ sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
+ new loop.shared.actions.SetupEmptyRoom({localRoomId: fakeRoomID}));
+ });
+ });
+
it("should trigger a gatherCallData action", function() {
loop.conversation.init();
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
new loop.shared.actions.GatherCallData({
callId: "42",
outgoing: false
@@ -133,21 +170,22 @@ describe("loop.conversation", function()
outgoing: true
}));
});
});
describe("ConversationControllerView", function() {
var store, conversation, client, ccView, oldTitle, dispatcher;
- function mountTestComponent() {
+ function mountTestComponent(localRoomStore) {
return TestUtils.renderIntoDocument(
- loop.conversation.ConversationControllerView({
+ loop.conversation.AppControllerView({
client: client,
conversation: conversation,
+ localRoomStore: localRoomStore,
sdk: {},
store: store
}));
}
beforeEach(function() {
oldTitle = document.title;
client = new loop.Client();
@@ -188,16 +226,32 @@ describe("loop.conversation", function()
it("should display the IncomingConversationView for incoming calls", function() {
store.set({outgoing: false});
ccView = mountTestComponent();
TestUtils.findRenderedComponentWithType(ccView,
loop.conversation.IncomingConversationView);
});
+
+ it("should display the EmptyRoomView for rooms", function() {
+ navigator.mozLoop.rooms = {
+ addCallback: function() {},
+ removeCallback: function() {}
+ };
+ var localRoomStore = new loop.store.LocalRoomStore({
+ mozLoop: navigator.mozLoop,
+ dispatcher: dispatcher
+ });
+
+ ccView = mountTestComponent(localRoomStore);
+
+ TestUtils.findRenderedComponentWithType(ccView,
+ loop.roomViews.EmptyRoomView);
+ });
});
describe("IncomingConversationView", function() {
var conversation, client, icView, oldTitle;
function mountTestComponent() {
return TestUtils.renderIntoDocument(
loop.conversation.IncomingConversationView({
--- a/browser/components/loop/test/desktop-local/index.html
+++ b/browser/components/loop/test/desktop-local/index.html
@@ -40,25 +40,28 @@
<script src="../../content/shared/js/views.js"></script>
<script src="../../content/shared/js/websocket.js"></script>
<script src="../../content/shared/js/actions.js"></script>
<script src="../../content/shared/js/validate.js"></script>
<script src="../../content/shared/js/dispatcher.js"></script>
<script src="../../content/shared/js/otSdkDriver.js"></script>
<script src="../../content/shared/js/roomListStore.js"></script>
<script src="../../content/js/client.js"></script>
+ <script src="../../content/shared/js/localRoomStore.js"></script>
+ <script src="../../content/js/roomViews.js"></script>
<script src="../../content/js/conversationViews.js"></script>
<script src="../../content/js/conversation.js"></script>
<script type="text/javascript;version=1.8" src="../../content/js/contacts.js"></script>
<script src="../../content/js/panel.js"></script>
<!-- Test scripts -->
<script src="client_test.js"></script>
<script src="conversation_test.js"></script>
<script src="panel_test.js"></script>
+ <script src="roomViews_test.js"></script>
<script src="conversationViews_test.js"></script>
<script>
// Stop the default init functions running to avoid conflicts in tests
document.removeEventListener('DOMContentLoaded', loop.panel.init);
document.removeEventListener('DOMContentLoaded', loop.conversation.init);
mocha.run(function () {
$("#mocha").append("<p id='complete'>Complete.</p>");
});
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/desktop-local/roomViews_test.js
@@ -0,0 +1,94 @@
+var expect = chai.expect;
+
+describe("loop.roomViews", function () {
+ "use strict";
+
+ var store, fakeWindow, sandbox, fakeAddCallback, fakeMozLoop,
+ fakeRemoveCallback, fakeRoomId, fakeWindow;
+
+ beforeEach(function() {
+ sandbox = sinon.sandbox.create();
+
+ fakeRoomId = "fakeRoomId";
+ fakeAddCallback =
+ sandbox.stub().withArgs(fakeRoomId, "RoomCreationError");
+ fakeRemoveCallback =
+ sandbox.stub().withArgs(fakeRoomId, "RoomCreationError");
+ fakeMozLoop = { rooms: { addCallback: fakeAddCallback,
+ removeCallback: fakeRemoveCallback } };
+
+ fakeWindow = { document: {} };
+ loop.roomViews.setRootObject(fakeWindow);
+
+ store = new loop.store.LocalRoomStore({
+ dispatcher: { register: function() {} },
+ mozLoop: fakeMozLoop
+ });
+ store.setStoreState({localRoomId: fakeRoomId});
+ });
+
+ afterEach(function() {
+ sinon.sandbox.restore();
+ loop.roomViews.setRootObject(window);
+ });
+
+ describe("EmptyRoomView", function() {
+ function mountTestComponent() {
+ return TestUtils.renderIntoDocument(
+ new loop.roomViews.EmptyRoomView({
+ mozLoop: fakeMozLoop,
+ localRoomStore: store
+ }));
+ }
+
+ describe("#componentDidMount", function() {
+ it("should add #onCreationError using mozLoop.rooms.addCallback",
+ function() {
+
+ var testComponent = mountTestComponent();
+
+ sinon.assert.calledOnce(fakeMozLoop.rooms.addCallback);
+ sinon.assert.calledWithExactly(fakeMozLoop.rooms.addCallback,
+ fakeRoomId, "RoomCreationError", testComponent.onCreationError);
+ });
+ });
+
+ describe("#componentWillUnmount", function () {
+ it("should remove #onCreationError using mozLoop.rooms.addCallback",
+ function () {
+ var testComponent = mountTestComponent();
+
+ testComponent.componentWillUnmount();
+
+ sinon.assert.calledOnce(fakeMozLoop.rooms.removeCallback);
+ sinon.assert.calledWithExactly(fakeMozLoop.rooms.removeCallback,
+ fakeRoomId, "RoomCreationError", testComponent.onCreationError);
+ });
+ });
+
+ describe("#onCreationError", function() {
+ it("should log an error using console.error", function() {
+ fakeWindow.console = { error: sandbox.stub() };
+ var testComponent = mountTestComponent();
+
+ testComponent.onCreationError(new Error("fake error"));
+
+ sinon.assert.calledOnce(fakeWindow.console.error);
+ });
+ });
+
+ describe("#render", function() {
+ it("should set document.title to store.serverData.roomName",
+ function() {
+ var fakeRoomName = "Monkey";
+ store.setStoreState({serverData: {roomName: fakeRoomName},
+ localRoomId: fakeRoomId});
+
+ mountTestComponent();
+
+ expect(fakeWindow.document.title).to.equal(fakeRoomName);
+ })
+ });
+
+ });
+});
--- a/browser/components/loop/test/functional/test_1_browser_call.py
+++ b/browser/components/loop/test/functional/test_1_browser_call.py
@@ -1,14 +1,14 @@
from marionette_test import MarionetteTestCase
from by import By
import urlparse
-from marionette.errors import NoSuchElementException, StaleElementException
+from errors import NoSuchElementException, StaleElementException
# noinspection PyUnresolvedReferences
-from marionette.wait import Wait
+from wait import Wait
from time import sleep
import os
import sys
sys.path.insert(1, os.path.dirname(os.path.abspath(__file__)))
from serversetup import LoopTestServers
from config import *
--- a/browser/components/loop/test/mochitest/browser_GoogleImporter.js
+++ b/browser/components/loop/test/mochitest/browser_GoogleImporter.js
@@ -18,60 +18,68 @@ function promiseImport() {
}
add_task(function* test_GoogleImport() {
let stats;
// An error may throw and the test will fail when that happens.
stats = yield promiseImport();
// Assert the world.
- Assert.equal(stats.total, 5, "Five contacts should get processed");
- Assert.equal(stats.success, 5, "Five contacts should be imported");
+ Assert.equal(stats.total, 6, "Five contacts should get processed");
+ Assert.equal(stats.success, 6, "Five contacts should be imported");
yield promiseImport();
- Assert.equal(Object.keys(mockDb._store).length, 5, "Database should contain only five contact after reimport");
+ Assert.equal(Object.keys(mockDb._store).length, 6, "Database should contain only five contact after reimport");
- let c = mockDb._store[mockDb._next_guid - 5];
+ let c = mockDb._store[mockDb._next_guid - 6];
Assert.equal(c.name[0], "John Smith", "Full name should match");
Assert.equal(c.givenName[0], "John", "Given name should match");
Assert.equal(c.familyName[0], "Smith", "Family name should match");
Assert.equal(c.email[0].type, "other", "Email type should match");
Assert.equal(c.email[0].value, "john.smith@example.com", "Email should match");
Assert.equal(c.email[0].pref, true, "Pref should match");
Assert.equal(c.category[0], "google", "Category should match");
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/0", "UID should match and be scoped to provider");
- c = mockDb._store[mockDb._next_guid - 4];
+ c = mockDb._store[mockDb._next_guid - 5];
Assert.equal(c.name[0], "Jane Smith", "Full name should match");
Assert.equal(c.givenName[0], "Jane", "Given name should match");
Assert.equal(c.familyName[0], "Smith", "Family name should match");
Assert.equal(c.email[0].type, "other", "Email type should match");
Assert.equal(c.email[0].value, "jane.smith@example.com", "Email should match");
Assert.equal(c.email[0].pref, true, "Pref should match");
Assert.equal(c.category[0], "google", "Category should match");
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/1", "UID should match and be scoped to provider");
- c = mockDb._store[mockDb._next_guid - 3];
+ c = mockDb._store[mockDb._next_guid - 4];
Assert.equal(c.name[0], "Davy Randall Jones", "Full name should match");
Assert.equal(c.givenName[0], "Davy Randall", "Given name should match");
Assert.equal(c.familyName[0], "Jones", "Family name should match");
Assert.equal(c.email[0].type, "other", "Email type should match");
Assert.equal(c.email[0].value, "davy.jones@example.com", "Email should match");
Assert.equal(c.email[0].pref, true, "Pref should match");
Assert.equal(c.category[0], "google", "Category should match");
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/2", "UID should match and be scoped to provider");
- c = mockDb._store[mockDb._next_guid - 2];
+ c = mockDb._store[mockDb._next_guid - 3];
Assert.equal(c.name[0], "noname@example.com", "Full name should match");
Assert.equal(c.email[0].type, "other", "Email type should match");
Assert.equal(c.email[0].value, "noname@example.com", "Email should match");
Assert.equal(c.email[0].pref, true, "Pref should match");
Assert.equal(c.category[0], "google", "Category should match");
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/3", "UID should match and be scoped to provider");
- c = mockDb._store[mockDb._next_guid - 1];
+ c = mockDb._store[mockDb._next_guid - 2];
Assert.equal(c.name[0], "lycnix", "Full name should match");
Assert.equal(c.email[0].type, "other", "Email type should match");
Assert.equal(c.email[0].value, "lycnix", "Email should match");
Assert.equal(c.email[0].pref, true, "Pref should match");
Assert.equal(c.category[0], "google", "Category should match");
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/7", "UID should match and be scoped to provider");
+
+ c = mockDb._store[mockDb._next_guid - 1];
+ Assert.equal(c.name[0], "+31-6-12345678", "Full name should match");
+ Assert.equal(c.tel[0].type, "mobile", "Email type should match");
+ Assert.equal(c.tel[0].value, "+31-6-12345678", "Email should match");
+ Assert.equal(c.tel[0].pref, false, "Pref should match");
+ Assert.equal(c.category[0], "google", "Category should match");
+ Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/8", "UID should match and be scoped to provider");
});
--- a/browser/components/loop/test/mochitest/fixtures/google_contacts.txt
+++ b/browser/components/loop/test/mochitest/fixtures/google_contacts.txt
@@ -86,9 +86,20 @@
<app:edited xmlns:app="http://www.w3.org/2007/app">2007-08-01T05:45:52.203Z</app:edited>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact"/>
<title/>
<link href="https://www.google.com/m8/feeds/photos/media/tester%40mochi.com/7" rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*"/>
<link href="https://www.google.com/m8/feeds/contacts/tester%40mochi.com/full/7" rel="self" type="application/atom+xml"/>
<link href="https://www.google.com/m8/feeds/contacts/tester%40mochi.com/full/7" rel="edit" type="application/atom+xml"/>
<gd:email address="lycnix" primary="true" rel="http://schemas.google.com/g/2005#other"/>
</entry>
+ <entry gd:etag=""RXkzfjVSLit7I2A9XRdRGUgITgA."">
+ <id>http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/8</id>
+ <updated>2014-10-10T14:55:44.786Z</updated>
+ <app:edited xmlns:app="http://www.w3.org/2007/app">2014-10-10T14:55:44.786Z</app:edited>
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact"/>
+ <title/>
+ <link href="https://www.google.com/m8/feeds/photos/media/tester%40mochi.com/8" rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*"/>
+ <link href="https://www.google.com/m8/feeds/contacts/tester%40mochi.com/full/8" rel="self" type="application/atom+xml"/>
+ <link href="https://www.google.com/m8/feeds/contacts/tester%40mochi.com/full/8" rel="edit" type="application/atom+xml"/>
+ <gd:phoneNumber rel="http://schemas.google.com/g/2005#mobile" uri="tel:+31-6-12345678">0612345678</gd:phoneNumber>
+ </entry>
</feed>
--- a/browser/components/loop/test/shared/conversationStore_test.js
+++ b/browser/components/loop/test/shared/conversationStore_test.js
@@ -1,14 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
var expect = chai.expect;
-describe("loop.ConversationStore", function () {
+describe("loop.store.ConversationStore", function () {
"use strict";
var CALL_STATES = loop.store.CALL_STATES;
var WS_STATES = loop.store.WS_STATES;
var sharedActions = loop.shared.actions;
var sharedUtils = loop.shared.utils;
var sandbox, dispatcher, client, store, fakeSessionData, sdkDriver;
var contact;
--- a/browser/components/loop/test/shared/index.html
+++ b/browser/components/loop/test/shared/index.html
@@ -38,28 +38,30 @@
<script src="../../content/shared/js/mixins.js"></script>
<script src="../../content/shared/js/views.js"></script>
<script src="../../content/shared/js/websocket.js"></script>
<script src="../../content/shared/js/feedbackApiClient.js"></script>
<script src="../../content/shared/js/validate.js"></script>
<script src="../../content/shared/js/actions.js"></script>
<script src="../../content/shared/js/dispatcher.js"></script>
<script src="../../content/shared/js/otSdkDriver.js"></script>
+ <script src="../../content/shared/js/localRoomStore.js"></script>
<script src="../../content/shared/js/conversationStore.js"></script>
<script src="../../content/shared/js/roomListStore.js"></script>
<!-- Test scripts -->
<script src="models_test.js"></script>
<script src="mixins_test.js"></script>
<script src="utils_test.js"></script>
<script src="views_test.js"></script>
<script src="websocket_test.js"></script>
<script src="feedbackApiClient_test.js"></script>
<script src="validate_test.js"></script>
<script src="dispatcher_test.js"></script>
+ <script src="localRoomStore_test.js"></script>
<script src="conversationStore_test.js"></script>
<script src="otSdkDriver_test.js"></script>
<script src="roomListStore_test.js"></script>
<script>
mocha.run(function () {
$("#mocha").append("<p id='complete'>Complete.</p>");
});
</script>
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/shared/localRoomStore_test.js
@@ -0,0 +1,110 @@
+/* global chai */
+
+var expect = chai.expect;
+var sharedActions = loop.shared.actions;
+
+describe("loop.store.LocalRoomStore", function () {
+ "use strict";
+
+ var sandbox, dispatcher;
+
+ beforeEach(function() {
+ sandbox = sinon.sandbox.create();
+ dispatcher = new loop.Dispatcher();
+ });
+
+ afterEach(function() {
+ sandbox.restore();
+ });
+
+ describe("#constructor", function() {
+ it("should throw an error if the dispatcher is missing", function() {
+ expect(function() {
+ new loop.store.LocalRoomStore({mozLoop: {}});
+ }).to.Throw(/dispatcher/);
+ });
+
+ it("should throw an error if mozLoop is missing", function() {
+ expect(function() {
+ new loop.store.LocalRoomStore({dispatcher: dispatcher});
+ }).to.Throw(/mozLoop/);
+ });
+ });
+
+ describe("#setupEmptyRoom", function() {
+ var store, fakeMozLoop, fakeRoomId, fakeRoomName;
+
+ beforeEach(function() {
+ fakeRoomId = "337-ff-54";
+ fakeRoomName = "Monkeys";
+ fakeMozLoop = {
+ rooms: { getRoomData: sandbox.stub() }
+ };
+
+ store = new loop.store.LocalRoomStore(
+ {mozLoop: fakeMozLoop, dispatcher: dispatcher});
+ fakeMozLoop.rooms.getRoomData.
+ withArgs(fakeRoomId).
+ callsArgOnWith(1, // index of callback argument
+ store, // |this| to call it on
+ null, // args to call the callback with...
+ {roomName: fakeRoomName}
+ );
+ });
+
+ it("should trigger a change event", function(done) {
+ store.on("change", function() {
+ done();
+ });
+
+ dispatcher.dispatch(new sharedActions.SetupEmptyRoom(
+ {localRoomId: fakeRoomId}));
+ });
+
+ it("should set localRoomId on the store from the action data",
+ function(done) {
+
+ store.once("change", function () {
+ expect(store.getStoreState()).
+ to.have.property('localRoomId', fakeRoomId);
+ done();
+ });
+
+ dispatcher.dispatch(
+ new sharedActions.SetupEmptyRoom({localRoomId: fakeRoomId}));
+ });
+
+ it("should set serverData.roomName from the getRoomData callback",
+ function(done) {
+
+ store.once("change", function () {
+ expect(store.getStoreState()).to.have.deep.property(
+ 'serverData.roomName', fakeRoomName);
+ done();
+ });
+
+ dispatcher.dispatch(
+ new sharedActions.SetupEmptyRoom({localRoomId: fakeRoomId}));
+ });
+
+ it("should set error on the store when getRoomData calls back an error",
+ function(done) {
+
+ var fakeError = new Error("fake error");
+ fakeMozLoop.rooms.getRoomData.
+ withArgs(fakeRoomId).
+ callsArgOnWith(1, // index of callback argument
+ store, // |this| to call it on
+ fakeError); // args to call the callback with...
+
+ store.once("change", function() {
+ expect(this.getStoreState()).to.have.property('error', fakeError);
+ done();
+ });
+
+ dispatcher.dispatch(
+ new sharedActions.SetupEmptyRoom({localRoomId: fakeRoomId}));
+ });
+
+ });
+});
--- a/browser/components/loop/test/shared/mixins_test.js
+++ b/browser/components/loop/test/shared/mixins_test.js
@@ -33,16 +33,20 @@ describe("loop.shared.mixins", function(
onDocumentHidden: onDocumentHiddenStub,
onDocumentVisible: onDocumentVisibleStub,
render: function() {
return React.DOM.div();
}
});
});
+ afterEach(function() {
+ loop.shared.mixins.setRootObject(window);
+ });
+
function setupFakeVisibilityEventDispatcher(event) {
loop.shared.mixins.setRootObject({
document: {
addEventListener: function(_, fn) {
fn(event);
},
removeEventListener: sandbox.stub()
}
--- a/browser/components/loop/test/xpcshell/head.js
+++ b/browser/components/loop/test/xpcshell/head.js
@@ -3,16 +3,17 @@
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Http.jsm");
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource:///modules/loop/MozLoopService.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
const { MozLoopServiceInternal } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
"resource:///modules/loop/MozLoopPushHandler.jsm");
const kMockWebSocketChannelName = "Mock WebSocket Channel";
const kWebSocketChannelContractID = "@mozilla.org/network/protocol;1?name=wss";
--- a/browser/components/loop/ui/index.html
+++ b/browser/components/loop/ui/index.html
@@ -37,16 +37,17 @@
<script src="../content/shared/js/models.js"></script>
<script src="../content/shared/js/mixins.js"></script>
<script src="../content/shared/js/views.js"></script>
<script src="../content/shared/js/websocket.js"></script>
<script src="../content/shared/js/validate.js"></script>
<script src="../content/shared/js/dispatcher.js"></script>
<script src="../content/shared/js/conversationStore.js"></script>
<script src="../content/shared/js/roomListStore.js"></script>
+ <script src="../content/js/roomViews.js"></script>
<script src="../content/js/conversationViews.js"></script>
<script src="../content/js/client.js"></script>
<script src="../standalone/content/js/webapp.js"></script>
<script type="text/javascript;version=1.8" src="../content/js/contacts.js"></script>
<script>
if (!loop.contacts) {
// For browsers that don't support ES6 without special flags (all but Fx
// at the moment), we shim the contacts namespace with its most barebone
--- a/browser/components/places/content/sidebarUtils.js
+++ b/browser/components/places/content/sidebarUtils.js
@@ -5,66 +5,63 @@
var SidebarUtils = {
handleTreeClick: function SU_handleTreeClick(aTree, aEvent, aGutterSelect) {
// right-clicks are not handled here
if (aEvent.button == 2)
return;
var tbo = aTree.treeBoxObject;
- var row = { }, col = { }, obj = { };
- tbo.getCellAt(aEvent.clientX, aEvent.clientY, row, col, obj);
+ var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);
- if (row.value == -1 || obj.value == "twisty")
+ if (cell.row == -1 || cell.childElt == "twisty")
return;
var mouseInGutter = false;
if (aGutterSelect) {
- var x = { }, y = { }, w = { }, h = { };
- tbo.getCoordsForCellItem(row.value, col.value, "image",
- x, y, w, h);
+ var rect = tbo.getCoordsForCellItem(cell.row, cell.col, "image");
// getCoordsForCellItem returns the x coordinate in logical coordinates
// (i.e., starting from the left and right sides in LTR and RTL modes,
// respectively.) Therefore, we make sure to exclude the blank area
// before the tree item icon (that is, to the left or right of it in
// LTR and RTL modes, respectively) from the click target area.
var isRTL = window.getComputedStyle(aTree, null).direction == "rtl";
if (isRTL)
- mouseInGutter = aEvent.clientX > x.value;
+ mouseInGutter = aEvent.clientX > rect.x;
else
- mouseInGutter = aEvent.clientX < x.value;
+ mouseInGutter = aEvent.clientX < rect.x;
}
#ifdef XP_MACOSX
var modifKey = aEvent.metaKey || aEvent.shiftKey;
#else
var modifKey = aEvent.ctrlKey || aEvent.shiftKey;
#endif
- var isContainer = tbo.view.isContainer(row.value);
+ var isContainer = tbo.view.isContainer(cell.row);
var openInTabs = isContainer &&
(aEvent.button == 1 ||
(aEvent.button == 0 && modifKey)) &&
- PlacesUtils.hasChildURIs(tbo.view.nodeForTreeIndex(row.value), true);
+ PlacesUtils.hasChildURIs(tbo.view.nodeForTreeIndex(cell.row), true);
if (aEvent.button == 0 && isContainer && !openInTabs) {
- tbo.view.toggleOpenState(row.value);
+ tbo.view.toggleOpenState(cell.row);
return;
}
else if (!mouseInGutter && openInTabs &&
aEvent.originalTarget.localName == "treechildren") {
- tbo.view.selection.select(row.value);
+ tbo.view.selection.select(cell.row);
PlacesUIUtils.openContainerNodeInTabs(aTree.selectedNode, aEvent, aTree);
}
else if (!mouseInGutter && !isContainer &&
aEvent.originalTarget.localName == "treechildren") {
// Clear all other selection since we're loading a link now. We must
// do this *before* attempting to load the link since openURL uses
// selection as an indication of which link to load.
- tbo.view.selection.select(row.value);
+ tbo.view.selection.select(cell.row);
PlacesUIUtils.openNodeWithEvent(aTree.selectedNode, aEvent, aTree);
}
},
handleTreeKeyPress: function SU_handleTreeKeyPress(aEvent) {
// XXX Bug 627901: Post Fx4, this method should take a tree parameter.
let tree = aEvent.target;
let node = tree.selectedNode;
@@ -79,24 +76,23 @@ var SidebarUtils = {
* hovered over.
*/
handleTreeMouseMove: function SU_handleTreeMouseMove(aEvent) {
if (aEvent.target.localName != "treechildren")
return;
var tree = aEvent.target.parentNode;
var tbo = tree.treeBoxObject;
- var row = { }, col = { }, obj = { };
- tbo.getCellAt(aEvent.clientX, aEvent.clientY, row, col, obj);
+ var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);
- // row.value is -1 when the mouse is hovering an empty area within the tree.
+ // cell.row is -1 when the mouse is hovering an empty area within the tree.
// To avoid showing a URL from a previously hovered node for a currently
// hovered non-url node, we must clear the moused-over URL in these cases.
- if (row.value != -1) {
- var node = tree.view.nodeForTreeIndex(row.value);
+ if (cell.row != -1) {
+ var node = tree.view.nodeForTreeIndex(cell.row);
if (PlacesUtils.nodeIsURI(node))
this.setMouseoverURL(node.uri);
else
this.setMouseoverURL("");
}
else
this.setMouseoverURL("");
},
--- a/browser/components/places/content/tree.xml
+++ b/browser/components/places/content/tree.xml
@@ -742,52 +742,50 @@
this._controller.setDataTransfer(event);
event.stopPropagation();
]]></handler>
<handler event="dragover"><![CDATA[
if (event.target.localName != "treechildren")
return;
- let row = { }, col = { }, child = { };
- this.treeBoxObject.getCellAt(event.clientX, event.clientY,
- row, col, child);
- let node = row.value != -1 ?
- this.view.nodeForTreeIndex(row.value) :
+ let cell = this.treeBoxObject.getCellAt(event.clientX, event.clientY);
+ let node = cell.row != -1 ?
+ this.view.nodeForTreeIndex(cell.row) :
this.result.root;
// cache the dropTarget for the view
PlacesControllerDragHelper.currentDropTarget = node;
// We have to calculate the orientation since view.canDrop will use
// it and we want to be consistent with the dropfeedback.
let tbo = this.treeBoxObject;
let rowHeight = tbo.rowHeight;
let eventY = event.clientY - tbo.treeBody.boxObject.y -
- rowHeight * (row.value - tbo.getFirstVisibleRow());
+ rowHeight * (cell.row - tbo.getFirstVisibleRow());
let orientation = Ci.nsITreeView.DROP_BEFORE;
- if (row.value == -1) {
+ if (cell.row == -1) {
// If the row is not valid we try to insert inside the resultNode.
orientation = Ci.nsITreeView.DROP_ON;
}
else if (PlacesUtils.nodeIsContainer(node) &&
eventY > rowHeight * 0.75) {
// If we are below the 75% of a container the treeview we try
// to drop after the node.
orientation = Ci.nsITreeView.DROP_AFTER;
}
else if (PlacesUtils.nodeIsContainer(node) &&
eventY > rowHeight * 0.25) {
// If we are below the 25% of a container the treeview we try
// to drop inside the node.
orientation = Ci.nsITreeView.DROP_ON;
}
- if (!this.view.canDrop(row.value, orientation, event.dataTransfer))
+ if (!this.view.canDrop(cell.row, orientation, event.dataTransfer))
return;
event.preventDefault();
event.stopPropagation();
]]></handler>
<handler event="dragend"><![CDATA[
PlacesControllerDragHelper.currentDropTarget = null;
--- a/browser/components/places/tests/browser/browser_forgetthissite_single.js
+++ b/browser/components/places/tests/browser/browser_forgetthissite_single.js
@@ -57,17 +57,14 @@ function test() {
organizer.removeEventListener("unload", arguments.callee, false);
// Proceed
funcNext();
}, false);
// Close Library window.
organizer.close();
}, true);
// Get cell coordinates
- var x = {}, y = {}, width = {}, height = {};
- tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "text",
- x, y, width, height);
+ var rect = tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "text");
// Initiate a context menu for the selected cell
- EventUtils.synthesizeMouse(tree.body, x.value + width.value / 2, y.value + height.value / 2, {type: "contextmenu"}, organizer);
+ EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, {type: "contextmenu"}, organizer);
});
}
}
-
--- a/browser/components/places/tests/browser/browser_library_infoBox.js
+++ b/browser/components/places/tests/browser/browser_library_infoBox.js
@@ -47,17 +47,17 @@ gTests.push({
isnot(childNode, null, "History node first child is not null.");
PO._places.selectNode(childNode);
checkInfoBoxSelected(PO);
ok(infoBoxExpanderWrapper.hidden,
"Expander button is hidden for history child node.");
checkAddInfoFieldsCollapsed(PO);
// open history item
- var view = ContentTree.view.treeBoxObject.view;
+ var view = ContentTree.view.view;
ok(view.rowCount > 0, "History item exists.");
view.selection.select(0);
ok(infoBoxExpanderWrapper.hidden,
"Expander button is hidden for history item.");
checkAddInfoFieldsCollapsed(PO);
historyNode.containerOpen = false;
@@ -84,17 +84,17 @@ gTests.push({
"Correctly selected recently bookmarked node.");
PO._places.selectNode(childNode);
checkInfoBoxSelected(PO);
ok(!infoBoxExpanderWrapper.hidden,
"Expander button is not hidden for recently bookmarked node.");
checkAddInfoFieldsNotCollapsed(PO);
// open first bookmark
- var view = ContentTree.view.treeBoxObject.view;
+ var view = ContentTree.view.view;
ok(view.rowCount > 0, "Bookmark item exists.");
view.selection.select(0);
checkInfoBoxSelected(PO);
ok(!infoBoxExpanderWrapper.hidden,
"Expander button is not hidden for bookmark item.");
checkAddInfoFieldsNotCollapsed(PO);
checkAddInfoFields(PO, "bookmark item");
--- a/browser/components/places/tests/browser/browser_library_middleclick.js
+++ b/browser/components/places/tests/browser/browser_library_middleclick.js
@@ -267,15 +267,13 @@ function runNextTest() {
function mouseEventOnCell(aTree, aRowIndex, aColumnIndex, aEventDetails) {
var selection = aTree.view.selection;
selection.select(aRowIndex);
aTree.treeBoxObject.ensureRowIsVisible(aRowIndex);
var column = aTree.columns[aColumnIndex];
// get cell coordinates
- var x = {}, y = {}, width = {}, height = {};
- aTree.treeBoxObject.getCoordsForCellItem(aRowIndex, column, "text",
- x, y, width, height);
+ var rect = aTree.treeBoxObject.getCoordsForCellItem(aRowIndex, column, "text");
- EventUtils.synthesizeMouse(aTree.body, x.value, y.value,
+ EventUtils.synthesizeMouse(aTree.body, rect.x, rect.y,
aEventDetails, gLibrary);
}
--- a/browser/components/places/tests/browser/head.js
+++ b/browser/components/places/tests/browser/head.js
@@ -191,17 +191,15 @@ function synthesizeClickOnSelectedTreeCe
if (tbo.view.selection.count != 1)
throw new Error("The test node should be successfully selected");
// Get selection rowID.
let min = {}, max = {};
tbo.view.selection.getRangeAt(0, min, max);
let rowID = min.value;
tbo.ensureRowIsVisible(rowID);
// Calculate the click coordinates.
- let x = {}, y = {}, width = {}, height = {};
- tbo.getCoordsForCellItem(rowID, aTree.columns[0], "text",
- x, y, width, height);
- x = x.value + width.value / 2;
- y = y.value + height.value / 2;
+ var rect = tbo.getCoordsForCellItem(rowID, aTree.columns[0], "text");
+ var x = rect.x + rect.width / 2;
+ var y = rect.y + rect.height / 2;
// Simulate the click.
EventUtils.synthesizeMouse(aTree.body, x, y, aOptions || {},
aTree.ownerDocument.defaultView);
}
--- a/browser/components/preferences/cookies.js
+++ b/browser/components/preferences/cookies.js
@@ -33,17 +33,17 @@ var gCookiesWindow = {
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.removeObserver(this, "cookie-changed");
os.removeObserver(this, "perm-changed");
},
_populateList: function (aInitialLoad) {
this._loadCookies();
- this._tree.treeBoxObject.view = this._view;
+ this._tree.view = this._view;
if (aInitialLoad)
this.sort("rawHost");
if (this._view.rowCount > 0)
this._tree.view.selection.select(0);
if (aInitialLoad) {
if ("arguments" in window &&
window.arguments[0] &&
@@ -785,17 +785,17 @@ var gCookiesWindow = {
// Clear the Tree Display
this._view._filtered = false;
this._view._rowCount = 0;
this._tree.treeBoxObject.rowCountChanged(0, -this._view._filterSet.length);
this._view._filterSet = [];
// Just reload the list to make sure deletions are respected
this._loadCookies();
- this._tree.treeBoxObject.view = this._view;
+ this._tree.view = this._view;
// Restore sort order
var sortby = this._lastSortProperty;
if (sortby == "") {
this._lastSortAscending = false;
this.sort("rawHost");
}
else {
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -327,17 +327,17 @@ var gPermissionManager = {
while (enumerator.hasMoreElements()) {
var nextPermission = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
this._addPermissionToList(nextPermission);
}
this._view._rowCount = this._permissions.length;
// sort and display the table
- this._tree.treeBoxObject.view = this._view;
+ this._tree.view = this._view;
this.onPermissionSort("rawHost", false);
// disable "remove all" button if there are none
document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
},
_addPermissionToList: function (aPermission)
{
@@ -365,9 +365,8 @@ function setHost(aHost)
{
gPermissionManager.setHost(aHost);
}
function initWithParams(aParams)
{
gPermissionManager.init(aParams);
}
-
--- a/browser/components/preferences/tests/browser_bug705422.js
+++ b/browser/components/preferences/tests/browser_bug705422.js
@@ -77,34 +77,33 @@ function runTest(win, searchTerm, cookie
// "delete all cookies" should be enabled
isDisabled(win, false);
// select first cookie and delete
var tree = win.document.getElementById("cookiesList");
var deleteButton = win.document.getElementById("removeCookie");
- var x = {}, y = {}, width = {}, height = {};
- tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "cell", x, y, width, height);
- EventUtils.synthesizeMouse(tree.body, x.value + width.value / 2, y.value + height.value / 2, {}, win);
+ var rect = tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "cell");
+ EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, {}, win);
EventUtils.synthesizeMouseAtCenter(deleteButton, {}, win);
// count cookies should be matches-1
is(win.gCookiesWindow._view.rowCount, matches-1, "Deleted selected cookie");
// select two adjacent cells and delete
- EventUtils.synthesizeMouse(tree.body, x.value + width.value / 2, y.value + height.value / 2, {}, win);
+ EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, {}, win);
deleteButton = win.document.getElementById("removeCookies");
var eventObj = {};
if (navigator.platform.indexOf("Mac") >= 0)
eventObj.metaKey = true;
else
eventObj.ctrlKey = true;
- tree.treeBoxObject.getCoordsForCellItem(1, tree.columns[0], "cell", x, y, width, height);
- EventUtils.synthesizeMouse(tree.body, x.value + width.value / 2, y.value + height.value / 2, eventObj, win);
+ rect = tree.treeBoxObject.getCoordsForCellItem(1, tree.columns[0], "cell");
+ EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, eventObj, win);
EventUtils.synthesizeMouseAtCenter(deleteButton, {}, win);
// count cookies should be matches-3
is(win.gCookiesWindow._view.rowCount, matches-3, "Deleted selected two adjacent cookies");
// "delete all cookies" should be enabled
isDisabled(win, false);
--- a/browser/components/preferences/translation.js
+++ b/browser/components/preferences/translation.js
@@ -15,17 +15,17 @@ XPCOMUtils.defineLazyGetter(this, "gLang
const kPermissionType = "translate";
const kLanguagesPref = "browser.translation.neverForLanguages";
function Tree(aId, aData)
{
this._data = aData;
this._tree = document.getElementById(aId);
- this._tree.treeBoxObject.view = this;
+ this._tree.view = this;
}
Tree.prototype = {
get boxObject() this._tree.treeBoxObject,
get isEmpty() !this._data.length,
get hasSelection() this.selection.count > 0,
getSelectedItems: function() {
let result = [];
--- a/browser/components/search/test/browser_405664.js
+++ b/browser/components/search/test/browser_405664.js
@@ -1,16 +1,16 @@
function test() {
var searchBar = BrowserSearch.searchBar;
ok(searchBar, "got search bar");
searchBar.focus();
var pbo = searchBar._popup.popupBoxObject;
- ok(pbo, "popup is nsIPopupBoxObject");
+ ok(pbo, "popup is PopupBoxObject");
EventUtils.synthesizeKey("VK_UP", { altKey: true });
is(pbo.popupState, "showing", "popup is opening after Alt+Up");
EventUtils.synthesizeKey("VK_ESCAPE", {});
is(pbo.popupState, "closed", "popup is closed after ESC");
EventUtils.synthesizeKey("VK_DOWN", { altKey: true });
--- a/browser/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -139,34 +139,33 @@ function startNewSession() {
getBrowserWindow().BrowserHome();
}
function onListClick(aEvent) {
// don't react to right-clicks
if (aEvent.button == 2)
return;
- var row = {}, col = {};
- treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY, row, col, {});
- if (col.value) {
+ var cell = treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY);
+ if (cell.col) {
// Restore this specific tab in the same window for middle/double/accel clicking
// on a tab's title.
#ifdef XP_MACOSX
let accelKey = aEvent.metaKey;
#else
let accelKey = aEvent.ctrlKey;
#endif
if ((aEvent.button == 1 || aEvent.button == 0 && aEvent.detail == 2 || accelKey) &&
- col.value.id == "title" &&
- !treeView.isContainer(row.value)) {
- restoreSingleTab(row.value, aEvent.shiftKey);
+ cell.col.id == "title" &&
+ !treeView.isContainer(cell.row)) {
+ restoreSingleTab(cell.row, aEvent.shiftKey);
aEvent.stopPropagation();
}
- else if (col.value.id == "restore")
- toggleRowChecked(row.value);
+ else if (cell.col.id == "restore")
+ toggleRowChecked(cell.row);
}
}
function onListKeyDown(aEvent) {
switch (aEvent.keyCode)
{
case KeyEvent.DOM_VK_SPACE:
toggleRowChecked(document.getElementById("tabList").currentIndex);
--- a/browser/components/sessionstore/test/browser_590563.js
+++ b/browser/components/sessionstore/test/browser_590563.js
@@ -33,25 +33,23 @@ function test() {
});
}
function middleClickTest(win) {
let browser = win.gBrowser.selectedBrowser;
let tree = browser.contentDocument.getElementById("tabList");
is(tree.view.rowCount, 3, "There should be three items");
- let x = {}, y = {}, width = {}, height = {};
-
// click on the first tab item
- tree.treeBoxObject.getCoordsForCellItem(1, tree.columns[1], "text", x, y, width, height);
- EventUtils.synthesizeMouse(tree.body, x.value, y.value, { button: 1 },
+ var rect = tree.treeBoxObject.getCoordsForCellItem(1, tree.columns[1], "text");
+ EventUtils.synthesizeMouse(tree.body, rect.x, rect.y, { button: 1 },
browser.contentWindow);
// click on the second tab item
- tree.treeBoxObject.getCoordsForCellItem(2, tree.columns[1], "text", x, y, width, height);
- EventUtils.synthesizeMouse(tree.body, x.value, y.value, { button: 1 },
+ rect = tree.treeBoxObject.getCoordsForCellItem(2, tree.columns[1], "text");
+ EventUtils.synthesizeMouse(tree.body, rect.x, rect.y, { button: 1 },
browser.contentWindow);
is(win.gBrowser.tabs.length, 3,
"The total number of tabs should be 3 after restoring 2 tabs by middle click.");
is(win.gBrowser.visibleTabs.length, 3,
"The total number of visible tabs should be 3 after restoring 2 tabs by middle click");
}
--- a/browser/devtools/scratchpad/test/browser_scratchpad_edit_ui_updates.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_edit_ui_updates.js
@@ -1,21 +1,23 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* Bug 699130 */
"use strict";
let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
+let DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
+ Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, false);
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,test Edit menu updates Scratchpad - bug 699130";
}
@@ -170,16 +172,17 @@ function runTests()
closeMenu(hideAfterPaste);
};
let hideAfterPaste = function() {
if (pass == 0) {
pass++;
testContextMenu();
} else {
+ Services.prefs.clearUserPref(DEVTOOLS_CHROME_ENABLED);
finish();
}
};
let testContextMenu = function() {
info("will test the context menu");
editMenu = null;
--- a/browser/devtools/scratchpad/test/browser_scratchpad_initialization.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_initialization.js
@@ -1,16 +1,19 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
+let DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
+
function test()
{
waitForExplicitFinish();
+ Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, false);
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
ok(window.Scratchpad, "Scratchpad variable exists");
openScratchpad(runTests);
}, true);
@@ -39,10 +42,11 @@ function runTests()
"Error console command is disabled");
let chromeContextCommand = gScratchpadWindow.document.
getElementById("sp-cmd-browserContext");
ok(chromeContextCommand, "Chrome context command element exists");
is(chromeContextCommand.getAttribute("disabled"), "true",
"Chrome context command is disabled");
+ Services.prefs.clearUserPref(DEVTOOLS_CHROME_ENABLED);
finish();
}
--- a/browser/devtools/scratchpad/test/browser_scratchpad_modeline.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_modeline.js
@@ -15,16 +15,17 @@ let gFile; // Reference to the temporary
let DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
// The temporary file content.
let gFileContent = "function main() { return 0; }";
function test() {
waitForExplicitFinish();
+ Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, false);
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,<p>test file open and save in Scratchpad";
}
--- a/browser/devtools/shared/widgets/FastListWidget.js
+++ b/browser/devtools/shared/widgets/FastListWidget.js
@@ -190,17 +190,17 @@ FastListWidget.prototype = {
* The element to make visible.
*/
ensureElementIsVisible: function(element) {
if (!element) {
return;
}
// Ensure the element is visible but not scrolled horizontally.
- let boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
+ let boxObject = this._list.boxObject;
boxObject.ensureElementIsVisible(element);
boxObject.scrollBy(-this._list.clientWidth, 0);
},
/**
* Sets the text displayed in this container when empty.
* @param string aValue
*/
--- a/browser/devtools/shared/widgets/SideMenuWidget.jsm
+++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm
@@ -209,17 +209,17 @@ SideMenuWidget.prototype = {
* The element to make visible.
*/
ensureElementIsVisible: function(aElement) {
if (!aElement) {
return;
}
// Ensure the element is visible but not scrolled horizontally.
- let boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
+ let boxObject = this._list.boxObject;
boxObject.ensureElementIsVisible(aElement);
boxObject.scrollBy(-this._list.clientWidth, 0);
},
/**
* Shows all the groups, even the ones with no visible children.
*/
showEmptyGroups: function() {
--- a/browser/devtools/shared/widgets/SimpleListWidget.jsm
+++ b/browser/devtools/shared/widgets/SimpleListWidget.jsm
@@ -166,17 +166,17 @@ SimpleListWidget.prototype = {
* The element to make visible.
*/
ensureElementIsVisible: function(aElement) {
if (!aElement) {
return;
}
// Ensure the element is visible but not scrolled horizontally.
- let boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
+ let boxObject = this._list.boxObject;
boxObject.ensureElementIsVisible(aElement);
boxObject.scrollBy(-this._list.clientWidth, 0);
},
/**
* Sets the text displayed permanently in this container as a header.
* @param string aValue
*/
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -982,17 +982,17 @@ VariablesView.prototype = {
this._parent.removeAttribute("actions-first");
}
},
/**
* Gets the parent node holding this view.
* @return nsIDOMNode
*/
- get boxObject() this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject),
+ get boxObject() this._list.boxObject,
/**
* Gets the parent node holding this view.
* @return nsIDOMNode
*/
get parentNode() this._parent,
/**
--- a/browser/metro/base/content/bindings/bindings.xml
+++ b/browser/metro/base/content/bindings/bindings.xml
@@ -20,24 +20,21 @@
<handlers>
<handler event="scroll">
<![CDATA[
// if there no more items to insert, just return early
if (this._items.length == 0)
return;
if (this._contentScrollHeight == -1) {
- let scrollheight = {};
- this.scrollBoxObject.getScrolledSize({}, scrollheight);
- this._contentScrollHeight = scrollheight.value;
+ this._contentScrollHeight = this.scrollBoxObject.scrolledHeight;
}
- let y = {};
- this.scrollBoxObject.getPosition({}, y);
- let scrollRatio = (y.value + this._childrenHeight) / this._contentScrollHeight;
+ let y = this.scrollBoxObject.positionY;
+ let scrollRatio = (y + this._childrenHeight) / this._contentScrollHeight;
// If we're scrolled 80% to the bottom of the list, append the next
// set of items
if (scrollRatio > 0.8)
this._insertItems();
]]>
</handler>
</handlers>
--- a/browser/metro/base/content/input.js
+++ b/browser/metro/base/content/input.js
@@ -470,27 +470,27 @@ var ScrollUtils = {
// if element is content or the startui page, get the browser scroll interface
if (elem.ownerDocument == Browser.selectedBrowser.contentDocument) {
elem = Browser.selectedBrowser;
}
for (; elem; elem = elem.parentNode) {
try {
if (elem.anonScrollBox) {
scrollbox = elem.anonScrollBox;
- qinterface = scrollbox.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
+ qinterface = scrollbox.boxObject;
} else if (elem.scrollBoxObject) {
scrollbox = elem;
qinterface = elem.scrollBoxObject;
break;
} else if (elem.customDragger) {
scrollbox = elem;
break;
} else if (elem.boxObject) {
let qi = (elem._cachedSBO) ? elem._cachedSBO
- : elem.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
+ : elem.boxObject;
if (qi) {
scrollbox = elem;
scrollbox._cachedSBO = qinterface = qi;
break;
}
}
} catch (e) { /* we aren't here to deal with your exceptions, we'll just keep
traversing until we find something more well-behaved, as we
@@ -507,50 +507,43 @@ var ScrollUtils = {
/**
* The default dragger object used by TouchModule when dragging a scrollable
* element that provides no customDragger. Simply performs the expected
* regular scrollBy calls on the scroller.
*/
_defaultDragger: {
isDraggable: function isDraggable(target, scroller) {
- let sX = {}, sY = {},
- pX = {}, pY = {};
- scroller.getPosition(pX, pY);
- scroller.getScrolledSize(sX, sY);
let rect = target.getBoundingClientRect();
- return { x: (sX.value > rect.width || pX.value != 0),
- y: (sY.value > rect.height || pY.value != 0) };
+ return { x: (scroller.scrolledWidth > rect.width || scroller.positionX != 0),
+ y: (scroller.scrolledHeight > rect.height || scroller.positionY != 0) };
},
dragStart: function dragStart(cx, cy, target, scroller) {
scroller.element.addEventListener("PanBegin", this._showScrollbars, false);
},
dragStop: function dragStop(dx, dy, scroller) {
scroller.element.removeEventListener("PanBegin", this._showScrollbars, false);
return this.dragMove(dx, dy, scroller);
},
dragMove: function dragMove(dx, dy, scroller) {
- if (scroller.getPosition) {
try {
- let oldX = {}, oldY = {};
- scroller.getPosition(oldX, oldY);
+ let oldX = scroller.positionX,
+ oldY = scroller.positionY;
scroller.scrollBy(dx, dy);
- let newX = {}, newY = {};
- scroller.getPosition(newX, newY);
+ let newX = scroller.positionX,
+ newY = scroller.positionY;
- return (newX.value != oldX.value) || (newY.value != oldY.value);
+ return (newX != oldX) || (newY != oldY);
} catch (e) { /* we have no time for whiny scrollers! */ }
- }
-
return false;
},
_showScrollbars: function _showScrollbars(aEvent) {
let scrollbox = aEvent.target;
scrollbox.setAttribute("panning", "true");
let hideScrollbars = function() {
--- a/browser/modules/UITour.jsm
+++ b/browser/modules/UITour.jsm
@@ -6,16 +6,17 @@
this.EXPORTED_SYMBOLS = ["UITour"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
"resource://gre/modules/LightweightThemeManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ResetProfile",
"resource://gre/modules/ResetProfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
@@ -34,16 +35,19 @@ const BUCKET_TIMESTEPS = [
3 * 60 * 1000, // Until 3 minutes after tab is closed/inactive.
10 * 60 * 1000, // Until 10 minutes after tab is closed/inactive.
60 * 60 * 1000, // Until 1 hour after tab is closed/inactive.
];
// Time after which seen Page IDs expire.
const SEENPAGEID_EXPIRY = 8 * 7 * 24 * 60 * 60 * 1000; // 8 weeks.
+// Prefix for any target matching a search engine.
+const TARGET_SEARCHENGINE_PREFIX = "searchEngine-";
+
this.UITour = {
url: null,
seenPageIDs: null,
pageIDSourceTabs: new WeakMap(),
pageIDSourceWindows: new WeakMap(),
/* Map from browser windows to a set of tabs in which a tour is open */
originTabs: new WeakMap(),
@@ -370,17 +374,20 @@ this.UITour = {
}
case "removePinnedTab": {
this.removePinnedTab(window);
break;
}
case "showMenu": {
- this.showMenu(window, data.name);
+ this.showMenu(window, data.name, () => {
+ if (typeof data.showCallbackID == "string")
+ this.sendPageCallback(contentDocument, data.showCallbackID);
+ });
break;
}
case "hideMenu": {
this.hideMenu(window, data.name);
break;
}
@@ -680,16 +687,21 @@ this.UITour = {
if (aTargetName == "pinnedTab") {
deferred.resolve({
targetName: aTargetName,
node: this.ensurePinnedTab(aWindow, aSticky)
});
return deferred.promise;
}
+ if (aTargetName.startsWith(TARGET_SEARCHENGINE_PREFIX)) {
+ let engineID = aTargetName.slice(TARGET_SEARCHENGINE_PREFIX.length);
+ return this.getSearchEngineTarget(aWindow, engineID);
+ }
+
let targetObject = this.targets.get(aTargetName);
if (!targetObject) {
deferred.reject("The specified target name is not in the allowed set");
return deferred.promise;
}
let targetQuery = targetObject.query;
aWindow.PanelUI.ensureReady().then(() => {
@@ -812,35 +824,49 @@ this.UITour = {
},
/**
* @param aTarget The element to highlight.
* @param aEffect (optional) The effect to use from UITour.highlightEffects or "none".
* @see UITour.highlightEffects
*/
showHighlight: function(aTarget, aEffect = "none") {
- function showHighlightPanel(aTargetEl) {
- let highlighter = aTargetEl.ownerDocument.getElementById("UITourHighlight");
+ let window = aTarget.node.ownerDocument.defaultView;
+
+ function showHighlightPanel() {
+ if (aTarget.targetName.startsWith(TARGET_SEARCHENGINE_PREFIX)) {
+ // This won't affect normal higlights done via the panel, so we need to
+ // manually hide those.
+ this.hideHighlight(window);
+ aTarget.node.setAttribute("_moz-menuactive", true);
+ return;
+ }
+
+ // Conversely, highlights for search engines are highlighted via CSS
+ // rather than a panel, so need to be manually removed.
+ this._hideSearchEngineHighlight(window);
+
+ let highlighter = aTarget.node.ownerDocument.getElementById("UITourHighlight");
let effect = aEffect;
if (effect == "random") {
// Exclude "random" from the randomly selected effects.
let randomEffect = 1 + Math.floor(Math.random() * (this.highlightEffects.length - 1));
if (randomEffect == this.highlightEffects.length)
randomEffect--; // On the order of 1 in 2^62 chance of this happening.
effect = this.highlightEffects[randomEffect];
}
// Toggle the effect attribute to "none" and flush layout before setting it so the effect plays.
highlighter.setAttribute("active", "none");
- aTargetEl.ownerDocument.defaultView.getComputedStyle(highlighter).animationName;
+ aTarget.node.ownerDocument.defaultView.getComputedStyle(highlighter).animationName;
highlighter.setAttribute("active", effect);
highlighter.parentElement.setAttribute("targetName", aTarget.targetName);
highlighter.parentElement.hidden = false;
- let targetRect = aTargetEl.getBoundingClientRect();
+ let targetRect = aTarget.node.getBoundingClientRect();
let highlightHeight = targetRect.height;
let highlightWidth = targetRect.width;
let minDimension = Math.min(highlightHeight, highlightWidth);
let maxDimension = Math.max(highlightHeight, highlightWidth);
// If the dimensions are within 200% of each other (to include the bookmarks button),
// make the highlight a circle with the largest dimension as the diameter.
if (maxDimension / minDimension <= 3.0) {
@@ -854,52 +880,69 @@ this.UITour = {
highlighter.style.width = highlightWidth + "px";
// Close a previous highlight so we can relocate the panel.
if (highlighter.parentElement.state == "showing" || highlighter.parentElement.state == "open") {
highlighter.parentElement.hidePopup();
}
/* The "overlap" position anchors from the top-left but we want to centre highlights at their
minimum size. */
- let highlightWindow = aTargetEl.ownerDocument.defaultView;
+ let highlightWindow = aTarget.node.ownerDocument.defaultView;
let containerStyle = highlightWindow.getComputedStyle(highlighter.parentElement);
let paddingTopPx = 0 - parseFloat(containerStyle.paddingTop);
let paddingLeftPx = 0 - parseFloat(containerStyle.paddingLeft);
let highlightStyle = highlightWindow.getComputedStyle(highlighter);
let highlightHeightWithMin = Math.max(highlightHeight, parseFloat(highlightStyle.minHeight));
let highlightWidthWithMin = Math.max(highlightWidth, parseFloat(highlightStyle.minWidth));
let offsetX = paddingTopPx
- (Math.max(0, highlightWidthWithMin - targetRect.width) / 2);
let offsetY = paddingLeftPx
- (Math.max(0, highlightHeightWithMin - targetRect.height) / 2);
-
this._addAnnotationPanelMutationObserver(highlighter.parentElement);
- highlighter.parentElement.openPopup(aTargetEl, "overlap", offsetX, offsetY);
+ highlighter.parentElement.openPopup(aTarget.node, "overlap", offsetX, offsetY);
}
// Prevent showing a panel at an undefined position.
if (!this.isElementVisible(aTarget.node))
return;
this._setAppMenuStateForAnnotation(aTarget.node.ownerDocument.defaultView, "highlight",
this.targetIsInAppMenu(aTarget),
- showHighlightPanel.bind(this, aTarget.node));
+ showHighlightPanel.bind(this));
},
hideHighlight: function(aWindow) {
let tabData = this.pinnedTabs.get(aWindow);
if (tabData && !tabData.sticky)
this.removePinnedTab(aWindow);
let highlighter = aWindow.document.getElementById("UITourHighlight");
this._removeAnnotationPanelMutationObserver(highlighter.parentElement);
highlighter.parentElement.hidePopup();
highlighter.removeAttribute("active");
this._setAppMenuStateForAnnotation(aWindow, "highlight", false);
+ this._hideSearchEngineHighlight(aWindow);
+ },
+
+ _hideSearchEngineHighlight: function(aWindow) {
+ // We special case highlighting items in the search engines dropdown,
+ // so just blindly remove any highlight there.
+ let searchMenuBtn = null;
+ try {
+ searchMenuBtn = this.targets.get("searchProvider").query(aWindow.document);
+ } catch (e) { /* This is ok to fail. */ }
+ if (searchMenuBtn) {
+ let searchPopup = aWindow.document
+ .getAnonymousElementByAttribute(searchMenuBtn,
+ "anonid",
+ "searchbar-popup");
+ for (let menuItem of searchPopup.children)
+ menuItem.removeAttribute("_moz-menuactive");
+ }
},
/**
* Show an info panel.
*
* @param {Document} aContentDocument
* @param {Node} aAnchor
* @param {String} [aTitle=""]
@@ -989,16 +1032,21 @@ this.UITour = {
this._addAnnotationPanelMutationObserver(tooltip);
tooltip.openPopup(aAnchorEl, alignment);
}
// Prevent showing a panel at an undefined position.
if (!this.isElementVisible(aAnchor.node))
return;
+ // Due to a platform limitation, we can't anchor a panel to an element in a
+ // <menupopup>. So we can't support showing info panels for search engines.
+ if (aAnchor.targetName.startsWith(TARGET_SEARCHENGINE_PREFIX))
+ return;
+
this._setAppMenuStateForAnnotation(aAnchor.node.ownerDocument.defaultView, "info",
this.targetIsInAppMenu(aAnchor),
showInfoPanel.bind(this, aAnchor.node));
},
hideInfo: function(aWindow) {
let document = aWindow.document;
@@ -1008,25 +1056,25 @@ this.UITour = {
this._setAppMenuStateForAnnotation(aWindow, "info", false);
let tooltipButtons = document.getElementById("UITourTooltipButtons");
while (tooltipButtons.firstChild)
tooltipButtons.firstChild.remove();
},
showMenu: function(aWindow, aMenuName, aOpenCallback = null) {
- function openMenuButton(aID) {
- let menuBtn = aWindow.document.getElementById(aID);
- if (!menuBtn || !menuBtn.boxObject) {
- aOpenCallback();
+ function openMenuButton(aMenuBtn) {
+ if (!aMenuBtn || !aMenuBtn.boxObject || aMenuBtn.open) {
+ if (aOpenCallback)
+ aOpenCallback();
return;
}
if (aOpenCallback)
- menuBtn.addEventListener("popupshown", onPopupShown);
- menuBtn.boxObject.QueryInterface(Ci.nsIMenuBoxObject).openMenu(true);
+ aMenuBtn.addEventListener("popupshown", onPopupShown);
+ aMenuBtn.boxObject.openMenu(true);
}
function onPopupShown(event) {
this.removeEventListener("popupshown", onPopupShown);
aOpenCallback(event);
}
if (aMenuName == "appMenu") {
aWindow.PanelUI.panel.setAttribute("noautohide", "true");
@@ -1036,33 +1084,41 @@ this.UITour = {
}
aWindow.PanelUI.panel.addEventListener("popuphiding", this.hidePanelAnnotations);
aWindow.PanelUI.panel.addEventListener("ViewShowing", this.hidePanelAnnotations);
if (aOpenCallback) {
aWindow.PanelUI.panel.addEventListener("popupshown", onPopupShown);
}
aWindow.PanelUI.show();
} else if (aMenuName == "bookmarks") {
- openMenuButton("bookmarks-menu-button");
+ let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
+ openMenuButton(menuBtn);
+ } else if (aMenuName == "searchEngines") {
+ this.getTarget(aWindow, "searchProvider").then(target => {
+ openMenuButton(target.node);
+ }).catch(Cu.reportError);
}
},
hideMenu: function(aWindow, aMenuName) {
- function closeMenuButton(aID) {
- let menuBtn = aWindow.document.getElementById(aID);
- if (menuBtn && menuBtn.boxObject)
- menuBtn.boxObject.QueryInterface(Ci.nsIMenuBoxObject).openMenu(false);
+ function closeMenuButton(aMenuBtn) {
+ if (aMenuBtn && aMenuBtn.boxObject)
+ aMenuBtn.boxObject.openMenu(false);
}
if (aMenuName == "appMenu") {
aWindow.PanelUI.panel.removeAttribute("noautohide");
aWindow.PanelUI.hide();
this.recreatePopup(aWindow.PanelUI.panel);
} else if (aMenuName == "bookmarks") {
- closeMenuButton("bookmarks-menu-button");
+ let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
+ closeMenuButton(menuBtn);
+ } else if (aMenuName == "searchEngines") {
+ let menuBtn = this.targets.get("searchProvider").query(aWindow.document);
+ closeMenuButton(menuBtn);
}
},
hidePanelAnnotations: function(aEvent) {
let win = aEvent.target.ownerDocument.defaultView;
let annotationElements = new Map([
// [annotationElement (panel), method to hide the annotation]
[win.document.getElementById("UITourHighlightContainer"), UITour.hideHighlight.bind(UITour)],
@@ -1153,41 +1209,49 @@ this.UITour = {
break;
default:
Cu.reportError("getConfiguration: Unknown configuration requested: " + aConfiguration);
break;
}
},
getAvailableTargets: function(aContentDocument, aCallbackID) {
- let window = this.getChromeWindow(aContentDocument);
- let data = this.availableTargetsCache.get(window);
- if (data) {
- this.sendPageCallback(aContentDocument, aCallbackID, data);
- return;
- }
+ Task.spawn(function*() {
+ let window = this.getChromeWindow(aContentDocument);
+ let data = this.availableTargetsCache.get(window);
+ if (data) {
+ this.sendPageCallback(aContentDocument, aCallbackID, data);
+ return;
+ }
- let promises = [];
- for (let targetName of this.targets.keys()) {
- promises.push(this.getTarget(window, targetName));
- }
- Promise.all(promises).then((targetObjects) => {
+ let promises = [];
+ for (let targetName of this.targets.keys()) {
+ promises.push(this.getTarget(window, targetName));
+ }
+ let targetObjects = yield Promise.all(promises);
+
let targetNames = [
"pinnedTab",
];
+
for (let targetObject of targetObjects) {
if (targetObject.node)
targetNames.push(targetObject.targetName);
}
- let data = {
+
+ targetNames = targetNames.concat(
+ yield this.getAvailableSearchEngineTargets(window)
+ );
+
+ data = {
targets: targetNames,
};
this.availableTargetsCache.set(window, data);
this.sendPageCallback(aContentDocument, aCallbackID, data);
- }, (err) => {
+ }.bind(this)).catch(err => {
Cu.reportError(err);
this.sendPageCallback(aContentDocument, aCallbackID, {
targets: [],
});
});
},
addNavBarWidget: function (aTarget, aContentDocument, aCallbackID) {
@@ -1243,11 +1307,60 @@ this.UITour = {
_annotationMutationCallback: function(aMutations) {
for (let mutation of aMutations) {
// Remove both attributes at once and ignore remaining mutations to be proccessed.
mutation.target.removeAttribute("width");
mutation.target.removeAttribute("height");
return;
}
},
+
+ getAvailableSearchEngineTargets(aWindow) {
+ return new Promise(resolve => {
+ this.getTarget(aWindow, "search").then(searchTarget => {
+ if (!searchTarget.node || this.targetIsInAppMenu(searchTarget))
+ return resolve([]);
+
+ Services.search.init(() => {
+ let engines = Services.search.getVisibleEngines();
+ resolve([TARGET_SEARCHENGINE_PREFIX + engine.identifier
+ for (engine of engines)
+ if (engine.identifier)]);
+ });
+ }).catch(() => resolve([]));
+ });
+ },
+
+ // We only allow matching based on a search engine's identifier - this gives
+ // us a non-changing ID and guarentees we only match against app-provided
+ // engines.
+ getSearchEngineTarget(aWindow, aIdentifier) {
+ return new Promise((resolve, reject) => {
+ Task.spawn(function*() {
+ let searchTarget = yield this.getTarget(aWindow, "search");
+ // We're not supporting having the searchbar in the app-menu, because
+ // popups within popups gets crazy. This restriction should be lifted
+ // once bug 988151 is implemented, as the page can then be responsible
+ // for opening each menu when appropriate.
+ if (!searchTarget.node || this.targetIsInAppMenu(searchTarget))
+ return reject("Search engine not available");
+
+ yield Services.search.init();
+
+ let searchPopup = searchTarget.node._popup;
+ for (let engineNode of searchPopup.children) {
+ let engine = engineNode.engine;
+ if (engine && engine.identifier == aIdentifier) {
+ return resolve({
+ targetName: TARGET_SEARCHENGINE_PREFIX + engine.identifier,
+ node: engineNode,
+ });
+ }
+ }
+ reject("Search engine not available");
+ }.bind(this)).catch(() => {
+ reject("Search engine not available");
+ });
+ });
+ }
};
this.UITour.init();
--- a/browser/modules/test/browser_UITour.js
+++ b/browser/modules/test/browser_UITour.js
@@ -190,16 +190,63 @@ let tests = [
}
let highlight = document.getElementById("UITourHighlight");
is_element_hidden(highlight, "Highlight should initially be hidden");
gContentAPI.showHighlight("urlbar");
waitForElementToBeVisible(highlight, checkDefaultEffect, "Highlight should be shown after showHighlight()");
},
+ function test_highlight_search_engine(done) {
+ let highlight = document.getElementById("UITourHighlight");
+ gContentAPI.showHighlight("urlbar");
+ waitForElementToBeVisible(highlight, () => {
+
+ gContentAPI.showMenu("searchEngines", function() {
+ let searchbar = document.getElementById("searchbar");
+ isnot(searchbar, null, "Should have found searchbar");
+ let searchPopup = document.getAnonymousElementByAttribute(searchbar,
+ "anonid",
+ "searchbar-popup");
+ isnot(searchPopup, null, "Should have found search popup");
+
+ function getEngineNode(identifier) {
+ let engineNode = null;
+ for (let node of searchPopup.children) {
+ if (node.engine.identifier == identifier) {
+ engineNode = node;
+ break;
+ }
+ }
+ isnot(engineNode, null, "Should have found search engine node in popup");
+ return engineNode;
+ }
+ let googleEngineNode = getEngineNode("google");
+ let bingEngineNode = getEngineNode("bing");
+
+ gContentAPI.showHighlight("searchEngine-google");
+ waitForCondition(() => googleEngineNode.getAttribute("_moz-menuactive") == "true", function() {
+ is_element_hidden(highlight, "Highlight panel should be hidden by highlighting search engine");
+
+ gContentAPI.showHighlight("searchEngine-bing");
+ waitForCondition(() => bingEngineNode.getAttribute("_moz-menuactive") == "true", function() {
+ isnot(googleEngineNode.getAttribute("_moz-menuactive"), "true", "Previous engine should no longer be highlighted");
+
+ gContentAPI.hideHighlight();
+ waitForCondition(() => bingEngineNode.getAttribute("_moz-menuactive") != "true", function() {
+ gContentAPI.hideMenu("searchEngines");
+ waitForCondition(() => searchPopup.state == "closed", function() {
+ done();
+ }, "Search dropdown should close");
+ }, "Menu item should get attribute removed");
+ }, "Menu item should get attribute to make it look active");
+ });
+ });
+ });
+ },
function test_highlight_effect_unsupported(done) {
function checkUnsupportedEffect() {
is(highlight.getAttribute("active"), "none", "No effect should be used when an unsupported effect is requested");
done();
}
let highlight = document.getElementById("UITourHighlight");
is_element_hidden(highlight, "Highlight should initially be hidden");
--- a/browser/modules/test/browser_UITour_availableTargets.js
+++ b/browser/modules/test/browser_UITour_availableTargets.js
@@ -9,16 +9,23 @@ let gContentWindow;
Components.utils.import("resource:///modules/UITour.jsm");
function test() {
requestLongerTimeout(2);
UITourTest();
}
+function searchEngineTargets() {
+ let engines = Services.search.getVisibleEngines();
+ return ["searchEngine-" + engine.identifier
+ for (engine of engines)
+ if (engine.identifier)];
+}
+
let tests = [
function test_availableTargets(done) {
gContentAPI.getConfiguration("availableTargets", (data) => {
ok_targets(data, [
"accountStatus",
"addons",
"appMenu",
"backForward",
@@ -28,17 +35,17 @@ let tests = [
"home",
"loop",
"pinnedTab",
"privateWindow",
"quit",
"search",
"searchProvider",
"urlbar",
- ]);
+ ].concat(searchEngineTargets()));
ok(UITour.availableTargetsCache.has(window),
"Targets should now be cached");
done();
});
},
function test_availableTargets_changeWidgets(done) {
CustomizableUI.removeWidgetFromArea("bookmarks-menu-button");
@@ -55,17 +62,17 @@ let tests = [
"loop",
"home",
"pinnedTab",
"privateWindow",
"quit",
"search",
"searchProvider",
"urlbar",
- ]);
+ ].concat(searchEngineTargets()));
ok(UITour.availableTargetsCache.has(window),
"Targets should now be cached again");
CustomizableUI.reset();
ok(!UITour.availableTargetsCache.has(window),
"Targets should not be cached after reset");
done();
});
},
--- a/browser/modules/test/uitour.js
+++ b/browser/modules/test/uitour.js
@@ -1,21 +1,19 @@
/* 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/. */
-// Copied from the proposed JS library for Bedrock (ie, www.mozilla.org).
-
// create namespace
if (typeof Mozilla == 'undefined') {
var Mozilla = {};
}
-(function($) {
- 'use strict';
+;(function($) {
+ 'use strict';
// create namespace
if (typeof Mozilla.UITour == 'undefined') {
Mozilla.UITour = {};
}
var themeIntervalId = null;
function _stopCyclingThemes() {
@@ -55,16 +53,19 @@ if (typeof Mozilla == 'undefined') {
}
document.addEventListener("mozUITourResponse", listener);
return id;
}
Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY = 10 * 1000;
+ Mozilla.UITour.CONFIGNAME_SYNC = "sync";
+ Mozilla.UITour.CONFIGNAME_AVAILABLETARGETS = "availableTargets";
+
Mozilla.UITour.registerPageID = function(pageID) {
_sendEvent('registerPageID', {
pageID: pageID
});
};
Mozilla.UITour.showHighlight = function(target, effect) {
_sendEvent('showHighlight', {
@@ -81,17 +82,17 @@ if (typeof Mozilla == 'undefined') {
var buttonData = [];
if (Array.isArray(buttons)) {
for (var i = 0; i < buttons.length; i++) {
buttonData.push({
label: buttons[i].label,
icon: buttons[i].icon,
style: buttons[i].style,
callbackID: _waitForCallback(buttons[i].callback)
- });
+ });
}
}
var closeButtonCallbackID, targetCallbackID;
if (options && options.closeButtonCallback)
closeButtonCallbackID = _waitForCallback(options.closeButtonCallback);
if (options && options.targetCallback)
targetCallbackID = _waitForCallback(options.targetCallback);
@@ -151,28 +152,44 @@ if (typeof Mozilla == 'undefined') {
Mozilla.UITour.addPinnedTab = function() {
_sendEvent('addPinnedTab');
};
Mozilla.UITour.removePinnedTab = function() {
_sendEvent('removePinnedTab');
};
- Mozilla.UITour.showMenu = function(name) {
+ Mozilla.UITour.showMenu = function(name, callback) {
+ var showCallbackID;
+ if (callback)
+ showCallbackID = _waitForCallback(callback);
+
_sendEvent('showMenu', {
- name: name
+ name: name,
+ showCallbackID: showCallbackID,
});
};
Mozilla.UITour.hideMenu = function(name) {
_sendEvent('hideMenu', {
name: name
});
};
+ Mozilla.UITour.startUrlbarCapture = function(text, url) {
+ _sendEvent('startUrlbarCapture', {
+ text: text,
+ url: url
+ });
+ };
+
+ Mozilla.UITour.endUrlbarCapture = function() {
+ _sendEvent('endUrlbarCapture');
+ };
+
Mozilla.UITour.getConfiguration = function(configName, callback) {
_sendEvent('getConfiguration', {
callbackID: _waitForCallback(callback),
configuration: configName,
});
};
Mozilla.UITour.showFirefoxAccounts = function() {
deleted file mode 100644
--- a/build/autoconf/ccache.m4
+++ /dev/null
@@ -1,36 +0,0 @@
-dnl This Source Code Form is subject to the terms of the Mozilla Public
-dnl License, v. 2.0. If a copy of the MPL was not distributed with this
-dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-dnl ======================================================
-dnl = Enable compiling with ccache
-dnl ======================================================
-AC_DEFUN([MOZ_CHECK_CCACHE],
-[
-MOZ_ARG_WITH_STRING(ccache,
-[ --with-ccache[=path/to/ccache]
- Enable compiling with ccache],
- CCACHE=$withval, CCACHE="no")
-
-if test "$CCACHE" != "no"; then
- if test -z "$CCACHE" -o "$CCACHE" = "yes"; then
- CCACHE=
- else
- if test ! -e "$CCACHE"; then
- AC_MSG_ERROR([$CCACHE not found])
- fi
- fi
- MOZ_PATH_PROGS(CCACHE, $CCACHE ccache)
- if test -z "$CCACHE" -o "$CCACHE" = ":"; then
- AC_MSG_ERROR([ccache not found])
- elif test -x "$CCACHE"; then
- CC="$CCACHE $CC"
- CXX="$CCACHE $CXX"
- MOZ_USING_CCACHE=1
- else
- AC_MSG_ERROR([$CCACHE is not executable])
- fi
-fi
-
-AC_SUBST(MOZ_USING_CCACHE)
-])
--- a/build/autoconf/nspr-build.m4
+++ b/build/autoconf/nspr-build.m4
@@ -192,16 +192,21 @@ AC_SUBST(NSPR_PKGCONF_CHECK)
fi # _IS_OUTER_CONFIGURE
])
AC_DEFUN([MOZ_SUBCONFIGURE_NSPR], [
if test -z "$MOZ_NATIVE_NSPR"; then
ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla"
+ if test -n "$MOZ_USING_CCACHE"; then
+ # Avoid double prepending ccache by omitting --with-ccache in building NSPR.
+ ac_configure_args="`echo $ac_configure_args | sed -e 's/--with-ccache[[^ ]]*//'`"
+ fi
+
if test -z "$MOZ_DEBUG"; then
ac_configure_args="$ac_configure_args --disable-debug"
else
ac_configure_args="$ac_configure_args --enable-debug"
if test -n "$MOZ_NO_DEBUG_RTL"; then
ac_configure_args="$ac_configure_args --disable-debug-rtl"
fi
fi
--- a/build/autoconf/wrapper.m4
+++ b/build/autoconf/wrapper.m4
@@ -7,16 +7,46 @@ dnl = Enable compiling with various comp
dnl =======================================================================
AC_DEFUN([MOZ_CHECK_COMPILER_WRAPPER],
[
MOZ_ARG_WITH_STRING(compiler_wrapper,
[ --with-compiler-wrapper[=path/to/wrapper]
Enable compiling with wrappers such as distcc and ccache],
COMPILER_WRAPPER=$withval, COMPILER_WRAPPER="no")
+MOZ_ARG_WITH_STRING(ccache,
+[ --with-ccache[=path/to/ccache]
+ Enable compiling with ccache],
+ CCACHE=$withval, CCACHE="no")
+
+if test "$CCACHE" != "no"; then
+ if test -z "$CCACHE" -o "$CCACHE" = "yes"; then
+ CCACHE=
+ else
+ if test ! -e "$CCACHE"; then
+ AC_MSG_ERROR([$CCACHE not found])
+ fi
+ fi
+ MOZ_PATH_PROGS(CCACHE, $CCACHE ccache)
+ if test -z "$CCACHE" -o "$CCACHE" = ":"; then
+ AC_MSG_ERROR([ccache not found])
+ elif test -x "$CCACHE"; then
+ if test "$COMPILER_WRAPPER" != "no"; then
+ COMPILER_WRAPPER="$CCACHE $COMPILER_WRAPPER"
+ else
+ COMPILER_WRAPPER="$CCACHE"
+ fi
+ MOZ_USING_CCACHE=1
+ else
+ AC_MSG_ERROR([$CCACHE is not executable])
+ fi
+fi
+
+AC_SUBST(MOZ_USING_CCACHE)
+
if test "$COMPILER_WRAPPER" != "no"; then
case "$target" in
*-mingw*)
dnl When giving a windows path with backslashes, js/src/configure
dnl fails because of double wrapping because the test further below
dnl doesn't work with backslashes. While fixing that test to work
dnl might seem better, a lot of the make build backend actually
dnl doesn't like backslashes, so normalize windows paths to use
--- a/client.mk
+++ b/client.mk
@@ -86,16 +86,19 @@ ifneq (,$(findstring mingw,$(CONFIG_GUES
# check for CRLF line endings
ifneq (0,$(shell $(PERL) -e 'binmode(STDIN); while (<STDIN>) { if (/\r/) { print "1"; exit } } print "0"' < $(TOPSRCDIR)/client.mk))
$(error This source tree appears to have Windows-style line endings. To \
convert it to Unix-style line endings, check \
"https://developer.mozilla.org/en-US/docs/Developer_Guide/Mozilla_build_FAQ\#Win32-specific_questions" \
for a workaround of this issue.)
endif
+
+# Set this for baseconfig.mk
+HOST_OS_ARCH=WINNT
endif
####################################
# Load mozconfig Options
# See build pages, http://www.mozilla.org/build/ for how to set up mozconfig.
define CR
@@ -162,16 +165,19 @@ OBJDIR_TARGETS = install export libs cle
#######################################################################
# Rules
# The default rule is build
build::
$(MAKE) -f $(TOPSRCDIR)/client.mk $(if $(MOZ_PGO),profiledbuild,realbuild) CREATE_MOZCONFIG_JSON=
+# Include baseconfig.mk for its $(MAKE) validation.
+include $(TOPSRCDIR)/config/baseconfig.mk
+
# Define mkdir
include $(TOPSRCDIR)/config/makefiles/makeutils.mk
include $(TOPSRCDIR)/config/makefiles/autotargets.mk
# Create a makefile containing the mk_add_options values from mozconfig,
# but only do so when OBJDIR is defined (see further above).
ifdef MOZ_BUILD_PROJECTS
ifdef MOZ_CURRENT_PROJECT
--- a/configure.in
+++ b/configure.in
@@ -7322,18 +7322,16 @@ dnl ====================================
MOZ_ARG_ENABLE_BOOL(gczeal,
[ --enable-gczeal Enable zealous JavaScript GCing],
JS_GC_ZEAL=1,
JS_GC_ZEAL= )
if test -n "$JS_GC_ZEAL" -o -n "$MOZ_DEBUG"; then
AC_DEFINE(JS_GC_ZEAL)
fi
-MOZ_CHECK_CCACHE
-
dnl ========================================================
dnl = Enable static checking using gcc-dehydra
dnl ========================================================
MOZ_ARG_WITH_STRING(static-checking,
[ --with-static-checking=path/to/gcc_dehydra.so
Enable static checking of code using GCC-dehydra],
DEHYDRA_PATH=$withval,
@@ -9187,21 +9185,16 @@ if test -n "$_subconfigure_subdir"; then
srcdir="$_save_srcdir"
fi
# No need to run subconfigures when building with LIBXUL_SDK_DIR
if test "$COMPILE_ENVIRONMENT" -a -z "$LIBXUL_SDK_DIR"; then
export WRAP_LDFLAGS
-if test -n "$MOZ_USING_CCACHE"; then
- # Avoid double prepending ccache by omitting --with-ccache in building NSPR.
- _SUBDIR_CONFIG_ARGS="`echo $_SUBDIR_CONFIG_ARGS | sed -e 's/--with-ccache[[^ ]]*//'`"
-fi
-
MOZ_SUBCONFIGURE_NSPR()
dnl ========================================================
dnl = Setup a nice relatively clean build environment for
dnl = sub-configures.
dnl ========================================================
CC="$_SUBDIR_CC"
CXX="$_SUBDIR_CXX"
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -128,18 +128,18 @@ class AnimationPlayer;
class Link;
class UndoManager;
class DOMRect;
class DOMRectList;
class DestinationInsertionPointList;
// IID for the dom::Element interface
#define NS_ELEMENT_IID \
-{ 0xb0135f9d, 0xa476, 0x4711, \
- { 0x8b, 0xb9, 0xca, 0xe5, 0x2a, 0x05, 0xf9, 0xbe } }
+{ 0xaa79cb98, 0xc785, 0x44c5, \
+ { 0x80, 0x80, 0x2e, 0x5f, 0x0c, 0xa5, 0xbd, 0x63 } }
class Element : public FragmentOrElement
{
public:
#ifdef MOZILLA_INTERNAL_API
explicit Element(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) :
FragmentOrElement(aNodeInfo),
mState(NS_EVENT_STATE_MOZ_READONLY)
@@ -669,16 +669,20 @@ public:
}
void SetPointerCapture(int32_t aPointerId, ErrorResult& aError)
{
bool activeState = false;
if (!nsIPresShell::GetPointerInfo(aPointerId, activeState)) {
aError.Throw(NS_ERROR_DOM_INVALID_POINTER_ERR);
return;
}
+ if (!IsInDoc()) {
+ aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
if (!activeState) {
return;
}
nsIPresShell::SetPointerCapturingContent(aPointerId, this);
}
void ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError)
{
bool activeState = false;
--- a/content/base/public/File.h
+++ b/content/base/public/File.h
@@ -86,21 +86,27 @@ public:
static already_AddRefed<File>
Create(nsISupports* aParent, const nsAString& aContentType,
uint64_t aLength);
static already_AddRefed<File>
Create(nsISupports* aParent, const nsAString& aContentType, uint64_t aStart,
uint64_t aLength);
+ // The returned File takes ownership of aMemoryBuffer. aMemoryBuffer will be
+ // freed by moz_free so it must be allocated by moz_malloc or something
+ // compatible with it.
static already_AddRefed<File>
CreateMemoryFile(nsISupports* aParent, void* aMemoryBuffer, uint64_t aLength,
const nsAString& aName, const nsAString& aContentType,
uint64_t aLastModifiedDate);
+ // The returned File takes ownership of aMemoryBuffer. aMemoryBuffer will be
+ // freed by moz_free so it must be allocated by moz_malloc or something
+ // compatible with it.
static already_AddRefed<File>
CreateMemoryFile(nsISupports* aParent, void* aMemoryBuffer, uint64_t aLength,
const nsAString& aContentType);
static already_AddRefed<File>
CreateTemporaryFileBlob(nsISupports* aParent, PRFileDesc* aFD,
uint64_t aStartPos, uint64_t aLength,
const nsAString& aContentType);
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -33,17 +33,16 @@ class nsIDocShell;
class nsDocShell;
class nsDOMNavigationTiming;
class nsFrameLoader;
class nsHTMLCSSStyleSheet;
class nsHTMLDocument;
class nsHTMLStyleSheet;
class nsIAtom;
class nsIBFCacheEntry;
-class nsIBoxObject;
class nsIChannel;
class nsIContent;
class nsIContentSink;
class nsIDocShell;
class nsIDocumentEncoder;
class nsIDocumentObserver;
class nsIDOMDocument;
class nsIDOMDocumentFragment;
@@ -90,16 +89,17 @@ class SVGAttrAnimationRuleProcessor;
namespace css {
class Loader;
class ImageLoader;
} // namespace css
namespace dom {
class AnimationTimeline;
class Attr;
+class BoxObject;
class CDATASection;
class Comment;
struct CustomElementDefinition;
class DocumentFragment;
class DocumentType;
class DOMImplementation;
class DOMStringList;
class Element;
@@ -1495,17 +1495,17 @@ public:
* @param aElement canonical nsIContent pointer of the box object's element
*/
virtual void ClearBoxObjectFor(nsIContent *aContent) = 0;
/**
* Get the box object for an element. This is not exposed through a
* scriptable interface except for XUL documents.
*/
- virtual already_AddRefed<nsIBoxObject>
+ virtual already_AddRefed<mozilla::dom::BoxObject>
GetBoxObjectFor(mozilla::dom::Element* aElement,
mozilla::ErrorResult& aRv) = 0;
/**
* Get the compatibility mode for this document
*/
nsCompatibility GetCompatibilityMode() const {
return mCompatMode;
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -243,18 +243,18 @@ private:
// Categories of node properties
// 0 is global.
#define DOM_USER_DATA 1
#define SMIL_MAPPED_ATTR_ANIMVAL 2
// IID for the nsINode interface
#define NS_INODE_IID \
-{ 0x3a60353e, 0x04e5, 0x49ca, \
- { 0x84, 0x1c, 0x59, 0xc6, 0xde, 0xe6, 0x36, 0xcc } }
+{ 0x8deda3f4, 0x0f45, 0x497a, \
+ { 0x89, 0x7c, 0xe6, 0x09, 0x12, 0x8a, 0xad, 0xd8 } }
/**
* An internal interface that abstracts some DOMNode-related parts that both
* nsIContent and nsIDocument share. An instance of this interface has a list
* of nsIContent children and provides access to them.
*/
class nsINode : public mozilla::dom::EventTarget
{
--- a/content/base/src/ImportManager.cpp
+++ b/content/base/src/ImportManager.cpp
@@ -479,17 +479,17 @@ ImportLoader::Open()
return;
}
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
rv = secMan->CheckLoadURIWithPrincipal(principal, mURI,
nsIScriptSecurityManager::STANDARD);
NS_ENSURE_SUCCESS_VOID(rv);
- nsCOMPtr<nsILoadGroup> loadGroup = mImportParent->GetDocumentLoadGroup();
+ nsCOMPtr<nsILoadGroup> loadGroup = master->GetDocumentLoadGroup();
nsCOMPtr<nsIChannelPolicy> channelPolicy;
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = principal->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS_VOID(rv);
if (csp) {
channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
channelPolicy->SetContentSecurityPolicy(csp);
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -215,16 +215,17 @@
#include "nsIDocumentActivity.h"
#include "nsIStructuredCloneContainer.h"
#include "nsIMutableArray.h"
#include "nsContentPermissionHelper.h"
#include "mozilla/dom/DOMStringList.h"
#include "nsWindowMemoryReporter.h"
#include "nsLocation.h"
#include "mozilla/dom/FontFaceSet.h"
+#include "mozilla/dom/BoxObject.h"
#ifdef MOZ_MEDIA_NAVIGATOR
#include "mozilla/MediaManager.h"
#endif // MOZ_MEDIA_NAVIGATOR
#ifdef MOZ_WEBRTC
#include "IPeerConnection.h"
#endif // MOZ_WEBRTC
@@ -6942,17 +6943,17 @@ nsDocument::DoNotifyPossibleTitleChange(
}
// Fire a DOM event for the title change.
nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
NS_LITERAL_STRING("DOMTitleChanged"),
true, true);
}
-already_AddRefed<nsIBoxObject>
+already_AddRefed<BoxObject>
nsDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv)
{
if (!aElement) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsIDocument* doc = aElement->OwnerDoc();
@@ -6969,17 +6970,17 @@ nsDocument::GetBoxObjectFor(Element* aEl
"UseOfGetBoxObjectForWarning");
}
if (!mBoxObjectTable) {
mBoxObjectTable = new nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject>(6);
} else {
nsCOMPtr<nsPIBoxObject> boxObject = mBoxObjectTable->Get(aElement);
if (boxObject) {
- return boxObject.forget();
+ return boxObject.forget().downcast<BoxObject>();
}
}
int32_t namespaceID;
nsCOMPtr<nsIAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
nsAutoCString contractID("@mozilla.org/layout/xul-boxobject");
if (namespaceID == kNameSpaceID_XUL) {
@@ -7010,17 +7011,17 @@ nsDocument::GetBoxObjectFor(Element* aEl
}
boxObject->Init(aElement);
if (mBoxObjectTable) {
mBoxObjectTable->Put(aElement, boxObject.get());
}
- return boxObject.forget();
+ return boxObject.forget().downcast<BoxObject>();
}
void
nsDocument::ClearBoxObjectFor(nsIContent* aContent)
{
if (mBoxObjectTable) {
nsPIBoxObject *boxObject = mBoxObjectTable->GetWeak(aContent);
if (boxObject) {
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -25,18 +25,16 @@
#include "nsStubDocumentObserver.h"
#include "nsIScriptGlobalObject.h"
#include "nsIContent.h"
#include "nsIPrincipal.h"
#include "nsIParser.h"
#include "nsBindingManager.h"
#include "nsInterfaceHashtable.h"
#include "nsJSThingHashtable.h"
-#include "nsIBoxObject.h"
-#include "nsPIBoxObject.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIURI.h"
#include "nsScriptLoader.h"
#include "nsIRadioGroupContainer.h"
#include "nsILayoutHistoryState.h"
#include "nsIRequest.h"
#include "nsILoadGroup.h"
#include "nsTObserverArray.h"
@@ -90,20 +88,22 @@ class nsChildContentList;
class nsHTMLStyleSheet;
class nsHTMLCSSStyleSheet;
class nsDOMNavigationTiming;
class nsWindowSizes;
class nsHtml5TreeOpExecutor;
class nsDocumentOnStack;
class nsPointerLockPermissionRequest;
class nsISecurityConsoleMessage;
+class nsPIBoxObject;
namespace mozilla {
class EventChainPreVisitor;
namespace dom {
+class BoxObject;
class UndoManager;
struct LifecycleCallbacks;
class CallbackFunction;
}
}
/**
* Right now our identifier map entries contain information for 'name'
@@ -997,18 +997,20 @@ public:
virtual void BlockOnload();
virtual void UnblockOnload(bool aFireSync);
virtual void AddStyleRelevantLink(mozilla::dom::Link* aLink);
virtual void ForgetLink(mozilla::dom::Link* aLink);
void ClearBoxObjectFor(nsIContent* aContent);
- already_AddRefed<nsIBoxObject> GetBoxObjectFor(mozilla::dom::Element* aElement,
- mozilla::ErrorResult& aRv) MOZ_OVERRIDE;
+
+ virtual already_AddRefed<mozilla::dom::BoxObject>
+ GetBoxObjectFor(mozilla::dom::Element* aElement,
+ mozilla::ErrorResult& aRv) MOZ_OVERRIDE;
virtual Element*
GetAnonymousElementByAttribute(nsIContent* aElement,
nsIAtom* aAttrName,
const nsAString& aAttrValue) const;
virtual Element* ElementFromPointHelper(float aX, float aY,
bool aIgnoreRootScrollFrame,
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -491,16 +491,17 @@ GK_ATOM(itemref, "itemref")
GK_ATOM(itemscope, "itemscope")
GK_ATOM(itemtype, "itemtype")
GK_ATOM(kbd, "kbd")
GK_ATOM(noautofocus, "noautofocus")
GK_ATOM(keepcurrentinview, "keepcurrentinview")
GK_ATOM(keepobjectsalive, "keepobjectsalive")
GK_ATOM(key, "key")
GK_ATOM(keycode, "keycode")
+GK_ATOM(keyschange, "keyschange")
GK_ATOM(keydown, "keydown")
GK_ATOM(keygen, "keygen")
GK_ATOM(keypress, "keypress")
GK_ATOM(keyset, "keyset")
GK_ATOM(keysystem, "keysystem")
GK_ATOM(keytext, "keytext")
GK_ATOM(keyup, "keyup")
GK_ATOM(kind, "kind")
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -1243,17 +1243,17 @@ nsScriptLoader::ReadyToExecuteScripts()
nsScriptLoader* ancestor = doc->ScriptLoader();
if (!ancestor->SelfReadyToExecuteScripts() &&
ancestor->AddPendingChildLoader(this)) {
AddExecuteBlocker();
return false;
}
}
- if (!mDocument->IsMasterDocument()) {
+ if (mDocument && !mDocument->IsMasterDocument()) {
nsRefPtr<ImportManager> im = mDocument->ImportManager();
nsRefPtr<ImportLoader> loader = im->Find(mDocument);
MOZ_ASSERT(loader, "How can we have an import document without a loader?");
// The referring link that counts in the execution order calculation
// (in spec: flagged as branch)
nsCOMPtr<nsINode> referrer = loader->GetMainReferrer();
MOZ_ASSERT(referrer, "There has to be a main referring link for each imports");
--- a/content/base/test/test_bug527896.html
+++ b/content/base/test/test_bug527896.html
@@ -17,18 +17,16 @@ https://bugzilla.mozilla.org/show_bug.cg
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 527896 **/
SimpleTest.waitForExplicitFinish();
-SimpleTest.expectAssertions(1);
-
var docWrittenSrcExecuted = false;
var scriptInsertedSrcExecuted = false;
// the iframe test runs with the HTML5 parser
var iframe = document.getElementsByTagName('iframe')[0];
iframe.contentWindow.document.open();
iframe.contentWindow.document.write("<!DOCTYPE html>");
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -1159,17 +1159,18 @@ nsresult HTMLMediaElement::LoadResource(
nsRefPtr<MediaSourceDecoder> decoder = new MediaSourceDecoder(this);
if (!source->Attach(decoder)) {
// TODO: Handle failure: run "If the media data cannot be fetched at
// all, due to network errors, causing the user agent to give up
// trying to fetch the resource" section of resource fetch algorithm.
return NS_ERROR_FAILURE;
}
mMediaSource = source.forget();
- nsRefPtr<MediaResource> resource = MediaSourceDecoder::CreateResource();
+ nsRefPtr<MediaResource> resource =
+ MediaSourceDecoder::CreateResource(mMediaSource->GetPrincipal());
return FinishDecoderSetup(decoder, resource, nullptr, nullptr);
}
nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
// check for a Content Security Policy to pass down to the channel
// created to load the media content
nsCOMPtr<nsIChannelPolicy> channelPolicy;
--- a/content/html/content/test/forms/test_meter_element.html
+++ b/content/html/content/test/forms/test_meter_element.html
@@ -16,22 +16,16 @@ https://bugzilla.mozilla.org/show_bug.cg
<div id="content" style="visibility: hidden;">
<form id='f' method='get' target='submit_frame' action='foo'>
<meter id='m' value=0.5></meter>
</form>
</div>
<pre id="test">
<script type="application/javascript">
-if (navigator.platform.startsWith("Win")) {
- SimpleTest.expectAssertions(0, 1);
-} else {
- SimpleTest.expectAssertions(1);
-}
-
/** Test for <meter> **/
function checkFormIDLAttribute(aElement)
{
is('form' in aElement, false, "<meter> shouldn't have a form attribute");
}
function checkAttribute(aElement, aAttribute, aNewValue, aExpectedValueForIDL)
--- a/content/html/content/test/test_bug242709.html
+++ b/content/html/content/test/test_bug242709.html
@@ -12,18 +12,16 @@ https://bugzilla.mozilla.org/show_bug.cg
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=242709">Mozilla Bug 242709</a>
<p id="display"></p>
<div id="content">
<iframe src="bug242709_iframe.html" id="a"></iframe>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
-SimpleTest.expectAssertions(1);
-
/** Test for Bug 242709 **/
SimpleTest.waitForExplicitFinish();
var submitted = function() {
ok(true, "Disabling button after form submission doesn't prevent submitting");
SimpleTest.finish();
}
--- a/content/html/content/test/test_bug277890.html
+++ b/content/html/content/test/test_bug277890.html
@@ -12,18 +12,16 @@ https://bugzilla.mozilla.org/show_bug.cg
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=277890">Mozilla Bug 277890</a>
<p id="display"></p>
<div id="content">
<iframe src="bug277890_iframe.html" id="a"></iframe>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
-SimpleTest.expectAssertions(1);
-
/** Test for Bug 277890 **/
SimpleTest.waitForExplicitFinish();
var submitted = function() {
ok(true, "Disabling button after form submission doesn't prevent submitting");
SimpleTest.finish();
}
--- a/content/html/content/test/test_bug523771.html
+++ b/content/html/content/test/test_bug523771.html
@@ -15,22 +15,16 @@ https://bugzilla.mozilla.org/show_bug.cg
<form action="form_submit_server.sjs" target="target_iframe" id="form"
method="POST" enctype="multipart/form-data">
<input id=singleFile name=singleFile type=file>
<input id=multiFile name=multiFile type=file multiple>
</form>
<pre id="test">
<script class="testbody" type="text/javascript">
-if (navigator.platform.startsWith("Win")) {
- SimpleTest.expectAssertions(0, 1);
-} else {
- SimpleTest.expectAssertions(1);
-}
-
var filesToKill = [];
singleFileInput = document.getElementById('singleFile');
multiFileInput = document.getElementById('multiFile');
var input1File = { name: "523771_file1", type: "", body: "file1 contents"};
var input2Files =
[{ name: "523771_file2", type: "", body: "second file contents" },
{ name: "523771_file3.txt", type: "text/plain", body: "123456" },
{ name: "523771_file4.html", type: "text/html", body: "<html>content</html>" }
--- a/content/html/content/test/test_iframe_sandbox_inheritance.html
+++ b/content/html/content/test/test_iframe_sandbox_inheritance.html
@@ -9,19 +9,16 @@ Implement HTML5 sandbox attribute for IF
<title>Test for Bug 341604</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<script type="application/javascript">
/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
/** Inheritance Tests **/
-// Assertion failure in docshell/shistory/src/nsSHEntry.cpp (currently line 625).
-// Bug 901876 raised.
-SimpleTest.expectAssertions(1);
SimpleTest.waitForExplicitFinish();
// A postMessage handler that is used by sandboxed iframes without
// 'allow-same-origin' to communicate pass/fail back to this main page.
// It expects to be called with an object like {ok: true/false, desc:
// <description of the test> which it then forwards to ok().
window.addEventListener("message", receiveMessage, false);
--- a/content/html/content/test/test_iframe_sandbox_navigation.html
+++ b/content/html/content/test/test_iframe_sandbox_navigation.html
@@ -10,17 +10,16 @@ Implement HTML5 sandbox attribute for IF
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<script type="application/javascript">
/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
/** Navigation tests Part 1**/
-SimpleTest.expectAssertions(1, 3);
SimpleTest.waitForExplicitFinish();
// a postMessage handler that is used by sandboxed iframes without
// 'allow-same-origin'/other windows to communicate pass/fail back to this main page.
// it expects to be called with an object like {ok: true/false, desc:
// <description of the test> which it then forwards to ok()
window.addEventListener("message", receiveMessage, false);
var testPassesReceived = 0;
--- a/content/html/document/test/test_bug448564.html
+++ b/content/html/document/test/test_bug448564.html
@@ -15,18 +15,16 @@ https://bugzilla.mozilla.org/show_bug.cg
<iframe src="bug448564-iframe-2.html"></iframe>
<iframe src="bug448564-iframe-3.html"></iframe>
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
-SimpleTest.expectAssertions(3);
-
/** Test for Bug 448564 **/
/**
* The three iframes are going to be loaded with some dirty constructed forms.
* Each of them will be submitted before the load event and a SJS will replace
* the frame content with the query string.
* Then, on the load event, our test file will check the content of each iframes
* and check if the query string were correctly formatted (implying that all
--- a/content/html/document/test/test_bug478251.html
+++ b/content/html/document/test/test_bug478251.html
@@ -12,18 +12,16 @@ https://bugzilla.mozilla.org/show_bug.cg
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=478251">Mozilla Bug 478251</a>
<p id="display"><iframe id="t"></iframe></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
-SimpleTest.expectAssertions(9, 10);
-
/** Test for Bug 478251 **/
var doc = $("t").contentDocument;
doc.open();
doc.write();
doc.close();
is(doc.documentElement.textContent, "", "Writing || failed");
doc.open();
--- a/content/media/eme/CDMCallbackProxy.cpp
+++ b/content/media/eme/CDMCallbackProxy.cpp
@@ -267,28 +267,48 @@ CDMCallbackProxy::SessionError(const nsC
}
void
CDMCallbackProxy::KeyIdUsable(const nsCString& aSessionId,
const nsTArray<uint8_t>& aKeyId)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
- CDMCaps::AutoLock caps(mProxy->Capabilites());
- caps.SetKeyUsable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
+ bool keysChange = false;
+ {
+ CDMCaps::AutoLock caps(mProxy->Capabilites());
+ keysChange = caps.SetKeyUsable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
+ }
+ if (keysChange) {
+ nsRefPtr<nsIRunnable> task;
+ task = NS_NewRunnableMethodWithArg<nsString>(mProxy,
+ &CDMProxy::OnKeysChange,
+ NS_ConvertUTF8toUTF16(aSessionId));
+ NS_DispatchToMainThread(task);
+ }
}
void
CDMCallbackProxy::KeyIdNotUsable(const nsCString& aSessionId,
const nsTArray<uint8_t>& aKeyId)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
- CDMCaps::AutoLock caps(mProxy->Capabilites());
- caps.SetKeyUnusable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
+ bool keysChange = false;
+ {
+ CDMCaps::AutoLock caps(mProxy->Capabilites());
+ keysChange = caps.SetKeyUnusable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
+ }
+ if (keysChange) {
+ nsRefPtr<nsIRunnable> task;
+ task = NS_NewRunnableMethodWithArg<nsString>(mProxy,
+ &CDMProxy::OnKeysChange,
+ NS_ConvertUTF8toUTF16(aSessionId));
+ NS_DispatchToMainThread(task);
+ }
}
void
CDMCallbackProxy::SetCaps(uint64_t aCaps)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
CDMCaps::AutoLock caps(mProxy->Capabilites());
--- a/content/media/eme/CDMCaps.cpp
+++ b/content/media/eme/CDMCaps.cpp
@@ -83,53 +83,63 @@ CDMCaps::AutoLock::IsKeyUsable(const Cen
for (size_t i = 0; i < keys.Length(); i++) {
if (keys[i].mId == aKeyId) {
return true;
}
}
return false;
}
-void
+bool
CDMCaps::AutoLock::SetKeyUsable(const CencKeyId& aKeyId,
const nsString& aSessionId)
{
mData.mMonitor.AssertCurrentThreadOwns();
- mData.mUsableKeyIds.AppendElement(UsableKey(aKeyId, aSessionId));
+ UsableKey key(aKeyId, aSessionId);
+ if (mData.mUsableKeyIds.Contains(key)) {
+ return false;
+ }
+ mData.mUsableKeyIds.AppendElement(key);
auto& waiters = mData.mWaitForKeys;
size_t i = 0;
while (i < waiters.Length()) {
auto& w = waiters[i];
if (w.mKeyId == aKeyId) {
if (waiters[i].mTarget) {
EME_LOG("SetKeyUsable() notified waiter.");
w.mTarget->Dispatch(w.mContinuation, NS_DISPATCH_NORMAL);
} else {
w.mContinuation->Run();
}
waiters.RemoveElementAt(i);
} else {
i++;
}
}
+ return true;
}
-void
+bool
CDMCaps::AutoLock::SetKeyUnusable(const CencKeyId& aKeyId,
const nsString& aSessionId)
{
mData.mMonitor.AssertCurrentThreadOwns();
+ UsableKey key(aKeyId, aSessionId);
+ if (!mData.mUsableKeyIds.Contains(key)) {
+ return false;
+ }
auto& keys = mData.mUsableKeyIds;
for (size_t i = 0; i < keys.Length(); i++) {
if (keys[i].mId == aKeyId &&
keys[i].mSessionId == aSessionId) {
keys.RemoveElementAt(i);
break;
}
}
+ return true;
}
void
CDMCaps::AutoLock::CallWhenKeyUsable(const CencKeyId& aKey,
nsIRunnable* aContinuation,
nsIThread* aTarget)
{
mData.mMonitor.AssertCurrentThreadOwns();
--- a/content/media/eme/CDMCaps.h
+++ b/content/media/eme/CDMCaps.h
@@ -33,18 +33,23 @@ public:
~AutoLock();
// Returns true if the capabilities of the CDM are known, i.e. they have
// been reported by the CDM to Gecko.
bool AreCapsKnown();
bool IsKeyUsable(const CencKeyId& aKeyId);
- void SetKeyUsable(const CencKeyId& aKeyId, const nsString& aSessionId);
- void SetKeyUnusable(const CencKeyId& aKeyId, const nsString& aSessionId);
+ // Returns true if setting this key usable results in the usable keys
+ // changing for this session, i.e. the key was not previously marked usable.
+ bool SetKeyUsable(const CencKeyId& aKeyId, const nsString& aSessionId);
+
+ // Returns true if setting this key unusable results in the usable keys
+ // changing for this session, i.e. the key was previously marked usable.
+ bool SetKeyUnusable(const CencKeyId& aKeyId, const nsString& aSessionId);
void DropKeysForSession(const nsAString& aSessionId);
void GetUsableKeysForSession(const nsAString& aSessionId,
nsTArray<CencKeyId>& aOutKeyIds);
// Sets the capabilities of the CDM. aCaps is the logical OR of the
// GMP_EME_CAP_* flags from gmp-decryption.h.
void SetCaps(uint64_t aCaps);
@@ -94,16 +99,21 @@ private:
const nsString& aSessionId)
: mId(aId)
, mSessionId(aSessionId)
{}
UsableKey(const UsableKey& aOther)
: mId(aOther.mId)
, mSessionId(aOther.mSessionId)
{}
+ bool operator==(const UsableKey& aOther) const {
+ return mId == aOther.mId &&
+ mSessionId == aOther.mSessionId;
+ };
+
CencKeyId mId;
nsString mSessionId;
};
nsTArray<UsableKey> mUsableKeyIds;
nsTArray<WaitForKeys> mWaitForKeys;
nsTArray<nsRefPtr<nsIRunnable>> mWaitForCaps;
--- a/content/media/eme/CDMProxy.cpp
+++ b/content/media/eme/CDMProxy.cpp
@@ -421,16 +421,29 @@ CDMProxy::OnSessionMessage(const nsAStri
}
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
if (session) {
session->DispatchKeyMessage(aMessage, aDestinationURL);
}
}
void
+CDMProxy::OnKeysChange(const nsAString& aSessionId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->DispatchKeysChange();
+ }
+}
+
+void
CDMProxy::OnExpirationChange(const nsAString& aSessionId,
GMPTimestamp aExpiryTime)
{
MOZ_ASSERT(NS_IsMainThread());
NS_WARNING("CDMProxy::OnExpirationChange() not implemented");
}
void
--- a/content/media/eme/CDMProxy.h
+++ b/content/media/eme/CDMProxy.h
@@ -157,16 +157,19 @@ public:
GMPErr aResult,
const nsTArray<uint8_t>& aDecryptedData);
// GMP thread only.
void gmp_Terminated();
CDMCaps& Capabilites();
+ // Main thread only.
+ void OnKeysChange(const nsAString& aSessionId);
+
#ifdef DEBUG
bool IsOnGMPThread();
#endif
private:
struct InitData {
uint32_t mPromiseId;
--- a/content/media/eme/MediaKeySession.cpp
+++ b/content/media/eme/MediaKeySession.cpp
@@ -8,16 +8,17 @@
#include "mozilla/dom/MediaKeySession.h"
#include "mozilla/dom/MediaKeyError.h"
#include "mozilla/dom/MediaKeyMessageEvent.h"
#include "mozilla/dom/MediaEncryptedEvent.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/CDMProxy.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Move.h"
+#include "nsContentUtils.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaKeySession,
DOMEventTargetHelper,
mMediaKeyError,
mKeys,
@@ -279,10 +280,29 @@ void
MediaKeySession::DispatchKeyError(uint32_t aSystemCode)
{
RefPtr<MediaKeyError> event(new MediaKeyError(this, aSystemCode));
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, event);
asyncDispatcher->PostDOMEvent();
}
+void
+MediaKeySession::DispatchKeysChange()
+{
+ if (IsClosed()) {
+ return;
+ }
+ DebugOnly<nsresult> rv =
+ nsContentUtils::DispatchTrustedEvent(mKeys->GetOwnerDoc(),
+ this,
+ NS_LITERAL_STRING("keyschange"),
+ false,
+ false);
+#ifdef DEBUG
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch keyschange event");
+ }
+#endif
+}
+
} // namespace dom
} // namespace mozilla
--- a/content/media/eme/MediaKeySession.h
+++ b/content/media/eme/MediaKeySession.h
@@ -79,16 +79,18 @@ public:
already_AddRefed<Promise> GetUsableKeyIds(ErrorResult& aRv);
void DispatchKeyMessage(const nsTArray<uint8_t>& aMessage,
const nsAString& aURL);
void DispatchKeyError(uint32_t system_code);
+ void DispatchKeysChange();
+
void OnClosed();
bool IsClosed() const;
private:
~MediaKeySession();
nsRefPtr<Promise> mClosed;
--- a/content/media/eme/MediaKeys.cpp
+++ b/content/media/eme/MediaKeys.cpp
@@ -586,10 +586,16 @@ CopyArrayBufferViewOrArrayBufferData(con
bufferview.ComputeLengthAndData();
aOutData.AppendElements(bufferview.Data(), bufferview.Length());
} else {
return false;
}
return true;
}
+nsIDocument*
+MediaKeys::GetOwnerDoc() const
+{
+ return mElement ? mElement->OwnerDoc() : nullptr;
+}
+
} // namespace dom
} // namespace mozilla
--- a/content/media/eme/MediaKeys.h
+++ b/content/media/eme/MediaKeys.h
@@ -117,16 +117,20 @@ public:
// Returns true if this MediaKeys has been bound to a media element.
bool IsBoundToMediaElement() const;
// Return NS_OK if the principals are the same as when the MediaKeys
// was created, failure otherwise.
nsresult CheckPrincipals();
+ // Returns a pointer to the bound media element's owner doc.
+ // If we're not bound, this returns null.
+ nsIDocument* GetOwnerDoc() const;
+
private:
static bool IsTypeSupported(const nsAString& aKeySystem,
const Optional<nsAString>& aInitDataType = Optional<nsAString>(),
const Optional<nsAString>& aContentType = Optional<nsAString>());
bool IsInPrivateBrowsing();
already_AddRefed<Promise> Init(ErrorResult& aRv);
--- a/content/media/mediasource/MediaSource.cpp
+++ b/content/media/mediasource/MediaSource.cpp
@@ -17,16 +17,17 @@
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/TimeRanges.h"
#include "mozilla/mozalloc.h"
#include "nsContentTypeParser.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsIRunnable.h"
+#include "nsIScriptObjectPrincipal.h"
#include "nsPIDOMWindow.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "prlog.h"
struct JSContext;
class JSObject;
@@ -389,22 +390,29 @@ MediaSource::GetBuffered(TimeRanges* aBu
MSE_DEBUG("MediaSource(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(intersectionRanges).get());
}
MediaSource::MediaSource(nsPIDOMWindow* aWindow)
: DOMEventTargetHelper(aWindow)
, mDuration(UnspecifiedNaN<double>())
, mDecoder(nullptr)
+ , mPrincipal(nullptr)
, mReadyState(MediaSourceReadyState::Closed)
, mFirstSourceBufferInitialized(false)
{
MOZ_ASSERT(NS_IsMainThread());
mSourceBuffers = new SourceBufferList(this);
mActiveSourceBuffers = new SourceBufferList(this);
+
+ nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
+ if (sop) {
+ mPrincipal = sop->GetPrincipal();
+ }
+
MSE_API("MediaSource(%p)::MediaSource(aWindow=%p) mSourceBuffers=%p mActiveSourceBuffers=%p",
this, aWindow, mSourceBuffers.get(), mActiveSourceBuffers.get());
}
void
MediaSource::SetReadyState(MediaSourceReadyState aState)
{
MOZ_ASSERT(NS_IsMainThread());
--- a/content/media/mediasource/MediaSource.h
+++ b/content/media/mediasource/MediaSource.h
@@ -80,16 +80,21 @@ public:
void SetReadyState(MediaSourceReadyState aState);
// Used by SourceBuffer to call CreateSubDecoder.
MediaSourceDecoder* GetDecoder()
{
return mDecoder;
}
+ nsIPrincipal* GetPrincipal()
+ {
+ return mPrincipal;
+ }
+
// Called by SourceBuffers to notify this MediaSource that data has
// been evicted from the buffered data. The start and end times
// that were evicted are provided.
void NotifyEvicted(double aStart, double aEnd);
// Queue InitializationEvent to run on the main thread. Called when a
// SourceBuffer has an initialization segment appended, but only
// dispatched the first time (using mFirstSourceBufferInitialized).
@@ -123,16 +128,18 @@ private:
double mDuration;
nsRefPtr<SourceBufferList> mSourceBuffers;
nsRefPtr<SourceBufferList> mActiveSourceBuffers;
nsRefPtr<MediaSourceDecoder> mDecoder;
+ nsRefPtr<nsIPrincipal> mPrincipal;
+
MediaSourceReadyState mReadyState;
bool mFirstSourceBufferInitialized;
};
NS_DEFINE_STATIC_IID_ACCESSOR(MediaSource, MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID)
} // namespace dom
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -102,19 +102,19 @@ MediaSourceDecoder::Shutdown()
}
// Kick WaitForData out of its slumber.
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mon.NotifyAll();
}
/*static*/
already_AddRefed<MediaResource>
-MediaSourceDecoder::CreateResource()
+MediaSourceDecoder::CreateResource(nsIPrincipal* aPrincipal)
{
- return nsRefPtr<MediaResource>(new MediaSourceResource()).forget();
+ return nsRefPtr<MediaResource>(new MediaSourceResource(aPrincipal)).forget();
}
void
MediaSourceDecoder::AttachMediaSource(dom::MediaSource* aMediaSource)
{
MOZ_ASSERT(!mMediaSource && !mDecoderStateMachine && NS_IsMainThread());
mMediaSource = aMediaSource;
}
--- a/content/media/mediasource/MediaSourceDecoder.h
+++ b/content/media/mediasource/MediaSourceDecoder.h
@@ -36,17 +36,17 @@ public:
virtual MediaDecoder* Clone() MOZ_OVERRIDE;
virtual MediaDecoderStateMachine* CreateStateMachine() MOZ_OVERRIDE;
virtual nsresult Load(nsIStreamListener**, MediaDecoder*) MOZ_OVERRIDE;
virtual nsresult GetSeekable(dom::TimeRanges* aSeekable) MOZ_OVERRIDE;
virtual void Shutdown() MOZ_OVERRIDE;
- static already_AddRefed<MediaResource> CreateResource();
+ static already_AddRefed<MediaResource> CreateResource(nsIPrincipal* aPrincipal = nullptr);
void AttachMediaSource(dom::MediaSource* aMediaSource);
void DetachMediaSource();
already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType);
void AddTrackBuffer(TrackBuffer* aTrackBuffer);
void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
--- a/content/media/mediasource/MediaSourceResource.h
+++ b/content/media/mediasource/MediaSourceResource.h
@@ -21,22 +21,22 @@ extern PRLogModuleInfo* GetMediaSourceAP
#define UNIMPLEMENTED() MSE_DEBUG("MediaSourceResource(%p): UNIMPLEMENTED FUNCTION at %s:%d", this, __FILE__, __LINE__)
namespace mozilla {
class MediaSourceResource MOZ_FINAL : public MediaResource
{
public:
- MediaSourceResource() {}
+ MediaSourceResource(nsIPrincipal* aPrincipal = nullptr)
+ : mPrincipal(aPrincipal) {}
virtual nsresult Close() MOZ_OVERRIDE { return NS_OK; }
virtual void Suspend(bool aCloseImmediately) MOZ_OVERRIDE { UNIMPLEMENTED(); }
virtual void Resume() MOZ_OVERRIDE { UNIMPLEMENTED(); }
- virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() MOZ_OVERRIDE { UNIMPLEMENTED(); return nullptr; }
virtual bool CanClone() MOZ_OVERRIDE { UNIMPLEMENTED(); return false; }
virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) MOZ_OVERRIDE { UNIMPLEMENTED(); return nullptr; }
virtual void SetReadMode(MediaCacheStream::ReadMode aMode) MOZ_OVERRIDE { UNIMPLEMENTED(); }
virtual void SetPlaybackRate(uint32_t aBytesPerSecond) MOZ_OVERRIDE { UNIMPLEMENTED(); }
virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) MOZ_OVERRIDE { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) MOZ_OVERRIDE { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
virtual nsresult Seek(int32_t aWhence, int64_t aOffset) MOZ_OVERRIDE { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
virtual int64_t Tell() MOZ_OVERRIDE { UNIMPLEMENTED(); return -1; }
@@ -47,16 +47,21 @@ public:
virtual int64_t GetNextCachedData(int64_t aOffset) MOZ_OVERRIDE { UNIMPLEMENTED(); return -1; }
virtual int64_t GetCachedDataEnd(int64_t aOffset) MOZ_OVERRIDE { UNIMPLEMENTED(); return -1; }
virtual bool IsDataCachedToEndOfResource(int64_t aOffset) MOZ_OVERRIDE { UNIMPLEMENTED(); return false; }
virtual bool IsSuspendedByCache() MOZ_OVERRIDE { UNIMPLEMENTED(); return false; }
virtual bool IsSuspended() MOZ_OVERRIDE { UNIMPLEMENTED(); return false; }
virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) MOZ_OVERRIDE { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
virtual nsresult Open(nsIStreamListener** aStreamListener) MOZ_OVERRIDE { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
+ virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() MOZ_OVERRIDE
+ {
+ return nsRefPtr<nsIPrincipal>(mPrincipal).forget();
+ }
+
virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) MOZ_OVERRIDE
{
UNIMPLEMENTED();
aRanges.AppendElement(MediaByteRange(0, GetLength()));
return NS_OK;
}
virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
@@ -71,16 +76,17 @@ private:
return size;
}
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
+ nsRefPtr<nsIPrincipal> mPrincipal;
const nsCString mType;
};
} // namespace mozilla
#undef UNIMPLEMENTED
#endif /* MOZILLA_MEDIASOURCERESOURCE_H_ */
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/1080986.html
@@ -0,0 +1,3 @@
+<html>
+<audio autoplay src="1080986.wav"></audio>
+</html>
new file mode 100644
index 0000000000000000000000000000000000000000..b96c59b7ec2ecdc5033904eb9ceb779adb05d154
GIT binary patch
literal 592
zc$^hcT}YE*6n@_CCl^^QFDjWt+TTUA#c*?Y5lw40`qwd67vdo7rmmVIQK3Yj-2@s~
zS6vi{8Fi6el#pnV&6El;gCHzpHODj!o!_?adwM;G1Lr(E&-vlh1_ObB0@VA%N1HCk
zb`}8;(*LNyrvgBMT_Pufbs<SY@KoS*ur3fPT*IO*O`dY6`=GNN7oxGKq~crVz{*o|
zvo>P2WoMOp`h#pNzsy7VN~KG4s)wz(c}ek#Au)j^vzdMRO)Zo=k79Ff^^Nt#c%Mz-
zy0V>RqC!m@y~bT%nVLrXI)_aae*L*x%iC-bhG-QXe933LrCrprc^7^PCla<!J-@Ok
z7uS1v#p=en+y}K)+{uoLX_T5jxJ?_^1Gxsq)h%XHnIO1Fn^GO9qEBgOmpN!u8}nSn
z8RLGYSILMQ`8KNf%qOBpq*yGP(PP<#S16X7%VG!{;KYD6LA$7Cx4FmxG%~{oPGSs8
zEMpi6eCJt&M3TSQh$Fa)esr>rbErXtLo7s+Uc6=tkHL%Me8e2TQo(w3@GqlC(S{<7
z@|r~JF-Z@0;4$Z!<S^gSl<XO<*(<5p2p4Lx8;dNFznh^071)afPV*nDunTEg+=@Y|
z+lLYq;3NCFOoOvb@U86kkRvi5<_k_qpJZBAI`Uj$DJo@O7u?tnkJS1RMgt=9h8nrG
QPev(BF34@h`CHEY54@=vF#rGn
--- a/content/media/test/crashtests/crashtests.list
+++ b/content/media/test/crashtests/crashtests.list
@@ -69,12 +69,13 @@ load 986901.html
load 990794.html
load 1015662.html
skip-if(Android||B2G) test-pref(media.navigator.permission.disabled,true) load 1028458.html # bug 1048863
load buffer-source-ended-1.html
HTTP load media-element-source-seek-1.html
load offline-buffer-source-ended-1.html
load oscillator-ended-1.html
load oscillator-ended-2.html
+load 1080986.html
include ../../mediasource/test/crashtests/crashtests.list
# This needs to run at the end to avoid leaking busted state into other tests.
skip-if(winWidget) load 691096-1.html
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -644,29 +644,28 @@ var gEMETests = [
keys: {
// "keyid" : "key"
"7e571d017e571d017e571d017e571d01" : "7e5711117e5711117e5711117e571111",
"7e571d027e571d027e571d027e571d02" : "7e5722227e5722227e5722227e572222",
},
sessionType:"temporary",
duration:0.47
},
- // XXX Bug 1082239
- //{
- // name:"gizmo-frag-cencinit.mp4",
- // fragments: [ "gizmo-frag-cencinit.mp4", "gizmo-frag-cenc1.m4s", "gizmo-frag-cenc2.m4s" ],
- // type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
- // keys: {
- // // "keyid" : "key"
- // "7e571d037e571d037e571d037e571d03" : "7e5733337e5733337e5733337e573333",
- // "7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
- // },
- // sessionType:"temporary",
- // duration:2.00,
- //},
+ {
+ name:"gizmo-frag-cencinit.mp4",
+ fragments: [ "gizmo-frag-cencinit.mp4", "gizmo-frag-cenc1.m4s", "gizmo-frag-cenc2.m4s" ],
+ type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
+ keys: {
+ // "keyid" : "key"
+ "7e571d037e571d037e571d037e571d03" : "7e5733337e5733337e5733337e573333",
+ "7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
+ },
+ sessionType:"temporary",
+ duration:2.00,
+ },
];
function checkMetadata(msg, e, test) {
if (test.width) {
is(e.videoWidth, test.width, msg + " video width");
}
if (test.height) {
is(e.videoHeight, test.height, msg + " video height");
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -472,16 +472,17 @@ skip-if = buildapp == 'b2g' # bug 102168
[test_texttrackcue.html]
[test_texttracklist.html]
[test_texttrackregion.html]
[test_timeupdate_small_files.html]
[test_trackelementevent.html]
[test_trackevent.html]
[test_unseekable.html]
[test_video_to_canvas.html]
+[test_video_in_audio_element.html]
[test_videoDocumentTitle.html]
[test_VideoPlaybackQuality.html]
[test_VideoPlaybackQuality_disabled.html]
[test_volume.html]
[test_vttparser.html]
[test_webvtt_disabled.html]
# The tests below contain backend-specific tests. Write backend independent
--- a/content/media/test/test_encryptedMediaExtensions.html
+++ b/content/media/test/test_encryptedMediaExtensions.html
@@ -148,16 +148,35 @@ function PlayTest(test, elem)
return;
}
// This file isn't fragmented; set the media source normally.
elem.src = test.name;
elem.play();
}
+function KeysChangeFunc(session, keys) {
+ session.keyIdsReceived = [];
+ for (var keyid in keys) {
+ info("Set " + keyid + " to false in session.keyIdsReceived");
+ session.keyIdsReceived[keyid] = false;
+ }
+ return function(ev) {
+ var session = ev.target;
+ session.gotKeysChanged = true;
+ session.getUsableKeyIds().then(function(keyIds) {
+ for (var k = 0; k < keyIds.length; k++) {
+ var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
+ ok(kid in session.keyIdsReceived, "session.keyIdsReceived contained " + kid + " as expected.");
+ session.keyIdsReceived[kid] = true;
+ }
+ }, bail("Failed to get keyIds"));
+ }
+}
+
function startTest(test, token)
{
manager.started(test._token);
var v = document.createElement("video");
var gotEncrypted = false;
var gotPlaying = false;
@@ -165,24 +184,27 @@ function startTest(test, token)
gotEncrypted = true;
info(token + " got encrypted event");
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
token + " MediaKeys should support this keysystem");
MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
info(token + " created MediaKeys object ok");
+ mediaKeys.sessions = [];
return v.setMediaKeys(mediaKeys);
}, bail("failed to create MediaKeys object")).then(function() {
info(token + " set MediaKeys on <video> element ok");
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
"MediaKeys should still support keysystem after CDM created...");
var session = v.mediaKeys.createSession(test.sessionType);
+ v.mediaKeys.sessions.push(session);
+ session.addEventListener("keyschange", KeysChangeFunc(session, test.keys), false);
session.addEventListener("message", UpdateSessionFunc(test));
session.generateRequest(ev.initDataType, ev.initData).then(function() {
}, bail(token + " Failed to initialise MediaKeySession"));
}, bail(token + " Failed to set MediaKeys on <video> element"));
});
v.addEventListener("playing", function () { gotPlaying = true; });
@@ -193,16 +215,27 @@ function startTest(test, token)
ok(gotEncrypted, token + " encrypted event should have fired");
ok(gotPlaying, token + " playing event should have fired");
ok(Math.abs(test.duration - v.duration) < 0.1,
token + " Duration of video should be corrrect");
ok(Math.abs(test.duration - v.currentTime) < 0.1,
token + " Current time should be same as duration");
+ // Verify all sessions had all keys went sent the to the CDM usable, and thus
+ // that we received keyschange event(s).
+ var sessions = v.mediaKeys.sessions;
+ is(sessions.length, 1, "should have 1 session");
+ for (var i = 0; i < sessions.length; i++) {
+ var session = sessions[i];
+ ok(session.gotKeysChanged, "Should have received at least one keychange event");
+ for (var kid in session.keyIdsReceived) {
+ ok(session.keyIdsReceived[kid], "key with id " + kid + " was usable as expected");
+ }
+ }
});
v.addEventListener("error", bail(token + " got error event"));
PlayTest(test, v);
}
function testIsTypeSupported()
--- a/content/media/test/test_imagecapture.html
+++ b/content/media/test/test_imagecapture.html
@@ -33,16 +33,17 @@ function gcTest(track) {
};
imageCapture.onerror = function(error) {
ok(false, "takePhoto failure in gc testing");
reject();
};
imageCapture.takePhoto();
}
+ info("Call gc ");
SpecialPowers.gc();
});
}
// Continue calling takePhoto() in rapid succession.
function rapidTest(track) {
return new Promise(function(resolve, reject) {
var imageCapture = new ImageCapture(track);
@@ -106,18 +107,30 @@ function trackTest(track) {
track.enabled = false;
imageCapture.takePhoto()
});
}
function init() {
return new Promise(function(resolve, reject) {
+ var constraints;
+ if (SpecialPowers.Services.appinfo.widgetToolkit == "gonk") {
+ info("B2G ImageCapture test");
+ // Reduce repeat count due to b2g emulator is very slow.
+ repeat = 20;
+ // Use gonk camera, MedieEngine will be the backend of ImageCapture.
+ constraints = {video: true};
+ } else {
+ // use fake camera, MediaStreamGraph will be the backend of ImageCapture.
+ constraints = {video: true, fake: true}
+ }
+
window.navigator.mozGetUserMedia(
- {video: true, fake: true},
+ constraints,
function(stream) {
var track = stream.getVideoTracks()[0];
resolve(track);
},
function(err) {
reject(err);
}
);
@@ -132,20 +145,21 @@ function start() {
info("ImageCapture blob test.");
return blobTest(track);
}).then(function(track) {
info("ImageCapture rapid takePhoto() test.");
return rapidTest(track);
}).then(function(track) {
info("ImageCapture multiple instances test.");
return gcTest(track);
- }).then(function() {
- SimpleTest.finish();
- });
+ }).then(SimpleTest.finish);
}
+SimpleTest.requestCompleteLog();
SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({"set": [["dom.imagecapture.enabled", true]]}, start);
+SpecialPowers.pushPrefEnv({"set": [["dom.imagecapture.enabled", true],
+ ["media.navigator.permission.disabled", true]
+ ]}, start);
</script>
</pre>
</body>
</html>
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_video_in_audio_element.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1060896
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1060896</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript" src="manifest.js"></script>
+ <script type="application/javascript">
+
+ /**
+ * Test for Bug 1060896; tests that loading a video inside an audio element works.
+ **/
+
+ var manager = new MediaTestManager;
+
+ function ended(event) {
+ var a = event.target;
+ removeNodeAndSource(a);
+ manager.finished(a.token);
+ }
+
+ function initTest(test, token) {
+ var a = document.createElement('audio');
+ a.token = token;
+ manager.started(token);
+ a.autoplay = true;
+
+ a.addEventListener("ended", ended, false);
+
+ a.src = test.name;
+ }
+
+ var videos = gSmallTests.filter(function(x){return /^video/.test(x.type);});
+
+ manager.runTests(videos, initTest);
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1060896">Mozilla Bug 1060896</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/content/media/wave/WaveReader.cpp
+++ b/content/media/wave/WaveReader.cpp
@@ -546,27 +546,27 @@ WaveReader::LoadListChunk(uint32_t aChun
// List chunks are always word (two byte) aligned.
NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() % 2 == 0,
"LoadListChunk called with unaligned resource");
static const unsigned int MAX_CHUNK_SIZE = 1 << 16;
static_assert(uint64_t(MAX_CHUNK_SIZE) < UINT_MAX / sizeof(char),
"MAX_CHUNK_SIZE too large for enumerator.");
- if (aChunkSize > MAX_CHUNK_SIZE) {
+ if (aChunkSize > MAX_CHUNK_SIZE || aChunkSize < 4) {
return false;
}
nsAutoArrayPtr<char> chunk(new char[aChunkSize]);
if (!ReadAll(chunk.get(), aChunkSize)) {
return false;
}
static const uint32_t INFO_LIST_MAGIC = 0x494e464f;
- const char *p = chunk.get();
+ const char* p = chunk.get();
if (ReadUint32BE(&p) != INFO_LIST_MAGIC) {
return false;
}
const waveIdToName ID_TO_NAME[] = {
{ 0x49415254, NS_LITERAL_CSTRING("artist") }, // IART
{ 0x49434d54, NS_LITERAL_CSTRING("comments") }, // ICMT
{ 0x49474e52, NS_LITERAL_CSTRING("genre") }, // IGNR
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -68,17 +68,17 @@
#include "nsXULControllers.h"
#include "nsIBoxObject.h"
#include "nsPIBoxObject.h"
#include "XULDocument.h"
#include "nsXULPopupListener.h"
#include "nsRuleWalker.h"
#include "nsIDOMCSSStyleDeclaration.h"
#include "nsCSSParser.h"
-#include "nsIListBoxObject.h"
+#include "ListBoxObject.h"
#include "nsContentUtils.h"
#include "nsContentList.h"
#include "mozilla/InternalMutationEvent.h"
#include "mozilla/MouseEvents.h"
#include "nsIDOMMutationEvent.h"
#include "nsPIDOMWindow.h"
#include "nsJSPrincipals.h"
#include "nsDOMAttributeMap.h"
@@ -104,16 +104,17 @@
#include "nsXBLBinding.h"
#include "mozilla/EventDispatcher.h"
#include "mozAutoDocUpdate.h"
#include "nsIDOMXULCommandEvent.h"
#include "nsCCUncollectableMarker.h"
#include "nsICSSDeclaration.h"
#include "mozilla/dom/XULElementBinding.h"
+#include "mozilla/dom/BoxObject.h"
using namespace mozilla;
using namespace mozilla::dom;
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
uint32_t nsXULPrototypeAttribute::gNumElements;
uint32_t nsXULPrototypeAttribute::gNumAttributes;
uint32_t nsXULPrototypeAttribute::gNumCacheTests;
@@ -1500,17 +1501,17 @@ nsXULElement::GetControllers(ErrorResult
NS_IMETHODIMP
nsXULElement::GetBoxObject(nsIBoxObject** aResult)
{
ErrorResult rv;
*aResult = GetBoxObject(rv).take();
return rv.ErrorCode();
}
-already_AddRefed<nsIBoxObject>
+already_AddRefed<BoxObject>
nsXULElement::GetBoxObject(ErrorResult& rv)
{
// XXX sXBL/XBL2 issue! Owner or current document?
return OwnerDoc()->GetBoxObjectFor(this, rv);
}
// Methods for setting/getting attributes from nsIDOMXULElement
#define NS_IMPL_XUL_STRING_ATTR(_method, _atom) \
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -21,17 +21,16 @@
#include "nsIControllers.h"
#include "nsIDOMElement.h"
#include "nsIDOMXULElement.h"
#include "nsIDOMXULMultSelectCntrlEl.h"
#include "nsIRDFCompositeDataSource.h"
#include "nsIRDFResource.h"
#include "nsIURI.h"
#include "nsIXULTemplateBuilder.h"
-#include "nsIBoxObject.h"
#include "nsLayoutCID.h"
#include "nsAttrAndChildArray.h"
#include "nsGkAtoms.h"
#include "nsAutoPtr.h"
#include "nsStyledElement.h"
#include "nsIFrameLoader.h"
#include "nsFrameLoader.h"
#include "mozilla/dom/DOMRect.h"
@@ -49,16 +48,19 @@ class nsXULPrototypeNode;
typedef nsTArray<nsRefPtr<nsXULPrototypeNode> > nsPrototypeArray;
namespace mozilla {
class EventChainPreVisitor;
class EventListenerManager;
namespace css {
class StyleRule;
}
+namespace dom {
+class BoxObject;
+}
}
namespace JS {
class SourceBufferHolder;
}
////////////////////////////////////////////////////////////////////////
@@ -582,17 +584,17 @@ public:
bool AllowEvents() const
{
return BoolAttrIsTrue(nsGkAtoms::allowevents);
}
already_AddRefed<nsIRDFCompositeDataSource> GetDatabase();
already_AddRefed<nsIXULTemplateBuilder> GetBuilder();
already_AddRefed<nsIRDFResource> GetResource(mozilla::ErrorResult& rv);
nsIControllers* GetControllers(mozilla::ErrorResult& rv);
- already_AddRefed<nsIBoxObject> GetBoxObject(mozilla::ErrorResult& rv);
+ already_AddRefed<mozilla::dom::BoxObject> GetBoxObject(mozilla::ErrorResult& rv);
void Focus(mozilla::ErrorResult& rv);
void Blur(mozilla::ErrorResult& rv);
void Click(mozilla::ErrorResult& rv);
// The XPCOM DoCommand never fails, so it's OK for us.
already_AddRefed<nsINodeList>
GetElementsByAttribute(const nsAString& aAttribute,
const nsAString& aValue);
already_AddRefed<nsINodeList>
--- a/content/xul/document/src/XULDocument.cpp
+++ b/content/xul/document/src/XULDocument.cpp
@@ -39,16 +39,17 @@
#include "nsXMLContentSink.h"
#include "nsXULContentSink.h"
#include "nsXULContentUtils.h"
#include "nsIXULOverlayProvider.h"
#include "nsIStringEnumerator.h"
#include "nsNetUtil.h"
#include "nsParserCIID.h"
#include "nsPIBoxObject.h"
+#include "mozilla/dom/BoxObject.h"
#include "nsXPIDLString.h"
#include "nsPIDOMWindow.h"
#include "nsPIWindowRoot.h"
#include "nsXULCommandDispatcher.h"
#include "nsXULElement.h"
#include "prlog.h"
#include "rdf.h"
#include "nsIFrame.h"
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4190,17 +4190,17 @@ nsDocShell::GetChildSHEntry(int32_t aChi
NS_IMETHODIMP
nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
int32_t aChildOffset, uint32_t loadType,
bool aCloneChildren)
{
nsresult rv;
- if (mLSHE && loadType != LOAD_PUSHSTATE) {
+ if (mLSHE && loadType != LOAD_PUSHSTATE && !aCloneRef) {
/* You get here if you are currently building a
* hierarchy ie.,you just visited a frameset page
*/
nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
if (container) {
rv = container->AddChild(aNewEntry, aChildOffset);
}
}
--- a/docshell/test/test_bug580069.html
+++ b/docshell/test/test_bug580069.html
@@ -11,18 +11,16 @@ https://bugzilla.mozilla.org/show_bug.cg
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=580069">Mozilla Bug 580069</a>
<iframe id='iframe' src='file_bug580069_1.html'></iframe>
<script type="application/javascript">
-SimpleTest.expectAssertions(1);
-
SimpleTest.waitForExplicitFinish();
var iframe = document.getElementById('iframe');
var iframeCw = iframe.contentWindow;
// Called when file_bug580069_1.html loads.
function page1Load() {
// This should cause us to load file 2.
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -81,17 +81,16 @@
#include "nsIDOMCSSSupportsRule.h"
#include "nsIDOMMozCSSKeyframeRule.h"
#include "nsIDOMMozCSSKeyframesRule.h"
#include "nsIDOMCSSCounterStyleRule.h"
#include "nsIDOMCSSPageRule.h"
#include "nsIDOMCSSStyleRule.h"
#include "nsIDOMXULCommandDispatcher.h"
#include "nsIControllers.h"
-#include "nsIBoxObject.h"
#ifdef MOZ_XUL
#include "nsITreeSelection.h"
#include "nsITreeContentView.h"
#include "nsITreeView.h"
#include "nsIXULTemplateBuilder.h"
#include "nsITreeColumns.h"
#endif
#include "nsIDOMXPathNSResolver.h"
@@ -246,18 +245,16 @@ static nsDOMClassInfoData sClassInfoData
// XUL classes
#ifdef MOZ_XUL
NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULCommandDispatcher, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
#endif
NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULControllers, nsNonDOMObjectSH,
DEFAULT_SCRIPTABLE_FLAGS)
- NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(BoxObject, nsDOMGenericSH,
- DEFAULT_SCRIPTABLE_FLAGS)
#ifdef MOZ_XUL
NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(TreeSelection, nsDOMGenericSH,
DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(TreeContentView, nsDOMGenericSH,
DEFAULT_SCRIPTABLE_FLAGS)
#endif
#ifdef MOZ_XUL
@@ -691,20 +688,16 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCommandDispatcher)
DOM_CLASSINFO_MAP_END
#endif
DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULControllers, nsIControllers)
DOM_CLASSINFO_MAP_ENTRY(nsIControllers)
DOM_CLASSINFO_MAP_END
- DOM_CLASSINFO_MAP_BEGIN(BoxObject, nsIBoxObject)
- DOM_CLASSINFO_MAP_ENTRY(nsIBoxObject)
- DOM_CLASSINFO_MAP_END
-
#ifdef MOZ_XUL
DOM_CLASSINFO_MAP_BEGIN(TreeSelection, nsITreeSelection)
DOM_CLASSINFO_MAP_ENTRY(nsITreeSelection)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(TreeContentView, nsITreeContentView)
DOM_CLASSINFO_MAP_ENTRY(nsITreeContentView)
DOM_CLASSINFO_MAP_ENTRY(nsITreeView)
@@ -1387,41 +1380,28 @@ nsDOMClassInfo::ShutDown()
sConstructor_id = JSID_VOID;
sWrappedJSObject_id = JSID_VOID;
NS_IF_RELEASE(sXPConnect);
sIsInitialized = false;
}
-static nsDOMConstructorFunc
-FindConstructorFunc(const nsDOMClassInfoData *aDOMClassInfoData)
-{
- return nullptr;
-}
-
static nsresult
BaseStubConstructor(nsIWeakReference* aWeakOwner,
const nsGlobalNameStruct *name_struct, JSContext *cx,
JS::Handle<JSObject*> obj, const JS::CallArgs &args)
{
MOZ_ASSERT(obj);
MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
nsresult rv;
nsCOMPtr<nsISupports> native;
if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
- const nsDOMClassInfoData* ci_data =
- &sClassInfoData[name_struct->mDOMClassInfoID];
- nsDOMConstructorFunc func = FindConstructorFunc(ci_data);
- if (func) {
- rv = func(getter_AddRefs(native));
- } else {
- rv = NS_ERROR_NOT_AVAILABLE;
- }
+ rv = NS_ERROR_NOT_AVAILABLE;
} else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
native = do_CreateInstance(name_struct->mCID, &rv);
} else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) {
native = do_CreateInstance(name_struct->mAlias->mCID, &rv);
} else {
native = do_CreateInstance(*name_struct->mData->mConstructorCID, &rv);
}
if (NS_FAILED(rv)) {
@@ -1621,17 +1601,17 @@ private:
static bool IsConstructable(const nsDOMClassInfoData *aData)
{
if (IS_EXTERNAL(aData->mCachedClassInfo)) {
const nsExternalDOMClassInfoData* data =
static_cast<const nsExternalDOMClassInfoData*>(aData);
return data->mConstructorCID != nullptr;
}
- return FindConstructorFunc(aData);
+ return nullptr;
}
static bool IsConstructable(const nsGlobalNameStruct *aNameStruct)
{
return
(aNameStruct->mType == nsGlobalNameStruct::eTypeClassConstructor &&
IsConstructable(&sClassInfoData[aNameStruct->mDOMClassInfoID])) ||
(aNameStruct->mType == nsGlobalNameStruct::eTypeExternalClassInfo &&
IsConstructable(aNameStruct->mData)) ||
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -14,17 +14,16 @@ DOMCI_CLASS(CSSImportRule)
DOMCI_CLASS(CSSMediaRule)
DOMCI_CLASS(CSSNameSpaceRule)
// XUL classes
#ifdef MOZ_XUL
DOMCI_CLASS(XULCommandDispatcher)
#endif
DOMCI_CLASS(XULControllers)
-DOMCI_CLASS(BoxObject)
#ifdef MOZ_XUL
DOMCI_CLASS(TreeSelection)
DOMCI_CLASS(TreeContentView)
#endif
#ifdef MOZ_XUL
DOMCI_CLASS(XULTemplateBuilder)
DOMCI_CLASS(XULTreeBuilder)
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -170,16 +170,20 @@ DOMInterfaces = {
'nativeType': 'mozilla::dom::bluetooth::BluetoothPairingHandle',
},
'BluetoothPairingListener': {
'nativeType':
'mozilla::dom::bluetooth::BluetoothPairingListener',
},
+'BoxObject': {
+ 'resultNotAddRefed': ['element'],
+},
+
'CameraCapabilities': {
'nativeType': 'mozilla::dom::CameraCapabilities',
'headerFile': 'DOMCameraCapabilities.h'
},
'CameraControl': {
'nativeType': 'mozilla::nsDOMCameraControl',
'headerFile': 'DOMCameraControl.h',
@@ -861,16 +865,20 @@ DOMInterfaces = {
'headerFile' : 'nsPluginArray.h',
'nativeType': 'nsPluginElement',
},
'PluginArray': {
'nativeType': 'nsPluginArray',
},
+'PopupBoxObject': {
+ 'resultNotAddRefed': ['triggerNode', 'anchorNode'],
+},
+
'Position': {
'headerFile': 'nsGeoPosition.h'
},
'PositionError': {
'headerFile': 'nsGeolocation.h'
},
@@ -1797,31 +1805,30 @@ def addExternalIface(iface, nativeType=N
addExternalIface('ApplicationCache', nativeType='nsIDOMOfflineResourceList')
addExternalIface('Counter')
addExternalIface('CSSRule')
addExternalIface('RTCDataChannel', nativeType='nsIDOMDataChannel')
addExternalIface('HitRegionOptions', nativeType='nsISupports')
addExternalIface('imgINotificationObserver', nativeType='imgINotificationObserver')
addExternalIface('imgIRequest', nativeType='imgIRequest', notflattened=True)
addExternalIface('MenuBuilder', nativeType='nsIMenuBuilder', notflattened=True)
-addExternalIface('MozBoxObject', nativeType='nsIBoxObject')
addExternalIface('MozControllers', nativeType='nsIControllers')
addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
addExternalIface('MozFrameRequestCallback', nativeType='nsIFrameRequestCallback',
notflattened=True)
addExternalIface('MozMmsMessage')
addExternalIface('MozObserver', nativeType='nsIObserver', notflattened=True)
addExternalIface('MozRDFCompositeDataSource', nativeType='nsIRDFCompositeDataSource',
notflattened=True)
addExternalIface('MozRDFResource', nativeType='nsIRDFResource', notflattened=True)
addExternalIface('MozSmsMessage')
-addExternalIface('MozTreeBoxObject', nativeType='nsITreeBoxObject',
- notflattened=True)
addExternalIface('MozTreeColumn', nativeType='nsITreeColumn',
headerFile='nsITreeColumns.h')
+addExternalIface('MozTreeView', nativeType='nsITreeView',
+ headerFile='nsITreeView.h')
addExternalIface('MozWakeLockListener', headerFile='nsIDOMWakeLockListener.h')
addExternalIface('MozXULTemplateBuilder', nativeType='nsIXULTemplateBuilder')
addExternalIface('nsIBrowserDOMWindow', nativeType='nsIBrowserDOMWindow',
notflattened=True)
addExternalIface('nsIControllers', nativeType='nsIControllers')
addExternalIface('nsIDOMCrypto', nativeType='nsIDOMCrypto',
headerFile='Crypto.h')
addExternalIface('nsIInputStreamCallback', nativeType='nsIInputStreamCallback',
@@ -1830,16 +1837,17 @@ addExternalIface('nsIFile', nativeType='
addExternalIface('nsIMessageBroadcaster', nativeType='nsIMessageBroadcaster',
headerFile='nsIMessageManager.h', notflattened=True)
addExternalIface('nsISelectionListener', nativeType='nsISelectionListener')
addExternalIface('nsIStreamListener', nativeType='nsIStreamListener', notflattened=True)
addExternalIface('nsISupports', nativeType='nsISupports')
addExternalIface('nsIDocShell', nativeType='nsIDocShell', notflattened=True)
addExternalIface('nsIEditor', nativeType='nsIEditor', notflattened=True)
addExternalIface('nsIVariant', nativeType='nsIVariant', notflattened=True)
+addExternalIface('nsIScriptableRegion', nativeType='nsIScriptableRegion', notflattened=True)
addExternalIface('OutputStream', nativeType='nsIOutputStream',
notflattened=True)
addExternalIface('Principal', nativeType='nsIPrincipal',
headerFile='nsIPrincipal.h', notflattened=True)
addExternalIface('StackFrame', nativeType='nsIStackFrame',
headerFile='nsIException.h', notflattened=True)
addExternalIface('URI', nativeType='nsIURI', headerFile='nsIURI.h',
notflattened=True)
--- a/dom/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth2/BluetoothAdapter.cpp
@@ -9,16 +9,17 @@
#include "BluetoothUtils.h"
#include "DOMRequest.h"
#include "nsTArrayHelpers.h"
#include "mozilla/dom/BluetoothAdapter2Binding.h"
#include "mozilla/dom/BluetoothAttributeEvent.h"
#include "mozilla/dom/BluetoothStatusChangedEvent.h"
#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/File.h"
#include "mozilla/dom/bluetooth/BluetoothAdapter.h"
#include "mozilla/dom/bluetooth/BluetoothClassOfDevice.h"
#include "mozilla/dom/bluetooth/BluetoothDevice.h"
#include "mozilla/dom/bluetooth/BluetoothDiscoveryHandle.h"
#include "mozilla/dom/bluetooth/BluetoothPairingListener.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
@@ -1020,17 +1021,17 @@ BluetoothAdapter::Disconnect(BluetoothDe
}
bs->Disconnect(address, serviceUuid, results);
return request.forget();
}
already_AddRefed<DOMRequest>
BluetoothAdapter::SendFile(const nsAString& aDeviceAddress,
- nsIDOMBlob* aBlob, ErrorResult& aRv)
+ File& aBlob, ErrorResult& aRv)
{
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
if (!win) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<DOMRequest> request = new DOMRequest(win);
@@ -1040,25 +1041,25 @@ BluetoothAdapter::SendFile(const nsAStri
BluetoothService* bs = BluetoothService::Get();
if (!bs) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
if (XRE_GetProcessType() == GeckoProcessType_Default) {
// In-process transfer
- bs->SendFile(aDeviceAddress, aBlob, results);
+ bs->SendFile(aDeviceAddress, &aBlob, results);
} else {
ContentChild *cc = ContentChild::GetSingleton();
if (!cc) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
- BlobChild* actor = cc->GetOrCreateActorForBlob(aBlob);
+ BlobChild* actor = cc->GetOrCreateActorForBlob(&aBlob);
if (!actor) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
bs->SendFile(aDeviceAddress, nullptr, actor, results);
}
--- a/dom/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth2/BluetoothAdapter.h
@@ -13,16 +13,17 @@
#include "mozilla/dom/BluetoothAdapter2Binding.h"
#include "mozilla/dom/BluetoothDeviceEvent.h"
#include "mozilla/dom/Promise.h"
#include "nsCOMPtr.h"
namespace mozilla {
namespace dom {
class DOMRequest;
+class File;
struct MediaMetaData;
struct MediaPlayStatus;
}
}
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothDevice;
@@ -119,17 +120,17 @@ public:
Disconnect(BluetoothDevice& aDevice,
const Optional<short unsigned int>& aServiceUuid,
ErrorResult& aRv);
already_AddRefed<DOMRequest> GetConnectedDevices(uint16_t aServiceUuid,
ErrorResult& aRv);
// OPP file transfer related methods
already_AddRefed<DOMRequest> SendFile(const nsAString& aDeviceAddress,
- nsIDOMBlob* aBlob,
+ File& aBlob,
ErrorResult& aRv);
already_AddRefed<DOMRequest> StopSendingFile(const nsAString& aDeviceAddress,
ErrorResult& aRv);
already_AddRefed<DOMRequest>
ConfirmReceivingFile(const nsAString& aDeviceAddress,
bool aConfirmation,
ErrorResult& aRv);
--- a/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
@@ -10,36 +10,37 @@
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUtils.h"
#include "BluetoothUuid.h"
#include "ObexBase.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/File.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsAutoPtr.h"
#include "nsCExternalHandlerService.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
-#include "nsIDOMFile.h"
#include "nsIFile.h"
#include "nsIInputStream.h"
#include "nsIMIMEService.h"
#include "nsIOutputStream.h"
#include "nsIVolumeService.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#define TARGET_SUBDIR "Download/Bluetooth/"
USING_BLUETOOTH_NAMESPACE
using namespace mozilla;
+using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace {
// Sending system message "bluetooth-opp-update-progress" every 50kb
static const uint32_t kUpdateProgressBase = 50 * 1024;
/*
* The format of the header of an PUT request is
@@ -344,17 +345,18 @@ BluetoothOppManager::StartSendingNextFil
}
bool
BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
BlobParent* aActor)
{
MOZ_ASSERT(NS_IsMainThread());
- nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
+ nsRefPtr<FileImpl> impl = aActor->GetBlobImpl();
+ nsCOMPtr<nsIDOMBlob> blob = new File(nullptr, impl);
return SendFile(aDeviceAddress, blob.get());
}
bool
BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
nsIDOMBlob* aBlob)
{
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
@@ -820,17 +820,17 @@ public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
{
sBondingRunnableArray.RemoveElement(mRunnable);
ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("CreatedPairedDevice"));
}
private:
- BluetoothReplyRunnable* mRunnable;
+ nsRefPtr<BluetoothReplyRunnable> mRunnable;
};
nsresult
BluetoothServiceBluedroid::CreatePairedDeviceInternal(
const nsAString& aDeviceAddress, int aTimeout,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
@@ -855,17 +855,17 @@ public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
{
sUnbondingRunnableArray.RemoveElement(mRunnable);
ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("RemoveDevice"));
}
private:
- BluetoothReplyRunnable* mRunnable;
+ nsRefPtr<BluetoothReplyRunnable> mRunnable;
};
nsresult
BluetoothServiceBluedroid::RemoveDeviceInternal(
const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
@@ -363,24 +363,37 @@ public:
{
MOZ_ASSERT(NS_IsMainThread());
if (mImpl->IsShutdownOnMainThread()) {
BT_LOGD("mConsumer is null, aborting receive!");
return;
}
+ if (aConnectionStatus != 0) {
+ mImpl->mConsumer->NotifyError();
+ return;
+ }
+
mImpl->mConsumer->SetAddress(aBdAddress);
XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new AcceptTask(mImpl, aFd));
}
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("BluetoothSocketInterface::Accept failed: %d", (int)aStatus);
+
+ if (!mImpl->IsShutdownOnMainThread()) {
+ // Instead of NotifyError(), call NotifyDisconnect() to trigger
+ // BluetoothOppManager::OnSocketDisconnect() as
+ // DroidSocketImpl::OnFileCanReadWithoutBlocking() in Firefox OS 2.0 in
+ // order to keep the same behavior and reduce regression risk.
+ mImpl->mConsumer->NotifyDisconnect();
+ }
}
private:
DroidSocketImpl* mImpl;
};
class AcceptRunnable MOZ_FINAL : public SocketIORunnable<DroidSocketImpl>
{
@@ -499,27 +512,43 @@ public:
MOZ_ASSERT(mImpl);
}
void Connect(int aFd, const nsAString& aBdAddress,
int aConnectionStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
- if (!mImpl->IsShutdownOnMainThread()) {
- mImpl->mConsumer->SetAddress(aBdAddress);
+ if (mImpl->IsShutdownOnMainThread()) {
+ BT_LOGD("mConsumer is null, aborting send!");
+ return;
}
+
+ if (aConnectionStatus != 0) {
+ mImpl->mConsumer->NotifyError();
+ return;
+ }
+
+ mImpl->mConsumer->SetAddress(aBdAddress);
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
new SocketConnectTask(mImpl, aFd));
}
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
BT_WARNING("Connect failed: %d", (int)aStatus);
+
+ if (!mImpl->IsShutdownOnMainThread()) {
+ // Instead of NotifyError(), call NotifyDisconnect() to trigger
+ // BluetoothOppManager::OnSocketDisconnect() as
+ // DroidSocketImpl::OnFileCanReadWithoutBlocking() in Firefox OS 2.0 in
+ // order to keep the same behavior and reduce regression risk.
+ mImpl->mConsumer->NotifyDisconnect();
+ }
}
private:
DroidSocketImpl* mImpl;
};
bool
BluetoothSocket::ConnectSocket(const nsAString& aDeviceAddress, int aChannel)
--- a/dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.cpp
@@ -281,17 +281,17 @@ private:
iv.iov_len = MSG1_SIZE;
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iv;
msg.msg_iovlen = 1;
ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
- if (res < 0) {
+ if (res <= 0) {
return STATUS_FAIL;
}
mLen += res;
return STATUS_SUCCESS;
}
@@ -306,17 +306,17 @@ private:
struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iv;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
- if (res < 0) {
+ if (res <= 0) {
return STATUS_FAIL;
}
mLen += res;
if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
return STATUS_FAIL;
}
--- a/dom/bluetooth2/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothOppManager.cpp
@@ -10,36 +10,37 @@
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUtils.h"
#include "BluetoothUuid.h"
#include "ObexBase.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/File.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsAutoPtr.h"
#include "nsCExternalHandlerService.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
-#include "nsIDOMFile.h"
#include "nsIFile.h"
#include "nsIInputStream.h"
#include "nsIMIMEService.h"
#include "nsIOutputStream.h"
#include "nsIVolumeService.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#define TARGET_SUBDIR "Download/Bluetooth/"
USING_BLUETOOTH_NAMESPACE
using namespace mozilla;
+using namespace mozilla::dom;
using namespace mozilla::ipc;
using mozilla::TimeDuration;
using mozilla::TimeStamp;
namespace {
// Sending system message "bluetooth-opp-update-progress" every 50kb
static const uint32_t kUpdateProgressBase = 50 * 1024;
@@ -366,17 +367,18 @@ BluetoothOppManager::StartSendingNextFil
}
bool
BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
BlobParent* aActor)
{
MOZ_ASSERT(NS_IsMainThread());
- nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
+ nsRefPtr<FileImpl> impl = aActor->GetBlobImpl();
+ nsCOMPtr<nsIDOMBlob> blob = new File(nullptr, impl);
return SendFile(aDeviceAddress, blob.get());
}
bool
BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
nsIDOMBlob* aBlob)
{
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -3199,17 +3199,17 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
// in the font, and adjust accordingly.
// (The same will be true for HTML text layout.)
const gfxFont::Metrics& metrics = mTextRun->GetFontGroup()->
GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal);
mCtx->mTarget->SetTransform(mCtx->mTarget->GetTransform().Copy().
PreTranslate(baselineOrigin). // translate origin for rotation
PreRotate(gfx::Float(M_PI / 2.0)). // turn 90deg clockwise
PreTranslate(-baselineOrigin). // undo the translation
- PreTranslate(Point(0, metrics.emAscent - metrics.emDescent) / 2));
+ PreTranslate(Point(0, (metrics.emAscent - metrics.emDescent) / 2)));
// and offset the (alphabetic) baseline of the
// horizontally-shaped text from the (centered)
// default baseline used for vertical
}
RefPtr<GlyphRenderingOptions> renderingOptions = font->GetGlyphRenderingOptions();
GlyphBuffer buffer;
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -73,17 +73,18 @@ WebGLContext::InitWebGL2()
WebGLExtensionID::OES_texture_half_float,
WebGLExtensionID::OES_texture_half_float_linear,
WebGLExtensionID::OES_vertex_array_object,
WebGLExtensionID::WEBGL_depth_texture,
WebGLExtensionID::WEBGL_draw_buffers
};
const GLFeature sFeatureRequiredArr[] = {
GLFeature::instanced_non_arrays,
- GLFeature::transform_feedback2
+ GLFeature::transform_feedback2,
+ GLFeature::invalidate_framebuffer
};
// check WebGL extensions that are supposed to be natively supported
for (size_t i = 0; i < size_t(MOZ_ARRAY_LENGTH(sExtensionNativelySupportedArr)); i++)
{
WebGLExtensionID extension = sExtensionNativelySupportedArr[i];
if (!IsExtensionSupported(extension)) {
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -243,13 +243,14 @@ public:
private:
WebGL2Context();
bool ValidateSizedInternalFormat(GLenum internalFormat, const char* info);
bool ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
GLsizei width, GLsizei height, GLsizei depth,
const char* info);
+ JS::Value GetTexParameterInternal(const TexTarget& target, GLenum pname) MOZ_OVERRIDE;
};
} // namespace mozilla
#endif
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -27,27 +27,83 @@ WebGL2Context::FramebufferTextureLayer(G
}
void
WebGL2Context::GetInternalformatParameter(JSContext*, GLenum target, GLenum internalformat, GLenum pname, JS::MutableHandleValue retval)
{
MOZ_CRASH("Not Implemented.");
}
+// Map attachments intended for the default buffer, to attachments for a non-
+// default buffer.
+static void
+TranslateDefaultAttachments(const dom::Sequence<GLenum>& in, dom::Sequence<GLenum>* out)
+{
+ for (size_t i = 0; i < in.Length(); i++) {
+ switch (in[i]) {
+ case LOCAL_GL_COLOR:
+ out->AppendElement(LOCAL_GL_COLOR_ATTACHMENT0);
+ break;
+ case LOCAL_GL_DEPTH:
+ out->AppendElement(LOCAL_GL_DEPTH_ATTACHMENT);
+ break;
+ case LOCAL_GL_STENCIL:
+ out->AppendElement(LOCAL_GL_STENCIL_ATTACHMENT);
+ break;
+ }
+ }
+}
+
void
WebGL2Context::InvalidateFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments)
{
- MOZ_CRASH("Not Implemented.");
+ if (IsContextLost())
+ return;
+ MakeContextCurrent();
+
+ if (target != LOCAL_GL_FRAMEBUFFER)
+ return ErrorInvalidEnumInfo("invalidateFramebuffer: target", target);
+ for (size_t i = 0; i < attachments.Length(); i++) {
+ if (!ValidateFramebufferAttachment(attachments[i], "invalidateFramebuffer"))
+ return;
+ }
+
+ if (!mBoundFramebuffer && !gl->IsDrawingToDefaultFramebuffer()) {
+ dom::Sequence<GLenum> tmpAttachments;
+ TranslateDefaultAttachments(attachments, &tmpAttachments);
+ gl->fInvalidateFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements());
+ } else {
+ gl->fInvalidateFramebuffer(target, attachments.Length(), attachments.Elements());
+ }
}
void
-WebGL2Context::InvalidateSubFramebuffer (GLenum target, const dom::Sequence<GLenum>& attachments,
- GLint x, GLint y, GLsizei width, GLsizei height)
+WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
+ GLint x, GLint y, GLsizei width, GLsizei height)
{
- MOZ_CRASH("Not Implemented.");
+ if (IsContextLost())
+ return;
+ MakeContextCurrent();
+
+ if (target != LOCAL_GL_FRAMEBUFFER)
+ return ErrorInvalidEnumInfo("invalidateFramebuffer: target", target);
+ for (size_t i = 0; i < attachments.Length(); i++) {
+ if (!ValidateFramebufferAttachment(attachments[i], "invalidateSubFramebuffer"))
+ return;
+ }
+
+ if (!mBoundFramebuffer && !gl->IsDrawingToDefaultFramebuffer()) {
+ dom::Sequence<GLenum> tmpAttachments;
+ TranslateDefaultAttachments(attachments, &tmpAttachments);
+ gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements(),
+ x, y, width, height);
+ } else {
+ gl->fInvalidateSubFramebuffer(target, attachments.Length(), attachments.Elements(),
+ x, y, width, height);
+ }
}
void
WebGL2Context::ReadBuffer(GLenum mode)
{
MOZ_CRASH("Not Implemented.");
}
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -287,17 +287,17 @@ WebGL2Context::TexSubImage3D(GLenum rawT
yoffset == 0 &&
zoffset == 0 &&
width == imageInfo.Width() &&
height == imageInfo.Height() &&
depth == imageInfo.Depth();
if (coversWholeImage) {
tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
} else {
- tex->DoDeferredImageInitialization(texImageTarget, level);
+ tex->EnsureNoUninitializedImageData(texImageTarget, level);
}
}
GLenum driverType = LOCAL_GL_NONE;
GLenum driverInternalFormat = LOCAL_GL_NONE;
GLenum driverFormat = LOCAL_GL_NONE;
DriverFormatsFromEffectiveInternalFormat(gl,
existingEffectiveInternalFormat,
@@ -340,8 +340,22 @@ WebGL2Context::CompressedTexImage3D(GLen
void
WebGL2Context::CompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
GLsizei width, GLsizei height, GLsizei depth,
GLenum format, GLsizei imageSize, const dom::ArrayBufferView& data)
{
MOZ_CRASH("Not Implemented.");
}
+
+JS::Value
+WebGL2Context::GetTexParameterInternal(const TexTarget& target, GLenum pname)
+{
+ switch (pname) {
+ case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
+ {
+ GLint i = 0;
+ gl->fGetTexParameteriv(target.get(), pname, &i);
+ return JS::NumberValue(uint32_t(i));
+ }
+ }
+ return WebGLContext::GetTexParameterInternal(target, pname);
+}
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -985,16 +985,18 @@ protected:
void UndoFakeVertexAttrib0();
static CheckedUint32 GetImageSize(GLsizei height,
GLsizei width,
GLsizei depth,
uint32_t pixelSize,
uint32_t alignment);
+ virtual JS::Value GetTexParameterInternal(const TexTarget& target, GLenum pname);
+
// Returns x rounded to the next highest multiple of y.
static CheckedUint32 RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y) {
return ((x + y - 1) / y) * y;
}
nsRefPtr<gl::GLContext> gl;
CheckedUint32 mGeneration;
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -448,17 +448,17 @@ WebGLContext::CopyTexSubImage2D_base(Tex
// the rect doesn't fit in the framebuffer
// first, we initialize the texture as black
if (!sub) {
tex->SetImageInfo(texImageTarget, level, width, height, 1,
effectiveInternalFormat,
WebGLImageDataStatus::UninitializedImageData);
- tex->DoDeferredImageInitialization(texImageTarget, level);
+ tex->EnsureNoUninitializedImageData(texImageTarget, level);
}
// if we are completely outside of the framebuffer, we can exit now with our black texture
if ( x >= framebufferWidth
|| x+width <= 0
|| y >= framebufferHeight
|| y+height <= 0)
{
@@ -596,17 +596,17 @@ WebGLContext::CopyTexSubImage2D(GLenum r
if (imageInfo.HasUninitializedImageData()) {
bool coversWholeImage = xoffset == 0 &&
yoffset == 0 &&
width == texWidth &&
height == texHeight;
if (coversWholeImage) {
tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
} else {
- tex->DoDeferredImageInitialization(texImageTarget, level);
+ tex->EnsureNoUninitializedImageData(texImageTarget, level);
}
}
TexInternalFormat internalformat;
TexType type;
UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(imageInfo.EffectiveInternalFormat(),
&internalformat, &type);
return CopyTexSubImage2D_base(texImageTarget, level, internalformat, xoffset, yoffset, x, y, width, height, true);
@@ -922,17 +922,17 @@ WebGLContext::GenerateMipmap(GLenum rawT
const TexImageTarget imageTarget = (target == LOCAL_GL_TEXTURE_2D)
? LOCAL_GL_TEXTURE_2D
: LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
if (!tex->HasImageInfoAt(imageTarget, 0))
{
return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
}
- if (!tex->IsFirstImagePowerOfTwo())
+ if (!IsWebGL2() && !tex->IsFirstImagePowerOfTwo())
return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
TexInternalFormat internalformat = tex->ImageInfoAt(imageTarget, 0).EffectiveInternalFormat();
if (IsTextureFormatCompressed(internalformat))
return ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed.");
if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
(IsGLDepthFormat(internalformat) || IsGLDepthStencilFormat(internalformat)))
@@ -1606,16 +1606,22 @@ WebGLContext::GetTexParameter(GLenum raw
const TexTarget target(rawTarget);
if (!activeBoundTextureForTarget(target)) {
ErrorInvalidOperation("getTexParameter: no texture bound");
return JS::NullValue();
}
+ return GetTexParameterInternal(target, pname);
+}
+
+JS::Value
+WebGLContext::GetTexParameterInternal(const TexTarget& target, GLenum pname)
+{
switch (pname) {
case LOCAL_GL_TEXTURE_MIN_FILTER:
case LOCAL_GL_TEXTURE_MAG_FILTER:
case LOCAL_GL_TEXTURE_WRAP_S:
case LOCAL_GL_TEXTURE_WRAP_T:
{
GLint i = 0;
gl->fGetTexParameteriv(target.get(), pname, &i);
@@ -3425,17 +3431,17 @@ WebGLContext::CompressedTexSubImage2D(GL
if (levelInfo.HasUninitializedImageData()) {
bool coversWholeImage = xoffset == 0 &&
yoffset == 0 &&
width == levelInfo.Width() &&
height == levelInfo.Height();
if (coversWholeImage) {
tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
} else {
- tex->DoDeferredImageInitialization(texImageTarget, level);
+ tex->EnsureNoUninitializedImageData(texImageTarget, level);
}
}
MakeContextCurrent();
gl->fCompressedTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, width, height, internalformat, byteLength, view.Data());
}
JS::Value
@@ -3961,17 +3967,17 @@ WebGLContext::TexSubImage2D_base(TexImag
if (imageInfo.HasUninitializedImageData()) {
bool coversWholeImage = xoffset == 0 &&
yoffset == 0 &&
width == imageInfo.Width() &&
height == imageInfo.Height();
if (coversWholeImage) {
tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
} else {
- tex->DoDeferredImageInitialization(texImageTarget, level);
+ tex->EnsureNoUninitializedImageData(texImageTarget, level);
}
}
MakeContextCurrent();
size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
uint32_t dstTexelSize = GetBitsPerTexel(existingEffectiveInternalFormat) / 8;
size_t dstPlainRowSize = dstTexelSize * width;
// There are checks above to ensure that this won't overflow.
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -172,16 +172,28 @@ WebGLContext::GetParameter(JSContext* cx
if (mBoundVertexArray == mDefaultVertexArray){
return WebGLObjectAsJSValue(cx, (WebGLVertexArray *) nullptr, rv);
}
return WebGLObjectAsJSValue(cx, mBoundVertexArray.get(), rv);
}
}
+ if (IsWebGL2()) {
+ switch (pname) {
+ case LOCAL_GL_MAX_SAMPLES:
+ case LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE:
+ case LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS: {
+ GLint val;
+ gl->fGetIntegerv(pname, &val);
+ return JS::NumberValue(uint32_t(val));
+ }
+ }
+ }
+
switch (pname) {
//
// String params
//
case LOCAL_GL_VENDOR:
return StringValue(cx, "Mozilla", rv);
case LOCAL_GL_RENDERER:
return StringValue(cx, "Mozilla", rv);
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -334,16 +334,28 @@ bool WebGLContext::ValidateGLSLString(co
/**
* Return true if the framebuffer attachment is valid. Attachment must
* be one of depth/stencil/depth_stencil/color attachment.
*/
bool
WebGLContext::ValidateFramebufferAttachment(GLenum attachment, const char* funcName)
{
+ if (!mBoundFramebuffer) {
+ switch (attachment) {
+ case LOCAL_GL_COLOR:
+ case LOCAL_GL_DEPTH:
+ case LOCAL_GL_STENCIL:
+ return true;
+ default:
+ ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", funcName, attachment);
+ return false;
+ }
+ }
+
if (attachment == LOCAL_GL_DEPTH_ATTACHMENT ||
attachment == LOCAL_GL_STENCIL_ATTACHMENT ||
attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
{
return true;
}
GLenum colorAttachCount = 1;
@@ -799,18 +811,20 @@ WebGLContext::ValidateTexImageSize(TexIm
InfoFrom(func, dims), level, maxTexImageSize);
return false;
}
/* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
* "If level is greater than zero, and either width or
* height is not a power-of-two, the error INVALID_VALUE is
* generated."
+ *
+ * This restriction does not apply to GL ES Version 3.0+.
*/
- if (level > 0) {
+ if (!IsWebGL2() && level > 0) {
if (!is_pot_assuming_nonnegative(width)) {
ErrorInvalidValue("%s: level >= 0, width of %d must be a power of two.",
InfoFrom(func, dims), width);
return false;
}
if (!is_pot_assuming_nonnegative(height)) {
ErrorInvalidValue("%s: level >= 0, height of %d must be a power of two.",
@@ -822,17 +836,17 @@ WebGLContext::ValidateTexImageSize(TexIm
// TODO: WebGL 2
if (texImageTarget == LOCAL_GL_TEXTURE_3D) {
if (depth < 0) {
ErrorInvalidValue("%s: depth must be >= 0", InfoFrom(func, dims));
return false;
}
- if (depth > 0 && !is_pot_assuming_nonnegative(depth)) {
+ if (!IsWebGL2() && !is_pot_assuming_nonnegative(depth)) {
ErrorInvalidValue("%s: level >= 0, depth of %d must be a power of two.",
InfoFrom(func, dims), depth);
return false;
}
}
return true;
}
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -181,29 +181,29 @@ void
WebGLTexture::SetCustomMipmap() {
if (mHaveGeneratedMipmap) {
// if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode,
// we need to compute now all the mipmap image info.
// since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info,
// and are power-of-two.
ImageInfo imageInfo = ImageInfoAtFace(0, 0);
- NS_ASSERTION(imageInfo.IsPowerOfTwo(), "this texture is NPOT, so how could GenerateMipmap() ever accept it?");
+ NS_ASSERTION(mContext->IsWebGL2() || imageInfo.IsPowerOfTwo(),
+ "this texture is NPOT, so how could GenerateMipmap() ever accept it?");
GLsizei size = std::max(std::max(imageInfo.mWidth, imageInfo.mHeight), imageInfo.mDepth);
- // so, the size is a power of two, let's find its log in base 2.
+ // Find floor(log2(size)). (ES 3.0.4, 3.8 - Mipmapping).
size_t maxLevel = 0;
for (GLsizei n = size; n > 1; n >>= 1)
++maxLevel;
EnsureMaxLevelWithCustomImagesAtLeast(maxLevel);
for (size_t level = 1; level <= maxLevel; ++level) {
- // again, since the sizes are powers of two, no need for any max(1,x) computation
imageInfo.mWidth = std::max(imageInfo.mWidth / 2, 1);
imageInfo.mHeight = std::max(imageInfo.mHeight / 2, 1);
imageInfo.mDepth = std::max(imageInfo.mDepth / 2, 1);
for(size_t face = 0; face < mFacesCount; ++face)
ImageInfoAtFace(face, level) = imageInfo;
}
}
mHaveGeneratedMipmap = false;
@@ -280,67 +280,69 @@ WebGLTexture::ResolvedFakeBlackStatus()
int dim = mTarget == LOCAL_GL_TEXTURE_2D ? 2 : 3;
if (DoesMinFilterRequireMipmap())
{
if (!IsMipmapComplete()) {
mContext->GenerateWarning
("%s is a %dD texture, with a minification filter requiring a mipmap, "
"and is not mipmap complete (as defined in section 3.7.10).", msg_rendering_as_black, dim);
mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
- } else if (!ImageInfoBase().IsPowerOfTwo()) {
+ } else if (!mContext->IsWebGL2() && !ImageInfoBase().IsPowerOfTwo()) {
mContext->GenerateWarning
("%s is a %dD texture, with a minification filter requiring a mipmap, "
"and either its width or height is not a power of two.", msg_rendering_as_black);
mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
}
}
else // no mipmap required
{
if (!ImageInfoBase().IsPositive()) {
mContext->GenerateWarning
("%s is a %dD texture and its width or height is equal to zero.",
msg_rendering_as_black, dim);
mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
- } else if (!AreBothWrapModesClampToEdge() && !ImageInfoBase().IsPowerOfTwo()) {
+ } else if (!AreBothWrapModesClampToEdge() && !mContext->IsWebGL2() && !ImageInfoBase().IsPowerOfTwo()) {
mContext->GenerateWarning
("%s is a %dD texture, with a minification filter not requiring a mipmap, "
"with its width or height not a power of two, and with a wrap mode "
"different from CLAMP_TO_EDGE.", msg_rendering_as_black, dim);
mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
}
}
}
else // cube map
{
- bool areAllLevel0ImagesPOT = true;
- for (size_t face = 0; face < mFacesCount; ++face)
- areAllLevel0ImagesPOT &= ImageInfoAtFace(face, 0).IsPowerOfTwo();
+ bool legalImageSize = true;
+ if (!mContext->IsWebGL2()) {
+ for (size_t face = 0; face < mFacesCount; ++face)
+ legalImageSize &= ImageInfoAtFace(face, 0).IsPowerOfTwo();
+ }
if (DoesMinFilterRequireMipmap())
{
if (!IsMipmapCubeComplete()) {
mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, "
"and is not mipmap cube complete (as defined in section 3.7.10).",
msg_rendering_as_black);
mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
- } else if (!areAllLevel0ImagesPOT) {
+ } else if (!legalImageSize) {
mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, "
"and either the width or the height of some level 0 image is not a power of two.",
msg_rendering_as_black);
mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
}
}
else // no mipmap required
{
if (!IsCubeComplete()) {
mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, "
"and is not cube complete (as defined in section 3.7.10).",
msg_rendering_as_black);
mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
- } else if (!AreBothWrapModesClampToEdge() && !areAllLevel0ImagesPOT) {
+ } else if (!AreBothWrapModesClampToEdge() && !legalImageSize) {
mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, "
"with some level 0 image having width or height not a power of two, and with a wrap mode "
"different from CLAMP_TO_EDGE.", msg_rendering_as_black);
mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
}
}
}
@@ -417,17 +419,17 @@ WebGLTexture::ResolvedFakeBlackStatus()
// in this case we know that we can't be dealing with a depth texture per WEBGL_depth_texture
// and ANGLE_depth_texture (which allow only one image per texture) so we can assume that
// glTexImage2D is able to upload data to images.
for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
for (size_t face = 0; face < mFacesCount; ++face) {
TexImageTarget imageTarget = TexImageTargetForTargetAndFace(mTarget, face);
const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
if (imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData) {
- DoDeferredImageInitialization(imageTarget, level);
+ EnsureNoUninitializedImageData(imageTarget, level);
}
}
}
mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
} else {
// The texture only contains uninitialized image data. In this case,
// we can use a black texture for it.
mFakeBlackStatus = WebGLTextureFakeBlackStatus::UninitializedImageData;
@@ -537,20 +539,21 @@ ClearWithTempFB(WebGLContext* context, G
mask |= LOCAL_GL_COLOR_BUFFER_BIT;
// Last chance!
return ClearByMask(context, mask);
}
void
-WebGLTexture::DoDeferredImageInitialization(TexImageTarget imageTarget, GLint level)
+WebGLTexture::EnsureNoUninitializedImageData(TexImageTarget imageTarget, GLint level)
{
const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
- MOZ_ASSERT(imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
+ if (!imageInfo.HasUninitializedImageData())
+ return;
mContext->MakeContextCurrent();
// Try to clear with glCLear.
if (imageTarget == LOCAL_GL_TEXTURE_2D) {
bool cleared = ClearWithTempFB(mContext, GLName(),
imageTarget, level,
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -194,17 +194,17 @@ public:
MOZ_ASSERT(newStatus != WebGLImageDataStatus::NoImageData ||
imageInfo.mImageDataStatus == WebGLImageDataStatus::NoImageData);
if (imageInfo.mImageDataStatus != newStatus) {
SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
}
imageInfo.mImageDataStatus = newStatus;
}
- void DoDeferredImageInitialization(TexImageTarget imageTarget, GLint level);
+ void EnsureNoUninitializedImageData(TexImageTarget imageTarget, GLint level);
protected:
TexMinFilter mMinFilter;
TexMagFilter mMagFilter;
TexWrap mWrapS, mWrapT;
size_t mFacesCount, mMaxLevelWithCustomImages;
--- a/dom/canvas/test/webgl-mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest.ini
@@ -19,8 +19,10 @@ skip-if = toolkit == 'android' #bug 8654
[webgl-mochitest/test_webgl_conformance.html]
skip-if = buildapp == 'mulet' || toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
[webgl-mochitest/test_webgl_request_context.html]
skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
[webgl-mochitest/test_webgl_request_mismatch.html]
skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
[webgl-mochitest/test_webgl2_not_exposed.html]
skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
+[webgl-mochitest/test_webgl2_invalidate_framebuffer.html]
+skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/test_webgl2_invalidate_framebuffer.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+
+<title>WebGL2 test: Framebuffers</title>
+
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script src="driver-info.js"></script>
+<script src="webgl-util.js"></script>
+<body>
+<canvas id="c" width="64" height="64"></canvas>
+<script>
+
+WebGLUtil.withWebGL2('c', function (gl) {
+ gl.invalidateFramebuffer(gl.FRAMEBUFFER, [gl.COLOR]);
+ ok(gl.getError() == 0, 'invalidateFramebuffer');
+ gl.invalidateSubFramebuffer(gl.FRAMEBUFFER, [gl.COLOR], 0, 0, 64, 64);
+ ok(gl.getError() == 0, 'invalidateSubFramebuffer');
+ gl.invalidateFramebuffer(gl.FRAMEBUFFER, [gl.GL_COLOR_ATTACHMENT0]);
+ ok(gl.getError() == gl.INVALID_ENUM, 'invalidateFrameBuffer should fail with GL_COLOR_ATTACHMENT on the default framebuffer');
+}, function () {
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
--- a/dom/canvas/test/webgl-mochitest/webgl-util.js
+++ b/dom/canvas/test/webgl-mochitest/webgl-util.js
@@ -53,16 +53,54 @@ WebGLUtil = (function() {
if (!gl) {
error('WebGL context could not be retrieved from \'' + canvasId + '\'.');
return null;
}
return gl;
}
+ function withWebGL2(canvasId, callback, onFinished) {
+ var prefArrArr = [
+ ['webgl.force-enabled', true],
+ ['webgl.disable-angle', true],
+ ['webgl.enable-prototype-webgl2', true],
+ ];
+ var prefEnv = {'set': prefArrArr};
+ SpecialPowers.pushPrefEnv(prefEnv, function() {
+ var canvas = document.getElementById(canvasId);
+
+ var gl = null;
+ try {
+ gl = canvas.getContext('webgl2');
+ } catch(e) {}
+
+ if (!gl) {
+ try {
+ gl = canvas.getContext('experimental-webgl2');
+ } catch(e) {}
+ }
+
+ if (!gl) {
+ todo(false, 'WebGL2 is not supported');
+ onFinished();
+ return;
+ }
+
+ function errorFunc(str) {
+ ok(false, 'Error: ' + str);
+ }
+ setErrorFunc(errorFunc);
+ setWarningFunc(errorFunc);
+
+ callback(gl);
+ onFinished();
+ });
+ }
+
function getContentFromElem(elem) {
var str = "";
var k = elem.firstChild;
while (k) {
if (k.nodeType == 3)
str += k.textContent;
k = k.nextSibling;
@@ -120,12 +158,13 @@ WebGLUtil = (function() {
return prog;
}
return {
setErrorFunc: setErrorFunc,
setWarningFunc: setWarningFunc,
getWebGL: getWebGL,
+ withWebGL2: withWebGL2,
createShaderById: createShaderById,
createProgramByIds: createProgramByIds,
};
})();
--- a/dom/events/test/test_bug602962.xul
+++ b/dom/events/test/test_bug602962.xul
@@ -25,36 +25,35 @@ var oldWidth = 0, oldHeight = 0;
var win = null;
function openWindow() {
win = window.open("chrome://mochitests/content/chrome/dom/events/test/bug602962.xul", "_blank", "width=600,height=600");
}
function doTest() {
scrollbox = win.document.getElementById("page-scrollbox");
- sbo = scrollbox.boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
+ sbo = scrollbox.boxObject;
content = win.document.getElementById("page-box");
content.style.width = 400 + "px";
win.addEventListener("resize", function() {
win.removeEventListener("resize", arguments.callee, false);
sbo.scrollBy(200, 0);
setTimeout(function() { resize(); }, 0);
}, false);
oldWidth = win.outerWidth;
oldHeight = win.outerHeight;
win.resizeTo(200, 400);
}
function resize() {
- let x = {}, y = {};
- sbo.getPosition(x, y);
- scrollX = x.value, scrollY = y.value;
+ scrollX = sbo.positionX;
+ scrollY = sbo.positionY;
win.addEventListener("resize", function() {
content.style.width = (oldWidth + 400) + "px";
win.removeEventListener("resize", arguments.callee, true);
setTimeout(function() {
finish();
}, 0);
@@ -67,20 +66,18 @@ function finish() {
if (win.outerWidth != oldWidth ||
win.outerHeight != oldHeight) {
// We should eventually get back to the original size.
setTimeout(finish, 0);
return;
}
sbo.scrollBy(scrollX, scrollY);
- let x = {}, y = {};
- sbo.getPosition(x, y);
- is(x.value, 200, "Scroll X should have been restored to the value before the resize");
- is(y.value, 0, "Scroll Y should have been restored to the value before the resize");
+ is(sbo.positionX, 200, "Scroll X should have been restored to the value before the resize");
+ is(sbo.positionY, 0, "Scroll Y should have been restored to the value before the resize");
is(win.outerWidth, oldWidth, "Width should be resized");
is(win.outerHeight, oldHeight, "Height should be resized");
win.close();
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3017,16 +3017,17 @@ TabChild::RecvUIResolutionChanged()
nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
presContext->UIResolutionChanged();
return true;
}
TabChildGlobal::TabChildGlobal(TabChildBase* aTabChild)
: mTabChild(aTabChild)
{
+ SetIsNotDOMBinding();
}
TabChildGlobal::~TabChildGlobal()
{
}
void
TabChildGlobal::Init()
--- a/dom/locales/en-US/chrome/layout/css.properties
+++ b/dom/locales/en-US/chrome/layout/css.properties
@@ -101,16 +101,19 @@ PEExpectedInt=Expected an integer but fo
PEColorBadRGBContents=Expected number or percentage in rgb() but found '%1$S'.
PEColorComponentBadTerm=Expected '%2$S' but found '%1$S'.
PEColorHueEOF=hue
PEExpectedComma=Expected ',' but found '%1$S'.
PEColorSaturationEOF=saturation
PEColorLightnessEOF=lightness
PEColorOpacityEOF=opacity in color value
PEExpectedNumber=Expected a number but found '%1$S'.
+PEPositionEOF=<position>
+PEExpectedPosition=Expected <position> but found '%1$S'.
+PEExpectedRadius=Expected radius but found '%1$S'.
PEExpectedCloseParen=Expected ')' but found '%1$S'.
PEDeclEndEOF=';' or '}' to end declaration
PEParseDeclarationNoColon=Expected ':' but found '%1$S'.
PEParseDeclarationDeclExpected=Expected declaration but found '%1$S'.
PEEndOfDeclEOF=end of declaration
PEImportantEOF=important
PEExpectedImportant=Expected 'important' but found '%1$S'.
PEBadDeclEnd=Expected ';' to terminate declaration but found '%1$S'.
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -2434,17 +2434,17 @@ PeerConnectionWrapper.prototype = {
},
/**
* Checks that we are getting the media streams we expect.
*
* @param {object} stats
* The stats to check from this PeerConnectionWrapper
*/
- checkStats : function PCW_checkStats(stats) {
+ checkStats : function PCW_checkStats(stats, twoMachines) {
function toNum(obj) {
return obj? obj : 0;
}
function numTracks(streams) {
var n = 0;
streams.forEach(function(stream) {
n += stream.getAudioTracks().length + stream.getVideoTracks().length;
});
@@ -2456,17 +2456,23 @@ PeerConnectionWrapper.prototype = {
// Use spec way of enumerating stats
var counters = {};
for (var key in stats) {
if (stats.hasOwnProperty(key)) {
var res = stats[key];
// validate stats
ok(res.id == key, "Coherent stats id");
var nowish = Date.now() + 1000; // TODO: clock drift observed
+ if (twoMachines) {
+ nowish += 10000; // let's be very relaxed about clock sync
+ }
var minimum = this.whenCreated - 1000; // on Windows XP (Bug 979649)
+ if (twoMachines) {
+ minimum -= 10000; // let's be very relaxed about clock sync
+ }
if (isWinXP) {
todo(false, "Can't reliably test rtcp timestamps on WinXP (Bug 979649)");
} else {
ok(res.timestamp >= minimum,
"Valid " + (res.isRemote? "rtcp" : "rtp") + " timestamp " +
res.timestamp + " >= " + minimum + " (" +
(res.timestamp - minimum) + " ms)");
ok(res.timestamp <= nowish,
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -473,26 +473,26 @@ var commandsPeerConnection = [
test.next();
});
}
],
[
'PC_LOCAL_CHECK_STATS',
function (test) {
test.pcLocal.getStats(null, function(stats) {
- test.pcLocal.checkStats(stats);
+ test.pcLocal.checkStats(stats, test.steeplechase);
test.next();
});
}
],
[
'PC_REMOTE_CHECK_STATS',
function (test) {
test.pcRemote.getStats(null, function(stats) {
- test.pcRemote.checkStats(stats);
+ test.pcRemote.checkStats(stats, test.steeplechase);
test.next();
});
}
],
[
'PC_LOCAL_CHECK_GETSTATS_AUDIOTRACK_OUTBOUND',
function (test) {
var pc = test.pcLocal;
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -735,18 +735,18 @@ void NetworkUtils::postTetherInterfaceLi
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
// Send the dummy command to continue the function chain.
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
char buf[BUF_SIZE];
- const char* reason = NS_ConvertUTF16toUTF8(aResult.mResultReason).get();
- memcpy(buf, reason, strlen(reason));
+ NS_ConvertUTF16toUTF8 reason(aResult.mResultReason);
+ memcpy(buf, reason.get(), reason.Length() + 1);
split(buf, INTERFACE_DELIMIT, GET_FIELD(mInterfaceList));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::setIpForwardingEnabled(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -668,16 +668,18 @@ var interfaceNamesInGlobalScope =
"MediaStreamAudioSourceNode",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MediaStreamEvent", pref: "media.peerconnection.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MediaStreamTrackEvent", pref: "media.peerconnection.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"MediaStreamTrack",
// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "MenuBoxObject", xbl: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
"MessageEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MessagePort",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MimeType",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MimeTypeArray",
// IMPORTANT: Do not change this list without review from a DOM peer!
@@ -858,16 +860,18 @@ var interfaceNamesInGlobalScope =
"PluginArray",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "PointerEvent", pref: "dom.w3c_pointer_events.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"PopStateEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"PopupBlockedEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "PopupBoxObject", xbl: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
"ProcessingInstruction",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ProgressEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"Promise",
// IMPORTANT: Do not change this list without review from a DOM peer!
"PropertyNodeList",
// IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BoxObject.webidl
@@ -0,0 +1,33 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[Func="IsChromeOrXBL"]
+interface BoxObject {
+ readonly attribute Element? element;
+
+ readonly attribute long x;
+ readonly attribute long y;
+ [Throws]
+ readonly attribute long screenX;
+ [Throws]
+ readonly attribute long screenY;
+ readonly attribute long width;
+ readonly attribute long height;
+
+ nsISupports? getPropertyAsSupports(DOMString propertyName);
+ void setPropertyAsSupports(DOMString propertyName, nsISupports value);
+ [Throws]
+ DOMString? getProperty(DOMString propertyName);
+ void setProperty(DOMString propertyName, DOMString propertyValue);
+ void removeProperty(DOMString propertyName);
+
+ // for stepping through content in the expanded dom with box-ordinal-group order
+ readonly attribute Element? parentBox;
+ readonly attribute Element? firstChild;
+ readonly attribute Element? lastChild;
+ readonly attribute Element? nextSibling;
+ readonly attribute Element? previousSibling;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/ContainerBoxObject.webidl
@@ -0,0 +1,12 @@
+
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[NoInterfaceObject]
+interface ContainerBoxObject : BoxObject {
+ [ChromeOnly]
+ readonly attribute nsIDocShell? docShell;
+};
--- a/dom/webidl/LegacyQueryInterface.webidl
+++ b/dom/webidl/LegacyQueryInterface.webidl
@@ -15,16 +15,17 @@ interface LegacyQueryInterface {
// Legacy QueryInterface, only exposed to chrome or XBL code on the
// main thread.
[Exposed=Window]
nsISupports queryInterface(IID iid);
};
Attr implements LegacyQueryInterface;
BarProp implements LegacyQueryInterface;
+BoxObject implements LegacyQueryInterface;
CaretPosition implements LegacyQueryInterface;
Comment implements LegacyQueryInterface;
Crypto implements LegacyQueryInterface;
CSSPrimitiveValue implements LegacyQueryInterface;
CSSStyleDeclaration implements LegacyQueryInterface;
CSSValueList implements LegacyQueryInterface;
DOMImplementation implements LegacyQueryInterface;
DOMParser implements LegacyQueryInterface;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/ListBoxObject.webidl
@@ -0,0 +1,21 @@
+
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[NoInterfaceObject]
+interface ListBoxObject : BoxObject {
+
+ long getRowCount();
+ long getNumberOfVisibleRows();
+ long getIndexOfFirstVisibleRow();
+
+ void ensureIndexIsVisible(long rowIndex);
+ void scrollToIndex(long rowIndex);
+ void scrollByLines(long numLines);
+
+ Element? getItemAtIndex(long index);
+ long getIndexOfItem(Element item);
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MenuBoxObject.webidl
@@ -0,0 +1,19 @@
+
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[Func="IsChromeOrXBL"]
+interface MenuBoxObject : BoxObject {
+
+ void openMenu(boolean openFlag);
+
+ attribute Element? activeChild;
+
+ boolean handleKeyPress(KeyboardEvent keyEvent);
+
+ readonly attribute boolean openedWithKey;
+
+};
rename from layout/xul/nsIPopupBoxObject.idl
rename to dom/webidl/PopupBoxObject.webidl
--- a/layout/xul/nsIPopupBoxObject.idl
+++ b/dom/webidl/PopupBoxObject.webidl
@@ -1,84 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "nsISupports.idl"
-
-interface nsIDOMElement;
-interface nsIDOMNode;
-interface nsIDOMEvent;
-interface nsIDOMClientRect;
-
-[scriptable, uuid(492861e3-d168-410f-b2bb-6eb8ce503d4a)]
-interface nsIPopupBoxObject : nsISupports
+[Func="IsChromeOrXBL"]
+interface PopupBoxObject : BoxObject
{
/**
* This method is deprecated. Use openPopup or openPopupAtScreen instead.
*/
- void showPopup(in nsIDOMElement srcContent, in nsIDOMElement popupContent,
- in long xpos, in long ypos,
- in wstring popupType, in wstring anchorAlignment,
- in wstring popupAlignment);
+ void showPopup(Element? srcContent, Element popupContent,
+ long xpos, long ypos,
+ DOMString popupType, DOMString anchorAlignment,
+ DOMString popupAlignment);
/**
* Hide the popup if it is open. The cancel argument is used as a hint that
* the popup is being closed because it has been cancelled, rather than
* something being selected within the panel.
*
* @param cancel if true, then the popup is being cancelled.
*/
- void hidePopup([optional] in bool cancel);
+ void hidePopup(optional boolean cancel = false);
- /**
+ /**
* Allow the popup to automatically position itself.
*/
attribute boolean autoPosition;
/**
* If keyboard navigation is enabled, the keyboard may be used to navigate
* the menuitems on the popup. Enabling keyboard navigation is the default
* behaviour and will install capturing key event listeners on the popup
* that do not propagate key events to the contents. If you wish to place
* elements in a popup which accept key events, such as textboxes, keyboard
* navigation should be disabled.
*
* Setting ignorekeys="true" on the popup element also disables keyboard
* navigation, and is recommended over calling this method.
*/
- void enableKeyboardNavigator(in boolean enableKeyboardNavigator);
+ void enableKeyboardNavigator(boolean enableKeyboardNavigator);
- /**
+ /**
* Enable automatic popup dismissal. This only has effect when called
* on an open popup.
*/
- void enableRollup(in boolean enableRollup);
+ void enableRollup(boolean enableRollup);
/**
* Control whether the event that caused the popup to be automatically
* dismissed ("rolled up") should be consumed, or dispatched as a
* normal event. This should be set immediately before calling showPopup()
* if non-default behavior is desired.
*/
- const uint32_t ROLLUP_DEFAULT = 0; /* widget/platform default */
- const uint32_t ROLLUP_CONSUME = 1; /* consume the rollup event */
- const uint32_t ROLLUP_NO_CONSUME = 2; /* don't consume the rollup event */
- void setConsumeRollupEvent(in uint32_t consume);
+ const unsigned long ROLLUP_DEFAULT = 0; /* widget/platform default */
+ const unsigned long ROLLUP_CONSUME = 1; /* consume the rollup event */
+ const unsigned long ROLLUP_NO_CONSUME = 2; /* don't consume the rollup event */
+ void setConsumeRollupEvent(unsigned long consume);
- /**
+ /**
* Size the popup to the given dimensions
*/
- void sizeTo(in long width, in long height);
+ void sizeTo(long width, long height);
/**
* Move the popup to a point on screen in CSS pixels.
*/
- void moveTo(in long left, in long top);
+ void moveTo(long left, long top);
/**
* Open the popup relative to a specified node at a specific location.
*
* The popup may be either anchored to another node or opened freely.
* To anchor a popup to a node, supply an anchor node and set the position
* to a string indicating the manner in which the popup should be anchored.
* Possible values for position are:
@@ -88,17 +81,17 @@ interface nsIPopupBoxObject : nsISupport
*
* The anchor node does not need to be in the same document as the popup.
*
* If the attributesOverride argument is true, the popupanchor, popupalign
* and position attributes on the popup node override the position value
* argument. If attributesOverride is false, the attributes are only used
* if position is empty.
*
- * For an anchored popup, the x and y arguments may be used to offset the
+ * For an anchored popup, the x and y arguments may be used to offset the
* popup from its anchored position by some distance, measured in CSS pixels.
* x increases to the right and y increases down. Negative values may also
* be used to move to the left and upwards respectively.
*
* Unanchored popups may be created by supplying null as the anchor node.
* An unanchored popup appears at the position specified by x and y,
* relative to the viewport of the document containing the popup node. In
* this case, position and attributesOverride are ignored.
@@ -106,82 +99,75 @@ interface nsIPopupBoxObject : nsISupport
* @param anchorElement the node to anchor the popup to, may be null
* @param position manner is which to anchor the popup to node
* @param x horizontal offset
* @param y vertical offset
* @param isContextMenu true for context menus, false for other popups
* @param attributesOverride true if popup node attributes override position
* @param triggerEvent the event that triggered this popup (mouse click for example)
*/
- void openPopup(in nsIDOMElement anchorElement,
- in AString position,
- in long x, in long y,
- in boolean isContextMenu,
- in boolean attributesOverride,
- in nsIDOMEvent triggerEvent);
+ void openPopup(Element? anchorElement,
+ DOMString position,
+ long x, long y,
+ boolean isContextMenu,
+ boolean attributesOverride,
+ Event? triggerEvent);
/**
* Open the popup at a specific screen position specified by x and y. This
* position may be adjusted if it would cause the popup to be off of the
* screen. The x and y coordinates are measured in CSS pixels, and like all
* screen coordinates, are given relative to the top left of the primary
* screen.
*
* @param isContextMenu true for context menus, false for other popups
* @param x horizontal screen position
* @param y vertical screen position
* @param triggerEvent the event that triggered this popup (mouse click for example)
*/
- void openPopupAtScreen(in long x, in long y,
- in boolean isContextMenu,
- in nsIDOMEvent triggerEvent);
+ void openPopupAtScreen(long x, long y,
+ boolean isContextMenu,
+ Event? triggerEvent);
/**
* Returns the state of the popup:
* closed - the popup is closed
* open - the popup is open
* showing - the popup is in the process of being shown
* hiding - the popup is in the process of being hidden
*/
- readonly attribute AString popupState;
+ readonly attribute DOMString popupState;
/**
* The node that triggered the popup. If the popup is not open, will return
* null.
*/
- readonly attribute nsIDOMNode triggerNode;
+ readonly attribute Node? triggerNode;
/**
* Retrieve the anchor that was specified to openPopup or for menupopups in a
* menu, the parent menu.
*/
- readonly attribute nsIDOMElement anchorNode;
+ readonly attribute Element? anchorNode;
/**
* Retrieve the screen rectangle of the popup, including the area occupied by
* any titlebar or borders present.
*/
- nsIDOMClientRect getOuterScreenRect();
+ DOMRect getOuterScreenRect();
/**
* Move an open popup to the given anchor position. The arguments have the same
* meaning as the corresponding argument to openPopup. This method has no effect
* on popups that are not open.
*/
- void moveToAnchor(in nsIDOMElement anchorElement,
- in AString position,
- in long x, in long y,
- in boolean attributesOverride);
+ void moveToAnchor(Element? anchorElement,
+ DOMString position,
+ long x, long y,
+ boolean attributesOverride);
/** Returns the alignment position where the popup has appeared relative to its
* anchor node or point, accounting for any flipping that occurred.
*/
- readonly attribute AString alignmentPosition;
+ readonly attribute DOMString alignmentPosition;
readonly attribute long alignmentOffset;
-};
-%{C++
-class nsIBoxObject;
-
-nsresult
-NS_NewPopupBoxObject(nsIBoxObject** aResult);
-
-%}
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/ScrollBoxObject.webidl
@@ -0,0 +1,70 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[NoInterfaceObject]
+interface ScrollBoxObject : BoxObject {
+
+ /**
+ * Scroll to the given coordinates, in css pixels.
+ * (0,0) will put the top left corner of the scrolled element's padding-box
+ * at the top left corner of the scrollport (which is its inner-border-box).
+ * Values will be clamped to legal values.
+ */
+ [Throws]
+ void scrollTo(long x, long y);
+
+ /**
+ * Scroll the given amount of device pixels to the right and down.
+ * Values will be clamped to make the resuling position legal.
+ */
+ [Throws]
+ void scrollBy(long dx, long dy);
+ [Throws]
+ void scrollByLine(long dlines);
+ [Throws]
+ void scrollByIndex(long dindexes);
+ [Throws]
+ void scrollToLine(long line);
+ [Throws]
+ void scrollToElement(Element child);
+ [Throws]
+ void scrollToIndex(long index);
+
+ /**
+ * Get the current scroll position in css pixels.
+ * @see scrollTo for the definition of x and y.
+ */
+ [Pure, Throws]
+ readonly attribute long positionX;
+ [Pure, Throws]
+ readonly attribute long positionY;
+ [Pure, Throws]
+ readonly attribute long scrolledWidth;
+ [Pure, Throws]
+ readonly attribute long scrolledHeight;
+
+ /**
+ * DEPRECATED: Please use positionX and positionY
+ *
+ * Get the current scroll position in css pixels.
+ * @see scrollTo for the definition of x and y.
+ */
+ [Throws]
+ void getPosition(object x, object y);
+
+ /**
+ * DEPRECATED: Please use scrolledWidth and scrolledHeight
+ */
+ [Throws]
+ void getScrolledSize(object width, object height);
+
+ [Throws]
+ void ensureElementIsVisible(Element child);
+ [Throws]
+ void ensureIndexIsVisible(long index);
+ [Throws]
+ void ensureLineIsVisible(long line);
+};
copy from layout/xul/tree/nsITreeBoxObject.idl
copy to dom/webidl/TreeBoxObject.webidl
--- a/layout/xul/tree/nsITreeBoxObject.idl
+++ b/dom/webidl/TreeBoxObject.webidl
@@ -1,64 +1,68 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "nsISupports.idl"
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
-interface nsIDOMElement;
-interface nsITreeView;
-interface nsITreeSelection;
-interface nsITreeColumn;
-interface nsITreeColumns;
+interface MozTreeView;
+interface MozTreeColumn;
interface nsIScriptableRegion;
-[scriptable, uuid(64BA5199-C4F4-4498-BBDC-F8E4C369086C)]
-interface nsITreeBoxObject : nsISupports
-{
+dictionary TreeCellInfo {
+ long row = 0;
+ MozTreeColumn? col = null;
+ DOMString childElt = "";
+};
+
+[NoInterfaceObject]
+interface TreeBoxObject : BoxObject {
+
/**
* Obtain the columns.
*/
- readonly attribute nsITreeColumns columns;
+ readonly attribute TreeColumns? columns;
/**
* The view that backs the tree and that supplies it with its data.
* It is dynamically settable, either using a view attribute on the
* tree tag or by setting this attribute to a new value.
*/
- attribute nsITreeView view;
+ [SetterThrows]
+ attribute MozTreeView? view;
/**
* Whether or not we are currently focused.
*/
attribute boolean focused;
/**
* Obtain the treebody content node
*/
- readonly attribute nsIDOMElement treeBody;
+ readonly attribute Element? treeBody;
/**
* Obtain the height of a row.
*/
readonly attribute long rowHeight;
/**
* Obtain the width of a row.
*/
readonly attribute long rowWidth;
/**
- * Get the pixel position of the horizontal scrollbar.
+ * Get the pixel position of the horizontal scrollbar.
*/
readonly attribute long horizontalPosition;
/**
- * Return the region for the visible parts of the selection, in device pixels.
+ * Return the region for the visible parts of the selection, in device pixels
*/
readonly attribute nsIScriptableRegion selectionRegion;
/**
* Get the index of the first visible row.
*/
long getFirstVisibleRow();
@@ -70,110 +74,125 @@ interface nsITreeBoxObject : nsISupports
/**
* Gets the number of possible visible rows.
*/
long getPageLength();
/**
* Ensures that a row at a given index is visible.
*/
- void ensureRowIsVisible(in long index);
+ void ensureRowIsVisible(long index);
/**
* Ensures that a given cell in the tree is visible.
*/
- void ensureCellIsVisible(in long row, in nsITreeColumn col);
+ void ensureCellIsVisible(long row, MozTreeColumn? col);
/**
* Scrolls such that the row at index is at the top of the visible view.
*/
- void scrollToRow(in long index);
+ void scrollToRow(long index);
/**
* Scroll the tree up or down by numLines lines. Positive
* values move down in the tree. Prevents scrolling off the
- * end of the tree.
+ * end of the tree.
*/
- void scrollByLines(in long numLines);
+ void scrollByLines(long numLines);
/**
* Scroll the tree up or down by numPages pages. A page
* is considered to be the amount displayed by the tree.
* Positive values move down in the tree. Prevents scrolling
* off the end of the tree.
*/
- void scrollByPages(in long numPages);
-
- /**
- * Scrolls such that a given cell is visible (if possible)
- * at the top left corner of the visible view.
- */
- void scrollToCell(in long row, in nsITreeColumn col);
+ void scrollByPages(long numPages);
/**
- * Scrolls horizontally so that the specified column is
+ * Scrolls such that a given cell is visible (if possible)
+ * at the top left corner of the visible view.
+ */
+ void scrollToCell(long row, MozTreeColumn? col);
+
+ /**
+ * Scrolls horizontally so that the specified column is
* at the left of the view (if possible).
*/
- void scrollToColumn(in nsITreeColumn col);
+ void scrollToColumn(MozTreeColumn? col);
/**
* Scroll to a specific horizontal pixel position.
*/
- void scrollToHorizontalPosition(in long horizontalPosition);
+ void scrollToHorizontalPosition(long horizontalPosition);
/**
* Invalidation methods for fine-grained painting control.
*/
void invalidate();
- void invalidateColumn(in nsITreeColumn col);
- void invalidateRow(in long index);
- void invalidateCell(in long row, in nsITreeColumn col);
- void invalidateRange(in long startIndex, in long endIndex);
- void invalidateColumnRange(in long startIndex, in long endIndex,
- in nsITreeColumn col);
+ void invalidateColumn(MozTreeColumn? col);
+ void invalidateRow(long index);
+ void invalidateCell(long row, MozTreeColumn? col);
+ void invalidateRange(long startIndex, long endIndex);
+ void invalidateColumnRange(long startIndex, long endIndex, MozTreeColumn? col);
/**
* A hit test that can tell you what row the mouse is over.
* returns -1 for invalid mouse coordinates.
*
* The coordinate system is the client coordinate system for the
* document this boxObject lives in, and the units are CSS pixels.
*/
- long getRowAt(in long x, in long y);
+ long getRowAt(long x, long y);
/**
- * A hit test that can tell you what cell the mouse is over. Row is the row index
- * hit, returns -1 for invalid mouse coordinates. ColID is the column hit.
- * ChildElt is the pseudoelement hit: this can have values of
+ * A hit test that can tell you what cell the mouse is over.
+ * TreeCellInfo.row is the row index hit, returns -1 for invalid mouse
+ * coordinates. TreeCellInfo.col is the column hit.
+ * TreeCellInfo.childElt is the pseudoelement hit: this can have values of
* "cell", "twisty", "image", and "text".
*
* The coordinate system is the client coordinate system for the
* document this boxObject lives in, and the units are CSS pixels.
*/
- void getCellAt(in long x, in long y, out long row, out nsITreeColumn col, out ACString childElt);
+ [Throws]
+ TreeCellInfo getCellAt(long x, long y);
- /**
- * Find the coordinates of an element within a specific cell.
+ /**
+ * DEPRECATED: please use above version
+ */
+ [Throws]
+ void getCellAt(long x, long y, object row, object column, object childElt);
+
+ /**
+ * Find the coordinates of an element within a specific cell.
*/
- void getCoordsForCellItem(in long row, in nsITreeColumn col, in ACString element,
- out long x, out long y, out long width, out long height);
+ [Throws]
+ DOMRect? getCoordsForCellItem(long row, MozTreeColumn col, DOMString element);
- /**
+ /**
+ * DEPRECATED: Please use above version
+ */
+ [Throws]
+ void getCoordsForCellItem(long row, MozTreeColumn col, DOMString element,
+ object x, object y, object width, object height);
+
+ /**
* Determine if the text of a cell is being cropped or not.
*/
- boolean isCellCropped(in long row, in nsITreeColumn col);
+ [Throws]
+ boolean isCellCropped(long row, MozTreeColumn? col);
/**
* The view is responsible for calling these notification methods when
* rows are added or removed. Index is the position at which the new
* rows were added or at which rows were removed. For
* non-contiguous additions/removals, this method should be called multiple times.
*/
- void rowCountChanged(in long index, in long count);
-
+ void rowCountChanged(long index, long count);
+
/**
* Notify the tree that the view is about to perform a batch
* update, that is, add, remove or invalidate several rows at once.
* This must be followed by calling endUpdateBatch(), otherwise the tree
* will get out of sync.
*/
void beginUpdateBatch();
--- a/dom/webidl/TreeColumns.webidl
+++ b/dom/webidl/TreeColumns.webidl
@@ -1,21 +1,20 @@
/* 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/. */
-interface MozTreeBoxObject;
interface MozTreeColumn;
[Func="IsChromeOrXBL"]
interface TreeColumns {
/**
* The tree widget for these columns.
*/
- readonly attribute MozTreeBoxObject? tree;
+ readonly attribute TreeBoxObject? tree;
/**
* The number of columns.
*/
readonly attribute unsigned long count;
/**
* An alias for count (for the benefit of scripts which treat this as an
--- a/dom/webidl/XULDocument.webidl
+++ b/dom/webidl/XULDocument.webidl
@@ -3,17 +3,16 @@
* 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/.
*
* The origin of this IDL file is:
* dom/interfaces/xul/nsIDOMXULDocument.idl
*/
interface XULCommandDispatcher;
-interface MozBoxObject;
interface MozObserver;
[Func="IsChromeOrXBL"]
interface XULDocument : Document {
attribute Node? popupNode;
/**
* These attributes correspond to trustedGetPopupNode().rangeOffset and
@@ -46,13 +45,13 @@ interface XULDocument : Document {
DOMString attr);
void removeBroadcastListenerFor(Element broadcaster, Element observer,
DOMString attr);
[Throws]
void persist([TreatNullAs=EmptyString] DOMString id, DOMString attr);
[Throws]
- MozBoxObject? getBoxObjectFor(Element? element);
+ BoxObject? getBoxObjectFor(Element? element);
[Throws]
void loadOverlay(DOMString url, MozObserver? observer);
};
--- a/dom/webidl/XULElement.webidl
+++ b/dom/webidl/XULElement.webidl
@@ -1,15 +1,14 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-interface MozBoxObject;
interface MozControllers;
interface MozFrameLoader;
interface MozRDFCompositeDataSource;
interface MozRDFResource;
interface MozXULTemplateBuilder;
[Func="IsChromeOrXBL"]
interface XULElement : Element {
@@ -90,17 +89,17 @@ interface XULElement : Element {
readonly attribute MozRDFCompositeDataSource? database;
readonly attribute MozXULTemplateBuilder? builder;
[Throws]
readonly attribute MozRDFResource? resource;
[Throws]
readonly attribute MozControllers controllers;
[Throws]
- readonly attribute MozBoxObject? boxObject;
+ readonly attribute BoxObject? boxObject;
[Throws]
void focus();
[Throws]
void blur();
[Throws]
void click();
void doCommand();
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -48,16 +48,17 @@ WEBIDL_FILES = [
'AudioTrackList.webidl',
'AutocompleteInfo.webidl',
'BarProp.webidl',
'BatteryManager.webidl',
'BeforeAfterKeyboardEvent.webidl',
'BeforeUnloadEvent.webidl',
'BiquadFilterNode.webidl',
'Blob.webidl',
+ 'BoxObject.webidl',
'BrowserElementDictionaries.webidl',
'CallsList.webidl',
'CameraCapabilities.webidl',
'CameraControl.webidl',
'CameraManager.webidl',
'CameraUtil.webidl',
'CanvasRenderingContext2D.webidl',
'CaretPosition.webidl',
@@ -69,16 +70,17 @@ WEBIDL_FILES = [
'ChromeNotifications.webidl',
'ClipboardEvent.webidl',
'CommandEvent.webidl',
'Comment.webidl',
'CompositionEvent.webidl',
'Console.webidl',
'Constraints.webidl',
'Contacts.webidl',
+ 'ContainerBoxObject.webidl',
'ConvolverNode.webidl',
'Coordinates.webidl',
'CSPReport.webidl',
'CSS.webidl',
'CSSPrimitiveValue.webidl',
'CSSRuleList.webidl',
'CSSStyleDeclaration.webidl',
'CSSStyleSheet.webidl',
@@ -245,29 +247,31 @@ WEBIDL_FILES = [
'InterAppConnection.webidl',
'InterAppConnectionRequest.webidl',
'InterAppMessagePort.webidl',
'KeyAlgorithm.webidl',
'KeyboardEvent.webidl',
'KeyEvent.webidl',
'LegacyQueryInterface.webidl',
'LinkStyle.webidl',
+ 'ListBoxObject.webidl',
'LocalMediaStream.webidl',
'Location.webidl',
'MediaElementAudioSourceNode.webidl',
'MediaError.webidl',
'MediaList.webidl',
'MediaQueryList.webidl',
'MediaRecorder.webidl',
'MediaSource.webidl',
'MediaStream.webidl',
'MediaStreamAudioDestinationNode.webidl',
'MediaStreamAudioSourceNode.webidl',
'MediaStreamTrack.webidl',
'MediaTrackConstraintSet.webidl',
+ 'MenuBoxObject.webidl',
'MessageChannel.webidl',
'MessageEvent.webidl',
'MessagePort.webidl',
'MessagePortList.webidl',
'MimeType.webidl',
'MimeTypeArray.webidl',
'MouseEvent.webidl',
'MouseScrollEvent.webidl',
@@ -315,16 +319,17 @@ WEBIDL_FILES = [
'PerformanceResourceTiming.webidl',
'PerformanceTiming.webidl',
'PeriodicWave.webidl',
'PermissionSettings.webidl',
'PhoneNumberService.webidl',
'Plugin.webidl',
'PluginArray.webidl',
'PointerEvent.webidl',
+ 'PopupBoxObject.webidl',
'Position.webidl',
'PositionError.webidl',
'ProcessingInstruction.webidl',
'ProfileTimelineMarker.webidl',
'Promise.webidl',
'PromiseDebugging.webidl',
'PushManager.webidl',
'RadioNodeList.webidl',
@@ -342,16 +347,17 @@ WEBIDL_FILES = [
'RTCPeerConnectionStatic.webidl',
'RTCRtpReceiver.webidl',
'RTCRtpSender.webidl',
'RTCSessionDescription.webidl',
'RTCStatsReport.webidl',
'Screen.webidl',
'ScriptProcessorNode.webidl',
'ScrollAreaEvent.webidl',
+ 'ScrollBoxObject.webidl',
'Selection.webidl',
'ServiceWorker.webidl',
'ServiceWorkerContainer.webidl',
'ServiceWorkerGlobalScope.webidl',
'ServiceWorkerRegistration.webidl',
'SettingChangeNotification.webidl',
'SettingsManager.webidl',
'ShadowRoot.webidl',
@@ -487,16 +493,17 @@ WEBIDL_FILES = [
'TextTrackCueList.webidl',
'TextTrackList.webidl',
'TimeEvent.webidl',
'TimeRanges.webidl',
'Touch.webidl',
'TouchEvent.webidl',
'TouchList.webidl',
'TransitionEvent.webidl',
+ 'TreeBoxObject.webidl',
'TreeColumns.webidl',
'TreeWalker.webidl',
'UDPMessageEvent.webidl',
'UDPSocket.webidl',
'UIEvent.webidl',
'UndoManager.webidl',
'URL.webidl',
'URLSearchParams.webidl',
--- a/dom/xslt/tests/buster/buster-handlers.js
+++ b/dom/xslt/tests/buster/buster-handlers.js
@@ -3,17 +3,17 @@
* 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/. */
var xalan_field;
function onLoad()
{
view.tree = document.getElementById('out');
- view.boxObject = view.tree.boxObject.QueryInterface(Components.interfaces.nsITreeBoxObject);
+ view.boxObject = view.tree.boxObject;
{
view.mIframe = document.getElementById('hiddenHtml');
view.mIframe.webNavigation.allowPlugins = false;
view.mIframe.webNavigation.allowJavascript = false;
view.mIframe.webNavigation.allowMetaRedirects = false;
view.mIframe.webNavigation.allowImages = false;
}
view.database = view.tree.database;
--- a/editor/composer/test/test_bug434998.xul
+++ b/editor/composer/test/test_bug434998.xul
@@ -93,17 +93,17 @@ https://bugzilla.mozilla.org/show_bug.cg
mEditor: null
};
var progress, progressListener;
function runTest() {
var newEditorElement = document.getElementById("editor");
newEditorElement.makeEditable("html", true);
- var docShell = newEditorElement.boxObject.QueryInterface(Components.interfaces.nsIEditorBoxObject).docShell;
+ var docShell = newEditorElement.boxObject.docShell;
progress = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebProgress);
progressListener = new EditorContentListener(newEditorElement);
progress.addProgressListener(progressListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
newEditorElement.setAttribute("src", "data:text/html,");
}
]]>
</script>
</window>
--- a/editor/libeditor/tests/test_bug607584.xul
+++ b/editor/libeditor/tests/test_bug607584.xul
@@ -99,17 +99,17 @@ https://bugzilla.mozilla.org/show_bug.cg
mEditor: null
};
var progress, progressListener;
function runTest() {
var newEditorElement = document.getElementById("editor");
newEditorElement.makeEditable("html", true);
- var docShell = newEditorElement.boxObject.QueryInterface(Components.interfaces.nsIEditorBoxObject).docShell;
+ var docShell = newEditorElement.boxObject.docShell;
progress = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebProgress);
progressListener = new EditorContentListener(newEditorElement);
progress.addProgressListener(progressListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
newEditorElement.setAttribute("src", "data:text/html,");
}
]]>
</script>
</window>
--- a/editor/libeditor/tests/test_bug616590.xul
+++ b/editor/libeditor/tests/test_bug616590.xul
@@ -89,17 +89,17 @@ https://bugzilla.mozilla.org/show_bug.cg
mEditor: null
};
var progress, progressListener;
function runTest() {
var editorElement = document.getElementById("editor");
editorElement.makeEditable("htmlmail", true);
- var docShell = editorElement.boxObject.QueryInterface(Components.interfaces.nsIEditorBoxObject).docShell;
+ var docShell = editorElement.boxObject.docShell;
progress = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebProgress);
progressListener = new EditorContentListener(editorElement);
progress.addProgressListener(progressListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
editorElement.setAttribute("src", "data:text/html,");
}
]]>
</script>
</window>
--- a/editor/libeditor/tests/test_bug780908.xul
+++ b/editor/libeditor/tests/test_bug780908.xul
@@ -97,17 +97,17 @@ adapted from test_bug607584.xul by Kent
mEditor: null
};
var progress, progressListener;
function runTest() {
var newEditorElement = document.getElementById("editor");
newEditorElement.makeEditable("html", true);
- var docShell = newEditorElement.boxObject.QueryInterface(Components.interfaces.nsIEditorBoxObject).docShell;
+ var docShell = newEditorElement.boxObject.docShell;
progress = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebProgress);
progressListener = new EditorContentListener(newEditorElement);
progress.addProgressListener(progressListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
newEditorElement.setAttribute("src", "data:text/html,");
}
]]>
</script>
</window>
--- a/extensions/cookie/test/unit/test_permmanager_defaults.js
+++ b/extensions/cookie/test/unit/test_permmanager_defaults.js
@@ -1,15 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// The origin we use in most of the tests.
const TEST_ORIGIN = "example.org";
const TEST_ORIGIN_2 = "example.com";
const TEST_PERMISSION = "test-permission";
+Components.utils.import("resource://gre/modules/Promise.jsm");
function promiseTimeout(delay) {
let deferred = Promise.defer();
do_timeout(delay, deferred.resolve);
return deferred.promise;
}
function run_test() {
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -79,16 +79,17 @@ static const char *sExtensionNames[] = {
"GL_ARB_copy_buffer",
"GL_ARB_depth_texture",
"GL_ARB_draw_buffers",
"GL_ARB_draw_instanced",
"GL_ARB_framebuffer_object",
"GL_ARB_framebuffer_sRGB",
"GL_ARB_half_float_pixel",
"GL_ARB_instanced_arrays",
+ "GL_ARB_invalidate_subdata",
"GL_ARB_map_buffer_range",
"GL_ARB_occlusion_query2",
"GL_ARB_pixel_buffer_object",
"GL_ARB_robustness",
"GL_ARB_sampler_objects",
"GL_ARB_sync",
"GL_ARB_texture_compression",
"GL_ARB_texture_float",
@@ -1351,16 +1352,31 @@ GLContext::InitWithPrefix(const char *pr
if (!LoadSymbols(&umnSymbols[0], trygl, prefix)) {
NS_ERROR("GL supports uniform matrix with non-square dim without supplying its functions.");
MarkUnsupported(GLFeature::uniform_matrix_nonsquare);
ClearSymbols(umnSymbols);
}
}
+ if (IsSupported(GLFeature::invalidate_framebuffer)) {
+ SymLoadStruct invSymbols[] = {
+ { (PRFuncPtr *) &mSymbols.fInvalidateFramebuffer, { "InvalidateFramebuffer", nullptr } },
+ { (PRFuncPtr *) &mSymbols.fInvalidateSubFramebuffer, { "InvalidateSubFramebuffer", nullptr } },
+ END_SYMBOLS
+ };
+
+ if (!LoadSymbols(&invSymbols[0], trygl, prefix)) {
+ NS_ERROR("GL supports framebuffer invalidation without supplying its functions.");
+
+ MarkUnsupported(GLFeature::invalidate_framebuffer);
+ ClearSymbols(invSymbols);
+ }
+ }
+
if (IsExtensionSupported(KHR_debug)) {
SymLoadStruct extSymbols[] = {
{ (PRFuncPtr*) &mSymbols.fDebugMessageControl, { "DebugMessageControl", "DebugMessageControlKHR", nullptr } },
{ (PRFuncPtr*) &mSymbols.fDebugMessageInsert, { "DebugMessageInsert", "DebugMessageInsertKHR", nullptr } },
{ (PRFuncPtr*) &mSymbols.fDebugMessageCallback, { "DebugMessageCallback", "DebugMessageCallbackKHR", nullptr } },
{ (PRFuncPtr*) &mSymbols.fGetDebugMessageLog, { "GetDebugMessageLog", "GetDebugMessageLogKHR", nullptr } },
{ (PRFuncPtr*) &mSymbols.fGetPointerv, { "GetPointerv", "GetPointervKHR", nullptr } },
{ (PRFuncPtr*) &mSymbols.fPushDebugGroup, { "PushDebugGroup", "PushDebugGroupKHR", nullptr } },
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -38,16 +38,17 @@
#include "GLContextTypes.h"
#include "GLTextureImage.h"
#include "SurfaceTypes.h"
#include "GLScreenBuffer.h"
#include "GLContextSymbols.h"
#include "base/platform_thread.h" // for PlatformThreadId
#include "mozilla/GenericRefCounted.h"
#include "gfx2DGlue.h"
+#include "GeckoProfiler.h"
class nsIntRegion;
class nsIRunnable;
class nsIThread;
namespace android {
class GraphicBuffer;
}
@@ -95,16 +96,17 @@ MOZ_BEGIN_ENUM_CLASS(GLFeature)
framebuffer_multisample,
framebuffer_object,
get_integer_indexed,
get_integer64_indexed,
get_query_object_iv,
gpu_shader4,
instanced_arrays,
instanced_non_arrays,
+ invalidate_framebuffer,
map_buffer_range,
occlusion_query,
occlusion_query_boolean,
occlusion_query2,
packed_depth_stencil,
query_objects,
renderbuffer_color_float,
renderbuffer_color_half_float,
@@ -362,16 +364,17 @@ public:
ARB_copy_buffer,
ARB_depth_texture,
ARB_draw_buffers,
ARB_draw_instanced,
ARB_framebuffer_object,
ARB_framebuffer_sRGB,
ARB_half_float_pixel,
ARB_instanced_arrays,
+ ARB_invalidate_subdata,
ARB_map_buffer_range,
ARB_occlusion_query2,
ARB_pixel_buffer_object,
ARB_robustness,
ARB_sampler_objects,
ARB_sync,
ARB_texture_compression,
ARB_texture_float,
@@ -759,36 +762,49 @@ private:
GLContext *tip = this;
while (tip->mSharedContext)
tip = tip->mSharedContext;
return tip;
}
static void AssertNotPassingStackBufferToTheGL(const void* ptr);
+#ifdef MOZ_WIDGET_ANDROID
+// Record the name of the GL call for better hang stacks on Android.
+#define BEFORE_GL_CALL \
+ PROFILER_LABEL_FUNC( \
+ js::ProfileEntry::Category::GRAPHICS);\
+ BeforeGLCall(MOZ_FUNCTION_NAME)
+#else
#define BEFORE_GL_CALL \
do { \
BeforeGLCall(MOZ_FUNCTION_NAME); \
} while (0)
+#endif
#define AFTER_GL_CALL \
do { \
AfterGLCall(MOZ_FUNCTION_NAME); \
} while (0)
#define TRACKING_CONTEXT(a) \
do { \
TrackingContext()->a; \
} while (0)
#define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) AssertNotPassingStackBufferToTheGL(ptr)
#else // ifdef DEBUG
+#ifdef MOZ_WIDGET_ANDROID
+// Record the name of the GL call for better hang stacks on Android.
+#define BEFORE_GL_CALL PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS)
+#else
#define BEFORE_GL_CALL do { } while (0)
+#endif
#define AFTER_GL_CALL do { } while (0)
#define TRACKING_CONTEXT(a) do {} while (0)
#define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) do {} while (0)
#endif // ifdef DEBUG
#define ASSERT_SYMBOL_PRESENT(func) \
do {\
@@ -885,16 +901,30 @@ public:
default:
// Nothing we care about, likely an error.
break;
}
raw_fBindFramebuffer(target, framebuffer);
}
+ void fInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fInvalidateFramebuffer);
+ mSymbols.fInvalidateFramebuffer(target, numAttachments, attachments);
+ AFTER_GL_CALL;
+ }
+
+ void fInvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fInvalidateSubFramebuffer);
+ mSymbols.fInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height);
+ AFTER_GL_CALL;
+ }
+
void fBindTexture(GLenum target, GLuint texture) {
BEFORE_GL_CALL;
mSymbols.fBindTexture(target, texture);
AFTER_GL_CALL;
}
void fBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
BEFORE_GL_CALL;
@@ -3532,16 +3562,20 @@ public:
/* Clear to transparent black, with 0 depth and stencil,
* while preserving current ClearColor etc. values.
* Useful for resizing offscreen buffers.
*/
void ClearSafely();
bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
+ bool IsDrawingToDefaultFramebuffer() {
+ return Screen()->IsDrawFramebufferDefault();
+ }
+
protected:
nsRefPtr<TextureGarbageBin> mTexGarbageBin;
public:
TextureGarbageBin* TexGarbageBin() {
MOZ_ASSERT(mTexGarbageBin);
return mTexGarbageBin;
}
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -284,16 +284,25 @@ static const FeatureInfo sFeatureInfoArr
}
/* This is an expanded version of `instanced_arrays` that allows for all
* enabled active attrib arrays to have non-zero divisors.
* ANGLE_instanced_arrays and NV_instanced_arrays forbid this, but GLES3
* has no such restriction.
*/
},
{
+ "invalidate_framebuffer",
+ 430, // OpenGL version
+ 300, // OpenGL ES version
+ GLContext::ARB_invalidate_subdata,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
"map_buffer_range",
300, // OpenGL version
300, // OpenGL ES version
GLContext::ARB_map_buffer_range,
{
GLContext::Extensions_End
}
},
--- a/gfx/gl/GLContextSymbols.h
+++ b/gfx/gl/GLContextSymbols.h
@@ -329,16 +329,21 @@ struct GLContextSymbols
PFNGLISFRAMEBUFFER fIsFramebuffer;
typedef realGLboolean (GLAPIENTRY * PFNGLISRENDERBUFFER) (GLuint renderbuffer);
PFNGLISRENDERBUFFER fIsRenderbuffer;
typedef realGLboolean (GLAPIENTRY * PFNGLISVERTEXARRAY) (GLuint array);
PFNGLISVERTEXARRAY fIsVertexArray;
typedef void (GLAPIENTRY * PFNGLRENDERBUFFERSTORAGE) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height);
PFNGLRENDERBUFFERSTORAGE fRenderbufferStorage;
+ typedef void (GLAPIENTRY * PFNINVALIDATEFRAMEBUFFER) (GLenum target, GLsizei numAttachments, const GLenum* attachments);
+ PFNINVALIDATEFRAMEBUFFER fInvalidateFramebuffer;
+ typedef void (GLAPIENTRY * PFNINVALIDATESUBFRAMEBUFFER) (GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height);
+ PFNINVALIDATESUBFRAMEBUFFER fInvalidateSubFramebuffer;
+
// These functions are only used by Skia/GL in desktop mode.
// Other parts of Gecko should avoid using these
typedef void (GLAPIENTRY * PFNGLCLIENTACTIVETEXTURE) (GLenum texture);
PFNGLCLIENTACTIVETEXTURE fClientActiveTexture;
typedef void (GLAPIENTRY * PFNDISABLECLIENTSTATE) (GLenum capability);
PFNDISABLECLIENTSTATE fDisableClientState;
typedef void (GLAPIENTRY * PFNENABLECLIENTSTATE) (GLenum capability);
PFNENABLECLIENTSTATE fEnableClientState;
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -7,16 +7,17 @@
#if defined(MOZ_X11)
#include "mozilla/X11Util.h"
#endif
#include "GLLibraryLoader.h"
#include "mozilla/ThreadLocal.h"
#include "nsIFile.h"
+#include "GeckoProfiler.h"
#include <bitset>
#if defined(XP_WIN)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
@@ -70,27 +71,38 @@ namespace gl {
# define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__
# elif defined(_MSC_VER)
# define MOZ_FUNCTION_NAME __FUNCTION__
# else
# define MOZ_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name.
# endif
#endif
+#ifdef MOZ_WIDGET_ANDROID
+// Record the name of the GL call for better hang stacks on Android.
+#define BEFORE_GL_CALL \
+ PROFILER_LABEL_FUNC( \
+ js::ProfileEntry::Category::GRAPHICS);\
+ BeforeGLCall(MOZ_FUNCTION_NAME)
+#else
#define BEFORE_GL_CALL do { \
BeforeGLCall(MOZ_FUNCTION_NAME); \
} while (0)
+#endif
#define AFTER_GL_CALL do { \
AfterGLCall(MOZ_FUNCTION_NAME); \
} while (0)
-// We rely on the fact that GLLibraryEGL.h #defines BEFORE_GL_CALL and
-// AFTER_GL_CALL to nothing if !defined(DEBUG).
+#else
+#ifdef MOZ_WIDGET_ANDROID
+// Record the name of the GL call for better hang stacks on Android.
+#define BEFORE_GL_CALL PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS)
#else
#define BEFORE_GL_CALL
+#endif
#define AFTER_GL_CALL
#endif
class GLLibraryEGL
{
public:
GLLibraryEGL()
: mInitialized(false),
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -524,16 +524,30 @@ GLScreenBuffer::Readback(SharedSurface*
}
if (needsSwap) {
src->UnlockProd();
SharedSurf()->LockProd();
}
}
+bool
+GLScreenBuffer::IsDrawFramebufferDefault() const
+{
+ if (!mDraw)
+ return IsReadFramebufferDefault();
+ return mDraw->mFB == 0;
+}
+
+bool
+GLScreenBuffer::IsReadFramebufferDefault() const
+{
+ return SharedSurf()->mAttachType == AttachmentType::Screen;
+}
+
////////////////////////////////////////////////////////////////////////
// DrawBuffer
bool
DrawBuffer::Create(GLContext* const gl,
const SurfaceCaps& caps,
const GLFormats& formats,
const gfx::IntSize& size,
--- a/gfx/gl/GLScreenBuffer.h
+++ b/gfx/gl/GLScreenBuffer.h
@@ -256,14 +256,17 @@ public:
GLuint GetDrawFB() const;
GLuint GetReadFB() const;
// Here `fb` is the actual framebuffer you want bound. Binding 0 will
// bind the (generally useless) default framebuffer.
void BindFB_Internal(GLuint fb);
void BindDrawFB_Internal(GLuint fb);
void BindReadFB_Internal(GLuint fb);
+
+ bool IsDrawFramebufferDefault() const;
+ bool IsReadFramebufferDefault() const;
};
} // namespace gl
} // namespace mozilla
#endif // SCREEN_BUFFER_H_
--- a/gfx/gl/SharedSurface.cpp
+++ b/gfx/gl/SharedSurface.cpp
@@ -1,17 +1,19 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SharedSurface.h"
+#include "../2d/2D.h"
#include "GLBlitHelper.h"
#include "GLContext.h"
+#include "GLReadTexImageHelper.h"
#include "nsThreadUtils.h"
#include "ScopedGLHelpers.h"
#include "SharedSurfaceGL.h"
namespace mozilla {
namespace gl {
/*static*/ void
@@ -442,10 +444,111 @@ ScopedReadbackFB::~ScopedReadbackFB()
if (mSurfToUnlock) {
mSurfToUnlock->UnlockProd();
}
if (mSurfToLock) {
mSurfToLock->LockProd();
}
}
+////////////////////////////////////////////////////////////////////////////////
+
+class AutoLockBits
+{
+ gfx::DrawTarget* mDT;
+ uint8_t* mLockedBits;
+
+public:
+ AutoLockBits(gfx::DrawTarget* dt)
+ : mDT(dt)
+ , mLockedBits(nullptr)
+ {
+ MOZ_ASSERT(mDT);
+ }
+
+ bool Lock(uint8_t** data, gfx::IntSize* size, int32_t* stride,
+ gfx::SurfaceFormat* format)
+ {
+ bool success = mDT->LockBits(data, size, stride, format);
+ if (success)
+ mLockedBits = *data;
+ return success;
+ }
+
+ ~AutoLockBits() {
+ if (mLockedBits)
+ mDT->ReleaseBits(mLockedBits);
+ }
+};
+
+bool
+ReadbackSharedSurface(SharedSurface* src, gfx::DrawTarget* dst)
+{
+ AutoLockBits lock(dst);
+
+ uint8_t* dstBytes;
+ gfx::IntSize dstSize;
+ int32_t dstStride;
+ gfx::SurfaceFormat dstFormat;
+ if (!dst->LockBits(&dstBytes, &dstSize, &dstStride, &dstFormat))
+ return false;
+
+ const bool isDstRGBA = (dstFormat == gfx::SurfaceFormat::R8G8B8A8 ||
+ dstFormat == gfx::SurfaceFormat::R8G8B8X8);
+ MOZ_ASSERT_IF(!isDstRGBA, dstFormat == gfx::SurfaceFormat::B8G8R8A8 ||
+ dstFormat == gfx::SurfaceFormat::B8G8R8X8);
+
+ size_t width = src->mSize.width;
+ size_t height = src->mSize.height;
+ MOZ_ASSERT(width == (size_t)dstSize.width);
+ MOZ_ASSERT(height == (size_t)dstSize.height);
+
+ GLenum readGLFormat;
+ GLenum readType;
+
+ {
+ ScopedReadbackFB autoReadback(src);
+
+
+ // We have a source FB, now we need a format.
+ GLenum dstGLFormat = isDstRGBA ? LOCAL_GL_BGRA : LOCAL_GL_RGBA;
+ GLenum dstType = LOCAL_GL_UNSIGNED_BYTE;
+
+ // We actually don't care if they match, since we can handle
+ // any read{Format,Type} we get.
+ GLContext* gl = src->mGL;
+ GetActualReadFormats(gl, dstGLFormat, dstType, &readGLFormat,
+ &readType);
+
+ MOZ_ASSERT(readGLFormat == LOCAL_GL_RGBA ||
+ readGLFormat == LOCAL_GL_BGRA);
+ MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
+
+ // ReadPixels from the current FB into lockedBits.
+ {
+ size_t alignment = 8;
+ if (dstStride % 4 == 0)
+ alignment = 4;
+ ScopedPackAlignment autoAlign(gl, alignment);
+
+ gl->raw_fReadPixels(0, 0, width, height, readGLFormat, readType,
+ dstBytes);
+ }
+ }
+
+ const bool isReadRGBA = readGLFormat == LOCAL_GL_RGBA;
+
+ if (isReadRGBA != isDstRGBA) {
+ for (size_t j = 0; j < height; ++j) {
+ uint8_t* rowItr = dstBytes + j*dstStride;
+ uint8_t* rowEnd = rowItr + 4*width;
+ while (rowItr != rowEnd) {
+ Swap(rowItr[0], rowItr[2]);
+ rowItr += 4;
+ }
+ }
+ }
+
+ return true;
+}
+
} /* namespace gfx */
} /* namespace mozilla */
--- a/gfx/gl/SharedSurface.h
+++ b/gfx/gl/SharedSurface.h
@@ -26,16 +26,19 @@
#include "mozilla/UniquePtr.h"
#include "mozilla/WeakPtr.h"
#include "ScopedGLHelpers.h"
#include "SurfaceTypes.h"
class nsIThread;
namespace mozilla {
+namespace gfx {
+class DrawTarget;
+}
namespace gl {
class GLContext;
class SurfaceFactory;
class ShSurfHandle;
class SharedSurface
{
@@ -248,12 +251,14 @@ class ScopedReadbackFB
SharedSurface* mSurfToUnlock;
SharedSurface* mSurfToLock;
public:
ScopedReadbackFB(SharedSurface* src);
~ScopedReadbackFB();
};
+bool ReadbackSharedSurface(SharedSurface* src, gfx::DrawTarget* dst);
+
} // namespace gl
} // namespace mozilla
#endif // SHARED_SURFACE_H_
--- a/gfx/gl/SharedSurfaceGL.cpp
+++ b/gfx/gl/SharedSurfaceGL.cpp
@@ -78,85 +78,29 @@ SharedSurface_Basic::SharedSurface_Basic
mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
LOCAL_GL_COLOR_ATTACHMENT0,
LOCAL_GL_TEXTURE_2D,
mTex,
0);
DebugOnly<GLenum> status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
-
- int32_t stride = gfx::GetAlignedStride<4>(size.width * BytesPerPixel(format));
- mData = gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
- // Leave the extra return for clarity, in case we decide more code should
- // be added after this check, that should run even if mData is null.
- if (NS_WARN_IF(!mData)) {
- return;
- }
}
SharedSurface_Basic::~SharedSurface_Basic()
{
if (!mGL->MakeCurrent())
return;
if (mFB)
mGL->fDeleteFramebuffers(1, &mFB);
mGL->fDeleteTextures(1, &mTex);
}
-void
-SharedSurface_Basic::Fence()
-{
- // The constructor can fail to get us mData, we should deal with it:
- if (NS_WARN_IF(!mData)) {
- return;
- }
-
- mGL->MakeCurrent();
- ScopedBindFramebuffer autoFB(mGL, mFB);
- ReadPixelsIntoDataSurface(mGL, mData);
-}
-
-bool
-SharedSurface_Basic::WaitSync()
-{
- return true;
-}
-
-bool
-SharedSurface_Basic::PollSync()
-{
- return true;
-}
-
-void
-SharedSurface_Basic::Fence_ContentThread_Impl()
-{
-}
-
-bool
-SharedSurface_Basic::WaitSync_ContentThread_Impl()
-{
- mGL->MakeCurrent();
- ScopedBindFramebuffer autoFB(mGL, mFB);
- ReadPixelsIntoDataSurface(mGL, mData);
- return true;
-}
-
-bool