зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central into mozilla-inbound
This commit is contained in:
Коммит
4bb8ace1ba
|
@ -155,8 +155,10 @@ Components.utils.import('resource://gre/modules/ctypes.jsm');
|
|||
})();
|
||||
|
||||
// =================== Debugger ====================
|
||||
SettingsListener.observe('devtools.debugger.remote-enabled', false, function(enabled) {
|
||||
SettingsListener.observe('devtools.debugger.remote-enabled', false, function(value) {
|
||||
Services.prefs.setBoolPref('devtools.debugger.remote-enabled', value);
|
||||
// This preference is consulted during startup
|
||||
Services.prefs.savePrefFile(null);
|
||||
});
|
||||
|
||||
SettingsListener.observe('devtools.debugger.log', false, function(value) {
|
||||
|
|
|
@ -867,7 +867,11 @@ function ElementEditor(aContainer, aNode)
|
|||
return;
|
||||
}
|
||||
|
||||
this._applyAttributes(aVal);
|
||||
try {
|
||||
this._applyAttributes(aVal);
|
||||
} catch (x) {
|
||||
return;
|
||||
}
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
|
@ -962,11 +966,16 @@ ElementEditor.prototype = {
|
|||
this.undo.startBatch();
|
||||
|
||||
// Remove the attribute stored in this editor and re-add any attributes
|
||||
// parsed out of the input element.
|
||||
this._removeAttribute(this.node, aAttr.name)
|
||||
this._applyAttributes(aVal, attr);
|
||||
|
||||
this.undo.endBatch();
|
||||
// parsed out of the input element. Restore original attribute if
|
||||
// parsing fails.
|
||||
this._removeAttribute(this.node, aAttr.name);
|
||||
try {
|
||||
this._applyAttributes(aVal, attr);
|
||||
this.undo.endBatch();
|
||||
} catch (e) {
|
||||
this.undo.endBatch();
|
||||
this.undo.undo();
|
||||
}
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
|
@ -987,6 +996,7 @@ ElementEditor.prototype = {
|
|||
* @param Element aAttrNode the attribute editor that created this
|
||||
* set of attributes, used to place new attributes where the
|
||||
* user put them.
|
||||
* @throws SYNTAX_ERR if aValue is not well-formed.
|
||||
*/
|
||||
_applyAttributes: function EE__applyAttributes(aValue, aAttrNode)
|
||||
{
|
||||
|
@ -996,6 +1006,7 @@ ElementEditor.prototype = {
|
|||
let parseTag = (this.node.namespaceURI.match(/svg/i) ? "svg" :
|
||||
(this.node.namespaceURI.match(/mathml/i) ? "math" : "div"));
|
||||
let parseText = "<" + parseTag + " " + aValue + "/>";
|
||||
// Throws exception if parseText is not well-formed.
|
||||
dummyNode.innerHTML = parseText;
|
||||
let parsedNode = dummyNode.firstChild;
|
||||
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="node22" class="unchanged"></div>
|
||||
<div id="node23"></div>
|
||||
<div id="node24"></div>
|
||||
<div id="retag-me">
|
||||
<div id="retag-me-2"></div>
|
||||
</div>
|
||||
|
|
|
@ -73,6 +73,28 @@ function test() {
|
|||
}
|
||||
},
|
||||
|
||||
// Try change an attribute to a badly formed string
|
||||
{
|
||||
before: function() {
|
||||
assertAttributes(doc.querySelector("#node22"), {
|
||||
id: "node22",
|
||||
class: "unchanged"
|
||||
});
|
||||
},
|
||||
execute: function() {
|
||||
let editor = markup.getContainer(doc.querySelector("#node22")).editor;
|
||||
let attr = editor.attrs["class"].querySelector(".editable");
|
||||
editField(attr, 'class="""');
|
||||
},
|
||||
after: function() {
|
||||
assertAttributes(doc.querySelector("#node22"), {
|
||||
id: "node22",
|
||||
class: "unchanged"
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Remove an attribute
|
||||
{
|
||||
before: function() {
|
||||
|
@ -114,6 +136,25 @@ function test() {
|
|||
}
|
||||
},
|
||||
|
||||
// Try add a badly formed attribute by clicking the empty space after a node
|
||||
{
|
||||
before: function() {
|
||||
assertAttributes(doc.querySelector("#node23"), {
|
||||
id: "node23",
|
||||
});
|
||||
},
|
||||
execute: function() {
|
||||
let editor = markup.getContainer(doc.querySelector("#node23")).editor;
|
||||
let attr = editor.newAttr;
|
||||
editField(attr, 'class="newclass" style="""');
|
||||
},
|
||||
after: function() {
|
||||
assertAttributes(doc.querySelector("#node23"), {
|
||||
id: "node23",
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Add attributes by adding to an existing attribute's entry
|
||||
{
|
||||
setup: function() {
|
||||
|
@ -140,6 +181,25 @@ function test() {
|
|||
}
|
||||
},
|
||||
|
||||
// Try add attributes by adding to an existing attribute's entry
|
||||
{
|
||||
before: function() {
|
||||
assertAttributes(doc.querySelector("#node24"), {
|
||||
id: "node24",
|
||||
});
|
||||
},
|
||||
execute: function() {
|
||||
let editor = markup.getContainer(doc.querySelector("#node24")).editor;
|
||||
let attr = editor.attrs["id"].querySelector(".editable");
|
||||
editField(attr, attr.textContent + ' class="""');
|
||||
},
|
||||
after: function() {
|
||||
assertAttributes(doc.querySelector("#node24"), {
|
||||
id: "node24",
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Remove an element with the delete key
|
||||
{
|
||||
before: function() {
|
||||
|
|
|
@ -729,13 +729,16 @@ var Scratchpad = {
|
|||
*/
|
||||
getRecentFiles: function SP_getRecentFiles()
|
||||
{
|
||||
let maxRecent = Services.prefs.getIntPref(PREF_RECENT_FILES_MAX);
|
||||
let branch = Services.prefs.
|
||||
getBranch("devtools.scratchpad.");
|
||||
|
||||
let branch = Services.prefs.getBranch("devtools.scratchpad.");
|
||||
let filePaths = [];
|
||||
|
||||
// WARNING: Do not use getCharPref here, it doesn't play nicely with
|
||||
// Unicode strings.
|
||||
|
||||
if (branch.prefHasUserValue("recentFilePaths")) {
|
||||
filePaths = JSON.parse(branch.getCharPref("recentFilePaths"));
|
||||
let data = branch.getComplexValue("recentFilePaths",
|
||||
Ci.nsISupportsString).data;
|
||||
filePaths = JSON.parse(data);
|
||||
}
|
||||
|
||||
return filePaths;
|
||||
|
@ -781,10 +784,16 @@ var Scratchpad = {
|
|||
|
||||
filePaths.push(aFile.path);
|
||||
|
||||
let branch = Services.prefs.
|
||||
getBranch("devtools.scratchpad.");
|
||||
branch.setCharPref("recentFilePaths", JSON.stringify(filePaths));
|
||||
return;
|
||||
// WARNING: Do not use setCharPref here, it doesn't play nicely with
|
||||
// Unicode strings.
|
||||
|
||||
let str = Cc["@mozilla.org/supports-string;1"]
|
||||
.createInstance(Ci.nsISupportsString);
|
||||
str.data = JSON.stringify(filePaths);
|
||||
|
||||
let branch = Services.prefs.getBranch("devtools.scratchpad.");
|
||||
branch.setComplexValue("recentFilePaths",
|
||||
Ci.nsISupportsString, str);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -868,11 +877,19 @@ var Scratchpad = {
|
|||
|
||||
let filePaths = this.getRecentFiles();
|
||||
if (maxRecent < filePaths.length) {
|
||||
let branch = Services.prefs.
|
||||
getBranch("devtools.scratchpad.");
|
||||
let diff = filePaths.length - maxRecent;
|
||||
filePaths.splice(0, diff);
|
||||
branch.setCharPref("recentFilePaths", JSON.stringify(filePaths));
|
||||
|
||||
// WARNING: Do not use setCharPref here, it doesn't play nicely with
|
||||
// Unicode strings.
|
||||
|
||||
let str = Cc["@mozilla.org/supports-string;1"]
|
||||
.createInstance(Ci.nsISupportsString);
|
||||
str.data = JSON.stringify(filePaths);
|
||||
|
||||
let branch = Services.prefs.getBranch("devtools.scratchpad.");
|
||||
branch.setComplexValue("recentFilePaths",
|
||||
Ci.nsISupportsString, str);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -27,7 +27,7 @@ var lists = {
|
|||
|
||||
// Temporary file names.
|
||||
let gFileName01 = "file01_ForBug651942.tmp"
|
||||
let gFileName02 = "file02_ForBug651942.tmp"
|
||||
let gFileName02 = "☕" // See bug 783858 for more information
|
||||
let gFileName03 = "file03_ForBug651942.tmp"
|
||||
let gFileName04 = "file04_ForBug651942.tmp"
|
||||
|
||||
|
|
|
@ -943,15 +943,15 @@ PropertyView.prototype = {
|
|||
}
|
||||
|
||||
if (!this.tree.viewedElement || !this.visible) {
|
||||
this.valueNode.innerHTML = "";
|
||||
this.valueNode.textContent = "";
|
||||
this.matchedSelectorsContainer.parentNode.hidden = true;
|
||||
this.matchedSelectorsContainer.innerHTML = "";
|
||||
this.matchedSelectorsContainer.textContent = "";
|
||||
this.matchedExpander.removeAttribute("open");
|
||||
return;
|
||||
}
|
||||
|
||||
this.tree.numVisibleProperties++;
|
||||
this.valueNode.innerHTML = this.propertyInfo.value;
|
||||
this.valueNode.textContent = this.propertyInfo.value;
|
||||
this.refreshAllSelectors();
|
||||
},
|
||||
|
||||
|
|
|
@ -1124,12 +1124,16 @@ CssRuleView.prototype = {
|
|||
*/
|
||||
_onMenuUpdate: function CssRuleView_onMenuUpdate(aEvent)
|
||||
{
|
||||
let node = this.doc.popupNode;
|
||||
|
||||
// Copy selection.
|
||||
let disable = this.doc.defaultView.getSelection().isCollapsed;
|
||||
let editorSelection = node.className == "styleinspector-propertyeditor" &&
|
||||
node.selectionEnd - node.selectionStart != 0;
|
||||
let disable = this.doc.defaultView.getSelection().isCollapsed &&
|
||||
!editorSelection;
|
||||
this._copyItem.disabled = disable;
|
||||
|
||||
// Copy property, copy property name & copy property value.
|
||||
let node = this.doc.popupNode;
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
@ -1159,16 +1163,26 @@ CssRuleView.prototype = {
|
|||
*/
|
||||
_onCopy: function CssRuleView_onCopy(aEvent)
|
||||
{
|
||||
let win = this.doc.defaultView;
|
||||
let text = win.getSelection().toString();
|
||||
let target = this.doc.popupNode || aEvent.target;
|
||||
let text;
|
||||
|
||||
// Remove any double newlines.
|
||||
text = text.replace(/(\r?\n)\r?\n/g, "$1");
|
||||
if (target.nodeName == "input") {
|
||||
let start = Math.min(target.selectionStart, target.selectionEnd);
|
||||
let end = Math.max(target.selectionStart, target.selectionEnd);
|
||||
let count = end - start;
|
||||
text = target.value.substr(start, count);
|
||||
} else {
|
||||
let win = this.doc.defaultView;
|
||||
text = win.getSelection().toString();
|
||||
|
||||
// Remove "inline"
|
||||
let inline = _strings.GetStringFromName("rule.sourceInline");
|
||||
let rx = new RegExp("^" + inline + "\\r?\\n?", "g");
|
||||
text = text.replace(rx, "");
|
||||
// Remove any double newlines.
|
||||
text = text.replace(/(\r?\n)\r?\n/g, "$1");
|
||||
|
||||
// Remove "inline"
|
||||
let inline = _strings.GetStringFromName("rule.sourceInline");
|
||||
let rx = new RegExp("^" + inline + "\\r?\\n?", "g");
|
||||
text = text.replace(rx, "");
|
||||
}
|
||||
|
||||
clipboardHelper.copyString(text, this.doc);
|
||||
|
||||
|
@ -1277,12 +1291,13 @@ CssRuleView.prototype = {
|
|||
_onCopyProperty: function CssRuleView_onCopyProperty(aEvent)
|
||||
{
|
||||
let node = this.doc.popupNode;
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node.classList.contains("ruleview-propertyname")) {
|
||||
node = node.querySelector(".ruleview-propertyname");
|
||||
node = node.parentNode.parentNode.querySelector(".ruleview-propertyname");
|
||||
}
|
||||
|
||||
if (node) {
|
||||
|
@ -1303,7 +1318,7 @@ CssRuleView.prototype = {
|
|||
}
|
||||
|
||||
if (!node.classList.contains("ruleview-propertyvalue")) {
|
||||
node = node.querySelector(".ruleview-propertyvalue");
|
||||
node = node.parentNode.parentNode.querySelector(".ruleview-propertyvalue");
|
||||
}
|
||||
|
||||
if (node) {
|
||||
|
@ -1381,6 +1396,19 @@ RuleEditor.prototype = {
|
|||
}
|
||||
}.bind(this), false);
|
||||
|
||||
this.element.addEventListener("mousedown", function() {
|
||||
let editorNodes =
|
||||
this.doc.querySelectorAll(".styleinspector-propertyeditor");
|
||||
|
||||
if (editorNodes) {
|
||||
for (let node of editorNodes) {
|
||||
if (node.inplaceEditor) {
|
||||
node.inplaceEditor._clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this), false);
|
||||
|
||||
this.propertyList = createChild(code, "ul", {
|
||||
class: "ruleview-propertylist"
|
||||
});
|
||||
|
|
|
@ -88,8 +88,8 @@ function testClip()
|
|||
SimpleTest.waitForClipboard(function IUI_boundCopyPropCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},
|
||||
checkCopyRule, checkCopyRuleWithEditorSelected, function() {
|
||||
failedClipboard(expectedPattern, checkCopyRuleWithEditorSelected);
|
||||
checkCopyRule, checkCopyProperty, function() {
|
||||
failedClipboard(expectedPattern, checkCopyProperty);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -118,57 +118,6 @@ function checkCopyRule() {
|
|||
menu.hidePopup();
|
||||
}
|
||||
|
||||
function checkCopyRuleWithEditorSelected()
|
||||
{
|
||||
let contentDoc = ruleViewFrame().contentDocument;
|
||||
let rules = contentDoc.querySelectorAll(".ruleview-rule");
|
||||
let propNodes = contentDoc.querySelectorAll(".ruleview-property");
|
||||
let propNode = propNodes[2];
|
||||
let propNameNode = propNode.querySelector(".ruleview-propertyname");
|
||||
|
||||
ok(propNameNode, "we have the property name node");
|
||||
|
||||
info("Checking that _boundCopyRule() returns the correct clipboard value");
|
||||
let expectedPattern = "element {[\\r\\n]+" +
|
||||
" margin: 10em;[\\r\\n]+" +
|
||||
" font-size: 14pt;[\\r\\n]+" +
|
||||
" font-family: helvetica,sans-serif;[\\r\\n]+" +
|
||||
" color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
|
||||
"}[\\r\\n]*";
|
||||
|
||||
let elementRuleEditor = rules[0]._ruleEditor;
|
||||
waitForEditorFocus(elementRuleEditor.element, function onNewElement(aEditor) {
|
||||
ok(aEditor, "we have the editor");
|
||||
|
||||
waitForBlur.editor = aEditor;
|
||||
|
||||
// We need the context menu to open in the correct place in order for
|
||||
// popupNode to be propertly set.
|
||||
EventUtils.synthesizeMouse(aEditor.input, 1, 1,
|
||||
{ type: "contextmenu", button: 2 }, ruleViewFrame().contentWindow);
|
||||
|
||||
SimpleTest.waitForClipboard(function IUI_boundCopyCheckWithSelection() {
|
||||
let menu = contentDoc.querySelector("#rule-view-context-menu");
|
||||
ok(menu, "we have the context menu");
|
||||
menu.hidePopup();
|
||||
|
||||
return checkClipboardData(expectedPattern);
|
||||
}, ruleView()._boundCopyRule, waitForBlur, function() {
|
||||
failedClipboard(expectedPattern, checkCopyProperty);
|
||||
});
|
||||
});
|
||||
EventUtils.synthesizeMouse(propNameNode, 1, 1, { }, ruleViewFrame().contentWindow);
|
||||
}
|
||||
|
||||
function waitForBlur()
|
||||
{
|
||||
waitForEditorBlur(waitForBlur.editor, function() {
|
||||
waitForBlur.editor = null;
|
||||
checkCopyProperty();
|
||||
});
|
||||
waitForBlur.editor.input.blur();
|
||||
}
|
||||
|
||||
function checkCopyProperty()
|
||||
{
|
||||
let contentDoc = ruleViewFrame().contentDocument;
|
||||
|
@ -197,7 +146,7 @@ function checkCopyPropertyName()
|
|||
{
|
||||
info("Checking that _onCopyProperty() returns " +
|
||||
"the correct clipboard value");
|
||||
let expectedPattern = "font-family";
|
||||
let expectedPattern = "margin";
|
||||
|
||||
SimpleTest.waitForClipboard(function IUI_boundCopyPropNameCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
|
@ -212,7 +161,7 @@ function checkCopyPropertyValue()
|
|||
{
|
||||
info("Checking that _onCopyPropertyValue() " +
|
||||
" returns the correct clipboard value");
|
||||
let expectedPattern = "helvetica,sans-serif";
|
||||
let expectedPattern = "10em";
|
||||
|
||||
SimpleTest.waitForClipboard(function IUI_boundCopyPropValueCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
|
@ -248,11 +197,61 @@ function checkCopySelection()
|
|||
|
||||
SimpleTest.waitForClipboard(function IUI_boundCopyCheck() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},ruleView()._boundCopy, finishup, function() {
|
||||
failedClipboard(expectedPattern, finishup);
|
||||
},ruleView()._boundCopy, testSimpleCopy, function() {
|
||||
failedClipboard(expectedPattern, testSimpleCopy);
|
||||
});
|
||||
}
|
||||
|
||||
function testSimpleCopy()
|
||||
{
|
||||
executeSoon(function() {
|
||||
info("Checking that _onCopy() returns the correct clipboard value");
|
||||
let expectedPattern = "element {[\\r\\n]+" +
|
||||
" margin: 10em;[\\r\\n]+" +
|
||||
" font-size: 14pt;[\\r\\n]+" +
|
||||
" font-family: helvetica,sans-serif;[\\r\\n]+" +
|
||||
" color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
|
||||
"}[\\r\\n]*";
|
||||
|
||||
SimpleTest.waitForClipboard(function IUI_testSimpleCopy() {
|
||||
return checkClipboardData(expectedPattern);
|
||||
},
|
||||
checkSimpleCopy, finishup, function() {
|
||||
failedClipboard(expectedPattern, finishup);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkSimpleCopy() {
|
||||
let contentDoc = ruleViewFrame().contentDocument;
|
||||
let props = contentDoc.querySelectorAll(".ruleview-code");
|
||||
|
||||
is(props.length, 2, "checking property length");
|
||||
|
||||
let prop = props[0];
|
||||
|
||||
selectNode(prop);
|
||||
|
||||
// We need the context menu to open in the correct place in order for
|
||||
// popupNode to be propertly set.
|
||||
EventUtils.synthesizeMouse(prop, 1, 1, { type: "contextmenu", button: 2 },
|
||||
ruleViewFrame().contentWindow);
|
||||
|
||||
ruleView()._boundCopy();
|
||||
let menu = contentDoc.querySelector("#rule-view-context-menu");
|
||||
ok(menu, "we have the context menu");
|
||||
menu.hidePopup();
|
||||
}
|
||||
|
||||
function selectNode(aNode) {
|
||||
let doc = aNode.ownerDocument;
|
||||
let win = doc.defaultView;
|
||||
let range = doc.createRange();
|
||||
|
||||
range.selectNode(aNode);
|
||||
win.getSelection().addRange(range);
|
||||
}
|
||||
|
||||
function checkClipboardData(aExpectedPattern)
|
||||
{
|
||||
let actual = SpecialPowers.getClipboardData("text/unicode");
|
||||
|
|
|
@ -35,7 +35,7 @@ interface nsIEventListenerInfo : nsISupports
|
|||
nsISupports getDebugObject();
|
||||
};
|
||||
|
||||
[scriptable, uuid(0cf94aa6-ea9a-44cb-a063-be834afa679d)]
|
||||
[scriptable, uuid(f6964bfb-dabe-4cab-9733-be0ee2bf8171)]
|
||||
interface nsIEventListenerService : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -81,5 +81,16 @@ interface nsIEventListenerService : nsISupports
|
|||
in DOMString type,
|
||||
in nsIDOMEventListener listener,
|
||||
in boolean useCapture);
|
||||
|
||||
void addListenerForAllEvents(in nsIDOMEventTarget target,
|
||||
in nsIDOMEventListener listener,
|
||||
[optional] in boolean aUseCapture,
|
||||
[optional] in boolean aWantsUntrusted,
|
||||
[optional] in boolean aSystemEventGroup);
|
||||
|
||||
void removeListenerForAllEvents(in nsIDOMEventTarget target,
|
||||
in nsIDOMEventListener listener,
|
||||
[optional] in boolean aUseCapture,
|
||||
[optional] in boolean aSystemEventGroup);
|
||||
};
|
||||
|
||||
|
|
|
@ -58,9 +58,10 @@
|
|||
using namespace mozilla::dom;
|
||||
using namespace mozilla::hal;
|
||||
|
||||
#define EVENT_TYPE_EQUALS( ls, type, userType ) \
|
||||
(ls->mEventType == type && \
|
||||
(ls->mEventType != NS_USER_DEFINED_EVENT || ls->mTypeAtom == userType))
|
||||
#define EVENT_TYPE_EQUALS(ls, type, userType, allEvents) \
|
||||
((ls->mEventType == type && \
|
||||
(ls->mEventType != NS_USER_DEFINED_EVENT || ls->mTypeAtom == userType)) || \
|
||||
(allEvents && ls->mAllEvents))
|
||||
|
||||
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
|
||||
NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
|
||||
|
@ -193,9 +194,10 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
|
|||
uint32_t aType,
|
||||
nsIAtom* aTypeAtom,
|
||||
int32_t aFlags,
|
||||
bool aHandler)
|
||||
bool aHandler,
|
||||
bool aAllEvents)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aType && aTypeAtom, "Missing type");
|
||||
NS_ABORT_IF_FALSE((aType && aTypeAtom) || aAllEvents, "Missing type");
|
||||
|
||||
if (!aListener) {
|
||||
return;
|
||||
|
@ -210,7 +212,7 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
|
|||
if (ls->mListener == aListener &&
|
||||
ls->mListenerIsHandler == aHandler &&
|
||||
ls->mFlags == aFlags &&
|
||||
EVENT_TYPE_EQUALS(ls, aType, aTypeAtom)) {
|
||||
EVENT_TYPE_EQUALS(ls, aType, aTypeAtom, aAllEvents)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -218,13 +220,14 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
|
|||
mNoListenerForEvent = NS_EVENT_TYPE_NULL;
|
||||
mNoListenerForEventAtom = nullptr;
|
||||
|
||||
ls = mListeners.AppendElement();
|
||||
ls = aAllEvents ? mListeners.InsertElementAt(0) : mListeners.AppendElement();
|
||||
ls->mListener = aListener;
|
||||
ls->mEventType = aType;
|
||||
ls->mTypeAtom = aTypeAtom;
|
||||
ls->mFlags = aFlags;
|
||||
ls->mListenerIsHandler = aHandler;
|
||||
ls->mHandlerIsString = false;
|
||||
ls->mAllEvents = aAllEvents;
|
||||
|
||||
// Detect the type of event listener.
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wjs;
|
||||
|
@ -406,7 +409,8 @@ void
|
|||
nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener,
|
||||
uint32_t aType,
|
||||
nsIAtom* aUserType,
|
||||
int32_t aFlags)
|
||||
int32_t aFlags,
|
||||
bool aAllEvents)
|
||||
{
|
||||
if (!aListener || !aType) {
|
||||
return;
|
||||
|
@ -424,7 +428,7 @@ nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener,
|
|||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
ls = &mListeners.ElementAt(i);
|
||||
if (EVENT_TYPE_EQUALS(ls, aType, aUserType)) {
|
||||
if (EVENT_TYPE_EQUALS(ls, aType, aUserType, aAllEvents)) {
|
||||
++typeCount;
|
||||
if (ls->mListener == aListener &&
|
||||
(ls->mFlags & ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED) == aFlags) {
|
||||
|
@ -442,14 +446,14 @@ nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener,
|
|||
}
|
||||
}
|
||||
|
||||
if (deviceType && typeCount == 0) {
|
||||
if (!aAllEvents && deviceType && typeCount == 0) {
|
||||
DisableDevice(aType);
|
||||
} else if (timeChangeEvent && typeCount == 0) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (window) {
|
||||
window->DisableTimeChangeNotifications();
|
||||
}
|
||||
} else if (networkEvent && typeCount == 0) {
|
||||
} else if (!aAllEvents && networkEvent && typeCount == 0) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (window) {
|
||||
window->DisableNetworkEvent(aType);
|
||||
|
@ -463,9 +467,9 @@ ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent)
|
|||
// This is slightly different from EVENT_TYPE_EQUALS in that it returns
|
||||
// true even when aEvent->message == NS_USER_DEFINED_EVENT and
|
||||
// aLs=>mEventType != NS_USER_DEFINED_EVENT as long as the atoms are the same
|
||||
return aEvent->message == NS_USER_DEFINED_EVENT ?
|
||||
return (aEvent->message == NS_USER_DEFINED_EVENT ?
|
||||
(aLs->mTypeAtom == aEvent->userType) :
|
||||
(aLs->mEventType == aEvent->message);
|
||||
(aLs->mEventType == aEvent->message)) || aLs->mAllEvents;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -498,8 +502,8 @@ nsEventListenerManager::FindEventHandler(uint32_t aEventType,
|
|||
uint32_t count = mListeners.Length();
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
ls = &mListeners.ElementAt(i);
|
||||
if (ls->mListenerIsHandler && EVENT_TYPE_EQUALS(ls, aEventType, aTypeAtom))
|
||||
{
|
||||
if (ls->mListenerIsHandler &&
|
||||
EVENT_TYPE_EQUALS(ls, aEventType, aTypeAtom, false)) {
|
||||
return ls;
|
||||
}
|
||||
}
|
||||
|
@ -992,6 +996,34 @@ nsEventListenerManager::RemoveEventListener(const nsAString& aType,
|
|||
RemoveEventListenerByType(aListener, aType, flags);
|
||||
}
|
||||
|
||||
void
|
||||
nsEventListenerManager::AddListenerForAllEvents(nsIDOMEventListener* aListener,
|
||||
bool aUseCapture,
|
||||
bool aWantsUntrusted,
|
||||
bool aSystemEventGroup)
|
||||
{
|
||||
PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
|
||||
if (aWantsUntrusted) {
|
||||
flags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
|
||||
}
|
||||
if (aSystemEventGroup) {
|
||||
flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
|
||||
}
|
||||
AddEventListener(aListener, NS_EVENT_TYPE_ALL, nullptr, flags, false, true);
|
||||
}
|
||||
|
||||
void
|
||||
nsEventListenerManager::RemoveListenerForAllEvents(nsIDOMEventListener* aListener,
|
||||
bool aUseCapture,
|
||||
bool aSystemEventGroup)
|
||||
{
|
||||
PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
|
||||
if (aSystemEventGroup) {
|
||||
flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
|
||||
}
|
||||
RemoveEventListener(aListener, NS_EVENT_TYPE_ALL, nullptr, flags, true);
|
||||
}
|
||||
|
||||
bool
|
||||
nsEventListenerManager::HasMutationListeners()
|
||||
{
|
||||
|
|
|
@ -46,6 +46,7 @@ struct nsListenerStruct
|
|||
uint8_t mListenerType;
|
||||
bool mListenerIsHandler : 1;
|
||||
bool mHandlerIsString : 1;
|
||||
bool mAllEvents : 1;
|
||||
|
||||
nsIJSEventListener* GetJSListener() const {
|
||||
return (mListenerType == eJSEventListener) ?
|
||||
|
@ -83,6 +84,14 @@ public:
|
|||
nsIDOMEventListener* aListener,
|
||||
bool aUseCapture);
|
||||
|
||||
void AddListenerForAllEvents(nsIDOMEventListener* aListener,
|
||||
bool aUseCapture,
|
||||
bool aWantsUntrusted,
|
||||
bool aSystemEventGroup);
|
||||
void RemoveListenerForAllEvents(nsIDOMEventListener* aListener,
|
||||
bool aUseCapture,
|
||||
bool aSystemEventGroup);
|
||||
|
||||
/**
|
||||
* Sets events listeners of all types.
|
||||
* @param an event listener
|
||||
|
@ -292,11 +301,13 @@ protected:
|
|||
uint32_t aType,
|
||||
nsIAtom* aTypeAtom,
|
||||
int32_t aFlags,
|
||||
bool aHandler = false);
|
||||
bool aHandler = false,
|
||||
bool aAllEvents = false);
|
||||
void RemoveEventListener(nsIDOMEventListener *aListener,
|
||||
uint32_t aType,
|
||||
nsIAtom* aUserType,
|
||||
int32_t aFlags);
|
||||
int32_t aFlags,
|
||||
bool aAllEvents = false);
|
||||
void RemoveAllListeners();
|
||||
const EventTypeData* GetTypeDataForIID(const nsIID& aIID);
|
||||
const EventTypeData* GetTypeDataForEventName(nsIAtom* aName);
|
||||
|
|
|
@ -279,6 +279,35 @@ nsEventListenerService::RemoveSystemEventListener(nsIDOMEventTarget *aTarget,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsEventListenerService::AddListenerForAllEvents(nsIDOMEventTarget* aTarget,
|
||||
nsIDOMEventListener* aListener,
|
||||
bool aUseCapture,
|
||||
bool aWantsUntrusted,
|
||||
bool aSystemEventGroup)
|
||||
{
|
||||
NS_ENSURE_STATE(aTarget && aListener);
|
||||
nsEventListenerManager* manager = aTarget->GetListenerManager(true);
|
||||
NS_ENSURE_STATE(manager);
|
||||
manager->AddListenerForAllEvents(aListener, aUseCapture, aWantsUntrusted,
|
||||
aSystemEventGroup);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsEventListenerService::RemoveListenerForAllEvents(nsIDOMEventTarget* aTarget,
|
||||
nsIDOMEventListener* aListener,
|
||||
bool aUseCapture,
|
||||
bool aSystemEventGroup)
|
||||
{
|
||||
NS_ENSURE_STATE(aTarget && aListener);
|
||||
nsEventListenerManager* manager = aTarget->GetListenerManager(false);
|
||||
if (manager) {
|
||||
manager->RemoveListenerForAllEvents(aListener, aUseCapture, aSystemEventGroup);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NS_NewEventListenerService(nsIEventListenerService** aResult)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=448602
|
|||
|
||||
/** Test for Bug 448602 **/
|
||||
|
||||
var els, root, l2, l3;
|
||||
|
||||
function runTests() {
|
||||
/*
|
||||
|
@ -27,11 +28,11 @@ function runTests() {
|
|||
var jsd = SpecialPowers.Components.classes['@mozilla.org/js/jsd/debugger-service;1']
|
||||
.getService(jsdIDebuggerService);
|
||||
*/
|
||||
var els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(SpecialPowers.Ci.nsIEventListenerService);
|
||||
els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(SpecialPowers.Ci.nsIEventListenerService);
|
||||
|
||||
// Event listener info tests
|
||||
var root = document.getElementById("testroot");
|
||||
root = document.getElementById("testroot");
|
||||
var infos = els.getListenerInfoFor(root, {});
|
||||
is(infos.length, 0, "Element shouldn't have listeners (1)");
|
||||
|
||||
|
@ -103,8 +104,8 @@ function runTests() {
|
|||
is(infos[0].allowsUntrusted, true, "Should allow untrusted events (3)");
|
||||
|
||||
// Event target chain tests
|
||||
var l2 = document.getElementById("testlevel2");
|
||||
var l3 = document.getElementById("testlevel3");
|
||||
l2 = document.getElementById("testlevel2");
|
||||
l3 = document.getElementById("testlevel3");
|
||||
var textnode = l3.firstChild;
|
||||
var chain = SpecialPowers.unwrap(els.getEventTargetChainFor(textnode, {}));
|
||||
ok(chain.length > 3, "Too short event target chain.");
|
||||
|
@ -138,10 +139,82 @@ function runTests() {
|
|||
} catch (ex) {
|
||||
ok(true, "We should be still running.");
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
setTimeout(testAllListener, 0);
|
||||
}
|
||||
|
||||
function dispatchTrusted(t, o) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
t.dispatchEvent(new Event("testevent", o));
|
||||
}
|
||||
|
||||
function testAllListener() {
|
||||
els = SpecialPowers.wrap(els);
|
||||
var results = [];
|
||||
var expectedResults =
|
||||
[ { target: "testlevel3", phase: 3, trusted: false },
|
||||
{ target: "testlevel3", phase: 3, trusted: false },
|
||||
{ target: "testlevel3", phase: 3, trusted: true },
|
||||
{ target: "testlevel3", phase: 3, trusted: true },
|
||||
{ target: "testlevel3", phase: 3, trusted: true }
|
||||
];
|
||||
|
||||
function allListener(e) {
|
||||
results.push({
|
||||
target: e.target.id,
|
||||
phase: e.eventPhase,
|
||||
trusted: e.isTrusted
|
||||
});
|
||||
e.stopPropagation();
|
||||
}
|
||||
function allListenerTrustedOnly(e) {
|
||||
results.push({
|
||||
target: e.target.id,
|
||||
phase: e.eventPhase,
|
||||
trusted: e.isTrusted
|
||||
});
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
els.addListenerForAllEvents(root, allListener, false, true);
|
||||
els.addListenerForAllEvents(root, allListener, false, true, true);
|
||||
els.addListenerForAllEvents(root, allListenerTrustedOnly, false, false, true);
|
||||
l3.dispatchEvent(new Event("testevent", { bubbles: true }));
|
||||
dispatchTrusted(l3, { bubbles: true });
|
||||
els.removeListenerForAllEvents(root, allListener, false);
|
||||
els.removeListenerForAllEvents(root, allListener, false, true);
|
||||
els.removeListenerForAllEvents(root, allListenerTrustedOnly, false, true);
|
||||
// make sure removeListenerForAllEvents works.
|
||||
l3.dispatchEvent(new Event("testevent", { bubbles: true }));
|
||||
dispatchTrusted(l3, { bubbles: true });
|
||||
|
||||
// Test the order of event listeners.
|
||||
var clickListenerCalled = false;
|
||||
var allListenerCalled = false;
|
||||
function clickListener() {
|
||||
clickListenerCalled = true;
|
||||
ok(allListenerCalled, "Should have called '*' listener before normal listener!");
|
||||
}
|
||||
function allListener2() {
|
||||
allListenerCalled = true;
|
||||
notok(clickListenerCalled, "Shouldn't have called click listener before '*' listener!");
|
||||
}
|
||||
root.onclick = null; // Remove the listener added in earlier tests.
|
||||
root.addEventListener("click", clickListener);
|
||||
els.addListenerForAllEvents(root, allListener2, false, true);
|
||||
l3.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
root.removeEventListener("click", clickListener);
|
||||
els.removeListenerForAllEvents(root, allListener2, false);
|
||||
ok(allListenerCalled, "Should have called '*' listener");
|
||||
ok(clickListenerCalled, "Should have called click listener");
|
||||
|
||||
is(results.length, expectedResults.length, "count");
|
||||
for (var i = 0; i < expectedResults.length; ++i) {
|
||||
for (var p in expectedResults[i]) {
|
||||
is(results[i][p], expectedResults[i][p], p);
|
||||
}
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(runTests);
|
||||
|
|
|
@ -134,6 +134,7 @@ class nsHashKey;
|
|||
#define NS_EVENT_BUBBLE_MASK (~(NS_EVENT_FLAG_CAPTURE | NS_EVENT_FLAG_NO_CONTENT_DISPATCH))
|
||||
|
||||
#define NS_EVENT_TYPE_NULL 0
|
||||
#define NS_EVENT_TYPE_ALL 1 // Not a real event type
|
||||
|
||||
/**
|
||||
* GUI MESSAGES
|
||||
|
|
Загрузка…
Ссылка в новой задаче