Merge lp:~mardy/ubuntu-system-settings-online-accounts/empty-page-1594944 into lp:ubuntu-system-settings-online-accounts

Proposed by Alberto Mardegan
Status: Merged
Approved by: Alberto Mardegan
Approved revision: 372
Merged at revision: 391
Proposed branch: lp:~mardy/ubuntu-system-settings-online-accounts/empty-page-1594944
Merge into: lp:ubuntu-system-settings-online-accounts
Diff against target: 235 lines (+44/-45)
3 files modified
online-accounts-ui/provider-request.cpp (+29/-26)
online-accounts-ui/qml/ProviderRequest.qml (+12/-17)
tests/online-accounts-ui/tst_provider_request.cpp (+3/-2)
To merge this branch: bzr merge lp:~mardy/ubuntu-system-settings-online-accounts/empty-page-1594944
Reviewer Review Type Date Requested Status
dobey (community) Needs Fixing
PS Jenkins bot continuous-integration Pending
Review via email: mp+298123@code.launchpad.net

Commit message

Properly handle early termination of account access requests

When a degenerate request (coming from an unconfined process with empty application ID) was received, the allowed() signal was emitted from the Component.onCompleted handler.
However, given that this all was happening inside the QQuickView::setSource() method, the signal was effectively emitted before the C++ code had a chance to setup the signal connections, with the result that such a signal was lost.

To solve this, we export a "request" object to QML, whose methods replace the old signals.

Description of the change

Properly handle early termination of account access requests

When a degenerate request (coming from an unconfined process with empty application ID) was received, the allowed() signal was emitted from the Component.onCompleted handler.
However, given that this all was happening inside the QQuickView::setSource() method, the signal was effectively emitted before the C++ code had a chance to setup the signal connections, with the result that such a signal was lost.

To solve this, we export a "request" object to QML, whose methods replace the old signals.

To post a comment you must log in.
Revision history for this message
dobey (dobey) wrote :

I've build this in the silo where I have the ubuntuone-credentials changes, and a blank window is still opened, so this doesn't seem to resolve the bug.

Also, I was trying to find where this file exists on disk, so I could edit in place instead of building a silo, given it's a trivial change, however I do not see this file being installed to the system at all. Is it only included as a resource in Qt?

review: Needs Fixing
368. By Alberto Mardegan

Expose request to QML

369. By Alberto Mardegan

Avoid using context properties

370. By Alberto Mardegan

Don't connect to inexistent signal

371. By Alberto Mardegan

revert

372. By Alberto Mardegan

Update tst_provider_request

Revision history for this message
Alberto Mardegan (mardy) wrote :

Update.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'online-accounts-ui/provider-request.cpp'
--- online-accounts-ui/provider-request.cpp 2015-11-18 08:27:12 +0000
+++ online-accounts-ui/provider-request.cpp 2016-06-23 13:59:19 +0000
@@ -41,6 +41,8 @@
41{41{
42 Q_OBJECT42 Q_OBJECT
43 Q_DECLARE_PUBLIC(ProviderRequest)43 Q_DECLARE_PUBLIC(ProviderRequest)
44 Q_PROPERTY(QVariantMap application READ applicationInfo CONSTANT)
45 Q_PROPERTY(QVariantMap provider READ providerInfo CONSTANT)
4446
45public:47public:
46 ProviderRequestPrivate(ProviderRequest *request);48 ProviderRequestPrivate(ProviderRequest *request);
@@ -48,18 +50,21 @@
4850
49 void start();51 void start();
5052
53 QVariantMap applicationInfo() const { return m_applicationInfo; }
54 QVariantMap providerInfo() const { return m_providerInfo; }
55
56public Q_SLOTS:
57 void deny();
58 void allow(int accountId);
59
51private Q_SLOTS:60private Q_SLOTS:
52 void onWindowVisibleChanged(bool visible);61 void onWindowVisibleChanged(bool visible);
53 void onDenied();
54 void onAllowed(int accountId);
55
56private:
57 QVariantMap providerInfo(const QString &providerId) const;
5862
59private:63private:
60 mutable ProviderRequest *q_ptr;64 mutable ProviderRequest *q_ptr;
61 QQuickView *m_view;65 QQuickView *m_view;
62 QVariantMap m_applicationInfo;66 QVariantMap m_applicationInfo;
67 QVariantMap m_providerInfo;
63};68};
6469
65} // namespace70} // namespace
@@ -112,7 +117,7 @@
112 } else {117 } else {
113 providerId = q->parameters().value(OAU_KEY_PROVIDER).toString();118 providerId = q->parameters().value(OAU_KEY_PROVIDER).toString();
114 }119 }
115 QVariantMap providerInfo = appManager->providerInfo(providerId);120 m_providerInfo = appManager->providerInfo(providerId);
116121
117 m_view = new QQuickView;122 m_view = new QQuickView;
118 QObject::connect(m_view, SIGNAL(visibleChanged(bool)),123 QObject::connect(m_view, SIGNAL(visibleChanged(bool)),
@@ -126,7 +131,7 @@
126 * <package-dir>/lib/<DEB_HOST_MULTIARCH>131 * <package-dir>/lib/<DEB_HOST_MULTIARCH>
127 * to the QML import path.132 * to the QML import path.
128 */133 */
129 QString packageDir = providerInfo.value("package-dir").toString();134 QString packageDir = m_providerInfo.value("package-dir").toString();
130 if (!packageDir.isEmpty()) {135 if (!packageDir.isEmpty()) {
131 engine->addImportPath(packageDir + "/lib");136 engine->addImportPath(packageDir + "/lib");
132#ifdef DEB_HOST_MULTIARCH137#ifdef DEB_HOST_MULTIARCH
@@ -142,22 +147,15 @@
142 QUrl::fromLocalFile(QStandardPaths::writableLocation(147 QUrl::fromLocalFile(QStandardPaths::writableLocation(
143 QStandardPaths::GenericDataLocation) +148 QStandardPaths::GenericDataLocation) +
144 "/accounts/qml-plugins/"));149 "/accounts/qml-plugins/"));
145 context->setContextProperty("provider", providerInfo);150 context->setContextProperty("request", this);
146 context->setContextProperty("application", m_applicationInfo);
147 context->setContextProperty("mainWindow", m_view);151 context->setContextProperty("mainWindow", m_view);
148152
149 m_view->setSource(QUrl(QStringLiteral("qrc:/qml/ProviderRequest.qml")));153 m_view->setSource(QUrl(QStringLiteral("qrc:/qml/ProviderRequest.qml")));
150 QQuickItem *root = m_view->rootObject();154 /* It could be that allow() or deny() have been already called; don't show
151 QObject::connect(root, SIGNAL(denied()),155 * the window in that case. */
152 this, SLOT(onDenied()));156 if (q->isInProgress()) {
153 QObject::connect(root, SIGNAL(allowed(int)),157 q->setWindow(m_view);
154 this, SLOT(onAllowed(int)));
155 if (root->property("wasDenied").toBool()) {
156 DEBUG() << "Was denied";
157 q->setResult(QVariantMap());
158 return;
159 }158 }
160 q->setWindow(m_view);
161}159}
162160
163void ProviderRequestPrivate::onWindowVisibleChanged(bool visible)161void ProviderRequestPrivate::onWindowVisibleChanged(bool visible)
@@ -171,22 +169,27 @@
171 }169 }
172}170}
173171
174void ProviderRequestPrivate::onDenied()172void ProviderRequestPrivate::deny()
175{173{
174 Q_Q(ProviderRequest);
176 DEBUG();175 DEBUG();
177 /* Just close the window; this will deliver the empty result to the176 if (m_view->isVisible()) {
178 * client */177 /* Just close the window; this will deliver the empty result to the
179 m_view->close();178 * client */
179 m_view->close();
180 } else {
181 q->setResult(QVariantMap());
182 }
180}183}
181184
182void ProviderRequestPrivate::onAllowed(int accountId)185void ProviderRequestPrivate::allow(int accountId)
183{186{
184 Q_Q(ProviderRequest);187 Q_Q(ProviderRequest);
185 DEBUG() << "Access allowed for account:" << accountId;188 DEBUG() << "Access allowed for account:" << accountId;
186 /* If the request came from an app, add a small delay so that we could189 /* If the request came from an app, add a small delay so that we could
187 * serve an authentication request coming right after this one. */190 * serve an authentication request coming right after this one. */
188 if (m_applicationInfo.value("id").toString() !=191 if (m_view->isVisible() &&
189 QStringLiteral("system-settings")) {192 m_applicationInfo.value("id").toString() != "system-settings") {
190 q->setDelay(3000);193 q->setDelay(3000);
191 }194 }
192 QVariantMap result;195 QVariantMap result;
193196
=== modified file 'online-accounts-ui/qml/ProviderRequest.qml'
--- online-accounts-ui/qml/ProviderRequest.qml 2016-05-19 11:06:28 +0000
+++ online-accounts-ui/qml/ProviderRequest.qml 2016-06-23 13:59:19 +0000
@@ -24,14 +24,10 @@
24MainView {24MainView {
25 id: root25 id: root
2626
27 property variant applicationInfo: application27 property var applicationInfo: request.application
28 property variant providerInfo: provider28 property var providerInfo: request.provider
29 property bool wasDenied: false
30 property int __createdAccountId: 029 property int __createdAccountId: 0
3130
32 signal denied
33 signal allowed(int accountId)
34
35 width: units.gu(48)31 width: units.gu(48)
36 height: units.gu(60)32 height: units.gu(60)
37 automaticOrientation: true33 automaticOrientation: true
@@ -40,14 +36,14 @@
40 i18n.domain = "ubuntu-system-settings-online-accounts"36 i18n.domain = "ubuntu-system-settings-online-accounts"
41 if (accessModel.count === 0 && !accessModel.canCreateAccounts) {37 if (accessModel.count === 0 && !accessModel.canCreateAccounts) {
42 /* No accounts to authorize */38 /* No accounts to authorize */
43 denied()39 request.deny()
44 return40 return
45 }41 }
46 if (!application.id && accessModel.count == 1 &&42 if (!applicationInfo.id && accessModel.count == 1 &&
47 applicationInfo.profile == "unconfined") {43 applicationInfo.profile == "unconfined") {
48 /* Degenerate case: unconfined app making requests with no valid44 /* Degenerate case: unconfined app making requests with no valid
49 * app ID */45 * app ID */
50 allowed(accountsModel.get(0, "accountId"))46 request.allow(accountsModel.get(0, "accountId"))
51 return47 return
52 }48 }
53 loader.active = true49 loader.active = true
@@ -55,9 +51,6 @@
55 }51 }
5652
57 on__CreatedAccountIdChanged: grantAccessIfReady()53 on__CreatedAccountIdChanged: grantAccessIfReady()
58 onDenied: wasDenied = true
59
60 onAllowed: loader.sourceComponent = spinnerComponent
6154
62 PageStack {55 PageStack {
63 id: pageStack56 id: pageStack
@@ -111,7 +104,7 @@
111 AccountCreationPage {104 AccountCreationPage {
112 providerId: providerInfo.id105 providerId: providerInfo.id
113 onFinished: {106 onFinished: {
114 if (accountId == 0) root.denied()107 if (accountId == 0) request.deny()
115 /* if an account was created, just remember its ID. when the108 /* if an account was created, just remember its ID. when the
116 * accountsModel will notice it we'll proceed with the access109 * accountsModel will notice it we'll proceed with the access
117 * grant */110 * grant */
@@ -127,7 +120,7 @@
127 application: applicationInfo120 application: applicationInfo
128 provider: providerInfo121 provider: providerInfo
129 canAddAnotherAccount: accessModel.canCreateAccounts122 canAddAnotherAccount: accessModel.canCreateAccounts
130 onDenied: root.denied()123 onDenied: request.deny()
131 onAllowed: root.grantAccess(accountId)124 onAllowed: root.grantAccess(accountId)
132 onCreateAccount: pageStack.push(createAccountPageComponent)125 onCreateAccount: pageStack.push(createAccountPageComponent)
133 }126 }
@@ -198,7 +191,8 @@
198 if (root.__createdAccountId != 0) {191 if (root.__createdAccountId != 0) {
199 // If the request comes from system settings, stop here192 // If the request comes from system settings, stop here
200 if (applicationInfo.id === "system-settings") {193 if (applicationInfo.id === "system-settings") {
201 root.allowed(root.__createdAccountId)194 request.allow(root.__createdAccountId)
195 loader.sourceComponent = spinnerComponent
202 return196 return
203 }197 }
204198
@@ -216,7 +210,7 @@
216 if (i < 0) {210 if (i < 0) {
217 // very unlikely; maybe the account has been deleted in the meantime211 // very unlikely; maybe the account has been deleted in the meantime
218 console.log("Account not found:" + accountId)212 console.log("Account not found:" + accountId)
219 root.denied()213 request.deny()
220 return214 return
221 }215 }
222216
@@ -249,6 +243,7 @@
249243
250 function accountEnablingDone() {244 function accountEnablingDone() {
251 console.log("account enabling done")245 console.log("account enabling done")
252 allowed(account.accountId)246 request.allow(account.accountId)
247 loader.sourceComponent = spinnerComponent
253 }248 }
254}249}
255250
=== modified file 'tests/online-accounts-ui/tst_provider_request.cpp'
--- tests/online-accounts-ui/tst_provider_request.cpp 2015-06-23 13:17:10 +0000
+++ tests/online-accounts-ui/tst_provider_request.cpp 2016-06-23 13:59:19 +0000
@@ -173,12 +173,13 @@
173 QTRY_COMPARE(setWindowCalled.count(), 1);173 QTRY_COMPARE(setWindowCalled.count(), 1);
174 QQuickView *view = static_cast<QQuickView*>(setWindowCalled.at(0).at(0).value<QWindow*>());174 QQuickView *view = static_cast<QQuickView*>(setWindowCalled.at(0).at(0).value<QWindow*>());
175 QQmlContext *context = view->engine()->rootContext();175 QQmlContext *context = view->engine()->rootContext();
176 QObject *request = context->contextProperty("request").value<QObject*>();
176177
177 QCOMPARE(applicationInfoCalled.count(), 1);178 QCOMPARE(applicationInfoCalled.count(), 1);
178 QCOMPARE(applicationInfoCalled.at(0).at(1).toString(), profile);179 QCOMPARE(applicationInfoCalled.at(0).at(1).toString(), profile);
179180
180 QCOMPARE(context->contextProperty("provider").toMap(), providerInfo);181 QCOMPARE(request->property("provider").toMap(), providerInfo);
181 QCOMPARE(context->contextProperty("application").toMap(), applicationInfo);182 QCOMPARE(request->property("application").toMap(), applicationInfo);
182 } else {183 } else {
183 QTRY_COMPARE(failCalled.count(), 1);184 QTRY_COMPARE(failCalled.count(), 1);
184 QCOMPARE(failCalled.at(0).at(0).toString(), errorName);185 QCOMPARE(failCalled.at(0).at(0).toString(), errorName);

Subscribers

People subscribed via source and target branches