Merge remote-tracking branch 'origin/master' into media-works

This commit is contained in:
2022-04-18 11:31:14 +08:00
23 changed files with 2614 additions and 278 deletions
+3 -3
View File
@@ -493,7 +493,7 @@ WINDOW_EVENT(hashchange,
EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
eBasicEventClass)
WINDOW_EVENT(languagechange,
NS_LANGUAGECHANGE,
eLanguageChange,
EventNameType_HTMLBodyOrFramesetOnly,
eBasicEventClass)
// XXXbz Should the onmessage attribute on <body> really not work? If so, do we
@@ -504,11 +504,11 @@ WINDOW_EVENT(message,
EventNameType_None,
eBasicEventClass)
WINDOW_EVENT(offline,
NS_OFFLINE,
eOffline,
EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
eBasicEventClass)
WINDOW_EVENT(online,
NS_ONLINE,
eOnline,
EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
eBasicEventClass)
WINDOW_EVENT(pagehide,
+92 -18
View File
@@ -375,7 +375,7 @@ MozInputMethod.prototype = {
},
addInput: function(inputId, inputManifest) {
return this._sendPromise(function(resolverId) {
return this.createPromiseWithId(function(resolverId) {
let appId = this._window.document.nodePrincipal.appId;
cpmm.sendAsyncMessage('InputRegistry:Add', {
@@ -567,7 +567,7 @@ MozInputContext.prototype = {
switch (msg.name) {
case "Keyboard:SendKey:Result:OK":
resolver.resolve();
resolver.resolve(true);
break;
case "Keyboard:SendKey:Result:Error":
resolver.reject(json.error);
@@ -590,7 +590,7 @@ MozInputContext.prototype = {
break;
case "Keyboard:SetComposition:Result:OK": // Fall through.
case "Keyboard:EndComposition:Result:OK":
resolver.resolve();
resolver.resolve(true);
break;
default:
dump("Could not find a handler for " + msg.name);
@@ -732,42 +732,79 @@ MozInputContext.prototype = {
return this.replaceSurroundingText(null, offset, length);
},
sendKey: function ic_sendKey(keyCode, charCode, modifiers, repeat) {
let self = this;
sendKey: function ic_sendKey(dictOrKeyCode, charCode, modifiers, repeat) {
if (typeof dictOrKeyCode === 'number') {
// XXX: modifiers are ignored in this API method.
// XXX: modifiers are ignored in this API method.
return this._sendPromise((resolverId) => {
cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
contextId: this._contextId,
requestId: resolverId,
method: 'sendKey',
keyCode: dictOrKeyCode,
charCode: charCode,
repeat: repeat
});
});
} else if (typeof dictOrKeyCode === 'object') {
return this._sendPromise((resolverId) => {
cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
contextId: this._contextId,
requestId: resolverId,
method: 'sendKey',
keyboardEventDict: this._getkeyboardEventDict(dictOrKeyCode)
});
});
} else {
// XXX: Should not reach here; implies WebIDL binding error.
throw new TypeError('Unknown argument passed.');
}
},
return this._sendPromise(function(resolverId) {
cpmmSendAsyncMessageWithKbID(self, 'Keyboard:SendKey', {
contextId: self._contextId,
keydown: function ic_keydown(dict) {
return this._sendPromise((resolverId) => {
cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
contextId: this._contextId,
requestId: resolverId,
method: 'keydown',
keyboardEventDict: this._getkeyboardEventDict(dict)
});
});
},
keyup: function ic_keyup(dict) {
return this._sendPromise((resolverId) => {
cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
contextId: this._contextId,
requestId: resolverId,
keyCode: keyCode,
charCode: charCode,
repeat: repeat
method: 'keyup',
keyboardEventDict: this._getkeyboardEventDict(dict)
});
});
},
setComposition: function ic_setComposition(text, cursor, clauses) {
setComposition: function ic_setComposition(text, cursor, clauses, dict) {
let self = this;
return this._sendPromise(function(resolverId) {
return this._sendPromise((resolverId) => {
cpmmSendAsyncMessageWithKbID(self, 'Keyboard:SetComposition', {
contextId: self._contextId,
requestId: resolverId,
text: text,
cursor: (typeof cursor !== 'undefined') ? cursor : text.length,
clauses: clauses || null
clauses: clauses || null,
keyboardEventDict: this._getkeyboardEventDict(dict)
});
});
},
endComposition: function ic_endComposition(text) {
endComposition: function ic_endComposition(text, dict) {
let self = this;
return this._sendPromise(function(resolverId) {
return this._sendPromise((resolverId) => {
cpmmSendAsyncMessageWithKbID(self, 'Keyboard:EndComposition', {
contextId: self._contextId,
requestId: resolverId,
text: text || ''
text: text || '',
keyboardEventDict: this._getkeyboardEventDict(dict)
});
});
},
@@ -782,6 +819,43 @@ MozInputContext.prototype = {
}
callback(aResolverId);
});
},
// Take a MozInputMethodKeyboardEventDict dict, creates a keyboardEventDict
// object that can be sent to forms.js
_getkeyboardEventDict: function(dict) {
if (typeof dict !== 'object' || !dict.key) {
return;
}
var keyboardEventDict = {
key: dict.key,
code: dict.code,
repeat: dict.repeat,
flags: 0
};
if (dict.printable) {
keyboardEventDict.flags |=
Ci.nsITextInputProcessor.KEY_FORCE_PRINTABLE_KEY;
}
if (/^[a-zA-Z0-9]$/.test(dict.key)) {
// keyCode must follow the key value in this range;
// disregard the keyCode from content.
keyboardEventDict.keyCode = dict.key.toUpperCase().charCodeAt(0);
} else if (typeof dict.keyCode === 'number') {
// Allow keyCode to be specified for other key values.
keyboardEventDict.keyCode = dict.keyCode;
// Allow keyCode to be explicitly set to zero.
if (dict.keyCode === 0) {
keyboardEventDict.flags |=
Ci.nsITextInputProcessor.KEY_KEEP_KEYCODE_ZERO;
}
}
return keyboardEventDict;
}
};
+111 -45
View File
@@ -11,6 +11,7 @@ dump("###################################### forms.js loaded\n");
let Ci = Components.interfaces;
let Cc = Components.classes;
let Cu = Components.utils;
let Cr = Components.results;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
@@ -650,7 +651,7 @@ let FormAssistant = {
break;
case "keydown":
if (!this.focusedElement) {
if (!this.focusedElement || this._editing) {
break;
}
@@ -658,7 +659,7 @@ let FormAssistant = {
break;
case "keyup":
if (!this.focusedElement) {
if (!this.focusedElement || this._editing) {
break;
}
@@ -733,55 +734,100 @@ let FormAssistant = {
break;
}
// The naive way to figure out if the key to dispatch is printable.
let printable = !!json.charCode;
// If we receive a keyboardEventDict from json, that means the user
// is calling the method with the new arguments.
// Otherwise, we would have to construct our own keyboardEventDict
// based on legacy values we have received.
let keyboardEventDict = json.keyboardEventDict;
let flags = 0;
if (keyboardEventDict) {
if ('flags' in keyboardEventDict) {
flags = keyboardEventDict.flags;
}
} else {
// The naive way to figure out if the key to dispatch is printable.
let printable = !!json.charCode;
let keyboardEventDict = {
// For printable keys, the value should be the actual character.
// For non-printable keys, it should be a value in the D3E spec.
// Here we make some educated guess for it.
key: printable ?
String.fromCharCode(json.charCode) :
guessKeyNameFromKeyCode(win.KeyboardEvent, json.keyCode),
// We don't have any information to tell the virtual key the
// user have interacted with.
code: "",
// We violate the spec here and ask TextInputProcessor not to infer
// this value from value of key nor code so we could keep the original
let key = printable ?
String.fromCharCode(json.charCode) :
guessKeyNameFromKeyCode(win.KeyboardEvent, json.keyCode);
// keyCode from content is only respected when the key is not an
// an alphanumeric character. We also ask TextInputProcessor not to
// infer this value for non-printable keys to keep the original
// behavior.
keyCode: json.keyCode,
// We do not have the information to infer location of the virtual key
// either (and we would need TextInputProcessor not to compute it).
location: 0,
// This indicates the key is triggered for repeats.
repeat: json.repeat
};
let keyCode = (printable && /^[a-zA-Z0-9]$/.test(key)) ?
key.toUpperCase().charCodeAt(0) :
json.keyCode;
keyboardEventDict = {
key: key,
keyCode: keyCode,
// We don't have any information to tell the virtual key the
// user have interacted with.
code: "",
// We do not have the information to infer location of the virtual key
// either (and we would need TextInputProcessor not to compute it).
location: 0,
// This indicates the key is triggered for repeats.
repeat: json.repeat
};
flags = tip.KEY_KEEP_KEY_LOCATION_STANDARD;
if (!printable) {
flags |= tip.KEY_NON_PRINTABLE_KEY;
}
if (!keyboardEventDict.keyCode) {
flags |= tip.KEY_KEEP_KEYCODE_ZERO;
}
}
let keyboardEvent = new win.KeyboardEvent("", keyboardEventDict);
let flags = tip.KEY_KEEP_KEY_LOCATION_STANDARD;
if (!printable) {
flags |= tip.KEY_NON_PRINTABLE_KEY;
}
if (!json.keyCode) {
flags |= tip.KEY_KEEP_KEYCODE_ZERO;
}
let keydownDefaultPrevented;
let keydownDefaultPrevented = false;
try {
let consumedFlags = tip.keydown(keyboardEvent, flags);
keydownDefaultPrevented =
!!(tip.KEYDOWN_IS_CONSUMED & consumedFlags);
if (!json.repeat) {
tip.keyup(keyboardEvent, flags);
switch (json.method) {
case 'sendKey': {
let consumedFlags = tip.keydown(keyboardEvent, flags);
keydownDefaultPrevented =
!!(tip.KEYDOWN_IS_CONSUMED & consumedFlags);
if (!keyboardEventDict.repeat) {
tip.keyup(keyboardEvent, flags);
}
break;
}
case 'keydown': {
let consumedFlags = tip.keydown(keyboardEvent, flags);
keydownDefaultPrevented =
!!(tip.KEYDOWN_IS_CONSUMED & consumedFlags);
break;
}
case 'keyup': {
tip.keyup(keyboardEvent, flags);
break;
}
}
} catch (e) {
dump("forms.js:" + e.toString() + "\n");
} catch (err) {
dump("forms.js:" + err.toString() + "\n");
if (json.requestId) {
sendAsyncMessage("Forms:SendKey:Result:Error", {
requestId: json.requestId,
error: "Unable to type into destoryed input."
});
if (err instanceof Ci.nsIException &&
err.result == Cr.NS_ERROR_ILLEGAL_VALUE) {
sendAsyncMessage("Forms:SendKey:Result:Error", {
requestId: json.requestId,
error: "The values specified are illegal."
});
} else {
sendAsyncMessage("Forms:SendKey:Result:Error", {
requestId: json.requestId,
error: "Unable to type into destroyed input."
});
}
}
break;
@@ -913,7 +959,7 @@ let FormAssistant = {
case "Forms:SetComposition": {
CompositionManager.setComposition(target, json.text, json.cursor,
json.clauses);
json.clauses, json.keyboardEventDict);
sendAsyncMessage("Forms:SetComposition:Result:OK", {
requestId: json.requestId,
selectioninfo: this.getSelectionInfo()
@@ -922,7 +968,7 @@ let FormAssistant = {
}
case "Forms:EndComposition": {
CompositionManager.endComposition(json.text);
CompositionManager.endComposition(json.text, json.keyboardEventDict);
sendAsyncMessage("Forms:EndComposition:Result:OK", {
requestId: json.requestId,
selectioninfo: this.getSelectionInfo()
@@ -1442,6 +1488,7 @@ function replaceSurroundingText(element, text, offset, length) {
let CompositionManager = {
_isStarted: false,
_tip: null,
_KeyboardEventForWin: null,
_clauseAttrMap: {
'raw-input':
Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE,
@@ -1453,7 +1500,7 @@ let CompositionManager = {
Ci.nsITextInputProcessor.ATTR_SELECTED_CLAUSE
},
setComposition: function cm_setComposition(element, text, cursor, clauses) {
setComposition: function cm_setComposition(element, text, cursor, clauses, dict) {
// Check parameters.
if (!element) {
return;
@@ -1506,13 +1553,22 @@ let CompositionManager = {
if (cursor >= 0) {
tip.setCaretInPendingComposition(cursor);
}
this._isStarted = tip.flushPendingComposition();
if (!dict) {
this._isStarted = tip.flushPendingComposition();
} else {
let keyboardEvent = new win.KeyboardEvent("", dict);
let flags = dict.flags;
this._isStarted = tip.flushPendingComposition(keyboardEvent, flags);
}
if (this._isStarted) {
this._tip = tip;
this._KeyboardEventForWin = win.KeyboardEvent;
}
},
endComposition: function cm_endComposition(text) {
endComposition: function cm_endComposition(text, dict) {
if (!this._isStarted) {
return;
}
@@ -1521,9 +1577,18 @@ let CompositionManager = {
return;
}
tip.commitCompositionWith(text ? text : "");
text = text || "";
if (!dict) {
tip.commitCompositionWith(text);
} else {
let keyboardEvent = new this._KeyboardEventForWin("", dict);
let flags = dict.flags;
tip.commitCompositionWith(text, keyboardEvent, flags);
}
this._isStarted = false;
this._tip = null;
this._KeyboardEventForWin = null;
},
// Composition ends due to external actions.
@@ -1534,5 +1599,6 @@ let CompositionManager = {
this._isStarted = false;
this._tip = null;
this._KeyboardEventForWin = null;
}
};
+2
View File
@@ -25,3 +25,5 @@ support-files =
[test_sync_edit.html]
[test_two_inputs.html]
[test_two_selects.html]
[test_unload.html]
[test_bug1137557.html]
File diff suppressed because it is too large Load Diff
+167
View File
@@ -0,0 +1,167 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1122463
https://bugzilla.mozilla.org/show_bug.cgi?id=820057
-->
<head>
<title>Test focus when page unloads</title>
<script type="application/javascript;version=1.7" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript;version=1.7" src="inputmethod_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1122463">Mozilla Bug 1122463</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=820057">Mozilla Bug 820057</a>
<p id="display"></p>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.7">
inputmethod_setup(function() {
runTest();
});
let appFrameScript = function appFrameScript() {
let form1 = content.document.body.firstElementChild;
let input1 = form1.firstElementChild;
let submit1 = form1.lastElementChild;
let input2;
let cancelSubmit = function(evt) {
evt.preventDefault();
};
// Content of the second page.
form1.action = 'data:text/html,<html><body><input value="Second"></body></html>';
let i = 1;
input1.focus();
addMessageListener('test:next', function() {
i++;
switch (i) {
case 2:
// Click the submit button, trigger the submit event and make our
// installed event listener preventing the submission.
form1.addEventListener('submit', cancelSubmit);
submit1.click();
sendAsyncMessage('test:step');
break;
case 3:
// Actually submit the form.
form1.removeEventListener('submit', cancelSubmit);
submit1.click();
break;
case 4:
if (!content.document.body) {
content.onload = function() {
content.onload = null;
let input2 = content.document.body.firstElementChild;
input2.focus();
};
return;
}
input2 = content.document.body.firstElementChild;
input2.focus();
break;
case 5:
content.location.href = 'data:text/html,Hello!';
break;
}
});
};
function runTest() {
let im = navigator.mozInputMethod;
let i = 0;
function nextStep() {
let inputcontext = navigator.mozInputMethod.inputcontext;
i++;
switch (i) {
// focus on the first input receives the first input context.
case 1:
ok(!!inputcontext, '1) Receving the first input context');
is(inputcontext.textAfterCursor, 'First');
mm.sendAsyncMessage('test:next');
break;
// Cancelled submission should not cause us lost focus.
case 2:
ok(!!inputcontext, '2) Receving the first input context');
is(inputcontext.textAfterCursor, 'First');
mm.sendAsyncMessage('test:next');
break;
// Real submit and page transition should cause us lost focus.
// XXX: Unless we could delay the page transition, we does not know if
// the inputcontext is lost because of the submit or the pagehide/beforeload
// event.
case 3:
is(inputcontext, null, '3) Receving null inputcontext');
mm.sendAsyncMessage('test:next');
break;
// Regaining focus of input in the second page.
case 4:
ok(!!inputcontext, '4) Receving the second input context');
is(inputcontext.textAfterCursor, 'Second');
mm.sendAsyncMessage('test:next');
break;
// Page transition should cause us lost focus
case 5:
is(inputcontext, null, '5) Receving null inputcontext');
inputmethod_cleanup();
break;
}
}
// Set current page as an input method.
SpecialPowers.wrap(im).setActive(true);
let iframe = document.createElement('iframe');
iframe.src = 'data:text/html,<html><body><form id="form"><input value="First"><input type="submit"></form></body></html>';
iframe.setAttribute('mozbrowser', true);
document.body.appendChild(iframe);
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
im.oninputcontextchange = nextStep;
let frameScriptLoaded = false;
iframe.addEventListener('mozbrowserloadend', function() {
if (frameScriptLoaded)
return;
frameScriptLoaded = true;
mm.addMessageListener('test:step', nextStep);
mm.loadFrameScript('data:,(' + encodeURIComponent(appFrameScript.toString()) + ')();', false);
});
}
</script>
</pre>
</body>
</html>
+2 -2
View File
@@ -196,7 +196,7 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam
if (!win)
return TRUE;
// The DispatchEvent(NS_PLUGIN_ACTIVATE) below can trigger a reentrant focus
// The DispatchEvent(ePluginActivate) below can trigger a reentrant focus
// event which might destroy us. Hold a strong ref on the plugin instance
// to prevent that, bug 374229.
nsRefPtr<nsNPAPIPluginInstance> inst;
@@ -271,7 +271,7 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam
nsCOMPtr<nsIWidget> widget;
win->GetPluginWidget(getter_AddRefs(widget));
if (widget) {
WidgetGUIEvent event(true, NS_PLUGIN_ACTIVATE, widget);
WidgetGUIEvent event(true, ePluginActivate, widget);
nsEventStatus status;
widget->DispatchEvent(&event, status);
}
+126 -17
View File
@@ -193,17 +193,54 @@ interface MozInputContext: EventTarget {
attribute EventHandler onsurroundingtextchange;
/*
* send a character with its key events.
* @param modifiers this paramater is no longer honored.
* @param repeat indicates whether a key would be sent repeatedly.
* @return true if succeeds. Otherwise false if the input context becomes void.
* Alternative: sendKey(KeyboardEvent event), but we will likely
* waste memory for creating the KeyboardEvent object.
* Note that, if you want to send a key n times repeatedly, make sure set
* parameter repeat to true and invoke sendKey n-1 times, and then set
* repeat to false in the last invoke.
*/
Promise<boolean> sendKey(long keyCode, long charCode, long modifiers, optional boolean repeat);
* Send a string/character with its key events. There are two ways of invocating
* the method for backward compability purpose.
*
* (1) The recommended way, allow specifying DOM level 3 properties like |code|.
* @param dictOrKeyCode See MozInputMethodKeyboardEventDict.
* @param charCode disregarded
* @param modifiers disregarded
* @param repeat disregarded
*
* (2) Deprecated, reserved for backward compability.
* @param dictOrKeyCode keyCode of the key to send, should be one of the DOM_VK_ value in KeyboardEvent.
* @param charCode charCode of the character, should be 0 for non-printable keys.
* @param modifiers this paramater is no longer honored.
* @param repeat indicates whether a key would be sent repeatedly.
*
* @return A promise. Resolve to true if succeeds.
* Rejects to a string indicating the error.
*
* Note that, if you want to send a key n times repeatedly, make sure set
* parameter repeat to true and invoke sendKey n times, and invoke keyup
* after the end of the input.
*/
Promise<boolean> sendKey((MozInputMethodRequiredKeyboardEventDict or long) dictOrKeyCode,
optional long charCode,
optional long modifiers,
optional boolean repeat);
/*
* Send a string/character with keydown, and keypress events.
* keyup should be called afterwards to ensure properly sequence.
*
* @param dict See MozInputMethodKeyboardEventDict.
*
* @return A promise. Resolve to true if succeeds.
* Rejects to a string indicating the error.
*/
Promise<boolean> keydown(MozInputMethodRequiredKeyboardEventDict dict);
/*
* Send a keyup event. keydown should be called first to ensure properly sequence.
*
* @param dict See MozInputMethodKeyboardEventDict.
*
* @return A promise. Resolve to true if succeeds.
* Rejects to a string indicating the error.
*
*/
Promise<boolean> keyup(MozInputMethodRequiredKeyboardEventDict dict);
/*
* Set current composing text. This method will start composition or update
@@ -219,7 +256,11 @@ interface MozInputContext: EventTarget {
* cursor will be positioned after the composition text.
* @param clauses The array of composition clause information. If not set,
* only one clause is supported.
*
* @param dict The properties of the keyboard event that cause the composition
* to set. keydown or keyup event will be fired if it's necessary.
* For compatibility, we recommend that you should always set this argument
* if it's caused by a key operation.
*
* The composing text, which is shown with underlined style to distinguish
* from the existing text, is used to compose non-ASCII characters from
* keystrokes, e.g. Pinyin or Hiragana. The composing text is the
@@ -232,9 +273,10 @@ interface MozInputContext: EventTarget {
* To finish composition and commit text to current input field, an IME
* should call |endComposition|.
*/
// XXXbz what is this promise resolved with?
Promise<any> setComposition(DOMString text, optional long cursor,
optional sequence<CompositionClauseParameters> clauses);
Promise<boolean> setComposition(DOMString text,
optional long cursor,
optional sequence<CompositionClauseParameters> clauses,
optional MozInputMethodKeyboardEventDict dict);
/*
* End composition, clear the composing text and commit given text to
@@ -242,6 +284,10 @@ interface MozInputContext: EventTarget {
* position.
* @param text The text to commited before cursor position. If empty string
* is given, no text will be committed.
* @param dict The properties of the keyboard event that cause the composition
* to end. keydown or keyup event will be fired if it's necessary.
* For compatibility, we recommend that you should always set this argument
* if it's caused by a key operation.
*
* Note that composition always ends automatically with nothing to commit if
* the composition does not explicitly end by calling |endComposition|, but
@@ -249,8 +295,8 @@ interface MozInputContext: EventTarget {
* |replaceSurroundingText|, |deleteSurroundingText|, user moving the
* cursor, changing the focus, etc.
*/
// XXXbz what is this promise resolved with?
Promise<any> endComposition(optional DOMString text);
Promise<boolean> endComposition(optional DOMString text,
optional MozInputMethodKeyboardEventDict dict);
};
enum CompositionClauseSelectionType {
@@ -264,3 +310,66 @@ dictionary CompositionClauseParameters {
DOMString selectionType = "raw-input";
long length;
};
/*
* A MozInputMethodKeyboardEventDictBase contains the following properties,
* indicating the properties of the keyboard event caused.
*
* This is the base dictionary type for us to create two child types that could
* be used as argument type in two types of methods, as WebIDL parser required.
*
*/
dictionary MozInputMethodKeyboardEventDictBase {
/*
* String/character to output, or a registered name of non-printable key.
* (To be defined in the inheriting dictionary types.)
*/
// DOMString key;
/*
* String/char indicating the virtual hardware key pressed. Optional.
* Must be a value defined in
* http://www.w3.org/TR/DOM-Level-3-Events-code/#keyboard-chording-virtual
* If your keyboard app emulates physical keyboard layout, this value should
* not be empty string. Otherwise, it should be empty string.
*/
DOMString code = "";
/*
* keyCode of the keyboard event. Optional.
* To be disregarded if |key| is an alphanumeric character.
* If the key causes inputting a character and if your keyboard app emulates
* a physical keyboard layout, this value should be same as the value used
* by Firefox for desktop. If the key causes inputting an ASCII character
* but if your keyboard app doesn't emulate any physical keyboard layouts,
* the value should be proper value for the key value.
*/
long? keyCode;
/*
* Indicates whether a key would be sent repeatedly. Optional.
*/
boolean repeat = false;
/*
* Optional. True if |key| property is explicitly referring to a printable key.
* When this is set, key will be printable even if the |key| value matches any
* of the registered name of non-printable keys.
*/
boolean printable = false;
};
/*
* For methods like setComposition() and endComposition(), the optional
* dictionary type argument is really optional when all of it's property
* are optional.
* This dictionary type is used to denote that argument.
*/
dictionary MozInputMethodKeyboardEventDict : MozInputMethodKeyboardEventDictBase {
DOMString? key;
};
/*
* For methods like keydown() and keyup(), the dictionary type argument is
* considered required only if at least one of it's property is required.
* This dictionary type is used to denote that argument.
*/
dictionary MozInputMethodRequiredKeyboardEventDict : MozInputMethodKeyboardEventDictBase {
required DOMString key;
};
+2 -2
View File
@@ -898,8 +898,8 @@ nsListControlFrame::HandleEvent(nsPresContext* aPresContext,
"NS_MOUSE_LEFT_CLICK",
"NS_MOUSE_MIDDLE_CLICK",
"NS_MOUSE_RIGHT_CLICK"};
int inx = aEvent->mMessage-NS_MOUSE_MESSAGE_START;
if (inx >= 0 && inx <= (NS_MOUSE_RIGHT_CLICK-NS_MOUSE_MESSAGE_START)) {
int inx = aEvent->mMessage - eMouseEventFirst;
if (inx >= 0 && inx <= (NS_MOUSE_RIGHT_CLICK - eMouseEventFirst)) {
printf("Mouse in ListFrame %s [%d]\n", desc[inx], aEvent->mMessage);
} else {
printf("Mouse in ListFrame <UNKNOWN> [%d]\n", aEvent->mMessage);
+12 -6
View File
@@ -606,9 +606,19 @@ nsPluginFrame::CallSetWindow(bool aCheckIsHidden)
if (aCheckIsHidden && IsHidden())
return NS_ERROR_FAILURE;
// Calling either nsPluginInstanceOwner::FixUpPluginWindow() (here,
// on OS X) or SetWindow() (below, on all platforms) can destroy this
// frame. (FixUpPluginWindow() calls SetWindow()). So grab a safe
// reference to mInstanceOwner which we can use below, if needed.
nsRefPtr<nsPluginInstanceOwner> instanceOwnerRef(mInstanceOwner);
// refresh the plugin port as well
#ifdef XP_MACOSX
mInstanceOwner->FixUpPluginWindow(nsPluginInstanceOwner::ePluginPaintEnable);
// Bail now if our frame has been destroyed.
if (!instanceOwnerRef->GetFrame()) {
return NS_ERROR_FAILURE;
}
#endif
window->window = mInstanceOwner->GetPluginPort();
@@ -640,10 +650,6 @@ nsPluginFrame::CallSetWindow(bool aCheckIsHidden)
window->width = intBounds.width / intScaleFactor;
window->height = intBounds.height / intScaleFactor;
// Calling SetWindow might destroy this frame. We need to use the instance
// owner to clean up so hold a ref.
nsRefPtr<nsPluginInstanceOwner> instanceOwnerRef(mInstanceOwner);
// This will call pi->SetWindow and take care of window subclassing
// if needed, see bug 132759. Calling SetWindow can destroy this frame
// so check for that before doing anything else with this frame's memory.
@@ -1751,13 +1757,13 @@ nsPluginFrame::HandleEvent(nsPresContext* aPresContext,
mInstanceOwner->ConsiderNewEventloopNestingLevel();
if (anEvent->mMessage == NS_PLUGIN_ACTIVATE) {
if (anEvent->mMessage == ePluginActivate) {
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(GetContent());
if (fm && elem)
return fm->SetFocus(elem, 0);
}
else if (anEvent->mMessage == NS_PLUGIN_FOCUS) {
else if (anEvent->mMessage == ePluginFocus) {
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm) {
nsCOMPtr<nsIContent> content = GetContent();
+11 -2
View File
@@ -64,6 +64,8 @@ nsSocketTransportService::nsSocketTransportService()
, mIdleCount(0)
, mSentBytesCount(0)
, mReceivedBytesCount(0)
, mEventQueueLock("nsSocketTransportService::mEventQueueLock")
, mPendingSocketQ(mEventQueueLock)
, mSendBufferSize(0)
, mKeepaliveIdleTimeS(600)
, mKeepaliveRetryIntervalS(1)
@@ -159,7 +161,10 @@ nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event)
return Dispatch(event, NS_DISPATCH_NORMAL);
}
mPendingSocketQ.PutEvent(event);
{
MutexAutoLock lock(mEventQueueLock);
mPendingSocketQ.PutEvent(event, lock);
}
return NS_OK;
}
@@ -212,7 +217,11 @@ nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *s
// notify the first element on the pending socket queue...
//
nsCOMPtr<nsIRunnable> event;
if (mPendingSocketQ.GetPendingEvent(getter_AddRefs(event))) {
{
MutexAutoLock lock(mEventQueueLock);
mPendingSocketQ.GetPendingEvent(getter_AddRefs(event), lock);
}
if (event) {
// move event from pending queue to dispatch queue
return Dispatch(event, NS_DISPATCH_NORMAL);
}
+1
View File
@@ -213,6 +213,7 @@ private:
// pending socket queue - see NotifyWhenCanAttachSocket
//-------------------------------------------------------------------------
mozilla::Mutex mEventQueueLock;
nsEventQueue mPendingSocketQ; // queue of nsIRunnable objects
// Preference Monitor for SendBufferSize and Keepalive prefs.
+22 -26
View File
@@ -31,39 +31,35 @@ NS_EVENT_MESSAGE(eAfterKeyUp, eWindowEventFirst + 37)
NS_EVENT_MESSAGE(eResize, eWindowEventFirst + 60)
NS_EVENT_MESSAGE(eScroll, eWindowEventFirst + 61)
// A plugin was clicked or otherwise focused. NS_PLUGIN_ACTIVATE should be
// used when the window is not active. NS_PLUGIN_FOCUS should be used when
// A plugin was clicked or otherwise focused. ePluginActivate should be
// used when the window is not active. ePluginFocus should be used when
// the window is active. In the latter case, the dispatcher of the event
// is expected to ensure that the plugin's widget is focused beforehand.
NS_EVENT_MESSAGE(NS_PLUGIN_ACTIVATE, eWindowEventFirst + 62)
NS_EVENT_MESSAGE(NS_PLUGIN_FOCUS, eWindowEventFirst + 63)
NS_EVENT_MESSAGE(ePluginActivate, eWindowEventFirst + 62)
NS_EVENT_MESSAGE(ePluginFocus, eWindowEventFirst + 63)
NS_EVENT_MESSAGE(NS_OFFLINE, eWindowEventFirst + 64)
NS_EVENT_MESSAGE(NS_ONLINE, eWindowEventFirst + 65)
NS_EVENT_MESSAGE(eOffline, eWindowEventFirst + 64)
NS_EVENT_MESSAGE(eOnline, eWindowEventFirst + 65)
// NS_BEFORERESIZE_EVENT used to be here (eWindowEventFirst + 66)
// Indicates that the user is either idle or active
NS_EVENT_MESSAGE(NS_MOZ_USER_IDLE, eWindowEventFirst + 67)
NS_EVENT_MESSAGE(NS_MOZ_USER_ACTIVE, eWindowEventFirst + 68)
NS_EVENT_MESSAGE(eLanguageChange, eWindowEventFirst + 70)
NS_EVENT_MESSAGE(NS_LANGUAGECHANGE, eWindowEventFirst + 70)
NS_EVENT_MESSAGE(NS_MOUSE_MESSAGE_START, 300)
NS_EVENT_MESSAGE(NS_MOUSE_MOVE, NS_MOUSE_MESSAGE_START)
NS_EVENT_MESSAGE(NS_MOUSE_BUTTON_UP, NS_MOUSE_MESSAGE_START + 1)
NS_EVENT_MESSAGE(NS_MOUSE_BUTTON_DOWN, NS_MOUSE_MESSAGE_START + 2)
NS_EVENT_MESSAGE(NS_MOUSE_ENTER_WIDGET, NS_MOUSE_MESSAGE_START + 22)
NS_EVENT_MESSAGE(NS_MOUSE_EXIT_WIDGET, NS_MOUSE_MESSAGE_START + 23)
NS_EVENT_MESSAGE(NS_MOUSE_DOUBLECLICK, NS_MOUSE_MESSAGE_START + 24)
NS_EVENT_MESSAGE(NS_MOUSE_CLICK, NS_MOUSE_MESSAGE_START + 27)
NS_EVENT_MESSAGE(NS_MOUSE_ACTIVATE, NS_MOUSE_MESSAGE_START + 30)
NS_EVENT_MESSAGE(NS_MOUSE_OVER, NS_MOUSE_MESSAGE_START + 31)
NS_EVENT_MESSAGE(NS_MOUSE_OUT, NS_MOUSE_MESSAGE_START + 32)
NS_EVENT_MESSAGE(NS_MOUSE_MOZHITTEST, NS_MOUSE_MESSAGE_START + 33)
NS_EVENT_MESSAGE(NS_MOUSEENTER, NS_MOUSE_MESSAGE_START + 34)
NS_EVENT_MESSAGE(NS_MOUSELEAVE, NS_MOUSE_MESSAGE_START + 35)
NS_EVENT_MESSAGE(NS_MOUSE_MOZLONGTAP, NS_MOUSE_MESSAGE_START + 36)
NS_EVENT_MESSAGE(eMouseEventFirst, 300)
NS_EVENT_MESSAGE(NS_MOUSE_MOVE, eMouseEventFirst)
NS_EVENT_MESSAGE(NS_MOUSE_BUTTON_UP, eMouseEventFirst + 1)
NS_EVENT_MESSAGE(NS_MOUSE_BUTTON_DOWN, eMouseEventFirst + 2)
NS_EVENT_MESSAGE(NS_MOUSE_ENTER_WIDGET, eMouseEventFirst + 22)
NS_EVENT_MESSAGE(NS_MOUSE_EXIT_WIDGET, eMouseEventFirst + 23)
NS_EVENT_MESSAGE(NS_MOUSE_DOUBLECLICK, eMouseEventFirst + 24)
NS_EVENT_MESSAGE(NS_MOUSE_CLICK, eMouseEventFirst + 27)
NS_EVENT_MESSAGE(NS_MOUSE_ACTIVATE, eMouseEventFirst + 30)
NS_EVENT_MESSAGE(NS_MOUSE_OVER, eMouseEventFirst + 31)
NS_EVENT_MESSAGE(NS_MOUSE_OUT, eMouseEventFirst + 32)
NS_EVENT_MESSAGE(NS_MOUSE_MOZHITTEST, eMouseEventFirst + 33)
NS_EVENT_MESSAGE(NS_MOUSEENTER, eMouseEventFirst + 34)
NS_EVENT_MESSAGE(NS_MOUSELEAVE, eMouseEventFirst + 35)
NS_EVENT_MESSAGE(NS_MOUSE_MOZLONGTAP, eMouseEventFirst + 36)
// Pointer spec events
NS_EVENT_MESSAGE(NS_POINTER_EVENT_START, 4400)
+2 -2
View File
@@ -151,8 +151,8 @@ WidgetEvent::HasIMEEventMessage() const
bool
WidgetEvent::HasPluginActivationEventMessage() const
{
return mMessage == NS_PLUGIN_ACTIVATE ||
mMessage == NS_PLUGIN_FOCUS;
return mMessage == ePluginActivate ||
mMessage == ePluginFocus;
}
/******************************************************************************
+113 -2
View File
@@ -11,9 +11,10 @@
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#include "nsXPCOM.h"
#include "mozilla/Monitor.h"
#include "gtest/gtest.h"
class nsRunner MOZ_FINAL : public nsIRunnable {
class nsRunner final : public nsIRunnable {
~nsRunner() {}
public:
NS_DECL_THREADSAFE_ISUPPORTS
@@ -61,7 +62,7 @@ TEST(Threads, Main)
PR_Sleep(PR_MillisecondsToInterval(100)); // hopefully the runner will quit here
}
class nsStressRunner MOZ_FINAL : public nsIRunnable {
class nsStressRunner final : public nsIRunnable {
public:
NS_DECL_THREADSAFE_ISUPPORTS
@@ -124,6 +125,116 @@ TEST(Threads, Stress)
}
}
mozilla::Monitor* gAsyncShutdownReadyMonitor;
mozilla::Monitor* gBeginAsyncShutdownMonitor;
class AsyncShutdownPreparer : public nsIRunnable {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_IMETHOD Run() override {
EXPECT_FALSE(mWasRun);
mWasRun = true;
mozilla::MonitorAutoLock lock(*gAsyncShutdownReadyMonitor);
lock.Notify();
return NS_OK;
}
explicit AsyncShutdownPreparer() : mWasRun(false) {}
private:
virtual ~AsyncShutdownPreparer() {
EXPECT_TRUE(mWasRun);
}
protected:
bool mWasRun;
};
NS_IMPL_ISUPPORTS(AsyncShutdownPreparer, nsIRunnable)
class AsyncShutdownWaiter : public nsIRunnable {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_IMETHOD Run() override {
EXPECT_FALSE(mWasRun);
mWasRun = true;
nsCOMPtr<nsIThread> t;
nsresult rv;
{
mozilla::MonitorAutoLock lock(*gBeginAsyncShutdownMonitor);
rv = NS_NewThread(getter_AddRefs(t), new AsyncShutdownPreparer());
EXPECT_TRUE(NS_SUCCEEDED(rv));
lock.Wait();
}
rv = t->AsyncShutdown();
EXPECT_TRUE(NS_SUCCEEDED(rv));
return NS_OK;
}
explicit AsyncShutdownWaiter() : mWasRun(false) {}
private:
virtual ~AsyncShutdownWaiter() {
EXPECT_TRUE(mWasRun);
}
protected:
bool mWasRun;
};
NS_IMPL_ISUPPORTS(AsyncShutdownWaiter, nsIRunnable)
class SameThreadSentinel : public nsIRunnable {
public:
NS_DECL_ISUPPORTS
NS_IMETHOD Run() override {
mozilla::MonitorAutoLock lock(*gBeginAsyncShutdownMonitor);
lock.Notify();
return NS_OK;
}
private:
virtual ~SameThreadSentinel() {}
};
NS_IMPL_ISUPPORTS(SameThreadSentinel, nsIRunnable)
TEST(Threads, AsyncShutdown)
{
gAsyncShutdownReadyMonitor = new mozilla::Monitor("gAsyncShutdownReady");
gBeginAsyncShutdownMonitor = new mozilla::Monitor("gBeginAsyncShutdown");
nsCOMPtr<nsIThread> t;
nsresult rv;
{
mozilla::MonitorAutoLock lock(*gAsyncShutdownReadyMonitor);
rv = NS_NewThread(getter_AddRefs(t), new AsyncShutdownWaiter());
EXPECT_TRUE(NS_SUCCEEDED(rv));
lock.Wait();
}
NS_DispatchToCurrentThread(new SameThreadSentinel());
rv = t->Shutdown();
EXPECT_TRUE(NS_SUCCEEDED(rv));
delete gAsyncShutdownReadyMonitor;
delete gBeginAsyncShutdownMonitor;
}
static void threadProc(void *arg)
{
// printf(" running thread %d\n", (int) arg);
+7
View File
@@ -456,6 +456,13 @@ LazyIdleThread::GetPRThread(PRThread** aPRThread)
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
LazyIdleThread::AsyncShutdown()
{
ASSERT_OWNING_THREAD();
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
LazyIdleThread::Shutdown()
{
+37 -52
View File
@@ -27,18 +27,18 @@ GetLog()
#endif
#define LOG(args) MOZ_LOG(GetLog(), mozilla::LogLevel::Debug, args)
nsEventQueue::nsEventQueue()
: mReentrantMonitor("nsEventQueue.mReentrantMonitor")
, mHead(nullptr)
nsEventQueue::nsEventQueue(Mutex& aLock)
: mHead(nullptr)
, mTail(nullptr)
, mOffsetHead(0)
, mOffsetTail(0)
, mEventsAvailable(aLock, "[nsEventQueue.mEventsAvailable]")
{
}
nsEventQueue::~nsEventQueue()
{
// It'd be nice to be able to assert that no one else is holding the monitor,
// It'd be nice to be able to assert that no one else is holding the lock,
// but NSPR doesn't really expose APIs for it.
NS_ASSERTION(IsEmpty(),
"Non-empty event queue being destroyed; events being leaked.");
@@ -49,33 +49,30 @@ nsEventQueue::~nsEventQueue()
}
bool
nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult)
nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult,
MutexAutoLock& aProofOfLock)
{
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
while (IsEmpty()) {
if (!aMayWait) {
if (aResult) {
*aResult = nullptr;
}
return false;
while (IsEmpty()) {
if (!aMayWait) {
if (aResult) {
*aResult = nullptr;
}
LOG(("EVENTQ(%p): wait begin\n", this));
mon.Wait();
LOG(("EVENTQ(%p): wait end\n", this));
return false;
}
LOG(("EVENTQ(%p): wait begin\n", this));
mEventsAvailable.Wait();
LOG(("EVENTQ(%p): wait end\n", this));
}
if (aResult) {
*aResult = mHead->mEvents[mOffsetHead++];
if (aResult) {
*aResult = mHead->mEvents[mOffsetHead++];
// Check if mHead points to empty Page
if (mOffsetHead == EVENTS_PER_PAGE) {
Page* dead = mHead;
mHead = mHead->mNext;
FreePage(dead);
mOffsetHead = 0;
}
// Check if mHead points to empty Page
if (mOffsetHead == EVENTS_PER_PAGE) {
Page* dead = mHead;
mHead = mHead->mNext;
FreePage(dead);
mOffsetHead = 0;
}
}
@@ -83,28 +80,9 @@ nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult)
}
void
nsEventQueue::PutEvent(nsIRunnable* aRunnable)
nsEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable,
MutexAutoLock& aProofOfLock)
{
nsCOMPtr<nsIRunnable> event(aRunnable);
PutEvent(event.forget());
}
void
nsEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable)
{
// Avoid calling AddRef+Release while holding our monitor.
nsCOMPtr<nsIRunnable> event(aRunnable);
if (ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
// With probability 0.5, yield so other threads have a chance to
// dispatch events to this queue first.
if (ChaosMode::randomUint32LessThan(2)) {
PR_Sleep(PR_INTERVAL_NO_WAIT);
}
}
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mHead) {
mHead = NewPage();
MOZ_ASSERT(mHead);
@@ -121,17 +99,24 @@ nsEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable)
mOffsetTail = 0;
}
event.swap(mTail->mEvents[mOffsetTail]);
nsIRunnable*& queueLocation = mTail->mEvents[mOffsetTail];
MOZ_ASSERT(!queueLocation);
queueLocation = aRunnable.take();
++mOffsetTail;
LOG(("EVENTQ(%p): notify\n", this));
mon.NotifyAll();
mEventsAvailable.Notify();
}
void
nsEventQueue::PutEvent(nsIRunnable* aRunnable, MutexAutoLock& aProofOfLock)
{
nsCOMPtr<nsIRunnable> event(aRunnable);
PutEvent(event.forget(), aProofOfLock);
}
size_t
nsEventQueue::Count()
nsEventQueue::Count(MutexAutoLock& aProofOfLock)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// It is obvious count is 0 when the queue is empty.
if (!mHead) {
return 0;
+32 -21
View File
@@ -8,25 +8,29 @@
#define nsEventQueue_h__
#include <stdlib.h>
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/CondVar.h"
#include "mozilla/Mutex.h"
#include "nsIRunnable.h"
#include "nsCOMPtr.h"
#include "mozilla/AlreadyAddRefed.h"
class nsThreadPool;
// A threadsafe FIFO event queue...
class nsEventQueue
{
typedef mozilla::ReentrantMonitor ReentrantMonitor;
public:
nsEventQueue();
typedef mozilla::MutexAutoLock MutexAutoLock;
explicit nsEventQueue(mozilla::Mutex& aLock);
~nsEventQueue();
// This method adds a new event to the pending event queue. The queue holds
// a strong reference to the event after this method returns. This method
// cannot fail.
void PutEvent(nsIRunnable* aEvent);
void PutEvent(already_AddRefed<nsIRunnable>&& aEvent);
void PutEvent(nsIRunnable* aEvent, MutexAutoLock& aProofOfLock);
void PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
MutexAutoLock& aProofOfLock);
// This method gets an event from the event queue. If mayWait is true, then
// the method will block the calling thread until an event is available. If
@@ -34,30 +38,24 @@ public:
// or not an event is pending. When the resulting event is non-null, the
// caller is responsible for releasing the event object. This method does
// not alter the reference count of the resulting event.
bool GetEvent(bool aMayWait, nsIRunnable** aEvent);
bool GetEvent(bool aMayWait, nsIRunnable** aEvent,
MutexAutoLock& aProofOfLock);
// This method returns true if there is a pending event.
bool HasPendingEvent()
bool HasPendingEvent(MutexAutoLock& aProofOfLock)
{
return GetEvent(false, nullptr);
return GetEvent(false, nullptr, aProofOfLock);
}
// This method returns the next pending event or null.
bool GetPendingEvent(nsIRunnable** runnable)
bool GetPendingEvent(nsIRunnable** aRunnable, MutexAutoLock& aProofOfLock)
{
return GetEvent(false, runnable);
return GetEvent(false, aRunnable, aProofOfLock);
}
// Expose the event queue's monitor for "power users"
ReentrantMonitor& GetReentrantMonitor()
{
return mReentrantMonitor;
}
size_t Count();
size_t Count(MutexAutoLock&);
private:
bool IsEmpty()
{
return !mHead || (mHead == mTail && mOffsetHead == mOffsetTail);
@@ -89,13 +87,26 @@ private:
free(aPage);
}
ReentrantMonitor mReentrantMonitor;
Page* mHead;
Page* mTail;
uint16_t mOffsetHead; // offset into mHead where next item is removed
uint16_t mOffsetTail; // offset into mTail where next item is added
mozilla::CondVar mEventsAvailable;
// These methods are made available to nsThreadPool as a hack, since
// nsThreadPool needs to have its threads sleep for fixed amounts of
// time as well as being able to wake up all threads when thread
// limits change.
friend class nsThreadPool;
void Wait(PRIntervalTime aInterval)
{
mEventsAvailable.Wait(aInterval);
}
void NotifyAll()
{
mEventsAvailable.NotifyAll();
}
};
#endif // nsEventQueue_h__
+23 -1
View File
@@ -17,7 +17,7 @@
*
* See nsIThreadManager for the API used to create and locate threads.
*/
[scriptable, uuid(9c889946-a73a-4af3-ae9a-ea64f7d4e3ca)]
[scriptable, uuid(594feb13-6164-4054-b5a1-ad62e10ea15d)]
interface nsIThread : nsIEventTarget
{
/**
@@ -82,4 +82,26 @@ interface nsIThread : nsIEventTarget
* not the current thread.
*/
boolean processNextEvent(in boolean mayWait);
/**
* Shutdown the thread asynchronously. This method immediately prevents
* further dispatch of events to the thread, and it causes any pending events
* to run to completion before this thread joins with the current thread.
*
* UNLIKE shutdown() this does not process events on the current thread.
* Instead it merely ensures that the current thread continues running until
* this thread has shut down.
*
* This method MAY NOT be executed from the thread itself. Instead, it is
* meant to be executed from another thread (usually the thread that created
* this thread or the main application thread). When this function returns,
* the thread will continue running until it exhausts its event queue.
*
* @throws NS_ERROR_UNEXPECTED
* Indicates that this method was erroneously called when this thread was
* the current thread, that this thread was not created with a call to
* nsIThreadManager::NewThread, or if this method was called more than once
* on the thread object.
*/
void asyncShutdown();
};
+116 -30
View File
@@ -21,6 +21,7 @@
#include "nsIClassInfoImpl.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsQueryObject.h"
#include "pratom.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/Logging.h"
@@ -238,28 +239,43 @@ private:
struct nsThreadShutdownContext
{
// NB: This will be the last reference.
nsRefPtr<nsThread> terminatingThread;
nsThread* joiningThread;
bool shutdownAck;
bool awaitingShutdownAck;
};
// This event is responsible for notifying nsThread::Shutdown that it is time
// to call PR_JoinThread.
class nsThreadShutdownAckEvent : public nsRunnable
// to call PR_JoinThread. It implements nsICancelableRunnable so that it can
// run on a DOM Worker thread (where all events must implement
// nsICancelableRunnable.)
class nsThreadShutdownAckEvent : public nsRunnable,
public nsICancelableRunnable
{
public:
explicit nsThreadShutdownAckEvent(nsThreadShutdownContext* aCtx)
: mShutdownContext(aCtx)
{
}
NS_IMETHOD Run()
NS_DECL_ISUPPORTS_INHERITED
NS_IMETHOD Run() override
{
mShutdownContext->shutdownAck = true;
mShutdownContext->terminatingThread->ShutdownComplete(mShutdownContext);
return NS_OK;
}
NS_IMETHOD Cancel() override
{
return Run();
}
private:
virtual ~nsThreadShutdownAckEvent() { }
nsThreadShutdownContext* mShutdownContext;
};
NS_IMPL_ISUPPORTS_INHERITED(nsThreadShutdownAckEvent, nsRunnable,
nsICancelableRunnable)
// This event is responsible for setting mShutdownContext
class nsThreadShutdownEvent : public nsRunnable
{
@@ -342,9 +358,12 @@ nsThread::ThreadFunc(void* aArg)
// Wait for and process startup event
nsCOMPtr<nsIRunnable> event;
if (!self->GetEvent(true, getter_AddRefs(event))) {
NS_WARNING("failed waiting for thread startup event");
return;
{
MutexAutoLock lock(self->mLock);
if (!self->mEvents->GetEvent(true, getter_AddRefs(event), lock)) {
NS_WARNING("failed waiting for thread startup event");
return;
}
}
event->Run(); // unblocks nsThread::Init
event = nullptr;
@@ -369,11 +388,18 @@ nsThread::ThreadFunc(void* aArg)
// mEventsAreDoomed atomically with the removal of the last event. The key
// invariant here is that we will never permit PutEvent to succeed if the
// event would be left in the queue after our final call to
// NS_ProcessPendingEvents.
// NS_ProcessPendingEvents. We also have to keep processing events as long
// as we have outstanding mRequestedShutdownContexts.
while (true) {
// Check and see if we're waiting on any threads.
while (self->mRequestedShutdownContexts.Length()) {
// We can't stop accepting events just yet. Block and check again.
NS_ProcessNextEvent(self, true);
}
{
MutexAutoLock lock(self->mLock);
if (!self->mEvents->HasPendingEvent()) {
if (!self->mEvents->HasPendingEvent(lock)) {
// No events in the queue, so we will stop now. Don't let any more
// events be added, since they won't be processed. It is critical
// that no PutEvent can occur between testing that the event queue is
@@ -394,7 +420,8 @@ nsThread::ThreadFunc(void* aArg)
nsThreadManager::get()->UnregisterCurrentThread(self);
// Dispatch shutdown ACK
event = new nsThreadShutdownAckEvent(self->mShutdownContext);
MOZ_ASSERT(self->mShutdownContext->terminatingThread == self);
event = do_QueryObject(new nsThreadShutdownAckEvent(self->mShutdownContext));
self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
// Release any observer of the thread here.
@@ -417,6 +444,7 @@ nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
: mLock("nsThread.mLock")
, mScriptObserver(nullptr)
, mEvents(&mEventsRoot)
, mEventsRoot(mLock)
, mPriority(PRIORITY_NORMAL)
, mThread(nullptr)
, mNestedEventLoopDepth(0)
@@ -456,7 +484,7 @@ nsThread::Init()
// that mThread is set properly.
{
MutexAutoLock lock(mLock);
mEventsRoot.PutEvent(startup); // retain a reference
mEventsRoot.PutEvent(startup, lock); // retain a reference
}
// Wait for thread to call ThreadManager::SetupCurrentThread, which completes
@@ -502,7 +530,7 @@ nsThread::PutEvent(already_AddRefed<nsIRunnable>&& aEvent, nsNestedEventTarget*
nsIRunnable* temp2 = temp.forget().take(); // can't use unused << aEvent here due to Windows (boo)
return temp2 ? NS_ERROR_UNEXPECTED : NS_ERROR_UNEXPECTED; // to make compiler not bletch on us
}
queue->PutEvent(Move(aEvent));
queue->PutEvent(Move(aEvent), lock);
// Make sure to grab the observer before dropping the lock, otherwise the
// event that we just placed into the queue could run and eventually delete
@@ -603,9 +631,9 @@ nsThread::GetPRThread(PRThread** aResult)
}
NS_IMETHODIMP
nsThread::Shutdown()
nsThread::AsyncShutdown()
{
LOG(("THRD(%p) shutdown\n", this));
LOG(("THRD(%p) async shutdown\n", this));
// XXX If we make this warn, then we hit that warning at xpcom shutdown while
// shutting down a thread in a thread pool. That happens b/c the thread
@@ -614,37 +642,61 @@ nsThread::Shutdown()
return NS_OK;
}
return !!ShutdownInternal(/* aSync = */ false) ? NS_OK : NS_ERROR_UNEXPECTED;
}
nsThreadShutdownContext*
nsThread::ShutdownInternal(bool aSync)
{
MOZ_ASSERT(mThread);
if (NS_WARN_IF(mThread == PR_GetCurrentThread())) {
return NS_ERROR_UNEXPECTED;
return nullptr;
}
// Prevent multiple calls to this method
{
MutexAutoLock lock(mLock);
if (!mShutdownRequired) {
return NS_ERROR_UNEXPECTED;
return nullptr;
}
mShutdownRequired = false;
}
nsThreadShutdownContext context;
context.joiningThread = nsThreadManager::get()->GetCurrentThread();
context.shutdownAck = false;
nsThread* currentThread = nsThreadManager::get()->GetCurrentThread();
MOZ_ASSERT(currentThread);
nsAutoPtr<nsThreadShutdownContext>& context =
*currentThread->mRequestedShutdownContexts.AppendElement();
context = new nsThreadShutdownContext();
context->terminatingThread = this;
context->joiningThread = currentThread;
context->awaitingShutdownAck = aSync;
// Set mShutdownContext and wake up the thread in case it is waiting for
// events to process.
nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, context);
// XXXroc What if posting the event fails due to OOM?
PutEvent(event.forget(), nullptr);
// We could still end up with other events being added after the shutdown
// task, but that's okay because we process pending events in ThreadFunc
// after setting mShutdownContext just before exiting.
return context;
}
// Process events on the current thread until we receive a shutdown ACK.
// Allows waiting; ensure no locks are held that would deadlock us!
while (!context.shutdownAck) {
NS_ProcessNextEvent(context.joiningThread, true);
void
nsThread::ShutdownComplete(nsThreadShutdownContext* aContext)
{
MOZ_ASSERT(mThread);
MOZ_ASSERT(aContext->terminatingThread == this);
if (aContext->awaitingShutdownAck) {
// We're in a synchronous shutdown, so tell whatever is up the stack that
// we're done and unwind the stack so it can call us again.
aContext->awaitingShutdownAck = false;
return;
}
// Now, it should be safe to join without fear of dead-locking.
@@ -664,6 +716,34 @@ nsThread::Shutdown()
}
#endif
// Delete aContext.
MOZ_ALWAYS_TRUE(
aContext->joiningThread->mRequestedShutdownContexts.RemoveElement(aContext));
}
NS_IMETHODIMP
nsThread::Shutdown()
{
LOG(("THRD(%p) sync shutdown\n", this));
// XXX If we make this warn, then we hit that warning at xpcom shutdown while
// shutting down a thread in a thread pool. That happens b/c the thread
// in the thread pool is already shutdown by the thread manager.
if (!mThread) {
return NS_OK;
}
nsThreadShutdownContext* context = ShutdownInternal(/* aSync = */ true);
NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED);
// Process events on the current thread until we receive a shutdown ACK.
// Allows waiting; ensure no locks are held that would deadlock us!
while (context->awaitingShutdownAck) {
NS_ProcessNextEvent(context->joiningThread, true);
}
ShutdownComplete(context);
return NS_OK;
}
@@ -674,7 +754,10 @@ nsThread::HasPendingEvents(bool* aResult)
return NS_ERROR_NOT_SAME_THREAD;
}
*aResult = mEvents->GetEvent(false, nullptr);
{
MutexAutoLock lock(mLock);
*aResult = mEvents->HasPendingEvent(lock);
}
return NS_OK;
}
@@ -833,7 +916,10 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
// If we are shutting down, then do not wait for new events.
nsCOMPtr<nsIRunnable> event;
mEvents->GetEvent(reallyWait, getter_AddRefs(event));
{
MutexAutoLock lock(mLock);
mEvents->GetEvent(reallyWait, getter_AddRefs(event), lock);
}
*aResult = (event.get() != nullptr);
@@ -989,7 +1075,7 @@ nsThread::PushEventQueue(nsIEventTarget** aResult)
return NS_ERROR_NOT_SAME_THREAD;
}
nsChainedEventQueue* queue = new nsChainedEventQueue();
nsChainedEventQueue* queue = new nsChainedEventQueue(mLock);
queue->mEventTarget = new nsNestedEventTarget(this, queue);
{
@@ -1031,8 +1117,8 @@ nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget)
mEvents = mEvents->mNext;
nsCOMPtr<nsIRunnable> event;
while (queue->GetEvent(false, getter_AddRefs(event))) {
mEvents->PutEvent(event.forget());
while (queue->GetEvent(false, getter_AddRefs(event), lock)) {
mEvents->PutEvent(event.forget(), lock);
}
// Don't let the event target post any more events.
+19 -13
View File
@@ -77,6 +77,8 @@ public:
uint32_t
RecursionDepth() const;
void ShutdownComplete(struct nsThreadShutdownContext* aContext);
protected:
class nsChainedEventQueue;
@@ -103,43 +105,44 @@ protected:
}
// Wrappers for event queue methods:
bool GetEvent(bool aMayWait, nsIRunnable** aEvent)
{
return mEvents->GetEvent(aMayWait, aEvent);
}
nsresult PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget);
nsresult PutEvent(already_AddRefed<nsIRunnable>&& aEvent, nsNestedEventTarget* aTarget);
nsresult DispatchInternal(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aFlags,
nsNestedEventTarget* aTarget);
struct nsThreadShutdownContext* ShutdownInternal(bool aSync);
// Wrapper for nsEventQueue that supports chaining.
class nsChainedEventQueue
{
public:
nsChainedEventQueue()
explicit nsChainedEventQueue(mozilla::Mutex& aLock)
: mNext(nullptr)
, mQueue(aLock)
{
}
bool GetEvent(bool aMayWait, nsIRunnable** aEvent)
bool GetEvent(bool aMayWait, nsIRunnable** aEvent,
mozilla::MutexAutoLock& aProofOfLock)
{
return mQueue.GetEvent(aMayWait, aEvent);
return mQueue.GetEvent(aMayWait, aEvent, aProofOfLock);
}
void PutEvent(nsIRunnable* aEvent)
void PutEvent(nsIRunnable* aEvent, mozilla::MutexAutoLock& aProofOfLock)
{
mQueue.PutEvent(aEvent);
mQueue.PutEvent(aEvent, aProofOfLock);
}
void PutEvent(already_AddRefed<nsIRunnable>&& aEvent)
void PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
mozilla::MutexAutoLock& aProofOfLock)
{
mQueue.PutEvent(mozilla::Move(aEvent));
mQueue.PutEvent(mozilla::Move(aEvent), aProofOfLock);
}
bool HasPendingEvent()
bool HasPendingEvent(mozilla::MutexAutoLock& aProofOfLock)
{
return mQueue.HasPendingEvent();
return mQueue.HasPendingEvent(aProofOfLock);
}
nsChainedEventQueue* mNext;
@@ -193,7 +196,10 @@ protected:
uint32_t mNestedEventLoopDepth;
uint32_t mStackSize;
// The shutdown context for ourselves.
struct nsThreadShutdownContext* mShutdownContext;
// The shutdown contexts for any other threads we've asked to shut down.
nsTArray<nsAutoPtr<struct nsThreadShutdownContext>> mRequestedShutdownContexts;
bool mShutdownRequired;
// Set to true when events posted to this thread will never run.
+37 -36
View File
@@ -48,13 +48,16 @@ NS_IMPL_QUERY_INTERFACE_CI(nsThreadPool, nsIThreadPool, nsIEventTarget,
NS_IMPL_CI_INTERFACE_GETTER(nsThreadPool, nsIThreadPool, nsIEventTarget)
nsThreadPool::nsThreadPool()
: mThreadLimit(DEFAULT_THREAD_LIMIT)
: mMutex("[nsThreadPool.mMutex]")
, mEvents(mMutex)
, mThreadLimit(DEFAULT_THREAD_LIMIT)
, mIdleThreadLimit(DEFAULT_IDLE_THREAD_LIMIT)
, mIdleThreadTimeout(DEFAULT_IDLE_THREAD_TIMEOUT)
, mIdleCount(0)
, mStackSize(nsIThreadManager::DEFAULT_STACK_SIZE)
, mShutdown(false)
{
LOG(("THRD-P(%p) constructor!!!\n", this));
}
nsThreadPool::~nsThreadPool()
@@ -79,7 +82,7 @@ nsThreadPool::PutEvent(already_AddRefed<nsIRunnable>&& aEvent)
bool spawnThread = false;
uint32_t stackSize = 0;
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
if (NS_WARN_IF(mShutdown)) {
return NS_ERROR_NOT_AVAILABLE;
@@ -92,11 +95,11 @@ nsThreadPool::PutEvent(already_AddRefed<nsIRunnable>&& aEvent)
if (mThreads.Count() < (int32_t)mThreadLimit &&
// Spawn a new thread if we don't have enough idle threads to serve
// pending events immediately.
mEvents.Count() >= mIdleCount) {
mEvents.Count(lock) >= mIdleCount) {
spawnThread = true;
}
mEvents.PutEvent(Move(aEvent));
mEvents.PutEvent(Move(aEvent), lock);
stackSize = mStackSize;
}
@@ -115,7 +118,7 @@ nsThreadPool::PutEvent(already_AddRefed<nsIRunnable>&& aEvent)
bool killThread = false;
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
if (mThreads.Count() < (int32_t)mThreadLimit) {
mThreads.AppendObject(thread);
} else {
@@ -124,15 +127,9 @@ nsThreadPool::PutEvent(already_AddRefed<nsIRunnable>&& aEvent)
}
LOG(("THRD-P(%p) put [%p kill=%d]\n", this, thread.get(), killThread));
if (killThread) {
// Pending events are processed on the current thread during
// nsIThread::Shutdown() execution, so if nsThreadPool::Dispatch() is called
// under caller's lock then deadlock could occur. This happens e.g. in case
// of nsStreamCopier. To prevent this situation, dispatch a shutdown event
// to the current thread instead of calling nsIThread::Shutdown() directly.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(thread,
&nsIThread::Shutdown);
NS_DispatchToCurrentThread(r);
// We never dispatched any events to the thread, so we can shut it down
// asynchronously without worrying about anything.
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->AsyncShutdown()));
} else {
thread->Dispatch(this, NS_DISPATCH_NORMAL);
}
@@ -157,10 +154,10 @@ nsThreadPool::ShutdownThread(nsIThread* aThread)
NS_IMETHODIMP
nsThreadPool::Run()
{
LOG(("THRD-P(%p) enter\n", this));
mThreadNaming.SetThreadPoolName(mName);
LOG(("THRD-P(%p) enter %s\n", this, mName.BeginReading()));
nsCOMPtr<nsIThread> current;
nsThreadManager::get()->GetCurrentThread(getter_AddRefs(current));
@@ -171,7 +168,7 @@ nsThreadPool::Run()
nsCOMPtr<nsIThreadPoolListener> listener;
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
listener = mListener;
}
@@ -182,8 +179,9 @@ nsThreadPool::Run()
do {
nsCOMPtr<nsIRunnable> event;
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
if (!mEvents.GetPendingEvent(getter_AddRefs(event))) {
MutexAutoLock lock(mMutex);
if (!mEvents.GetPendingEvent(getter_AddRefs(event), lock)) {
PRIntervalTime now = PR_IntervalNow();
PRIntervalTime timeout = PR_MillisecondsToInterval(mIdleThreadTimeout);
@@ -215,8 +213,9 @@ nsThreadPool::Run()
shutdownThreadOnExit = mThreads.RemoveObject(current);
} else {
PRIntervalTime delta = timeout - (now - idleSince);
LOG(("THRD-P(%p) waiting [%d]\n", this, delta));
mon.Wait(delta);
LOG(("THRD-P(%p) %s waiting [%d]\n", this, mName.BeginReading(), delta));
mEvents.Wait(delta);
LOG(("THRD-P(%p) done waiting\n", this));
}
} else if (wasIdle) {
wasIdle = false;
@@ -224,7 +223,7 @@ nsThreadPool::Run()
}
}
if (event) {
LOG(("THRD-P(%p) running [%p]\n", this, event.get()));
LOG(("THRD-P(%p) %s running [%p]\n", this, mName.BeginReading(), event.get()));
event->Run();
}
} while (!exitThread);
@@ -281,7 +280,7 @@ nsThreadPool::Dispatch(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aFlags)
NS_IMETHODIMP
nsThreadPool::IsOnCurrentThread(bool* aResult)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
if (NS_WARN_IF(mShutdown)) {
return NS_ERROR_NOT_AVAILABLE;
}
@@ -303,9 +302,9 @@ nsThreadPool::Shutdown()
nsCOMArray<nsIThread> threads;
nsCOMPtr<nsIThreadPoolListener> listener;
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
mShutdown = true;
mon.NotifyAll();
mEvents.NotifyAll();
threads.AppendObjects(mThreads);
mThreads.Clear();
@@ -336,14 +335,15 @@ nsThreadPool::GetThreadLimit(uint32_t* aValue)
NS_IMETHODIMP
nsThreadPool::SetThreadLimit(uint32_t aValue)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
LOG(("THRD-P(%p) thread limit [%u]\n", this, aValue));
mThreadLimit = aValue;
if (mIdleThreadLimit > mThreadLimit) {
mIdleThreadLimit = mThreadLimit;
}
if (static_cast<uint32_t>(mThreads.Count()) > mThreadLimit) {
mon.NotifyAll(); // wake up threads so they observe this change
mEvents.NotifyAll(); // wake up threads so they observe this change
}
return NS_OK;
}
@@ -358,7 +358,8 @@ nsThreadPool::GetIdleThreadLimit(uint32_t* aValue)
NS_IMETHODIMP
nsThreadPool::SetIdleThreadLimit(uint32_t aValue)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
LOG(("THRD-P(%p) idle thread limit [%u]\n", this, aValue));
mIdleThreadLimit = aValue;
if (mIdleThreadLimit > mThreadLimit) {
mIdleThreadLimit = mThreadLimit;
@@ -366,7 +367,7 @@ nsThreadPool::SetIdleThreadLimit(uint32_t aValue)
// Do we need to kill some idle threads?
if (mIdleCount > mIdleThreadLimit) {
mon.NotifyAll(); // wake up threads so they observe this change
mEvents.NotifyAll(); // wake up threads so they observe this change
}
return NS_OK;
}
@@ -381,13 +382,13 @@ nsThreadPool::GetIdleThreadTimeout(uint32_t* aValue)
NS_IMETHODIMP
nsThreadPool::SetIdleThreadTimeout(uint32_t aValue)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
uint32_t oldTimeout = mIdleThreadTimeout;
mIdleThreadTimeout = aValue;
// Do we need to notify any idle threads that their sleep time has shortened?
if (mIdleThreadTimeout < oldTimeout && mIdleCount > 0) {
mon.NotifyAll(); // wake up threads so they observe this change
mEvents.NotifyAll(); // wake up threads so they observe this change
}
return NS_OK;
}
@@ -395,7 +396,7 @@ nsThreadPool::SetIdleThreadTimeout(uint32_t aValue)
NS_IMETHODIMP
nsThreadPool::GetThreadStackSize(uint32_t* aValue)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
*aValue = mStackSize;
return NS_OK;
}
@@ -403,7 +404,7 @@ nsThreadPool::GetThreadStackSize(uint32_t* aValue)
NS_IMETHODIMP
nsThreadPool::SetThreadStackSize(uint32_t aValue)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
mStackSize = aValue;
return NS_OK;
}
@@ -411,7 +412,7 @@ nsThreadPool::SetThreadStackSize(uint32_t aValue)
NS_IMETHODIMP
nsThreadPool::GetListener(nsIThreadPoolListener** aListener)
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
NS_IF_ADDREF(*aListener = mListener);
return NS_OK;
}
@@ -421,7 +422,7 @@ nsThreadPool::SetListener(nsIThreadPoolListener* aListener)
{
nsCOMPtr<nsIThreadPoolListener> swappedListener(aListener);
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
mListener.swap(swappedListener);
}
return NS_OK;
@@ -431,7 +432,7 @@ NS_IMETHODIMP
nsThreadPool::SetName(const nsACString& aName)
{
{
ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
MutexAutoLock lock(mMutex);
if (mThreads.Count()) {
return NS_ERROR_NOT_AVAILABLE;
}
+3
View File
@@ -16,6 +16,8 @@
#include "nsThreadUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Mutex.h"
#include "mozilla/Monitor.h"
class nsThreadPool final
: public nsIThreadPool
@@ -41,6 +43,7 @@ private:
nsresult PutEvent(already_AddRefed<nsIRunnable>&& aEvent);
nsCOMArray<nsIThread> mThreads;
mozilla::Mutex mMutex;
nsEventQueue mEvents;
uint32_t mThreadLimit;
uint32_t mIdleThreadLimit;