Bug 592802 - Send change event when <input type='file'> is changed even with display:none. r=sicking a2.0=blocking

This commit is contained in:
Mounir Lamouri 2010-09-14 20:35:37 +02:00
parent f897233b6a
commit 77e3387289
3 changed files with 237 additions and 25 deletions

View File

@ -307,10 +307,8 @@ AsyncClickHandler::Run()
if (!filePicker)
return NS_ERROR_FAILURE;
nsFileControlFrame* frame = static_cast<nsFileControlFrame*>(mInput->GetPrimaryFrame());
nsTextControlFrame* textFrame = nsnull;
if (frame)
textFrame = static_cast<nsTextControlFrame*>(frame->GetTextFrame());
nsFileControlFrame* frame =
static_cast<nsFileControlFrame*>(mInput->GetPrimaryFrame());
PRBool multi;
rv = mInput->GetMultiple(&multi);
@ -384,16 +382,13 @@ AsyncClickHandler::Run()
filePicker->SetDisplayDirectory(localFile);
}
// Tell our textframe to remember the currently focused value
if (textFrame)
textFrame->InitFocusedValue();
// Open dialog
PRInt16 mode;
rv = filePicker->Show(&mode);
NS_ENSURE_SUCCESS(rv, rv);
if (mode == nsIFilePicker::returnCancel)
if (mode == nsIFilePicker::returnCancel) {
return NS_OK;
}
// Collect new selected filenames
nsCOMArray<nsIDOMFile> newFiles;
@ -404,7 +399,9 @@ AsyncClickHandler::Run()
nsCOMPtr<nsISupports> tmp;
PRBool prefSaved = PR_FALSE;
while (NS_SUCCEEDED(iter->GetNext(getter_AddRefs(tmp)))) {
PRBool loop = PR_TRUE;
while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) {
iter->GetNext(getter_AddRefs(tmp));
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(tmp);
if (localFile) {
nsString unicodePath;
@ -444,21 +441,14 @@ AsyncClickHandler::Run()
// Set new selected files
if (newFiles.Count()) {
// Tell mTextFrame that this update of the value is a user initiated
// change. Otherwise it'll think that the value is being set by a script
// and not fire onchange when it should.
PRBool oldState;
if (textFrame) {
oldState = textFrame->GetFireChangeEventState();
textFrame->SetFireChangeEventState(PR_TRUE);
}
// The text control frame (if there is one) isn't going to send a change
// event because it will think this is done by a script.
// So, we can safely send one by ourself.
mInput->SetFiles(newFiles);
if (textFrame) {
textFrame->SetFireChangeEventState(oldState);
// May need to fire an onchange here
textFrame->CheckFireOnChange();
}
nsContentUtils::DispatchTrustedEvent(mInput->GetOwnerDoc(),
static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
NS_LITERAL_STRING("change"), PR_FALSE,
PR_FALSE);
}
return NS_OK;

View File

@ -223,6 +223,7 @@ _TEST_FILES = \
test_bug590363.html \
test_bug557628-1.html \
test_bug557628-2.html \
test_bug592802.html \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,221 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=592802
-->
<head>
<title>Test for Bug 592802</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.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=592802">Mozilla Bug 592802</a>
<p id="display"></p>
<div id="content">
<input id='a' type='file'>
<input id='a2' type='file'>
</div>
<button id='b' onclick="document.getElementById('a').click();">Show Filepicker</button>
<button id='b2' onclick="document.getElementById('a2').click();">Show Filepicker</button>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 592802 **/
SimpleTest.waitForExplicitFinish();
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cm = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
const Cu = Components.utils;
const Cr = Components.results;
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://mochikit/content/browser/toolkit/content/tests/browser/common/mockObjects.js", this);
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function simpleEnumerator(items)
{
this._items = items;
this._nextIndex = 0;
}
simpleEnumerator.prototype = {
QueryInterface: function(aIID)
{
if (Ci.nsISimpleEnumerator.equals(aIID) ||
Ci.nsISupports.equals(aIID)) {
return this;
}
throw Cr.NS_ERROR_NO_INTERFACE;
},
hasMoreElements: function()
{
return this._nextIndex < this._items.length;
},
getNext: function()
{
if (!this.hasMoreElements()) {
throw Cr.NS_ERROR_FAILURE;
}
return this._items[this._nextIndex++];
}
};
function MockFilePicker()
{
}
MockFilePicker.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker]),
// Constants
returnOK: 0, // the user hits OK (select a file)
returnCancel: 1, // the user cancel the file selection
returnReplace: 2, // the user replace the selection
// Properties
defaultExtension: "",
defaultString: "",
get displayDirectory() { return null; },
set displayDirectory(val) { },
get fileURL() { return null; },
filterIndex: 0,
get file() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var fileName = "592808_file";
var fileData = "file content";
var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties);
var testFile = dirSvc.get("ProfD", Components.interfaces.nsIFile);
testFile.append(fileName);
var outStream = Components.
classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
0666, 0);
outStream.write(fileData, fileData.length);
outStream.close();
return testFile;
},
get files() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var fileName = "592808_file";
var fileData = "file content";
var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties);
var testFile = dirSvc.get("ProfD", Components.interfaces.nsIFile);
testFile.append(fileName);
var outStream = Components.
classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
0666, 0);
outStream.write(fileData, fileData.length);
outStream.close();
return new simpleEnumerator([testFile]);
},
appendFilter: function(val) {},
appendFilters: function(val) {},
init: function() {},
show: function()
{
if (firstFilePickerShow) {
firstFilePickerShow = false;
return this.returnCancel;
} else {
return this.returnOK;
}
}
};
var mockFilePickerRegisterer =
new MockObjectRegisterer("@mozilla.org/filepicker;1",MockFilePicker);
mockFilePickerRegisterer.register();
var testData = [
/* visibility | display | multiple */
[ "", "", false ],
[ "hidden", "", false ],
[ "", "none", false ],
[ "", "", true ],
[ "hidden", "", true ],
[ "", "none", true ],
];
var testCounter = 0;
var testNb = testData.length;
var firstFilePickerShow = true;
function finished()
{
mockFilePickerRegisterer.unregister();
SimpleTest.finish();
}
SimpleTest.waitForFocus(function() {
// mockFilePicker will simulate a cancel for the first time the file picker will be shown.
var b2 = document.getElementById('b2');
b2.focus(); // Be sure the element is visible.
document.getElementById('b2').addEventListener("change", function(aEvent) {
aEvent.target.removeEventListener("change", arguments.callee, false);
ok(false, "When cancel is received, change should not fire");
}, false);
synthesizeMouse(b2, 2, 2, {});
// Now, we can launch tests when file picker isn't canceled.
var b = document.getElementById('b');
b.focus(); // Be sure the element is visible.
document.getElementById('a').addEventListener("change", function(aEvent) {
ok(true, "change event correctly sent");
testCounter++;
if (testCounter >= testNb) {
aEvent.target.removeEventListener("change", arguments.callee, false);
SimpleTest.executeSoon(finished);
} else {
var data = testData[testCounter];
var a = document.getElementById('a');
a.style.visibility = data[0];
a.style.display = data[1];
a.multiple = data[2];
SimpleTest.executeSoon(function() {
synthesizeMouse(b, 2, 2, {});
});
}
}, false);
synthesizeMouse(b, 2, 2, {});
});
</script>
</pre>
</body>
</html>