mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to s-c.
This commit is contained in:
commit
d6695099cc
@ -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);
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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) {
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "c5f8db7e1e71a9bfe0d29665d2b4cf3ae773094e",
|
||||
"revision": "2f947703e8fdb71a105462394ed2a4e0e3db391e",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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);
|
||||
|
@ -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":
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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) {
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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";
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
};
|
@ -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);
|
||||
});
|
||||
});
|
||||
};
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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">
|
||||
|
@ -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);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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++) {
|
||||
|
@ -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++) {
|
||||
|
@ -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() {
|
||||
|
@ -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),
|
||||
|
@ -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" &&
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
},
|
||||
|
||||
/**
|
||||
|
179
browser/devtools/sourceeditor/autocomplete.js
Normal file
179
browser/devtools/sourceeditor/autocomplete.js
Normal 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;
|
798
browser/devtools/sourceeditor/css-autocompleter.js
Normal file
798
browser/devtools/sourceeditor/css-autocompleter.js
Normal 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;
|
717
browser/devtools/sourceeditor/css-tokenizer.js
Normal file
717
browser/devtools/sourceeditor/css-tokenizer.js
Normal 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;
|
||||
|
||||
}));
|
@ -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);
|
||||
|
@ -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'
|
||||
]
|
||||
|
@ -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]
|
||||
|
146
browser/devtools/sourceeditor/test/browser_css_autocompletion.js
Normal file
146
browser/devtools/sourceeditor/test/browser_css_autocompletion.js
Normal 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();
|
||||
}
|
113
browser/devtools/sourceeditor/test/browser_css_statemachine.js
Normal file
113
browser/devtools/sourceeditor/test/browser_css_statemachine.js
Normal 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();
|
||||
}
|
@ -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']],
|
||||
]
|
@ -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);
|
||||
}
|
||||
}
|
@ -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', '', '']],
|
||||
]
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
22
browser/devtools/styleeditor/test/autocomplete.html
Normal file
22
browser/devtools/styleeditor/test/autocomplete.html
Normal 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>
|
@ -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]
|
||||
|
@ -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();
|
||||
}
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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 + '';
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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
@ -1 +1,2 @@
|
||||
0.7.933
|
||||
0.8.6
|
||||
4728574
|
||||
|
@ -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() {
|
||||
|
@ -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">×</span></a>
|
||||
<div id="overlay">
|
||||
<a id="fallback" href="#">Shumway <span class="icon">×</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>
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
|
@ -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.
|
||||
|
@ -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. -->
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
|
@ -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?-->
|
||||
|
||||
|
@ -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">
|
||||
|
@ -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)
|
||||
|
111
browser/metro/base/tests/mochiperf/browser_apzc.js
Normal file
111
browser/metro/base/tests/mochiperf/browser_apzc.js
Normal 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" }
|
||||
]);
|
||||
}
|
||||
});
|
@ -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" },
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
@ -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");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user