mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 862442 - Use a content script to listen for input and change events; r=yoric
This commit is contained in:
parent
3d11af6229
commit
268d8cd42f
@ -12,8 +12,12 @@ function debug(msg) {
|
|||||||
*/
|
*/
|
||||||
let EventListener = {
|
let EventListener = {
|
||||||
|
|
||||||
|
DOM_EVENTS: [
|
||||||
|
"pageshow", "change", "input"
|
||||||
|
],
|
||||||
|
|
||||||
init: function () {
|
init: function () {
|
||||||
addEventListener("pageshow", this, true);
|
this.DOM_EVENTS.forEach(e => addEventListener(e, this, true));
|
||||||
},
|
},
|
||||||
|
|
||||||
handleEvent: function (event) {
|
handleEvent: function (event) {
|
||||||
@ -22,6 +26,10 @@ let EventListener = {
|
|||||||
if (event.persisted)
|
if (event.persisted)
|
||||||
sendAsyncMessage("SessionStore:pageshow");
|
sendAsyncMessage("SessionStore:pageshow");
|
||||||
break;
|
break;
|
||||||
|
case "input":
|
||||||
|
case "change":
|
||||||
|
sendAsyncMessage("SessionStore:input");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
debug("received unknown event '" + event.type + "'");
|
debug("received unknown event '" + event.type + "'");
|
||||||
break;
|
break;
|
||||||
|
@ -62,6 +62,18 @@ const CAPABILITIES = [
|
|||||||
"DNSPrefetch", "Auth", "WindowControl"
|
"DNSPrefetch", "Auth", "WindowControl"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const MESSAGES = [
|
||||||
|
// The content script tells us that its form data (or that of one of its
|
||||||
|
// subframes) might have changed. This can be the contents or values of
|
||||||
|
// standard form fields or of ContentEditables.
|
||||||
|
"SessionStore:input",
|
||||||
|
|
||||||
|
// The content script has received a pageshow event. This happens when a
|
||||||
|
// page is loaded from bfcache without any network activity, i.e. when
|
||||||
|
// clicking the back or forward button.
|
||||||
|
"SessionStore:pageshow"
|
||||||
|
];
|
||||||
|
|
||||||
// These are tab events that we listen to.
|
// These are tab events that we listen to.
|
||||||
const TAB_EVENTS = [
|
const TAB_EVENTS = [
|
||||||
"TabOpen", "TabClose", "TabSelect", "TabShow", "TabHide", "TabPinned",
|
"TabOpen", "TabClose", "TabSelect", "TabShow", "TabHide", "TabPinned",
|
||||||
@ -621,6 +633,9 @@ let SessionStoreInternal = {
|
|||||||
case "SessionStore:pageshow":
|
case "SessionStore:pageshow":
|
||||||
this.onTabLoad(win, browser);
|
this.onTabLoad(win, browser);
|
||||||
break;
|
break;
|
||||||
|
case "SessionStore:input":
|
||||||
|
this.onTabInput(win, browser);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
debug("received unknown message '" + aMessage.name + "'");
|
debug("received unknown message '" + aMessage.name + "'");
|
||||||
break;
|
break;
|
||||||
@ -646,11 +661,6 @@ let SessionStoreInternal = {
|
|||||||
this.restoreDocument(win, browser, aEvent);
|
this.restoreDocument(win, browser, aEvent);
|
||||||
this.onTabLoad(win, browser);
|
this.onTabLoad(win, browser);
|
||||||
break;
|
break;
|
||||||
case "change":
|
|
||||||
case "input":
|
|
||||||
case "DOMAutoComplete":
|
|
||||||
this.onTabInput(win, aEvent.currentTarget);
|
|
||||||
break;
|
|
||||||
case "TabOpen":
|
case "TabOpen":
|
||||||
this.onTabAdd(win, aEvent.originalTarget);
|
this.onTabAdd(win, aEvent.originalTarget);
|
||||||
break;
|
break;
|
||||||
@ -1212,11 +1222,9 @@ let SessionStoreInternal = {
|
|||||||
onTabAdd: function ssi_onTabAdd(aWindow, aTab, aNoNotification) {
|
onTabAdd: function ssi_onTabAdd(aWindow, aTab, aNoNotification) {
|
||||||
let browser = aTab.linkedBrowser;
|
let browser = aTab.linkedBrowser;
|
||||||
browser.addEventListener("load", this, true);
|
browser.addEventListener("load", this, true);
|
||||||
browser.addEventListener("change", this, true);
|
|
||||||
browser.addEventListener("input", this, true);
|
|
||||||
browser.addEventListener("DOMAutoComplete", this, true);
|
|
||||||
|
|
||||||
browser.messageManager.addMessageListener("SessionStore:pageshow", this);
|
let mm = browser.messageManager;
|
||||||
|
MESSAGES.forEach(msg => mm.addMessageListener(msg, this));
|
||||||
|
|
||||||
if (!aNoNotification) {
|
if (!aNoNotification) {
|
||||||
this.saveStateDelayed(aWindow);
|
this.saveStateDelayed(aWindow);
|
||||||
@ -1237,11 +1245,9 @@ let SessionStoreInternal = {
|
|||||||
onTabRemove: function ssi_onTabRemove(aWindow, aTab, aNoNotification) {
|
onTabRemove: function ssi_onTabRemove(aWindow, aTab, aNoNotification) {
|
||||||
let browser = aTab.linkedBrowser;
|
let browser = aTab.linkedBrowser;
|
||||||
browser.removeEventListener("load", this, true);
|
browser.removeEventListener("load", this, true);
|
||||||
browser.removeEventListener("change", this, true);
|
|
||||||
browser.removeEventListener("input", this, true);
|
|
||||||
browser.removeEventListener("DOMAutoComplete", this, true);
|
|
||||||
|
|
||||||
browser.messageManager.removeMessageListener("SessionStore:pageshow", this);
|
let mm = browser.messageManager;
|
||||||
|
MESSAGES.forEach(msg => mm.removeMessageListener(msg, this));
|
||||||
|
|
||||||
delete browser.__SS_data;
|
delete browser.__SS_data;
|
||||||
delete browser.__SS_tabStillLoading;
|
delete browser.__SS_tabStillLoading;
|
||||||
@ -2254,16 +2260,8 @@ let SessionStoreInternal = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// designMode is undefined e.g. for XUL documents (as about:config)
|
// designMode is undefined e.g. for XUL documents (as about:config)
|
||||||
if ((aContent.document.designMode || "") == "on" && aContent.document.body) {
|
if ((aContent.document.designMode || "") == "on" && aContent.document.body)
|
||||||
if (aData.innerHTML === undefined && !aFullData) {
|
|
||||||
// we get no "input" events from iframes - listen for keypress here
|
|
||||||
let _this = this;
|
|
||||||
aContent.addEventListener("keypress", function(aEvent) {
|
|
||||||
_this.saveStateDelayed(aWindow, 3000);
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
aData.innerHTML = aContent.document.body.innerHTML;
|
aData.innerHTML = aContent.document.body.innerHTML;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get scroll position from nsIDOMWindowUtils, since it allows avoiding a
|
// get scroll position from nsIDOMWindowUtils, since it allows avoiding a
|
||||||
|
@ -25,6 +25,8 @@ MOCHITEST_BROWSER_FILES = \
|
|||||||
browser_form_restore_events_sample.html \
|
browser_form_restore_events_sample.html \
|
||||||
browser_formdata_format.js \
|
browser_formdata_format.js \
|
||||||
browser_formdata_format_sample.html \
|
browser_formdata_format_sample.html \
|
||||||
|
browser_input.js \
|
||||||
|
browser_input_sample.html \
|
||||||
browser_pageshow.js \
|
browser_pageshow.js \
|
||||||
browser_248970_b_perwindowpb.js \
|
browser_248970_b_perwindowpb.js \
|
||||||
browser_248970_b_sample.html \
|
browser_248970_b_sample.html \
|
||||||
|
121
browser/components/sessionstore/test/browser_input.js
Normal file
121
browser/components/sessionstore/test/browser_input.js
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
const URL = "http://mochi.test:8888/browser/" +
|
||||||
|
"browser/components/sessionstore/test/browser_input_sample.html";
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
TestRunner.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test ensures that modifying form input fields on a web page marks the
|
||||||
|
* window as dirty and causes the corresponding form data of the tab that
|
||||||
|
* changed to be re-collected.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function runTests() {
|
||||||
|
// Create a dummy window that is regarded as active. We need to do this
|
||||||
|
// because we always collect data for tabs of active windows no matter if
|
||||||
|
// the window is dirty or not.
|
||||||
|
let win = OpenBrowserWindow();
|
||||||
|
yield waitForLoad(win);
|
||||||
|
|
||||||
|
// Create a tab with some form fields.
|
||||||
|
let tab = gBrowser.selectedTab = gBrowser.addTab(URL);
|
||||||
|
let browser = gBrowser.selectedBrowser;
|
||||||
|
yield waitForLoad(browser);
|
||||||
|
|
||||||
|
// All windows currently marked as dirty will be written to disk
|
||||||
|
// and thus marked clean afterwards.
|
||||||
|
yield forceWriteState();
|
||||||
|
|
||||||
|
// Modify the checkbox field's state.
|
||||||
|
let chk = browser.contentDocument.getElementById("chk");
|
||||||
|
EventUtils.sendMouseEvent({type: "click"}, chk);
|
||||||
|
yield waitForInput();
|
||||||
|
|
||||||
|
// Check that we'll save the form data state correctly.
|
||||||
|
let state = JSON.parse(ss.getBrowserState());
|
||||||
|
let {formdata} = state.windows[0].tabs[1].entries[0];
|
||||||
|
is(formdata.id.chk, true, "chk's value is correct");
|
||||||
|
|
||||||
|
// Clear dirty state of all windows again.
|
||||||
|
yield forceWriteState();
|
||||||
|
|
||||||
|
// Modify the text input field's state.
|
||||||
|
browser.contentDocument.getElementById("txt").focus();
|
||||||
|
EventUtils.synthesizeKey("m", {});
|
||||||
|
yield waitForInput();
|
||||||
|
|
||||||
|
// Check that we'll save the form data state correctly.
|
||||||
|
let state = JSON.parse(ss.getBrowserState());
|
||||||
|
let {formdata} = state.windows[0].tabs[1].entries[0];
|
||||||
|
is(formdata.id.chk, true, "chk's value is correct");
|
||||||
|
is(formdata.id.txt, "m", "txt's value is correct");
|
||||||
|
|
||||||
|
// Clear dirty state of all windows again.
|
||||||
|
yield forceWriteState();
|
||||||
|
|
||||||
|
// Modify the state of the checkbox contained in the iframe.
|
||||||
|
let cdoc = browser.contentDocument.getElementById("ifr").contentDocument;
|
||||||
|
EventUtils.sendMouseEvent({type: "click"}, cdoc.getElementById("chk"));
|
||||||
|
yield waitForInput();
|
||||||
|
|
||||||
|
// Modify the state of text field contained in the iframe.
|
||||||
|
cdoc.getElementById("txt").focus();
|
||||||
|
EventUtils.synthesizeKey("m", {});
|
||||||
|
yield waitForInput();
|
||||||
|
|
||||||
|
// Check that we'll save the iframe's form data correctly.
|
||||||
|
let state = JSON.parse(ss.getBrowserState());
|
||||||
|
let {formdata} = state.windows[0].tabs[1].entries[0].children[0];
|
||||||
|
is(formdata.id.chk, true, "iframe chk's value is correct");
|
||||||
|
is(formdata.id.txt, "m", "iframe txt's value is correct");
|
||||||
|
|
||||||
|
// Clear dirty state of all windows again.
|
||||||
|
yield forceWriteState();
|
||||||
|
|
||||||
|
// Modify the content editable's state.
|
||||||
|
browser.contentDocument.getElementById("ced").focus();
|
||||||
|
EventUtils.synthesizeKey("m", {});
|
||||||
|
yield waitForInput();
|
||||||
|
|
||||||
|
// Check the we'll correctly save the content editable's state.
|
||||||
|
let state = JSON.parse(ss.getBrowserState());
|
||||||
|
let {innerHTML} = state.windows[0].tabs[1].entries[0].children[1];
|
||||||
|
is(innerHTML, "m", "content editable's value is correct");
|
||||||
|
|
||||||
|
// Clean up after ourselves.
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
win.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function forceWriteState() {
|
||||||
|
const PREF = "browser.sessionstore.interval";
|
||||||
|
const TOPIC = "sessionstore-state-write";
|
||||||
|
|
||||||
|
Services.obs.addObserver(function observe() {
|
||||||
|
Services.obs.removeObserver(observe, TOPIC);
|
||||||
|
Services.prefs.clearUserPref(PREF);
|
||||||
|
executeSoon(next);
|
||||||
|
}, TOPIC, false);
|
||||||
|
|
||||||
|
Services.prefs.setIntPref(PREF, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForLoad(aElement) {
|
||||||
|
aElement.addEventListener("load", function onLoad() {
|
||||||
|
aElement.removeEventListener("load", onLoad, true);
|
||||||
|
executeSoon(next);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForInput() {
|
||||||
|
let mm = gBrowser.selectedBrowser.messageManager;
|
||||||
|
|
||||||
|
mm.addMessageListener("SessionStore:input", function onPageShow() {
|
||||||
|
mm.removeMessageListener("SessionStore:input", onPageShow);
|
||||||
|
executeSoon(next);
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>sessionstore input test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<input id="chk" type="checkbox" /><input id="txt" />
|
||||||
|
<iframe id="ifr" src="data:text/html;charset=utf-8,<input id=chk type=checkbox /><input id=txt />"></iframe>
|
||||||
|
<iframe id="ced"></iframe>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
addEventListener("load", () => {
|
||||||
|
document.getElementById("ced").contentDocument.designMode = "on";
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user