Merge mozilla-central to b2g-inbound
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
269
accessible/src/jsat/ContentControl.jsm
Normal 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
|
||||
])
|
||||
};
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
},
|
||||
|
@ -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');
|
||||
|
@ -9,6 +9,7 @@ JS_MODULES_PATH = 'modules/accessibility'
|
||||
EXTRA_JS_MODULES += [
|
||||
'AccessFu.jsm',
|
||||
'Constants.jsm',
|
||||
'ContentControl.jsm',
|
||||
'EventManager.jsm',
|
||||
'OutputGenerator.jsm',
|
||||
'Presentation.jsm',
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -28,8 +28,6 @@ LIBS += \
|
||||
endif
|
||||
endif
|
||||
|
||||
STL_FLAGS=
|
||||
|
||||
LIBS += $(JEMALLOC_LIBS)
|
||||
|
||||
LIBS += \
|
||||
|
@ -41,3 +41,5 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
]
|
||||
|
||||
LDFLAGS += ['-Wl,--export-dynamic']
|
||||
|
||||
DISABLE_STL_WRAPPING = True
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -472,7 +472,6 @@ BrowserGlue.prototype = {
|
||||
PageThumbs.init();
|
||||
NewTabUtils.init();
|
||||
BrowserNewTabPreloader.init();
|
||||
CustomizationTabPreloader.init();
|
||||
SignInToWebsiteUX.init();
|
||||
PdfJs.init();
|
||||
#ifdef NIGHTLY_BUILD
|
||||
|
@ -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));
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
},
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
});
|
@ -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");
|
||||
},
|
||||
|
@ -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.
|
||||
|
@ -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]
|
||||
|
@ -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>
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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":
|
||||
|
@ -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');
|
||||
|
@ -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>
|
||||
|
@ -32,6 +32,7 @@ let Elements = {};
|
||||
["urlbarState", "bcast_urlbarState"],
|
||||
["loadingState", "bcast_loadingState"],
|
||||
["windowState", "bcast_windowState"],
|
||||
["chromeState", "bcast_chromeState"],
|
||||
["mainKeyset", "mainKeyset"],
|
||||
["stack", "stack"],
|
||||
["tabList", "tabs"],
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"/>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -341,4 +341,49 @@ gTests.push({
|
||||
including correct values for state variables (e.g. _downloadCount, _downloadsInProgress) \
|
||||
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();
|
||||
}
|
||||
});
|
95
browser/metro/base/tests/mochitest/browser_notifications.js
Normal 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
|
||||
});
|
||||
|
@ -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.
|
||||
|
@ -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]
|
||||
|
@ -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 =
|
||||
|
@ -18,3 +18,5 @@ for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM'):
|
||||
DEFINES[var] = True
|
||||
|
||||
NO_PGO = True
|
||||
|
||||
RCINCLUDE = 'CommandExecuteHandler.rc'
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 23 KiB |
@ -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)
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 60 KiB |
@ -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
|
||||
|
@ -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"],
|
||||
|
Before Width: | Height: | Size: 668 B |
@ -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 {
|
||||
|
BIN
browser/themes/shared/devtools/images/controls.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
browser/themes/shared/devtools/images/controls@2x.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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)
|
||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 23 KiB |
@ -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.
|
||||
|
@ -52,3 +52,4 @@ LOCAL_INCLUDES += [
|
||||
'stlport',
|
||||
]
|
||||
|
||||
DISABLE_STL_WRAPPING = True
|
||||
|
@ -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
|
||||
|
@ -17,3 +17,5 @@ if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
|
||||
FORCE_STATIC_LIB = True
|
||||
|
||||
NO_PGO = True
|
||||
|
||||
DISABLE_STL_WRAPPING = True
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
@ -244,13 +243,21 @@ private:
|
||||
// ever passed to Mutated().
|
||||
enum { eMaxMutations = 300 };
|
||||
|
||||
|
||||
|
||||
// sMutationCount is a global mutation counter which is decreased by one at
|
||||
// every mutation. It is capped at 0 to avoid wrapping.
|
||||
// Its value is always between 0 and 300, inclusive.
|
||||
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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
#include "nsICommandManager.h"
|
||||
#include "mozilla/dom/HTMLSharedElement.h"
|
||||
#include "nsDOMEvent.h"
|
||||
|
||||
class nsIEditor;
|
||||
class nsIParser;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|