mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 05:37:11 +00:00
Merge remote-tracking branch 'origin/master' into media-works
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user