master
roytam1 4 months ago
parent c554d461ab
commit 023203e555
  1. 6
      dom/events/EventNameList.h
  2. 112
      dom/inputmethod/MozKeyboard.js
  3. 156
      dom/inputmethod/forms.js
  4. 2
      dom/inputmethod/mochitest/mochitest.ini
  5. 1674
      dom/inputmethod/mochitest/test_bug1137557.html
  6. 167
      dom/inputmethod/mochitest/test_unload.html
  7. 4
      dom/plugins/base/nsPluginNativeWindowWin.cpp
  8. 143
      dom/webidl/InputMethod.webidl
  9. 4
      layout/forms/nsListControlFrame.cpp
  10. 18
      layout/generic/nsPluginFrame.cpp
  11. 13
      netwerk/base/nsSocketTransportService2.cpp
  12. 1
      netwerk/base/nsSocketTransportService2.h
  13. 50
      widget/EventMessageList.h
  14. 4
      widget/WidgetEventImpl.cpp
  15. 115
      xpcom/tests/gtest/TestThreads.cpp
  16. 7
      xpcom/threads/LazyIdleThread.cpp
  17. 89
      xpcom/threads/nsEventQueue.cpp
  18. 53
      xpcom/threads/nsEventQueue.h
  19. 24
      xpcom/threads/nsIThread.idl
  20. 146
      xpcom/threads/nsThread.cpp
  21. 32
      xpcom/threads/nsThread.h
  22. 73
      xpcom/threads/nsThreadPool.cpp
  23. 3
      xpcom/threads/nsThreadPool.h

@ -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;
// XXX: modifiers are ignored in this API method.
sendKey: function ic_sendKey(dictOrKeyCode, charCode, modifiers, repeat) {
if (typeof dictOrKeyCode === 'number') {
// XXX: modifiers are ignored in this API method.
return this._sendPromise(function(resolverId) {
cpmmSendAsyncMessageWithKbID(self, 'Keyboard:SendKey', {
contextId: self._contextId,
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.');
}
},
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;
}
};

@ -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);
}

@ -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.

@ -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(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(eLanguageChange, eWindowEventFirst + 70)
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)
{
nsCOMPtr<nsIRunnable> event(aRunnable);
PutEvent(event.forget());
}
void
nsEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable)
nsEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable,
MutexAutoLock& aProofOfLock)
{
// 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();
}
size_t
nsEventQueue::Count()
void
nsEventQueue::PutEvent(nsIRunnable* aRunnable, MutexAutoLock& aProofOfLock)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nsCOMPtr<nsIRunnable> event(aRunnable);
PutEvent(event.forget(), aProofOfLock);
}
size_t
nsEventQueue::Count(MutexAutoLock& aProofOfLock)
{
// 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