Merge fx-team to m-c a=merge

This commit is contained in:
Wes Kocher 2015-04-08 17:25:12 -07:00
commit 5cfea2e420
32 changed files with 381 additions and 112 deletions

View File

@ -164,6 +164,9 @@ let handleContentContextMenu = function (event) {
let baseURI = doc.baseURI; let baseURI = doc.baseURI;
let referrer = doc.referrer; let referrer = doc.referrer;
let referrerPolicy = doc.referrerPolicy; let referrerPolicy = doc.referrerPolicy;
let frameOuterWindowID = doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
// Media related cache info parent needs for saving // Media related cache info parent needs for saving
let contentType = null; let contentType = null;
@ -199,7 +202,8 @@ let handleContentContextMenu = function (event) {
sendSyncMessage("contextmenu", sendSyncMessage("contextmenu",
{ editFlags, spellInfo, customMenuItems, addonInfo, { editFlags, spellInfo, customMenuItems, addonInfo,
principal, docLocation, charSet, baseURI, referrer, principal, docLocation, charSet, baseURI, referrer,
referrerPolicy, contentType, contentDisposition }, referrerPolicy, contentType, contentDisposition,
frameOuterWindowID },
{ event, popupNode: event.target }); { event, popupNode: event.target });
} }
else { else {

View File

@ -599,24 +599,31 @@ nsContextMenu.prototype = {
this.focusedWindow = win; this.focusedWindow = win;
this.focusedElement = elt; this.focusedElement = elt;
let ownerDoc = this.target.ownerDocument;
// If this is a remote context menu event, use the information from // If this is a remote context menu event, use the information from
// gContextMenuContentData instead. // gContextMenuContentData instead.
if (this.isRemote) { if (this.isRemote) {
this.browser = gContextMenuContentData.browser; this.browser = gContextMenuContentData.browser;
this.principal = gContextMenuContentData.principal; this.principal = gContextMenuContentData.principal;
this.frameOuterWindowID = gContextMenuContentData.frameOuterWindowID;
} else { } else {
editFlags = SpellCheckHelper.isEditable(this.target, window); editFlags = SpellCheckHelper.isEditable(this.target, window);
this.browser = this.target.ownerDocument.defaultView this.browser = ownerDoc.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor) .QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation) .getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell) .QueryInterface(Ci.nsIDocShell)
.chromeEventHandler; .chromeEventHandler;
this.principal = this.target.ownerDocument.nodePrincipal; this.principal = ownerDoc.nodePrincipal;
this.frameOuterWindowID = ownerDoc.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
} }
this.onSocial = !!this.browser.getAttribute("origin"); this.onSocial = !!this.browser.getAttribute("origin");
// Check if we are in a synthetic document (stand alone image, video, etc.). // Check if we are in a synthetic document (stand alone image, video, etc.).
this.inSyntheticDoc = this.target.ownerDocument.mozSyntheticDocument; this.inSyntheticDoc = ownerDoc.mozSyntheticDocument;
// First, do checks for nodes that never have children. // First, do checks for nodes that never have children.
if (this.target.nodeType == Node.ELEMENT_NODE) { if (this.target.nodeType == Node.ELEMENT_NODE) {
// See if the user clicked on an image. // See if the user clicked on an image.
@ -635,7 +642,7 @@ nsContextMenu.prototype = {
var descURL = this.target.getAttribute("longdesc"); var descURL = this.target.getAttribute("longdesc");
if (descURL) { if (descURL) {
this.imageDescURL = makeURLAbsolute(this.target.ownerDocument.body.baseURI, descURL); this.imageDescURL = makeURLAbsolute(ownerDoc.body.baseURI, descURL);
} }
} }
else if (this.target instanceof HTMLCanvasElement) { else if (this.target instanceof HTMLCanvasElement) {
@ -685,7 +692,7 @@ nsContextMenu.prototype = {
this.onKeywordField = (editFlags & SpellCheckHelper.KEYWORD); this.onKeywordField = (editFlags & SpellCheckHelper.KEYWORD);
} }
else if (this.target instanceof HTMLHtmlElement) { else if (this.target instanceof HTMLHtmlElement) {
var bodyElt = this.target.ownerDocument.body; var bodyElt = ownerDoc.body;
if (bodyElt) { if (bodyElt) {
let computedURL; let computedURL;
try { try {
@ -776,11 +783,11 @@ nsContextMenu.prototype = {
this.onMathML = true; this.onMathML = true;
// See if the user clicked in a frame. // See if the user clicked in a frame.
var docDefaultView = this.target.ownerDocument.defaultView; var docDefaultView = ownerDoc.defaultView;
if (docDefaultView != docDefaultView.top) { if (docDefaultView != docDefaultView.top) {
this.inFrame = true; this.inFrame = true;
if (this.target.ownerDocument.isSrcdocDocument) { if (ownerDoc.isSrcdocDocument) {
this.inSrcdocFrame = true; this.inSrcdocFrame = true;
} }
} }
@ -805,7 +812,7 @@ nsContextMenu.prototype = {
InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo); InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
} }
else { else {
var targetWin = this.target.ownerDocument.defaultView; var targetWin = ownerDoc.defaultView;
var editingSession = targetWin.QueryInterface(Ci.nsIInterfaceRequestor) var editingSession = targetWin.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation) .getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor) .QueryInterface(Ci.nsIInterfaceRequestor)

View File

@ -3776,6 +3776,7 @@
referrerPolicy: aMessage.data.referrerPolicy, referrerPolicy: aMessage.data.referrerPolicy,
contentType: aMessage.data.contentType, contentType: aMessage.data.contentType,
contentDisposition: aMessage.data.contentDisposition, contentDisposition: aMessage.data.contentDisposition,
frameOuterWindowID: aMessage.data.frameOuterWindowID,
}; };
let popup = browser.ownerDocument.getElementById("contentAreaContextMenu"); let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
let event = gContextMenuContentData.event; let event = gContextMenuContentData.event;

View File

@ -5,6 +5,7 @@
const TESTROOT = "http://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; const TESTROOT = "http://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/";
const TESTROOT2 = "http://example.org/browser/toolkit/mozapps/extensions/test/xpinstall/"; const TESTROOT2 = "http://example.org/browser/toolkit/mozapps/extensions/test/xpinstall/";
const SECUREROOT = "https://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; const SECUREROOT = "https://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/";
const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts"; const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
const PROGRESS_NOTIFICATION = "addon-progress"; const PROGRESS_NOTIFICATION = "addon-progress";
@ -86,6 +87,52 @@ function wait_for_notification_close(aCallback) {
}, false); }, false);
} }
function wait_for_install_dialog(aCallback) {
if (Preferences.get("xpinstall.customConfirmationUI", false)) {
wait_for_notification("addon-install-confirmation", function(aPanel) {
aCallback();
});
return;
}
info("Waiting for install dialog");
Services.wm.addListener({
onOpenWindow: function(aXULWindow) {
info("Install dialog opened, waiting for focus");
Services.wm.removeListener(this);
var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
waitForFocus(function() {
info("Saw install dialog");
is(domwindow.document.location.href, XPINSTALL_URL, "Should have seen the right window open");
// Override the countdown timer on the accept button
var button = domwindow.document.documentElement.getButton("accept");
button.disabled = false;
aCallback();
}, domwindow);
},
onCloseWindow: function(aXULWindow) {
},
onWindowTitleChange: function(aXULWindow, aNewTitle) {
}
});
}
function accept_install_dialog() {
if (Preferences.get("xpinstall.customConfirmationUI", false)) {
document.getElementById("addon-install-confirmation-accept").click();
} else {
let win = Services.wm.getMostRecentWindow("Addons:Install");
win.document.documentElement.acceptDialog();
}
}
function wait_for_single_notification(aCallback) { function wait_for_single_notification(aCallback) {
function inner_waiter() { function inner_waiter() {
info("Waiting for single notification"); info("Waiting for single notification");
@ -165,7 +212,7 @@ function test_blocked_install() {
"Should have seen the right message"); "Should have seen the right message");
// Wait for the install confirmation dialog // Wait for the install confirmation dialog
wait_for_notification("addon-install-confirmation", function(aPanel) { wait_for_install_dialog(function() {
// Wait for the complete notification // Wait for the complete notification
wait_for_notification("addon-install-complete", function(aPanel) { wait_for_notification("addon-install-complete", function(aPanel) {
let notification = aPanel.childNodes[0]; let notification = aPanel.childNodes[0];
@ -183,7 +230,7 @@ function test_blocked_install() {
}); });
}); });
document.getElementById("addon-install-confirmation-accept").click(); accept_install_dialog();
}); });
// Click on Allow // Click on Allow
@ -208,7 +255,7 @@ function test_whitelisted_install() {
gBrowser.selectedTab = originalTab; gBrowser.selectedTab = originalTab;
// Wait for the install confirmation dialog // Wait for the install confirmation dialog
wait_for_notification("addon-install-confirmation", function(aPanel) { wait_for_install_dialog(function() {
is(gBrowser.selectedTab, tab, is(gBrowser.selectedTab, tab,
"tab selected in response to the addon-install-confirmation notification"); "tab selected in response to the addon-install-confirmation notification");
@ -230,7 +277,7 @@ function test_whitelisted_install() {
}); });
}); });
document.getElementById("addon-install-confirmation-accept").click(); accept_install_dialog();
}); });
}); });
@ -331,7 +378,7 @@ function test_restartless() {
// Wait for the progress notification // Wait for the progress notification
wait_for_progress_notification(function(aPanel) { wait_for_progress_notification(function(aPanel) {
// Wait for the install confirmation dialog // Wait for the install confirmation dialog
wait_for_notification("addon-install-confirmation", function(aPanel) { wait_for_install_dialog(function() {
// Wait for the complete notification // Wait for the complete notification
wait_for_notification("addon-install-complete", function(aPanel) { wait_for_notification("addon-install-complete", function(aPanel) {
let notification = aPanel.childNodes[0]; let notification = aPanel.childNodes[0];
@ -352,7 +399,7 @@ function test_restartless() {
}); });
}); });
document.getElementById("addon-install-confirmation-accept").click(); accept_install_dialog();
}); });
}); });
@ -370,7 +417,7 @@ function test_multiple() {
// Wait for the progress notification // Wait for the progress notification
wait_for_progress_notification(function(aPanel) { wait_for_progress_notification(function(aPanel) {
// Wait for the install confirmation dialog // Wait for the install confirmation dialog
wait_for_notification("addon-install-confirmation", function(aPanel) { wait_for_install_dialog(function() {
// Wait for the complete notification // Wait for the complete notification
wait_for_notification("addon-install-complete", function(aPanel) { wait_for_notification("addon-install-complete", function(aPanel) {
let notification = aPanel.childNodes[0]; let notification = aPanel.childNodes[0];
@ -393,7 +440,7 @@ function test_multiple() {
}); });
}); });
document.getElementById("addon-install-confirmation-accept").click(); accept_install_dialog();
}); });
}); });
@ -412,7 +459,7 @@ function test_url() {
// Wait for the progress notification // Wait for the progress notification
wait_for_progress_notification(function(aPanel) { wait_for_progress_notification(function(aPanel) {
// Wait for the install confirmation dialog // Wait for the install confirmation dialog
wait_for_notification("addon-install-confirmation", function(aPanel) { wait_for_install_dialog(function() {
// Wait for the complete notification // Wait for the complete notification
wait_for_notification("addon-install-complete", function(aPanel) { wait_for_notification("addon-install-complete", function(aPanel) {
let notification = aPanel.childNodes[0]; let notification = aPanel.childNodes[0];
@ -430,7 +477,7 @@ function test_url() {
}); });
}); });
document.getElementById("addon-install-confirmation-accept").click(); accept_install_dialog();
}); });
}); });
@ -499,7 +546,7 @@ function test_reload() {
// Wait for the progress notification // Wait for the progress notification
wait_for_progress_notification(function(aPanel) { wait_for_progress_notification(function(aPanel) {
// Wait for the install confirmation dialog // Wait for the install confirmation dialog
wait_for_notification("addon-install-confirmation", function(aPanel) { wait_for_install_dialog(function() {
// Wait for the complete notification // Wait for the complete notification
wait_for_notification("addon-install-complete", function(aPanel) { wait_for_notification("addon-install-complete", function(aPanel) {
let notification = aPanel.childNodes[0]; let notification = aPanel.childNodes[0];
@ -534,7 +581,7 @@ function test_reload() {
gBrowser.loadURI(TESTROOT2 + "enabled.html"); gBrowser.loadURI(TESTROOT2 + "enabled.html");
}); });
document.getElementById("addon-install-confirmation-accept").click(); accept_install_dialog();
}); });
}); });
@ -552,7 +599,7 @@ function test_theme() {
// Wait for the progress notification // Wait for the progress notification
wait_for_progress_notification(function(aPanel) { wait_for_progress_notification(function(aPanel) {
// Wait for the install confirmation dialog // Wait for the install confirmation dialog
wait_for_notification("addon-install-confirmation", function(aPanel) { wait_for_install_dialog(function() {
// Wait for the complete notification // Wait for the complete notification
wait_for_notification("addon-install-complete", function(aPanel) { wait_for_notification("addon-install-complete", function(aPanel) {
let notification = aPanel.childNodes[0]; let notification = aPanel.childNodes[0];
@ -577,7 +624,7 @@ function test_theme() {
}); });
}); });
document.getElementById("addon-install-confirmation-accept").click(); accept_install_dialog();
}); });
}); });
@ -630,7 +677,7 @@ function test_renotify_installed() {
// Wait for the progress notification // Wait for the progress notification
wait_for_progress_notification(function(aPanel) { wait_for_progress_notification(function(aPanel) {
// Wait for the install confirmation dialog // Wait for the install confirmation dialog
wait_for_notification("addon-install-confirmation", function(aPanel) { wait_for_install_dialog(function() {
// Wait for the complete notification // Wait for the complete notification
wait_for_notification("addon-install-complete", function(aPanel) { wait_for_notification("addon-install-complete", function(aPanel) {
// Dismiss the notification // Dismiss the notification
@ -640,7 +687,7 @@ function test_renotify_installed() {
// Wait for the progress notification // Wait for the progress notification
wait_for_progress_notification(function(aPanel) { wait_for_progress_notification(function(aPanel) {
// Wait for the install confirmation dialog // Wait for the install confirmation dialog
wait_for_notification("addon-install-confirmation", function(aPanel) { wait_for_install_dialog(function() {
info("Timeouts after this probably mean bug 589954 regressed"); info("Timeouts after this probably mean bug 589954 regressed");
// Wait for the complete notification // Wait for the complete notification
@ -655,7 +702,7 @@ function test_renotify_installed() {
}); });
}); });
document.getElementById("addon-install-confirmation-accept").click(); accept_install_dialog();
}); });
}); });
@ -667,7 +714,7 @@ function test_renotify_installed() {
aPanel.hidePopup(); aPanel.hidePopup();
}); });
document.getElementById("addon-install-confirmation-accept").click(); accept_install_dialog();
}); });
}); });

View File

@ -282,7 +282,10 @@ let LoopCallsInternal = {
*/ */
_startCall: function(callData) { _startCall: function(callData) {
const openChat = () => { const openChat = () => {
this.conversationInProgress.id = MozLoopService.openChatWindow(callData); let windowId = MozLoopService.openChatWindow(callData);
if (windowId) {
this.conversationInProgress.id = windowId;
}
}; };
if (callData.type == "incoming" && ("callerId" in callData) && if (callData.type == "incoming" && ("callerId" in callData) &&

View File

@ -62,11 +62,7 @@ const extend = function(target, source) {
*/ */
const containsParticipant = function(room, participant) { const containsParticipant = function(room, participant) {
for (let user of room.participants) { for (let user of room.participants) {
// XXX until a bug 1100318 is implemented and deployed, if (user.roomConnectionId == participant.roomConnectionId) {
// we need to check the "id" field here as well - roomConnectionId is the
// official value for the interface.
if (user.roomConnectionId == participant.roomConnectionId &&
user.id == participant.id) {
return true; return true;
} }
} }
@ -451,10 +447,7 @@ let LoopRoomsInternal = {
let origRoom = this.rooms.get(roomToken); let origRoom = this.rooms.get(roomToken);
let patchData = { let patchData = {
roomName: newRoomName, roomName: newRoomName
// XXX We have to supply the max size and room owner due to bug 1099063.
maxSize: origRoom.maxSize,
roomOwner: origRoom.roomOwner
}; };
MozLoopService.hawkRequest(this.sessionType, url, "PATCH", patchData) MozLoopService.hawkRequest(this.sessionType, url, "PATCH", patchData)
.then(response => { .then(response => {

View File

@ -827,7 +827,8 @@ let MozLoopServiceInternal = {
* *
* @param {Object} conversationWindowData The data to be obtained by the * @param {Object} conversationWindowData The data to be obtained by the
* window when it opens. * window when it opens.
* @returns {Number} The id of the window. * @returns {Number} The id of the window, null if a window could not
* be opened.
*/ */
openChatWindow: function(conversationWindowData) { openChatWindow: function(conversationWindowData) {
// So I guess the origin is the loop server!? // So I guess the origin is the loop server!?
@ -913,7 +914,9 @@ let MozLoopServiceInternal = {
}.bind(this), true); }.bind(this), true);
}; };
Chat.open(null, origin, "", url, undefined, undefined, callback); if (!Chat.open(null, origin, "", url, undefined, undefined, callback)) {
return null;
}
return windowId; return windowId;
}, },

View File

@ -33,7 +33,6 @@
<script type="text/javascript" src="loop/shared/js/dispatcher.js"></script> <script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
<script type="text/javascript" src="loop/shared/js/otSdkDriver.js"></script> <script type="text/javascript" src="loop/shared/js/otSdkDriver.js"></script>
<script type="text/javascript" src="loop/shared/js/store.js"></script> <script type="text/javascript" src="loop/shared/js/store.js"></script>
<script type="text/javascript" src="loop/shared/js/roomStore.js"></script>
<script type="text/javascript" src="loop/shared/js/conversationStore.js"></script> <script type="text/javascript" src="loop/shared/js/conversationStore.js"></script>
<script type="text/javascript" src="loop/shared/js/roomStates.js"></script> <script type="text/javascript" src="loop/shared/js/roomStates.js"></script>
<script type="text/javascript" src="loop/shared/js/fxOSActiveRoomStore.js"></script> <script type="text/javascript" src="loop/shared/js/fxOSActiveRoomStore.js"></script>
@ -46,6 +45,7 @@
<script type="text/javascript" src="loop/js/conversationAppStore.js"></script> <script type="text/javascript" src="loop/js/conversationAppStore.js"></script>
<script type="text/javascript" src="loop/js/client.js"></script> <script type="text/javascript" src="loop/js/client.js"></script>
<script type="text/javascript" src="loop/js/conversationViews.js"></script> <script type="text/javascript" src="loop/js/conversationViews.js"></script>
<script type="text/javascript" src="loop/js/roomStore.js"></script>
<script type="text/javascript" src="loop/js/roomViews.js"></script> <script type="text/javascript" src="loop/js/roomViews.js"></script>
<script type="text/javascript" src="loop/js/conversation.js"></script> <script type="text/javascript" src="loop/js/conversation.js"></script>
</body> </body>

View File

@ -28,11 +28,11 @@
<script type="text/javascript" src="loop/shared/js/actions.js"></script> <script type="text/javascript" src="loop/shared/js/actions.js"></script>
<script type="text/javascript" src="loop/shared/js/dispatcher.js"></script> <script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
<script type="text/javascript" src="loop/shared/js/store.js"></script> <script type="text/javascript" src="loop/shared/js/store.js"></script>
<script type="text/javascript" src="loop/shared/js/roomStore.js"></script>
<script type="text/javascript" src="loop/shared/js/roomStates.js"></script> <script type="text/javascript" src="loop/shared/js/roomStates.js"></script>
<script type="text/javascript" src="loop/shared/js/fxOSActiveRoomStore.js"></script> <script type="text/javascript" src="loop/shared/js/fxOSActiveRoomStore.js"></script>
<script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script> <script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
<script type="text/javascript;version=1.8" src="loop/js/contacts.js"></script> <script type="text/javascript;version=1.8" src="loop/js/contacts.js"></script>
<script type="text/javascript" src="loop/js/roomStore.js"></script>
<script type="text/javascript" src="loop/js/panel.js"></script> <script type="text/javascript" src="loop/js/panel.js"></script>
</body> </body>
</html> </html>

View File

@ -18,6 +18,7 @@ browser.jar:
content/browser/loop/js/panel.js (content/js/panel.js) content/browser/loop/js/panel.js (content/js/panel.js)
content/browser/loop/js/contacts.js (content/js/contacts.js) content/browser/loop/js/contacts.js (content/js/contacts.js)
content/browser/loop/js/conversationViews.js (content/js/conversationViews.js) content/browser/loop/js/conversationViews.js (content/js/conversationViews.js)
content/browser/loop/js/roomStore.js (content/js/roomStore.js)
content/browser/loop/js/roomViews.js (content/js/roomViews.js) content/browser/loop/js/roomViews.js (content/js/roomViews.js)
# Desktop styles # Desktop styles
@ -71,7 +72,6 @@ browser.jar:
content/browser/loop/shared/js/actions.js (content/shared/js/actions.js) content/browser/loop/shared/js/actions.js (content/shared/js/actions.js)
content/browser/loop/shared/js/conversationStore.js (content/shared/js/conversationStore.js) content/browser/loop/shared/js/conversationStore.js (content/shared/js/conversationStore.js)
content/browser/loop/shared/js/store.js (content/shared/js/store.js) content/browser/loop/shared/js/store.js (content/shared/js/store.js)
content/browser/loop/shared/js/roomStore.js (content/shared/js/roomStore.js)
content/browser/loop/shared/js/roomStates.js (content/shared/js/roomStates.js) content/browser/loop/shared/js/roomStates.js (content/shared/js/roomStates.js)
content/browser/loop/shared/js/fxOSActiveRoomStore.js (content/shared/js/fxOSActiveRoomStore.js) content/browser/loop/shared/js/fxOSActiveRoomStore.js (content/shared/js/fxOSActiveRoomStore.js)
content/browser/loop/shared/js/activeRoomStore.js (content/shared/js/activeRoomStore.js) content/browser/loop/shared/js/activeRoomStore.js (content/shared/js/activeRoomStore.js)

View File

@ -50,7 +50,6 @@
<script src="../../content/shared/js/otSdkDriver.js"></script> <script src="../../content/shared/js/otSdkDriver.js"></script>
<script src="../../content/shared/js/store.js"></script> <script src="../../content/shared/js/store.js"></script>
<script src="../../content/shared/js/conversationStore.js"></script> <script src="../../content/shared/js/conversationStore.js"></script>
<script src="../../content/shared/js/roomStore.js"></script>
<script src="../../content/shared/js/roomStates.js"></script> <script src="../../content/shared/js/roomStates.js"></script>
<script src="../../content/shared/js/fxOSActiveRoomStore.js"></script> <script src="../../content/shared/js/fxOSActiveRoomStore.js"></script>
<script src="../../content/shared/js/activeRoomStore.js"></script> <script src="../../content/shared/js/activeRoomStore.js"></script>
@ -59,6 +58,7 @@
<script src="../../content/shared/js/feedbackViews.js"></script> <script src="../../content/shared/js/feedbackViews.js"></script>
<script src="../../content/js/client.js"></script> <script src="../../content/js/client.js"></script>
<script src="../../content/js/conversationAppStore.js"></script> <script src="../../content/js/conversationAppStore.js"></script>
<script src="../../content/js/roomStore.js"></script>
<script src="../../content/js/roomViews.js"></script> <script src="../../content/js/roomViews.js"></script>
<script src="../../content/js/conversationViews.js"></script> <script src="../../content/js/conversationViews.js"></script>
<script src="../../content/js/conversation.js"></script> <script src="../../content/js/conversation.js"></script>
@ -74,6 +74,7 @@
<script src="conversationViews_test.js"></script> <script src="conversationViews_test.js"></script>
<script src="contacts_test.js"></script> <script src="contacts_test.js"></script>
<script src="l10n_test.js"></script> <script src="l10n_test.js"></script>
<script src="roomStore_test.js"></script>
<script> <script>
// Stop the default init functions running to avoid conflicts in tests // Stop the default init functions running to avoid conflicts in tests
document.removeEventListener('DOMContentLoaded', loop.panel.init); document.removeEventListener('DOMContentLoaded', loop.panel.init);

View File

@ -54,7 +54,6 @@
<script src="../../content/shared/js/roomStates.js"></script> <script src="../../content/shared/js/roomStates.js"></script>
<script src="../../content/shared/js/fxOSActiveRoomStore.js"></script> <script src="../../content/shared/js/fxOSActiveRoomStore.js"></script>
<script src="../../content/shared/js/activeRoomStore.js"></script> <script src="../../content/shared/js/activeRoomStore.js"></script>
<script src="../../content/shared/js/roomStore.js"></script>
<script src="../../content/shared/js/conversationStore.js"></script> <script src="../../content/shared/js/conversationStore.js"></script>
<script src="../../content/shared/js/feedbackStore.js"></script> <script src="../../content/shared/js/feedbackStore.js"></script>
<script src="../../content/shared/js/views.js"></script> <script src="../../content/shared/js/views.js"></script>
@ -77,7 +76,6 @@
<script src="feedbackStore_test.js"></script> <script src="feedbackStore_test.js"></script>
<script src="otSdkDriver_test.js"></script> <script src="otSdkDriver_test.js"></script>
<script src="store_test.js"></script> <script src="store_test.js"></script>
<script src="roomStore_test.js"></script>
<script> <script>
describe("Uncaught Error Check", function() { describe("Uncaught Error Check", function() {
it("should load the tests without errors", function() { it("should load the tests without errors", function() {

View File

@ -33,6 +33,7 @@ add_task(function* test_busy_2fxa_calls() {
Chat.open = function(contentWindow, origin, title, url) { Chat.open = function(contentWindow, origin, title, url) {
opened++; opened++;
windowId = url.match(/about:loopconversation\#(\d+)$/)[1]; windowId = url.match(/about:loopconversation\#(\d+)$/)[1];
return windowId;
}; };
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA); mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);

View File

@ -19,6 +19,7 @@ add_task(function test_startDirectCall_opens_window() {
let openedUrl; let openedUrl;
Chat.open = function(contentWindow, origin, title, url) { Chat.open = function(contentWindow, origin, title, url) {
openedUrl = url; openedUrl = url;
return 1;
}; };
LoopCalls.startDirectCall(contact, "audio-video"); LoopCalls.startDirectCall(contact, "audio-video");
@ -34,6 +35,7 @@ add_task(function test_startDirectCall_getConversationWindowData() {
let openedUrl; let openedUrl;
Chat.open = function(contentWindow, origin, title, url) { Chat.open = function(contentWindow, origin, title, url) {
openedUrl = url; openedUrl = url;
return 2;
}; };
LoopCalls.startDirectCall(contact, "audio-video"); LoopCalls.startDirectCall(contact, "audio-video");
@ -49,6 +51,36 @@ add_task(function test_startDirectCall_getConversationWindowData() {
LoopCalls.clearCallInProgress(windowId); LoopCalls.clearCallInProgress(windowId);
}); });
add_task(function test_startDirectCall_not_busy_if_window_fails_to_open() {
let openedUrl;
// Simulate no window available to open.
Chat.open = function(contentWindow, origin, title, url) {
openedUrl = url;
return null;
};
LoopCalls.startDirectCall(contact, "audio-video");
do_check_true(!!openedUrl, "should have attempted to open chat window");
openedUrl = null;
// Window opens successfully this time.
Chat.open = function(contentWindow, origin, title, url) {
openedUrl = url;
return 3;
};
LoopCalls.startDirectCall(contact, "audio-video");
do_check_true(!!openedUrl, "should open a chat window");
// Stop the busy kicking in for following tests.
let windowId = openedUrl.match(/about:loopconversation\#(\d+)$/)[1];
LoopCalls.clearCallInProgress(windowId);
});
function run_test() { function run_test() {
do_register_cleanup(function() { do_register_cleanup(function() {
// Revert original Chat.open implementation // Revert original Chat.open implementation

View File

@ -49,7 +49,6 @@
<script src="../content/shared/js/validate.js"></script> <script src="../content/shared/js/validate.js"></script>
<script src="../content/shared/js/dispatcher.js"></script> <script src="../content/shared/js/dispatcher.js"></script>
<script src="../content/shared/js/store.js"></script> <script src="../content/shared/js/store.js"></script>
<script src="../content/shared/js/roomStore.js"></script>
<script src="../content/shared/js/conversationStore.js"></script> <script src="../content/shared/js/conversationStore.js"></script>
<script src="../content/shared/js/roomStates.js"></script> <script src="../content/shared/js/roomStates.js"></script>
<script src="../content/shared/js/fxOSActiveRoomStore.js"></script> <script src="../content/shared/js/fxOSActiveRoomStore.js"></script>
@ -57,6 +56,7 @@
<script src="../content/shared/js/feedbackStore.js"></script> <script src="../content/shared/js/feedbackStore.js"></script>
<script src="../content/shared/js/views.js"></script> <script src="../content/shared/js/views.js"></script>
<script src="../content/shared/js/feedbackViews.js"></script> <script src="../content/shared/js/feedbackViews.js"></script>
<script src="../content/js/roomStore.js"></script>
<script src="../content/js/roomViews.js"></script> <script src="../content/js/roomViews.js"></script>
<script src="../content/js/conversationViews.js"></script> <script src="../content/js/conversationViews.js"></script>
<script src="../content/js/client.js"></script> <script src="../content/js/client.js"></script>

View File

@ -3,6 +3,7 @@ tags = devtools
skip-if = e10s # Handle in Bug 1077464 for profiler skip-if = e10s # Handle in Bug 1077464 for profiler
subsuite = devtools subsuite = devtools
support-files = support-files =
doc_innerHTML.html
doc_simple-test.html doc_simple-test.html
head.js head.js
@ -10,6 +11,9 @@ support-files =
# that need to be moved over to performance tool # that need to be moved over to performance tool
[browser_perf-aaa-run-first-leaktest.js] [browser_perf-aaa-run-first-leaktest.js]
[browser_markers-parse-html.js]
[browser_perf-allocations-to-samples.js] [browser_perf-allocations-to-samples.js]
[browser_perf-compatibility-01.js] [browser_perf-compatibility-01.js]
[browser_perf-compatibility-02.js] [browser_perf-compatibility-02.js]

View File

@ -0,0 +1,36 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get a "Parse HTML" marker when a page sets innerHTML.
*/
const TEST_URL = EXAMPLE_URL + "doc_innerHTML.html"
function* getMarkers(front) {
const { promise, resolve } = Promise.defer();
const handler = (_, markers) => {
resolve(markers);
};
front.on("markers", handler);
yield front.startRecording({ withTicks: true });
const markers = yield promise;
front.off("markers", handler);
yield front.stopRecording();
return markers;
}
function* spawnTest () {
let { target, front } = yield initBackend(TEST_URL);
const markers = yield getMarkers(front);
info("markers = " + JSON.stringify(markers, null, 2));
ok(markers.some(m => m.name === "Parse HTML" && m.stack != undefined),
"Should get some Parse HTML markers");
yield removeTab(target.tab);
finish();
}

View File

@ -0,0 +1,20 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Performance tool + innerHTML test page</title>
</head>
<body>
<script type="text/javascript">
window.test = function () {
document.body.innerHTML = "<h1>LOL</h1>";
};
setInterval(window.test, 100);
</script>
</body>
</html>

View File

@ -53,6 +53,16 @@ const TIMELINE_BLUEPRINT = {
colorName: "highlight-lightorange", colorName: "highlight-lightorange",
label: L10N.getStr("timeline.label.javascript2") label: L10N.getStr("timeline.label.javascript2")
}, },
"Parse HTML": {
group: 1,
colorName: "highlight-lightorange",
label: L10N.getStr("timeline.label.parseHTML")
},
"Parse XML": {
group: 1,
colorName: "highlight-lightorange",
label: L10N.getStr("timeline.label.parseXML")
},
"ConsoleTime": { "ConsoleTime": {
group: 2, group: 2,
colorName: "highlight-bluegrey", colorName: "highlight-bluegrey",

View File

@ -40,6 +40,8 @@ timeline.label.styles2=Recalculate Style
timeline.label.reflow2=Layout timeline.label.reflow2=Layout
timeline.label.paint=Paint timeline.label.paint=Paint
timeline.label.javascript2=Function Call timeline.label.javascript2=Function Call
timeline.label.parseHTML=Parse HTML
timeline.label.parseXML=Parse XML
timeline.label.domevent=DOM Event timeline.label.domevent=DOM Event
timeline.label.consoleTime=Console timeline.label.consoleTime=Console

View File

@ -27,8 +27,8 @@
} }
/* Square back and forward buttons */ /* Square back and forward buttons */
#back-button:not(:-moz-lwtheme) > .toolbarbutton-icon, #back-button > .toolbarbutton-icon,
#forward-button:not(:-moz-lwtheme) > .toolbarbutton-icon { #forward-button > .toolbarbutton-icon {
margin: 0; margin: 0;
border: none; border: none;
padding: 2px 6px; padding: 2px 6px;

View File

@ -218,7 +218,7 @@ toolbar[brighttext] #downloads-indicator-counter {
:root[devtoolstheme="dark"] .browserContainer > findbar .findbar-textbox { :root[devtoolstheme="dark"] .browserContainer > findbar .findbar-textbox {
background-color: var(--url-and-searchbar-background-color) !important; background-color: var(--url-and-searchbar-background-color) !important;
background-image: none !important; background-image: none !important;
color: var(--url-and-searchbar-color); color: var(--url-and-searchbar-color) !important;
border: none !important; border: none !important;
box-shadow: none !important; box-shadow: none !important;
} }

View File

@ -0,0 +1,73 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef AutoTimelineMarker_h__
#define AutoTimelineMarker_h__
#include "mozilla/GuardObjects.h"
#include "mozilla/Move.h"
#include "nsDocShell.h"
#include "nsRefPtr.h"
namespace mozilla {
// # AutoTimelineMarker
//
// An RAII class to trace some task in the platform by adding a start and end
// timeline marker pair. These markers are then rendered in the devtools'
// performance tool's waterfall graph.
//
// Example usage:
//
// {
// AutoTimelineMarker marker(mDocShell, "Parse CSS");
// nsresult rv = ParseTheCSSFile(mFile);
// ...
// }
class MOZ_STACK_CLASS AutoTimelineMarker
{
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
nsRefPtr<nsDocShell> mDocShell;
const char* mName;
bool
DocShellIsRecording(nsDocShell& aDocShell)
{
bool isRecording = false;
if (nsDocShell::gProfileTimelineRecordingsCount > 0) {
aDocShell.GetRecordProfileTimelineMarkers(&isRecording);
}
return isRecording;
}
public:
explicit AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mDocShell(nullptr)
, mName(aName)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
nsDocShell* docShell = static_cast<nsDocShell*>(aDocShell);
if (docShell && DocShellIsRecording(*docShell)) {
mDocShell = docShell;
mDocShell->AddProfileTimelineMarker(mName, TRACING_INTERVAL_START);
}
}
~AutoTimelineMarker()
{
if (mDocShell) {
mDocShell->AddProfileTimelineMarker(mName, TRACING_INTERVAL_END);
}
}
};
} // namespace mozilla
#endif /* AutoTimelineMarker_h__ */

View File

@ -42,6 +42,7 @@ EXPORTS += [
] ]
EXPORTS.mozilla += [ EXPORTS.mozilla += [
'AutoTimelineMarker.h',
'IHistory.h', 'IHistory.h',
'LoadContext.h', 'LoadContext.h',
] ]

View File

@ -30,6 +30,7 @@
#include "mozilla/ArrayUtils.h" #include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/AutoRestore.h" #include "mozilla/AutoRestore.h"
#include "mozilla/AutoTimelineMarker.h"
#include "mozilla/Base64.h" #include "mozilla/Base64.h"
#include "mozilla/CheckedInt.h" #include "mozilla/CheckedInt.h"
#include "mozilla/DebugOnly.h" #include "mozilla/DebugOnly.h"
@ -4246,6 +4247,8 @@ nsContentUtils::ParseFragmentHTML(const nsAString& aSourceBuffer,
bool aQuirks, bool aQuirks,
bool aPreventScriptExecution) bool aPreventScriptExecution)
{ {
AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");
if (nsContentUtils::sFragmentParsingActive) { if (nsContentUtils::sFragmentParsingActive) {
NS_NOTREACHED("Re-entrant fragment parsing attempted."); NS_NOTREACHED("Re-entrant fragment parsing attempted.");
return NS_ERROR_DOM_INVALID_STATE_ERR; return NS_ERROR_DOM_INVALID_STATE_ERR;
@ -4272,6 +4275,8 @@ nsContentUtils::ParseDocumentHTML(const nsAString& aSourceBuffer,
nsIDocument* aTargetDocument, nsIDocument* aTargetDocument,
bool aScriptingEnabledForNoscriptParsing) bool aScriptingEnabledForNoscriptParsing)
{ {
AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML");
if (nsContentUtils::sFragmentParsingActive) { if (nsContentUtils::sFragmentParsingActive) {
NS_NOTREACHED("Re-entrant fragment parsing attempted."); NS_NOTREACHED("Re-entrant fragment parsing attempted.");
return NS_ERROR_DOM_INVALID_STATE_ERR; return NS_ERROR_DOM_INVALID_STATE_ERR;
@ -4297,6 +4302,8 @@ nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
bool aPreventScriptExecution, bool aPreventScriptExecution,
nsIDOMDocumentFragment** aReturn) nsIDOMDocumentFragment** aReturn)
{ {
AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML");
if (nsContentUtils::sFragmentParsingActive) { if (nsContentUtils::sFragmentParsingActive) {
NS_NOTREACHED("Re-entrant fragment parsing attempted."); NS_NOTREACHED("Re-entrant fragment parsing attempted.");
return NS_ERROR_DOM_INVALID_STATE_ERR; return NS_ERROR_DOM_INVALID_STATE_ERR;

View File

@ -297,14 +297,16 @@ final class GeckoEditable
} }
} }
/**
* Remove the head of the queue. Throw if queue is empty.
*/
void poll() { void poll() {
if (DEBUG) { if (DEBUG) {
ThreadUtils.assertOnGeckoThread(); ThreadUtils.assertOnGeckoThread();
} }
if (mActions.isEmpty()) { if (mActions.poll() == null) {
throw new IllegalStateException("empty actions queue"); throw new IllegalStateException("empty actions queue");
} }
mActions.poll();
synchronized(this) { synchronized(this) {
if (mActions.isEmpty()) { if (mActions.isEmpty()) {
@ -313,13 +315,15 @@ final class GeckoEditable
} }
} }
/**
* Return, but don't remove, the head of the queue, or null if queue is empty.
*
* @return head of the queue or null if empty.
*/
Action peek() { Action peek() {
if (DEBUG) { if (DEBUG) {
ThreadUtils.assertOnGeckoThread(); ThreadUtils.assertOnGeckoThread();
} }
if (mActions.isEmpty()) {
throw new IllegalStateException("empty actions queue");
}
return mActions.peek(); return mActions.peek();
} }
@ -669,7 +673,11 @@ final class GeckoEditable
// GeckoEditableListener methods should all be called from the Gecko thread // GeckoEditableListener methods should all be called from the Gecko thread
ThreadUtils.assertOnGeckoThread(); ThreadUtils.assertOnGeckoThread();
} }
final Action action = mActionQueue.peek(); final Action action = mActionQueue.peek();
if (action == null) {
throw new IllegalStateException("empty actions queue");
}
if (DEBUG) { if (DEBUG) {
Log.d(LOGTAG, "reply: Action(" + Log.d(LOGTAG, "reply: Action(" +
@ -834,8 +842,8 @@ final class GeckoEditable
/* An event (keypress, etc.) has potentially changed the selection, /* An event (keypress, etc.) has potentially changed the selection,
synchronize the selection here. There is not a race with the IC thread synchronize the selection here. There is not a race with the IC thread
because the IC thread should be blocked on the event action */ because the IC thread should be blocked on the event action */
if (!mActionQueue.isEmpty() && final Action action = mActionQueue.peek();
mActionQueue.peek().mType == Action.TYPE_EVENT) { if (action != null && action.mType == Action.TYPE_EVENT) {
Selection.setSelection(mText, start, end); Selection.setSelection(mText, start, end);
return; return;
} }
@ -868,6 +876,11 @@ final class GeckoEditable
mText.insert(start, newText); mText.insert(start, newText);
} }
private boolean isSameText(int start, int oldEnd, CharSequence newText) {
return oldEnd - start == newText.length() &&
TextUtils.regionMatches(mText, start, newText, 0, oldEnd - start);
}
@Override @Override
public void onTextChange(final CharSequence text, final int start, public void onTextChange(final CharSequence text, final int start,
final int unboundedOldEnd, final int unboundedNewEnd) { final int unboundedOldEnd, final int unboundedNewEnd) {
@ -909,46 +922,51 @@ final class GeckoEditable
TextUtils.copySpansFrom(mText, start, Math.min(oldEnd, newEnd), TextUtils.copySpansFrom(mText, start, Math.min(oldEnd, newEnd),
Object.class, mChangedText, 0); Object.class, mChangedText, 0);
if (!mActionQueue.isEmpty()) { final Action action = mActionQueue.peek();
final Action action = mActionQueue.peek(); if (action != null &&
if ((action.mType == Action.TYPE_REPLACE_TEXT || (action.mType == Action.TYPE_REPLACE_TEXT ||
action.mType == Action.TYPE_COMPOSE_TEXT) && action.mType == Action.TYPE_COMPOSE_TEXT) &&
start <= action.mStart && start <= action.mStart &&
action.mStart + action.mSequence.length() <= newEnd) { action.mStart + action.mSequence.length() <= newEnd) {
// actionNewEnd is the new end of the original replacement action // actionNewEnd is the new end of the original replacement action
final int actionNewEnd = action.mStart + action.mSequence.length(); final int actionNewEnd = action.mStart + action.mSequence.length();
int selStart = Selection.getSelectionStart(mText); int selStart = Selection.getSelectionStart(mText);
int selEnd = Selection.getSelectionEnd(mText); int selEnd = Selection.getSelectionEnd(mText);
// Replace old spans with new spans // Replace old spans with new spans
mChangedText.replace(action.mStart - start, actionNewEnd - start, mChangedText.replace(action.mStart - start, actionNewEnd - start,
action.mSequence); action.mSequence);
geckoReplaceText(start, oldEnd, mChangedText); geckoReplaceText(start, oldEnd, mChangedText);
// delete/insert above might have moved our selection to somewhere else // delete/insert above might have moved our selection to somewhere else
// this happens when the Gecko text change covers a larger range than // this happens when the Gecko text change covers a larger range than
// the original replacement action. Fix selection here // the original replacement action. Fix selection here
if (selStart >= start && selStart <= oldEnd) { if (selStart >= start && selStart <= oldEnd) {
selStart = selStart < action.mStart ? selStart : selStart = selStart < action.mStart ? selStart :
selStart < action.mEnd ? actionNewEnd : selStart < action.mEnd ? actionNewEnd :
selStart + actionNewEnd - action.mEnd; selStart + actionNewEnd - action.mEnd;
mText.setSpan(Selection.SELECTION_START, selStart, selStart, mText.setSpan(Selection.SELECTION_START, selStart, selStart,
Spanned.SPAN_POINT_POINT); Spanned.SPAN_POINT_POINT);
}
if (selEnd >= start && selEnd <= oldEnd) {
selEnd = selEnd < action.mStart ? selEnd :
selEnd < action.mEnd ? actionNewEnd :
selEnd + actionNewEnd - action.mEnd;
mText.setSpan(Selection.SELECTION_END, selEnd, selEnd,
Spanned.SPAN_POINT_POINT);
}
} else {
geckoReplaceText(start, oldEnd, mChangedText);
} }
if (selEnd >= start && selEnd <= oldEnd) {
selEnd = selEnd < action.mStart ? selEnd :
selEnd < action.mEnd ? actionNewEnd :
selEnd + actionNewEnd - action.mEnd;
mText.setSpan(Selection.SELECTION_END, selEnd, selEnd,
Spanned.SPAN_POINT_POINT);
}
} else { } else {
// Gecko side initiated the text change.
if (isSameText(start, oldEnd, mChangedText)) {
// Nothing to do because the text is the same.
// This could happen when the composition is updated for example.
return;
}
geckoReplaceText(start, oldEnd, mChangedText); geckoReplaceText(start, oldEnd, mChangedText);
} }
geckoPostToIc(new Runnable() { geckoPostToIc(new Runnable() {
@Override @Override
public void run() { public void run() {

View File

@ -385,6 +385,12 @@ var SelectionHandler = {
return this.START_ERROR_NONTEXT_INPUT; return this.START_ERROR_NONTEXT_INPUT;
} }
const focus = Services.focus.focusedWindow;
if (focus) {
// Make sure any previous focus is cleared.
Services.focus.clearFocus(focus);
}
this._initTargetInfo(aElement, this.TYPE_SELECTION); this._initTargetInfo(aElement, this.TYPE_SELECTION);
// Perform the appropriate selection method, if we can't determine method, or it fails, return // Perform the appropriate selection method, if we can't determine method, or it fails, return

View File

@ -184,7 +184,7 @@ let TimelineActor = exports.TimelineActor = protocol.ActorClass({
let markers = []; let markers = [];
for (let docShell of this.docShells) { for (let docShell of this.docShells) {
markers = [...markers, ...docShell.popProfileTimelineMarkers()]; markers.push(...docShell.popProfileTimelineMarkers());
} }
// The docshell may return markers with stack traces attached. // The docshell may return markers with stack traces attached.

View File

@ -32,6 +32,7 @@ using mozilla::unused;
#include "nsFocusManager.h" #include "nsFocusManager.h"
#include "nsIWidgetListener.h" #include "nsIWidgetListener.h"
#include "nsViewManager.h" #include "nsViewManager.h"
#include "nsISelection.h"
#include "nsIDOMSimpleGestureEvent.h" #include "nsIDOMSimpleGestureEvent.h"
@ -178,7 +179,6 @@ nsWindow::nsWindow() :
mParent(nullptr), mParent(nullptr),
mFocus(nullptr), mFocus(nullptr),
mIMEMaskSelectionUpdate(false), mIMEMaskSelectionUpdate(false),
mIMEMaskTextUpdate(false),
mIMEMaskEventsCount(1), // Mask IME events since there's no focus yet mIMEMaskEventsCount(1), // Mask IME events since there's no focus yet
mIMERanges(new TextRangeArray()), mIMERanges(new TextRangeArray()),
mIMEUpdatingContext(false), mIMEUpdatingContext(false),
@ -1692,7 +1692,6 @@ nsWindow::RemoveIMEComposition()
nsRefPtr<nsWindow> kungFuDeathGrip(this); nsRefPtr<nsWindow> kungFuDeathGrip(this);
AutoIMEMask selMask(mIMEMaskSelectionUpdate); AutoIMEMask selMask(mIMEMaskSelectionUpdate);
AutoIMEMask textMask(mIMEMaskTextUpdate);
WidgetCompositionEvent compositionCommitEvent(true, WidgetCompositionEvent compositionCommitEvent(true,
NS_COMPOSITION_COMMIT_AS_IS, NS_COMPOSITION_COMMIT_AS_IS,
@ -1704,7 +1703,6 @@ nsWindow::RemoveIMEComposition()
void void
nsWindow::OnIMEEvent(AndroidGeckoEvent *ae) nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
{ {
MOZ_ASSERT(!mIMEMaskTextUpdate);
MOZ_ASSERT(!mIMEMaskSelectionUpdate); MOZ_ASSERT(!mIMEMaskSelectionUpdate);
/* /*
Rules for managing IME between Gecko and Java: Rules for managing IME between Gecko and Java:
@ -1781,9 +1779,6 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
Selection updates are masked so the result of our temporary Selection updates are masked so the result of our temporary
selection event is not passed on to Java selection event is not passed on to Java
Text updates are passed on, so the Java text can shadow the
Gecko text
*/ */
AutoIMEMask selMask(mIMEMaskSelectionUpdate); AutoIMEMask selMask(mIMEMaskSelectionUpdate);
const auto composition(GetIMEComposition()); const auto composition(GetIMEComposition());
@ -1932,11 +1927,10 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
to eliminate the possibility of this event altering the to eliminate the possibility of this event altering the
text content unintentionally. text content unintentionally.
Selection and text updates are masked so the result of Selection updates are masked so the result of
temporary events are not passed on to Java temporary events are not passed on to Java
*/ */
AutoIMEMask selMask(mIMEMaskSelectionUpdate); AutoIMEMask selMask(mIMEMaskSelectionUpdate);
AutoIMEMask textMask(mIMEMaskTextUpdate);
const auto composition(GetIMEComposition()); const auto composition(GetIMEComposition());
MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent()); MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent());
@ -2006,11 +2000,10 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
* Remove any previous composition. This is only used for * Remove any previous composition. This is only used for
* visual indication and does not affect the text content. * visual indication and does not affect the text content.
* *
* Selection and text updates are masked so the result of * Selection updates are masked so the result of
* temporary events are not passed on to Java * temporary events are not passed on to Java
*/ */
AutoIMEMask selMask(mIMEMaskSelectionUpdate); AutoIMEMask selMask(mIMEMaskSelectionUpdate);
AutoIMEMask textMask(mIMEMaskTextUpdate);
RemoveIMEComposition(); RemoveIMEComposition();
mIMERanges->Clear(); mIMERanges->Clear();
} }
@ -2197,7 +2190,19 @@ nsWindow::PostFlushIMEChanges()
void void
nsWindow::FlushIMEChanges() nsWindow::FlushIMEChanges()
{ {
// Only send change notifications if we are *not* masking events,
// i.e. if we have a focused editor,
NS_ENSURE_TRUE_VOID(!mIMEMaskEventsCount);
nsCOMPtr<nsISelection> imeSelection;
nsCOMPtr<nsIContent> imeRoot;
// If we are receiving notifications, we must have selection/root content.
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(IMEStateManager::GetFocusSelectionAndRoot(
getter_AddRefs(imeSelection), getter_AddRefs(imeRoot))));
nsRefPtr<nsWindow> kungFuDeathGrip(this); nsRefPtr<nsWindow> kungFuDeathGrip(this);
for (uint32_t i = 0; i < mIMETextChanges.Length(); i++) { for (uint32_t i = 0; i < mIMETextChanges.Length(); i++) {
IMEChange &change = mIMETextChanges[i]; IMEChange &change = mIMETextChanges[i];
@ -2213,8 +2218,8 @@ nsWindow::FlushIMEChanges()
event.InitForQueryTextContent(change.mStart, event.InitForQueryTextContent(change.mStart,
change.mNewEnd - change.mStart); change.mNewEnd - change.mStart);
DispatchEvent(&event); DispatchEvent(&event);
if (!event.mSucceeded) NS_ENSURE_TRUE_VOID(event.mSucceeded);
return; NS_ENSURE_TRUE_VOID(event.mReply.mContentsRoot == imeRoot.get());
} }
GeckoAppShell::NotifyIMEChange(event.mReply.mString, change.mStart, GeckoAppShell::NotifyIMEChange(event.mReply.mString, change.mStart,
@ -2227,8 +2232,8 @@ nsWindow::FlushIMEChanges()
InitEvent(event, nullptr); InitEvent(event, nullptr);
DispatchEvent(&event); DispatchEvent(&event);
if (!event.mSucceeded) NS_ENSURE_TRUE_VOID(event.mSucceeded);
return; NS_ENSURE_TRUE_VOID(event.mReply.mContentsRoot == imeRoot.get());
GeckoAppShell::NotifyIMEChange(EmptyString(), GeckoAppShell::NotifyIMEChange(EmptyString(),
int32_t(event.GetSelectionStart()), int32_t(event.GetSelectionStart()),
@ -2243,9 +2248,6 @@ nsWindow::NotifyIMEOfTextChange(const IMENotification& aIMENotification)
MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE, MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
"NotifyIMEOfTextChange() is called with invaild notification"); "NotifyIMEOfTextChange() is called with invaild notification");
if (mIMEMaskTextUpdate)
return NS_OK;
ALOGIME("IME: NotifyIMEOfTextChange: s=%d, oe=%d, ne=%d", ALOGIME("IME: NotifyIMEOfTextChange: s=%d, oe=%d, ne=%d",
aIMENotification.mTextChangeData.mStartOffset, aIMENotification.mTextChangeData.mStartOffset,
aIMENotification.mTextChangeData.mOldEndOffset, aIMENotification.mTextChangeData.mOldEndOffset,

View File

@ -217,7 +217,7 @@ protected:
nsCOMPtr<nsIIdleServiceInternal> mIdleService; nsCOMPtr<nsIIdleServiceInternal> mIdleService;
bool mIMEMaskSelectionUpdate, mIMEMaskTextUpdate; bool mIMEMaskSelectionUpdate;
int32_t mIMEMaskEventsCount; // Mask events when > 0 int32_t mIMEMaskEventsCount; // Mask events when > 0
nsRefPtr<mozilla::TextRangeArray> mIMERanges; nsRefPtr<mozilla::TextRangeArray> mIMERanges;
bool mIMEUpdatingContext; bool mIMEUpdatingContext;