Merge m-c to s-c.

This commit is contained in:
Richard Newman 2014-01-28 15:14:23 -08:00
commit d6695099cc
644 changed files with 14380 additions and 4869 deletions

View File

@ -581,7 +581,6 @@ Accessible::VisibilityState()
return states::INVISIBLE;
nsIFrame* curFrame = frame;
nsPoint framePos(0, 0);
do {
nsView* view = curFrame->GetView();
if (view && view->GetVisibility() == nsViewVisibility_kHide)
@ -605,11 +604,11 @@ Accessible::VisibilityState()
// If contained by scrollable frame then check that at least 12 pixels
// around the object is visible, otherwise the object is offscreen.
framePos += curFrame->GetPosition();
nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);
if (scrollableFrame) {
nsRect scrollPortRect = scrollableFrame->GetScrollPortRect();
nsRect frameRect(framePos, frame->GetSize());
nsRect frameRect = nsLayoutUtils::TransformFrameRectToAncestor(
frame, frame->GetRectRelativeToSelf(), parentFrame);
if (!scrollPortRect.Contains(frameRect)) {
const nscoord kMinPixels = nsPresContext::CSSPixelsToAppUnits(12);
scrollPortRect.Deflate(kMinPixels, kMinPixels);

View File

@ -3,37 +3,50 @@ const Cu = Components.utils;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
this.EXPORTED_SYMBOLS = ['Roles', 'Events', 'Relations', 'Filters'];
this.EXPORTED_SYMBOLS = ['Roles', 'Events', 'Relations', 'Filters', 'States'];
function ConstantsMap (aObject, aPrefix) {
function ConstantsMap (aObject, aPrefix, aMap = {}, aModifier = null) {
let offset = aPrefix.length;
for (var name in aObject) {
if (name.indexOf(aPrefix) === 0) {
this[name.slice(offset)] = aObject[name];
aMap[name.slice(offset)] = aModifier ?
aModifier(aObject[name]) : aObject[name];
}
}
return aMap;
}
XPCOMUtils.defineLazyGetter(
this, 'Roles',
function() {
return new ConstantsMap(Ci.nsIAccessibleRole, 'ROLE_');
return ConstantsMap(Ci.nsIAccessibleRole, 'ROLE_');
});
XPCOMUtils.defineLazyGetter(
this, 'Events',
function() {
return new ConstantsMap(Ci.nsIAccessibleEvent, 'EVENT_');
return ConstantsMap(Ci.nsIAccessibleEvent, 'EVENT_');
});
XPCOMUtils.defineLazyGetter(
this, 'Relations',
function() {
return new ConstantsMap(Ci.nsIAccessibleRelation, 'RELATION_');
return ConstantsMap(Ci.nsIAccessibleRelation, 'RELATION_');
});
XPCOMUtils.defineLazyGetter(
this, 'Filters',
function() {
return new ConstantsMap(Ci.nsIAccessibleTraversalRule, 'FILTER_');
return ConstantsMap(Ci.nsIAccessibleTraversalRule, 'FILTER_');
});
XPCOMUtils.defineLazyGetter(
this, 'States',
function() {
let statesMap = ConstantsMap(Ci.nsIAccessibleStates, 'STATE_', {},
(val) => { return { base: val, extended: 0 }; });
ConstantsMap(Ci.nsIAccessibleStates, 'EXT_STATE_', statesMap,
(val) => { return { base: 0, extended: val }; });
return statesMap;
});

View File

@ -24,6 +24,8 @@ XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Events',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'States',
'resource://gre/modules/accessibility/Constants.jsm');
this.EXPORTED_SYMBOLS = ['EventManager'];
@ -147,13 +149,13 @@ this.EventManager.prototype = {
case Events.STATE_CHANGE:
{
let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
if (event.state == Ci.nsIAccessibleStates.STATE_CHECKED &&
!(event.isExtraState)) {
let state = Utils.getState(event);
if (state.contains(States.CHECKED)) {
this.present(
Presentation.
actionInvoked(aEvent.accessible,
event.isEnabled ? 'check' : 'uncheck'));
} else if (event.state == Ci.nsIAccessibleStates.STATE_SELECTED) {
} else if (state.contains(States.SELECTED)) {
this.present(
Presentation.
actionInvoked(aEvent.accessible,
@ -176,10 +178,10 @@ this.EventManager.prototype = {
QueryInterface(Ci.nsIAccessibleCaretMoveEvent).caretOffset;
// Update editing state, both for presenter and other things
let [,extState] = Utils.getStates(acc);
let state = Utils.getState(acc);
let editState = {
editing: !!(extState & Ci.nsIAccessibleStates.EXT_STATE_EDITABLE),
multiline: !!(extState & Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE),
editing: state.contains(States.EDITABLE),
multiline: state.contains(States.MULTI_LINE),
atStart: caretOffset == 0,
atEnd: caretOffset == characterCount
};

View File

@ -29,6 +29,8 @@ XPCOMUtils.defineLazyModuleGetter(this, 'PluralForm',
'resource://gre/modules/PluralForm.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'States',
'resource://gre/modules/accessibility/Constants.jsm');
var gStringBundle = Cc['@mozilla.org/intl/stringbundle;1'].
getService(Ci.nsIStringBundleService).
@ -97,7 +99,7 @@ this.OutputGenerator = {
* context information for a given accessible and its relationship with
* another accessible.
* @return {Array} Two string array. The first string describes the object
* and its states. The second string is the object's name. Whether the
* and its state. The second string is the object's name. Whether the
* object's description or it's role is included is determined by
* {@link roleRuleMap}.
*/
@ -112,11 +114,8 @@ this.OutputGenerator = {
if (aAccessible.childCount == 0)
flags |= INCLUDE_NAME;
let state = {};
let extState = {};
aAccessible.getState(state, extState);
let states = {base: state.value, ext: extState.value};
return func.apply(this, [aAccessible, roleString, states, flags, aContext]);
return func.apply(this, [aAccessible, roleString,
Utils.getState(aAccessible), flags, aContext]);
},
/**
@ -240,7 +239,7 @@ this.OutputGenerator = {
_getLocalizedRole: function _getLocalizedRole(aRoleStr) {},
_getLocalizedStates: function _getLocalizedStates(aStates) {},
_getLocalizedState: function _getLocalizedState(aState) {},
_getPluralFormString: function _getPluralFormString(aString, aCount) {
let str = gStringBundle.GetStringFromName(this._getOutputName(aString));
@ -322,11 +321,11 @@ this.OutputGenerator = {
'definitionlist': INCLUDE_DESC | INCLUDE_NAME},
objectOutputFunctions: {
_generateBaseOutput: function _generateBaseOutput(aAccessible, aRoleStr, aStates, aFlags) {
_generateBaseOutput: function _generateBaseOutput(aAccessible, aRoleStr, aState, aFlags) {
let output = [];
if (aFlags & INCLUDE_DESC) {
let desc = this._getLocalizedStates(aStates);
let desc = this._getLocalizedState(aState);
let roleStr = this._getLocalizedRole(aRoleStr);
if (roleStr) {
this._addType(desc, aAccessible, aRoleStr);
@ -349,7 +348,7 @@ this.OutputGenerator = {
return output;
},
label: function label(aAccessible, aRoleStr, aStates, aFlags, aContext) {
label: function label(aAccessible, aRoleStr, aState, aFlags, aContext) {
if (aContext.isNestedControl ||
aContext.accessible == Utils.getEmbeddedControl(aAccessible)) {
// If we are on a nested control, or a nesting label,
@ -360,20 +359,19 @@ this.OutputGenerator = {
return this.objectOutputFunctions.defaultFunc.apply(this, arguments);
},
entry: function entry(aAccessible, aRoleStr, aStates, aFlags) {
let rolestr = (aStates.ext & Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE) ?
'textarea' : 'entry';
entry: function entry(aAccessible, aRoleStr, aState, aFlags) {
let rolestr = aState.contains(States.MULTI_LINE) ? 'textarea' : 'entry';
return this.objectOutputFunctions.defaultFunc.apply(
this, [aAccessible, rolestr, aStates, aFlags]);
this, [aAccessible, rolestr, aState, aFlags]);
},
pagetab: function pagetab(aAccessible, aRoleStr, aStates, aFlags) {
pagetab: function pagetab(aAccessible, aRoleStr, aState, aFlags) {
let localizedRole = this._getLocalizedRole(aRoleStr);
let itemno = {};
let itemof = {};
aAccessible.groupPosition({}, itemof, itemno);
let output = [];
let desc = this._getLocalizedStates(aStates);
let desc = this._getLocalizedState(aState);
desc.push(
gStringBundle.formatStringFromName(
'objItemOf', [localizedRole, itemno.value, itemof.value], 3));
@ -385,7 +383,7 @@ this.OutputGenerator = {
return output;
},
table: function table(aAccessible, aRoleStr, aStates, aFlags) {
table: function table(aAccessible, aRoleStr, aState, aFlags) {
let output = [];
let table;
try {
@ -499,11 +497,11 @@ this.UtteranceGenerator = {
__proto__: OutputGenerator.objectOutputFunctions,
defaultFunc: function defaultFunc(aAccessible, aRoleStr, aStates, aFlags) {
defaultFunc: function defaultFunc(aAccessible, aRoleStr, aState, aFlags) {
return this.objectOutputFunctions._generateBaseOutput.apply(this, arguments);
},
heading: function heading(aAccessible, aRoleStr, aStates, aFlags) {
heading: function heading(aAccessible, aRoleStr, aState, aFlags) {
let level = {};
aAccessible.groupPosition(level, {}, {});
let utterance =
@ -515,7 +513,7 @@ this.UtteranceGenerator = {
return utterance;
},
listitem: function listitem(aAccessible, aRoleStr, aStates, aFlags) {
listitem: function listitem(aAccessible, aRoleStr, aState, aFlags) {
let itemno = {};
let itemof = {};
aAccessible.groupPosition({}, itemof, itemno);
@ -531,26 +529,26 @@ this.UtteranceGenerator = {
return utterance;
},
list: function list(aAccessible, aRoleStr, aStates, aFlags) {
list: function list(aAccessible, aRoleStr, aState, aFlags) {
return this._getListUtterance
(aAccessible, aRoleStr, aFlags, aAccessible.childCount);
},
definitionlist: function definitionlist(aAccessible, aRoleStr, aStates, aFlags) {
definitionlist: function definitionlist(aAccessible, aRoleStr, aState, aFlags) {
return this._getListUtterance
(aAccessible, aRoleStr, aFlags, aAccessible.childCount / 2);
},
application: function application(aAccessible, aRoleStr, aStates, aFlags) {
application: function application(aAccessible, aRoleStr, aState, aFlags) {
// Don't utter location of applications, it gets tiring.
if (aAccessible.name != aAccessible.DOMNode.location)
return this.objectOutputFunctions.defaultFunc.apply(this,
[aAccessible, aRoleStr, aStates, aFlags]);
[aAccessible, aRoleStr, aState, aFlags]);
return [];
},
cell: function cell(aAccessible, aRoleStr, aStates, aFlags, aContext) {
cell: function cell(aAccessible, aRoleStr, aState, aFlags, aContext) {
let utterance = [];
let cell = aContext.getCellInfo(aAccessible);
if (cell) {
@ -612,10 +610,10 @@ this.UtteranceGenerator = {
}
},
_getLocalizedStates: function _getLocalizedStates(aStates) {
_getLocalizedState: function _getLocalizedState(aState) {
let stateUtterances = [];
if (aStates.base & Ci.nsIAccessibleStates.STATE_UNAVAILABLE) {
if (aState.contains(States.UNAVAILABLE)) {
stateUtterances.push(gStringBundle.GetStringFromName('stateUnavailable'));
}
@ -623,31 +621,31 @@ this.UtteranceGenerator = {
// This is because we expose the checked information on the node itself.
// XXX: this means the checked state is always appended to the end, regardless
// of the utterance ordering preference.
if (Utils.AndroidSdkVersion < 16 && aStates.base & Ci.nsIAccessibleStates.STATE_CHECKABLE) {
let stateStr = (aStates.base & Ci.nsIAccessibleStates.STATE_CHECKED) ?
if (Utils.AndroidSdkVersion < 16 && aState.contains(States.CHECKABLE)) {
let statetr = aState.contains(States.CHECKED) ?
'stateChecked' : 'stateNotChecked';
stateUtterances.push(gStringBundle.GetStringFromName(stateStr));
stateUtterances.push(gStringBundle.GetStringFromName(statetr));
}
if (aStates.ext & Ci.nsIAccessibleStates.EXT_STATE_EXPANDABLE) {
let stateStr = (aStates.base & Ci.nsIAccessibleStates.STATE_EXPANDED) ?
if (aState.contains(States.EXPANDABLE)) {
let statetr = aState.contains(States.EXPANDED) ?
'stateExpanded' : 'stateCollapsed';
stateUtterances.push(gStringBundle.GetStringFromName(stateStr));
stateUtterances.push(gStringBundle.GetStringFromName(statetr));
}
if (aStates.base & Ci.nsIAccessibleStates.STATE_REQUIRED) {
if (aState.contains(States.REQUIRED)) {
stateUtterances.push(gStringBundle.GetStringFromName('stateRequired'));
}
if (aStates.base & Ci.nsIAccessibleStates.STATE_TRAVERSED) {
if (aState.contains(States.TRAVERSED)) {
stateUtterances.push(gStringBundle.GetStringFromName('stateTraversed'));
}
if (aStates.base & Ci.nsIAccessibleStates.STATE_HASPOPUP) {
if (aState.contains(States.HASPOPUP)) {
stateUtterances.push(gStringBundle.GetStringFromName('stateHasPopup'));
}
if (aStates.base & Ci.nsIAccessibleStates.STATE_SELECTED) {
if (aState.contains(States.SELECTED)) {
stateUtterances.push(gStringBundle.GetStringFromName('stateSelected'));
}
@ -717,11 +715,11 @@ this.BrailleGenerator = {
__proto__: OutputGenerator.objectOutputFunctions,
defaultFunc: function defaultFunc(aAccessible, aRoleStr, aStates, aFlags) {
defaultFunc: function defaultFunc(aAccessible, aRoleStr, aState, aFlags) {
return this.objectOutputFunctions._generateBaseOutput.apply(this, arguments);
},
listitem: function listitem(aAccessible, aRoleStr, aStates, aFlags) {
listitem: function listitem(aAccessible, aRoleStr, aState, aFlags) {
let braille = [];
this._addName(braille, aAccessible, aFlags);
@ -730,7 +728,7 @@ this.BrailleGenerator = {
return braille;
},
cell: function cell(aAccessible, aRoleStr, aStates, aFlags, aContext) {
cell: function cell(aAccessible, aRoleStr, aState, aFlags, aContext) {
let braille = [];
let cell = aContext.getCellInfo(aAccessible);
if (cell) {
@ -763,7 +761,7 @@ this.BrailleGenerator = {
return this.objectOutputFunctions.cell.apply(this, arguments);
},
statictext: function statictext(aAccessible, aRoleStr, aStates, aFlags) {
statictext: function statictext(aAccessible, aRoleStr, aState, aFlags) {
// Since we customize the list bullet's output, we add the static
// text from the first node in each listitem, so skip it here.
if (aAccessible.parent.role == Roles.LISTITEM) {
@ -773,10 +771,10 @@ this.BrailleGenerator = {
return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
},
_useStateNotRole: function _useStateNotRole(aAccessible, aRoleStr, aStates, aFlags) {
_useStateNotRole: function _useStateNotRole(aAccessible, aRoleStr, aState, aFlags) {
let braille = [];
let desc = this._getLocalizedStates(aStates);
let desc = this._getLocalizedState(aState);
braille.push(desc.join(' '));
this._addName(braille, aAccessible, aFlags);
@ -785,15 +783,15 @@ this.BrailleGenerator = {
return braille;
},
checkbutton: function checkbutton(aAccessible, aRoleStr, aStates, aFlags) {
checkbutton: function checkbutton(aAccessible, aRoleStr, aState, aFlags) {
return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
},
radiobutton: function radiobutton(aAccessible, aRoleStr, aStates, aFlags) {
radiobutton: function radiobutton(aAccessible, aRoleStr, aState, aFlags) {
return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
},
togglebutton: function radiobutton(aAccessible, aRoleStr, aStates, aFlags) {
togglebutton: function radiobutton(aAccessible, aRoleStr, aState, aFlags) {
return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
}
},
@ -823,14 +821,14 @@ this.BrailleGenerator = {
}
},
_getLocalizedStates: function _getLocalizedStates(aStates) {
_getLocalizedState: function _getLocalizedState(aState) {
let stateBraille = [];
let getCheckedState = function getCheckedState() {
let resultMarker = [];
let state = aStates.base;
let fill = !!(state & Ci.nsIAccessibleStates.STATE_CHECKED) ||
!!(state & Ci.nsIAccessibleStates.STATE_PRESSED);
let state = aState;
let fill = state.contains(States.CHECKED) ||
state.contains(States.PRESSED);
resultMarker.push('(');
resultMarker.push(fill ? 'x' : ' ');
@ -839,7 +837,7 @@ this.BrailleGenerator = {
return resultMarker.join('');
};
if (aStates.base & Ci.nsIAccessibleStates.STATE_CHECKABLE) {
if (aState.contains(States.CHECKABLE)) {
stateBraille.push(getCheckedState());
}

View File

@ -22,6 +22,8 @@ XPCOMUtils.defineLazyModuleGetter(this, 'BrailleGenerator',
'resource://gre/modules/accessibility/OutputGenerator.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'States',
'resource://gre/modules/accessibility/Constants.jsm');
this.EXPORTED_SYMBOLS = ['Presentation'];
@ -284,16 +286,14 @@ AndroidPresenter.prototype = {
});
}
} else {
let state = Utils.getStates(aContext.accessible)[0];
let state = Utils.getState(aContext.accessible);
androidEvents.push({eventType: (isExploreByTouch) ?
this.ANDROID_VIEW_HOVER_ENTER : focusEventType,
text: UtteranceGenerator.genForContext(aContext).output,
bounds: aContext.bounds,
clickable: aContext.accessible.actionCount > 0,
checkable: !!(state &
Ci.nsIAccessibleStates.STATE_CHECKABLE),
checked: !!(state &
Ci.nsIAccessibleStates.STATE_CHECKED),
checkable: state.contains(States.CHECKABLE),
checked: state.contains(States.CHECKED),
brailleOutput: brailleOutput});
}
@ -305,10 +305,10 @@ AndroidPresenter.prototype = {
},
actionInvoked: function AndroidPresenter_actionInvoked(aObject, aActionName) {
let state = Utils.getStates(aObject)[0];
let state = Utils.getState(aObject);
// Checkable objects will have a state changed event we will use instead.
if (state & Ci.nsIAccessibleStates.STATE_CHECKABLE)
if (state.contains(States.CHECKABLE))
return null;
return {
@ -316,7 +316,7 @@ AndroidPresenter.prototype = {
details: [{
eventType: this.ANDROID_VIEW_CLICKED,
text: UtteranceGenerator.genForAction(aObject, aActionName),
checked: !!(state & Ci.nsIAccessibleStates.STATE_CHECKED)
checked: state.contains(States.CHECKED)
}]
};
},

View File

@ -17,6 +17,8 @@ XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Filters',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'States',
'resource://gre/modules/accessibility/Constants.jsm');
let gSkipEmptyImages = new PrefCache('accessibility.accessfu.skip_empty_images');
@ -153,10 +155,7 @@ this.TraversalRules = {
function Anchor_match(aAccessible)
{
// We want to ignore links, only focus named anchors.
let state = {};
let extraState = {};
aAccessible.getState(state, extraState);
if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
if (Utils.getState(aAccessible).contains(States.LINKED)) {
return Filters.IGNORE;
} else {
return Filters.MATCH;
@ -221,10 +220,7 @@ this.TraversalRules = {
function Link_match(aAccessible)
{
// We want to ignore anchors, only focus real links.
let state = {};
let extraState = {};
aAccessible.getState(state, extraState);
if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
if (Utils.getState(aAccessible).contains(States.LINKED)) {
return Filters.MATCH;
} else {
return Filters.IGNORE;

View File

@ -19,6 +19,8 @@ XPCOMUtils.defineLazyModuleGetter(this, 'Events',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Relations',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'States',
'resource://gre/modules/accessibility/Constants.jsm');
this.EXPORTED_SYMBOLS = ['Utils', 'Logger', 'PivotContext', 'PrefCache', 'SettingCache'];
@ -186,14 +188,36 @@ this.Utils = {
}
},
getStates: function getStates(aAccessible) {
if (!aAccessible)
return [0, 0];
getState: function getState(aAccessibleOrEvent) {
function State(aBase, aExtended) {
this.base = aBase;
this.extended = aExtended;
let state = {};
let extState = {};
aAccessible.getState(state, extState);
return [state.value, extState.value];
this.contains = (other) => {
return !!(this.base & other.base || this.extended & other.extended);
};
this.toString = () => {
let stateStrings = Utils.AccRetrieval.
getStringStates(this.base, this.extended);
let statesArray = new Array(stateStrings.length);
for (let i = 0; i < statesArray.length; i++) {
statesArray[i] = stateStrings.item(i);
}
return '[' + statesArray.join(', ') + ']';
};
}
if (aAccessibleOrEvent instanceof Ci.nsIAccessibleStateChangeEvent) {
return new State(
aAccessibleOrEvent.isExtraState ? 0 : aAccessibleOrEvent.state,
aAccessibleOrEvent.isExtraState ? aAccessibleOrEvent.state : 0);
} else {
let state = {};
let extState = {};
aAccessibleOrEvent.getState(state, extState);
return new State(state.value, extState.value);
}
},
getAttributes: function getAttributes(aAccessible) {
@ -251,11 +275,8 @@ this.Utils = {
}
try {
let extstate = {};
let state = {};
aAccessible.getState(state, extstate);
if (extstate.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT ||
state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE ||
let state = this.getState(aAccessible);
if (state.contains(States.DEFUNCT) || state.contains(States.INVISIBLE) ||
Utils.inHiddenSubtree(aAccessible)) {
return false;
}
@ -409,12 +430,7 @@ this.Logger = {
},
statesToString: function statesToString(aAccessible) {
let [state, extState] = Utils.getStates(aAccessible);
let stringArray = [];
let stateStrings = Utils.AccRetrieval.getStringStates(state, extState);
for (var i=0; i < stateStrings.length; i++)
stringArray.push(stateStrings.item(i));
return stringArray.join(' ');
return Utils.getState(aAccessible).toString();
},
dumpTree: function dumpTree(aLogLevel, aRootAccessible) {
@ -587,8 +603,7 @@ PivotContext.prototype = {
if (this._includeInvisible) {
include = true;
} else {
let [state,] = Utils.getStates(child);
include = !(state & Ci.nsIAccessibleStates.STATE_INVISIBLE);
include = !(Utils.getState(child).contains(States.INVISIBLE));
}
if (include) {
if (aPreorder) {
@ -716,9 +731,7 @@ PivotContext.prototype = {
_isDefunct: function _isDefunct(aAccessible) {
try {
let extstate = {};
aAccessible.getState({}, extstate);
return !!(extstate.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT);
return Utils.getState(aAccessible).contains(States.DEFUNCT);
} catch (x) {
return true;
}

View File

@ -344,13 +344,21 @@ function scroll(aMessage) {
function adjustRange(aMessage) {
function sendUpDownKey(aAccessible) {
let acc = Utils.getEmbeddedControl(aAccessible) || aAccessible;
if (acc.DOMNode) {
let evt = content.document.createEvent('KeyboardEvent');
let keycode = aMessage.json.direction == 'forward' ?
content.KeyEvent.DOM_VK_DOWN : content.KeyEvent.DOM_VK_UP;
evt.initKeyEvent(
"keypress", false, true, null, false, false, false, false, keycode, 0);
acc.DOMNode.dispatchEvent(evt);
let elem = acc.DOMNode;
if (elem) {
if (elem.tagName === 'INPUT' && elem.type === 'range') {
elem[aMessage.json.direction === 'forward' ? 'stepDown' : 'stepUp']();
let changeEvent = content.document.createEvent('UIEvent');
changeEvent.initEvent('change', true, true);
elem.dispatchEvent(changeEvent);
} else {
let evt = content.document.createEvent('KeyboardEvent');
let keycode = aMessage.json.direction == 'forward' ?
content.KeyEvent.DOM_VK_DOWN : content.KeyEvent.DOM_VK_UP;
evt.initKeyEvent(
"keypress", false, true, null, false, false, false, false, keycode, 0);
elem.dispatchEvent(evt);
}
}
}

View File

@ -129,6 +129,10 @@
// roles transformed by ARIA state attributes
testRole("togglebutton", ROLE_TOGGLE_BUTTON);
//////////////////////////////////////////////////////////////////////////
// ignore unknown roles, take first known
testRole("unknown_roles", ROLE_PUSHBUTTON);
//////////////////////////////////////////////////////////////////////////
// misc roles
testRole("note", ROLE_NOTE);
@ -300,6 +304,9 @@
<!-- roles transformed by ARIA state attributes -->
<button aria-pressed="true" id="togglebutton">
<!-- take the first known mappable role -->
<div role="wiggly:worm abc123 button" id="unknown_roles">worm button</div>
<!-- misc roles -->
<div role="note" id="note">note</div>
<div role="scrollbar" id="scrollbar">scrollbar</div>

View File

@ -119,6 +119,7 @@
{
testStates("div", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
testStates("div_off", STATE_OFFSCREEN, 0, STATE_INVISIBLE);
testStates("div_transformed", STATE_OFFSCREEN, 0, STATE_INVISIBLE);
testStates("div_abschild", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
gQueue = new eventQueue();
@ -160,6 +161,9 @@
<div id="div_off" style="position: absolute; left:-999px; top:-999px">
offscreen!
</div>
<div id="div_transformed" style="transform: translate(-999px, -999px);">
transformed!
</div>
<!-- edge case: no rect but has out of flow child -->
<div id="div_abschild">

View File

@ -1088,28 +1088,12 @@ let RemoteDebugger = {
if (!DebuggerServer.initialized) {
// Ask for remote connections.
DebuggerServer.init(this.prompt.bind(this));
DebuggerServer.chromeWindowType = "navigator:browser";
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js");
// Prevent tab actors to be loaded in parent process,
// unless we enable certified apps debugging
if (!Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps")) {
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/script.js");
DebuggerServer.addGlobalActor(DebuggerServer.ChromeDebuggerActor, "chromeDebugger");
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webconsole.js");
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/gcli.js");
if ("nsIProfiler" in Ci) {
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/profiler.js");
}
DebuggerServer.registerModule("devtools/server/actors/inspector");
DebuggerServer.registerModule("devtools/server/actors/styleeditor");
DebuggerServer.registerModule("devtools/server/actors/stylesheets");
DebuggerServer.registerModule("devtools/server/actors/tracer");
DebuggerServer.registerModule("devtools/server/actors/webgl");
DebuggerServer.registerModule("devtools/server/actors/memory");
}
// Add Firefox-specific actors, but prevent tab actors to be loaded in
// the parent process, unless we enable certified apps debugging.
let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
DebuggerServer.addBrowserActors("navigator:browser", restrictPrivileges);
DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webapps.js");
DebuggerServer.registerModule("devtools/server/actors/device");
#ifdef MOZ_WIDGET_GONK
DebuggerServer.onConnectionChange = function(what) {
@ -1122,8 +1106,8 @@ let RemoteDebugger = {
"/data/local/debugger-socket";
try {
DebuggerServer.openListener(path);
// Temporary event, until bug 942756 lands and offer a way to know
// when the server is up and running
// Temporary event, until bug 942756 lands and offers a way to know
// when the server is up and running.
Services.obs.notifyObservers(null, 'debugger-server-started', null);
this._running = true;
} catch (e) {

View File

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="75ea7d07cdb590722634016410e12819faf82e5a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="59e477229162926981cc00cf5d6c53f9614f17dd"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
@ -96,6 +96,6 @@
<project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
<project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="a3daa50e9b5db558696951ae724f913e4e5c7489"/>
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="83dbccadb19d0d3c07828c2bb0e5c2be12c0f980"/>
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="11663426672fcc46e2a0f29239afa736b90635ba"/>
<project name="android-sdk" path="sdk" remote="b2g" revision="5701d3cb45c2e848cc57003cda2e1141288ecae4"/>
</manifest>

View File

@ -6,12 +6,12 @@
<remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="fce1a137746dbd354bca1918f02f96d51c40bad2">
<project name="platform_build" path="build" remote="b2g" revision="317f25e0a4cb3e8e86e2b76c37a14081372f0307">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="75ea7d07cdb590722634016410e12819faf82e5a"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="59e477229162926981cc00cf5d6c53f9614f17dd"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
@ -24,6 +24,7 @@
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
<project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
<project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="1342fd7b4b000ac3e76a5dfe111a0de9d710b4c8"/>
<project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="1b26ad444462ccbd97f6319565b4735f7bd779e5"/>
<project name="device/common" path="device/common" revision="4e1a38704dcfadef60ed2da3cfeba02a56b069d2"/>
<project name="device/sample" path="device/sample" revision="b045905b46c8b4ee630d0c2aee7db63eaec722d9"/>
<project name="platform/abi/cpp" path="abi/cpp" revision="fa873799be5cf200f1d1d32a63953949c9dcdda8"/>
@ -105,7 +106,7 @@
<project name="platform/ndk" path="ndk" revision="7666b97bbaf1d645cdd6b4430a367b7a2bb53369"/>
<project name="platform/prebuilts/misc" path="prebuilts/misc" revision="f6ab40b3257abc07741188fd173ac392575cc8d2"/>
<project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="e52099755d0bd3a579130eefe8e58066cc6c0cb6"/>
<project name="platform/prebuilts/qemu-kernel" path="prebuilts/qemu-kernel" revision="42011ad6bfec9978f9a297cf4a754af67b7c478a"/>
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="11663426672fcc46e2a0f29239afa736b90635ba"/>
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
<project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
@ -118,10 +119,12 @@
<default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
<!-- Emulator specific things -->
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="9e1ff573f5669a9e0756e199cb4237ab18546388"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87e1478ffc36b0b446119ae0a1ea7a02e1baea5e"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="09485b73629856b21b2ed6073e327ab0e69a1189"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8f7c9ac889ae2c778197b4a4c0529d60530f480b"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2838a77ce4b8c09fa6a46fe25410bb3a4474cbd4"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="f995ccdb7c023b7edd8064c9d06fbea8f7108c45"/>
<project name="platform/development" path="development" revision="1f18cfe031ce23b7fb838fe3d4379dd802b49e71"/>
<project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
</manifest>

View File

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="75ea7d07cdb590722634016410e12819faf82e5a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="59e477229162926981cc00cf5d6c53f9614f17dd"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
@ -96,6 +96,6 @@
<project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
<project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="a3daa50e9b5db558696951ae724f913e4e5c7489"/>
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="83dbccadb19d0d3c07828c2bb0e5c2be12c0f980"/>
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="11663426672fcc46e2a0f29239afa736b90635ba"/>
<project name="android-sdk" path="sdk" remote="b2g" revision="5701d3cb45c2e848cc57003cda2e1141288ecae4"/>
</manifest>

View File

@ -1,4 +1,4 @@
{
"revision": "c5f8db7e1e71a9bfe0d29665d2b4cf3ae773094e",
"revision": "2f947703e8fdb71a105462394ed2a4e0e3db391e",
"repo_path": "/integration/gaia-central"
}

View File

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="75ea7d07cdb590722634016410e12819faf82e5a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="59e477229162926981cc00cf5d6c53f9614f17dd"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

View File

@ -10,7 +10,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="75ea7d07cdb590722634016410e12819faf82e5a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="59e477229162926981cc00cf5d6c53f9614f17dd"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

View File

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="75ea7d07cdb590722634016410e12819faf82e5a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="59e477229162926981cc00cf5d6c53f9614f17dd"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

View File

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="75ea7d07cdb590722634016410e12819faf82e5a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="59e477229162926981cc00cf5d6c53f9614f17dd"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

View File

@ -6,12 +6,12 @@
<remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="fce1a137746dbd354bca1918f02f96d51c40bad2">
<project name="platform_build" path="build" remote="b2g" revision="317f25e0a4cb3e8e86e2b76c37a14081372f0307">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="75ea7d07cdb590722634016410e12819faf82e5a"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="59e477229162926981cc00cf5d6c53f9614f17dd"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
@ -24,6 +24,7 @@
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
<project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
<project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="1342fd7b4b000ac3e76a5dfe111a0de9d710b4c8"/>
<project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="1b26ad444462ccbd97f6319565b4735f7bd779e5"/>
<project name="device/common" path="device/common" revision="4e1a38704dcfadef60ed2da3cfeba02a56b069d2"/>
<project name="device/sample" path="device/sample" revision="b045905b46c8b4ee630d0c2aee7db63eaec722d9"/>
<project name="platform/abi/cpp" path="abi/cpp" revision="fa873799be5cf200f1d1d32a63953949c9dcdda8"/>
@ -105,7 +106,7 @@
<project name="platform/ndk" path="ndk" revision="7666b97bbaf1d645cdd6b4430a367b7a2bb53369"/>
<project name="platform/prebuilts/misc" path="prebuilts/misc" revision="f6ab40b3257abc07741188fd173ac392575cc8d2"/>
<project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="e52099755d0bd3a579130eefe8e58066cc6c0cb6"/>
<project name="platform/prebuilts/qemu-kernel" path="prebuilts/qemu-kernel" revision="42011ad6bfec9978f9a297cf4a754af67b7c478a"/>
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="11663426672fcc46e2a0f29239afa736b90635ba"/>
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
<project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>

View File

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="75ea7d07cdb590722634016410e12819faf82e5a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="59e477229162926981cc00cf5d6c53f9614f17dd"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

View File

@ -1158,6 +1158,7 @@ pref("devtools.scratchpad.enableCodeFolding", true);
// Enable the Style Editor.
pref("devtools.styleeditor.enabled", true);
pref("devtools.styleeditor.source-maps-enabled", false);
pref("devtools.styleeditor.autocompletion-enabled", true);
// Enable the Shader Editor.
pref("devtools.shadereditor.enabled", false);

View File

@ -271,7 +271,8 @@ var gPluginHandler = {
if (eventType == "PluginRemoved") {
let doc = event.target;
let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
this._setPluginNotificationIcon(browser);
if (browser)
this._setPluginNotificationIcon(browser);
return;
}
@ -301,6 +302,8 @@ var gPluginHandler = {
let shouldShowNotification = false;
let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
if (!browser)
return;
switch (eventType) {
case "PluginCrashed":

View File

@ -283,7 +283,7 @@ toolbarpaletteitem > #personal-bookmarks > #bookmarks-toolbar-placeholder,
}
#urlbar-container {
min-width: 30ch;
min-width: 28ch;
}
#search-container {
@ -379,7 +379,7 @@ panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .a
}
#identity-icon-labels {
max-width: 18em;
max-width: 12vw;
}
#identity-icon-country-label {
@ -786,7 +786,11 @@ chatbar {
/*** Visibility of downloads indicator controls ***/
#downloads-button[indicator] > image.toolbarbutton-icon {
/* Bug 924050: If we've loaded the indicator, for now we hide it in the menu panel,
and just show the icon. This is a hack to side-step very weird layout bugs that
seem to be caused by the indicator stack interacting with the menu panel. */
#downloads-button[indicator]:not([cui-areatype="menu-panel"]) > image.toolbarbutton-icon,
#downloads-button[indicator][cui-areatype="menu-panel"] > #downloads-indicator-anchor {
display: none;
}

View File

@ -708,6 +708,7 @@
type="menu-button"
label="&bookmarksMenuButton.label;"
tooltiptext="&bookmarksMenuButton.tooltip;"
anchor="dropmarker"
ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
ondragover="PlacesMenuDNDHandler.onDragOver(event);"
ondragleave="PlacesMenuDNDHandler.onDragLeave(event);"
@ -718,7 +719,6 @@
placespopup="true"
context="placesContext"
openInTabs="children"
anonanchorclass="toolbarbutton-menubutton-dropmarker"
oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
onpopupshowing="BookmarkingUI.onPopupShowing(event);
@ -832,12 +832,11 @@
<toolbaritem id="PanelUI-button"
class="chromeclass-toolbar-additional"
removable="false"
title="&appmenu.title;">
removable="false">
<toolbarbutton id="PanelUI-menu-button"
class="toolbarbutton-1"
label="&brandShortName;"
tooltiptext="&appmenu.title;"/>
tooltiptext="&appmenu.tooltip;"/>
</toolbaritem>
<hbox id="window-controls" hidden="true" pack="end" skipintoolbarset="true"

View File

@ -5,7 +5,7 @@
<hbox id="customization-container" flex="1" hidden="true">
<vbox flex="1" id="customization-palette-container">
<label id="customization-header">
&customizeMode.menuAndToolbars.header;
&customizeMode.menuAndToolbars.header2;
</label>
<hbox id="customization-empty" hidden="true">
<label>&customizeMode.menuAndToolbars.empty;</label>

View File

@ -127,7 +127,7 @@
<label value="&feedsMenu.label;" class="panel-subview-header"/>
</panelview>
<panelview id="PanelUI-helpView" flex="1">
<panelview id="PanelUI-helpView" flex="1" class="PanelUI-subView">
<label value="&helpMenu.label;" class="panel-subview-header"/>
<vbox id="PanelUI-helpItems"/>
</panelview>

View File

@ -165,6 +165,9 @@ const PanelUI = {
handleEvent: function(aEvent) {
switch (aEvent.type) {
case "command":
this.onCommandHandler(aEvent);
break;
case "popupshowing":
// Fall through
case "popupshown":
@ -408,11 +411,11 @@ const PanelUI = {
}
items.appendChild(fragment);
this.addEventListener("command", PanelUI.onCommandHandler);
this.addEventListener("command", PanelUI);
},
_onHelpViewHide: function(aEvent) {
this.removeEventListener("command", PanelUI.onCommandHandler);
this.removeEventListener("command", PanelUI);
}
};

View File

@ -1204,6 +1204,11 @@ let CustomizableUIInternal = {
let target = aEvent.originalTarget;
let panel = this._getPanelForNode(aEvent.currentTarget);
// This can happen in e.g. customize mode. If there's no panel,
// there's clearly nothing for us to close; pretend we're interactive.
if (!panel) {
return true;
}
// We keep track of:
// whether we're in an input container (text field)
let inInput = false;

View File

@ -268,7 +268,7 @@ const CustomizableWidgets = [{
}
items.appendChild(fragment);
aEvent.target.addEventListener("command", win.PanelUI.onCommandHandler);
aEvent.target.addEventListener("command", win.PanelUI);
},
onViewHiding: function(aEvent) {
let doc = aEvent.target.ownerDocument;
@ -284,8 +284,7 @@ const CustomizableWidgets = [{
}
parent.appendChild(items);
aEvent.target.removeEventListener("command",
win.PanelUI.onCommandHandler);
aEvent.target.removeEventListener("command", win.PanelUI);
}
}, {
id: "add-ons-button",

View File

@ -1411,10 +1411,13 @@ CustomizeMode.prototype = {
_setGridDragActive: function(aDragOverNode, aDraggedItem, aValue) {
let targetArea = this._getCustomizableParent(aDragOverNode);
let draggedWrapper = this.document.getElementById("wrapper-" + aDraggedItem.id);
let originArea = this._getCustomizableParent(draggedWrapper);
let positionManager = DragPositionManager.getManagerForArea(targetArea);
let draggedSize = this._getDragItemSize(aDragOverNode, aDraggedItem);
let isWide = aDraggedItem.classList.contains(CustomizableUI.WIDE_PANEL_CLASS);
positionManager.insertPlaceholder(targetArea, aDragOverNode, isWide, draggedSize);
positionManager.insertPlaceholder(targetArea, aDragOverNode, isWide, draggedSize,
originArea == targetArea);
},
_getDragItemSize: function(aDragOverNode, aDraggedItem) {

View File

@ -126,7 +126,7 @@ AreaPositionManager.prototype = {
* they would have if we had inserted something before aBefore. We use CSS
* transforms for this, which are CSS transitioned.
*/
insertPlaceholder: function(aContainer, aBefore, aWide, aSize) {
insertPlaceholder: function(aContainer, aBefore, aWide, aSize, aIsFromThisArea) {
let isShifted = false;
let shiftDown = aWide;
for (let child of aContainer.children) {
@ -157,7 +157,7 @@ AreaPositionManager.prototype = {
if (this.__moveDown) {
shiftDown = true;
}
if (!this._lastPlaceholderInsertion) {
if (aIsFromThisArea && !this._lastPlaceholderInsertion) {
child.setAttribute("notransition", "true");
}
// Determine the CSS transform based on the next node:
@ -167,7 +167,8 @@ AreaPositionManager.prototype = {
child.style.transform = "";
}
}
if (aContainer.lastChild && !this._lastPlaceholderInsertion) {
if (aContainer.lastChild && aIsFromThisArea &&
!this._lastPlaceholderInsertion) {
// Flush layout:
aContainer.lastChild.getBoundingClientRect();
// then remove all the [notransition]

View File

@ -490,7 +490,7 @@
<binding id="places-popup-arrow"
extends="chrome://browser/content/places/menu.xml#places-popup-base">
<content flip="both" side="top" position="bottomcenter topleft">
<xul:box anonid="container" class="panel-arrowcontainer" flex="1"
<xul:vbox anonid="container" class="panel-arrowcontainer" flex="1"
xbl:inherits="side,panelopen">
<xul:box anonid="arrowbox" class="panel-arrowbox">
<xul:image anonid="arrow" class="panel-arrow" xbl:inherits="side"/>
@ -504,7 +504,7 @@
<children/>
</xul:arrowscrollbox>
</xul:box>
</xul:box>
</xul:vbox>
</content>
<implementation>
@ -521,20 +521,19 @@
var container = document.getAnonymousElementByAttribute(this, "anonid", "container");
var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");
// if this panel has a "sliding" arrow, we may have previously set margins...
arrowbox.style.removeProperty("margin");
var position = this.alignmentPosition;
var offset = this.alignmentOffset;
// if this panel has a "sliding" arrow, we may have previously set margins...
arrowbox.style.removeProperty("transform");
if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
container.orient = "";
container.orient = "horizontal";
arrowbox.orient = "vertical";
if (position.indexOf("_after") > 0) {
arrowbox.pack = "end";
arrowbox.style.marginBottom = this.alignmentOffset + "px";
} else {
arrowbox.pack = "start";
arrowbox.style.marginTop = this.alignmentOffset + "px";
}
arrowbox.style.transform = "translate(0, " + -offset + "px)";
// The assigned side stays the same regardless of direction.
var isRTL = (window.getComputedStyle(this).direction == "rtl");
@ -549,15 +548,14 @@
}
}
else if (position.indexOf("before_") == 0 || position.indexOf("after_") == 0) {
container.orient = "vertical";
container.orient = "";
arrowbox.orient = "";
if (position.indexOf("_end") > 0) {
arrowbox.pack = "end";
arrowbox.style.marginRight = this.alignmentOffset + "px";
} else {
arrowbox.pack = "start";
arrowbox.style.marginLeft = this.alignmentOffset + "px";
}
arrowbox.style.transform = "translate(" + -offset + "px, 0)";
if (position.indexOf("before_") == 0) {
container.dir = "reverse";
@ -568,6 +566,7 @@
this.setAttribute("side", "top");
}
}
arrow.hidden = false;
]]></body>
</method>
@ -579,23 +578,6 @@
]]></handler>
<handler event="popupshown" phase="target"><![CDATA[
this.setAttribute("panelopen", "true");
// Allow anchoring to a specified element inside the anchor.
var anchorClass = this.getAttribute("anonanchorclass");
if (anchorClass && this.anchorNode) {
let anchor =
document.getAnonymousElementByAttribute(this.anchorNode, "class",
anchorClass);
if (anchor) {
let offsetX = anchor.boxObject.width / 2;
if (this.alignmentPosition.endsWith("_end"))
offsetX *= -1;
this.popupBoxObject.moveToAnchor(anchor, this.alignmentPosition,
offsetX, 0,
false);
this.adjustArrowPosition();
}
}
]]></handler>
<handler event="popuphidden" phase="target"><![CDATA[
this.removeAttribute("panelopen");

View File

@ -242,7 +242,7 @@ let SessionSaverInternal = {
* Write the given state object to disk.
*/
_writeState: function (state) {
stopWatchStart("SERIALIZE_DATA_MS", "SERIALIZE_DATA_LONGEST_OP_MS");
stopWatchStart("SERIALIZE_DATA_MS", "SERIALIZE_DATA_LONGEST_OP_MS", "WRITE_STATE_LONGEST_OP_MS");
let data = JSON.stringify(state);
stopWatchFinish("SERIALIZE_DATA_MS", "SERIALIZE_DATA_LONGEST_OP_MS");
@ -251,6 +251,7 @@ let SessionSaverInternal = {
// Don't touch the file if an observer has deleted all state data.
if (!data) {
stopWatchCancel("WRITE_STATE_LONGEST_OP_MS");
return Promise.resolve();
}
@ -263,10 +264,16 @@ let SessionSaverInternal = {
// Write (atomically) to a session file, using a tmp file. Once the session
// file is successfully updated, save the time stamp of the last save and
// notify the observers.
return SessionFile.write(data).then(() => {
stopWatchStart("SEND_SERIALIZED_STATE_LONGEST_OP_MS");
let promise = SessionFile.write(data);
stopWatchFinish("WRITE_STATE_LONGEST_OP_MS",
"SEND_SERIALIZED_STATE_LONGEST_OP_MS");
promise = promise.then(() => {
this.updateLastSaveTime();
notify(null, "sessionstore-state-write-complete");
}, console.error);
return promise;
},
/**

View File

@ -64,6 +64,7 @@ MOZ_PAY=1
# Enable activities. These are used for FxOS developers currently.
MOZ_ACTIVITIES=1
MOZ_JSDOWNLOADS=1
MOZ_WEBM_ENCODER=1
# Enable exact rooting on desktop.
JSGC_USE_EXACT_ROOTING=1

View File

@ -1071,11 +1071,13 @@ FilterView.prototype = {
if (aText) {
this._searchbox.value = aOperator + aText;
return;
}
else if (DebuggerView.editor.somethingSelected()) {
if (DebuggerView.editor.somethingSelected()) {
this._searchbox.value = aOperator + DebuggerView.editor.getSelection();
return;
}
else {
if (SEARCH_AUTOFILL.indexOf(aOperator) != -1) {
let cursor = DebuggerView.editor.getCursor();
let content = DebuggerView.editor.getText();
let location = DebuggerView.Sources.selectedValue;
@ -1084,8 +1086,12 @@ FilterView.prototype = {
if (identifier && identifier.name) {
this._searchbox.value = aOperator + identifier.name;
this._searchbox.select();
this._searchbox.selectionStart += aOperator.length;
return;
}
}
this._searchbox.value = aOperator;
},
/**
@ -1385,7 +1391,7 @@ FilteredFunctionsView.prototype = Heritage.extend(ResultsPanelContainer.prototyp
// cache the syntax tree nodes generated by the reflection API.
// Make sure the currently displayed source is parsed first. Once the
// maximum allowed number of resutls are found, parsing will be halted.
// maximum allowed number of results are found, parsing will be halted.
let currentUrl = DebuggerView.Sources.selectedValue;
let currentSource = aSources.filter(([sourceUrl]) => sourceUrl == currentUrl)[0];
aSources.splice(aSources.indexOf(currentSource), 1);
@ -1402,7 +1408,7 @@ FilteredFunctionsView.prototype = Heritage.extend(ResultsPanelContainer.prototyp
let sourceResults = parsedSource.getNamedFunctionDefinitions(aToken);
for (let scriptResult of sourceResults) {
for (let parseResult of scriptResult.parseResults) {
for (let parseResult of scriptResult) {
aStore.push({
sourceUrl: scriptResult.sourceUrl,
scriptOffset: scriptResult.scriptOffset,

View File

@ -26,6 +26,7 @@ const SEARCH_FUNCTION_FLAG = "@";
const SEARCH_TOKEN_FLAG = "#";
const SEARCH_LINE_FLAG = ":";
const SEARCH_VARIABLE_FLAG = "*";
const SEARCH_AUTOFILL = [SEARCH_GLOBAL_FLAG, SEARCH_FUNCTION_FLAG, SEARCH_TOKEN_FLAG];
const EDITOR_VARIABLE_HOVER_DELAY = 350; // ms
const EDITOR_VARIABLE_POPUP_POSITION = "topcenter bottomleft";
const TOOLBAR_ORDER_POPUP_POSITION = "topcenter bottomleft";

View File

@ -165,6 +165,7 @@ support-files =
[browser_dbg_scripts-switching-01.js]
[browser_dbg_scripts-switching-02.js]
[browser_dbg_scripts-switching-03.js]
[browser_dbg_search-autofill-identifier.js]
[browser_dbg_search-basic-01.js]
[browser_dbg_search-basic-02.js]
[browser_dbg_search-basic-03.js]
@ -247,7 +248,6 @@ support-files =
[browser_dbg_variables-view-webidl.js]
[browser_dbg_watch-expressions-01.js]
[browser_dbg_watch-expressions-02.js]
[browser_dbg_search-function.js]
[browser_dbg_chrome-create.js]
skip-if = os == "linux" # Bug 847558
[browser_dbg_on-pause-raise.js]

View File

@ -0,0 +1,135 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that Debugger Search uses the identifier under cursor if nothing is
* selected or manually passed and searching using certain operators.
*/
"use strict";
function test() {
const TAB_URL = EXAMPLE_URL + "doc_function-search.html";
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
let Source = 'code_function-search-01.js';
let Debugger = aPanel.panelWin;
let Editor = Debugger.DebuggerView.editor;
let Filtering = Debugger.DebuggerView.Filtering;
function doSearch(aOperator) {
Editor.dropSelection();
Filtering._doSearch(aOperator);
}
waitForSourceShown(aPanel, Source).then(() => {
info("Testing with cursor at the beginning of the file...");
doSearch();
is(Filtering._searchbox.value, "",
"The searchbox value should not be auto-filled when searching for files.");
is(Filtering._searchbox.selectionStart, Filtering._searchbox.selectionEnd,
"The searchbox contents should not be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
doSearch("!");
is(Filtering._searchbox.value, "!",
"The searchbox value should not be auto-filled when searching across all files.");
is(Filtering._searchbox.selectionStart, Filtering._searchbox.selectionEnd,
"The searchbox contents should not be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
doSearch("@");
is(Filtering._searchbox.value, "@",
"The searchbox value should not be auto-filled when searching for functions.");
is(Filtering._searchbox.selectionStart, Filtering._searchbox.selectionEnd,
"The searchbox contents should not be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
doSearch("#");
is(Filtering._searchbox.value, "#",
"The searchbox value should not be auto-filled when searching inside a file.");
is(Filtering._searchbox.selectionStart, Filtering._searchbox.selectionEnd,
"The searchbox contents should not be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
doSearch(":");
is(Filtering._searchbox.value, ":",
"The searchbox value should not be auto-filled when searching for a line.");
is(Filtering._searchbox.selectionStart, Filtering._searchbox.selectionEnd,
"The searchbox contents should not be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
doSearch("*");
is(Filtering._searchbox.value, "*",
"The searchbox value should not be auto-filled when searching for variables.");
is(Filtering._searchbox.selectionStart, Filtering._searchbox.selectionEnd,
"The searchbox contents should not be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
Editor.setCursor({ line: 7, ch: 0});
info("Testing with cursor at line 8 and char 1...");
doSearch();
is(Filtering._searchbox.value, "",
"The searchbox value should not be auto-filled when searching for files.");
is(Filtering._searchbox.selectionStart, Filtering._searchbox.selectionEnd,
"The searchbox contents should not be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
doSearch("!");
is(Filtering._searchbox.value, "!test",
"The searchbox value was incorrect when searching across all files.");
is(Filtering._searchbox.selectionStart, 1,
"The searchbox operator should not be selected");
is(Filtering._searchbox.selectionEnd, 5,
"The searchbox contents should be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
doSearch("@");
is(Filtering._searchbox.value, "@test",
"The searchbox value was incorrect when searching for functions.");
is(Filtering._searchbox.selectionStart, 1,
"The searchbox operator should not be selected");
is(Filtering._searchbox.selectionEnd, 5,
"The searchbox contents should be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
doSearch("#");
is(Filtering._searchbox.value, "#test",
"The searchbox value should be auto-filled when searching inside a file.");
is(Filtering._searchbox.selectionStart, 1,
"The searchbox operator should not be selected");
is(Filtering._searchbox.selectionEnd, 5,
"The searchbox contents should be selected");
is(Editor.getSelection(), "test",
"The selection in the editor should be 'test'.");
doSearch(":");
is(Filtering._searchbox.value, ":",
"The searchbox value should not be auto-filled when searching for a line.");
is(Filtering._searchbox.selectionStart, Filtering._searchbox.selectionEnd,
"The searchbox contents should not be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
doSearch("*");
is(Filtering._searchbox.value, "*",
"The searchbox value should not be auto-filled when searching for variables.");
is(Filtering._searchbox.selectionStart, Filtering._searchbox.selectionEnd,
"The searchbox contents should not be selected");
is(Editor.getSelection(), "",
"The selection in the editor should be empty.");
closeDebuggerAndFinish(aPanel);
});
});
};

View File

@ -1,28 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that Debugger Search uses the identifier under cursor
* if nothing is selected or manually passed
*/
"use strict";
function test() {
const TAB_URL = EXAMPLE_URL + "doc_function-search.html";
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
let Source = 'code_function-search-01.js';
let Debugger = aPanel.panelWin;
let Editor = Debugger.DebuggerView.editor;
let Filtering = Debugger.DebuggerView.Filtering;
waitForSourceShown(aPanel, Source).then(() => {
Editor.setCursor({ line: 7, ch: 0});
Filtering._doSearch("@");
is(Filtering._searchbox.value, "@test", "Searchbox value should be set to the identifier under cursor if no aText or selection provided");
closeDebuggerAndFinish(aPanel);
});
});
};

View File

@ -32,6 +32,8 @@ this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun) {
this._runCallback = aOnRun;
this._telemetry = new Telemetry();
this.close = this.close.bind(this);
Services.obs.addObserver(this.close, "quit-application", false);
this._initServer();
this._initProfile();
this._create();
@ -154,19 +156,27 @@ BrowserToolboxProcess.prototype = {
},
/**
* Closes the remote debugger, removing the profile and killing the process.
* Closes the remote debugging server and kills the toolbox process.
*/
close: function() {
if (this.closed) {
return;
}
dumpn("Cleaning up the chrome debugging process.");
Services.obs.removeObserver(this.close, "quit-application");
if (this._dbgProcess.isRunning) {
this._dbgProcess.kill();
}
this._telemetry.toolClosed("jsbrowserdebugger");
this.debuggerServer.destroy();
if (this.debuggerServer) {
this.debuggerServer.destroy();
}
dumpn("Chrome toolbox is now closed...");
this.closed = true;
if (typeof this._closeCallback == "function") {
this._closeCallback.call({}, this);
}

View File

@ -7,7 +7,6 @@
}
#options-panel {
overflow-y: auto;
display: block;
}
@ -23,7 +22,7 @@
.options-vertical-pane {
margin: 5px;
width: calc(50% - 30px);
min-width: 400px;
min-width: 350px;
-moz-padding-start: 5px;
}

View File

@ -66,6 +66,9 @@
<checkbox label="&options.stylesheetSourceMaps.label;"
tooltiptext="&options.stylesheetSourceMaps.tooltip;"
data-pref="devtools.styleeditor.source-maps-enabled"/>
<checkbox label="&options.stylesheetAutocompletion.label;"
tooltiptext="&options.stylesheetAutocompletion.tooltip;"
data-pref="devtools.styleeditor.autocompletion-enabled"/>
</vbox>
<label value="&options.profiler.label;"/>
<vbox id="profiler-options" class="options-groupbox">

View File

@ -250,26 +250,23 @@ SelectorSearch.prototype = {
if (this.searchPopup.isOpen) {
this.searchPopup.hidePopup();
}
}
else {
this.showSuggestions();
}
this.searchBox.classList.remove("devtools-no-search-result");
this.searchBox.classList.remove("devtools-no-search-result");
return this._selectResult(0);
}
else {
if (query.match(/[\s>+]$/)) {
this._lastValidSearch = query + "*";
return this._selectResult(0);
}
else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
let lastPart = query.match(/[\s+>][\.#a-zA-Z][^>\s+]*$/)[0];
this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
}
this.searchBox.classList.add("devtools-no-search-result");
this.showSuggestions();
return this._selectResult(0).then(() => {
this.searchBox.classList.remove("devtools-no-search-result");
}).then( () => this.showSuggestions());
}
return undefined;
if (query.match(/[\s>+]$/)) {
this._lastValidSearch = query + "*";
}
else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
let lastPart = query.match(/[\s+>][\.#a-zA-Z][^>\s+]*$/)[0];
this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
}
this.searchBox.classList.add("devtools-no-search-result");
return this.showSuggestions();
});
},
@ -414,13 +411,6 @@ SelectorSearch.prototype = {
* Populates the suggestions list and show the suggestion popup.
*/
_showPopup: function SelectorSearch__showPopup(aList, aFirstPart) {
// Sort alphabetically in increaseing order.
aList = aList.sort();
// Sort based on count= in decreasing order.
aList = aList.sort(function([a1,a2], [b1,b2]) {
return a2 < b2;
});
let total = 0;
let query = this.searchBox.value;
let toLowerCase = false;
@ -471,111 +461,44 @@ SelectorSearch.prototype = {
* searchbox.
*/
showSuggestions: function SelectorSearch_showSuggestions() {
if (!this.walker.isLocal()) {
return;
}
let query = this.searchBox.value;
if (this._lastValidSearch != "" &&
this._lastToLastValidSearch != this._lastValidSearch) {
this._searchSuggestions = {
ids: new Map(),
classes: new Map(),
tags: new Map(),
};
let nodes = [];
try {
nodes = this.doc.querySelectorAll(this._lastValidSearch);
} catch (ex) {}
for (let node of nodes) {
this._searchSuggestions.ids.set(node.id, 1);
this._searchSuggestions.tags
.set(node.tagName,
(this._searchSuggestions.tags.get(node.tagName) || 0) + 1);
for (let className of node.classList) {
this._searchSuggestions.classes
.set(className,
(this._searchSuggestions.classes.get(className) || 0) + 1);
}
}
this._lastToLastValidSearch = this._lastValidSearch;
}
else if (this._lastToLastValidSearch != this._lastValidSearch) {
this._searchSuggestions = {
ids: new Map(),
classes: new Map(),
tags: new Map(),
};
if (query.length == 0) {
return;
}
let nodes = null;
if (this.state == this.States.CLASS) {
nodes = this.doc.querySelectorAll("[class]");
for (let node of nodes) {
for (let className of node.classList) {
this._searchSuggestions.classes
.set(className,
(this._searchSuggestions.classes.get(className) || 0) + 1);
}
}
}
else if (this.state == this.States.ID) {
nodes = this.doc.querySelectorAll("[id]");
for (let node of nodes) {
this._searchSuggestions.ids.set(node.id, 1);
}
}
else if (this.state == this.States.TAG) {
nodes = this.doc.getElementsByTagName("*");
for (let node of nodes) {
this._searchSuggestions.tags
.set(node.tagName,
(this._searchSuggestions.tags.get(node.tagName) || 0) + 1);
}
}
else {
return;
}
this._lastToLastValidSearch = this._lastValidSearch;
}
// Filter the suggestions based on search box value.
let result = [];
let firstPart = "";
if (this.state == this.States.TAG) {
// gets the tag that is being completed. For ex. 'div.foo > s' returns 's',
// 'di' returns 'di' and likewise.
firstPart = (query.match(/[\s>+]?([a-zA-Z]*)$/) || ["",query])[1];
for (let [tag, count] of this._searchSuggestions.tags) {
if (tag.toLowerCase().startsWith(firstPart.toLowerCase())) {
result.push([tag, count]);
}
}
firstPart = (query.match(/[\s>+]?([a-zA-Z]*)$/) || ["", query])[1];
query = query.slice(0, query.length - firstPart.length);
}
else if (this.state == this.States.CLASS) {
// gets the class that is being completed. For ex. '.foo.b' returns 'b'
firstPart = query.match(/\.([^\.]*)$/)[1];
for (let [className, count] of this._searchSuggestions.classes) {
if (className.startsWith(firstPart)) {
result.push(["." + className, count]);
}
}
firstPart = "." + firstPart;
query = query.slice(0, query.length - firstPart.length - 1);
}
else if (this.state == this.States.ID) {
// gets the id that is being completed. For ex. '.foo#b' returns 'b'
firstPart = query.match(/#([^#]*)$/)[1];
for (let [id, count] of this._searchSuggestions.ids) {
if (id.startsWith(firstPart)) {
result.push(["#" + id, 1]);
}
}
firstPart = "#" + firstPart;
query = query.slice(0, query.length - firstPart.length - 1);
}
this._showPopup(result, firstPart);
// TODO: implement some caching so that over the wire request is not made
// everytime.
if (/[\s+>~]$/.test(query)) {
query += "*";
}
this._currentSuggesting = query;
return this.walker.getSuggestionsForQuery(query, firstPart, this.state).then(result => {
if (this._currentSuggesting != result.query) {
// This means that this response is for a previous request and the user
// as since typed something extra leading to a new request.
return;
}
this._lastToLastValidSearch = this._lastValidSearch;
if (this.state == this.States.CLASS) {
firstPart = "." + firstPart;
}
else if (this.state == this.States.ID) {
firstPart = "#" + firstPart;
}
this._showPopup(result.suggestions, firstPart);
});
},
};

View File

@ -68,7 +68,8 @@ function test()
let outlineRect = getHighlighterOutlineRect();
let iframeRect = iframeNode.getBoundingClientRect();
for (let dim of ["width", "height", "top", "left"]) {
is(Math.floor(outlineRect[dim]), Math.floor(iframeRect[dim]), "Outline dimension is correct");
is(Math.floor(outlineRect[dim]), Math.floor(iframeRect[dim]),
"Outline dimension is correct " + outlineRect[dim]);
}
iframeNode.style.marginBottom = doc.defaultView.innerHeight + "px";
@ -101,8 +102,8 @@ function test()
function moveMouseOver(aElement, x, y, cb)
{
inspector.toolbox.once("picker-node-hovered", cb);
EventUtils.synthesizeMouse(aElement, x, y, {type: "mousemove"},
aElement.ownerDocument.defaultView);
inspector.toolbox.once("picker-node-hovered", cb);
}
}

View File

@ -89,8 +89,7 @@ function test()
inspector.searchSuggestions._lastQuery.then(() => {
let [key, suggestions] = keyStates[state];
let actualSuggestions = popup.getItems();
is(popup._panel.state == "open" || popup._panel.state == "showing"
? actualSuggestions.length: 0, suggestions.length,
is(popup.isOpen ? actualSuggestions.length: 0, suggestions.length,
"There are expected number of suggestions at " + state + "th step.");
actualSuggestions = actualSuggestions.reverse();
for (let i = 0; i < suggestions.length; i++) {

View File

@ -90,8 +90,7 @@ function test()
inspector.searchSuggestions._lastQuery.then(() => {
let [key, suggestions] = keyStates[state];
let actualSuggestions = popup.getItems();
is(popup._panel.state == "open" || popup._panel.state == "showing"
? actualSuggestions.length: 0, suggestions.length,
is(popup.isOpen ? actualSuggestions.length: 0, suggestions.length,
"There are expected number of suggestions at " + state + "th step.");
actualSuggestions = actualSuggestions.reverse();
for (let i = 0; i < suggestions.length; i++) {

View File

@ -31,7 +31,6 @@ function createDocument() {
div2 = iframe2.contentDocument.createElement('div');
div2.textContent = 'nested div';
iframe2.contentDocument.body.appendChild(div2);
// Open the inspector, start the picker mode, and start the tests
openInspector(aInspector => {
inspector = aInspector;
@ -48,11 +47,11 @@ function createDocument() {
}
function moveMouseOver(aElement, cb) {
EventUtils.synthesizeMouse(aElement, 2, 2, {type: "mousemove"},
aElement.ownerDocument.defaultView);
inspector.toolbox.once("picker-node-hovered", () => {
executeSoon(cb);
});
EventUtils.synthesizeMouseAtCenter(aElement, {type: "mousemove"},
aElement.ownerDocument.defaultView);
}
function runTests() {

View File

@ -60,7 +60,7 @@ Tools.options = {
id: "options",
ordinal: 0,
url: "chrome://browser/content/devtools/framework/toolbox-options.xul",
icon: "chrome://browser/skin/devtools/tool-options@2x.png",
icon: "chrome://browser/skin/devtools/tool-options.svg",
bgTheme: "theme-body",
tooltip: l10n("optionsButton.tooltip", toolboxStrings),
inMenu: false,
@ -79,7 +79,7 @@ Tools.webConsole = {
accesskey: l10n("webConsoleCmd.accesskey", webConsoleStrings),
modifiers: Services.appinfo.OS == "Darwin" ? "accel,alt" : "accel,shift",
ordinal: 1,
icon: "chrome://browser/skin/devtools/tool-webconsole@2x.png",
icon: "chrome://browser/skin/devtools/tool-webconsole.svg",
url: "chrome://browser/content/devtools/webconsole.xul",
label: l10n("ToolboxTabWebconsole.label", webConsoleStrings),
menuLabel: l10n("MenuWebconsole.label", webConsoleStrings),
@ -101,7 +101,7 @@ Tools.inspector = {
key: l10n("inspector.commandkey", inspectorStrings),
ordinal: 2,
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
icon: "chrome://browser/skin/devtools/tool-inspector@2x.png",
icon: "chrome://browser/skin/devtools/tool-inspector.svg",
url: "chrome://browser/content/devtools/inspector/inspector.xul",
label: l10n("inspector.label", inspectorStrings),
tooltip: l10n("inspector.tooltip", inspectorStrings),
@ -128,8 +128,8 @@ Tools.jsdebugger = {
accesskey: l10n("debuggerMenu.accesskey", debuggerStrings),
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
ordinal: 3,
icon: "chrome://browser/skin/devtools/tool-debugger@2x.png",
highlightedicon: "chrome://browser/skin/devtools/tool-debugger-paused@2x.png",
icon: "chrome://browser/skin/devtools/tool-debugger.svg",
highlightedicon: "chrome://browser/skin/devtools/tool-debugger-paused.svg",
url: "chrome://browser/content/devtools/debugger.xul",
label: l10n("ToolboxDebugger.label", debuggerStrings),
tooltip: l10n("ToolboxDebugger.tooltip", debuggerStrings),
@ -151,7 +151,7 @@ Tools.styleEditor = {
ordinal: 4,
accesskey: l10n("open.accesskey", styleEditorStrings),
modifiers: "shift",
icon: "chrome://browser/skin/devtools/tool-styleeditor@2x.png",
icon: "chrome://browser/skin/devtools/tool-styleeditor.svg",
url: "chrome://browser/content/devtools/styleeditor.xul",
label: l10n("ToolboxStyleEditor.label", styleEditorStrings),
tooltip: l10n("ToolboxStyleEditor.tooltip2", styleEditorStrings),
@ -171,7 +171,7 @@ Tools.shaderEditor = {
id: "shadereditor",
ordinal: 5,
visibilityswitch: "devtools.shadereditor.enabled",
icon: "chrome://browser/skin/devtools/tool-styleeditor@2x.png",
icon: "chrome://browser/skin/devtools/tool-styleeditor.svg",
url: "chrome://browser/content/devtools/shadereditor.xul",
label: l10n("ToolboxShaderEditor.label", shaderEditorStrings),
tooltip: l10n("ToolboxShaderEditor.tooltip", shaderEditorStrings),
@ -193,7 +193,7 @@ Tools.jsprofiler = {
ordinal: 6,
modifiers: "shift",
visibilityswitch: "devtools.profiler.enabled",
icon: "chrome://browser/skin/devtools/tool-profiler@2x.png",
icon: "chrome://browser/skin/devtools/tool-profiler.svg",
url: "chrome://browser/content/devtools/profiler.xul",
label: l10n("profiler.label", profilerStrings),
tooltip: l10n("profiler.tooltip2", profilerStrings),
@ -216,7 +216,7 @@ Tools.netMonitor = {
ordinal: 7,
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
visibilityswitch: "devtools.netmonitor.enabled",
icon: "chrome://browser/skin/devtools/tool-network@2x.png",
icon: "chrome://browser/skin/devtools/tool-network.svg",
url: "chrome://browser/content/devtools/netmonitor.xul",
label: l10n("netmonitor.label", netMonitorStrings),
tooltip: l10n("netmonitor.tooltip", netMonitorStrings),
@ -236,7 +236,7 @@ Tools.scratchpad = {
id: "scratchpad",
ordinal: 8,
visibilityswitch: "devtools.scratchpad.enabled",
icon: "chrome://browser/skin/devtools/tool-scratchpad@2x.png",
icon: "chrome://browser/skin/devtools/tool-scratchpad.svg",
url: "chrome://browser/content/devtools/scratchpad.xul",
label: l10n("scratchpad.label", scratchpadStrings),
tooltip: l10n("scratchpad.tooltip", scratchpadStrings),

View File

@ -130,9 +130,7 @@ function test() {
is(editor.input.selectionEnd, selEnd,
"Selection is ending at the right location for state " + state);
if (popupOpen) {
ok(editor.popup._panel.state == "open" ||
editor.popup._panel.state == "showing",
"Popup is open for state " + state);
ok(editor.popup.isOpen, "Popup is open for state " + state);
}
else {
ok(editor.popup._panel.state != "open" &&

View File

@ -71,6 +71,7 @@ let NetMonitorView = {
this.Toolbar.initialize();
this.RequestsMenu.initialize();
this.NetworkDetails.initialize();
this.CustomRequest.initialize();
},
/**
@ -80,6 +81,7 @@ let NetMonitorView = {
this.Toolbar.destroy();
this.RequestsMenu.destroy();
this.NetworkDetails.destroy();
this.CustomRequest.destroy();
this._destroyPanes();
},
@ -271,6 +273,30 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this.widget.addEventListener("select", this._onSelect, false);
this._splitter.addEventListener("mousemove", this._onResize, false);
window.addEventListener("resize", this._onResize, false);
this.requestsMenuSortEvent = getKeyWithEvent(this.sortBy.bind(this));
this.requestsMenuFilterEvent = getKeyWithEvent(this.filterOn.bind(this));
this.clearEvent = this.clear.bind(this);
this._onContextShowing = this._onContextShowing.bind(this);
this._onContextNewTabCommand = this.openRequestInTab.bind(this);
this._onContextCopyUrlCommand = this.copyUrl.bind(this);
this._onContextResendCommand = this.cloneSelectedRequest.bind(this);
this.sendCustomRequestEvent = this.sendCustomRequest.bind(this);
this.closeCustomRequestEvent = this.closeCustomRequest.bind(this);
this.cloneSelectedRequestEvent = this.cloneSelectedRequest.bind(this);
$("#toolbar-labels").addEventListener("click", this.requestsMenuSortEvent, false);
$("#requests-menu-footer").addEventListener("click", this.requestsMenuFilterEvent, false);
$("#requests-menu-clear-button").addEventListener("click", this.clearEvent, false);
$("#network-request-popup").addEventListener("popupshowing", this._onContextShowing, false);
$("#request-menu-context-newtab").addEventListener("command", this._onContextNewTabCommand, false);
$("#request-menu-context-copy-url").addEventListener("command", this._onContextCopyUrlCommand, false);
$("#request-menu-context-resend").addEventListener("command", this._onContextResendCommand, false);
$("#custom-request-send-button").addEventListener("click", this.sendCustomRequestEvent, false);
$("#custom-request-close-button").addEventListener("click", this.closeCustomRequestEvent, false);
$("#headers-summary-resend").addEventListener("click", this.cloneSelectedRequestEvent, false);
},
/**
@ -282,6 +308,18 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this.widget.removeEventListener("select", this._onSelect, false);
this._splitter.removeEventListener("mousemove", this._onResize, false);
window.removeEventListener("resize", this._onResize, false);
$("#toolbar-labels").removeEventListener("click", this.requestsMenuSortEvent, false);
$("#requests-menu-footer").removeEventListener("click", this.requestsMenuFilterEvent, false);
$("#requests-menu-clear-button").removeEventListener("click", this.clearEvent, false);
$("#network-request-popup").removeEventListener("popupshowing", this._onContextShowing, false);
$("#request-menu-context-newtab").removeEventListener("command", this._onContextNewTabCommand, false);
$("#request-menu-context-copy-url").removeEventListener("command", this._onContextCopyUrlCommand, false);
$("#request-menu-context-resend").removeEventListener("command", this._onContextResendCommand, false);
$("#custom-request-send-button").removeEventListener("click", this.sendCustomRequestEvent, false);
$("#custom-request-close-button").removeEventListener("click", this.closeCustomRequestEvent, false);
$("#headers-summary-resend").removeEventListener("click", this.cloneSelectedRequestEvent, false);
},
/**
@ -1435,6 +1473,26 @@ function CustomRequestView() {
}
CustomRequestView.prototype = {
/**
* Initialization function, called when the network monitor is started.
*/
initialize: function() {
dumpn("Initializing the CustomRequestView");
this.updateCustomRequestEvent = getKeyWithEvent(this.onUpdate.bind(this));
$("#custom-pane").addEventListener("input", this.updateCustomRequestEvent, false);
},
/**
* Destruction function, called when the network monitor is closed.
*/
destroy: function() {
dumpn("Destroying the CustomRequestView");
$("#custom-pane").removeEventListener("input", this.updateCustomRequestEvent, false);
},
/**
* Populates this view with the specified data.
*
@ -2237,6 +2295,23 @@ function writeQueryString(aParams) {
return [(name + "=" + value) for ({name, value} of aParams)].join("&");
}
/**
* Helper method to get a wrapped function which can be bound to as an event listener directly and is executed only when data-key is present in event.target.
*
* @param function callback
* Function to execute execute when data-key is present in event.target.
* @return function
* Wrapped function with the target data-key as the first argument.
*/
function getKeyWithEvent(callback) {
return function(event) {
var key = event.target.getAttribute("data-key");
if (key) {
callback.call(null, key);
}
};
}
/**
* Preliminary setup for the NetMonitorView object.
*/

View File

@ -21,20 +21,16 @@
<script type="text/javascript" src="netmonitor-view.js"/>
<popupset id="networkPopupSet">
<menupopup id="network-request-popup"
onpopupshowing="NetMonitorView.RequestsMenu._onContextShowing(event);">
<menupopup id="network-request-popup">
<menuitem id="request-menu-context-newtab"
label="&netmonitorUI.context.newTab;"
accesskey="&netmonitorUI.context.newTab.accesskey;"
oncommand="NetMonitorView.RequestsMenu.openRequestInTab();"/>
accesskey="&netmonitorUI.context.newTab.accesskey;"/>
<menuitem id="request-menu-context-copy-url"
label="&netmonitorUI.context.copyUrl;"
accesskey="&netmonitorUI.context.copyUrl.accesskey;"
oncommand="NetMonitorView.RequestsMenu.copyUrl();"/>
accesskey="&netmonitorUI.context.copyUrl.accesskey;"/>
<menuitem id="request-menu-context-resend"
label="&netmonitorUI.summary.editAndResend;"
accesskey="&netmonitorUI.summary.editAndResend.accesskey;"
oncommand="NetMonitorView.RequestsMenu.cloneSelectedRequest();"/>
accesskey="&netmonitorUI.summary.editAndResend.accesskey;"/>
</menupopup>
</popupset>
@ -51,12 +47,12 @@
align="center">
<button id="requests-menu-status-button"
class="requests-menu-header-button requests-menu-status"
onclick="NetMonitorView.RequestsMenu.sortBy('status')"
data-key="status"
label="&netmonitorUI.toolbar.status2;">
</button>
<button id="requests-menu-method-button"
class="requests-menu-header-button requests-menu-method"
onclick="NetMonitorView.RequestsMenu.sortBy('method')"
data-key="method"
label="&netmonitorUI.toolbar.method;"
flex="1">
</button>
@ -66,7 +62,7 @@
align="center">
<button id="requests-menu-file-button"
class="requests-menu-header-button requests-menu-file"
onclick="NetMonitorView.RequestsMenu.sortBy('file')"
data-key="file"
label="&netmonitorUI.toolbar.file;"
flex="1">
</button>
@ -76,7 +72,7 @@
align="center">
<button id="requests-menu-domain-button"
class="requests-menu-header-button requests-menu-domain"
onclick="NetMonitorView.RequestsMenu.sortBy('domain')"
data-key="domain"
label="&netmonitorUI.toolbar.domain;"
flex="1">
</button>
@ -86,7 +82,7 @@
align="center">
<button id="requests-menu-type-button"
class="requests-menu-header-button requests-menu-type"
onclick="NetMonitorView.RequestsMenu.sortBy('type')"
data-key="type"
label="&netmonitorUI.toolbar.type;"
flex="1">
</button>
@ -96,7 +92,7 @@
align="center">
<button id="requests-menu-size-button"
class="requests-menu-header-button requests-menu-size"
onclick="NetMonitorView.RequestsMenu.sortBy('size')"
data-key="size"
label="&netmonitorUI.toolbar.size;"
flex="1">
</button>
@ -107,7 +103,7 @@
flex="1">
<button id="requests-menu-waterfall-button"
class="requests-menu-header-button requests-menu-waterfall"
onclick="NetMonitorView.RequestsMenu.sortBy('waterfall')"
data-key="waterfall"
pack="start"
flex="1">
<label id="requests-menu-waterfall-label"
@ -160,47 +156,47 @@
<button id="requests-menu-filter-all-button"
class="requests-menu-footer-button"
checked="true"
onclick="NetMonitorView.RequestsMenu.filterOn('all')"
data-key="all"
label="&netmonitorUI.footer.filterAll;">
</button>
<button id="requests-menu-filter-html-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('html')"
data-key="html"
label="&netmonitorUI.footer.filterHTML;">
</button>
<button id="requests-menu-filter-css-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('css')"
data-key="css"
label="&netmonitorUI.footer.filterCSS;">
</button>
<button id="requests-menu-filter-js-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('js')"
data-key="js"
label="&netmonitorUI.footer.filterJS;">
</button>
<button id="requests-menu-filter-xhr-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('xhr')"
data-key="xhr"
label="&netmonitorUI.footer.filterXHR;">
</button>
<button id="requests-menu-filter-fonts-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('fonts')"
data-key="fonts"
label="&netmonitorUI.footer.filterFonts;">
</button>
<button id="requests-menu-filter-images-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('images')"
data-key="images"
label="&netmonitorUI.footer.filterImages;">
</button>
<button id="requests-menu-filter-media-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('media')"
data-key="media"
label="&netmonitorUI.footer.filterMedia;">
</button>
<button id="requests-menu-filter-flash-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('flash')"
data-key="flash"
label="&netmonitorUI.footer.filterFlash;">
</button>
<spacer id="requests-menu-spacer-end"
@ -212,7 +208,6 @@
crop="end"/>
<button id="requests-menu-clear-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.clear()"
label="&netmonitorUI.footer.clear;">
</button>
</hbox>
@ -229,22 +224,22 @@
class="plain tabpanel-summary-label
custom-header"/>
<hbox flex="1" pack="end">
<button class="devtools-toolbarbutton"
label="&netmonitorUI.custom.send;"
onclick="NetMonitorView.RequestsMenu.sendCustomRequest();"/>
<button class="devtools-toolbarbutton"
label="&netmonitorUI.custom.cancel;"
onclick="NetMonitorView.RequestsMenu.closeCustomRequest();"/>
<button id="custom-request-send-button"
class="devtools-toolbarbutton"
label="&netmonitorUI.custom.send;"/>
<button id="custom-request-close-button"
class="devtools-toolbarbutton"
label="&netmonitorUI.custom.cancel;"/>
</hbox>
</hbox>
<hbox id="custom-method-and-url"
class="tabpanel-summary-container"
align="center">
<textbox id="custom-method-value"
oninput="NetMonitorView.CustomRequest.onUpdate('method');"/>
data-key="method"/>
<textbox id="custom-url-value"
flex="1"
oninput="NetMonitorView.CustomRequest.onUpdate('url');"/>
data-key="url"/>
</hbox>
<vbox id="custom-query"
class="tabpanel-summary-container custom-section">
@ -255,7 +250,7 @@
multiline="true"
rows="4"
wrap="off"
oninput="NetMonitorView.CustomRequest.onUpdate('query');"/>
data-key="query"/>
</vbox>
<vbox id="custom-headers"
class="tabpanel-summary-container custom-section">
@ -266,7 +261,7 @@
multiline="true"
rows="8"
wrap="off"
oninput="NetMonitorView.CustomRequest.onUpdate('headers');"/>
data-key="headers"/>
</vbox>
<vbox id="custom-postdata"
class="tabpanel-summary-container custom-section">
@ -277,7 +272,7 @@
multiline="true"
rows="6"
wrap="off"
oninput="NetMonitorView.CustomRequest.onUpdate('body');"/>
data-key="body"/>
</vbox>
</vbox>
<tabbox id="event-details-pane"
@ -326,9 +321,8 @@
crop="end"
flex="1"/>
<button id="headers-summary-resend"
label="&netmonitorUI.summary.editAndResend;"
class="devtools-toolbarbutton"
onclick="NetMonitorView.RequestsMenu.cloneSelectedRequest();"/>
class="devtools-toolbarbutton"
label="&netmonitorUI.summary.editAndResend;"/>
</hbox>
<hbox id="headers-summary-version"
class="tabpanel-summary-container"

View File

@ -85,7 +85,7 @@ Parser.prototype = {
}
}
let pool = new SyntaxTreesPool(syntaxTrees);
let pool = new SyntaxTreesPool(syntaxTrees, aUrl);
// Cache the syntax trees pool by the specified url. This is entirely
// optional, but it's strongly encouraged to cache ASTs because
@ -123,9 +123,12 @@ Parser.prototype = {
*
* @param object aSyntaxTrees
* A collection of AST nodes generated for a source.
* @param string aUrl [optional]
* The source url.
*/
function SyntaxTreesPool(aSyntaxTrees) {
function SyntaxTreesPool(aSyntaxTrees, aUrl = "<unknown>") {
this._trees = aSyntaxTrees;
this._url = aUrl;
this._cache = new Map();
}
@ -134,7 +137,7 @@ SyntaxTreesPool.prototype = {
* @see SyntaxTree.prototype.getIdentifierAt
*/
getIdentifierAt: function({ line, column, scriptIndex }) {
return this._first(this._call("getIdentifierAt", scriptIndex, line, column));
return this._call("getIdentifierAt", scriptIndex, line, column)[0];
},
/**
@ -177,18 +180,6 @@ SyntaxTreesPool.prototype = {
return info;
},
/**
* Gets the first script results from a source results set.
* If no results are found, null is returned.
*
* @return array
* A collection of parse results for the first script in a source.
*/
_first: function(aSourceResults) {
let scriptResult = aSourceResults.filter(e => !!e.parseResults)[0];
return scriptResult ? scriptResult.parseResults : null;
},
/**
* Handles a request for a specific or all known syntax trees.
*
@ -216,17 +207,18 @@ SyntaxTreesPool.prototype = {
for (let syntaxTree of targettedTrees) {
try {
results.push({
sourceUrl: syntaxTree.url,
scriptLength: syntaxTree.length,
scriptOffset: syntaxTree.offset,
parseResults: syntaxTree[aFunction].apply(syntaxTree, aParams)
});
let parseResults = syntaxTree[aFunction].apply(syntaxTree, aParams);
if (parseResults) {
parseResults.sourceUrl = syntaxTree.url;
parseResults.scriptLength = syntaxTree.length;
parseResults.scriptOffset = syntaxTree.offset;
results.push(parseResults);
}
} catch (e) {
// Can't guarantee that the tree traversal logic is forever perfect :)
// Language features may be added, in which case the recursive methods
// need to be updated. If an exception is thrown here, file a bug.
DevToolsUtils.reportException("syntax tree", e);
DevToolsUtils.reportException("Syntax tree visitor for " + aUrl, e);
}
}
this._cache.set(requestId, results);

View File

@ -159,7 +159,7 @@ AutocompletePopup.prototype = {
* Check if the autocomplete popup is open.
*/
get isOpen() {
return this._panel.state == "open";
return this._panel.state == "open" || this._panel.state == "showing";
},
/**

View File

@ -0,0 +1,179 @@
/* vim:set ts=2 sw=2 sts=2 et tw=80:
* 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/. */
const cssAutoCompleter = require("devtools/sourceeditor/css-autocompleter");
const { AutocompletePopup } = require("devtools/shared/autocomplete-popup");
const privates = new WeakMap();
/**
* Prepares an editor instance for autocompletion, setting up the popup and the
* CSS completer instance.
*/
function setupAutoCompletion(ctx, walker) {
let { cm, ed, Editor } = ctx;
let win = ed.container.contentWindow.wrappedJSObject;
let completer = null;
if (ed.config.mode == Editor.modes.css)
completer = new cssAutoCompleter({walker: walker});
let popup = new AutocompletePopup(win.parent.document, {
position: "after_start",
fixedWidth: true,
theme: "auto",
autoSelect: true
});
let keyMap = {
"Tab": cm => {
if (popup && popup.isOpen) {
cycleSuggestions(ed);
return;
}
return win.CodeMirror.Pass;
},
"Shift-Tab": cm => {
if (popup && popup.isOpen) {
cycleSuggestions(ed, true);
return;
}
return win.CodeMirror.Pass;
},
};
keyMap[Editor.accel("Space")] = cm => autoComplete(ctx);
cm.addKeyMap(keyMap);
cm.on("keydown", (cm, e) => onEditorKeypress(ed, e));
ed.on("change", () => autoComplete(ctx));
ed.on("destroy", () => {
cm.off("keydown", (cm, e) => onEditorKeypress(ed, e));
ed.off("change", () => autoComplete(ctx));
popup.destroy();
popup = null;
completer = null;
});
privates.set(ed, {
popup: popup,
completer: completer,
insertingSuggestion: false,
suggestionInsertedOnce: false
});
}
/**
* Provides suggestions to autocomplete the current token/word being typed.
*/
function autoComplete({ ed, cm }) {
let private = privates.get(ed);
let { completer, popup } = private;
if (!completer || private.insertingSuggestion || private.doNotAutocomplete) {
private.insertingSuggestion = false;
return;
}
let cur = ed.getCursor();
completer.complete(cm.getRange({line: 0, ch: 0}, cur), cur)
.then(suggestions => {
if (!suggestions || !suggestions.length || !suggestions[0].preLabel) {
private.suggestionInsertedOnce = false;
popup.hidePopup();
ed.emit("after-suggest");
return;
}
// The cursor is at the end of the currently entered part of the token, like
// "backgr|" but we need to open the popup at the beginning of the character
// "b". Thus we need to calculate the width of the entered part of the token
// ("backgr" here). 4 comes from the popup's left padding.
let left = suggestions[0].preLabel.length * cm.defaultCharWidth() + 4;
popup.hidePopup();
popup.setItems(suggestions);
popup.openPopup(cm.display.cursor, -1 * left, 0);
private.suggestionInsertedOnce = false;
// This event is used in tests.
ed.emit("after-suggest");
});
}
/**
* Cycles through provided suggestions by the popup in a top to bottom manner
* when `reverse` is not true. Opposite otherwise.
*/
function cycleSuggestions(ed, reverse) {
let private = privates.get(ed);
let { popup, completer } = private;
let cur = ed.getCursor();
private.insertingSuggestion = true;
if (!private.suggestionInsertedOnce) {
private.suggestionInsertedOnce = true;
let firstItem;
if (reverse) {
firstItem = popup.getItemAtIndex(popup.itemCount - 1);
popup.selectPreviousItem();
} else {
firstItem = popup.getItemAtIndex(0);
if (firstItem.label == firstItem.preLabel && popup.itemCount > 1) {
firstItem = popup.getItemAtIndex(1);
popup.selectNextItem();
}
}
if (popup.itemCount == 1)
popup.hidePopup();
ed.replaceText(firstItem.label.slice(firstItem.preLabel.length), cur, cur);
} else {
let fromCur = {
line: cur.line,
ch : cur.ch - popup.selectedItem.label.length
};
if (reverse)
popup.selectPreviousItem();
else
popup.selectNextItem();
ed.replaceText(popup.selectedItem.label, fromCur, cur);
}
// This event is used in tests.
ed.emit("suggestion-entered");
}
/**
* onkeydown handler for the editor instance to prevent autocompleting on some
* keypresses.
*/
function onEditorKeypress(ed, event) {
let private = privates.get(ed);
switch (event.keyCode) {
case event.DOM_VK_UP:
case event.DOM_VK_DOWN:
case event.DOM_VK_LEFT:
case event.DOM_VK_RIGHT:
case event.DOM_VK_HOME:
case event.DOM_VK_END:
case event.DOM_VK_BACK_SPACE:
case event.DOM_VK_DELETE:
case event.DOM_VK_ENTER:
case event.DOM_VK_RETURN:
case event.DOM_VK_ESCAPE:
private.doNotAutocomplete = true;
private.popup.hidePopup();
break;
default:
private.doNotAutocomplete = false;
}
}
/**
* Returns the private popup. This method is used by tests to test the feature.
*/
function getPopup({ ed }) {
return privates.get(ed).popup;
}
// Export functions
module.exports.setupAutoCompletion = setupAutoCompletion;
module.exports.getAutocompletionPopup = getPopup;

View File

@ -0,0 +1,798 @@
/* 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/. */
const { Cc, Ci, Cu } = require('chrome');
const cssTokenizer = require("devtools/sourceeditor/css-tokenizer");
const promise = Cu.import("resource://gre/modules/Promise.jsm");
/**
* Here is what this file (+ ./css-tokenizer.js) do.
*
* The main objective here is to provide as much suggestions to the user editing
* a stylesheet in Style Editor. The possible things that can be suggested are:
* - CSS property names
* - CSS property values
* - CSS Selectors
* - Some other known CSS keywords
*
* Gecko provides a list of both property names and their corresponding values.
* We take out a list of matching selectors using the Inspector actor's
* `getSuggestionsForQuery` method. Now the only thing is to parse the CSS being
* edited by the user, figure out what token or word is being written and last
* but the most difficult, what is being edited.
*
* The file 'css-tokenizer' helps in converting the CSS into meaningful tokens,
* each having a certain type associated with it. These tokens help us to figure
* out the currently edited word and to write a CSS state machine to figure out
* what the user is currently editing. By that, I mean, whether he is editing a
* selector or a property or a value, or even fine grained information like an
* id in the selector.
*
* The `resolveState` method iterated over the tokens spitted out by the
* tokenizer, using switch cases, follows a state machine logic and finally
* figures out these informations:
* - The state of the CSS at the cursor (one out of CSS_STATES)
* - The current token that is being edited `cmpleting`
* - If the state is "selector", the selector state (one of SELECTOR_STATES)
* - If the state is "selector", the current selector till the cursor
* - If the state is "value", the corresponding property name
*
* In case of "value" and "property" states, we simply use the information
* provided by Gecko to filter out the possible suggestions.
* For "selector" state, we request the Inspector actor to query the page DOM
* and filter out the possible suggestions.
* For "media" and "keyframes" state, the only possible suggestions for now are
* "media" and "keyframes" respectively, although "media" can have suggestions
* like "max-width", "orientation" etc. Similarly "value" state can also have
* much better logical suggestions if we fine grain identify a sub state just
* like we do for the "selector" state.
*/
// Autocompletion types.
const CSS_STATES = {
"null": "null",
property: "property", // foo { bar|: … }
value: "value", // foo {bar: baz|}
selector: "selector", // f| {bar: baz}
media: "media", // @med| , or , @media scr| { }
keyframes: "keyframes", // @keyf|
frame: "frame", // @keyframs foobar { t|
};
const SELECTOR_STATES = {
"null": "null",
id: "id", // #f|
class: "class", // #foo.b|
tag: "tag", // fo|
pseudo: "pseudo", // foo:|
attribute: "attribute", // foo[b|
value: "value", // foo[bar=b|
};
const { properties, propertyNames } = getCSSKeywords();
/**
* Constructor for the autocompletion object.
*
* @param options {Object} An options object containing the following options:
* - walker {Object} The object used for query selecting from the current
* target's DOM.
* - maxEntries {Number} Maximum selectors suggestions to display.
*/
function CSSCompleter(options = {}) {
this.walker = options.walker;
this.maxEntries = options.maxEntries || 15;
}
CSSCompleter.prototype = {
/**
* Returns a list of suggestions based on the caret position.
*
* @param source {String} String of the source code.
* @param caret {Object} Cursor location with line and ch properties.
*
* @returns [{object}] A sorted list of objects containing the following
* peroperties:
* - label {String} Full keyword for the suggestion
* - preLabel {String} Already entered part of the label
*/
complete: function(source, caret) {
// Getting the context from the caret position.
if (!this.resolveState(source, caret)) {
// We couldn't resolve the context, we won't be able to complete.
return Promise.resolve([]);
}
// Properly suggest based on the state.
switch(this.state) {
case CSS_STATES.property:
return this.completeProperties(this.completing);
case CSS_STATES.value:
return this.completeValues(this.propertyName, this.completing);
case CSS_STATES.selector:
return this.suggestSelectors();
case CSS_STATES.media:
case CSS_STATES.keyframes:
if ("media".startsWith(this.completing)) {
return Promise.resolve([{
label: "media",
preLabel: this.completing
}]);
} else if ("keyframes".startsWith(this.completing)) {
return Promise.resolve([{
label: "keyrames",
preLabel: this.completing
}]);
}
}
return Promise.resolve([]);
},
/**
* Resolves the state of CSS at the cursor location. This method implements a
* custom written CSS state machine. The various switch statements provide the
* transition rules for the state. It also finds out various informatino about
* the nearby CSS like the property name being completed, the complete
* selector, etc.
*
* @param source {String} String of the source code.
* @param caret {Object} Cursor location with line and ch properties.
*
* @returns CSS_STATE
* One of CSS_STATE enum or null if the state cannot be resolved.
*/
resolveState: function(source, {line, ch}) {
// Function to return the last element of an array
let peek = arr => arr[arr.length - 1];
let tokens = cssTokenizer(source, {loc:true});
let tokIndex = tokens.length - 1;
if (tokens[tokIndex].loc.end.line < line ||
(tokens[tokIndex].loc.end.line === line &&
tokens[tokIndex].loc.end.column < ch)) {
// If the last token is not an EOF, we didn't tokenize it correctly.
// This special case is handled in case we couldn't tokenize, but the last
// token that *could be tokenized* was an identifier.
return null;
}
// Since last token is EOF, the cursor token is last - 1
tokIndex--;
// _state can be one of CSS_STATES;
let _state = CSS_STATES.null;
let cursor = 0;
// This will maintain a stack of paired elements like { & }, @m & }, : & ; etc
let scopeStack = [];
let token = null;
let propertyName = null;
let selector = "";
let selectorBeforeNot = "";
let selectorState = SELECTOR_STATES.null;
while (cursor <= tokIndex && (token = tokens[cursor++])) {
switch (_state) {
case CSS_STATES.property:
// From CSS_STATES.property, we can either go to CSS_STATES.value state
// when we hit the first ':' or CSS_STATES.selector if "}" is reached.
switch(token.tokenType) {
case ":":
scopeStack.push(":");
if (tokens[cursor - 2].tokenType != "WHITESPACE")
propertyName = tokens[cursor - 2].value;
else
propertyName = tokens[cursor - 3].value;
_state = CSS_STATES.value;
break;
case "}":
if (/[{f]/.test(peek(scopeStack))) {
let popped = scopeStack.pop();
if (popped == "f") {
_state = CSS_STATES.frame;
} else {
selector = "";
_state = CSS_STATES.null;
}
}
break;
}
break;
case CSS_STATES.value:
// From CSS_STATES.value, we can go to one of CSS_STATES.property,
// CSS_STATES.frame, CSS_STATES.selector and CSS_STATES.null
switch(token.tokenType) {
case ";":
if (/[:]/.test(peek(scopeStack))) {
scopeStack.pop();
_state = CSS_STATES.property;
}
break;
case "}":
if (peek(scopeStack) == ":")
scopeStack.pop();
if (/[{f]/.test(peek(scopeStack))) {
let popped = scopeStack.pop();
if (popped == "f") {
_state = CSS_STATES.frame;
} else {
selector = "";
_state = CSS_STATES.null;
}
}
break;
}
break;
case CSS_STATES.selector:
// From CSS_STATES.selector, we can only go to CSS_STATES.property when
// we hit "{"
if (token.tokenType == "{") {
scopeStack.push("{");
_state = CSS_STATES.property;
break;
}
switch(selectorState) {
case SELECTOR_STATES.id:
case SELECTOR_STATES.class:
case SELECTOR_STATES.tag:
switch(token.tokenType) {
case "HASH":
selectorState = SELECTOR_STATES.id;
selector += "#" + token.value;
break;
case "DELIM":
if (token.value == ".") {
selectorState = SELECTOR_STATES.class;
selector += ".";
if (cursor <= tokIndex &&
tokens[cursor].tokenType == "IDENT") {
token = tokens[cursor++];
selector += token.value;
}
} else if (token.value == "#") {
selectorState = SELECTOR_STATES.id;
selector += "#";
} else if (/[>~+]/.test(token.value)) {
selectorState = SELECTOR_STATES.null;
selector += token.value;
} else if (token.value == ",") {
selectorState = SELECTOR_STATES.null;
selector = "";
}
break;
case ":":
selectorState = SELECTOR_STATES.pseudo;
selector += ":";
if (cursor > tokIndex)
break;
token = tokens[cursor++];
switch(token.tokenType) {
case "FUNCTION":
selectorState = SELECTOR_STATES.null;
selectorBeforeNot = selector;
selector = "";
scopeStack.push("(");
break;
case "IDENT":
selector += token.value;
break;
}
break;
case "[":
selectorState = SELECTOR_STATES.attribute;
scopeStack.push("[");
selector += "[";
break;
case ")":
if (peek(scopeStack) == "(") {
scopeStack.pop();
selector = selectorBeforeNot + "not(" + selector + ")";
selectorState = SELECTOR_STATES.null;
}
break;
case "WHITESPACE":
selectorState = SELECTOR_STATES.null;
selector && (selector += " ");
break;
}
break;
case SELECTOR_STATES.null:
// From SELECTOR_STATES.null state, we can go to one of
// SELECTOR_STATES.id, SELECTOR_STATES.class or SELECTOR_STATES.tag
switch(token.tokenType) {
case "HASH":
selectorState = SELECTOR_STATES.id;
selector += "#" + token.value;
break;
case "IDENT":
selectorState = SELECTOR_STATES.tag;
selector += token.value;
break;
case "DELIM":
if (token.value == ".") {
selectorState = SELECTOR_STATES.class;
selector += ".";
if (cursor <= tokIndex &&
tokens[cursor].tokenType == "IDENT") {
token = tokens[cursor++];
selector += token.value;
}
} else if (token.value == "#") {
selectorState = SELECTOR_STATES.id;
selector += "#";
} else if (token.value == "*") {
selectorState = SELECTOR_STATES.tag;
selector += "*";
} else if (/[>~+]/.test(token.value)) {
selector += token.value;
} else if (token.value == ",") {
selectorState = SELECTOR_STATES.null;
selector = "";
}
break;
case ":":
selectorState = SELECTOR_STATES.pseudo;
selector += ":";
if (cursor > tokIndex)
break;
token = tokens[cursor++];
switch(token.tokenType) {
case "FUNCTION":
selectorState = SELECTOR_STATES.null;
selectorBeforeNot = selector;
selector = "";
scopeStack.push("(");
break;
case "IDENT":
selector += token.value;
break;
}
break;
case "[":
selectorState = SELECTOR_STATES.attribute;
scopeStack.push("[");
selector += "[";
break;
case ")":
if (peek(scopeStack) == "(") {
scopeStack.pop();
selector = selectorBeforeNot + "not(" + selector + ")";
selectorState = SELECTOR_STATES.null;
}
break;
case "WHITESPACE":
selector && (selector += " ");
break;
}
break;
case SELECTOR_STATES.pseudo:
switch(token.tokenType) {
case "DELIM":
if (/[>~+]/.test(token.value)) {
selectorState = SELECTOR_STATES.null;
selector += token.value;
} else if (token.value == ",") {
selectorState = SELECTOR_STATES.null;
selector = "";
}
break;
case ":":
selectorState = SELECTOR_STATES.pseudo;
selector += ":";
if (cursor > tokIndex)
break;
token = tokens[cursor++];
switch(token.tokenType) {
case "FUNCTION":
selectorState = SELECTOR_STATES.null;
selectorBeforeNot = selector;
selector = "";
scopeStack.push("(");
break;
case "IDENT":
selector += token.value;
break;
}
break;
case "[":
selectorState = SELECTOR_STATES.attribute;
scopeStack.push("[");
selector += "[";
break;
case "WHITESPACE":
selectorState = SELECTOR_STATES.null;
selector && (selector += " ");
break;
}
break;
case SELECTOR_STATES.attribute:
switch(token.tokenType) {
case "DELIM":
if (/[~|^$*]/.test(token.value)) {
selector += token.value;
token = tokens[cursor++];
}
if(token.value == "=") {
selectorState = SELECTOR_STATES.value;
selector += token.value;
}
break;
case "IDENT":
case "STRING":
selector += token.value;
break;
case "]":
if (peek(scopeStack) == "[")
scopeStack.pop();
selectorState = SELECTOR_STATES.null;
selector += "]";
break;
case "WHITESPACE":
selector && (selector += " ");
break;
}
break;
case SELECTOR_STATES.value:
switch(token.tokenType) {
case "STRING":
case "IDENT":
selector += token.value;
break;
case "]":
if (peek(scopeStack) == "[")
scopeStack.pop();
selectorState = SELECTOR_STATES.null;
selector += "]";
break;
case "WHITESPACE":
selector && (selector += " ");
break;
}
break;
}
break;
case CSS_STATES.null:
// From CSS_STATES.null state, we can go to either CSS_STATES.media or
// CSS_STATES.selector.
switch(token.tokenType) {
case "HASH":
selectorState = SELECTOR_STATES.id;
selector = "#" + token.value;
_state = CSS_STATES.selector;
break;
case "IDENT":
selectorState = SELECTOR_STATES.tag;
selector = token.value;
_state = CSS_STATES.selector;
break;
case "DELIM":
if (token.value == ".") {
selectorState = SELECTOR_STATES.class;
selector = ".";
_state = CSS_STATES.selector;
if (cursor <= tokIndex &&
tokens[cursor].tokenType == "IDENT") {
token = tokens[cursor++];
selector += token.value;
}
} else if (token.value == "#") {
selectorState = SELECTOR_STATES.id;
selector = "#";
_state = CSS_STATES.selector;
} else if (token.value == "*") {
selectorState = SELECTOR_STATES.tag;
selector = "*";
_state = CSS_STATES.selector;
}
break;
case ":":
_state = CSS_STATES.selector;
selectorState = SELECTOR_STATES.pseudo;
selector += ":";
if (cursor > tokIndex)
break;
token = tokens[cursor++];
switch(token.tokenType) {
case "FUNCTION":
selectorState = SELECTOR_STATES.null;
selectorBeforeNot = selector;
selector = "";
scopeStack.push("(");
break;
case "IDENT":
selector += token.value;
break;
}
break;
case "[":
_state = CSS_STATES.selector;
selectorState = SELECTOR_STATES.attribute;
scopeStack.push("[");
selector += "[";
break;
case "AT-KEYWORD":
_state = token.value.startsWith("m") ? CSS_STATES.media
: CSS_STATES.keyframes;
break;
case "}":
if (peek(scopeStack) == "@m")
scopeStack.pop();
break;
}
break;
case CSS_STATES.media:
// From CSS_STATES.media, we can only go to CSS_STATES.null state when
// we hit the first '{'
if (token.tokenType == "{") {
scopeStack.push("@m");
_state = CSS_STATES.null;
}
break;
case CSS_STATES.keyframes:
// From CSS_STATES.keyframes, we can only go to CSS_STATES.frame state
// when we hit the first '{'
if (token.tokenType == "{") {
scopeStack.push("@k");
_state = CSS_STATES.frame;
}
break;
case CSS_STATES.frame:
// From CSS_STATES.frame, we can either go to CSS_STATES.property state
// when we hit the first '{' or to CSS_STATES.selector when we hit '}'
if (token.tokenType == "{") {
scopeStack.push("f");
_state = CSS_STATES.property;
} else if (token.tokenType == "}") {
if (peek(scopeStack) == "@k")
scopeStack.pop();
_state = CSS_STATES.null;
}
break;
}
}
this.state = _state;
if (!token)
return _state;
if (token && token.tokenType != "WHITESPACE") {
this.completing = ((token.value || token.repr || token.tokenType) + "")
.slice(0, ch - token.loc.start.column)
.replace(/^[.#]$/, "");
} else {
this.completing = "";
}
// Special check for !important; case.
if (tokens[cursor - 2] && tokens[cursor - 2].value == "!" &&
this.completing == "important".slice(0, this.completing.length)) {
this.completing = "!" + this.completing;
}
this.propertyName = _state == CSS_STATES.value ? propertyName : null;
selector = selector.slice(0, selector.length + token.loc.end.column - ch);
this.selector = _state == CSS_STATES.selector ? selector : null;
this.selectorState = _state == CSS_STATES.selector ? selectorState : null;
return _state;
},
/**
* Queries the DOM Walker actor for suggestions regarding the selector being
* completed
*/
suggestSelectors: function () {
let walker = this.walker;
if (!walker)
return Promise.resolve([]);
let query = this.selector;
// Even though the selector matched atleast one node, there is still
// possibility of suggestions.
switch(this.selectorState) {
case SELECTOR_STATES.null:
query += "*";
break;
case SELECTOR_STATES.tag:
query = query.slice(0, query.length - this.completing.length);
break;
case SELECTOR_STATES.id:
case SELECTOR_STATES.class:
case SELECTOR_STATES.pseudo:
if (/^[.:#]$/.test(this.completing)) {
query = query.slice(0, query.length - this.completing.length);
this.completing = "";
} else {
query = query.slice(0, query.length - this.completing.length - 1);
}
break;
}
if (/[\s+>~]$/.test(query) &&
this.selectorState != SELECTOR_STATES.attribute &&
this.selectorState != SELECTOR_STATES.value) {
query += "*";
}
// Set the values that this request was supposed to suggest to.
this._currentQuery = query;
return walker.getSuggestionsForQuery(query, this.completing, this.selectorState)
.then(result => this.prepareSelectorResults(result));
},
/**
* Prepares the selector suggestions returned by the walker actor.
*/
prepareSelectorResults: function(result) {
if (this._currentQuery != result.query)
return [];
result = result.suggestions;
let query = this.selector;
let completion = [];
for (let value of result) {
switch(this.selectorState) {
case SELECTOR_STATES.id:
case SELECTOR_STATES.class:
case SELECTOR_STATES.pseudo:
if (/^[.:#]$/.test(this.completing)) {
value[0] = query.slice(0, query.length - this.completing.length) +
value[0];
} else {
value[0] = query.slice(0, query.length - this.completing.length - 1) +
value[0];
}
break;
case SELECTOR_STATES.tag:
value[0] = query.slice(0, query.length - this.completing.length) +
value[0];
break;
case SELECTOR_STATES.null:
value[0] = query + value[0];
break;
default:
value[0] = query.slice(0, query.length - this.completing.length) +
value[0];
}
completion.push({
label: value[0],
preLabel: query,
score: value[1]
});
if (completion.length > this.maxEntries - 1)
break;
}
return completion;
},
/**
* Returns CSS property name suggestions based on the input.
*
* @param startProp {String} Initial part of the property being completed.
*/
completeProperties: function(startProp) {
let finalList = [];
let length = propertyNames.length;
let i = 0, count = 0;
for (; i < length && count < this.maxEntries; i++) {
if (propertyNames[i].startsWith(startProp)) {
count++;
finalList.push({
preLabel: startProp,
label: propertyNames[i]
});
} else if (propertyNames[i] > startProp) {
// We have crossed all possible matches alphabetically.
break;
}
}
return Promise.resolve(finalList);
},
/**
* Returns CSS value suggestions based on the corresponding property.
*
* @param propName {String} The property to which the value being completed
* belongs.
* @param startValue {String} Initial part of the value being completed.
*/
completeValues: function(propName, startValue) {
let finalList = [];
let list = ["!important;", ...(properties[propName] || [])];
let length = list.length;
let i = 0, count = 0;
for (; i < length && count < this.maxEntries; i++) {
if (list[i].startsWith(startValue)) {
count++;
finalList.push({
preLabel: startValue,
label: list[i]
});
} else if (list[i] > startValue) {
// We have crossed all possible matches alphabetically.
break;
}
}
return Promise.resolve(finalList);
},
}
/**
* Returns a list of all property names and a map of property name vs possible
* CSS values provided by the Gecko engine.
*
* @return {Object} An object with following properties:
* - propertyNames {Array} Array of string containing all the possible
* CSS property names.
* - properties {Object|Map} A map where key is the property name and
* value is an array of string containing all the possible
* CSS values the property can have.
*/
function getCSSKeywords() {
let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
.getService(Ci.inIDOMUtils);
let props = {};
let propNames = domUtils.getCSSPropertyNames(domUtils.INCLUDE_ALIASES);
propNames.forEach(prop => {
props[prop] = domUtils.getCSSValuesForProperty(prop).sort();
});
return {
properties: props,
propertyNames: propNames.sort()
};
}
module.exports = CSSCompleter;

View File

@ -0,0 +1,717 @@
/**
* This file is taken from the below mentioned url and is under CC0 license.
* https://github.com/tabatkins/css-parser/blob/master/tokenizer.js
* Please retain this comment while updating this file from upstream.
*/
(function (root, factory) {
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
// Rhino, and plain browser loading.
if (typeof define === 'function' && define.amd) {
define(['exports'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports);
} else {
factory(root);
}
}(this, function (exports) {
var between = function (num, first, last) { return num >= first && num <= last; }
function digit(code) { return between(code, 0x30,0x39); }
function hexdigit(code) { return digit(code) || between(code, 0x41,0x46) || between(code, 0x61,0x66); }
function uppercaseletter(code) { return between(code, 0x41,0x5a); }
function lowercaseletter(code) { return between(code, 0x61,0x7a); }
function letter(code) { return uppercaseletter(code) || lowercaseletter(code); }
function nonascii(code) { return code >= 0xa0; }
function namestartchar(code) { return letter(code) || nonascii(code) || code == 0x5f; }
function namechar(code) { return namestartchar(code) || digit(code) || code == 0x2d; }
function nonprintable(code) { return between(code, 0,8) || between(code, 0xe,0x1f) || between(code, 0x7f,0x9f); }
function newline(code) { return code == 0xa || code == 0xc; }
function whitespace(code) { return newline(code) || code == 9 || code == 0x20; }
function badescape(code) { return newline(code) || isNaN(code); }
// Note: I'm not yet acting smart enough to actually handle astral characters.
var maximumallowedcodepoint = 0x10ffff;
function tokenize(str, options) {
if(options == undefined) options = {transformFunctionWhitespace:false, scientificNotation:false};
var i = -1;
var tokens = [];
var state = "data";
var code;
var currtoken;
// Line number information.
var line = 0;
var column = 0;
// The only use of lastLineLength is in reconsume().
var lastLineLength = 0;
var incrLineno = function() {
line += 1;
lastLineLength = column;
column = 0;
};
var locStart = {line:line, column:column};
var next = function(num) { if(num === undefined) num = 1; return str.charCodeAt(i+num); };
var consume = function(num) {
if(num === undefined)
num = 1;
i += num;
code = str.charCodeAt(i);
if (newline(code)) incrLineno();
else column += num;
//console.log('Consume '+i+' '+String.fromCharCode(code) + ' 0x' + code.toString(16));
return true;
};
var reconsume = function() {
i -= 1;
if (newline(code)) {
line -= 1;
column = lastLineLength;
} else {
column -= 1;
}
locStart.line = line;
locStart.column = column;
return true;
};
var eof = function() { return i >= str.length; };
var donothing = function() {};
var emit = function(token) {
if(token) {
token.finish();
} else {
token = currtoken.finish();
}
if (options.loc === true) {
token.loc = {};
token.loc.start = {line:locStart.line, column:locStart.column};
locStart = {line: line, column: column};
token.loc.end = locStart;
}
tokens.push(token);
//console.log('Emitting ' + token);
currtoken = undefined;
return true;
};
var create = function(token) { currtoken = token; return true; };
var parseerror = function() { console.log("Parse error at index " + i + ", processing codepoint 0x" + code.toString(16) + " in state " + state + ".");return true; };
var switchto = function(newstate) {
state = newstate;
//console.log('Switching to ' + state);
return true;
};
var consumeEscape = function() {
// Assume the the current character is the \
consume();
if(hexdigit(code)) {
// Consume 1-6 hex digits
var digits = [];
for(var total = 0; total < 6; total++) {
if(hexdigit(code)) {
digits.push(code);
consume();
} else { break; }
}
var value = parseInt(digits.map(String.fromCharCode).join(''), 16);
if( value > maximumallowedcodepoint ) value = 0xfffd;
// If the current char is whitespace, cool, we'll just eat it.
// Otherwise, put it back.
if(!whitespace(code)) reconsume();
return value;
} else {
return code;
}
};
for(;;) {
if(i > str.length*2) return "I'm infinite-looping!";
consume();
switch(state) {
case "data":
if(whitespace(code)) {
emit(new WhitespaceToken);
while(whitespace(next())) consume();
}
else if(code == 0x22) switchto("double-quote-string");
else if(code == 0x23) switchto("hash");
else if(code == 0x27) switchto("single-quote-string");
else if(code == 0x28) emit(new OpenParenToken);
else if(code == 0x29) emit(new CloseParenToken);
else if(code == 0x2b) {
if(digit(next()) || (next() == 0x2e && digit(next(2)))) switchto("number") && reconsume();
else emit(new DelimToken(code));
}
else if(code == 0x2d) {
if(next(1) == 0x2d && next(2) == 0x3e) consume(2) && emit(new CDCToken);
else if(digit(next()) || (next(1) == 0x2e && digit(next(2)))) switchto("number") && reconsume();
else if(namestartchar(next())) switchto("identifier") && reconsume();
else emit(new DelimToken(code));
}
else if(code == 0x2e) {
if(digit(next())) switchto("number") && reconsume();
else emit(new DelimToken(code));
}
else if(code == 0x2f) {
if(next() == 0x2a) switchto("comment");
else emit(new DelimToken(code));
}
else if(code == 0x3a) emit(new ColonToken);
else if(code == 0x3b) emit(new SemicolonToken);
else if(code == 0x3c) {
if(next(1) == 0x21 && next(2) == 0x2d && next(3) == 0x2d) consume(3) && emit(new CDOToken);
else emit(new DelimToken(code));
}
else if(code == 0x40) switchto("at-keyword");
else if(code == 0x5b) emit(new OpenSquareToken);
else if(code == 0x5c) {
if(badescape(next())) parseerror() && emit(new DelimToken(code));
else switchto("identifier") && reconsume();
}
else if(code == 0x5d) emit(new CloseSquareToken);
else if(code == 0x7b) emit(new OpenCurlyToken);
else if(code == 0x7d) emit(new CloseCurlyToken);
else if(digit(code)) switchto("number") && reconsume();
else if(code == 0x55 || code == 0x75) {
if(next(1) == 0x2b && hexdigit(next(2))) consume() && switchto("unicode-range");
else if((next(1) == 0x52 || next(1) == 0x72) && (next(2) == 0x4c || next(2) == 0x6c) && (next(3) == 0x28)) consume(3) && switchto("url");
else switchto("identifier") && reconsume();
}
else if(namestartchar(code)) switchto("identifier") && reconsume();
else if(eof()) { emit(new EOFToken); return tokens; }
else emit(new DelimToken(code));
break;
case "double-quote-string":
if(currtoken == undefined) create(new StringToken);
if(code == 0x22) emit() && switchto("data");
else if(eof()) parseerror() && emit() && switchto("data");
else if(newline(code)) parseerror() && emit(new BadStringToken) && switchto("data") && reconsume();
else if(code == 0x5c) {
if(badescape(next())) parseerror() && emit(new BadStringToken) && switchto("data");
else if(newline(next())) consume();
else currtoken.append(consumeEscape());
}
else currtoken.append(code);
break;
case "single-quote-string":
if(currtoken == undefined) create(new StringToken);
if(code == 0x27) emit() && switchto("data");
else if(eof()) parseerror() && emit() && switchto("data");
else if(newline(code)) parseerror() && emit(new BadStringToken) && switchto("data") && reconsume();
else if(code == 0x5c) {
if(badescape(next())) parseerror() && emit(new BadStringToken) && switchto("data");
else if(newline(next())) consume();
else currtoken.append(consumeEscape());
}
else currtoken.append(code);
break;
case "hash":
if(namechar(code)) create(new HashToken(code)) && switchto("hash-rest");
else if(code == 0x5c) {
if(badescape(next())) parseerror() && emit(new DelimToken(0x23)) && switchto("data") && reconsume();
else create(new HashToken(consumeEscape())) && switchto('hash-rest');
}
else emit(new DelimToken(0x23)) && switchto('data') && reconsume();
break;
case "hash-rest":
if(namechar(code)) currtoken.append(code);
else if(code == 0x5c) {
if(badescape(next())) parseerror() && emit(new DelimToken(0x23)) && switchto("data") && reconsume();
else currtoken.append(consumeEscape());
}
else emit() && switchto('data') && reconsume();
break;
case "comment":
if(code == 0x2a) {
if(next() == 0x2f) consume() && switchto('data');
else donothing();
}
else if(eof()) parseerror() && switchto('data') && reconsume();
else donothing();
break;
case "at-keyword":
if(code == 0x2d) {
if(namestartchar(next())) consume() && create(new AtKeywordToken([0x40,code])) && switchto('at-keyword-rest');
else emit(new DelimToken(0x40)) && switchto('data') && reconsume();
}
else if(namestartchar(code)) create(new AtKeywordToken(code)) && switchto('at-keyword-rest');
else if(code == 0x5c) {
if(badescape(next())) parseerror() && emit(new DelimToken(0x23)) && switchto("data") && reconsume();
else create(new AtKeywordToken(consumeEscape())) && switchto('at-keyword-rest');
}
else emit(new DelimToken(0x40)) && switchto('data') && reconsume();
break;
case "at-keyword-rest":
if(namechar(code)) currtoken.append(code);
else if(code == 0x5c) {
if(badescape(next())) parseerror() && emit() && switchto("data") && reconsume();
else currtoken.append(consumeEscape());
}
else emit() && switchto('data') && reconsume();
break;
case "identifier":
if(code == 0x2d) {
if(namestartchar(next())) create(new IdentifierToken(code)) && switchto('identifier-rest');
else switchto('data') && reconsume();
}
else if(namestartchar(code)) create(new IdentifierToken(code)) && switchto('identifier-rest');
else if(code == 0x5c) {
if(badescape(next())) parseerror() && switchto("data") && reconsume();
else create(new IdentifierToken(consumeEscape())) && switchto('identifier-rest');
}
else switchto('data') && reconsume();
break;
case "identifier-rest":
if(namechar(code)) currtoken.append(code);
else if(code == 0x5c) {
if(badescape(next())) parseerror() && emit() && switchto("data") && reconsume();
else currtoken.append(consumeEscape());
}
else if(code == 0x28) emit(new FunctionToken(currtoken)) && switchto('data');
else if(whitespace(code) && options.transformFunctionWhitespace) switchto('transform-function-whitespace');
else emit() && switchto('data') && reconsume();
break;
case "transform-function-whitespace":
if(whitespace(code)) donothing();
else if(code == 0x28) emit(new FunctionToken(currtoken)) && switchto('data');
else emit() && switchto('data') && reconsume();
break;
case "number":
create(new NumberToken());
if(code == 0x2d) {
if(digit(next())) consume() && currtoken.append([0x2d,code]) && switchto('number-rest');
else if(next(1) == 0x2e && digit(next(2))) consume(2) && currtoken.append([0x2d,0x2e,code]) && switchto('number-fraction');
else switchto('data') && reconsume();
}
else if(code == 0x2b) {
if(digit(next())) consume() && currtoken.append([0x2b,code]) && switchto('number-rest');
else if(next(1) == 0x2e && digit(next(2))) consume(2) && currtoken.append([0x2b,0x2e,code]) && switchto('number-fraction');
else switchto('data') && reconsume();
}
else if(digit(code)) currtoken.append(code) && switchto('number-rest');
else if(code == 0x2e) {
if(digit(next())) consume() && currtoken.append([0x2e,code]) && switchto('number-fraction');
else switchto('data') && reconsume();
}
else switchto('data') && reconsume();
break;
case "number-rest":
if(digit(code)) currtoken.append(code);
else if(code == 0x2e) {
if(digit(next())) consume() && currtoken.append([0x2e,code]) && switchto('number-fraction');
else emit() && switchto('data') && reconsume();
}
else if(code == 0x25) emit(new PercentageToken(currtoken)) && switchto('data') && reconsume();
else if(code == 0x45 || code == 0x65) {
if(!options.scientificNotation) create(new DimensionToken(currtoken,code)) && switchto('dimension');
else if(digit(next())) consume() && currtoken.append([0x25,code]) && switchto('sci-notation');
else if((next(1) == 0x2b || next(1) == 0x2d) && digit(next(2))) currtoken.append([0x25,next(1),next(2)]) && consume(2) && switchto('sci-notation');
else create(new DimensionToken(currtoken,code)) && switchto('dimension');
}
else if(code == 0x2d) {
if(namestartchar(next())) consume() && create(new DimensionToken(currtoken,[0x2d,code])) && switchto('dimension');
else if(next(1) == 0x5c && badescape(next(2))) parseerror() && emit() && switchto('data') && reconsume();
else if(next(1) == 0x5c) consume() && create(new DimensionToken(currtoken, [0x2d,consumeEscape()])) && switchto('dimension');
else emit() && switchto('data') && reconsume();
}
else if(namestartchar(code)) create(new DimensionToken(currtoken, code)) && switchto('dimension');
else if(code == 0x5c) {
if(badescape(next)) emit() && switchto('data') && reconsume();
else create(new DimensionToken(currtoken,consumeEscape)) && switchto('dimension');
}
else emit() && switchto('data') && reconsume();
break;
case "number-fraction":
currtoken.type = "number";
if(digit(code)) currtoken.append(code);
else if(code == 0x2e) emit() && switchto('data') && reconsume();
else if(code == 0x25) emit(new PercentageToken(currtoken)) && switchto('data') && reconsume();
else if(code == 0x45 || code == 0x65) {
if(!options.scientificNotation) create(new DimensionToken(currtoken,code)) && switchto('dimension');
else if(digit(next())) consume() && currtoken.append([0x25,code]) && switchto('sci-notation');
else if((next(1) == 0x2b || next(1) == 0x2d) && digit(next(2))) currtoken.append([0x25,next(1),next(2)]) && consume(2) && switchto('sci-notation');
else create(new DimensionToken(currtoken,code)) && switchto('dimension');
}
else if(code == 0x2d) {
if(namestartchar(next())) consume() && create(new DimensionToken(currtoken,[0x2d,code])) && switchto('dimension');
else if(next(1) == 0x5c && badescape(next(2))) parseerror() && emit() && switchto('data') && reconsume();
else if(next(1) == 0x5c) consume() && create(new DimensionToken(currtoken, [0x2d,consumeEscape()])) && switchto('dimension');
else emit() && switchto('data') && reconsume();
}
else if(namestartchar(code)) create(new DimensionToken(currtoken, code)) && switchto('dimension');
else if(code == 0x5c) {
if(badescape(next)) emit() && switchto('data') && reconsume();
else create(new DimensionToken(currtoken,consumeEscape)) && switchto('dimension');
}
else emit() && switchto('data') && reconsume();
break;
case "dimension":
if(namechar(code)) currtoken.append(code);
else if(code == 0x5c) {
if(badescape(next())) parseerror() && emit() && switchto('data') && reconsume();
else currtoken.append(consumeEscape());
}
else emit() && switchto('data') && reconsume();
break;
case "sci-notation":
if(digit(code)) currtoken.append(code);
else emit() && switchto('data') && reconsume();
break;
case "url":
if(code == 0x22) switchto('url-double-quote');
else if(code == 0x27) switchto('url-single-quote');
else if(code == 0x29) emit(new URLToken) && switchto('data');
else if(whitespace(code)) donothing();
else switchto('url-unquoted') && reconsume();
break;
case "url-double-quote":
if(currtoken == undefined) create(new URLToken);
if(code == 0x22) switchto('url-end');
else if(newline(code)) parseerror() && switchto('bad-url');
else if(code == 0x5c) {
if(newline(next())) consume();
else if(badescape(next())) parseerror() && emit(new BadURLToken) && switchto('data') && reconsume();
else currtoken.append(consumeEscape());
}
else currtoken.append(code);
break;
case "url-single-quote":
if(currtoken == undefined) create(new URLToken);
if(code == 0x27) switchto('url-end');
else if(newline(code)) parseerror() && switchto('bad-url');
else if(code == 0x5c) {
if(newline(next())) consume();
else if(badescape(next())) parseerror() && emit(new BadURLToken) && switchto('data') && reconsume();
else currtoken.append(consumeEscape());
}
else currtoken.append(code);
break;
case "url-end":
if(whitespace(code)) donothing();
else if(code == 0x29) emit() && switchto('data');
else parseerror() && switchto('bad-url') && reconsume();
break;
case "url-unquoted":
if(currtoken == undefined) create(new URLToken);
if(whitespace(code)) switchto('url-end');
else if(code == 0x29) emit() && switchto('data');
else if(code == 0x22 || code == 0x27 || code == 0x28 || nonprintable(code)) parseerror() && switchto('bad-url');
else if(code == 0x5c) {
if(badescape(next())) parseerror() && switchto('bad-url');
else currtoken.append(consumeEscape());
}
else currtoken.append(code);
break;
case "bad-url":
if(code == 0x29) emit(new BadURLToken) && switchto('data');
else if(code == 0x5c) {
if(badescape(next())) donothing();
else consumeEscape()
}
else donothing();
break;
case "unicode-range":
// We already know that the current code is a hexdigit.
var start = [code], end = [code];
for(var total = 1; total < 6; total++) {
if(hexdigit(next())) {
consume();
start.push(code);
end.push(code);
}
else break;
}
if(next() == 0x3f) {
for(;total < 6; total++) {
if(next() == 0x3f) {
consume();
start.push("0".charCodeAt(0));
end.push("f".charCodeAt(0));
}
else break;
}
emit(new UnicodeRangeToken(start,end)) && switchto('data');
}
else if(next(1) == 0x2d && hexdigit(next(2))) {
consume();
consume();
end = [code];
for(var total = 1; total < 6; total++) {
if(hexdigit(next())) {
consume();
end.push(code);
}
else break;
}
emit(new UnicodeRangeToken(start,end)) && switchto('data');
}
else emit(new UnicodeRangeToken(start)) && switchto('data');
break;
default:
console.log("Unknown state '" + state + "'");
}
}
}
function stringFromCodeArray(arr) {
return String.fromCharCode.apply(null,arr.filter(function(e){return e;}));
}
function CSSParserToken(options) { return this; }
CSSParserToken.prototype.finish = function() { return this; }
CSSParserToken.prototype.toString = function() { return this.tokenType; }
CSSParserToken.prototype.toJSON = function() { return this.toString(); }
function BadStringToken() { return this; }
BadStringToken.prototype = new CSSParserToken;
BadStringToken.prototype.tokenType = "BADSTRING";
function BadURLToken() { return this; }
BadURLToken.prototype = new CSSParserToken;
BadURLToken.prototype.tokenType = "BADURL";
function WhitespaceToken() { return this; }
WhitespaceToken.prototype = new CSSParserToken;
WhitespaceToken.prototype.tokenType = "WHITESPACE";
WhitespaceToken.prototype.toString = function() { return "WS"; }
function CDOToken() { return this; }
CDOToken.prototype = new CSSParserToken;
CDOToken.prototype.tokenType = "CDO";
function CDCToken() { return this; }
CDCToken.prototype = new CSSParserToken;
CDCToken.prototype.tokenType = "CDC";
function ColonToken() { return this; }
ColonToken.prototype = new CSSParserToken;
ColonToken.prototype.tokenType = ":";
function SemicolonToken() { return this; }
SemicolonToken.prototype = new CSSParserToken;
SemicolonToken.prototype.tokenType = ";";
function OpenCurlyToken() { return this; }
OpenCurlyToken.prototype = new CSSParserToken;
OpenCurlyToken.prototype.tokenType = "{";
function CloseCurlyToken() { return this; }
CloseCurlyToken.prototype = new CSSParserToken;
CloseCurlyToken.prototype.tokenType = "}";
function OpenSquareToken() { return this; }
OpenSquareToken.prototype = new CSSParserToken;
OpenSquareToken.prototype.tokenType = "[";
function CloseSquareToken() { return this; }
CloseSquareToken.prototype = new CSSParserToken;
CloseSquareToken.prototype.tokenType = "]";
function OpenParenToken() { return this; }
OpenParenToken.prototype = new CSSParserToken;
OpenParenToken.prototype.tokenType = "(";
function CloseParenToken() { return this; }
CloseParenToken.prototype = new CSSParserToken;
CloseParenToken.prototype.tokenType = ")";
function EOFToken() { return this; }
EOFToken.prototype = new CSSParserToken;
EOFToken.prototype.tokenType = "EOF";
function DelimToken(code) {
this.value = String.fromCharCode(code);
return this;
}
DelimToken.prototype = new CSSParserToken;
DelimToken.prototype.tokenType = "DELIM";
DelimToken.prototype.toString = function() { return "DELIM("+this.value+")"; }
function StringValuedToken() { return this; }
StringValuedToken.prototype = new CSSParserToken;
StringValuedToken.prototype.append = function(val) {
if(val instanceof Array) {
for(var i = 0; i < val.length; i++) {
this.value.push(val[i]);
}
} else {
this.value.push(val);
}
return true;
}
StringValuedToken.prototype.finish = function() {
this.value = stringFromCodeArray(this.value);
return this;
}
function IdentifierToken(val) {
this.value = [];
this.append(val);
}
IdentifierToken.prototype = new StringValuedToken;
IdentifierToken.prototype.tokenType = "IDENT";
IdentifierToken.prototype.toString = function() { return "IDENT("+this.value+")"; }
function FunctionToken(val) {
// These are always constructed by passing an IdentifierToken
this.value = val.finish().value;
}
FunctionToken.prototype = new CSSParserToken;
FunctionToken.prototype.tokenType = "FUNCTION";
FunctionToken.prototype.toString = function() { return "FUNCTION("+this.value+")"; }
function AtKeywordToken(val) {
this.value = [];
this.append(val);
}
AtKeywordToken.prototype = new StringValuedToken;
AtKeywordToken.prototype.tokenType = "AT-KEYWORD";
AtKeywordToken.prototype.toString = function() { return "AT("+this.value+")"; }
function HashToken(val) {
this.value = [];
this.append(val);
}
HashToken.prototype = new StringValuedToken;
HashToken.prototype.tokenType = "HASH";
HashToken.prototype.toString = function() { return "HASH("+this.value+")"; }
function StringToken(val) {
this.value = [];
this.append(val);
}
StringToken.prototype = new StringValuedToken;
StringToken.prototype.tokenType = "STRING";
StringToken.prototype.toString = function() { return "\""+this.value+"\""; }
function URLToken(val) {
this.value = [];
this.append(val);
}
URLToken.prototype = new StringValuedToken;
URLToken.prototype.tokenType = "URL";
URLToken.prototype.toString = function() { return "URL("+this.value+")"; }
function NumberToken(val) {
this.value = [];
this.append(val);
this.type = "integer";
}
NumberToken.prototype = new StringValuedToken;
NumberToken.prototype.tokenType = "NUMBER";
NumberToken.prototype.toString = function() {
if(this.type == "integer")
return "INT("+this.value+")";
return "NUMBER("+this.value+")";
}
NumberToken.prototype.finish = function() {
this.repr = stringFromCodeArray(this.value);
this.value = this.repr * 1;
if(Math.abs(this.value) % 1 != 0) this.type = "number";
return this;
}
function PercentageToken(val) {
// These are always created by passing a NumberToken as val
val.finish();
this.value = val.value;
this.repr = val.repr;
}
PercentageToken.prototype = new CSSParserToken;
PercentageToken.prototype.tokenType = "PERCENTAGE";
PercentageToken.prototype.toString = function() { return "PERCENTAGE("+this.value+")"; }
function DimensionToken(val,unit) {
// These are always created by passing a NumberToken as the val
val.finish();
this.num = val.value;
this.unit = [];
this.repr = val.repr;
this.append(unit);
}
DimensionToken.prototype = new CSSParserToken;
DimensionToken.prototype.tokenType = "DIMENSION";
DimensionToken.prototype.toString = function() { return "DIM("+this.num+","+this.unit+")"; }
DimensionToken.prototype.append = function(val) {
if(val instanceof Array) {
for(var i = 0; i < val.length; i++) {
this.unit.push(val[i]);
}
} else {
this.unit.push(val);
}
return true;
}
DimensionToken.prototype.finish = function() {
this.unit = stringFromCodeArray(this.unit);
this.repr += this.unit;
return this;
}
function UnicodeRangeToken(start,end) {
// start and end are array of char codes, completely finished
start = parseInt(stringFromCodeArray(start),16);
if(end === undefined) end = start + 1;
else end = parseInt(stringFromCodeArray(end),16);
if(start > maximumallowedcodepoint) end = start;
if(end < start) end = start;
if(end > maximumallowedcodepoint) end = maximumallowedcodepoint;
this.start = start;
this.end = end;
return this;
}
UnicodeRangeToken.prototype = new CSSParserToken;
UnicodeRangeToken.prototype.tokenType = "UNICODE-RANGE";
UnicodeRangeToken.prototype.toString = function() {
if(this.start+1 == this.end)
return "UNICODE-RANGE("+this.start.toString(16).toUpperCase()+")";
if(this.start < this.end)
return "UNICODE-RANGE("+this.start.toString(16).toUpperCase()+"-"+this.end.toString(16).toUpperCase()+")";
return "UNICODE-RANGE()";
}
UnicodeRangeToken.prototype.contains = function(code) {
return code >= this.start && code < this.end;
}
// Exportation.
// TODO: also export the various tokens objects?
module.exports = tokenize;
}));

View File

@ -770,7 +770,7 @@ Editor.prototype = {
extend: function (funcs) {
Object.keys(funcs).forEach((name) => {
let cm = editors.get(this);
let ctx = { ed: this, cm: cm };
let ctx = { ed: this, cm: cm, Editor: Editor};
if (name === "initialize") {
funcs[name](ctx);

View File

@ -9,6 +9,9 @@ TEST_DIRS += ['test']
JS_MODULES_PATH = 'modules/devtools/sourceeditor'
EXTRA_JS_MODULES += [
'autocomplete.js',
'css-autocompleter.js',
'css-tokenizer.js',
'debugger.js',
'editor.js'
]

View File

@ -9,6 +9,9 @@ support-files =
cm_emacs_test.js
cm_vim_test.js
codemirror.html
css_statemachine_testcases.css
css_statemachine_tests.json
css_autocompletion_tests.json
vimemacs.html
head.js
@ -19,5 +22,7 @@ support-files =
[browser_editor_movelines.js]
[browser_editor_addons.js]
[browser_codemirror.js]
[browser_css_autocompletion.js]
[browser_css_statemachine.js]
[browser_vimemacs.js]
[browser_sourceeditor_initialization.js]

View File

@ -0,0 +1,146 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const cssAutoCompleter = require("devtools/sourceeditor/css-autocompleter");
const {InspectorFront} = require("devtools/server/actors/inspector");
const { Cc, Ci } = require("chrome");
const CSS_URI = "http://mochi.test:8888/browser/browser/devtools/sourceeditor" +
"/test/css_statemachine_testcases.css";
const TESTS_URI = "http://mochi.test:8888/browser/browser/devtools/sourceeditor" +
"/test/css_autocompletion_tests.json";
const source = read(CSS_URI);
const tests = eval(read(TESTS_URI));
const TEST_URI = "data:text/html;charset=UTF-8," + encodeURIComponent(
["<!DOCTYPE html>",
"<html>",
" <head>",
" <title>CSS State machine tests.</title>",
" <style type='text/css'>",
"#progress {",
" width: 500px; height: 30px;",
" border: 1px solid black;",
" position: relative",
"}",
"#progress div {",
" width: 0%; height: 100%;",
" background: green;",
" position: absolute;",
" z-index: -1; top: 0",
"}",
"#progress.failed div {",
" background: red !important;",
"}",
"#progress.failed:after {",
" content: 'Some tests failed';",
" color: white",
"}",
"#progress:before {",
" content: 'Running test ' attr(data-progress) ' of " + tests.length + "';",
" color: white;",
" text-shadow: 0 0 2px darkgreen;",
"}",
" </style>",
" </head>",
" <body>",
" <h2>State machine tests for CSS autocompleter.</h2><br>",
" <div id='progress' data-progress='0'>",
" <div></div>",
" </div>",
" <div id='devtools-menu' class='devtools-toolbarbutton'></div>",
" <div id='devtools-toolbarbutton' class='devtools-menulist'></div>",
" <div id='devtools-anotherone'></div>",
" <div id='devtools-yetagain'></div>",
" <div id='devtools-itjustgoeson'></div>",
" <div id='devtools-okstopitnow'></div>",
" <div class='hidden-labels-box devtools-toolbarbutton devtools-menulist'></div>",
" <div class='devtools-menulist'></div>",
" <div class='devtools-menulist'></div>",
" <tabs class='devtools-toolbarbutton'><tab></tab><tab></tab><tab></tab></tabs><tabs></tabs>",
" <button class='category-name visible'></button>",
" <div class='devtools-toolbarbutton' label='true'>",
" <hbox class='toolbarbutton-menubutton-button'></hbox></div>",
" </body>",
" </html>"
].join("\n"));
let doc = null;
let index = 0;
let completer = null;
let progress;
let progressDiv;
let inspector;
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
doc = content.document;
runTests();
}, true);
content.location = TEST_URI;
}
function runTests() {
progress = doc.getElementById("progress");
progressDiv = doc.querySelector("#progress > div");
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
target.makeRemote().then(() => {
inspector = InspectorFront(target.client, target.form);
inspector.getWalker().then(walker => {
completer = new cssAutoCompleter({walker: walker});
checkStateAndMoveOn();
});
});
}
function checkStateAndMoveOn() {
if (index == tests.length) {
finishUp();
return;
}
let test = tests[index];
progress.dataset.progress = ++index;
progressDiv.style.width = 100*index/tests.length + "%";
completer.complete(limit(source, test[0]),
{line: test[0][0], ch: test[0][1]}).then(suggestions => {
checkState(test[1], suggestions);
}).then(checkStateAndMoveOn);
}
function checkState(expected, actual) {
if (expected.length != actual.length) {
ok(false, "Number of suggestions did not match up for state " + index +
". Expected: " + expected.length + ", Actual: " + actual.length);
progress.classList.add("failed");
return;
}
for (let i = 0; i < actual.length; i++) {
if (expected[i] != actual[i].label) {
ok (false, "Suggestion " + i + " of state " + index + " did not match up" +
". Expected: " + expected[i] + ". Actual: " + actual[i].label);
return;
}
}
ok(true, "Test " + index + " passed. ");
}
function finishUp() {
completer.walker.release().then(() => {
inspector.destroy();
inspector = null;
completer = null;
});
progress = null;
progressDiv = null;
gBrowser.removeCurrentTab();
finish();
}

View File

@ -0,0 +1,113 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const cssAutoCompleter = require("devtools/sourceeditor/css-autocompleter");
const { Cc, Ci } = require("chrome");
const CSS_URI = "http://mochi.test:8888/browser/browser/devtools/sourceeditor" +
"/test/css_statemachine_testcases.css";
const TESTS_URI = "http://mochi.test:8888/browser/browser/devtools/sourceeditor" +
"/test/css_statemachine_tests.json";
const source = read(CSS_URI);
const tests = eval(read(TESTS_URI));
const TEST_URI = "data:text/html;charset=UTF-8," + encodeURIComponent(
["<!DOCTYPE html>",
"<html>",
" <head>",
" <title>CSS State machine tests.</title>",
" <style type='text/css'>",
"#progress {",
" width: 500px; height: 30px;",
" border: 1px solid black;",
" position: relative",
"}",
"#progress div {",
" width: 0%; height: 100%;",
" background: green;",
" position: absolute;",
" z-index: -1; top: 0",
"}",
"#progress.failed div {",
" background: red !important;",
"}",
"#progress.failed:after {",
" content: 'Some tests failed';",
" color: white",
"}",
"#progress:before {",
" content: 'Running test ' attr(data-progress) ' of " + tests.length + "';",
" color: white;",
" text-shadow: 0 0 2px darkgreen;",
"}",
" </style>",
" </head>",
" <body>",
" <h2>State machine tests for CSS autocompleter.</h2><br>",
" <div id='progress' data-progress='0'>",
" <div></div>",
" </div>",
" </body>",
" </html>"
].join("\n"));
let doc = null;
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
doc = content.document;
runTests();
}, true);
content.location = TEST_URI;
}
function runTests() {
let completer = new cssAutoCompleter();
let checkState = state => {
if (state[0] == 'null' && (!completer.state || completer.state == 'null')) {
return true;
} else if (state[0] == completer.state && state[0] == 'selector' &&
state[1] == completer.selectorState &&
state[2] == completer.completing &&
state[3] == completer.selector) {
return true;
} else if (state[0] == completer.state && state[0] == 'value' &&
state[2] == completer.completing &&
state[3] == completer.propertyName) {
return true;
} else if (state[0] == completer.state &&
state[2] == completer.completing &&
state[0] != 'selector' && state[0] != 'value') {
return true;
}
return false;
};
let progress = doc.getElementById("progress");
let progressDiv = doc.querySelector("#progress > div");
let i = 0;
for (let test of tests) {
progress.dataset.progress = ++i;
progressDiv.style.width = 100*i/tests.length + "%";
completer.resolveState(limit(source, test[0]),
{line: test[0][0], ch: test[0][1]});
if (checkState(test[1])) {
ok(true, "Test " + i + " passed. ");
}
else {
ok(false, "Test " + i + " failed. Expected state : [" + test[1] + "] but" +
" found [" + completer.state + ", " + completer.selectorState + ", " +
completer.completing + ", " +
(completer.propertyName || completer.selector) + "].");
progress.classList.add("failed");
}
}
gBrowser.removeCurrentTab();
finish();
}

View File

@ -0,0 +1,35 @@
// Test states to be tested for css state machine in css-autocompelter.js file.
// Test cases are of the following format:
// [
// [
// line, // The line location of the cursor
// ch // The column locaiton of the cursor
// ],
// suggestions // Array of expected results
// ]
[
[[0, 10], []],
[[4, 7], ['.devtools-menulist', '.devtools-toolbarbutton']],
[[5, 8], ['-moz-animation', '-moz-animation-delay', '-moz-animation-direction',
'-moz-animation-duration', '-moz-animation-fill-mode',
'-moz-animation-iteration-count', '-moz-animation-name',
'-moz-animation-play-state', '-moz-animation-timing-function',
'-moz-appearance']],
[[12, 20], ['none', 'number-input']],
[[12, 22], ['none']],
[[17, 22], ['hsl', 'hsla']],
[[22, 5], ['color', 'color-interpolation', 'color-interpolation-filters']],
[[25, 26], ['.devtools-toolbarbutton > tab',
'.devtools-toolbarbutton > .toolbarbutton-menubutton-button',
'.devtools-toolbarbutton > hbox']],
[[25, 31], ['.devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button']],
[[29, 20], ['.devtools-menulist:after', '.devtools-menulist:active']],
[[30, 10], ['#devtools-anotherone', '#devtools-itjustgoeson', '#devtools-menu',
'#devtools-okstopitnow', '#devtools-toolbarbutton', '#devtools-yetagain']],
[[39, 39], ['.devtools-toolbarbutton:not([label]) > tab']],
[[43, 51], ['.devtools-toolbarbutton:not([checked=true]):hover:after',
'.devtools-toolbarbutton:not([checked=true]):hover:active']],
[[58, 36], ['!important;']],
[[73, 42], [':last-child', ':lang(', ':last-of-type', ':link']],
[[77, 25], ['.visible']],
]

View File

@ -0,0 +1,121 @@
/* 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/. */
.devtools-toolbar {
-moz-appearance: none;
padding:4px 3px;border-bottom-width: 1px;
border-bottom-style: solid;
}
#devtools-menu.devtools-menulist,
.devtools-toolbarbutton#devtools-menu {
-moz-appearance: none;
-moz-box-align: center;
min-width: 78px;
min-height: 22px;
text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
border: 1px solid hsla(210,8%,5%,.45);
border-radius: 3px;
background: linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box;
box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15);
margin: 0 3px;
color: inherit;
}
.devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button {
-moz-box-orient: horizontal;
}
.devtools-menulist:active,
#devtools-toolbarbutton:focus {
outline: 1px dotted hsla(210,30%,85%,0.7);
outline-offset : -4px;
}
.devtools-toolbarbutton:not([label]) {
min-width: 32px;
}
.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
display: none;
}
.devtools-toolbarbutton:not([checked=true]):hover:active {
border-color: hsla(210,8%,5%,.6);
}
.devtools-menulist["open" ="true"],
.devtools-toolbarbutton["open" = true],
.devtools-toolbarbutton[checked= "true"] {
border-color: hsla(210,8%,5%,.6) !important;
}
.devtools-toolbarbutton["checked"="true"] {
color: hsl(208,100%,60%);
}
.devtools-toolbarbutton[checked=true]:hover {
background-color: transparent !important;
}
.devtools-toolbarbutton[checked=true]:hover:active {
background-color: hsla(210,8%,5%,.2) !important;
}
.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
-moz-appearance: none;
}
.devtools-sidebar-tabs > tabs > tab:first-of-type {
-moz-margin-start: -3px;
}
.devtools-sidebar-tabs > tabs > tab:not(:last-of-type) {
background-size: calc(100% - 2px) 100%, 1px 100%;
}
.hidden-labels-box:not(.visible) > label,
.hidden-labels-box.visible ~ .hidden-labels-box > label:last-child {
display: none;
}
/* Maximize the size of the viewport when the window is small */
@media (max-width: 800px) {
.category-name {
display: none;
}
}
@media all and (min-width: 300px) {
#error-box {
max-width: 50%;
margin: 0 auto;
background-image: url('chrome://global/skin/icons/information-32.png');
min-height: 36px;
-moz-padding-start: 38px;
}
button {
width: auto !important;
min-width: 150px;
}
@keyframes downloadsIndicatorNotificationFinish {
from { opacity: 0; transform: scale(1); }
20% {
opacity: .65;
animation-timing-function: ease-in;
} to { opacity: 0;
transform: scale(8); }
}
}
@keyframes smooth {
from { opacity: 0; transform: scale(1); }
20% { opacity: .65; animation-timing-function: ease-in; }
to {
opacity : 0;
transform: scale(8);
}
}

View File

@ -0,0 +1,84 @@
// Test states to be tested for css state machine in css-autocompelter.js file.
// Test cases are of the following format:
// [
// [
// line, // The line location of the cursor
// ch // The column locaiton of the cursor
// ],
// [
// state, // one of CSS_STATES
// selectorState, // one of SELECTOR_STATES
// completing, // what is being completed
// propertyName, // what property is being completed in case of value state
// // or the current selector that is being completed
// ]
// ]
[
[[0, 10], ['null', '', '', '']],
[[4, 3], ['selector', 'class', 'de', '.de']],
[[5, 8], ['property', 'null', '-moz-a']],
[[5, 21], ['value', 'null', 'no', '-moz-appearance']],
[[6, 18], ['property', 'null', 'padding']],
[[6, 24], ['value', 'null', '3', 'padding']],
[[6, 29], ['property', 'null', 'bo']],
[[6, 50], ['value', 'null', '1p', 'border-bottom-width']],
[[7, 24], ['value', 'null', 's', 'border-bottom-style']],
[[9, 0], ['null', 'null', '', '']],
[[10, 6], ['selector', 'id', 'devto', '#devto']],
[[10, 17], ['selector', 'class', 'de', '#devtools-menu.de']],
[[11, 5], ['selector', 'class', 'devt', '.devt']],
[[11, 30], ['selector', 'id', 'devtoo', '.devtools-toolbarbutton#devtoo']],
[[12, 10], ['property', 'null', '-moz-app']],
[[16, 27], ['value', 'null', 'hsl', 'text-shadow']],
[[19, 24], ['value', 'null', 'linear-gra', 'background']],
[[19, 55], ['value', 'null', 'hsl', 'background']],
[[19, 79], ['value', 'null', 'paddin', 'background']],
[[20, 47], ['value', 'null', 'ins', 'box-shadow']],
[[22, 15], ['value', 'null', 'inheri', 'color']],
[[25, 26], ['selector', 'null', '', '.devtools-toolbarbutton > ']],
[[25, 28], ['selector', 'tag', 'hb', '.devtools-toolbarbutton > hb']],
[[25, 41], ['selector', 'class', 'toolbarbut', '.devtools-toolbarbutton > hbox.toolbarbut']],
[[29, 21], ['selector', 'pseudo', 'ac', '.devtools-menulist:ac']],
[[30, 27], ['selector', 'pseudo', 'foc', '#devtools-toolbarbutton:foc']],
[[31, 18], ['value', 'null', 'dot', 'outline']],
[[32, 25], ['value', 'null', '-4p', 'outline-offset']],
[[35, 26], ['selector', 'pseudo', 'no', '.devtools-toolbarbutton:no']],
[[35, 28], ['selector', 'null', 'not', '']],
[[35, 30], ['selector', 'attribute', 'l', '[l']],
[[39, 46], ['selector', 'class', 'toolba', '.devtools-toolbarbutton:not([label]) > .toolba']],
[[43, 39], ['selector', 'value', 'tr', '[checked=tr']],
[[43, 47], ['selector', 'pseudo', 'hov', '.devtools-toolbarbutton:not([checked=true]):hov']],
[[43, 53], ['selector', 'pseudo', 'act', '.devtools-toolbarbutton:not([checked=true]):hover:act']],
[[47, 22], ['selector', 'attribute', 'op', '.devtools-menulist[op']],
[[47, 33], ['selector', 'value', 'tr', '.devtools-menulist[open =tr']],
[[48, 38], ['selector', 'value', 'tr', '.devtools-toolbarbutton[open = tr']],
[[49, 40], ['selector', 'value', 'true', '.devtools-toolbarbutton[checked= true']],
[[53, 34], ['selector', 'value', '=', '.devtools-toolbarbutton[checked=']],
[[58, 38], ['value', 'null', '!impor', 'background-color']],
[[61, 41], ['selector', 'pseudo', 'hov', '.devtools-toolbarbutton[checked=true]:hov']],
[[65, 47], ['selector', 'class', 'to', '.devtools-toolbarbutton[type=menu-button] > .to']],
[[69, 44], ['selector', 'pseudo', 'first-of', '.devtools-sidebar-tabs > tabs > tab:first-of']],
[[73, 45], ['selector', 'pseudo', 'last', ':last']],
[[77, 27], ['selector', 'class', 'vis', '.vis']],
[[78, 34], ['selector', 'class', 'hidd', '.hidden-labels-box.visible ~ .hidd']],
[[83, 5], ['media', 'null', 'medi']],
[[83, 22], ['media', 'null', '800']],
[[84, 9], ['selector', 'class', 'catego', '.catego']],
[[89, 9], ['media', 'null', 'al']],
[[90, 6], ['selector', 'id', 'err', '#err']],
[[93, 11], ['property', 'null', 'backgro']],
[[98, 6], ['selector', 'tag', 'butt', 'butt']],
[[99, 22], ['value', 'null', '!impor', 'width']],
[[103, 5], ['keyframes', 'null', 'ke']],
[[104, 7], ['frame', 'null', 'fro']],
[[104, 15], ['property', 'null', 'opac']],
[[104, 29], ['property', 'null', 'transf']],
[[104, 38], ['value', 'null', 'scal', 'transform']],
[[105, 8], ['frame', 'null', '']],
[[113, 6], ['keyframes', 'null', 'keyfr']],
[[114, 4], ['frame', 'null', 'fr']],
[[115, 3], ['frame', 'null', '2']],
[[117, 8], ['property', 'null', 'opac']],
[[117, 16], ['value', 'null', '0', 'opacity']],
[[121, 0], ['null', '', '']],
]

View File

@ -4,7 +4,8 @@
"use strict";
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const { require } = devtools;
const Editor = require("devtools/sourceeditor/editor");
function setup(cb) {
@ -44,4 +45,33 @@ function teardown(ed, win) {
ed.destroy();
win.close();
finish();
}
}
/**
* This method returns the portion of the input string `source` up to the
* [line, ch] location.
*/
function limit(source, [line, ch]) {
line++;
let list = source.split("\n");
if (list.length < line)
return source;
if (line == 1)
return list[0].slice(0, ch);
return [...list.slice(0, line - 1), list[line - 1].slice(0, ch)].join("\n");
}
function read(url) {
let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
.getService(Ci.nsIScriptableInputStream);
let channel = Services.io.newChannel(url, null, null);
let input = channel.open();
scriptableStream.init(input);
let data = scriptableStream.read(input.available());
scriptableStream.close();
input.close();
return data;
}

View File

@ -16,6 +16,7 @@ Cu.import("resource://gre/modules/PluralForm.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
Cu.import("resource:///modules/devtools/gDevTools.jsm");
Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
Cu.import("resource:///modules/devtools/SplitView.jsm");
Cu.import("resource:///modules/devtools/StyleSheetEditor.jsm");
@ -60,15 +61,6 @@ function StyleEditorUI(debuggee, target, panelDoc) {
this._clear = this._clear.bind(this);
this._onError = this._onError.bind(this);
this.createUI();
this._debuggee.getStyleSheets().then((styleSheets) => {
this._resetStyleSheetList(styleSheets);
this._target.on("will-navigate", this._clear);
this._target.on("navigate", this._onNewDocument);
});
this._prefObserver = new PrefObserver("devtools.styleeditor.");
this._prefObserver.on(PREF_ORIG_SOURCES, this._onNewDocument);
}
@ -79,8 +71,7 @@ StyleEditorUI.prototype = {
*
* @return boolean
*/
get isDirty()
{
get isDirty() {
if (this._markedDirty === true) {
return true;
}
@ -104,6 +95,24 @@ StyleEditorUI.prototype = {
this.selectedEditor.styleSheet.styleSheetIndex : -1;
},
/**
* Initiates the style editor ui creation and the inspector front to get
* reference to the walker.
*/
initialize: function() {
let toolbox = gDevTools.getToolbox(this._target);
return toolbox.initInspector().then(() => {
this._walker = toolbox.walker;
}).then(() => this.createUI())
.then(() => this._debuggee.getStyleSheets())
.then((styleSheets) => {
this._resetStyleSheetList(styleSheets);
this._target.on("will-navigate", this._clear);
this._target.on("navigate", this._onNewDocument);
});
},
/**
* Build the initial UI and wire buttons with event handlers.
*/
@ -236,7 +245,8 @@ StyleEditorUI.prototype = {
* Optional if stylesheet is a new sheet created by user
*/
_addStyleSheetEditor: function(styleSheet, file, isNew) {
let editor = new StyleSheetEditor(styleSheet, this._window, file, isNew);
let editor =
new StyleSheetEditor(styleSheet, this._window, file, isNew, this._walker);
editor.on("property-change", this._summaryChange.bind(this, editor));
editor.on("style-applied", this._summaryChange.bind(this, editor));
@ -258,8 +268,7 @@ StyleEditorUI.prototype = {
* @param {nsIWindow} parentWindow
* Optional parent window for the file picker.
*/
_importFromFile: function(file, parentWindow)
{
_importFromFile: function(file, parentWindow) {
let onFileSelected = function(file) {
if (!file) {
// nothing selected
@ -410,8 +419,9 @@ StyleEditorUI.prototype = {
this.switchToSelectedSheet();
}
// If this is the first stylesheet, select it
if (!this.selectedEditor
// If this is the first stylesheet and there is no pending request to
// select a particular style sheet, select this sheet.
if (!this.selectedEditor && !this._styleSheetBoundToSelect
&& editor.styleSheet.styleSheetIndex == 0) {
this._selectEditor(editor);
}
@ -443,6 +453,11 @@ StyleEditorUI.prototype = {
for each (let editor in this.editors) {
if (editor.styleSheet.href == sheet.href) {
// The _styleSheetBoundToSelect will always hold the latest pending
// requested style sheet (with line and column) which is not yet
// selected by the source editor. Only after we select that particular
// editor and go the required line and column, it will become null.
this._styleSheetBoundToSelect = this._styleSheetToSelect;
this._selectEditor(editor, sheet.line, sheet.col);
this._styleSheetToSelect = null;
return;
@ -466,6 +481,7 @@ StyleEditorUI.prototype = {
editor.getSourceEditor().then(() => {
editor.sourceEditor.setCursor({line: line, ch: col});
this._styleSheetBoundToSelect = null;
});
this.getEditorSummary(editor).then((summary) => {
@ -504,8 +520,7 @@ StyleEditorUI.prototype = {
* @param {Number} [col]
* Column to which the caret should be moved (zero-indexed).
*/
selectStyleSheet: function(href, line, col)
{
selectStyleSheet: function(href, line, col) {
this._styleSheetToSelect = {
href: href,
line: line,

View File

@ -15,6 +15,7 @@ const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devt
const Editor = require("devtools/sourceeditor/editor");
const promise = require("sdk/core/promise");
const {CssLogic} = require("devtools/styleinspector/css-logic");
const AutoCompleter = require("devtools/sourceeditor/autocomplete");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
@ -30,6 +31,9 @@ const SAVE_ERROR = "error-save";
// @see StyleEditor.updateStylesheet
const UPDATE_STYLESHEET_THROTTLE_DELAY = 500;
// Pref which decides if CSS autocompletion is enabled in Style Editor or not.
const AUTOCOMPLETION_PREF = "devtools.styleeditor.autocompletion-enabled";
/**
* StyleSheetEditor controls the editor linked to a particular StyleSheet
* object.
@ -47,8 +51,10 @@ const UPDATE_STYLESHEET_THROTTLE_DELAY = 500;
* Optional file that the sheet was imported from
* @param {boolean} isNew
* Optional whether the sheet was created by the user
* @param {Walker} walker
* Optional walker used for selectors autocompletion
*/
function StyleSheetEditor(styleSheet, win, file, isNew) {
function StyleSheetEditor(styleSheet, win, file, isNew, walker) {
EventEmitter.decorate(this);
this.styleSheet = styleSheet;
@ -57,6 +63,7 @@ function StyleSheetEditor(styleSheet, win, file, isNew) {
this._window = win;
this._isNew = isNew;
this.savedFile = file;
this.walker = walker;
this.errorMessage = null;
@ -197,6 +204,10 @@ StyleSheetEditor.prototype = {
let sourceEditor = new Editor(config);
sourceEditor.appendTo(inputElement).then(() => {
if (Services.prefs.getBoolPref(AUTOCOMPLETION_PREF)) {
sourceEditor.extend(AutoCompleter);
sourceEditor.setupAutoCompletion(this.walker);
}
sourceEditor.on("save", () => {
this.saveToFile();
});
@ -396,6 +407,9 @@ StyleSheetEditor.prototype = {
* Clean up for this editor.
*/
destroy: function() {
if (this.sourceEditor) {
this.sourceEditor.destroy();
}
this.styleSheet.off("property-change", this._onPropertyChange);
this.styleSheet.off("error", this._onError);
}

View File

@ -65,11 +65,13 @@ StyleEditorPanel.prototype = {
this._debuggee = StyleEditorFront(this.target.client, this.target.form);
}
this.UI = new StyleEditorUI(this._debuggee, this.target, this._panelDoc);
this.UI.on("error", this._showError);
this.UI.initialize().then(() => {
this.UI.on("error", this._showError);
this.isReady = true;
this.isReady = true;
deferred.resolve(this);
deferred.resolve(this);
});
}, console.error);
return deferred.promise;

View File

@ -0,0 +1,22 @@
<!doctype html>
<html>
<head>
<title>testcase for autocomplete testing</title>
<style type="text/css">
div {
font-size: 4em;
}
div > span {
text-decoration: underline;
}
div + button {
border: 2px dotted red;
}
</style>
</head>
<body>
<div>parent <span>child</span></div><button>sibling</button>
</body>
</html>

View File

@ -1,5 +1,6 @@
[DEFAULT]
support-files =
autocomplete.html
browser_styleeditor_cmd_edit.html
four.html
head.js
@ -26,6 +27,7 @@ support-files =
test_private.css
test_private.html
[browser_styleeditor_autocomplete.js]
[browser_styleeditor_bug_740541_iframes.js]
[browser_styleeditor_bug_851132_middle_click.js]
[browser_styleeditor_bug_870339.js]

View File

@ -0,0 +1,178 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "autocomplete.html";
const MAX_SUGGESTIONS = 15;
// Pref which decides if CSS autocompletion is enabled in Style Editor or not.
const AUTOCOMPLETION_PREF = "devtools.styleeditor.autocompletion-enabled";
// Test cases to test that autocompletion works correctly when enabled.
// Format:
// [
// -1 for pressing Ctrl + Space or the particular key to press,
// Number of suggestions in the popup (-1 if popup is closed),
// Index of selected suggestion,
// 1 to check whether the selected suggestion is inserted into the editor or not
// ]
let TEST_CASES = [
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
[-1, 1, 0],
['VK_DOWN', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
[-1, MAX_SUGGESTIONS, 0],
['VK_END', -1],
['VK_RETURN', -1],
['b', MAX_SUGGESTIONS, 0],
['a', 11, 0],
['VK_TAB', 11, 0, 1],
['VK_TAB', 11, 1, 1],
[':', -1],
['b', 9, 0],
['l', 4, 0],
['VK_TAB', 4, 0, 1],
['VK_TAB', 4, 1, 1],
['VK_TAB', 4, 2, 1],
['VK_DOWN', -1],
['VK_RETURN', -1],
['b', 2, 0],
['u', 1, 0],
['VK_TAB', -1],
['{', -1],
['VK_HOME', -1],
['VK_DOWN', -1],
['VK_DOWN', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
[-1, 1, 0],
];
let gEditor;
let gPopup;
let index = 0;
function test()
{
waitForExplicitFinish();
addTabAndOpenStyleEditor(function(panel) {
panel.UI.on("editor-added", testEditorAdded);
});
content.location = TESTCASE_URI;
}
function testEditorAdded(aEvent, aEditor) {
info("Editor added, getting the source editor and starting tests");
aEditor.getSourceEditor().then(editor => {
info("source editor found, starting tests.");
gEditor = editor.sourceEditor;
gPopup = gEditor.getAutocompletionPopup();
waitForFocus(testState, gPanelWindow);
});
}
function testState() {
if (index == TEST_CASES.length) {
testAutocompletionDisabled();
return;
}
let [key] = TEST_CASES[index];
let mods = {};
if (key == -1) {
info("pressing Ctrl + Space to get result: [" + TEST_CASES[index] +
"] for index " + index);
gEditor.once("after-suggest", checkState);
key = " ";
mods.accelKey = true;
}
else if (/(down|left|right|return|home|end)/ig.test(key)) {
info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
"] for index " + index);
gEditor.once("cursorActivity", checkState);
}
else if (key == "VK_TAB") {
info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
"] for index " + index);
gEditor.once("suggestion-entered", checkState);
}
else {
info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
"] for index " + index);
gEditor.once("after-suggest", checkState);
}
EventUtils.synthesizeKey(key, mods, gPanelWindow);
}
function checkState() {
executeSoon(() => {
info("After keypress for index " + index);
let [key, total, current, inserted] = TEST_CASES[index];
if (total != -1) {
ok(gPopup.isOpen, "Popup is open for index " + index);
is(total, gPopup.itemCount,
"Correct total suggestions for index " + index);
is(current, gPopup.selectedIndex,
"Correct index is selected for index " + index);
if (inserted) {
let { preLabel, label } = gPopup.getItemAtIndex(current);
let { line, ch } = gEditor.getCursor();
let lineText = gEditor.getText(line);
is(lineText.substring(ch - label.length, ch), label,
"Current suggestion from the popup is inserted into the editor.");
}
}
else {
ok(gPopup._panel.state != "open" && gPopup._panel.state != "showing",
"Popup is closed for index " + index);
}
index++;
testState();
});
}
function testAutocompletionDisabled() {
gBrowser.removeCurrentTab();
index = 0;
info("Starting test to check if autocompletion is disabled correctly.")
Services.prefs.setBoolPref(AUTOCOMPLETION_PREF, false);
addTabAndOpenStyleEditor(function(panel) {
panel.UI.on("editor-added", testEditorAddedDisabled);
});
content.location = TESTCASE_URI;
}
function testEditorAddedDisabled(aEvent, aEditor) {
info("Editor added, getting the source editor and starting tests");
aEditor.getSourceEditor().then(editor => {
ok(!editor.sourceEditor.getAutocompletionPopup,
"Autocompletion popup does not exist");
cleanup();
});
}
function cleanup() {
Services.prefs.clearUserPref(AUTOCOMPLETION_PREF);
gEditor = null;
gPopup = null;
finish();
}

View File

@ -105,9 +105,7 @@ function checkState(event) {
state);
}
else {
ok(editor.popup._panel.state == "open" ||
editor.popup._panel.state == "showing",
"Popup is open for state " + state);
ok(editor.popup.isOpen, "Popup is open for state " + state);
is(editor.popup.getItems().length, total,
"Number of suggestions match for state " + state);
is(editor.popup.selectedIndex, index,

View File

@ -117,9 +117,7 @@ function checkState(event) {
state);
}
else {
ok(editor.popup._panel.state == "open" ||
editor.popup._panel.state == "showing",
"Popup is open for state " + state);
ok(editor.popup.isOpen, "Popup is open for state " + state);
is(editor.popup.getItems().length, total,
"Number of suggestions match for state " + state);
is(editor.popup.selectedIndex, index,

View File

@ -114,9 +114,7 @@ function checkState(event) {
state);
}
else {
ok(editor.popup._panel.state == "open" ||
editor.popup._panel.state == "showing",
"Popup is open for state " + state);
ok(editor.popup.isOpen, "Popup is open for state " + state);
is(editor.popup.getItems().length, total,
"Number of suggestions match for state " + state);
is(editor.popup.selectedIndex, index,

View File

@ -37,18 +37,24 @@ const MAX_CLIPBOARD_DATA_SIZE = 8000;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/NetUtil.jsm');
Cu.import('resource://gre/modules/Promise.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
'resource://gre/modules/PrivateBrowsingUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'AddonManager',
'resource://gre/modules/AddonManager.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'ShumwayTelemetry',
'resource://shumway/ShumwayTelemetry.jsm');
let appInfo = Cc['@mozilla.org/xre/app-info;1'].getService(Ci.nsIXULAppInfo);
let Svc = {};
XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
'@mozilla.org/mime;1', 'nsIMIMEService');
let StringInputStream = Cc["@mozilla.org/io/string-input-stream;1"];
let MimeInputStream = Cc["@mozilla.org/network/mime-input-stream;1"];
function getBoolPref(pref, def) {
try {
return Services.prefs.getBoolPref(pref);
@ -188,6 +194,32 @@ function isShumwayEnabledFor(actions) {
return true;
}
function getVersionInfo() {
var deferred = Promise.defer();
var versionInfo = {
geckoMstone : 'unknown',
geckoBuildID: 'unknown',
shumwayVersion: 'unknown'
};
try {
versionInfo.geckoMstone = Services.prefs.getCharPref('gecko.mstone');
versionInfo.geckoBuildID = Services.prefs.getCharPref('gecko.buildID');
} catch (e) {
log('Error encountered while getting platform version info:', e);
}
try {
var addonId = "shumway@research.mozilla.org";
AddonManager.getAddonByID(addonId, function(addon) {
versionInfo.shumwayVersion = addon ? addon.version : 'n/a';
deferred.resolve(versionInfo);
});
} catch (e) {
log('Error encountered while getting Shumway version info:', e);
deferred.resolve(versionInfo);
}
return deferred.promise;
}
function fallbackToNativePlugin(window, userAction, activateCTP) {
var obj = window.frameElement;
var doc = obj.ownerDocument;
@ -366,7 +398,7 @@ ChromeActions.prototype = {
});
},
fallback: function(automatic) {
automatic = !!automatic; // cast to boolean
automatic = !!automatic;
fallbackToNativePlugin(this.window, !automatic, automatic);
},
setClipboard: function (data) {
@ -432,6 +464,28 @@ ChromeActions.prototype = {
break;
}
},
reportIssue: function(exceptions) {
var base = "http://shumway-issue-reporter.paas.allizom.org/input?";
var windowUrl = this.window.parent.wrappedJSObject.location + '';
var params = 'url=' + encodeURIComponent(windowUrl);
params += '&swf=' + encodeURIComponent(this.url);
getVersionInfo().then(function (versions) {
params += '&ffbuild=' + encodeURIComponent(versions.geckoMstone + ' (' +
versions.geckoBuildID + ')');
params += '&shubuild=' + encodeURIComponent(versions.shumwayVersion);
}).then(function () {
var postDataStream = StringInputStream.
createInstance(Ci.nsIStringInputStream);
postDataStream.data = 'exceptions=' + encodeURIComponent(exceptions);
var postData = MimeInputStream.createInstance(Ci.nsIMIMEInputStream);
postData.addHeader("Content-Type", "application/x-www-form-urlencoded");
postData.addContentLength = true;
postData.setData(postDataStream);
this.window.openDialog('chrome://browser/content', '_blank',
'all,dialog=no', base + params, null, null,
postData);
}.bind(this));
},
externalCom: function (data) {
if (!this.allowScriptAccess)
return;
@ -459,6 +513,9 @@ ChromeActions.prototype = {
case 'unregister':
return embedTag.__flash__unregisterCallback(data.functionName);
}
},
getWindowUrl: function() {
return this.window.parent.wrappedJSObject.location + '';
}
};

View File

@ -3875,6 +3875,13 @@ function createParsingContext(commitData) {
command: 'complete',
stats: stats
});
},
onexception: function (e) {
commitData({
type: 'exception',
message: e.message,
stack: e.stack
});
}
};
}
@ -5813,7 +5820,7 @@ var readHeader = function readHeader($bytes, $stream, $, swfVersion, tagCode) {
global['tagHandler'] = tagHandler;
global['readHeader'] = readHeader;
}(this));
function readTags(context, stream, swfVersion, final, onprogress) {
function readTags(context, stream, swfVersion, final, onprogress, onexception) {
var tags = context.tags;
var bytes = stream.bytes;
var lastSuccessfulPosition;
@ -5882,6 +5889,7 @@ function readTags(context, stream, swfVersion, final, onprogress) {
}
} catch (e) {
if (e !== StreamNoDataError) {
onexception && onexception(e);
throw e;
}
stream.pos = lastSuccessfulPosition;
@ -6041,7 +6049,7 @@ BodyParser.prototype = {
finalBlock = progressInfo.bytesLoaded >= progressInfo.bytesTotal;
}
var readStartTime = performance.now();
readTags(swf, stream, swfVersion, finalBlock, options.onprogress);
readTags(swf, stream, swfVersion, finalBlock, options.onprogress, options.onexception);
swf.parseTime += performance.now() - readStartTime;
var read = stream.pos;
buffer.removeHead(read);

File diff suppressed because it is too large Load Diff

View File

@ -1 +1,2 @@
0.7.933
0.8.6
4728574

View File

@ -108,12 +108,17 @@ function runViewer() {
parseSwf(movieUrl, movieParams, objectParams);
if (isOverlay) {
document.getElementById('overlay').className = 'enabled';
var fallbackDiv = document.getElementById('fallback');
fallbackDiv.className = 'enabled';
fallbackDiv.addEventListener('click', function(e) {
fallback();
e.preventDefault();
});
var reportDiv = document.getElementById('report');
reportDiv.addEventListener('click', function(e) {
reportIssue();
e.preventDefault();
});
var fallbackMenu = document.getElementById('fallbackMenu');
fallbackMenu.removeAttribute('hidden');
fallbackMenu.addEventListener('click', fallback);
@ -122,13 +127,14 @@ function runViewer() {
showURLMenu.addEventListener('click', showURL);
var inspectorMenu = document.getElementById('inspectorMenu');
inspectorMenu.addEventListener('click', showInInspector);
var reportMenu = document.getElementById('reportMenu');
reportMenu.addEventListener('click', reportIssue);
document.getElementById('copyProfileMenu').addEventListener('click', copyProfile);
}
function showURL() {
var flashParams = JSON.parse(FirefoxCom.requestSync('getPluginParams', null));
window.prompt("Copy to clipboard", flashParams.url);
window.prompt("Copy to clipboard", movieUrl);
}
function showInInspector() {
@ -140,6 +146,26 @@ function showInInspector() {
window.open(base + encodeURIComponent(movieUrl) + params);
}
function reportIssue() {
var duplicatesMap = Object.create(null);
var prunedExceptions = [];
avm2.exceptions.forEach(function(e) {
var ident = e.source + e.message + e.stack;
var entry = duplicatesMap[ident];
if (!entry) {
entry = duplicatesMap[ident] = {
source: e.source,
message: e.message,
stack: e.stack,
count: 0
};
prunedExceptions.push(entry);
}
entry.count++;
});
FirefoxCom.requestSync('reportIssue', JSON.stringify(prunedExceptions));
}
function copyProfile() {
function toArray(v) {
var array = [];
@ -236,6 +262,12 @@ function parseSwf(url, movieParams, objectParams) {
FirefoxCom.requestSync('getCompilerSettings', null));
enableVerifier.value = compilerSettings.verifier;
// init misc preferences
turboMode.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.turboMode', def: false});
hud.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.hud', def: false});
forceHidpi.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.force_hidpi', def: false});
dummyAnimation.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.dummyMode', def: false});
console.log("Compiler settings: " + JSON.stringify(compilerSettings));
console.log("Parsing " + url + "...");
function loaded() {

View File

@ -38,38 +38,50 @@ limitations under the License.
line-height: 0;
}
#fallback {
#overlay {
display: none;
}
#fallback.enabled {
#overlay.enabled {
display: block;
position:fixed;
bottom: 0;
right: 0;
}
#report, #fallback {
float: right;
width: 70px; height: 16px;
padding: 8px 4px 4px;
color: white;
right: 0px; bottom: 0px; width: 70px; height: 16px;
padding: 4px;
padding-top: 8px;
background-color: rgba(0, 0, 0, 0.62);
font: bold 10px sans-serif;
text-align: center;
text-decoration: none;
}
#report {
display: none;
width: 100px;
}
#overlay:hover #report {
display: block;
}
#fallback .icon {
display: none;
color: white;
}
#fallback.enabled:hover .icon {
#fallback:hover .icon {
display: inline-block;
}
#fallback:hover {
background-color: rgb(0, 0, 0);
#report:hover, #fallback:hover {
background-color: black;
}
@media screen and (max-width: 100px), screen and (max-height: 40px) {
body.started #fallback {
body.started #overlay {
display: none;
}
}
@ -79,11 +91,15 @@ limitations under the License.
<body contextmenu="shumwayMenu">
<div id="viewer"></div>
<section>
<a id="fallback" href="#">Shumway <span class="icon">&times;</span></a>
<div id="overlay">
<a id="fallback" href="#">Shumway <span class="icon">&times;</span></a>
<a id="report" href="#">Report Problems</a>
</div>
<menu type="context" id="shumwayMenu">
<menuitem label="Show URL" id="showURLMenu"></menuitem>
<menuitem label="Copy Profile" id="copyProfileMenu"></menuitem>
<menuitem label="Open in Inspector" id="inspectorMenu"></menuitem>
<menuitem label="Report Problems" id="reportMenu"></menuitem>
<menuitem label="Fallback to Flash" id="fallbackMenu" hidden></menuitem>
</menu>
</section>

View File

@ -776,6 +776,8 @@ bin/libfreebl_32int64_3.so
; [Crash Reporter]
;
#ifdef MOZ_CRASHREPORTER
@BINPATH@/components/CrashService.manifest
@BINPATH@/components/CrashService.js
#ifdef XP_MACOSX
@BINPATH@/crashreporter.app/
#else

View File

@ -15,7 +15,7 @@
inside the private browsing mode -->
<!ENTITY mainWindow.titlePrivateBrowsingSuffix "(Private Browsing)">
<!ENTITY appmenu.title "Customize and Control &brandFullName;">
<!ENTITY appmenu.tooltip "Open menu">
<!ENTITY navbarOverflow.label "More tools…">
<!-- Tab context menu -->
@ -674,7 +674,7 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY customizeMode.tabTitle "Customize &brandShortName;">
<!ENTITY customizeMode.menuAndToolbars.label "Menu and toolbars">
<!ENTITY customizeMode.menuAndToolbars.header "More Tools to Add to the Menu and Toolbar">
<!ENTITY customizeMode.menuAndToolbars.header2 "Additional Tools and Features">
<!ENTITY customizeMode.menuAndToolbars.empty "Want more tools?">
<!ENTITY customizeMode.menuAndToolbars.emptyLink "Choose from thousands of add-ons">
<!ENTITY customizeMode.restoreDefaults "Restore Defaults">

View File

@ -64,10 +64,6 @@ open.accesskey=y
# conjunction with accel (Command on Mac or Ctrl on other platforms) to Save
saveStyleSheet.commandkey=S
# LOCALIZATION NOTE (saveStyleSheet.title): This is the file picker title,
# when you save a style sheet from the Style Editor.
saveStyleSheet.title=Save style sheet
# LOCALIZATION NOTE (showOriginalSources.label): This is the label on the context
# menu item to toggle showing original sources in the editor.
showOriginalSources.label=Show original sources
@ -84,10 +80,6 @@ showCSSSources.label=Show CSS sources
# menu item to toggle back to showing only CSS sources in the editor.
showCSSSources.accesskey=C
# LOCALIZATION NOTE (saveStyleSheet.title): This is the file picker title,
# when you save a style sheet from the Style Editor.
saveStyleSheet.title=Save style sheet
# LOCALIZATION NOTE (ToolboxStyleEditor.label):
# This string is displayed in the title of the tab when the style editor is
# displayed inside the developer tools window and in the Developer Tools Menu.

View File

@ -130,6 +130,11 @@
<!ENTITY options.stylesheetSourceMaps.label "Show original sources">
<!ENTITY options.stylesheetSourceMaps.tooltip "Show original sources (e.g. Sass files) in the Style Editor and Inspector">
<!-- LOCALIZATION NOTE (options.stylesheetAutocompletion.label): This is the
- label for the checkbox that toggles autocompletion of css in the Style Editor -->
<!ENTITY options.stylesheetAutocompletion.label "Autocomplete CSS">
<!ENTITY options.stylesheetAutocompletion.tooltip "Autocomplete CSS properties, values and selectors in Style Editor as you type">
<!-- LOCALIZATION NOTE (options.profiler.label): This is the label for the
- heading of the group of JavaScript Profiler preferences in the options
- panel. -->

View File

@ -123,6 +123,9 @@ var Appbar = {
onMenuButton: function(aEvent) {
let typesArray = [];
if (BrowserUI.isPrivateBrowsingEnabled) {
typesArray.push("private-browsing");
}
if (!BrowserUI.isStartTabVisible) {
typesArray.push("find-in-page");
if (ContextCommands.getPageSource())

View File

@ -86,6 +86,8 @@ var BrowserUI = {
Services.prefs.addObserver(debugServerStateChanged, this, false);
Services.prefs.addObserver(debugServerPortChanged, this, false);
Services.prefs.addObserver("app.crashreporter.autosubmit", this, false);
Services.prefs.addObserver("metro.private_browsing.enabled", this, false);
this.updatePrivateBrowsingUI();
Services.obs.addObserver(this, "handle-xul-text-link", false);
@ -188,6 +190,12 @@ var BrowserUI = {
messageManager.removeMessageListener("Content:StateChange", this);
messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps);
Services.prefs.removeObserver(debugServerStateChanged, this);
Services.prefs.removeObserver(debugServerPortChanged, this);
Services.prefs.removeObserver("app.crashreporter.autosubmit", this);
Services.prefs.removeObserver("metro.private_browsing.enabled", this);
Services.obs.removeObserver(this, "handle-xul-text-link");
PanelUI.uninit();
@ -411,9 +419,26 @@ var BrowserUI = {
* Open a new tab in the foreground in response to a user action.
* See Browser.addTab for more documentation.
*/
addAndShowTab: function (aURI, aOwner) {
addAndShowTab: function (aURI, aOwner, aParams) {
ContextUI.peekTabs(kForegroundTabAnimationDelay);
return Browser.addTab(aURI || kStartURI, true, aOwner);
return Browser.addTab(aURI || kStartURI, true, aOwner, aParams);
},
addAndShowPrivateTab: function (aURI, aOwner) {
return this.addAndShowTab(aURI, aOwner, { private: true });
},
get isPrivateBrowsingEnabled() {
return Services.prefs.getBoolPref("metro.private_browsing.enabled");
},
updatePrivateBrowsingUI: function () {
let command = document.getElementById("cmd_newPrivateTab");
if (this.isPrivateBrowsingEnabled) {
command.removeAttribute("disabled");
} else {
command.setAttribute("disabled", "true");
}
},
/**
@ -609,8 +634,9 @@ var BrowserUI = {
BrowserUI.submitLastCrashReportOrShowPrompt;
#endif
break;
case "metro.private_browsing.enabled":
this.updatePrivateBrowsingUI();
break;
}
break;
}
@ -848,29 +874,33 @@ var BrowserUI = {
referrerURI = Services.io.newURI(json.referrer, null, null);
this.goToURI(json.uri);
break;
case "Content:StateChange":
let currBrowser = Browser.selectedBrowser;
if (this.shouldCaptureThumbnails(currBrowser)) {
PageThumbs.captureAndStore(currBrowser);
let currPage = currBrowser.currentURI.spec;
case "Content:StateChange": {
let tab = Browser.selectedTab;
if (this.shouldCaptureThumbnails(tab)) {
PageThumbs.captureAndStore(tab.browser);
let currPage = tab.browser.currentURI.spec;
Services.obs.notifyObservers(null, "Metro:RefreshTopsiteThumbnail", currPage);
}
break;
}
}
return {};
},
// Private Browsing is not supported on metro at this time, when it is added
// this function must be updated to skip capturing those pages
shouldCaptureThumbnails: function shouldCaptureThumbnails(aBrowser) {
shouldCaptureThumbnails: function shouldCaptureThumbnails(aTab) {
// Capture only if it's the currently selected tab.
if (aBrowser != Browser.selectedBrowser) {
if (aTab != Browser.selectedTab) {
return false;
}
// Skip private tabs
if (aTab.isPrivate) {
return false;
}
// FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
// that currently regresses Talos SVG tests.
let doc = aBrowser.contentDocument;
let browser = aTab.browser;
let doc = browser.contentDocument;
if (doc instanceof SVGDocument || doc instanceof XMLDocument) {
return false;
}
@ -883,17 +913,17 @@ var BrowserUI = {
return false;
}
// There's no point in taking screenshot of loading pages.
if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) {
if (browser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) {
return false;
}
// Don't take screenshots of about: pages.
if (aBrowser.currentURI.schemeIs("about")) {
if (browser.currentURI.schemeIs("about")) {
return false;
}
// No valid document channel. We shouldn't take a screenshot.
let channel = aBrowser.docShell.currentDocumentChannel;
let channel = browser.docShell.currentDocumentChannel;
if (!channel) {
return false;
}

View File

@ -476,6 +476,7 @@ var Browser = {
* is closed, we will return to a parent or "sibling" tab if possible.
* @param aParams Object (optional) with optional properties:
* index: Number specifying where in the tab list to insert the new tab.
* private: If true, the new tab should be have Private Browsing active.
* flags, postData, charset, referrerURI: See loadURIWithFlags.
*/
addTab: function browser_addTab(aURI, aBringFront, aOwner, aParams) {
@ -1255,6 +1256,13 @@ function Tab(aURI, aParams, aOwner) {
this._eventDeferred = null;
this._updateThumbnailTimeout = null;
this._private = false;
if ("private" in aParams) {
this._private = aParams.private;
} else if (aOwner) {
this._private = aOwner.private;
}
this.owner = aOwner || null;
// Set to 0 since new tabs that have not been viewed yet are good tabs to
@ -1282,6 +1290,10 @@ Tab.prototype = {
return this._chromeTab;
},
get isPrivate() {
return this._private;
},
get pageShowPromise() {
return this._eventDeferred ? this._eventDeferred.promise : null;
},
@ -1307,6 +1319,10 @@ Tab.prototype = {
this._eventDeferred = Promise.defer();
this._chromeTab = Elements.tabList.addTab(aParams.index);
if (this.isPrivate) {
this._chromeTab.setAttribute("private", "true");
}
this._id = Browser.createTabId();
let browser = this._createBrowser(aURI, null);
@ -1461,6 +1477,11 @@ Tab.prototype = {
// let the content area manager know about this browser.
ContentAreaObserver.onBrowserCreated(browser);
if (this.isPrivate) {
let ctx = browser.docShell.QueryInterface(Ci.nsILoadContext);
ctx.usePrivateBrowsing = true;
}
// stop about:blank from loading
browser.stop();
@ -1485,7 +1506,9 @@ Tab.prototype = {
},
updateThumbnail: function updateThumbnail() {
PageThumbs.captureToCanvas(this.browser.contentWindow, this._chromeTab.thumbnailCanvas);
if (!this.isPrivate) {
PageThumbs.captureToCanvas(this.browser.contentWindow, this._chromeTab.thumbnailCanvas);
}
},
updateFavicon: function updateFavicon() {

View File

@ -6,7 +6,7 @@
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/bindings.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/cssthrobber.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/tiles.css" type="text/css"?>
<?xml-stylesheet href="chrome://branding/content/metro-about.css" type="text/css"?>
@ -85,6 +85,7 @@
#ifdef MOZ_SERVICES_SYNC
<command id="cmd_remoteTabs" oncommand="CommandUpdater.doCommand(this.id);"/>
#endif
<command id="cmd_newPrivateTab" oncommand="BrowserUI.addAndShowPrivateTab();"/>
<!-- misc -->
<command id="cmd_close" oncommand="CommandUpdater.doCommand(this.id);"/>
@ -162,6 +163,7 @@ Desktop browser's sync prefs.
<key id="key_closeTab" key="&closeTab.key;" modifiers="accel" command="cmd_closeTab"/>
<key id="key_closeTab2" keycode="VK_F4" modifiers="accel" command="cmd_closeTab"/>
<key id="key_undoCloseTab" key="&newTab.key;" modifiers="accel,shift" command="cmd_undoCloseTab"/>
<key id="key_newPrivateTab" key="&newPrivateTab.key;" modifiers="accel,shift" command="cmd_newPrivateTab"/>
<!-- tab selection -->
<key id="key_nextTab" oncommand="BrowserUI.selectNextTab();" keycode="VK_TAB" modifiers="accel"/>
@ -400,28 +402,34 @@ Desktop browser's sync prefs.
<spacer flex="1"/>
</hbox>
<hbox id="checkingForUpdates" align="center">
<image class="update-throbber"/><label>&update.checkingForUpdates;</label>
<image class="update-throbber"/>
<description flex="1">&update.checkingForUpdates;</description>
</hbox>
<hbox id="checkingAddonCompat" align="center">
<image class="update-throbber"/><label>&update.checkingAddonCompat;</label>
<image class="update-throbber"/>
<description flex="1">&update.checkingAddonCompat;</description>
</hbox>
<hbox id="downloading" align="center">
<image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
<image class="update-throbber"/>
<description flex="1">
&update.downloading.start;<html:span id="downloadStatus"></html:span>&update.downloading.end;
</description>
</hbox>
<hbox id="applying" align="center">
<image class="update-throbber"/><label>&update.applying;</label>
<image class="update-throbber"/>
<description flex="1">&update.applying;</description>
</hbox>
<hbox id="downloadFailed" align="center">
<label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
</hbox>
<hbox id="adminDisabled" align="center">
<label>&update.adminDisabled;</label>
<label linewrap="true">&update.adminDisabled;</label>
</hbox>
<hbox id="noUpdatesFound" align="center">
<label>&update.noUpdatesFound;</label>
<label linewrap="true">&update.noUpdatesFound;</label>
</hbox>
<hbox id="otherInstanceHandlingUpdates" align="center">
<label>&update.otherInstanceHandlingUpdates;</label>
<label linewrap="true">&update.otherInstanceHandlingUpdates;</label>
</hbox>
<hbox id="manualUpdate" align="center">
<label>&update.manual.start;</label><label id="manualLink" linewrap="true" class="text-link"/><label>&update.manual.end;</label>
@ -807,6 +815,9 @@ Desktop browser's sync prefs.
</richlistitem>
<!-- standard buttons -->
<richlistitem id="context-newprivatetab" type="private-browsing" onclick="BrowserUI.addAndShowPrivateTab()">
<label value="&newPrivateTab.label;"/>
</richlistitem>
<richlistitem id="context-findinpage" type="find-in-page" onclick="ContextCommands.findInPage();">
<label value="&appbarFindInPage2.label;"/>
</richlistitem>

View File

@ -506,7 +506,7 @@ appUpdater.prototype =
*/
setupDownloadingUI: function() {
this.downloadStatus = document.getElementById("downloadStatus");
this.downloadStatus.value =
this.downloadStatus.textContent =
DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size);
this.selectPanel("downloading");
this.aus.addDownloadListener(this);
@ -598,7 +598,7 @@ appUpdater.prototype =
* See nsIProgressEventSink.idl
*/
onProgress: function(aRequest, aContext, aProgress, aProgressMax) {
this.downloadStatus.value =
this.downloadStatus.textContent =
DownloadUtils.getTransferTotal(aProgress, aProgressMax);
},

View File

@ -26,8 +26,7 @@
<meta name="viewport" content="width=device-width; user-scalable=false;" />
<title>&crashprompt.title;</title>
<link rel="stylesheet" href="chrome://browser/skin/platform.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/skin/browser.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/content/browser.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/content/bindings.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/skin/crashprompt.css" type="text/css" media="all" />
<!--TODO: Do we need a favicon?-->

View File

@ -5,9 +5,9 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/bindings.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/tiles.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/start.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">

View File

@ -75,7 +75,7 @@ chrome.jar:
content/appbar.js (content/appbar.js)
content/shell.xul (content/jsshell/shell.xul)
content/shell.html (content/jsshell/shell.html)
content/browser.css (content/browser.css)
content/bindings.css (content/bindings.css)
content/cursor.css (content/cursor.css)
content/sanitize.js (content/sanitize.js)
content/sanitizeUI.js (content/sanitizeUI.js)

View File

@ -0,0 +1,111 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gStartView = null;
function test() {
requestLongerTimeout(2);
runTests();
}
function setup() {
PanelUI.hide();
if (!BrowserUI.isStartTabVisible) {
let tab = yield addTab("about:start");
gStartView = tab.browser.contentWindow.BookmarksStartView._view;
yield waitForCondition(() => BrowserUI.isStartTabVisible);
yield hideContextUI();
}
BookmarksTestHelper.setup();
HistoryTestHelper.setup();
}
function tearDown() {
PanelUI.hide();
BookmarksTestHelper.restore();
HistoryTestHelper.restore();
}
gTests.push({
desc: "touch scroll",
run: function run() {
yield addTab(chromeRoot + "res/scroll_test.html");
yield hideContextUI();
yield hideNavBar();
let stopwatch = new StopWatch();
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
var touchdrag = new TouchDragAndHold();
touchdrag.useNativeEvents = true;
touchdrag.stepTimeout = 5;
touchdrag.numSteps = 20;
stopwatch.start();
let recordingHandle = domUtils.startFrameTimeRecording();
for (let count = 0; count < 3; count++) {
yield touchdrag.start(win, 100, (win.innerHeight - 50), 100, 50);
touchdrag.end();
yield touchdrag.start(win, 100, 50, 100, (win.innerHeight - 50));
touchdrag.end();
}
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareTest("14C693E5-3ED3-4A5D-93BC-A31F130A8CDE",
"touch scroll text", "graphics", "apzc",
"Measures performance of apzc scrolling for a large page of text using FTR.");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
}
});
gTests.push({
desc: "touch scroll start",
setUp: setup,
tearDown: tearDown,
run: function run() {
let stopwatch = new StopWatch();
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
var touchdrag = new TouchDragAndHold();
touchdrag.useNativeEvents = true;
touchdrag.stepTimeout = 5;
touchdrag.numSteps = 20;
stopwatch.start();
let recordingHandle = domUtils.startFrameTimeRecording();
for (let count = 0; count < 3; count++) {
yield touchdrag.start(win, (win.innerWidth - 50), (win.innerHeight - 50), 50, (win.innerHeight - 50));
touchdrag.end();
yield touchdrag.start(win, 50, (win.innerHeight - 50), (win.innerWidth - 50), (win.innerHeight - 50));
touchdrag.end();
}
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareTest("24C693E5-3ED3-4A5D-93BC-A31F130A8CDE",
"touch scroll about:start", "graphics", "apzc",
"Measures performance of apzc scrolling for about:start using FTR.");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
let results = PerfTest.computeHighLowBands(intervals, .1);
PerfTest.declareTest("2E60F8B5-8925-4628-988E-E4C0BC6B34C7",
"about:start jank", "graphics", "apzc",
"Displays the low, high, and average FTR frame intervals for about:start.");
info("results.low:" + results.low);
PerfTest.declareNumericalResults([
{ value: results.low, desc: "low" },
{ value: results.high, desc: "high" },
{ value: results.ave, desc: "average" }
]);
}
});

View File

@ -12,19 +12,17 @@ gTests.push({
run: function run() {
yield addTab("about:mozilla");
yield hideContextUI();
yield waitForMs(5000);
yield hideNavBar();
yield waitForMs(3000);
let shiftDataSet = new Array();
let paintDataSet = new Array();
let stopwatch = new StopWatch();
var paintCount = 0;
function onPaint() {
paintCount++;
}
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
for (let idx = 0; idx < 10; idx++) {
window.addEventListener("MozAfterPaint", onPaint, true);
let recordingHandle = domUtils.startFrameTimeRecording();
stopwatch.start();
let promise = waitForEvent(window, "MozDeckOffsetChanged");
ContentAreaObserver.shiftBrowserDeck(300);
@ -34,22 +32,21 @@ gTests.push({
yield promise;
stopwatch.stop();
yield waitForMs(500);
window.removeEventListener("MozAfterPaint", onPaint, true);
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
shiftDataSet.push(stopwatch.time());
paintDataSet.push(paintCount);
paintCount = 0;
paintDataSet.push(intervals.length);
}
PerfTest.declareTest("ecb5fbec-0b3d-490f-8d4a-13fa8963e54a",
"shift browser deck", "browser", "ux",
"Triggers multiple SKB deck shifting operations using an offset " +
"value of 300px. Measures total time in milliseconds for a up/down " +
"shift operation plus the total number of paints. Strips outliers.");
"shift operation plus the total number of frames. Strips outliers.");
let shiftAverage = PerfTest.computeAverage(shiftDataSet, { stripOutliers: true });
let paintAverage = PerfTest.computeAverage(paintDataSet, { stripOutliers: true });
PerfTest.declareNumericalResults([
{ value: shiftAverage, desc: "msec" },
{ value: paintAverage, desc: "paint count" },
{ value: paintAverage, desc: "frame count" },
]);
}
});

View File

@ -11,24 +11,24 @@ gTests.push({
desc: "rotating divs",
run: function run() {
yield addTab(chromeRoot + "res/divs_test.html", true);
yield hideContextUI();
yield hideNavBar();
let stopwatch = new StopWatch();
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
yield waitForEvent(win, "teststarted", 5000);
// the test runs for five seconds
let recordingHandle = domUtils.startFrameTimeRecording();
stopwatch.start();
let event = yield waitForEvent(win, "testfinished", 10000);
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareTest("B924F3FA-4CB5-4453-B131-53E3611E0765",
"rotating divs w/text", "graphics", "content",
"Measures animation frames for rotating translucent divs on top of a background of text.");
stopwatch.start();
// the test runs for ten seconds
let event = yield waitForEvent(win, "testfinished", 20000);
let msec = stopwatch.stop();
PerfTest.declareNumericalResult((event.detail.frames / msec) * 1000.0, "fps");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
}
});

View File

@ -18,81 +18,27 @@ gTests.push({
run: function run() {
yield addTab(chromeRoot + "res/scroll_test.html");
yield hideContextUI();
yield hideNavBar();
let stopwatch = new StopWatch();
let win = Browser.selectedTab.browser.contentWindow;
PerfTest.declareTest("79235F74-037C-4F6B-AE47-FDCCC13458C3",
"scrollBy scroll", "graphics", "content",
"Measures performance of single line scrolls using scrollBy for a large page of text.");
let mozpaints = 0;
function onPaint() {
mozpaints++;
}
win.addEventListener("MozAfterPaint", onPaint, true);
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let deferExit = Promise.defer();
let recordingHandle = domUtils.startFrameTimeRecording();
function step() {
if (stopwatch.time() < 10000) {
if (stopwatch.time() < 5000) {
win.scrollBy(0, 2);
// XXX slaves won't paint without this
win.mozRequestAnimationFrame(step);
return;
}
win.removeEventListener("MozAfterPaint", onPaint, true);
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareFrameRateResult(mozpaints, msec, "fps");
deferExit.resolve();
}
stopwatch.start();
win.mozRequestAnimationFrame(step);
yield deferExit.promise;
}
});
gTests.push({
desc: "scoll touch",
run: function run() {
yield addTab(chromeRoot + "res/scroll_test.html");
yield hideContextUI();
let stopwatch = new StopWatch();
let win = Browser.selectedTab.browser.contentWindow;
PerfTest.declareTest("14C693E5-3ED3-4A5D-93BC-A31F130A8CDE",
"touch scroll", "graphics", "content",
"Measures performance of single line scrolls using touch input for a large page of text.");
let y = win.innerHeight - 10;
EventUtils.synthesizeTouchAtPoint(100, y, { type: "touchstart" }, win);
EventUtils.synthesizeTouchAtPoint(100, y, { type: "touchmove" }, win);
y -= tapRadius() + 5;
EventUtils.synthesizeTouchAtPoint(100, y, { type: "touchmove" }, win);
let mozpaints = 0;
function onPaint() {
mozpaints++;
}
win.addEventListener("MozAfterPaint", onPaint, true);
let deferExit = Promise.defer();
function step() {
if (stopwatch.time() < 10000) {
y -= 2;
EventUtils.synthesizeTouchAtPoint(100, y, { type: "touchmove" }, win);
win.mozRequestAnimationFrame(step);
return;
}
win.removeEventListener("MozAfterPaint", onPaint, true);
let msec = stopwatch.stop();
EventUtils.synthesizeTouchAtPoint(100, y, { type: "touchend" }, win);
PerfTest.declareFrameRateResult(mozpaints, msec, "fps");
PerfTest.declareTest("79235F74-037C-4F6B-AE47-FDCCC13458C3",
"scrollBy scroll", "graphics", "content",
"Measures performance of single line scrolls for a large page of text using FTR.");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
deferExit.resolve();
}
@ -106,17 +52,22 @@ gTests.push({
desc: "canvas perf test",
run: function run() {
yield addTab(chromeRoot + "res/ripples.html");
let win = Browser.selectedTab.browser.contentWindow;
yield hideContextUI();
yield hideNavBar();
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let recordingHandle = domUtils.startFrameTimeRecording();
PerfTest.declareTest("6A455F96-2B2C-4B3C-B387-1AF2F1747CCF",
"ripples", "graphics", "canvas",
"Measures animation frames during a computationally " +
"heavy graphics demo using canvas.");
"heavy graphics demo using canvas using FTR.");
let stopwatch = new StopWatch(true);
let event = yield waitForEvent(win, "test", 20000);
// test page runs for 5 seconds
let event = yield waitForEvent(win, "test", 10000);
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareFrameRateResult(event.detail.frames, msec, "fps");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
}
});
@ -125,27 +76,24 @@ gTests.push({
run: function run() {
yield addTab(chromeRoot + "res/tidevideo.html");
let win = Browser.selectedTab.browser.contentWindow;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let video = win.document.getElementById("videoelement");
video.pause();
yield hideContextUI();
yield hideNavBar();
yield waitForMs(1000);
PerfTest.declareTest("7F55F9C4-0ECF-4A13-9A9C-A38D46922C0B",
"video playback (moz paints)", "graphics", "video",
"Measures MozAfterPaints per second during five seconds of playback of an mp4.");
"Measures frames per second during five seconds of playback of an mp4.");
var paintCount = 0;
function onPaint() {
paintCount++;
}
let recordingHandle = domUtils.startFrameTimeRecording();
let stopwatch = new StopWatch(true);
window.addEventListener("MozAfterPaint", onPaint, true);
video.play();
yield waitForMs(5000);
video.pause();
window.removeEventListener("MozAfterPaint", onPaint, true);
let intervals = domUtils.stopFrameTimeRecording(recordingHandle);
let msec = stopwatch.stop();
PerfTest.declareNumericalResult((paintCount / msec) * 1000.0, "pps");
PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
PerfTest.declareTest("E132D333-4642-4597-B1F0-1E74B625DBD7",
"video playback (moz stats)", "graphics", "video",
@ -158,4 +106,3 @@ gTests.push({
PerfTest.declareNumericalResults(results);
}
});

View File

@ -179,6 +179,29 @@ var PerfTest = {
return total / count;
},
computeHighLowBands: function computeHighLow(aArray, aPercentage) {
let bandCount = Math.ceil(aArray.length * aPercentage);
let lowGroup = 0, highGroup = 0;
let idx;
function compareNumbers(a, b) {
return a - b;
}
aArray.sort(compareNumbers);
for (idx = 0; idx < bandCount; idx++) {
lowGroup += aArray[idx];
}
let top = aArray.length - 1;
for (idx = top; idx > (top - bandCount); idx--) {
highGroup += aArray[idx];
}
return {
low: lowGroup / bandCount,
high: highGroup / bandCount,
ave: this.computeAverage(aArray, {})
};
},
/******************************************************
* Internal
*/

View File

@ -11,6 +11,7 @@ support-files =
res/divs_test.html
res/fx.png
[browser_apzc.js]
[browser_miscgfx_01.js]
[browser_tabs_01.js]
[browser_deck_01.js]

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