https://bugs.webkit.org/show_bug.cgi?id=80854
Patch by Dinu Jacob <dinu.jacob@nokia.com> on 2012-03-16
Reviewed by Simon Hausmann.
Source/WebKit2:
Crash resulted from attempting to create QFileDialog, a QtWidget based dialog from a
QGuiApplication. Replace QFileDialog with a QML implementable component.
Added a new property 'filePicker' to WebView experimental to set the QML component for
file upload triggered by an input file element.
Co-authored with Kasthuri Nallappasoundararajan <kasthuri.n-s@nokia.com>
* UIProcess/API/qt/qquickwebview.cpp:
(QQuickWebViewPrivate::QQuickWebViewPrivate):
(QQuickWebViewPrivate::chooseFiles):
(QQuickWebViewExperimental::filePicker):
(QQuickWebViewExperimental::setFilePicker):
* UIProcess/API/qt/qquickwebview_p.h:
* UIProcess/API/qt/qquickwebview_p_p.h:
(QQuickWebViewPrivate):
* UIProcess/API/qt/tests/qmltests/DesktopBehavior.pro:
* UIProcess/API/qt/tests/qmltests/DesktopBehavior/tst_singleFileUpload.qml: Added.
* UIProcess/API/qt/tests/qmltests/common/singlefileupload.html: Added.
* UIProcess/qt/QtDialogRunner.cpp:
(FilePickerContextObject):
(FilePickerContextObject::FilePickerContextObject):
(FilePickerContextObject::fileList):
(FilePickerContextObject::reject):
(FilePickerContextObject::accept):
(QtDialogRunner::initForFilePicker):
* UIProcess/qt/QtDialogRunner.h:
(QtDialogRunner):
(QtDialogRunner::filePaths):
(QtDialogRunner::onFileSelected):
Tools:
Added filePicker to WebView using experimental API.
* MiniBrowser/qt/MiniBrowser.qrc:
* MiniBrowser/qt/icons/folder.png: Added.
* MiniBrowser/qt/icons/titlebar.png: Added.
* MiniBrowser/qt/icons/up.png: Added.
* MiniBrowser/qt/qml/BrowserWindow.qml:
* MiniBrowser/qt/qml/FilePicker.qml: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@111012
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2012-03-16 Dinu Jacob <dinu.jacob@nokia.com>
+
+ [Qt][Wk2] Assertion Failure and crash on file upload
+ https://bugs.webkit.org/show_bug.cgi?id=80854
+
+ Reviewed by Simon Hausmann.
+
+ Crash resulted from attempting to create QFileDialog, a QtWidget based dialog from a
+ QGuiApplication. Replace QFileDialog with a QML implementable component.
+ Added a new property 'filePicker' to WebView experimental to set the QML component for
+ file upload triggered by an input file element.
+
+ Co-authored with Kasthuri Nallappasoundararajan <kasthuri.n-s@nokia.com>
+
+ * UIProcess/API/qt/qquickwebview.cpp:
+ (QQuickWebViewPrivate::QQuickWebViewPrivate):
+ (QQuickWebViewPrivate::chooseFiles):
+ (QQuickWebViewExperimental::filePicker):
+ (QQuickWebViewExperimental::setFilePicker):
+ * UIProcess/API/qt/qquickwebview_p.h:
+ * UIProcess/API/qt/qquickwebview_p_p.h:
+ (QQuickWebViewPrivate):
+ * UIProcess/API/qt/tests/qmltests/DesktopBehavior.pro:
+ * UIProcess/API/qt/tests/qmltests/DesktopBehavior/tst_singleFileUpload.qml: Added.
+ * UIProcess/API/qt/tests/qmltests/common/singlefileupload.html: Added.
+ * UIProcess/qt/QtDialogRunner.cpp:
+ (FilePickerContextObject):
+ (FilePickerContextObject::FilePickerContextObject):
+ (FilePickerContextObject::fileList):
+ (FilePickerContextObject::reject):
+ (FilePickerContextObject::accept):
+ (QtDialogRunner::initForFilePicker):
+ * UIProcess/qt/QtDialogRunner.h:
+ (QtDialogRunner):
+ (QtDialogRunner::filePaths):
+ (QtDialogRunner::onFileSelected):
+
2012-03-16 Dinu Jacob <dinu.jacob@nokia.com>
[Qt][WK2] Build failure when using --no-touch-events
#include <private/qquickflickable_p.h>
#include <JavaScriptCore/InitializeThreading.h>
#include <QDeclarativeEngine>
-#include <QFileDialog>
#include <QtQuick/QQuickCanvas>
#include <WebCore/IntPoint.h>
#include <WebCore/IntRect.h>
, certificateVerificationDialog(0)
, itemSelector(0)
, proxyAuthenticationDialog(0)
+ , filePicker(0)
, userDidOverrideContentWidth(false)
, userDidOverrideContentHeight(false)
, m_navigatorQtObjectEnabled(false)
void QQuickWebViewPrivate::chooseFiles(WKOpenPanelResultListenerRef listenerRef, const QStringList& selectedFileNames, QtWebPageUIClient::FileChooserType type)
{
-#ifndef QT_NO_FILEDIALOG
Q_Q(QQuickWebView);
- openPanelResultListener = listenerRef;
- // Qt does not support multiple files suggestion, so we get just the first suggestion.
- QString selectedFileName;
- if (!selectedFileNames.isEmpty())
- selectedFileName = selectedFileNames.at(0);
-
- Q_ASSERT(!fileDialog);
-
- QWindow* window = q->canvas();
- if (!window)
+ if (!filePicker || type == QtWebPageUIClient::MultipleFilesSelection)
return;
- fileDialog = new QFileDialog(0, QString(), selectedFileName);
- fileDialog->window()->winId(); // Ensure that the dialog has a window
- Q_ASSERT(fileDialog->window()->windowHandle());
- fileDialog->window()->windowHandle()->setTransientParent(window);
-
- fileDialog->open(q, SLOT(_q_onOpenPanelFilesSelected()));
-
- q->connect(fileDialog, SIGNAL(finished(int)), SLOT(_q_onOpenPanelFinished(int)));
-#endif
-}
+ QtDialogRunner dialogRunner;
+ if (!dialogRunner.initForFilePicker(filePicker, q, selectedFileNames))
+ return;
-void QQuickWebViewPrivate::_q_onOpenPanelFilesSelected()
-{
- const QStringList fileList = fileDialog->selectedFiles();
- Vector<RefPtr<APIObject> > wkFiles(fileList.size());
+ execDialogRunner(dialogRunner);
- for (unsigned i = 0; i < fileList.size(); ++i)
- wkFiles[i] = WebURL::create(QUrl::fromLocalFile(fileList.at(i)).toString());
+ if (dialogRunner.wasAccepted()) {
+ QStringList selectedPaths = dialogRunner.filePaths();
- WKOpenPanelResultListenerChooseFiles(openPanelResultListener, toAPI(ImmutableArray::adopt(wkFiles).leakRef()));
-}
+ Vector<RefPtr<APIObject> > wkFiles(selectedPaths.size());
+ for (unsigned i = 0; i < selectedPaths.size(); ++i)
+ wkFiles[i] = WebURL::create(QUrl::fromLocalFile(selectedPaths.at(i)).toString());
-void QQuickWebViewPrivate::_q_onOpenPanelFinished(int result)
-{
- if (result == QDialog::Rejected)
- WKOpenPanelResultListenerCancel(openPanelResultListener);
+ WKOpenPanelResultListenerChooseFiles(listenerRef, toAPI(ImmutableArray::adopt(wkFiles).leakRef()));
+ } else
+ WKOpenPanelResultListenerCancel(listenerRef);
- fileDialog->deleteLater();
- fileDialog = 0;
}
void QQuickWebViewPrivate::setViewInAttachedProperties(QObject* object)
emit itemSelectorChanged();
}
+QDeclarativeComponent* QQuickWebViewExperimental::filePicker() const
+{
+ Q_D(const QQuickWebView);
+ return d->filePicker;
+}
+
+void QQuickWebViewExperimental::setFilePicker(QDeclarativeComponent* filePicker)
+{
+ Q_D(QQuickWebView);
+ if (d->filePicker == filePicker)
+ return;
+ d->filePicker = filePicker;
+ emit filePickerChanged();
+}
+
QQuickUrlSchemeDelegate* QQuickWebViewExperimental::schemeDelegates_At(QDeclarativeListProperty<QQuickUrlSchemeDelegate>* property, int index)
{
const QObjectList children = property->object->children();
Q_PRIVATE_SLOT(d_func(), void _q_commitPositionChange(const QPointF&));
Q_PRIVATE_SLOT(d_func(), void _q_commitScaleChange());
- Q_PRIVATE_SLOT(d_func(), void _q_onOpenPanelFilesSelected());
- Q_PRIVATE_SLOT(d_func(), void _q_onOpenPanelFinished(int result));
Q_PRIVATE_SLOT(d_func(), void _q_onVisibleChanged());
Q_PRIVATE_SLOT(d_func(), void _q_onUrlChanged());
Q_PRIVATE_SLOT(d_func(), void _q_onReceivedResponseFromDownload(QWebDownloadItem*));
Q_PROPERTY(QDeclarativeComponent* proxyAuthenticationDialog READ proxyAuthenticationDialog WRITE setProxyAuthenticationDialog NOTIFY proxyAuthenticationDialogChanged)
Q_PROPERTY(QDeclarativeComponent* certificateVerificationDialog READ certificateVerificationDialog WRITE setCertificateVerificationDialog NOTIFY certificateVerificationDialogChanged)
Q_PROPERTY(QDeclarativeComponent* itemSelector READ itemSelector WRITE setItemSelector NOTIFY itemSelectorChanged)
+ Q_PROPERTY(QDeclarativeComponent* filePicker READ filePicker WRITE setFilePicker NOTIFY filePickerChanged)
Q_PROPERTY(QWebPreferences* preferences READ preferences CONSTANT FINAL)
Q_PROPERTY(QWebViewportInfo* viewportInfo READ viewportInfo CONSTANT FINAL)
Q_PROPERTY(QDeclarativeListProperty<QQuickUrlSchemeDelegate> urlSchemeDelegates READ schemeDelegates)
void setItemSelector(QDeclarativeComponent*);
QDeclarativeComponent* proxyAuthenticationDialog() const;
void setProxyAuthenticationDialog(QDeclarativeComponent*);
+ QDeclarativeComponent* filePicker() const;
+ void setFilePicker(QDeclarativeComponent*);
QWebViewportInfo* viewportInfo();
void authenticationDialogChanged();
void certificateVerificationDialogChanged();
void itemSelectorChanged();
+ void filePickerChanged();
void downloadRequested(QWebDownloadItem* downloadItem);
void permissionRequested(QWebPermissionRequest* permission);
void messageReceived(const QVariantMap& message);
QT_BEGIN_NAMESPACE
class QDeclarativeComponent;
-class QFileDialog;
QT_END_NAMESPACE
class QQuickWebViewPrivate {
virtual void _q_commitScaleChange() { }
void _q_commitPositionChange(const QPointF&);
- void _q_onOpenPanelFilesSelected();
- void _q_onOpenPanelFinished(int result);
void _q_onVisibleChanged();
void _q_onUrlChanged();
void _q_onReceivedResponseFromDownload(QWebDownloadItem*);
QDeclarativeComponent* certificateVerificationDialog;
QDeclarativeComponent* itemSelector;
QDeclarativeComponent* proxyAuthenticationDialog;
+ QDeclarativeComponent* filePicker;
WebCore::ViewportArguments viewportArguments;
- QFileDialog* fileDialog;
- WKOpenPanelResultListenerRef openPanelResultListener;
bool userDidOverrideContentWidth;
bool userDidOverrideContentHeight;
DesktopBehavior/tst_linkHovered.qml \
DesktopBehavior/tst_loadHtml.qml \
DesktopBehavior/tst_messaging.qml \
- DesktopBehavior/tst_navigationRequested.qml
+ DesktopBehavior/tst_navigationRequested.qml \
+ DesktopBehavior/tst_singlefileupload.qml
--- /dev/null
+import QtQuick 2.0
+import QtTest 1.0
+import QtWebKit 3.0
+import QtWebKit.experimental 1.0
+import "../common"
+
+// FIXME: Added to Desktop tests because we want to have mouseClick() to open the <input> tag. We can move it back
+// when TestCase starts supporting touch events, see https://bugreports.qt.nokia.com/browse/QTBUG-23083.
+TestWebView {
+ id: webView
+
+ width: 400
+ height: 400
+
+ property bool selectFile
+
+ experimental.filePicker: Item {
+ Timer {
+ running: true
+ interval: 1
+ onTriggered: {
+ if (selectFile)
+ model.accept("acceptedfilename");
+ else
+ model.reject();
+ }
+ }
+ }
+
+ SignalSpy {
+ id: titleSpy
+ target: webView
+ signalName: "titleChanged"
+ }
+
+ TestCase {
+ id: test
+ name: "WebViewSingleFilePicker"
+ when: windowShown
+
+ function init() {
+ webView.url = Qt.resolvedUrl("../common/singlefileupload.html")
+ verify(webView.waitForLoadSucceeded())
+ titleSpy.clear()
+ }
+
+ function openItemSelector() {
+ mouseClick(webView, 15, 15, Qt.LeftButton)
+ }
+
+ function test_accept() {
+ webView.selectFile = true;
+ openItemSelector()
+ titleSpy.wait()
+ compare(webView.title, "acceptedfilename")
+ }
+
+ function test_reject() {
+ var oldTitle = webView.title
+ webView.selectFile = false;
+ openItemSelector()
+ compare(webView.title, oldTitle)
+ }
+ }
+}
--- /dev/null
+<html>
+<head>
+<meta name="viewport" initial-scale=1">
+<title>No file selected</title>
+<script>
+function updateTitle()
+{
+ var inp = document.getElementById("upfile");
+ var allfiles = new String("");
+ var name = new String("");
+ for (var i = 0; i < inp.files.length; ++i)
+ {
+ name = inp.files.item(i).name;
+ if (allfiles.length == 0)
+ allfiles = name;
+ else
+ allfiles = allfiles + "," + name;
+ }
+ document.title = allfiles;
+}
+</script>
+
+<body>
+<input type="file" name="file" id="upfile" onchange="updateTitle()"/>
+</body>
+</html>
QString m_hostname;
};
+class FilePickerContextObject : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(QStringList fileList READ fileList CONSTANT)
+
+public:
+ FilePickerContextObject(const QStringList& selectedFiles)
+ : QObject()
+ , m_fileList(selectedFiles)
+ {
+ }
+
+ QStringList fileList() const { return m_fileList; }
+
+public slots:
+ void reject() { emit rejected();}
+ void accept(const QVariant& path) { emit fileSelected(path.toStringList()); }
+
+signals:
+ void rejected();
+ void fileSelected(const QStringList&);
+
+private:
+ QStringList m_fileList;
+};
+
bool QtDialogRunner::initForAlert(QDeclarativeComponent* component, QQuickItem* dialogParent, const QString& message)
{
DialogContextObject* contextObject = new DialogContextObject(message);
return true;
}
+bool QtDialogRunner::initForFilePicker(QDeclarativeComponent* component, QQuickItem* dialogParent, const QStringList& selectedFiles)
+{
+ FilePickerContextObject* contextObject = new FilePickerContextObject(selectedFiles);
+ if (!createDialog(component, dialogParent, contextObject))
+ return false;
+
+ connect(contextObject, SIGNAL(fileSelected(QStringList)), SLOT(onFileSelected(QStringList)));
+ connect(contextObject, SIGNAL(fileSelected(QStringList)), SLOT(quit()));
+ connect(contextObject, SIGNAL(rejected()), SLOT(quit()));
+
+ return true;
+}
+
bool QtDialogRunner::createDialog(QDeclarativeComponent* component, QQuickItem* dialogParent, QObject* contextObject)
{
QDeclarativeContext* baseContext = component->creationContext();
#define QtDialogRunner_h
#include <QtCore/QEventLoop>
+#include <QtCore/QStringList>
#include <wtf/OwnPtr.h>
class QDeclarativeComponent;
bool initForAuthentication(QDeclarativeComponent*, QQuickItem* dialogParent, const QString& hostname, const QString& realm, const QString& prefilledUsername);
bool initForCertificateVerification(QDeclarativeComponent*, QQuickItem*, const QString& hostname);
bool initForProxyAuthentication(QDeclarativeComponent*, QQuickItem*, const QString& hostname, uint16_t port, const QString& prefilledUsername);
+ bool initForFilePicker(QDeclarativeComponent*, QQuickItem*, const QStringList& selectedFiles);
QQuickItem* dialog() const { return m_dialog.get(); }
QString username() const { return m_username; }
QString password() const { return m_password; }
+ QStringList filePaths() const { return m_filepaths; }
+
public slots:
void onAccepted(const QString& result = QString())
{
m_password = password;
}
+ void onFileSelected(const QStringList& filePaths)
+ {
+ m_wasAccepted = true;
+ m_filepaths = filePaths;
+ }
+
private:
bool createDialog(QDeclarativeComponent*, QQuickItem* dialogParent, QObject* contextObject);
QString m_username;
QString m_password;
+ QStringList m_filepaths;
};
#endif // QtDialogRunner_h
+2012-03-16 Dinu Jacob <dinu.jacob@nokia.com>
+
+ [Qt][Wk2] Assertion Failure and crash on file upload
+ https://bugs.webkit.org/show_bug.cgi?id=80854
+
+ Reviewed by Simon Hausmann.
+
+ Added filePicker to WebView using experimental API.
+
+ * MiniBrowser/qt/MiniBrowser.qrc:
+ * MiniBrowser/qt/icons/folder.png: Added.
+ * MiniBrowser/qt/icons/titlebar.png: Added.
+ * MiniBrowser/qt/icons/up.png: Added.
+ * MiniBrowser/qt/qml/BrowserWindow.qml:
+ * MiniBrowser/qt/qml/FilePicker.qml: Added.
+
2012-03-16 Mahesh Kulkarni <mahesh.kulkarni@nokia.com>
Updating email for committer.py script.
<RCC>
<qresource prefix="/">
+ <file>icons/favicon.png</file>
+ <file>icons/folder.png</file>
<file>icons/info.png</file>
<file>icons/next.png</file>
<file>icons/plus.png</file>
<file>icons/previous.png</file>
<file>icons/refresh.png</file>
<file>icons/stop.png</file>
+ <file>icons/titlebar.png</file>
<file>icons/touch.png</file>
<file>icons/touchpoint.png</file>
+ <file>icons/up.png</file>
<file>qml/AlertDialog.qml</file>
<file>qml/AuthenticationDialog.qml</file>
<file>qml/BrowserWindow.qml</file>
<file>qml/Dialog.qml</file>
<file>qml/DialogButton.qml</file>
<file>qml/DialogLineInput.qml</file>
+ <file>qml/FilePicker.qml</file>
<file>qml/ItemSelector.qml</file>
<file>qml/MockTouchPoint.qml</file>
<file>qml/PromptDialog.qml</file>
<file>qml/ScrollIndicator.qml</file>
<file>qml/ViewportInfoItem.qml</file>
<file>useragentlist.txt</file>
- <file>icons/favicon.png</file>
</qresource>
</RCC>
experimental.promptDialog: PromptDialog { }
experimental.authenticationDialog: AuthenticationDialog { }
experimental.proxyAuthenticationDialog: ProxyAuthenticationDialog { }
+ experimental.filePicker: FilePicker { }
ScrollIndicator {
flickableItem: webView.experimental.flickable
--- /dev/null
+/*
+ * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+import QtQuick 2.0
+import Qt.labs.folderlistmodel 1.0
+
+Rectangle {
+ id: filePicker
+
+ property QtObject fileModel: model
+ property alias folder: folders.folder
+
+ color: "white"
+ width: 400
+ height: 500
+
+ smooth: true
+ radius: 5
+ anchors.centerIn: parent
+
+ border {
+ width: 1
+ color: "#bfbfbf"
+ }
+
+ BorderImage {
+ source: "../icons/titlebar.png";
+ width: parent.width;
+ height: 50
+ y: -7
+ id: titleBar
+
+ anchors {
+ top: parent.top
+ bottom: folderListView.top
+ }
+ Rectangle {
+ id: upButton
+ width: 48
+ height: titleBar.height - 7
+ color: "transparent"
+ Image { anchors.centerIn: parent; source: "../icons/up.png" }
+ MouseArea { id: upRegion; anchors.centerIn: parent
+ width: 48
+ height: 48
+ onClicked: if (folders.parentFolder != "") up()
+ }
+ }
+
+ Rectangle {
+ color: "gray"
+ x: 48
+ width: 1
+ height: 44
+ }
+
+ Text {
+ anchors {
+ left: upButton.right
+ right: parent.right
+ leftMargin: 4
+ rightMargin: 4
+ }
+
+ height: parent.height
+ text: folders.folder
+ color: "white"
+ elide: Text.ElideLeft;
+ horizontalAlignment: Text.AlignLeft;
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: 24
+ }
+ }
+
+ ListView {
+ id: folderListView
+
+ width: parent.width
+ height: 400
+ anchors.centerIn: parent
+ spacing: 2
+ clip: true
+
+ FolderListModel {
+ id: folders
+ }
+
+ Component {
+ id: fileDelegate
+
+ Rectangle {
+ function selected() {
+ if (folders.isFolder(index))
+ openFolder(filePath);
+ else
+ fileModel.accept(filePath);
+ }
+
+ height: 50
+ width: parent.width
+ color: folders.isFolder(index) ? "lightgray": "darkgray"
+
+ Item {
+ width: 48;
+ height: 48
+ Image {
+ source: "../icons/folder.png"
+ anchors.centerIn: parent
+ visible: folders.isFolder(index)
+ }
+ }
+
+ Text {
+ anchors.centerIn: parent
+ anchors.leftMargin: 50
+ text: fileName
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: selected();
+ }
+ }
+ }
+ model: folders
+ delegate: fileDelegate
+ }
+
+ Rectangle {
+ id: button
+
+ height: 50
+
+ border {
+ width: 1
+ color: "#bfbfbf"
+ }
+
+ anchors {
+ bottom: parent.bottom
+ top: folderListView.bottom
+ left: parent.left
+ right: parent.right
+ }
+
+ DialogButton {
+ id: cancel
+ text: "Cancel"
+ anchors {
+ horizontalCenter: parent.horizontalCenter;
+ verticalCenter: parent.verticalCenter
+ }
+
+ onClicked: fileModel.reject()
+ }
+ }
+
+ function openFolder(path) {
+ folders.folder = path;
+ }
+
+ function up() {
+ folders.folder = folders.parentFolder;
+ }
+}