Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-03-05 13:47:31 +01:00
commit bcc4f80b2c
614 changed files with 9677 additions and 3471 deletions

View File

@ -15,9 +15,9 @@
#include "Logging.h"
#endif
#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
#include "nsCURILoader.h"
#include "nsDocShellLoadTypes.h"
#include "nsDOMEvent.h"
#include "nsIChannel.h"
#include "nsIDOMDocument.h"
#include "nsEventListenerManager.h"

View File

@ -28,6 +28,7 @@
#include "nsIAccessibleRelation.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocShellTreeOwner.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/EventTarget.h"
#include "nsIDOMDataContainerEvent.h"
#include "nsIDOMXULMultSelectCntrlEl.h"
@ -39,7 +40,6 @@
#include "nsIWebBrowserChrome.h"
#include "nsReadableUtils.h"
#include "nsFocusManager.h"
#include "nsDOMEvent.h"
#ifdef MOZ_XUL
#include "nsIXULDocument.h"
@ -234,7 +234,7 @@ NS_IMETHODIMP
RootAccessible::HandleEvent(nsIDOMEvent* aDOMEvent)
{
MOZ_ASSERT(aDOMEvent);
nsDOMEvent* event = aDOMEvent->InternalDOMEvent();
Event* event = aDOMEvent->InternalDOMEvent();
nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
if (!origTargetNode)
return NS_OK;
@ -266,7 +266,7 @@ void
RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
{
MOZ_ASSERT(aDOMEvent);
nsDOMEvent* event = aDOMEvent->InternalDOMEvent();
Event* event = aDOMEvent->InternalDOMEvent();
nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
nsAutoString eventType;

View File

@ -309,7 +309,7 @@ this.AccessFu = {
case 'Accessibility:Focus':
this._focused = JSON.parse(aData);
if (this._focused) {
this.showCurrent(true);
this.autoMove({ forcePresent: true, noOpIfOnScreen: true });
}
break;
case 'Accessibility:MoveByGranularity':
@ -353,10 +353,11 @@ this.AccessFu = {
// We delay this for half a second so the awesomebar could close,
// and we could use the current coordinates for the content item.
// XXX TODO figure out how to avoid magic wait here.
Utils.win.setTimeout(
function () {
this.showCurrent(false);
}.bind(this), 500);
this.autoMove({
delay: 500,
forcePresent: true,
noOpIfOnScreen: true,
moveMethod: 'moveFirst' });
}
break;
}
@ -372,9 +373,9 @@ this.AccessFu = {
}
},
showCurrent: function showCurrent(aMove) {
autoMove: function autoMove(aOptions) {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:ShowCurrent', { move: aMove });
mm.sendAsyncMessage('AccessFu:AutoMove', aOptions);
},
announce: function announce(aAnnouncement) {

View File

@ -0,0 +1,269 @@
/* 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/. */
let Ci = Components.interfaces;
let Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'Services',
'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Utils',
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'TraversalRules',
'resource://gre/modules/accessibility/TraversalRules.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Presentation',
'resource://gre/modules/accessibility/Presentation.jsm');
this.EXPORTED_SYMBOLS = ['ContentControl'];
this.ContentControl = function ContentControl(aContentScope) {
this._contentScope = Cu.getWeakReference(aContentScope);
this._vcCache = new WeakMap();
this._childMessageSenders = new WeakMap();
};
this.ContentControl.prototype = {
messagesOfInterest: ['AccessFu:MoveCursor',
'AccessFu:ClearCursor',
'AccessFu:MoveToPoint',
'AccessFu:AutoMove'],
start: function ContentControl_start() {
let cs = this._contentScope.get();
for (let message of this.messagesOfInterest) {
cs.addMessageListener(message, this);
}
},
stop: function ContentControl_stop() {
let cs = this._contentScope.get();
for (let message of this.messagesOfInterest) {
cs.removeMessageListener(message, this);
}
},
get document() {
return this._contentScope.get().content.document;
},
get window() {
return this._contentScope.get().content;
},
get vc() {
return Utils.getVirtualCursor(this.document);
},
receiveMessage: function ContentControl_receiveMessage(aMessage) {
Logger.debug(() => {
return ['ContentControl.receiveMessage',
this.document.location.toString(),
JSON.stringify(aMessage.json)];
});
try {
switch (aMessage.name) {
case 'AccessFu:MoveCursor':
this.handleMove(aMessage);
break;
case 'AccessFu:ClearCursor':
this.handleClear(aMessage);
break;
case 'AccessFu:MoveToPoint':
this.handleMoveToPoint(aMessage);
break;
case 'AccessFu:AutoMove':
this.handleAutoMove(aMessage);
break;
default:
break;
}
} catch (x) {
Logger.logException(
x, 'Error handling message: ' + JSON.stringify(aMessage.json));
}
},
handleMove: function ContentControl_handleMove(aMessage) {
let origin = aMessage.json.origin;
let action = aMessage.json.action;
let vc = this.vc;
if (origin != 'child' && this.sendToChild(vc, aMessage)) {
// Forwarded succesfully to child cursor.
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 {
// We potentially landed on a new child cursor. If so, we want to
// either be on the first or last item in the child doc.
let childAction = action;
if (action === 'moveNext') {
childAction = 'moveFirst';
} 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});
}
} else if (!this._childMessageSenders.has(aMessage.target)) {
// We failed to move, and the message is not from a child, so forward
// to parent.
this.sendToParent(aMessage);
}
},
handleMoveToPoint: function ContentControl_handleMoveToPoint(aMessage) {
let [x, y] = [aMessage.json.x, aMessage.json.y];
let rule = TraversalRules[aMessage.json.rule];
let vc = this.vc;
let win = this.window;
let dpr = win.devicePixelRatio;
this.vc.moveToPoint(rule, x * dpr, y * dpr, true);
let delta = Utils.isContentProcess ?
{ x: x - win.mozInnerScreenX, y: y - win.mozInnerScreenY } : {};
this.sendToChild(vc, aMessage, delta);
},
handleClear: function ContentControl_handleClear(aMessage) {
this.sendToChild(this.vc, aMessage);
this.vc.position = null;
},
handleAutoMove: function ContentControl_handleAutoMove(aMessage) {
this.autoMove(null, aMessage.json);
},
getChildCursor: function ContentControl_getChildCursor(aAccessible) {
let acc = aAccessible || this.vc.position;
if (Utils.isAliveAndVisible(acc) && acc.role === Roles.INTERNAL_FRAME) {
let domNode = acc.DOMNode;
let mm = this._childMessageSenders.get(domNode, null);
if (!mm) {
mm = Utils.getMessageManager(domNode);
mm.addWeakMessageListener('AccessFu:MoveCursor', this);
this._childMessageSenders.set(domNode, mm);
}
return mm;
}
return null;
},
sendToChild: function ContentControl_sendToChild(aVirtualCursor,
aMessage,
aReplacer) {
let mm = this.getChildCursor(aVirtualCursor.position);
if (!mm) {
return false;
}
// XXX: This is a silly way to make a deep copy
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
newJSON.origin = 'parent';
for (let attr in aReplacer) {
newJSON[attr] = aReplacer[attr];
}
mm.sendAsyncMessage(aMessage.name, newJSON);
return true;
},
sendToParent: function ContentControl_sendToParent(aMessage) {
// XXX: This is a silly way to make a deep copy
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
newJSON.origin = 'child';
aMessage.target.sendAsyncMessage(aMessage.name, newJSON);
},
/**
* Move cursor and/or present its location.
* aOptions could have any of these fields:
* - delay: in ms, before actual move is performed. Another autoMove call
* would cancel it. Useful if we want to wait for a possible trailing
* focus move. Default 0.
* - noOpIfOnScreen: if accessible is alive and visible, don't do anything.
* - forcePresent: present cursor location, whether we move or don't.
* - moveToFocused: if there is a focused accessible move to that. This takes
* precedence over given anchor.
* - moveMethod: pivot move method to use, default is 'moveNext',
*/
autoMove: function ContentControl_autoMove(aAnchor, aOptions = {}) {
let win = this.window;
win.clearTimeout(this._autoMove);
let moveFunc = () => {
let vc = this.vc;
let acc = aAnchor;
let rule = aOptions.onScreenOnly ?
TraversalRules.SimpleOnScreen : TraversalRules.Simple;
let forcePresentFunc = () => {
if (aOptions.forcePresent) {
this._contentScope.get().sendAsyncMessage(
'AccessFu:Present', Presentation.pivotChanged(
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE,
vc.startOffset, vc.endOffset));
}
};
if (aOptions.noOpIfOnScreen &&
Utils.isAliveAndVisible(vc.position, true)) {
forcePresentFunc();
return;
}
if (aOptions.moveToFocused) {
acc = Utils.AccRetrieval.getAccessibleFor(
this.document.activeElement) || acc;
}
let moved = false;
let moveMethod = aOptions.moveMethod || 'moveNext'; // default is moveNext
let moveFirstOrLast = moveMethod in ['moveFirst', 'moveLast'];
if (!moveFirstOrLast || acc) {
// We either need next/previous or there is an anchor we need to use.
moved = vc[moveFirstOrLast ? 'moveNext' : moveMethod](rule, acc, true);
}
if (moveFirstOrLast && !moved) {
// We move to first/last after no anchor move happened or succeeded.
moved = vc[moveMethod](rule);
}
let sentToChild = this.sendToChild(vc, {
name: 'AccessFu:AutoMove',
json: aOptions
});
if (!moved && !sentToChild) {
forcePresentFunc();
}
};
if (aOptions.delay) {
this._autoMove = win.setTimeout(moveFunc, aOptions.delay);
} else {
moveFunc();
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
Ci.nsIMessageListener
])
};

View File

@ -98,18 +98,11 @@ this.EventManager.prototype = {
case 'wheel':
{
let attempts = 0;
let vc = Utils.getVirtualCursor(this.contentScope.content.document);
let intervalId = this.contentScope.content.setInterval(() => {
if (!Utils.isAliveAndVisible(vc.position, true)) {
this.contentScope.content.clearInterval(intervalId);
let delta = aEvent.deltaX || aEvent.deltaY;
this.contentScope.content.setTimeout(() => {
vc[delta > 0 ? 'moveNext' : 'movePrevious'](TraversalRules.SimpleOnScreen);
}, 100);
} else if (++attempts > 5) {
this.contentScope.content.clearInterval(intervalId);
}
}, 150);
let delta = aEvent.deltaX || aEvent.deltaY;
this.contentScope.contentControl.autoMove(
null,
{ moveMethod: delta > 0 ? 'moveNext' : 'movePrevious',
onScreenOnly: true, noOpIfOnScreen: true, delay: 500 });
break;
}
case 'scroll':
@ -162,11 +155,6 @@ this.EventManager.prototype = {
let reason = event.reason;
let oldAccessible = event.oldAccessible;
if (oldAccessible && oldAccessible.role == Roles.INTERNAL_FRAME) {
let mm = Utils.getMessageManager(oldAccessible.DOMNode);
mm.sendAsyncMessage('AccessFu:ClearCursor', {});
}
if (this.editState.editing) {
aEvent.accessibleDocument.takeFocus();
}
@ -195,8 +183,7 @@ this.EventManager.prototype = {
}
case Events.SCROLLING_START:
{
let vc = Utils.getVirtualCursor(this.contentScope.content.document);
vc.moveNext(TraversalRules.Simple, aEvent.accessible, true);
this.contentScope.contentControl.autoMove(aEvent.accessible);
break;
}
case Events.TEXT_CARET_MOVED:
@ -253,18 +240,25 @@ this.EventManager.prototype = {
}
case Events.HIDE:
{
let evt = aEvent.QueryInterface(Ci.nsIAccessibleHideEvent);
let {liveRegion, isPolite} = this._handleLiveRegion(
aEvent.QueryInterface(Ci.nsIAccessibleHideEvent),
['removals', 'all']);
// Only handle hide if it is a relevant live region.
if (!liveRegion) {
break;
evt, ['removals', 'all']);
if (liveRegion) {
// Hide for text is handled by the EVENT_TEXT_REMOVED handler.
if (aEvent.accessible.role === Roles.TEXT_LEAF) {
break;
}
this._queueLiveEvent(Events.HIDE, liveRegion, isPolite);
} else {
let vc = Utils.getVirtualCursor(this.contentScope.content.document);
if (vc.position &&
(Utils.getState(vc.position).contains(States.DEFUNCT) ||
Utils.isInSubtree(vc.position, aEvent.accessible))) {
this.contentScope.contentControl.autoMove(
evt.targetPrevSibling || evt.targetParent,
{ moveToFocused: true, delay: 500 });
}
}
// Hide for text is handled by the EVENT_TEXT_REMOVED handler.
if (aEvent.accessible.role === Roles.TEXT_LEAF) {
break;
}
this._queueLiveEvent(Events.HIDE, liveRegion, isPolite);
break;
}
case Events.TEXT_INSERTED:
@ -285,18 +279,14 @@ this.EventManager.prototype = {
let acc = aEvent.accessible;
let doc = aEvent.accessibleDocument;
if (acc.role != Roles.DOCUMENT && doc.role != Roles.CHROME_WINDOW) {
this.contentScope.content.clearTimeout(this._autoMove);
let vc = Utils.getVirtualCursor(this.contentScope.content.document);
vc.moveNext(TraversalRules.Simple, acc, true);
}
break;
this.contentScope.contentControl.autoMove(acc);
}
break;
}
case Events.DOCUMENT_LOAD_COMPLETE:
{
this._autoMove = this.contentScope.content.setTimeout(() => {
Utils.getVirtualCursor(this.contentScope.content.document)
.moveNext(TraversalRules.Simple, aEvent.accessible, true);
}, 500);
this.contentScope.contentControl.autoMove(
aEvent.accessible, { delay: 500 });
break;
}
}

View File

@ -247,6 +247,24 @@ this.Utils = {
return new Rect(objX.value, objY.value, objW.value, objH.value);
},
isInSubtree: function isInSubtree(aAccessible, aSubTreeRoot) {
let acc = aAccessible;
while (acc) {
if (acc == aSubTreeRoot) {
return true;
}
try {
acc = acc.parent;
} catch (x) {
Logger.debug('Failed to get parent:', x);
acc = null;
}
}
return false;
},
inHiddenSubtree: function inHiddenSubtree(aAccessible) {
for (let acc=aAccessible; acc; acc=acc.parent) {
let hidden = Utils.getAttributes(acc).hidden;
@ -565,8 +583,13 @@ PivotContext.prototype = {
_getAncestry: function _getAncestry(aAccessible) {
let ancestry = [];
let parent = aAccessible;
while (parent && (parent = parent.parent)) {
ancestry.push(parent);
try {
while (parent && (parent = parent.parent)) {
ancestry.push(parent);
}
} catch (e) {
// A defunct accessible will raise an exception geting parent.
Logger.debug('Failed to get parent:', x);
}
return ancestry.reverse();
},

View File

@ -20,118 +20,15 @@ XPCOMUtils.defineLazyModuleGetter(this, 'Utils',
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'EventManager',
'resource://gre/modules/accessibility/EventManager.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'ContentControl',
'resource://gre/modules/accessibility/ContentControl.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
'resource://gre/modules/accessibility/Constants.jsm');
Logger.debug('content-script.js');
let eventManager = null;
function clearCursor(aMessage) {
try {
Utils.getVirtualCursor(content.document).position = null;
forwardToChild(aMessage);
} catch (x) {
Logger.logException(x);
}
}
function moveCursor(aMessage) {
if (Logger.logLevel >= Logger.DEBUG) {
Logger.debug(aMessage.name, JSON.stringify(aMessage.json, null, ' '));
}
let vc = Utils.getVirtualCursor(content.document);
let origin = aMessage.json.origin;
let action = aMessage.json.action;
let rule = TraversalRules[aMessage.json.rule];
function moveCursorInner() {
try {
if (origin == 'parent' &&
!Utils.isAliveAndVisible(vc.position)) {
// We have a bad position in this frame, move vc to last or first item.
if (action == 'moveNext') {
return vc.moveFirst(rule);
} else if (action == 'movePrevious') {
return vc.moveLast(rule);
}
}
return vc[action](rule);
} catch (x) {
if (action == 'moveNext' || action == 'movePrevious') {
// If we are trying to move next/prev put the vc on the focused item.
let acc = Utils.AccRetrieval.
getAccessibleFor(content.document.activeElement);
return vc.moveNext(rule, acc, true);
} else {
throw x;
}
}
return false;
}
try {
if (origin != 'child' &&
forwardToChild(aMessage, moveCursor, vc.position)) {
// We successfully forwarded the move to the child document.
return;
}
if (moveCursorInner()) {
// If we moved, try forwarding the message to the new position,
// it may be a frame with a vc of its own.
forwardToChild(aMessage, moveCursor, vc.position);
} else {
// If we did not move, we probably reached the end or start of the
// document, go back to parent content and move us out of the iframe.
if (origin == 'parent') {
vc.position = null;
}
forwardToParent(aMessage);
}
} catch (x) {
Logger.logException(x, 'Cursor move failed');
}
}
function moveToPoint(aMessage) {
if (Logger.logLevel >= Logger.DEBUG) {
Logger.debug(aMessage.name, JSON.stringify(aMessage.json, null, ' '));
}
let vc = Utils.getVirtualCursor(content.document);
let details = aMessage.json;
let rule = TraversalRules[details.rule];
try {
let dpr = content.devicePixelRatio;
vc.moveToPoint(rule, details.x * dpr, details.y * dpr, true);
forwardToChild(aMessage, moveToPoint, vc.position);
} catch (x) {
Logger.logException(x, 'Failed move to point');
}
}
function showCurrent(aMessage) {
Logger.debug(() => {
return [aMessage.name, JSON.stringify(aMessage.json, null, ' ')];
});
let vc = Utils.getVirtualCursor(content.document);
if (!forwardToChild(vc, showCurrent, aMessage)) {
if (!vc.position && aMessage.json.move) {
vc.moveFirst(TraversalRules.Simple);
} else {
sendAsyncMessage('AccessFu:Present', Presentation.pivotChanged(
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE,
vc.startOffset, vc.endOffset));
}
}
}
let contentControl = null;
function forwardToParent(aMessage) {
// XXX: This is a silly way to make a deep copy
@ -385,22 +282,23 @@ addMessageListener(
if (m.json.buildApp)
Utils.MozBuildApp = m.json.buildApp;
addMessageListener('AccessFu:MoveToPoint', moveToPoint);
addMessageListener('AccessFu:MoveCursor', moveCursor);
addMessageListener('AccessFu:ShowCurrent', showCurrent);
addMessageListener('AccessFu:Activate', activateCurrent);
addMessageListener('AccessFu:ContextMenu', activateContextMenu);
addMessageListener('AccessFu:Scroll', scroll);
addMessageListener('AccessFu:AdjustRange', adjustRange);
addMessageListener('AccessFu:MoveCaret', moveCaret);
addMessageListener('AccessFu:MoveByGranularity', moveByGranularity);
addMessageListener('AccessFu:ClearCursor', clearCursor);
if (!eventManager) {
eventManager = new EventManager(this);
}
eventManager.start();
if (!contentControl) {
contentControl = new ContentControl(this);
}
contentControl.start();
sendAsyncMessage('AccessFu:ContentStarted');
});
@ -409,17 +307,14 @@ addMessageListener(
function(m) {
Logger.debug('AccessFu:Stop');
removeMessageListener('AccessFu:MoveToPoint', moveToPoint);
removeMessageListener('AccessFu:MoveCursor', moveCursor);
removeMessageListener('AccessFu:ShowCurrent', showCurrent);
removeMessageListener('AccessFu:Activate', activateCurrent);
removeMessageListener('AccessFu:ContextMenu', activateContextMenu);
removeMessageListener('AccessFu:Scroll', scroll);
removeMessageListener('AccessFu:MoveCaret', moveCaret);
removeMessageListener('AccessFu:MoveByGranularity', moveByGranularity);
removeMessageListener('AccessFu:ClearCursor', clearCursor);
eventManager.stop();
contentControl.stop();
});
sendAsyncMessage('AccessFu:Ready');

View File

@ -9,6 +9,7 @@ JS_MODULES_PATH = 'modules/accessibility'
EXTRA_JS_MODULES += [
'AccessFu.jsm',
'Constants.jsm',
'ContentControl.jsm',
'EventManager.jsm',
'OutputGenerator.jsm',
'Presentation.jsm',

View File

@ -12,18 +12,56 @@
'</body>' +
'</html>';
function showAlert() {
document.getElementById('alert').hidden = false;
}
function hideAlert() {
document.getElementById('alert').hidden = true;
}
</script>
<style>
#windows > iframe {
#windows {
position: relative;
width: 320px;
height: 480px;
}
#windows > iframe {
z-index: 1;
}
#windows > div[role='dialog'] {
z-index: 2;
background-color: pink;
}
#windows > * {
position: absolute;
width: 100%;
height: 100%;
}
iframe {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div>Phone status bar</div>
<div id="windows"></div>
<button>Home</button>
<div id="windows">
<div id="appframe"></div>
<div role="dialog" id="alert" hidden>
<h1>This is an alert!</h1>
<p>Do you agree?</p>
<button onclick="hideAlert()">Yes</button>
<button onclick="hideAlert()">No</button>
</div>
</div>
<button id="home">Home</button>
</body>
</html>

View File

@ -216,13 +216,17 @@ AccessFuContentTest.prototype = {
if (this.currentPair) {
if (this.currentPair[0] instanceof Function) {
this.currentPair[0](this.mms[0]);
} else {
} else if (this.currentPair[0]) {
this.mms[0].sendAsyncMessage(this.currentPair[0].name,
this.currentPair[0].json);
this.currentPair[0].json);
}
if (!this.currentPair[1]) {
this.pump();
}
} else if (this.finishedCallback) {
for (var mm of this.mms) {
mm.sendAsyncMessage('AccessFu:Stop');
mm.sendAsyncMessage('AccessFu:Stop');
}
this.finishedCallback();
}

View File

@ -125,10 +125,10 @@
// Move cursor with focus in outside document
[simpleMoveNext,
{ speak: 'Phone status bar Traversal Rule test document' }],
[ focusFunc('button', false), { speak: 'Home button' }],
[ focusFunc('button#home', false), { speak: 'Home button' }],
// Blur button and reset cursor
[focusFunc('button', true), null],
[focusFunc('button#home', true), null],
[clearCursor, null],
// Set focus on element outside of embedded frame while cursor is in frame
@ -136,12 +136,53 @@
{ speak: 'Phone status bar Traversal Rule test document' }],
[simpleMoveNext,
{ speak: 'wow heading level 1 such app' }],
[focusFunc('button', false), { speak: 'Home button' }]
[focusFunc('button#home', false), { speak: 'Home button' }]
// Blur button and reset cursor
[focusFunc('button#home', true), null],
[clearCursor, null],
// XXX: Set focus on iframe itself.
// XXX: Set focus on element in iframe when cursor is outside of it.
// XXX: Set focus on element in iframe when cursor is in iframe.
]);
// Open dialog in outer doc, while cursor is also in outer doc
[simpleMoveNext,
{ speak: 'Phone status bar Traversal Rule test document' }],
[doc.defaultView.showAlert,
{ speak: 'This is an alert! heading level 1 dialog' }],
[function () { doc.defaultView.hideAlert() },
{ speak: 'wow heading level 1 such app' }],
[clearCursor, null],
// Open dialog in outer doc, while cursor is in inner frame
[simpleMoveNext,
{ speak: 'Phone status bar Traversal Rule test document' }],
[simpleMoveNext,
{ speak: 'wow heading level 1 such app' }],
[doc.defaultView.showAlert,
{ speak: 'This is an alert! heading level 1 dialog' }],
// XXX: Place cursor back where it was.
[doc.defaultView.hideAlert,
{ speak: 'many option not checked check button such app' }],
[clearCursor, null],
// Open dialog, then focus on something when closing
[simpleMoveNext,
{ speak: 'Phone status bar Traversal Rule test document' }],
[doc.defaultView.showAlert,
{ speak: 'This is an alert! heading level 1 dialog' }],
[function () {
doc.defaultView.hideAlert();
doc.querySelector('button#home').focus();
},
{ speak: 'Home button Traversal Rule test document' }]
]);
contentTest.start(function () {
closeBrowserWindow();
@ -150,7 +191,7 @@
});
iframe.src = 'data:text/html;charset=utf-8,' + doc.defaultView.frameContents;
doc.querySelector('#windows').appendChild(iframe);
doc.getElementById('appframe').appendChild(iframe);
}
SimpleTest.waitForExplicitFinish();

View File

@ -28,8 +28,6 @@ LIBS += \
endif
endif
STL_FLAGS=
LIBS += $(JEMALLOC_LIBS)
LIBS += \

View File

@ -41,3 +41,5 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
]
LDFLAGS += ['-Wl,--export-dynamic']
DISABLE_STL_WRAPPING = True

View File

@ -26,8 +26,6 @@ endif #} LIBXUL_SDK
# Build a binary bootstrapping with XRE_main
STL_FLAGS=
LIBS += \
$(XPCOM_STANDALONE_GLUE_LDOPTS) \
$(NULL)
@ -55,7 +53,6 @@ NSDISTMODE = copy
include $(topsrcdir)/config/config.mk
ifeq ($(OS_ARCH),WINNT)
RCINCLUDE = splash.rc
# Rebuild firefox.exe if the manifest changes - it's included by splash.rc.
# (this dependency should really be just for firefox.exe, not other targets)
# Note the manifest file exists in the tree, so we use the explicit filename

View File

@ -39,6 +39,7 @@ if CONFIG['_MSC_VER']:
WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
if CONFIG['OS_ARCH'] == 'WINNT':
RCINCLUDE = 'splash.rc'
DEFINES['MOZ_PHOENIX'] = True
# Control the default heap size.
@ -51,3 +52,5 @@ if CONFIG['OS_ARCH'] == 'WINNT':
# Set it to 256k. See bug 127069.
if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']:
LDFLAGS += ['/HEAP:0x40000']
DISABLE_STL_WRAPPING = True

View File

@ -81,17 +81,31 @@ function promiseMessage(aMessage, aAction) {
function promisePopupNotification(aName, aShown) {
let deferred = Promise.defer();
// If aShown is true, the notification is expected to be opened by
// default and we wait for the panel to be populated; for dismissed
// notifications, we are happy as soon as we find the icon.
waitForCondition(() => PopupNotifications.getNotification(aName) &&
(!aShown || PopupNotifications.panel.firstChild),
waitForCondition(() => PopupNotifications.getNotification(aName),
() => {
ok(!!PopupNotifications.getNotification(aName),
aName + " notification appeared");
if (aShown)
ok(PopupNotifications.panel.firstChild, "notification panel populated");
deferred.resolve();
let notification = PopupNotifications.getNotification(aName);
ok(!!notification, aName + " notification appeared");
if (!notification || !aShown) {
deferred.resolve();
return;
}
// If aShown is true, the notification is expected to be opened by
// default, so we check that the panel has been populated.
if (PopupNotifications.panel.firstChild) {
ok(true, "notification panel populated");
deferred.resolve();
}
else {
todo(false, "We shouldn't have to force re-open the panel, see bug 976544");
notification.reshow();
waitForCondition(() => PopupNotifications.panel.firstChild,
() => {
ok(PopupNotifications.panel.firstChild, "notification panel populated");
deferred.resolve();
}, "timeout waiting for notification to be reshown");
}
}, "timeout waiting for popup notification " + aName);
return deferred.promise;

View File

@ -154,6 +154,10 @@ const PanelUI = {
this.panel.addEventListener("popupshown", function onPopupShown() {
this.removeEventListener("popupshown", onPopupShown);
// As an optimization for the customize mode transition, we preload
// about:customizing in the background once the menu panel is first
// shown.
gCustomizationTabPreloader.ensurePreloading();
deferred.resolve();
});
});

View File

@ -416,6 +416,11 @@ const CustomizableWidgets = [{
let zoomResetButton = node.childNodes[2];
let window = aDocument.defaultView;
function updateZoomResetButton() {
let updateDisplay = true;
// Label should always show 100% in customize mode, so don't update:
if (aDocument.documentElement.hasAttribute("customizing")) {
updateDisplay = false;
}
//XXXgijs in some tests we get called very early, and there's no docShell on the
// tabbrowser. This breaks the zoom toolkit code (see bug 897410). Don't let that happen:
let zoomFactor = 100;
@ -423,7 +428,7 @@ const CustomizableWidgets = [{
zoomFactor = Math.floor(window.ZoomManager.zoom * 100);
} catch (e) {}
zoomResetButton.setAttribute("label", CustomizableUI.getLocalizedProperty(
buttons[1], "label", [zoomFactor]
buttons[1], "label", [updateDisplay ? zoomFactor : 100]
));
};
@ -508,6 +513,18 @@ const CustomizableWidgets = [{
container.removeEventListener("TabSelect", updateZoomResetButton);
}.bind(this),
onCustomizeStart: function(aWindow) {
if (aWindow.document == aDocument) {
updateZoomResetButton();
}
},
onCustomizeEnd: function(aWindow) {
if (aWindow.document == aDocument) {
updateZoomResetButton();
}
},
onWidgetDrag: function(aWidgetId, aArea) {
if (aWidgetId != this.id)
return;

View File

@ -195,6 +195,7 @@ CustomizeMode.prototype = {
// Hide the palette before starting the transition for increased perf.
this.visiblePalette.hidden = true;
this.visiblePalette.removeAttribute("showing");
// Disable the button-text fade-out mask
// during the transition for increased perf.
@ -262,6 +263,12 @@ CustomizeMode.prototype = {
// Show the palette now that the transition has finished.
this.visiblePalette.hidden = false;
window.setTimeout(() => {
// Force layout reflow to ensure the animation runs,
// and make it async so it doesn't affect the timing.
this.visiblePalette.clientTop;
this.visiblePalette.setAttribute("showing", "true");
}, 0);
this.paletteSpacer.hidden = true;
this._updateEmptyPaletteNotice();
@ -269,6 +276,16 @@ CustomizeMode.prototype = {
panelContents.removeAttribute("customize-transitioning");
CustomizableUI.dispatchToolboxEvent("customizationready", {}, window);
this._enableOutlinesTimeout = window.setTimeout(() => {
this.document.getElementById("nav-bar").setAttribute("showoutline", "true");
this.panelUIContents.setAttribute("showoutline", "true");
delete this._enableOutlinesTimeout;
}, 0);
// It's possible that we didn't enter customize mode via the menu panel,
// meaning we didn't kick off about:customizing preloading. If that's
// the case, let's kick it off for the next time we load this mode.
window.gCustomizationTabPreloader.ensurePreloading();
if (!this._wantToBeInCustomizeMode) {
this.exit();
}
@ -302,6 +319,13 @@ CustomizeMode.prototype = {
this._handler.isExitingCustomizeMode = true;
if (this._enableOutlinesTimeout) {
this.window.clearTimeout(this._enableOutlinesTimeout);
} else {
this.document.getElementById("nav-bar").removeAttribute("showoutline");
this.panelUIContents.removeAttribute("showoutline");
}
this._removeExtraToolbarsIfEmpty();
CustomizableUI.removeListener(this);
@ -321,6 +345,7 @@ CustomizeMode.prototype = {
// Hide the palette before starting the transition for increased perf.
this.paletteSpacer.hidden = false;
this.visiblePalette.hidden = true;
this.visiblePalette.removeAttribute("showing");
this.paletteEmptyNotice.hidden = true;
// Disable the button-text fade-out mask
@ -340,10 +365,34 @@ CustomizeMode.prototype = {
yield this._doTransition(false);
let browser = document.getElementById("browser");
if (this.browser.selectedBrowser.currentURI.spec == kAboutURI) {
let custBrowser = this.browser.selectedBrowser;
if (custBrowser.canGoBack) {
// If there's history to this tab, just go back.
// Note that this throws an exception if the previous document has a
// problematic URL (e.g. about:idontexist)
try {
custBrowser.goBack();
} catch (ex) {
ERROR(ex);
}
} else {
// If we can't go back, we're removing the about:customization tab.
// We only do this if we're the top window for this window (so not
// a dialog window, for example).
if (window.getTopWin(true) == window) {
let customizationTab = this.browser.selectedTab;
if (this.browser.browsers.length == 1) {
window.BrowserOpenTab();
}
this.browser.removeTab(customizationTab);
}
}
}
browser.parentNode.selectedPanel = browser;
let customizer = document.getElementById("customization-container");
customizer.hidden = true;
let browser = document.getElementById("browser");
browser.parentNode.selectedPanel = browser;
window.gNavToolbox.removeEventListener("toolbarvisibilitychange", this);
@ -398,31 +447,6 @@ CustomizeMode.prototype = {
mainView.setAttribute("context", this._mainViewContext);
}
if (this.browser.selectedBrowser.currentURI.spec == kAboutURI) {
let custBrowser = this.browser.selectedBrowser;
if (custBrowser.canGoBack) {
// If there's history to this tab, just go back.
// Note that this throws an exception if the previous document has a
// problematic URL (e.g. about:idontexist)
try {
custBrowser.goBack();
} catch (ex) {
ERROR(ex);
}
} else {
// If we can't go back, we're removing the about:customization tab.
// We only do this if we're the top window for this window (so not
// a dialog window, for example).
if (window.getTopWin(true) == window) {
let customizationTab = this.browser.selectedTab;
if (this.browser.browsers.length == 1) {
window.BrowserOpenTab();
}
this.browser.removeTab(customizationTab);
}
}
}
if (this.document.documentElement._lightweightTheme)
this.document.documentElement._lightweightTheme.enable();

View File

@ -472,7 +472,6 @@ BrowserGlue.prototype = {
PageThumbs.init();
NewTabUtils.init();
BrowserNewTabPreloader.init();
CustomizationTabPreloader.init();
SignInToWebsiteUX.init();
PdfJs.init();
#ifdef NIGHTLY_BUILD

View File

@ -291,8 +291,8 @@ PlacesController.prototype = {
* @param aIsMoveCommand
* True if the command for which this method is called only moves the
* selected items to another container, false otherwise.
* @returns true if all nodes in the selection can be removed,
* false otherwise.
* @return true if all nodes in the selection can be removed,
* false otherwise.
*/
_hasRemovableSelection: function PC__hasRemovableSelection(aIsMoveCommand) {
var ranges = this._view.removableSelectionRanges;
@ -356,9 +356,9 @@ PlacesController.prototype = {
* Looks at the data on the clipboard to see if it is paste-able.
* Paste-able data is:
* - in a format that the view can receive
* @returns true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor,
- clipboard data is of type TEXT_UNICODE and
is a valid URI.
* @return true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor,
* - clipboard data is of type TEXT_UNICODE and
* is a valid URI.
*/
_isClipboardDataPasteable: function PC__isClipboardDataPasteable() {
// if the clipboard contains TYPE_X_MOZ_PLACE_* data, it is definitely
@ -413,10 +413,10 @@ PlacesController.prototype = {
* "separator" node is a separator line
* "host" node is a host
*
* @returns an array of objects corresponding the selected nodes. Each
* object has each of the properties above set if its corresponding
* node matches the rule. In addition, the annotations names for each
* node are set on its corresponding object as properties.
* @return an array of objects corresponding the selected nodes. Each
* object has each of the properties above set if its corresponding
* node matches the rule. In addition, the annotations names for each
* node are set on its corresponding object as properties.
* Notes:
* 1) This can be slow, so don't call it anywhere performance critical!
* 2) A single-object array corresponding the root node is returned if
@ -503,8 +503,8 @@ PlacesController.prototype = {
* the context menu item
* @param aMetaData
* meta data about the selection
* @returns true if the conditions (see buildContextMenu) are satisfied
* and the item can be displayed, false otherwise.
* @return true if the conditions (see buildContextMenu) are satisfied
* and the item can be displayed, false otherwise.
*/
_shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) {
var selectiontype = aMenuItem.getAttribute("selectiontype");
@ -782,7 +782,7 @@ PlacesController.prototype = {
* Node to check for containment.
* @param pastFolders
* List of folders the calling function has already traversed
* @returns true if the node should be skipped, false otherwise.
* @return true if the node should be skipped, false otherwise.
*/
_shouldSkipNode: function PC_shouldSkipNode(node, pastFolders) {
/**
@ -791,7 +791,7 @@ PlacesController.prototype = {
* The node to check for containment for
* @param parent
* The parent container to check for containment in
* @returns true if node is a member of parent's children, false otherwise.
* @return true if node is a member of parent's children, false otherwise.
*/
function isContainedBy(node, parent) {
var cursor = node.parent;
@ -1008,7 +1008,6 @@ PlacesController.prototype = {
*/
setDataTransfer: function PC_setDataTransfer(aEvent) {
let dt = aEvent.dataTransfer;
let doCopy = ["copyLink", "copy", "link"].indexOf(dt.effectAllowed) != -1;
let result = this._view.result;
let didSuppressNotifications = result.suppressNotifications;
@ -1016,7 +1015,7 @@ PlacesController.prototype = {
result.suppressNotifications = true;
function addData(type, index, overrideURI) {
let wrapNode = PlacesUtils.wrapNode(node, type, overrideURI, doCopy);
let wrapNode = PlacesUtils.wrapNode(node, type, overrideURI);
dt.mozSetDataAt(type, wrapNode, index);
}
@ -1116,11 +1115,10 @@ PlacesController.prototype = {
let livemarkInfo = this.getCachedLivemarkInfo(node);
let overrideURI = livemarkInfo ? livemarkInfo.feedURI.spec : null;
let resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node);
contents.forEach(function (content) {
content.entries.push(
PlacesUtils.wrapNode(node, content.type, overrideURI, resolveShortcuts)
PlacesUtils.wrapNode(node, content.type, overrideURI)
);
});
}, this);
@ -1262,6 +1260,13 @@ PlacesController.prototype = {
if (ip.index != PlacesUtils.bookmarks.DEFAULT_INDEX)
insertionIndex = ip.index + i;
// If this is not a copy, check for safety that we can move the source,
// otherwise report an error and fallback to a copy.
if (action != "copy" && !PlacesControllerDragHelper.canMoveUnwrappedNode(items[i])) {
Components.utils.reportError("Tried to move an unmovable Places node, " +
"reverting to a copy operation.");
action = "copy";
}
transactions.push(
PlacesUIUtils.makeTransaction(items[i], type, ip.itemId,
insertionIndex, action == "copy")
@ -1304,7 +1309,7 @@ PlacesController.prototype = {
* @param aNode
* a places result node.
* @return true if there's a cached mozILivemarkInfo object for
* aNode, false otherwise.
* aNode, false otherwise.
*/
hasCachedLivemarkInfo: function PC_hasCachedLivemarkInfo(aNode)
this._cachedLivemarkInfoObjects.has(aNode),
@ -1338,8 +1343,8 @@ let PlacesControllerDragHelper = {
* mouse is dragging over one of its submenus
* @param node
* The container node
* @returns true if the user is dragging over a node within the hierarchy of
* the container, false otherwise.
* @return true if the user is dragging over a node within the hierarchy of
* the container, false otherwise.
*/
draggingOverChildNode: function PCDH_draggingOverChildNode(node) {
let currentNode = this.currentDropTarget;
@ -1352,7 +1357,7 @@ let PlacesControllerDragHelper = {
},
/**
* @returns The current active drag session. Returns null if there is none.
* @return The current active drag session. Returns null if there is none.
*/
getSession: function PCDH__getSession() {
return this.dragService.getCurrentSession();
@ -1438,13 +1443,28 @@ let PlacesControllerDragHelper = {
return true;
},
/**
* Determines if an unwrapped node can be moved.
*
* @param aUnwrappedNode
* A node unwrapped by PlacesUtils.unwrapNodes().
* @return True if the node can be moved, false otherwise.
*/
canMoveUnwrappedNode: function (aUnwrappedNode) {
return aUnwrappedNode.id > 0 &&
!PlacesUtils.isRootItem(aUnwrappedNode.id) &&
aUnwrappedNode.parent != PlacesUtils.placesRootId &&
aUnwrappedNode.parent != PlacesUtils.tagsFolderId &&
aUnwrappedNode.grandParentId != PlacesUtils.tagsFolderId &&
!aUnwrappedNode.parentReadOnly;
},
/**
* Determines if a node can be moved.
*
* @param aNode
* A nsINavHistoryResultNode node.
* @returns True if the node can be moved, false otherwise.
* @return True if the node can be moved, false otherwise.
*/
canMoveNode:
function PCDH_canMoveNode(aNode) {
@ -1478,7 +1498,7 @@ let PlacesControllerDragHelper = {
* A bookmark folder id.
* @param [optional] aParentId
* The parent id of the folder.
* @returns True if the container can be moved to the target.
* @return True if the container can be moved to the target.
*/
canMoveContainer:
function PCDH_canMoveContainer(aId, aParentId) {
@ -1555,6 +1575,13 @@ let PlacesControllerDragHelper = {
transactions.push(tagTxn);
}
else {
// If this is not a copy, check for safety that we can move the source,
// otherwise report an error and fallback to a copy.
if (!doCopy && !PlacesControllerDragHelper.canMoveUnwrappedNode(unwrapped)) {
Components.utils.reportError("Tried to move an unmovable Places node, " +
"reverting to a copy operation.");
doCopy = true;
}
transactions.push(PlacesUIUtils.makeTransaction(unwrapped,
flavor, insertionPoint.itemId,
index, doCopy));

View File

@ -857,6 +857,8 @@ var gEditItemOverlay = {
var txn = new PlacesCreateFolderTransaction(defaultLabel, ip.itemId, ip.index);
PlacesUtils.transactionManager.doTransaction(txn);
this._folderTree.focus();
this._folderTree.selectItems([ip.itemId]);
PlacesUtils.asContainer(this._folderTree.selectedNode).containerOpen = true;
this._folderTree.selectItems([this._lastNewItem]);
this._folderTree.startEditing(this._folderTree.view.selection.currentIndex,
this._folderTree.columns.getFirstColumn());

View File

@ -62,7 +62,7 @@ var PlacesOrganizer = {
for (let container of hierarchy) {
switch (typeof container) {
case "number":
this._places.selectItems([container]);
this._places.selectItems([container], false);
break;
case "string":
if (container.substr(0, 6) == "place:")
@ -327,7 +327,7 @@ var PlacesOrganizer = {
openFlatContainer: function PO_openFlatContainerFlatContainer(aContainer) {
if (aContainer.itemId != -1)
this._places.selectItems([aContainer.itemId]);
this._places.selectItems([aContainer.itemId], false);
else if (PlacesUtils.nodeIsQuery(aContainer))
this._places.selectPlaceURI(aContainer.uri);
},

View File

@ -45,7 +45,7 @@
<property name="view">
<getter><![CDATA[
try {
return this.treeBoxObject.view.wrappedJSObject;
return this.treeBoxObject.view.wrappedJSObject || null;
}
catch(e) {
return null;
@ -312,9 +312,9 @@
for (let i = 0; i < rc; ++i) {
let min = { }, max = { };
selection.getRangeAt(i, min, max);
for (let j = min.value; j <= max.value; ++j)
for (let j = min.value; j <= max.value; ++j) {
nodes.push(resultview.nodeForTreeIndex(j));
}
}
return nodes;
]]></getter>
@ -453,8 +453,6 @@
//
// If the sole selection is the bookmarks toolbar folder, we insert
// into it even if it is not opened
var itemId =
PlacesUtils.getConcreteItemId(resultView.nodeForTreeIndex(max.value));
if (selection.count == 1 && resultView.isContainer(max.value) &&
!this.flatList)
orientation = Ci.nsITreeView.DROP_ON;
@ -546,13 +544,16 @@
</method>
<!-- This method will select the first node in the tree that matches
each given item id. It will open any parent nodes that it needs
each given item id. It will open any folder nodes that it needs
to in order to show the selected items.
-->
<method name="selectItems">
<parameter name="aIDs"/>
<parameter name="aOpenContainers"/>
<body><![CDATA[
// Never open containers in flat lists.
if (this.flatList)
aOpenContainers = false;
// By default, we do search and select within containers which were
// closed (note that containers in which nodes were not found are
// closed).
@ -601,8 +602,13 @@
nodesURIChecked.indexOf(node.uri) != -1)
return foundOne;
// Don't try to open a query or a shurtcut, since it may return
// any duplicate data and be infinitely nested. Though, if it has
// been explicitly opened by the caller, search into it.
let shouldOpen = aOpenContainers &&
node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER;
PlacesUtils.asContainer(node);
if (!aOpenContainers && !node.containerOpen)
if (!node.containerOpen && !shouldOpen)
return foundOne;
nodesURIChecked.push(node.uri);

View File

@ -452,7 +452,7 @@ PlacesTreeView.prototype = {
for (let i = 0; i < aNodesInfo.length; i++) {
let nodeInfo = aNodesInfo[i];
let row = this._getNewRowForRemovedNode(aUpdatedContainer,
aNodesInfo[i].node);
nodeInfo.node);
// Select the found node, if any.
if (row != -1) {
selection.rangedSelect(row, row, true);
@ -465,9 +465,11 @@ PlacesTreeView.prototype = {
// select the node at its old row, if any.
if (aNodesInfo.length == 1 && selection.count == 0) {
let row = Math.min(aNodesInfo[0].oldRow, this._rows.length - 1);
selection.rangedSelect(row, row, true);
if (aNodesInfo[0].wasVisible && scrollToRow == -1)
scrollToRow = aNodesInfo[0].oldRow;
if (row != -1) {
selection.rangedSelect(row, row, true);
if (aNodesInfo[0].wasVisible && scrollToRow == -1)
scrollToRow = aNodesInfo[0].oldRow;
}
}
if (scrollToRow != -1)
@ -705,7 +707,8 @@ PlacesTreeView.prototype = {
// Restore selection.
let rowToSelect = Math.min(oldRow, this._rows.length - 1);
this.selection.rangedSelect(rowToSelect, rowToSelect, true);
if (rowToSelect != -1)
this.selection.rangedSelect(rowToSelect, rowToSelect, true);
},
nodeMoved:

View File

@ -45,4 +45,5 @@ skip-if = true
[browser_416459_cut.js]
[browser_library_downloads.js]
[browser_library_left_pane_select_hierarchy.js]
[browser_435851_copy_query.js]
[browser_toolbarbutton_menu_context.js]

View File

@ -0,0 +1,59 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* test that copying a non movable query or folder shortcut makes a new query with the same url, not a deep copy */
const SHORTCUT_URL = "place:folder=2";
const QUERY_URL = "place:sort=8&maxResults=10";
add_task(function copy_toolbar_shortcut() {
let library = yield promiseLibrary();
registerCleanupFunction(function () {
library.close();
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
});
library.PlacesOrganizer.selectLeftPaneQuery("BookmarksToolbar");
yield promiseClipboard(function () { library.PlacesOrganizer._places.controller.copy(); },
PlacesUtils.TYPE_X_MOZ_PLACE);
library.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
library.ContentTree.view.controller.paste();
let toolbarCopyNode = library.ContentTree.view.view.nodeForTreeIndex(0);
is(toolbarCopyNode.type,
Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT,
"copy is still a folder shortcut");
PlacesUtils.bookmarks.removeItem(toolbarCopyNode.itemId);
library.PlacesOrganizer.selectLeftPaneQuery("BookmarksToolbar");
is(library.PlacesOrganizer._places.selectedNode.type,
Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT,
"original is still a folder shortcut");
});
add_task(function copy_history_query() {
let library = yield promiseLibrary();
library.PlacesOrganizer.selectLeftPaneQuery("History");
yield promiseClipboard(function () { library.PlacesOrganizer._places.controller.copy(); },
PlacesUtils.TYPE_X_MOZ_PLACE);
library.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
library.ContentTree.view.controller.paste();
let historyCopyNode = library.ContentTree.view.view.nodeForTreeIndex(0);
is(historyCopyNode.type,
Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY,
"copy is still a query");
PlacesUtils.bookmarks.removeItem(historyCopyNode.itemId);
library.PlacesOrganizer.selectLeftPaneQuery("History");
is(library.PlacesOrganizer._places.selectedNode.type,
Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY,
"original is still a query");
});

View File

@ -173,6 +173,8 @@ gTests.push({
},
selectNode: function(tree) {
tree.selectItems([PlacesUtils.unfiledBookmarksFolderId]);
PlacesUtils.asContainer(tree.selectedNode).containerOpen = true;
tree.selectItems([this._itemId]);
is(tree.selectedNode.itemId, this._itemId, "Bookmark has been selected");
},
@ -329,6 +331,8 @@ gTests.push({
},
selectNode: function(tree) {
tree.selectItems([PlacesUtils.unfiledBookmarksFolderId]);
PlacesUtils.asContainer(tree.selectedNode).containerOpen = true;
tree.selectItems([this._itemId]);
is(tree.selectedNode.itemId, this._itemId, "Bookmark has been selected");
},

View File

@ -29,6 +29,46 @@ function openLibrary(callback, aLeftPaneRoot) {
return library;
}
/**
* Returns a handle to a Library window.
* If one is opens returns itm otherwise it opens a new one.
*
* @param aLeftPaneRoot
* Hierarchy to open and select in the left pane.
*/
function promiseLibrary(aLeftPaneRoot) {
let deferred = Promise.defer();
let library = Services.wm.getMostRecentWindow("Places:Organizer");
if (library) {
if (aLeftPaneRoot)
library.PlacesOrganizer.selectLeftPaneContainerByHierarchy(aLeftPaneRoot);
deferred.resolve(library);
}
else {
openLibrary(aLibrary => deferred.resolve(aLibrary), aLeftPaneRoot);
}
return deferred.promise;
}
/**
* Waits for a clipboard operation to complete, looking for the expected type.
*
* @see waitForClipboard
*
* @param aPopulateClipboardFn
* Function to populate the clipboard.
* @param aFlavor
* Data flavor to expect.
*/
function promiseClipboard(aPopulateClipboardFn, aFlavor) {
let deferred = Promise.defer();
waitForClipboard(function (aData) !!aData,
aPopulateClipboardFn,
function () { deferred.resolve(); },
aFlavor);
return deferred.promise;
}
/**
* Waits for completion of a clear history operation, before
* proceeding with aCallback.

View File

@ -9,4 +9,5 @@ support-files = head.js
[test_bug549491.xul]
[test_bug631374_tags_selector_scroll.xul]
[test_editBookmarkOverlay_tags_liveUpdate.xul]
[test_selectItems_on_nested_tree.xul]
[test_treeview_date.xul]

View File

@ -0,0 +1,66 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="549192: History view not updated after deleting entry"
onload="runTest();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript" src="head.js" />
<body xmlns="http://www.w3.org/1999/xhtml" />
<tree id="tree"
type="places"
flex="1">
<treecols>
<treecol label="Title" id="title" anonid="title" primary="true" ordinal="1" flex="1"/>
</treecols>
<treechildren flex="1"/>
</tree>
<script type="application/javascript"><![CDATA[
/**
* Ensure that selectItems doesn't recurse infinitely in nested trees.
*/
function runTest() {
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
NetUtil.newURI("place:folder=UNFILED_BOOKMARKS"),
PlacesUtils.bookmarks.DEFAULT_INDEX,
"shortcut");
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
NetUtil.newURI("place:folder=UNFILED_BOOKMARKS&maxResults=10"),
PlacesUtils.bookmarks.DEFAULT_INDEX,
"query");
let folderId = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
"folder",
PlacesUtils.bookmarks.DEFAULT_INDEX);
let itemId = PlacesUtils.bookmarks.insertBookmark(folderId,
NetUtil.newURI("http://www.mozilla.org/"),
PlacesUtils.bookmarks.DEFAULT_INDEX,
"bookmark");
// Setup the places tree contents.
var tree = document.getElementById("tree");
tree.place = "place:folder=UNFILED_BOOKMARKS";
// Select the last bookmark.
tree.selectItems([itemId]);
is (tree.selectedNode.itemId, itemId, "The right node was selected");
}
]]></script>
</window>

View File

@ -40,12 +40,15 @@ function test() {
is($("#empty-cache-chart").childNodes.length, 1,
"There should be a real empty cache chart created now.");
is($all(".pie-chart-container:not([placeholder=true])").length, 2,
"Two real pie chart appear to be rendered correctly.");
is($all(".table-chart-container:not([placeholder=true])").length, 2,
"Two real table chart appear to be rendered correctly.");
Task.spawn(function*() {
yield until(() => $all(".pie-chart-container:not([placeholder=true])").length == 2);
ok(true, "Two real pie charts appear to be rendered correctly.");
teardown(aMonitor).then(finish);
yield until(() => $all(".table-chart-container:not([placeholder=true])").length == 2);
ok(true, "Two real table charts appear to be rendered correctly.")
teardown(aMonitor).then(finish);
});
});
});
@ -55,3 +58,15 @@ function test() {
"The current frontend mode is correct.");
});
}
function waitForTick() {
let deferred = promise.defer();
executeSoon(deferred.resolve);
return deferred.promise;
}
function until(predicate) {
return Task.spawn(function*() {
while (!predicate()) yield waitForTick();
});
}

View File

@ -117,8 +117,9 @@ var ContentAreaObserver = {
let newWidth = width || this.width;
let newHeight = height || this.height;
if (newHeight == oldHeight && newWidth == oldWidth)
if (newHeight == oldHeight && newWidth == oldWidth) {
return;
}
this.styles["window-width"].width = newWidth + "px";
this.styles["window-width"].maxWidth = newWidth + "px";
@ -132,28 +133,15 @@ var ContentAreaObserver = {
},
updateContentArea: function cao_updateContentArea (width, height) {
if (Browser.selectedBrowser) {
let notificationBox = Browser.getNotificationBox();
// If a notification and navbar are visible together,
// make the notification appear above the navbar.
if (ContextUI.navbarVisible && !notificationBox.notificationsHidden &&
notificationBox.allNotifications.length != 0) {
let navbarHeight = Elements.navbar.getBoundingClientRect().height;
notificationBox.style.paddingBottom = navbarHeight + "px";
} else {
notificationBox.style.paddingBottom = "";
}
}
let oldHeight = parseInt(this.styles["content-height"].height);
let oldWidth = parseInt(this.styles["content-width"].width);
let newWidth = width || this.width;
let newHeight = height || this.contentHeight;
if (newHeight == oldHeight && newWidth == oldWidth)
if (newHeight == oldHeight && newWidth == oldWidth) {
return;
}
this.styles["content-height"].height = newHeight + "px";
this.styles["content-height"].maxHeight = newHeight + "px";
@ -171,8 +159,9 @@ var ContentAreaObserver = {
let newWidth = width || this.width;
let newHeight = height || this.viewableHeight;
if (newHeight == oldHeight && newWidth == oldWidth)
if (newHeight == oldHeight && newWidth == oldWidth) {
return;
}
this.styles["viewable-height"].height = newHeight + "px";
this.styles["viewable-height"].maxHeight = newHeight + "px";

View File

@ -22,6 +22,7 @@ var ContextUI = {
Elements.browsers.addEventListener('URLChanged', this, true);
Elements.browsers.addEventListener("AlertActive", this, true);
Elements.browsers.addEventListener("AlertClose", this, true);
Elements.tabList.addEventListener('TabSelect', this, true);
Elements.panelUI.addEventListener('ToolPanelShown', this, false);
Elements.panelUI.addEventListener('ToolPanelHidden', this, false);
@ -175,6 +176,7 @@ var ContextUI = {
// Display the nav bar
displayNavbar: function () {
Elements.navbar.show();
Elements.chromeState.setAttribute("navbar", "visible");
ContentAreaObserver.updateContentArea();
},
@ -189,6 +191,7 @@ var ContextUI = {
if (!BrowserUI.isStartTabVisible) {
Elements.autocomplete.closePopup();
Elements.navbar.dismiss();
Elements.chromeState.removeAttribute("navbar");
ContentAreaObserver.updateContentArea();
}
},
@ -365,6 +368,7 @@ var ContextUI = {
break;
case "AlertActive":
case "AlertClose":
case "TabSelect":
ContentAreaObserver.updateContentArea();
break;
case "MozFlyoutPanelShowing":

View File

@ -52,6 +52,9 @@ autoscroller {
notificationbox {
-moz-binding: url('chrome://browser/content/bindings/notification.xml#notificationbox');
}
notification {
-moz-binding: url('chrome://browser/content/bindings/notification.xml#notification');
}
circularprogressindicator {
-moz-binding: url('chrome://browser/content/bindings/circularprogress.xml#circular-progress-indicator');

View File

@ -41,6 +41,46 @@
this.removeEventListener("AlertClose", this.handleEvent, true);
]]>
</destructor>
<method name="adoptNotification">
<parameter name="aItem"/>
<body>
<![CDATA[
// insert an existing notification element
// XXX: borrows code from appendNotification in toolkit/content/widgets/notification.xml
// if this sticks around, we'll want to refactor both to eliminate duplication
let priority = aItem.priority;
// check for where the notification should be inserted according to
// priority. If two are equal, the existing one appears on top.
let notifications = this.allNotifications;
let insertPos = null;
for (let n = notifications.length - 1; n >= 0; n--) {
if (notifications[n].priority < priority)
break;
insertPos = notifications[n];
}
if (!insertPos) {
aItem.style.position = "fixed";
aItem.style.top = "100%";
aItem.style.marginTop = "-15px";
aItem.style.opacity = "0";
}
let label = aItem.label;
this.insertBefore(aItem, insertPos);
aItem.label = label;
if (!insertPos)
this._showNotification(aItem, true, true);
// Fire event for accessibility APIs
var event = document.createEvent("Events");
event.initEvent("AlertActive", true, true);
aItem.dispatchEvent(event);
return aItem;
]]>
</body>
</method>
<method name="removeNotification">
<parameter name="aItem"/>
<parameter name="aSkipAnimation"/>
@ -76,5 +116,43 @@
</implementation>
</binding>
<binding id="notification" role="xul:alert" extends="chrome://global/content/bindings/notification.xml#notification">
<implementation>
<property name="_messageContainer" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'messageText');"/>
<property name="label">
<getter><![CDATA[
if (this._messageContainer.childElementCount) {
// return a document fragment when our label is a complex value containing elements
// by cloning childNodes into a document fragment, the returned value
// is *not* live and will survive unbinding of this notification
let frag = this.ownerDocument.createDocumentFragment();
let containerNode = this._messageContainer;
for(let cnode of containerNode.childNodes) {
frag.appendChild(cnode.cloneNode(true));
}
return frag;
} else {
return String.trim(this._messageContainer.textContent) ||
this.getAttribute("label");
}
]]></getter>
<setter><![CDATA[
// accept a string or node (e.g. document fragment, element or text node) as label value
if (val && "object" == typeof val && ('nodeType' in val)) {
let containerNode = this._messageContainer;
let cnode;
while((cnode = containerNode.firstChild)) {
cnode.remove();
}
if (val.ownerDocument !== this.ownerDocument) {
val = this.ownerDocument.importNode(val, true);
}
return containerNode.appendChild(val);
} else {
return (this._messageContainer.textContent = val);
}
]]></setter>
</property>
</implementation>
</binding>
</bindings>

View File

@ -32,6 +32,7 @@ let Elements = {};
["urlbarState", "bcast_urlbarState"],
["loadingState", "bcast_loadingState"],
["windowState", "bcast_windowState"],
["chromeState", "bcast_chromeState"],
["mainKeyset", "mainKeyset"],
["stack", "stack"],
["tabList", "tabs"],

View File

@ -1356,6 +1356,12 @@ Tab.prototype = {
this.updateViewport();
this._delayUpdateThumbnail();
break;
case "AlertClose": {
if (this == Browser.selectedTab) {
this.updateViewport();
}
break;
}
}
},
@ -1462,6 +1468,7 @@ Tab.prototype = {
Elements.browsers.insertBefore(notification, aInsertBefore);
notification.dir = "reverse";
notification.addEventListener("AlertClose", this);
// let the content area manager know about this browser.
ContentAreaObserver.onBrowserCreated(browser);
@ -1483,6 +1490,7 @@ Tab.prototype = {
_destroyBrowser: function _destroyBrowser() {
if (this._browser) {
let notification = this._notification;
notification.removeEventListener("AlertClose", this);
let browser = this._browser;
browser.active = false;

View File

@ -69,6 +69,8 @@
viewstate=""/>
<broadcaster id="bcast_loadingState"
loading="false"/>
<broadcaster id="bcast_chromeState"
navbar="visible"/>
</broadcasterset>
<observerset id="observerset">
@ -399,6 +401,8 @@ Desktop browser's sync prefs.
<stack id="content-viewport">
<observes element="bcast_windowState"
attribute="startpage"/>
<observes element="bcast_chromeState"
attribute="navbar"/>
<deck id="browsers"
flex="1"
observes="bcast_preciseInput"/>

View File

@ -549,12 +549,27 @@ var MetroDownloadsView = {
switch (aEvent.type) {
case 'TabClose': {
let browser = aEvent.originalTarget.linkedBrowser;
dump("DownloadNotificationsView handleEvent, got TabClose event for browser: "+browser+"\n");
let notn = this._getNotificationWithValue("download-progress");
if (notn && notn.defaultView == browser.contentWindow) {
let nextTab = Browser.getNextTab(aEvent.originalTarget);
let box = Browser.getNotificationBox(nextTab.linkedBrowser);
box.insertBefore(notn, box.firstChild);
let tab = Browser.getTabForBrowser(browser);
let notificationBox = Browser.getNotificationBox(browser);
// move any download-related notification before the tab and its notificationBox goes away
// The 3 possible values should be mutually exclusive
for(let name of ["download-progress",
"save-download",
"download-complete"]) {
let notn = notificationBox.getNotificationWithValue(name);
if (!notn) {
continue;
}
let nextTab = Browser.getNextTab(tab);
let nextBox = nextTab && Browser.getNotificationBox(nextTab.browser);
if (nextBox) {
// move notification to the next tab
nextBox.adoptNotification(notn);
} else {
// Alas, no browser to move the notifications to.
}
}
break;
}

View File

@ -342,3 +342,48 @@ gTests.push({
and the existence of the downloaded file.");
}
});
/**
* Make sure download notifications are moved when we close tabs.
*/
gTests.push({
desc: "Download notifications in closed tabs",
setUp: function() {
// put up a couple notifications on the initial tab
let notificationBox = Browser.getNotificationBox();
notificationBox.appendNotification("not important", "low-priority-thing", "", notificationBox.PRIORITY_INFO_LOW, []);
notificationBox.appendNotification("so important", "high-priority-thing", "", notificationBox.PRIORITY_CRITICAL_HIGH, []);
// open a new tab where we'll conduct the test
yield addTab("about:mozilla");
},
run: function(){
let notificationBox = Browser.getNotificationBox();
let notn = MetroDownloadsView.showNotification("download-progress", "test message", [],
notificationBox.PRIORITY_WARNING_LOW);
Browser.closeTab(Browser.selectedTab);
yield waitForEvent(Elements.tabList, "TabRemove");
// expected behavior when a tab is closed while a download notification is showing:
// * the notification remains visible as long as a next tab/browser exists
// * normal rules about priority apply
// * notifications - including any pre-existing ones - display in expected order
let nextBox = Browser.getNotificationBox();
let currentNotification;
ok(nextBox.getNotificationWithValue("download-progress"), "notification was moved to next tab");
currentNotification = nextBox.currentNotification;
is(currentNotification.value, "high-priority-thing", "high priority notification is current");
currentNotification.close();
currentNotification = nextBox.currentNotification;
is(currentNotification.value, "download-progress", "download notification is next");
currentNotification.close();
currentNotification = nextBox.currentNotification;
is(currentNotification.value, "low-priority-thing", "low priority notification is next");
currentNotification.close();
}
});

View File

@ -0,0 +1,95 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
function test() {
runTests();
}
function cleanup() {
let notificationBox = Browser.getNotificationBox();
notificationBox && notificationBox.removeAllNotifications(true);
}
const XHTML_NS = "http://www.w3.org/1999/xhtml";
function createTestNotification(aLabel, aID) {
let notificationBox = Browser.getNotificationBox();
let notn = notificationBox.appendNotification(aLabel || "some label", aID || "test-notification",
"", notificationBox.PRIORITY_INFO_LOW, []);
return notn;
}
gTests.push({
desc: "Verify notification bindings are correct",
run: function () {
let notificationBox = Browser.getNotificationBox();
let notn = createTestNotification();
let binding = notn && getComputedStyle(notn).MozBinding;
is(binding,
"url(\"chrome://browser/content/bindings/notification.xml#notification\")",
"notification has expected binding");
is(getComputedStyle(notificationBox).MozBinding,
"url(\"chrome://browser/content/bindings/notification.xml#notificationbox\")",
"notificationbox has expected binding");
},
tearDown: cleanup
});
gTests.push({
desc: "Check label property handling",
run: function () {
let notn = createTestNotification("the label");
is(notn.label, "the label");
let doc = notn.ownerDocument;
let fragment = doc.createDocumentFragment();
try {
let boldLabel = doc.createElementNS(XHTML_NS, "b");
boldLabel.innerHTML = 'The <span class="foo">label</span>';
fragment.appendChild(boldLabel);
notn.label = fragment;
} catch (ex) {
ok(!ex, "Exception creating notification label with markup: "+ex.message);
}
// expect to get a documentFragment back when the label has markup
let labelNode = notn.label;
is(labelNode.nodeType,
Components.interfaces.nsIDOMNode.DOCUMENT_FRAGMENT_NODE,
"notification label getter returns documentFragment nodeType "+Components.interfaces.nsIDOMNode.DOCUMENT_FRAGMENT_NODE+", when value contains markup");
ok(labelNode !== fragment,
"label fragment is a newly created fragment, not the one we assigned in the setter");
ok(labelNode.querySelector("b > .foo"),
"label fragment has the expected elements in it");
},
tearDown: cleanup
});
gTests.push({
desc: "Check adoptNotification does what we expect",
setUp: function() {
yield addTab("about:start");
yield addTab("about:mozilla");
},
run: function () {
let browser = getBrowser();
let notn = createTestNotification("label", "adopt-notification");
let firstBox = Browser.getNotificationBox();
let nextTab = Browser.getNextTab(Browser.getTabForBrowser(browser));
let nextBox = Browser.getNotificationBox(nextTab.browser);
ok(firstBox.getNotificationWithValue("adopt-notification"), "notificationbox has our notification intially");
nextBox.adoptNotification(notn);
ok(!firstBox.getNotificationWithValue("adopt-notification"), "after adoptNotification, original notificationbox no longer has our notification");
ok(nextBox.getNotificationWithValue("adopt-notification"), "next notificationbox has our notification");
},
// leave browser in clean state for next tests
tearDown: cleanUpOpenedTabs
});

View File

@ -310,10 +310,16 @@ function addTab(aUrl) {
function cleanUpOpenedTabs() {
let tab;
while(tab = gOpenedTabs.shift()) {
cleanupNotificationsForBrowser(tab.browser);
Browser.closeTab(Browser.getTabFromChrome(tab.chromeTab), { forceClose: true })
}
}
function cleanupNotificationsForBrowser(aBrowser) {
let notificationBox = Browser.getNotificationBox(aBrowser);
notificationBox && notificationBox.removeAllNotifications(true);
}
/**
* Waits a specified number of miliseconds for a specified event to be
* fired on a specified element.

View File

@ -54,6 +54,7 @@ support-files =
[browser_link_click.js]
[browser_menu_hoverstate.js]
[browser_mouse_events.js]
[browser_notifications.js]
[browser_onscreen_keyboard.js]
[browser_prefs_ui.js]
[browser_private_browsing.js]

View File

@ -5,7 +5,6 @@
include $(topsrcdir)/config/config.mk
DIST_PROGRAM = CommandExecuteHandler$(BIN_SUFFIX)
RCINCLUDE = CommandExecuteHandler.rc
# Don't link against mozglue.dll
MOZ_GLUE_LDFLAGS =

View File

@ -18,3 +18,5 @@ for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM'):
DEFINES[var] = True
NO_PGO = True
RCINCLUDE = 'CommandExecuteHandler.rc'

View File

@ -251,10 +251,6 @@ documenttab[selected] .documenttab-selection:-moz-locale-dir(rtl) {
/* Start UI ----------------------------------------------------------------- */
#content-viewport[startpage] .active-tab-notificationbox {
padding-bottom: @toolbar_height@;
}
#urlbar-autocomplete[viewstate="snapped"],
#urlbar-autocomplete[viewstate="portrait"]{
-moz-box-orient: vertical;
@ -505,6 +501,11 @@ documenttab[selected] .documenttab-selection:-moz-locale-dir(rtl) {
visibility: hidden;
}
#content-viewport[navbar="visible"] .active-tab-notificationbox:not([count="0"]):not([notificationsVisible="false"]) {
padding-bottom: @toolbar_height@;
}
/* Progress meter ---------------------------------------------------------- */
#progress-container {

View File

@ -21,12 +21,6 @@ const CUSTOMIZATION_URL = "about:customizing";
// The interval between swapping in a preload docShell and kicking off the
// next preload in the background.
const PRELOADER_INTERVAL_MS = 600;
// The initial delay before we start preloading our first customization page. The
// timer is started after the first 'browser-delayed-startup' has been sent.
const PRELOADER_INIT_DELAY_MS = 7000;
const TOPIC_TIMER_CALLBACK = "timer-callback";
const TOPIC_DELAYED_STARTUP = "browser-delayed-startup-finished";
function createTimer(obj, delay) {
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@ -42,10 +36,6 @@ function clearTimer(timer) {
}
this.CustomizationTabPreloader = {
init: function() {
CustomizationTabPreloaderInternal.init();
},
uninit: function () {
CustomizationTabPreloaderInternal.uninit();
},
@ -53,28 +43,23 @@ this.CustomizationTabPreloader = {
newTab: function (aTab) {
return CustomizationTabPreloaderInternal.newTab(aTab);
},
/**
* ensurePreloading starts the preloading of the about:customizing
* content page. This function is idempotent (until a call to uninit),
* so multiple calls to it are fine.
*/
ensurePreloading: function() {
CustomizationTabPreloaderInternal.ensurePreloading();
},
};
Object.freeze(CustomizationTabPreloader);
this.CustomizationTabPreloaderInternal = {
_browser: null,
_timer: null,
_observing: false,
init: function () {
Services.obs.addObserver(this, TOPIC_DELAYED_STARTUP, false);
this._observing = true;
},
uninit: function () {
this._timer = clearTimer(this._timer);
if (this._observing) {
Services.obs.removeObserver(this, TOPIC_DELAYED_STARTUP);
this._observing = false;
}
HostFrame.destroy();
if (this._browser) {
@ -92,23 +77,10 @@ this.CustomizationTabPreloaderInternal = {
return false;
},
observe: function (aSubject, aTopic, aData) {
if (aTopic == TOPIC_DELAYED_STARTUP) {
Services.obs.removeObserver(this, TOPIC_DELAYED_STARTUP);
this._observing = false;
this._startTimer();
} else if (aTopic == TOPIC_TIMER_CALLBACK) {
this._timer = null;
this._startPreloader();
ensurePreloading: function () {
if (!this._browser) {
this._browser = new HiddenBrowser();
}
},
_startTimer: function () {
this._timer = createTimer(this, PRELOADER_INIT_DELAY_MS);
},
_startPreloader: function () {
this._browser = new HiddenBrowser();
}
};

View File

@ -160,7 +160,8 @@ browser.jar:
* skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
* skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
skin/classic/browser/devtools/filters.svg (../shared/devtools/filters.svg)
skin/classic/browser/devtools/controls.png (../shared/devtools/controls.png)
skin/classic/browser/devtools/controls.png (../shared/devtools/images/controls.png)
skin/classic/browser/devtools/controls@2x.png (../shared/devtools/images/controls@2x.png)
* skin/classic/browser/devtools/widgets.css (devtools/widgets.css)
skin/classic/browser/devtools/commandline-icon.png (devtools/commandline-icon.png)
skin/classic/browser/devtools/command-paintflashing.png (../shared/devtools/images/command-paintflashing.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -273,7 +273,8 @@ browser.jar:
* skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
* skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
skin/classic/browser/devtools/filters.svg (../shared/devtools/filters.svg)
skin/classic/browser/devtools/controls.png (../shared/devtools/controls.png)
skin/classic/browser/devtools/controls.png (../shared/devtools/images/controls.png)
skin/classic/browser/devtools/controls@2x.png (../shared/devtools/images/controls@2x.png)
* skin/classic/browser/devtools/widgets.css (devtools/widgets.css)
skin/classic/browser/devtools/commandline-icon.png (devtools/commandline-icon.png)
skin/classic/browser/devtools/command-paintflashing.png (../shared/devtools/images/command-paintflashing.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -2,3 +2,4 @@
%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-reset-button, #zoom-in-button, #sync-button, #feed-button, #tabview-button, #webrtc-status-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #switch-to-metro-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button, #characterencoding-button, #email-link-button
%define inAnyPanel :-moz-any(:not([cui-areatype="toolbar"]),.overflowedItem)
%define nestedButtons #zoom-out-button, #zoom-in-button, #cut-button, #copy-button, #paste-button

View File

@ -56,11 +56,16 @@
#main-window[customize-entered] .customization-target:not(#PanelUI-contents):hover::before,
#main-window[customize-entered] .customization-target[customizing-dragovertarget]:not(#PanelUI-contents)::before,
/* nav-bar and panel outlines are always shown */
#main-window[customize-entered] #nav-bar-customization-target.customization-target::before {
#nav-bar[showoutline=true] > #nav-bar-customization-target.customization-target::before {
outline-color: rgb(102,102,102);
}
#main-window[customize-entered] .panel-customization-placeholder {
#nav-bar[showoutline=true] > #nav-bar-customization-target.customization-target::before {
transition: outline-color 250ms linear;
}
#PanelUI-contents[showoutline=true] > .panel-customization-placeholder {
transition: outline-color 250ms linear;
outline-color: #bbb;
}
@ -171,6 +176,15 @@ toolbarpaletteitem[place="panel"] {
transition: transform .3s ease-in-out;
}
#customization-palette {
transition: opacity .3s ease-in-out;
opacity: 0;
}
#customization-palette[showing="true"] {
opacity: 1;
}
toolbarpaletteitem[notransition].panel-customization-placeholder,
toolbarpaletteitem[notransition][place="toolbar"],
toolbarpaletteitem[notransition][place="palette"],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 668 B

View File

@ -21,40 +21,9 @@
color: #f5f7fa;
}
.theme-twisty {
cursor: pointer;
width: 14px;
height: 14px;
background-repeat: no-repeat;
background-image: url("chrome://browser/skin/devtools/controls.png");
background-position: -28px -14px;
}
.theme-twisty:-moz-focusring {
outline-style: none;
}
.theme-twisty[open] {
background-position: -42px -14px;
}
.theme-checkbox {
display: inline-block;
border: 0;
width: 14px;
height: 14px;
padding: 0;
outline: none;
background-image: url("chrome://browser/skin/devtools/controls.png");
background-position: -28px 0;
}
.theme-checkbox[checked] {
background-position: -42px 0;
}
.theme-selected {
background: #26394D;
background-color: #1d4f73;
color: #f5f7fa; /* Light foreground text */
}
.theme-bg-darker {
@ -254,6 +223,46 @@ div.CodeMirror span.eval-text {
min-height: 1.4em;
}
/* Twisty and checkbox controls */
.theme-twisty, .theme-checkbox {
width: 14px;
height: 14px;
background-repeat: no-repeat;
background-image: url("chrome://browser/skin/devtools/controls.png");
background-size: 56px 28px;
}
.theme-twisty {
cursor: pointer;
background-position: -28px -14px;
}
.theme-twisty:-moz-focusring {
outline-style: none;
}
.theme-twisty[open] {
background-position: -42px -14px;
}
.theme-checkbox {
display: inline-block;
border: 0;
padding: 0;
outline: none;
background-position: -28px 0;
}
.theme-checkbox[checked] {
background-position: -42px 0;
}
@media (min-resolution: 2dppx) {
.theme-twisty, .theme-checkbox {
background-image: url("chrome://browser/skin/devtools/controls@2x.png");
}
}
/* XUL panel styling (see browser/devtools/shared/widgets/Tooltip.js) */
.theme-tooltip-panel .panel-arrowcontent {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -21,40 +21,9 @@
color: #f5f7fa;
}
.theme-twisty {
cursor: pointer;
width: 14px;
height: 14px;
background-repeat: no-repeat;
background-image: url("chrome://browser/skin/devtools/controls.png");
background-position: 0 -14px;
}
.theme-twisty:-moz-focusring {
outline-style: none;
}
.theme-twisty[open] {
background-position: -14px -14px;
}
.theme-checkbox {
display: inline-block;
border: 0;
width: 14px;
height: 14px;
padding: 0;
outline: none;
background-image: url("chrome://browser/skin/devtools/controls.png");
background-position: 0 0;
}
.theme-checkbox[checked] {
background-position: -14px 0;
}
.theme-selected {
background-color: #CCC;
background-color: #4c9ed9;
color: #f5f7fa; /* Light foreground text */
}
.theme-bg-darker {
@ -253,6 +222,56 @@ div.CodeMirror span.eval-text {
min-height: 1.4em;
}
/* Twisty and checkbox controls */
.theme-twisty, .theme-checkbox {
width: 14px;
height: 14px;
background-repeat: no-repeat;
background-image: url("chrome://browser/skin/devtools/controls.png");
background-size: 56px 28px;
}
.theme-twisty {
cursor: pointer;
background-position: 0 -14px;
}
.theme-twisty:-moz-focusring {
outline-style: none;
}
.theme-twisty[open] {
background-position: -14px -14px;
}
/* Use white twisty when next to a selected item in markup view */
.theme-selected ~ .theme-twisty {
background-position: -28px -14px;
}
.theme-selected ~ .theme-twisty[open] {
background-position: -42px -14px;
}
.theme-checkbox {
display: inline-block;
border: 0;
padding: 0;
outline: none;
background-position: 0 0;
}
.theme-checkbox[checked] {
background-position: -14px 0;
}
@media (min-resolution: 2dppx) {
.theme-twisty, .theme-checkbox {
background-image: url("chrome://browser/skin/devtools/controls@2x.png");
}
}
/* XUL panel styling (see browser/devtools/shared/widgets/Tooltip.js) */
.theme-tooltip-panel .panel-arrowcontent {

View File

@ -14,3 +14,22 @@
.styleinspector-propertyeditor {
border: 1px solid #CCC;
}
/* Selected nodes in the tree should have light selected text.
theme-selected doesn't work in this case since the text is a
sibling of the class, not a child. */
.theme-selected ~ .editor,
.theme-selected ~ .editor .theme-fg-color1,
.theme-selected ~ .editor .theme-fg-color2,
.theme-selected ~ .editor .theme-fg-color3,
.theme-selected ~ .editor .theme-fg-color4,
.theme-selected ~ .editor .theme-fg-color5,
.theme-selected ~ .editor .theme-fg-color6,
.theme-selected ~ .editor .theme-fg-color7 {
color: #f5f7fa; /* Light foreground text */
}
.tag-line {
padding-left: 2px;
}

View File

@ -8,7 +8,6 @@
%define glassActiveBorderColor rgb(37, 44, 51)
%define glassInactiveBorderColor rgb(102, 102, 102)
%define nestedButtons #zoom-out-button, #zoom-in-button, #cut-button, #copy-button, #paste-button
%include downloads/indicator-aero.css

View File

@ -801,6 +801,12 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
}
%endif
#main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > #bookmarks-menu-button > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon:-moz-system-metric(windows-classic):not(:-moz-lwtheme),
#main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > toolbarpaletteitem > #bookmarks-menu-button > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon:-moz-system-metric(windows-classic):not(:-moz-lwtheme),
#main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > toolbarpaletteitem > toolbaritem > :-moz-any(@nestedButtons@):-moz-system-metric(windows-classic):not(:-moz-lwtheme),
#main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > toolbaritem > :-moz-any(@nestedButtons@):-moz-system-metric(windows-classic):not(:-moz-lwtheme),
#main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > toolbarpaletteitem > :-moz-any(@primaryToolbarButtons@):-moz-system-metric(windows-classic):not(:-moz-lwtheme),
#main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > :-moz-any(@primaryToolbarButtons@):-moz-system-metric(windows-classic):not(:-moz-lwtheme),
#home-button.bookmark-item:-moz-lwtheme-brighttext {
list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
}
@ -1640,6 +1646,8 @@ toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
background-origin: border-box;
}
#main-window[tabsintitlebar]:not([inFullscreen]) .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-system-metric(windows-classic):not(:-moz-lwtheme),
#main-window[tabsintitlebar]:not([inFullscreen]) .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-system-metric(windows-classic):not(:-moz-lwtheme),
.tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-lwtheme-brighttext,
.tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-lwtheme-brighttext {
list-style-image: url(chrome://browser/skin/tabbrowser/tab-arrow-left-inverted.png);
@ -1683,6 +1691,9 @@ toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
-moz-image-region: auto;
}
#main-window[tabsintitlebar]:not([inFullscreen]) .tabs-newtab-button:-moz-system-metric(windows-classic):not(:-moz-lwtheme),
#main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar > #new-tab-button:-moz-system-metric(windows-classic):not(:-moz-lwtheme),
#main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar > toolbarpaletteitem > #new-tab-button:-moz-system-metric(windows-classic):not(:-moz-lwtheme),
.tabs-newtab-button:-moz-lwtheme-brighttext,
#TabsToolbar > #new-tab-button:-moz-lwtheme-brighttext,
#TabsToolbar > toolbarpaletteitem > #new-tab-button:-moz-lwtheme-brighttext {
@ -1697,6 +1708,7 @@ toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow.png");
}
#main-window[tabsintitlebar]:not([inFullscreen]) #alltabs-button:-moz-system-metric(windows-classic):not(:-moz-lwtheme),
#alltabs-button:-moz-lwtheme-brighttext {
list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow-inverted.png");
}

View File

@ -189,7 +189,8 @@ browser.jar:
* skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
* skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
skin/classic/browser/devtools/filters.svg (../shared/devtools/filters.svg)
skin/classic/browser/devtools/controls.png (../shared/devtools/controls.png)
skin/classic/browser/devtools/controls.png (../shared/devtools/images/controls.png)
skin/classic/browser/devtools/controls@2x.png (../shared/devtools/images/controls@2x.png)
* skin/classic/browser/devtools/widgets.css (devtools/widgets.css)
skin/classic/browser/devtools/commandline-icon.png (devtools/commandline-icon.png)
skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
@ -510,7 +511,8 @@ browser.jar:
* skin/classic/aero/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
* skin/classic/aero/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
skin/classic/aero/browser/devtools/filters.svg (../shared/devtools/filters.svg)
skin/classic/aero/browser/devtools/controls.png (../shared/devtools/controls.png)
skin/classic/aero/browser/devtools/controls.png (../shared/devtools/images/controls.png)
skin/classic/aero/browser/devtools/controls@2x.png (../shared/devtools/images/controls@2x.png)
* skin/classic/aero/browser/devtools/widgets.css (devtools/widgets.css)
skin/classic/aero/browser/devtools/commandline-icon.png (devtools/commandline-icon.png)
skin/classic/aero/browser/devtools/command-paintflashing.png (../shared/devtools/images/command-paintflashing.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -3,7 +3,6 @@
# You can obtain one at http://mozilla.org/MPL/2.0/.
MODULES = stlport
STL_FLAGS =
# Force to build a static library, instead of a fake library, without
# installing it in dist/lib.

View File

@ -52,3 +52,4 @@ LOCAL_INCLUDES += [
'stlport',
]
DISABLE_STL_WRAPPING = True

View File

@ -2,7 +2,6 @@
# 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/.
STL_FLAGS =
NO_EXPAND_LIBS = 1
include $(topsrcdir)/config/rules.mk

View File

@ -17,3 +17,5 @@ if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
FORCE_STATIC_LIB = True
NO_PGO = True
DISABLE_STL_WRAPPING = True

View File

@ -590,7 +590,7 @@ endif
endif
COMPILE_CFLAGS = $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS) $(MOZBUILD_CFLAGS) $(EXTRA_COMPILE_FLAGS)
COMPILE_CXXFLAGS = $(STL_FLAGS) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS) $(MOZBUILD_CXXFLAGS) $(EXTRA_COMPILE_FLAGS)
COMPILE_CXXFLAGS = $(if $(DISABLE_STL_WRAPPING),,$(STL_FLAGS)) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS) $(MOZBUILD_CXXFLAGS) $(EXTRA_COMPILE_FLAGS)
COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS) $(EXTRA_COMPILE_FLAGS)
COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS) $(EXTRA_COMPILE_FLAGS)
ASFLAGS += $(EXTRA_ASSEMBLER_FLAGS)
@ -900,7 +900,7 @@ DEFINES += -DUNICODE -D_UNICODE
LOCAL_INCLUDES += -I'$(MOZ_DIRECTX_SDK_PATH)/include'
endif
STL_FLAGS=
DISABLE_STL_WRAPPING := 1
# Skip most Mozilla-specific include locations.
INCLUDES = -I. $(LOCAL_INCLUDES) -I$(DEPTH)/dist/include
endif

View File

@ -1088,7 +1088,8 @@ protected:
* Add/remove this element to the documents id cache
*/
void AddToIdTable(nsIAtom* aId);
void RemoveFromIdTable();
void RemoveFromIdTable(); // checks HasID() and uses DoGetID()
void RemoveFromIdTable(nsIAtom* aId);
/**
* Functions to carry out event default actions for links of all types

View File

@ -19,6 +19,7 @@
#include "nsPIDOMWindow.h" // for use in inline functions
#include "nsPropertyTable.h" // for member
#include "nsTHashtable.h" // for member
#include "nsWeakReference.h"
#include "mozilla/dom/DocumentBinding.h"
#include "mozilla/WeakPtr.h"
#include "Units.h"
@ -79,7 +80,6 @@ class nsWindowSizes;
class nsSmallVoidArray;
class nsDOMCaretPosition;
class nsViewportInfo;
class nsDOMEvent;
class nsIGlobalObject;
class nsCSSSelectorList;
@ -102,6 +102,7 @@ class DOMImplementation;
class DOMStringList;
class Element;
struct ElementRegistrationOptions;
class Event;
class EventTarget;
class FrameRequestCallback;
class HTMLBodyElement;
@ -2003,8 +2004,8 @@ public:
enum ElementCallbackType {
eCreated,
eEnteredView,
eLeftView,
eAttached,
eDetached,
eAttributeChanged
};
@ -2065,8 +2066,8 @@ public:
already_AddRefed<nsINode>
ImportNode(nsINode& aNode, bool aDeep, mozilla::ErrorResult& rv) const;
nsINode* AdoptNode(nsINode& aNode, mozilla::ErrorResult& rv);
already_AddRefed<nsDOMEvent> CreateEvent(const nsAString& aEventType,
mozilla::ErrorResult& rv) const;
already_AddRefed<mozilla::dom::Event>
CreateEvent(const nsAString& aEventType, mozilla::ErrorResult& rv) const;
already_AddRefed<nsRange> CreateRange(mozilla::ErrorResult& rv);
already_AddRefed<mozilla::dom::NodeIterator>
CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,

View File

@ -15,7 +15,6 @@
#include "nsNodeInfoManager.h" // for use in NodePrincipal()
#include "nsPropertyTable.h" // for typedefs
#include "nsTObserverArray.h" // for member
#include "nsWindowMemoryReporter.h" // for NS_DECL_SIZEOF_EXCLUDING_THIS
#include "mozilla/ErrorResult.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/EventTarget.h" // for base class
@ -251,6 +250,14 @@ private:
static uint32_t sMutationCount;
};
// This should be used for any nsINode sub-class that has fields of its own
// that it needs to measure; any sub-class that doesn't use it will inherit
// SizeOfExcludingThis from its super-class. SizeOfIncludingThis() need not be
// defined, it is inherited from nsINode.
// This macro isn't actually specific to nodes, and bug 956400 will move it into MFBT.
#define NS_DECL_SIZEOF_EXCLUDING_THIS \
virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// Categories of node properties
// 0 is global.
#define DOM_USER_DATA 1

View File

@ -719,23 +719,29 @@ void
Element::RemoveFromIdTable()
{
if (HasID()) {
if (HasFlag(NODE_IS_IN_SHADOW_TREE)) {
ShadowRoot* containingShadow = GetContainingShadow();
// Check for containingShadow because it may have
// been deleted during unlinking.
if (containingShadow) {
containingShadow->RemoveFromIdTable(this, DoGetID());
}
} else {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
nsIAtom* id = DoGetID();
// id can be null during mutation events evilness. Also, XUL elements
// loose their proto attributes during cc-unlink, so this can happen
// during cc-unlink too.
if (id) {
doc->RemoveFromIdTable(this, DoGetID());
}
RemoveFromIdTable(DoGetID());
}
}
void
Element::RemoveFromIdTable(nsIAtom* aId)
{
NS_ASSERTION(HasID(), "Node doesn't have an ID?");
if (HasFlag(NODE_IS_IN_SHADOW_TREE)) {
ShadowRoot* containingShadow = GetContainingShadow();
// Check for containingShadow because it may have
// been deleted during unlinking.
if (containingShadow) {
containingShadow->RemoveFromIdTable(this, aId);
}
} else {
nsIDocument* doc = GetCurrentDoc();
if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
// id can be null during mutation events evilness. Also, XUL elements
// loose their proto attributes during cc-unlink, so this can happen
// during cc-unlink too.
if (aId) {
doc->RemoveFromIdTable(this, aId);
}
}
}
@ -1158,8 +1164,8 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
SetInDocument();
if (GetCustomElementData()) {
// Enqueue an enteredView callback for the custom element.
aDocument->EnqueueLifecycleCallback(nsIDocument::eEnteredView, this);
// Enqueue an attached callback for the custom element.
aDocument->EnqueueLifecycleCallback(nsIDocument::eAttached, this);
}
// Unset this flag since we now really are in a document.
@ -1321,8 +1327,8 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
document->ClearBoxObjectFor(this);
if (GetCustomElementData()) {
// Enqueue a leftView callback for the custom element.
document->EnqueueLifecycleCallback(nsIDocument::eLeftView, this);
// Enqueue a detached callback for the custom element.
document->EnqueueLifecycleCallback(nsIDocument::eDetached, this);
}
}

View File

@ -193,6 +193,7 @@
#include "nsIAppsService.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/NodeFilterBinding.h"
@ -203,7 +204,6 @@
#include "nsDOMCaretPosition.h"
#include "nsIDOMHTMLTextAreaElement.h"
#include "nsViewportInfo.h"
#include "nsDOMEvent.h"
#include "nsIContentPermissionPrompt.h"
#include "mozilla/StaticPtr.h"
#include "nsITextControlElement.h"
@ -221,6 +221,7 @@
#include "nsIMutableArray.h"
#include "nsContentPermissionHelper.h"
#include "mozilla/dom/DOMStringList.h"
#include "nsWindowMemoryReporter.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -330,11 +331,11 @@ CustomElementCallback::Call()
static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
mOwnerData->mElementIsBeingCreated = false;
break;
case nsIDocument::eEnteredView:
static_cast<LifecycleEnteredViewCallback *>(mCallback.get())->Call(mThisObject, rv);
case nsIDocument::eAttached:
static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
break;
case nsIDocument::eLeftView:
static_cast<LifecycleLeftViewCallback *>(mCallback.get())->Call(mThisObject, rv);
case nsIDocument::eDetached:
static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv);
break;
case nsIDocument::eAttributeChanged:
static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
@ -1749,16 +1750,16 @@ CustomDefinitionsTraverse(CustomElementHashKey* aKey,
cb->NoteXPCOMChild(aDefinition->mCallbacks->mCreatedCallback.Value());
}
if (callbacks->mEnteredViewCallback.WasPassed()) {
if (callbacks->mAttachedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
"mCustomDefinitions->mCallbacks->mEnteredViewCallback");
cb->NoteXPCOMChild(aDefinition->mCallbacks->mEnteredViewCallback.Value());
"mCustomDefinitions->mCallbacks->mAttachedCallback");
cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttachedCallback.Value());
}
if (callbacks->mLeftViewCallback.WasPassed()) {
if (callbacks->mDetachedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
"mCustomDefinitions->mCallbacks->mLeftViewCallback");
cb->NoteXPCOMChild(aDefinition->mCallbacks->mLeftViewCallback.Value());
"mCustomDefinitions->mCallbacks->mDetachedCallback");
cb->NoteXPCOMChild(aDefinition->mCallbacks->mDetachedCallback.Value());
}
return PL_DHASH_NEXT;
@ -5570,15 +5571,15 @@ nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
}
break;
case nsIDocument::eEnteredView:
if (definition->mCallbacks->mEnteredViewCallback.WasPassed()) {
func = definition->mCallbacks->mEnteredViewCallback.Value();
case nsIDocument::eAttached:
if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
func = definition->mCallbacks->mAttachedCallback.Value();
}
break;
case nsIDocument::eLeftView:
if (definition->mCallbacks->mLeftViewCallback.WasPassed()) {
func = definition->mCallbacks->mLeftViewCallback.Value();
case nsIDocument::eDetached:
if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
func = definition->mCallbacks->mDetachedCallback.Value();
}
break;
@ -5921,12 +5922,12 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition);
if (elem->GetCurrentDoc()) {
// Normally callbacks can not be enqueued until the created
// callback has been invoked, however, the entered view callback
// callback has been invoked, however, the attached callback
// in element upgrade is an exception so pretend the created
// callback has been invoked.
elem->GetCustomElementData()->mCreatedCallbackInvoked = true;
EnqueueLifecycleCallback(nsIDocument::eEnteredView, elem, nullptr, definition);
EnqueueLifecycleCallback(nsIDocument::eAttached, elem, nullptr, definition);
}
}
}
@ -7648,7 +7649,7 @@ nsDocument::CreateEvent(const nsAString& aEventType, nsIDOMEvent** aReturn)
return rv.ErrorCode();
}
already_AddRefed<nsDOMEvent>
already_AddRefed<Event>
nsIDocument::CreateEvent(const nsAString& aEventType, ErrorResult& rv) const
{
nsIPresShell *shell = GetShell();

View File

@ -313,7 +313,7 @@ private:
// The this value to use for invocation of the callback.
nsRefPtr<mozilla::dom::Element> mThisObject;
nsRefPtr<mozilla::dom::CallbackFunction> mCallback;
// The type of callback (eCreated, eEnteredView, etc.)
// The type of callback (eCreated, eAttached, etc.)
nsIDocument::ElementCallbackType mType;
// Arguments to be passed to the callback,
// used by the attribute changed callback.

View File

@ -23,6 +23,7 @@
#include "nsIXULRuntime.h"
#include "nsIScriptError.h"
#include "nsIConsoleService.h"
#include "nsIMemoryReporter.h"
#include "nsIProtocolHandler.h"
#include "nsIScriptSecurityManager.h"
#include "nsIJSRuntimeService.h"

View File

@ -18,6 +18,9 @@
#include "mozilla/Likely.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Telemetry.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/ShadowRoot.h"
#include "nsAsyncDOMEvent.h"
#include "nsAttrValueOrString.h"
#include "nsBindingManager.h"
@ -41,7 +44,6 @@
#include "nsFocusManager.h"
#include "nsFrameManager.h"
#include "nsFrameSelection.h"
#include "mozilla/dom/Element.h"
#include "nsGenericHTMLElement.h"
#include "nsGkAtoms.h"
#include "nsIAnonymousContentCreator.h"
@ -96,11 +98,9 @@
#include "nsCSSParser.h"
#include "HTMLLegendElement.h"
#include "nsWrapperCacheInlines.h"
#include "mozilla/dom/ShadowRoot.h"
#include "WrapperFactory.h"
#include "DocumentType.h"
#include <algorithm>
#include "nsDOMEvent.h"
#include "nsGlobalWindow.h"
#include "nsDOMMutationObserver.h"
@ -2633,7 +2633,7 @@ nsINode::GetAttributes()
}
bool
EventTarget::DispatchEvent(nsDOMEvent& aEvent,
EventTarget::DispatchEvent(Event& aEvent,
ErrorResult& aRv)
{
bool result = false;

View File

@ -69,18 +69,18 @@
#include "nsIContentSecurityPolicy.h"
#include "nsIChannelPolicy.h"
#include "nsChannelPolicy.h"
#include "mozilla/dom/Element.h"
#include "GeckoProfiler.h"
#include "nsObjectFrame.h"
#include "nsDOMClassInfo.h"
#include "nsWrapperCacheInlines.h"
#include "nsDOMJSUtils.h"
#include "nsDOMEvent.h"
#include "nsWidgetsCID.h"
#include "nsContentCID.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/Telemetry.h"
#ifdef XP_WIN
@ -307,7 +307,7 @@ nsPluginCrashedEvent::Run()
}
ErrorResult rv;
nsRefPtr<nsDOMEvent> event =
nsRefPtr<Event> event =
doc->CreateEvent(NS_LITERAL_STRING("datacontainerevents"), rv);
nsCOMPtr<nsIDOMDataContainerEvent> containerEvent(do_QueryObject(event));
if (!containerEvent) {

View File

@ -2140,7 +2140,15 @@ HTMLInputElement::GetValueIfStepped(int32_t aStep,
return NS_OK;
}
if (GetValidityState(VALIDITY_STATE_STEP_MISMATCH) &&
// If the current value isn't aligned on a step, then shift the value to the
// nearest step that will cause the addition of aStep steps (further below)
// to |value| to hit the required value.
// (Instead of using GetValidityState(VALIDITY_STATE_STEP_MISMATCH) we have
// to check HasStepMismatch and pass true as its aUseZeroIfValueNaN argument
// since we need to treat the value "" as zero for stepping purposes even
// though we don't suffer from a step mismatch when our value is the empty
// string.)
if (HasStepMismatch(true) &&
value != minimum && value != maximum) {
if (aStep > 0) {
value -= NS_floorModulo(value - GetStepBase(), step);
@ -2778,7 +2786,7 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue,
if (!mParserCreating) {
SanitizeValue(value);
}
// else SanitizeValue will be called by DoneCreatingElement
// else DoneCreatingElement calls us again once mParserCreating is false
if (aSetValueChanged) {
SetValueChanged(true);
@ -2803,7 +2811,10 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue,
numberControlFrame->SetValueOfAnonTextControl(value);
}
}
OnValueChanged(!mParserCreating);
if (!mParserCreating) {
OnValueChanged(true);
}
// else DoneCreatingElement calls us again once mParserCreating is false
}
if (mType == NS_FORM_INPUT_COLOR) {
@ -3641,12 +3652,20 @@ HTMLInputElement::StepNumberControlForUserEvent(int32_t aDirection)
// want to wipe out what they typed if they try to increment/decrement the
// value. Better is to highlight the value as being invalid so that they
// can correct what they typed.
// We pass 'true' for UpdateValidityUIBits' aIsFocused argument regardless
// because we need the UI to update _now_ or the user will wonder why the
// step behavior isn't functioning.
UpdateValidityUIBits(true);
UpdateState(true);
return;
// We only do this if there actually is a value typed in by/displayed to
// the user. (IsValid() can return false if the 'required' attribute is
// set and the value is the empty string.)
nsNumberControlFrame* numberControlFrame =
do_QueryFrame(GetPrimaryFrame());
if (numberControlFrame &&
!numberControlFrame->AnonTextControlIsEmpty()) {
// We pass 'true' for UpdateValidityUIBits' aIsFocused argument
// regardless because we need the UI to update _now_ or the user will
// wonder why the step behavior isn't functioning.
UpdateValidityUIBits(true);
UpdateState(true);
return;
}
}
Decimal newValue = Decimal::nan(); // unchanged if value will not change
@ -6433,7 +6452,7 @@ HTMLInputElement::IsRangeUnderflow() const
}
bool
HTMLInputElement::HasStepMismatch() const
HTMLInputElement::HasStepMismatch(bool aUseZeroIfValueNaN) const
{
if (!DoesStepApply()) {
return false;
@ -6441,8 +6460,12 @@ HTMLInputElement::HasStepMismatch() const
Decimal value = GetValueAsDecimal();
if (value.isNaN()) {
// The element can't suffer from step mismatch if it's value isn't a number.
return false;
if (aUseZeroIfValueNaN) {
value = 0;
} else {
// The element can't suffer from step mismatch if it's value isn't a number.
return false;
}
}
Decimal step = GetStep();

View File

@ -256,7 +256,7 @@ public:
bool HasPatternMismatch() const;
bool IsRangeOverflow() const;
bool IsRangeUnderflow() const;
bool HasStepMismatch() const;
bool HasStepMismatch(bool aUseZeroIfValueNaN = false) const;
bool HasBadInput() const;
void UpdateTooLongValidityState();
void UpdateValueMissingValidityState();

View File

@ -6,8 +6,8 @@
#include "mozilla/dom/UndoManager.h"
#include "mozilla/dom/DOMTransactionBinding.h"
#include "mozilla/dom/Event.h"
#include "nsDOMClassInfoID.h"
#include "nsDOMEvent.h"
#include "nsIClassInfo.h"
#include "nsIDOMDocument.h"
#include "nsIXPCScriptable.h"
@ -1142,7 +1142,7 @@ UndoManager::DispatchTransactionEvent(JSContext* aCx, const nsAString& aType,
return;
}
nsRefPtr<nsDOMEvent> event = mHostNode->OwnerDoc()->CreateEvent(
nsRefPtr<Event> event = mHostNode->OwnerDoc()->CreateEvent(
NS_LITERAL_STRING("domtransaction"), aRv);
if (aRv.Failed()) {
return;

View File

@ -71,6 +71,18 @@ function getStepBase(element) {
Number(element.getAttribute("value") || "NaN") || 0;
}
function hasStepMismatch(element) {
var value = element.value;
if (value == "") {
value = 0;
}
var step = getStep(element);
if (step == "any") {
return false;
}
return ((value - getStepBase(element)) % step) != 0;
}
function floorModulo(x, y) {
return (x - y * Math.floor(x / y));
}
@ -101,7 +113,7 @@ function expectedValueAfterStepUpOrDown(stepFactor, element) {
return value;
}
if (element.validity.stepMismatch &&
if (hasStepMismatch(element) &&
value != minimum && value != maximum) {
if (stepFactor > 0) {
value -= floorModulo(value - getStepBase(element), step);
@ -138,8 +150,8 @@ function test() {
var elem = document.getElementById("input");
elem.focus();
elem.min = -3;
elem.max = 3;
elem.min = -5;
elem.max = 5;
elem.step = 2;
var defaultValue = 0;
var oldVal, expectedVal;
@ -203,6 +215,22 @@ function test() {
sendString("abc");
synthesizeKey(key, {});
is(elem.value, "", "Test " + key + " does nothing when the input is invalid");
// Test that no value does not block UI initiated stepping:
oldVal = elem.value = "";
elem.setAttribute("required", "required");
elem.select();
expectedVal = expectedValAfterKeyEvent(key, elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test " + key + " for number control with value set to the empty string and with the 'required' attribute set");
// Same again:
expectedVal = expectedValAfterKeyEvent(key, elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test repeat of " + key + " for number control");
// Reset 'required' attribute:
elem.removeAttribute("required");
}
}

View File

@ -20,7 +20,6 @@
#include "nsICommandManager.h"
#include "mozilla/dom/HTMLSharedElement.h"
#include "nsDOMEvent.h"
class nsIEditor;
class nsIParser;

View File

@ -255,20 +255,29 @@ public:
* *aFinished is set to false by the caller. If the callee sets it to true,
* we'll finish the stream and not call this again.
*/
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
{
MOZ_ASSERT(mInputCount <= 1 && mOutputCount <= 1);
*aOutput = aInput;
}
/**
* Produce the next block of audio samples, before input is provided.
* ProcessBlock() will be called later, and it then should not change
* aOutput. This is used only for DelayNodeEngine in a feedback loop.
*/
virtual void ProduceBlockBeforeInput(AudioChunk* aOutput)
{
NS_NOTREACHED("ProduceBlockBeforeInput called on wrong engine\n");
}
/**
* Produce the next block of audio samples, given input samples in the aInput
* array. There is one input sample per active port in aInput, in order.
* This is the multi-input/output version of ProduceAudioBlock. Only one kind
* of ProduceAudioBlock is called on each node, depending on whether the
* This is the multi-input/output version of ProcessBlock. Only one kind
* of ProcessBlock is called on each node, depending on whether the
* number of inputs and outputs are both 1 or not.
*
* aInput is always guaranteed to not contain more input AudioChunks than the
@ -279,10 +288,10 @@ public:
* corresponding AudioNode, in which case it will be interpreted as a channel
* of silence.
*/
virtual void ProduceAudioBlocksOnPorts(AudioNodeStream* aStream,
const OutputChunks& aInput,
OutputChunks& aOutput,
bool* aFinished)
virtual void ProcessBlocksOnPorts(AudioNodeStream* aStream,
const OutputChunks& aInput,
OutputChunks& aOutput,
bool* aFinished)
{
MOZ_ASSERT(mInputCount > 1 || mOutputCount > 1);
// Only produce one output port, and drop all other input ports.

View File

@ -324,8 +324,8 @@ ConvertSegmentToAudioBlock(AudioSegment* aSegment, AudioChunk* aBlock)
}
void
AudioNodeExternalInputStream::ProduceOutput(GraphTime aFrom, GraphTime aTo,
uint32_t aFlags)
AudioNodeExternalInputStream::ProcessInput(GraphTime aFrom, GraphTime aTo,
uint32_t aFlags)
{
// According to spec, number of outputs is always 1.
mLastChunks.SetLength(1);

View File

@ -25,7 +25,7 @@ public:
AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate);
~AudioNodeExternalInputStream();
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
private:
// For storing pointers and data about input tracks, like the last TrackTick which

View File

@ -399,7 +399,7 @@ AudioNodeStream::UpMixDownMixChunk(const AudioChunk* aChunk,
// The MediaStreamGraph guarantees that this is actually one block, for
// AudioNodeStreams.
void
AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
AudioNodeStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
{
EnsureTrack(AUDIO_TRACK, mSampleRate);
// No more tracks will be coming
@ -426,17 +426,10 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
ObtainInputBlock(inputChunks[i], i);
}
bool finished = false;
#ifdef DEBUG
for (uint16_t i = 0; i < outputCount; ++i) {
// Alter mDuration so we can detect if ProduceAudioBlock fails to set
// chunks.
mLastChunks[i].mDuration--;
}
#endif
if (maxInputs <= 1 && mEngine->OutputCount() <= 1) {
mEngine->ProduceAudioBlock(this, inputChunks[0], &mLastChunks[0], &finished);
mEngine->ProcessBlock(this, inputChunks[0], &mLastChunks[0], &finished);
} else {
mEngine->ProduceAudioBlocksOnPorts(this, inputChunks, mLastChunks, &finished);
mEngine->ProcessBlocksOnPorts(this, inputChunks, mLastChunks, &finished);
}
for (uint16_t i = 0; i < outputCount; ++i) {
NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE,

View File

@ -33,6 +33,9 @@ class AudioNodeEngine;
* integrates audio processing with the MediaStreamGraph.
*/
class AudioNodeStream : public ProcessedMediaStream {
typedef dom::ChannelCountMode ChannelCountMode;
typedef dom::ChannelInterpretation ChannelInterpretation;
public:
typedef mozilla::dom::AudioContext AudioContext;
@ -56,8 +59,8 @@ public:
mMuted(false)
{
MOZ_ASSERT(NS_IsMainThread());
mChannelCountMode = dom::ChannelCountMode::Max;
mChannelInterpretation = dom::ChannelInterpretation::Speakers;
mChannelCountMode = ChannelCountMode::Max;
mChannelInterpretation = ChannelInterpretation::Speakers;
// AudioNodes are always producing data
mHasCurrentData = true;
MOZ_COUNT_CTOR(AudioNodeStream);
@ -79,8 +82,13 @@ public:
// This consumes the contents of aData. aData will be emptied after this returns.
void SetRawArrayData(nsTArray<float>& aData);
void SetChannelMixingParameters(uint32_t aNumberOfChannels,
dom::ChannelCountMode aChannelCountMoe,
dom::ChannelInterpretation aChannelInterpretation);
ChannelCountMode aChannelCountMoe,
ChannelInterpretation aChannelInterpretation);
ChannelInterpretation GetChannelInterpretation()
{
return mChannelInterpretation;
}
void SetAudioParamHelperStream()
{
MOZ_ASSERT(!mAudioParamStream, "Can only do this once");
@ -93,9 +101,9 @@ public:
void SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
double aStreamTime);
void SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
dom::ChannelCountMode aChannelCountMoe,
dom::ChannelInterpretation aChannelInterpretation);
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
ChannelCountMode aChannelCountMoe,
ChannelInterpretation aChannelInterpretation);
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
TrackTicks GetCurrentPosition();
bool IsAudioParamStream() const
{
@ -171,8 +179,8 @@ protected:
// The number of input channels that this stream requires. 0 means don't care.
uint32_t mNumberOfInputChannels;
// The mixing modes
dom::ChannelCountMode mChannelCountMode;
dom::ChannelInterpretation mChannelInterpretation;
ChannelCountMode mChannelCountMode;
ChannelInterpretation mChannelInterpretation;
// Whether the stream should be marked as finished as soon
// as the current time range has been computed block by block.
bool mMarkAsFinishedAfterThisBlock;

View File

@ -99,6 +99,7 @@ struct AudioChunk {
mDuration = aDuration;
mVolume = 1.0f;
}
int ChannelCount() const { return mChannelData.Length(); }
TrackTicks mDuration; // in frames within the buffer
nsRefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes

View File

@ -1102,7 +1102,7 @@ MediaStreamGraphImpl::ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
for (uint32_t i = aStreamIndex; i < mStreams.Length(); ++i) {
ProcessedMediaStream* ps = mStreams[i]->AsProcessedStream();
if (ps) {
ps->ProduceOutput(t, next, (next == aTo) ? ProcessedMediaStream::ALLOW_FINISH : 0);
ps->ProcessInput(t, next, (next == aTo) ? ProcessedMediaStream::ALLOW_FINISH : 0);
}
}
t = next;
@ -1211,7 +1211,7 @@ MediaStreamGraphImpl::RunThread()
// Play stream contents.
bool allBlockedForever = true;
// True when we've done ProduceOutput for all processed streams.
// True when we've done ProcessInput for all processed streams.
bool doneAllProducing = false;
// Figure out what each stream wants to do
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
@ -1237,8 +1237,8 @@ MediaStreamGraphImpl::RunThread()
ticksProcessed += TimeToTicksRoundDown(n->SampleRate(), mStateComputedTime - prevComputedTime);
doneAllProducing = true;
} else {
ps->ProduceOutput(prevComputedTime, mStateComputedTime,
ProcessedMediaStream::ALLOW_FINISH);
ps->ProcessInput(prevComputedTime, mStateComputedTime,
ProcessedMediaStream::ALLOW_FINISH);
NS_WARN_IF_FALSE(stream->mBuffer.GetEnd() >=
GraphTimeToStreamTime(stream, mStateComputedTime),
"Stream did not produce enough data");

View File

@ -906,7 +906,7 @@ protected:
/**
* This stream processes zero or more input streams in parallel to produce
* its output. The details of how the output is produced are handled by
* subclasses overriding the ProduceOutput method.
* subclasses overriding the ProcessInput method.
*/
class ProcessedMediaStream : public MediaStream {
public:
@ -962,7 +962,7 @@ public:
* This will be called on streams that have finished. Most stream types should
* just return immediately if IsFinishedOnGraphThread(), but some may wish to
* update internal state (see AudioNodeStream).
* ProduceOutput is allowed to call FinishOnGraphThread only if ALLOW_FINISH
* ProcessInput is allowed to call FinishOnGraphThread only if ALLOW_FINISH
* is in aFlags. (This flag will be set when aTo >= mStateComputedTime, i.e.
* when we've producing the last block of data we need to produce.) Otherwise
* we can get into a situation where we've determined the stream should not
@ -972,7 +972,7 @@ public:
enum {
ALLOW_FINISH = 0x01
};
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) = 0;
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) = 0;
void SetAutofinishImpl(bool aAutofinish) { mAutofinish = aAutofinish; }
/**
@ -1065,7 +1065,7 @@ public:
* Dispatches a runnable that will run on the main thread after all
* main-thread stream state has been next updated.
* Should only be called during MediaStreamListener callbacks or during
* ProcessedMediaStream::ProduceOutput().
* ProcessedMediaStream::ProcessInput().
*/
void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
{

View File

@ -39,7 +39,7 @@ public:
}
ProcessedMediaStream::RemoveInput(aPort);
}
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE
{
if (IsFinishedOnGraphThread()) {
return;

View File

@ -30,9 +30,9 @@ class JSObject;
#ifdef PR_LOGGING
PRLogModuleInfo* gMediaSourceLog;
#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(type, msg)
#define MSE_DEBUG(...)
#endif
// Arbitrary limit.
@ -40,6 +40,47 @@ static const unsigned int MAX_SOURCE_BUFFERS = 16;
namespace mozilla {
static const char* const gMediaSourceTypes[6] = {
"video/webm",
"audio/webm",
"video/mp4",
"audio/mp4",
"audio/mpeg",
nullptr
};
static nsresult
IsTypeSupported(const nsAString& aType)
{
if (aType.IsEmpty()) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
// TODO: Further restrict this to formats in the spec.
nsContentTypeParser parser(aType);
nsAutoString mimeType;
nsresult rv = parser.GetType(mimeType);
if (NS_FAILED(rv)) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
bool found = false;
for (uint32_t i = 0; gMediaSourceTypes[i]; ++i) {
if (mimeType.EqualsASCII(gMediaSourceTypes[i])) {
found = true;
break;
}
}
if (!found) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
// Check aType against HTMLMediaElement list of MIME types. Since we've
// already restricted the container format, this acts as a specific check
// of any specified "codecs" parameter of aType.
if (dom::HTMLMediaElement::GetCanPlay(aType) == CANPLAY_NO) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
return NS_OK;
}
namespace dom {
/* static */ already_AddRefed<MediaSource>
@ -103,7 +144,9 @@ MediaSource::SetDuration(double aDuration, ErrorResult& aRv)
already_AddRefed<SourceBuffer>
MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
{
if (!IsTypeSupportedInternal(aType, aRv)) {
nsresult rv = mozilla::IsTypeSupported(aType);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return nullptr;
}
if (mSourceBuffers->Length() >= MAX_SOURCE_BUFFERS) {
@ -116,15 +159,15 @@ MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
}
nsContentTypeParser parser(aType);
nsAutoString mimeType;
nsresult rv = parser.GetType(mimeType);
rv = parser.GetType(mimeType);
if (NS_FAILED(rv)) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(this, NS_ConvertUTF16toUTF8(mimeType));
mSourceBuffers->Append(sourceBuffer);
LOG(PR_LOG_DEBUG, ("%p AddSourceBuffer(Type=%s) -> %p", this,
NS_ConvertUTF16toUTF8(mimeType).get(), sourceBuffer.get()));
MSE_DEBUG("%p AddSourceBuffer(Type=%s) -> %p", this,
NS_ConvertUTF16toUTF8(mimeType).get(), sourceBuffer.get());
return sourceBuffer.forget();
}
@ -165,21 +208,45 @@ MediaSource::EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, Er
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
EndOfStreamInternal(aError, aRv);
SetReadyState(MediaSourceReadyState::Ended);
mSourceBuffers->Ended();
if (!aError.WasPassed()) {
// TODO:
// Run duration change algorithm.
// DurationChange(highestDurationOfSourceBuffers, aRv);
// if (aRv.Failed()) {
// return;
// }
// Notify media element that all data is now available.
return;
}
switch (aError.Value()) {
case MediaSourceEndOfStreamError::Network:
// TODO: If media element has a readyState of:
// HAVE_NOTHING -> run resource fetch algorithm
// > HAVE_NOTHING -> run "interrupted" steps of resource fetch
break;
case MediaSourceEndOfStreamError::Decode:
// TODO: If media element has a readyState of:
// HAVE_NOTHING -> run "unsupported" steps of resource fetch
// > HAVE_NOTHING -> run "corrupted" steps of resource fetch
break;
default:
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
}
}
/* static */ bool
MediaSource::IsTypeSupported(const GlobalObject& aGlobal,
const nsAString& aType)
MediaSource::IsTypeSupported(const GlobalObject&, const nsAString& aType)
{
ErrorResult unused;
return IsTypeSupportedInternal(aType, unused);
return NS_SUCCEEDED(mozilla::IsTypeSupported(aType));
}
bool
MediaSource::Attach(MediaSourceDecoder* aDecoder)
{
LOG(PR_LOG_DEBUG, ("%p Attaching decoder %p owner %p", this, aDecoder, aDecoder->GetOwner()));
MSE_DEBUG("%p Attaching decoder %p owner %p", this, aDecoder, aDecoder->GetOwner());
MOZ_ASSERT(aDecoder);
if (mReadyState != MediaSourceReadyState::Closed) {
return false;
@ -193,7 +260,7 @@ MediaSource::Attach(MediaSourceDecoder* aDecoder)
void
MediaSource::Detach()
{
LOG(PR_LOG_DEBUG, ("%p Detaching decoder %p owner %p", this, mDecoder.get(), mDecoder->GetOwner()));
MSE_DEBUG("%p Detaching decoder %p owner %p", this, mDecoder.get(), mDecoder->GetOwner());
MOZ_ASSERT(mDecoder);
mDecoder->DetachMediaSource();
mDecoder = nullptr;
@ -253,14 +320,14 @@ MediaSource::SetReadyState(MediaSourceReadyState aState)
void
MediaSource::DispatchSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Dispatching event %s to MediaSource", this, aName));
MSE_DEBUG("%p Dispatching event %s to MediaSource", this, aName);
DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
}
void
MediaSource::QueueAsyncSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Queuing event %s to MediaSource", this, aName));
MSE_DEBUG("%p Queuing event %s to MediaSource", this, aName);
nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
@ -283,82 +350,6 @@ MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv)
// TODO: Update media element's duration and run element's duration change algorithm.
}
void
MediaSource::EndOfStreamInternal(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv)
{
SetReadyState(MediaSourceReadyState::Ended);
mSourceBuffers->Ended();
if (!aError.WasPassed()) {
// TODO:
// Run duration change algorithm.
// DurationChange(highestDurationOfSourceBuffers, aRv);
// if (aRv.Failed()) {
// return;
// }
// Notify media element that all data is now available.
return;
}
switch (aError.Value()) {
case MediaSourceEndOfStreamError::Network:
// TODO: If media element has a readyState of:
// HAVE_NOTHING -> run resource fetch algorithm
// > HAVE_NOTHING -> run "interrupted" steps of resource fetch
break;
case MediaSourceEndOfStreamError::Decode:
// TODO: If media element has a readyState of:
// HAVE_NOTHING -> run "unsupported" steps of resource fetch
// > HAVE_NOTHING -> run "corrupted" steps of resource fetch
break;
default:
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
}
}
static const char* const gMediaSourceTypes[6] = {
"video/webm",
"audio/webm",
"video/mp4",
"audio/mp4",
"audio/mpeg",
nullptr
};
/* static */ bool
MediaSource::IsTypeSupportedInternal(const nsAString& aType, ErrorResult& aRv)
{
if (aType.IsEmpty()) {
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return false;
}
// TODO: Further restrict this to formats in the spec.
nsContentTypeParser parser(aType);
nsAutoString mimeType;
nsresult rv = parser.GetType(mimeType);
if (NS_FAILED(rv)) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return false;
}
bool found = false;
for (uint32_t i = 0; gMediaSourceTypes[i]; ++i) {
if (mimeType.EqualsASCII(gMediaSourceTypes[i])) {
found = true;
break;
}
}
if (!found) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return false;
}
// Check aType against HTMLMediaElement list of MIME types. Since we've
// already restricted the container format, this acts as a specific check
// of any specified "codecs" parameter of aType.
if (HTMLMediaElement::GetCanPlay(aType) == CANPLAY_NO) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return false;
}
return true;
}
nsPIDOMWindow*
MediaSource::GetParentObject() const
{

View File

@ -60,8 +60,7 @@ public:
void RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv);
void EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv);
static bool IsTypeSupported(const GlobalObject& aGlobal,
const nsAString& aType);
static bool IsTypeSupported(const GlobalObject&, const nsAString& aType);
/** End WebIDL Methods. */
NS_DECL_ISUPPORTS_INHERITED
@ -98,9 +97,6 @@ private:
void QueueAsyncSimpleEvent(const char* aName);
void DurationChange(double aNewDuration, ErrorResult& aRv);
void EndOfStreamInternal(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv);
static bool IsTypeSupportedInternal(const nsAString& aType, ErrorResult& aRv);
double mDuration;

View File

@ -23,9 +23,9 @@
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaSourceLog;
#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(type, msg)
#define MSE_DEBUG(...)
#endif
namespace mozilla {
@ -202,7 +202,7 @@ MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mDecoders.AppendElement(decoder);
mReaders.AppendElement(reader);
LOG(PR_LOG_DEBUG, ("Registered subdecoder %p subreader %p", decoder.get(), reader.get()));
MSE_DEBUG("Registered subdecoder %p subreader %p", decoder.get(), reader.get());
mon.NotifyAll();
decoder->SetReader(reader.forget());
@ -221,7 +221,7 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
MediaDecoderReader* reader = readers[i];
MediaInfo mi;
nsresult rv = reader->ReadMetadata(&mi, aTags);
LOG(PR_LOG_DEBUG, ("ReadMetadata on SB reader %p", reader));
MSE_DEBUG("ReadMetadata on SB reader %p", reader);
if (NS_FAILED(rv)) {
return rv;
}

View File

@ -26,9 +26,9 @@ class JSObject;
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaSourceLog;
#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(type, msg)
#define MSE_DEBUG(...)
#endif
namespace mozilla {
@ -289,14 +289,14 @@ SourceBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
void
SourceBuffer::DispatchSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Dispatching event %s to SourceBuffer", this, aName));
MSE_DEBUG("%p Dispatching event %s to SourceBuffer", this, aName);
DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
}
void
SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Queuing event %s to SourceBuffer", this, aName));
MSE_DEBUG("%p Queuing event %s to SourceBuffer", this, aName);
nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
@ -339,7 +339,7 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
}
// TODO: Run coded frame eviction algorithm.
// TODO: Test buffer full flag.
LOG(PR_LOG_DEBUG, ("%p Append(ArrayBuffer=%u)", this, aLength));
MSE_DEBUG("%p Append(ArrayBuffer=%u)", this, aLength);
StartUpdating();
// XXX: For future reference: NDA call must run on the main thread.
mDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),

View File

@ -22,9 +22,9 @@ class JSObject;
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaSourceLog;
#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(type, msg)
#define MSE_DEBUG(...)
#endif
namespace mozilla {
@ -122,14 +122,14 @@ SourceBufferList::Ended()
void
SourceBufferList::DispatchSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Dispatching event %s to SourceBufferList", this, aName));
MSE_DEBUG("%p Dispatching event %s to SourceBufferList", this, aName);
DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
}
void
SourceBufferList::QueueAsyncSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Queuing event %s to SourceBufferList", this, aName));
MSE_DEBUG("%p Queuing event %s to SourceBufferList", this, aName);
nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBufferList>(this, aName);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}

View File

@ -16,9 +16,9 @@
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaSourceLog;
#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(type, msg)
#define MSE_DEBUG(...)
#endif
namespace mozilla {
@ -33,7 +33,7 @@ nsresult
SourceBufferResource::Close()
{
ReentrantMonitorAutoEnter mon(mMonitor);
LOG(PR_LOG_DEBUG, ("%p SBR::Close", this));
MSE_DEBUG("%p SBR::Close", this);
//MOZ_ASSERT(!mClosed);
mClosed = true;
mon.NotifyAll();
@ -49,20 +49,20 @@ SourceBufferResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
while (blockingRead &&
!mEnded &&
mOffset + aCount > static_cast<uint64_t>(GetLength())) {
LOG(PR_LOG_DEBUG, ("%p SBR::Read waiting for data", this));
MSE_DEBUG("%p SBR::Read waiting for data", this);
mon.Wait();
}
uint32_t available = GetLength() - mOffset;
uint32_t count = std::min(aCount, available);
if (!PR_GetEnv("MOZ_QUIET")) {
LOG(PR_LOG_DEBUG, ("%p SBR::Read aCount=%u length=%u offset=%u "
"available=%u count=%u, blocking=%d bufComplete=%d",
this, aCount, GetLength(), mOffset, available, count,
blockingRead, mEnded));
MSE_DEBUG("%p SBR::Read aCount=%u length=%u offset=%u "
"available=%u count=%u, blocking=%d bufComplete=%d",
this, aCount, GetLength(), mOffset, available, count,
blockingRead, mEnded);
}
if (available == 0) {
LOG(PR_LOG_DEBUG, ("%p SBR::Read EOF", this));
MSE_DEBUG("%p SBR::Read EOF", this);
*aBytes = 0;
return NS_OK;
}
@ -160,7 +160,7 @@ SourceBufferResource::Ended()
SourceBufferResource::~SourceBufferResource()
{
MOZ_COUNT_DTOR(SourceBufferResource);
LOG(PR_LOG_DEBUG, ("%p SBR::~SBR", this));
MSE_DEBUG("%p SBR::~SBR", this);
}
SourceBufferResource::SourceBufferResource(nsIPrincipal* aPrincipal,
@ -173,7 +173,7 @@ SourceBufferResource::SourceBufferResource(nsIPrincipal* aPrincipal,
, mEnded(false)
{
MOZ_COUNT_CTOR(SourceBufferResource);
LOG(PR_LOG_DEBUG, ("%p SBR::SBR()", this));
MSE_DEBUG("%p SBR::SBR()", this);
}
} // namespace mozilla

Some files were not shown because too many files have changed in this diff Show More