Merge lp:~zaspire/oxide/web-notifications_v2 into lp:~oxide-developers/oxide/oxide.trunk

Proposed by Maxim Ermilov
Status: Merged
Merged at revision: 1201
Proposed branch: lp:~zaspire/oxide/web-notifications_v2
Merge into: lp:~oxide-developers/oxide/oxide.trunk
Diff against target: 948 lines (+664/-2)
24 files modified
build/system.gyp (+19/-0)
qt/core/browser/oxide_qt_browser_platform_integration.cc (+5/-0)
qt/core/browser/oxide_qt_browser_platform_integration.h (+1/-0)
qt/core/browser/oxide_qt_web_view.cc (+10/-0)
qt/core/browser/oxide_qt_web_view.h (+2/-0)
qt/core/glue/oxide_qt_web_view_proxy_client.h (+3/-0)
qt/qmlplugin/oxide_qml_plugin.cc (+1/-0)
qt/quick/api/oxideqquickwebview.cc (+23/-0)
qt/quick/api/oxideqquickwebview_p.h (+1/-0)
qt/quick/api/oxideqquickwebview_p_p.h (+3/-0)
qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.html (+21/-0)
qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.js (+6/-0)
qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.qml (+198/-0)
shared/browser/oxide_browser_platform_integration.h (+2/-0)
shared/browser/oxide_content_browser_client.cc (+9/-1)
shared/browser/oxide_content_browser_client.h (+1/-0)
shared/browser/oxide_platform_notification_service.cc (+265/-0)
shared/browser/oxide_platform_notification_service.h (+77/-0)
shared/browser/oxide_web_view.cc (+1/-1)
shared/browser/permissions/oxide_permission_manager.cc (+5/-0)
shared/browser/permissions/oxide_permission_request_dispatcher.cc (+3/-0)
shared/browser/permissions/oxide_permission_request_dispatcher_client.h (+3/-0)
shared/browser/permissions/oxide_temporary_saved_permission_context.h (+1/-0)
shared/shared.gyp (+4/-0)
To merge this branch: bzr merge lp:~zaspire/oxide/web-notifications_v2
Reviewer Review Type Date Requested Status
Chris Coulson Pending
Review via email: mp+262466@code.launchpad.net

This proposal supersedes a proposal from 2015-04-07.

To post a comment you must log in.
Revision history for this message
Maxim Ermilov (zaspire) wrote : Posted in a previous version of this proposal

this work is required for PushNotifications.

Revision history for this message
Chris Coulson (chrisccoulson) wrote : Posted in a previous version of this proposal

Ok, I've reviewed this again.

There's also quite a few cases missing in the tests - please take a look at the geolocation permission tests.

Also, we should figure out a way to test the actual libnotify integration (I'm not sure of the best way to do that, but it shouldn't be too difficult to mock).

review: Needs Fixing
Revision history for this message
Maxim Ermilov (zaspire) wrote :

tests with mocked libnotify wrapper will go as separate merge request.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'build/system.gyp'
2--- build/system.gyp 2015-01-21 23:40:11 +0000
3+++ build/system.gyp 2015-06-19 15:52:24 +0000
4@@ -18,6 +18,25 @@
5 'variables': {
6 'pkg_config': 'pkg-config'
7 },
8+ 'targets': [
9+ {
10+ 'target_name': 'libnotify',
11+ 'type': 'none',
12+ 'direct_dependent_settings': {
13+ 'cflags_cc': [
14+ '<!@(<(pkg_config) --cflags libnotify)'
15+ ]
16+ },
17+ 'link_settings': {
18+ 'ldflags': [
19+ '<!@(<(pkg_config) --libs-only-L --libs-only-other libnotify)',
20+ ],
21+ 'libraries': [
22+ '<!@(<(pkg_config) --libs-only-l libnotify)',
23+ ],
24+ },
25+ },
26+ ],
27 'conditions': [
28 ['target_arch=="arm"', {
29 'targets': [
30
31=== modified file 'qt/core/browser/oxide_qt_browser_platform_integration.cc'
32--- qt/core/browser/oxide_qt_browser_platform_integration.cc 2015-06-12 07:39:38 +0000
33+++ qt/core/browser/oxide_qt_browser_platform_integration.cc 2015-06-19 15:52:24 +0000
34@@ -161,6 +161,11 @@
35 return state_;
36 }
37
38+std::string
39+BrowserPlatformIntegration::GetAppName() {
40+ return QCoreApplication::applicationName().toStdString();
41+}
42+
43 bool BrowserPlatformIntegration::eventFilter(QObject* watched, QEvent* event) {
44 if (event->type() == QEvent::ApplicationActivate ||
45 event->type() == QEvent::ApplicationDeactivate) {
46
47=== modified file 'qt/core/browser/oxide_qt_browser_platform_integration.h'
48--- qt/core/browser/oxide_qt_browser_platform_integration.h 2015-06-12 07:39:38 +0000
49+++ qt/core/browser/oxide_qt_browser_platform_integration.h 2015-06-19 15:52:24 +0000
50@@ -57,6 +57,7 @@
51 void BrowserThreadInit(content::BrowserThread::ID id) override;
52 content::LocationProvider* CreateLocationProvider() override;
53 ApplicationState GetApplicationState() override;
54+ std::string GetAppName() override;
55 ui::ClipboardOxideFactory GetClipboardOxideFactory() override;
56
57 // QObject implementation
58
59=== modified file 'qt/core/browser/oxide_qt_web_view.cc'
60--- qt/core/browser/oxide_qt_web_view.cc 2015-06-04 17:11:01 +0000
61+++ qt/core/browser/oxide_qt_web_view.cc 2015-06-19 15:52:24 +0000
62@@ -1011,6 +1011,16 @@
63 client_->RequestGeolocationPermission(req.release());
64 }
65
66+void WebView::RequestNotificationPermission(
67+ scoped_ptr<oxide::SimplePermissionRequest> request) {
68+ scoped_ptr<OxideQSimplePermissionRequest> req(
69+ OxideQSimplePermissionRequestPrivate::Create(
70+ request.Pass()));
71+
72+ // The embedder takes ownership of this
73+ client_->RequestNotificationPermission(req.release());
74+}
75+
76 void WebView::RequestMediaAccessPermission(
77 scoped_ptr<oxide::MediaAccessPermissionRequest> request) {
78 scoped_ptr<OxideQMediaAccessPermissionRequest> req(
79
80=== modified file 'qt/core/browser/oxide_qt_web_view.h'
81--- qt/core/browser/oxide_qt_web_view.h 2015-06-04 13:21:49 +0000
82+++ qt/core/browser/oxide_qt_web_view.h 2015-06-19 15:52:24 +0000
83@@ -177,6 +177,8 @@
84 // oxide::PermissionRequestDispatcherClient implementation
85 void RequestGeolocationPermission(
86 scoped_ptr<oxide::SimplePermissionRequest> request) override;
87+ void RequestNotificationPermission(
88+ scoped_ptr<oxide::SimplePermissionRequest> request) override;
89 void RequestMediaAccessPermission(
90 scoped_ptr<oxide::MediaAccessPermissionRequest> request) override;
91
92
93=== modified file 'qt/core/glue/oxide_qt_web_view_proxy_client.h'
94--- qt/core/glue/oxide_qt_web_view_proxy_client.h 2015-05-27 20:56:59 +0000
95+++ qt/core/glue/oxide_qt_web_view_proxy_client.h 2015-06-19 15:52:24 +0000
96@@ -32,6 +32,7 @@
97 class OxideQMediaAccessPermissionRequest;
98 class OxideQNavigationRequest;
99 class OxideQNewViewRequest;
100+class OxideQSimplePermissionRequest;
101
102 QT_BEGIN_NAMESPACE
103 class QCursor;
104@@ -128,6 +129,8 @@
105 OxideQGeolocationPermissionRequest* request) = 0;
106 virtual void RequestMediaAccessPermission(
107 OxideQMediaAccessPermissionRequest* request) = 0;
108+ virtual void RequestNotificationPermission(
109+ OxideQSimplePermissionRequest* request) = 0;
110
111 virtual void HandleUnhandledKeyboardEvent(QKeyEvent* event) = 0;
112
113
114=== modified file 'qt/qmlplugin/oxide_qml_plugin.cc'
115--- qt/qmlplugin/oxide_qml_plugin.cc 2015-06-05 18:43:21 +0000
116+++ qt/qmlplugin/oxide_qml_plugin.cc 2015-06-19 15:52:24 +0000
117@@ -134,6 +134,7 @@
118 qmlRegisterUncreatableType<OxideQLoadEvent, 2>(uri, 1, 8, "LoadEvent",
119 "LoadEvent is delivered by WebView.loadEvent");
120 qmlRegisterType<OxideQQuickWebView, 4>(uri, 1, 8, "WebView");
121+ qmlRegisterType<OxideQQuickWebView, 5>(uri, 1, 8, "WebView");
122
123 qmlRegisterType<OxideQQuickWebContext, 3>(uri, 1, 9, "WebContext");
124 }
125
126=== modified file 'qt/quick/api/oxideqquickwebview.cc'
127--- qt/quick/api/oxideqquickwebview.cc 2015-05-28 23:51:39 +0000
128+++ qt/quick/api/oxideqquickwebview.cc 2015-06-19 15:52:24 +0000
129@@ -461,6 +461,29 @@
130
131 }
132
133+void OxideQQuickWebViewPrivate::RequestNotificationPermission(
134+ OxideQSimplePermissionRequest* request) {
135+ Q_Q(OxideQQuickWebView);
136+
137+ QQmlEngine* engine = qmlEngine(q);
138+ if (!engine) {
139+ delete request;
140+ return;
141+ }
142+
143+ {
144+ QJSValue val = engine->newQObject(request);
145+ if (!val.isQObject()) {
146+ delete request;
147+ return;
148+ }
149+
150+ emit q->notificationPermissionRequested(val);
151+ }
152+
153+ engine->collectGarbage();
154+}
155+
156 void OxideQQuickWebViewPrivate::HandleUnhandledKeyboardEvent(
157 QKeyEvent* event) {
158 Q_Q(OxideQQuickWebView);
159
160=== modified file 'qt/quick/api/oxideqquickwebview_p.h'
161--- qt/quick/api/oxideqquickwebview_p.h 2015-05-28 23:51:39 +0000
162+++ qt/quick/api/oxideqquickwebview_p.h 2015-06-19 15:52:24 +0000
163@@ -345,6 +345,7 @@
164 void navigationRequested(OxideQNavigationRequest* request);
165 void newViewRequested(OxideQNewViewRequest* request);
166 void geolocationPermissionRequested(const QJSValue& request);
167+ Q_REVISION(5) void notificationPermissionRequested(const QJSValue& request);
168 Q_REVISION(4) void mediaAccessPermissionRequested(const QJSValue& request);
169 void javaScriptConsoleMessage(LogMessageSeverityLevel level,
170 const QString& message,
171
172=== modified file 'qt/quick/api/oxideqquickwebview_p_p.h'
173--- qt/quick/api/oxideqquickwebview_p_p.h 2015-05-28 23:51:39 +0000
174+++ qt/quick/api/oxideqquickwebview_p_p.h 2015-06-19 15:52:24 +0000
175@@ -128,6 +128,9 @@
176 OxideQGeolocationPermissionRequest* request) override;
177 void RequestMediaAccessPermission(
178 OxideQMediaAccessPermissionRequest* request) override;
179+ void RequestNotificationPermission(
180+ OxideQSimplePermissionRequest* request) override;
181+
182 void HandleUnhandledKeyboardEvent(QKeyEvent *event) override;
183 void FrameMetadataUpdated(
184 oxide::qt::FrameMetadataChangeFlags flags) override;
185
186=== added file 'qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.html'
187--- qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.html 1970-01-01 00:00:00 +0000
188+++ qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.html 2015-06-19 15:52:24 +0000
189@@ -0,0 +1,21 @@
190+<html>
191+<body>
192+</body>
193+<script>
194+
195+var vgaConstraints = {video: true, audio: true};
196+
197+function invoke() {
198+ Notification.requestPermission(function(permission){
199+ if(permission == 'granted'){
200+ var e = new CustomEvent("result", { bubbles: true, detail: { status: 0 } });
201+ document.documentElement.dispatchEvent(e);
202+ } else {
203+ var e = new CustomEvent("result", { bubbles: true, detail: { status: 1 } });
204+ document.documentElement.dispatchEvent(e);
205+ }
206+ });
207+}
208+setTimeout(invoke, 400);
209+</script>
210+</html>
211
212=== added file 'qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.js'
213--- qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.js 1970-01-01 00:00:00 +0000
214+++ qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.js 2015-06-19 15:52:24 +0000
215@@ -0,0 +1,6 @@
216+// ==UserScript==
217+// @run-at document-start
218+// ==/UserScript==
219+document.addEventListener("result", function(event) {
220+ oxide.sendMessage("TEST-RESPONSE", { status: event.detail.status });
221+});
222
223=== added file 'qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.qml'
224--- qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.qml 1970-01-01 00:00:00 +0000
225+++ qt/tests/qmltests/api/tst_WebNotificationPermissionRequest.qml 2015-06-19 15:52:24 +0000
226@@ -0,0 +1,198 @@
227+import QtQuick 2.0
228+import QtTest 1.0
229+import com.canonical.Oxide 1.8
230+import com.canonical.Oxide.Testing 1.0
231+
232+Item {
233+ width: 200
234+ height: 200
235+
236+ SignalSpy {
237+ id: spy
238+ signalName: "notificationPermissionRequested"
239+ }
240+
241+ Component {
242+ id: userScriptFactory
243+ UserScript {}
244+ }
245+
246+ TestWebContext {
247+ id: c
248+ Component.onCompleted: {
249+ var script = userScriptFactory.createObject(null, {
250+ context: "oxide://testutils/",
251+ url: Qt.resolvedUrl("tst_WebNotificationPermissionRequest.js"),
252+ matchAllFrames: true
253+ });
254+ addUserScript(script);
255+ }
256+ }
257+
258+ Component {
259+ id: webViewFactory
260+ TestWebView {
261+ context: c
262+
263+ property var lastRequest: null
264+ onNotificationPermissionRequested: {
265+ lastRequest = request;
266+ }
267+
268+ QtObject {
269+ id: _internal
270+ property var lastStatus: -1
271+ }
272+ property alias lastStatus: _internal.lastStatus
273+
274+ messageHandlers: [
275+ ScriptMessageHandler {
276+ msgId: "TEST-RESPONSE"
277+ contexts: [ "oxide://testutils/" ]
278+ callback: function(msg) {
279+ _internal.lastStatus = msg.args.status;
280+ }
281+ }
282+ ]
283+ }
284+ }
285+
286+ TestCase {
287+ id: test
288+ name: "NotificationPermissionRequest_session_persist"
289+ when: windowShown
290+
291+ function _test_accept(req) {
292+ req.allow();
293+ }
294+
295+ function _test_deny(req) {
296+ req.deny();
297+ }
298+
299+ function _test_destroy(req) {
300+ req.destroy();
301+ }
302+
303+ function init() {
304+ spy.target = null;
305+ spy.clear();
306+ c.clearTemporarySavedPermissionStatuses();
307+ }
308+
309+ function test_NotificationPermissionRequest_session_persist1_data() {
310+ return [
311+ { function: _test_accept, expected: 0, save: true },
312+ { function: _test_deny, expected: 1, save: true },
313+ { function: _test_destroy, expected: 1, save: false },
314+ ];
315+ }
316+
317+ function test_NotificationPermissionRequest_session_persist1(data) {
318+ var webView = webViewFactory.createObject(null, {});
319+ spy.target = webView;
320+
321+ webView.url = "http://foo.testsuite/tst_WebNotificationPermissionRequest.html";
322+
323+ verify(webView.waitForLoadSucceeded());
324+
325+ spy.wait();
326+
327+ compare(webView.lastRequest.embedder, "http://foo.testsuite/");
328+
329+ data.function(webView.lastRequest);
330+
331+ verify(webView.waitFor(function() { return webView.lastStatus != -1; }));
332+ compare(webView.lastStatus, data.expected);
333+
334+ spy.clear();
335+ webView.lastStatus = -1;
336+
337+ webView.reload();
338+ verify(webView.waitForLoadSucceeded());
339+
340+ if (data.save) {
341+ verify(webView.waitFor(function() { return webView.lastStatus != -1; }));
342+ compare(webView.lastStatus, data.expected);
343+ } else {
344+ spy.wait();
345+ compare(webView.lastRequest.embedder, "http://foo.testsuite/");
346+ }
347+ }
348+
349+ function test_NotificationPermissionRequest_session_persist2_data() {
350+ return test_NotificationPermissionRequest_session_persist1_data();
351+ }
352+
353+ function test_NotificationPermissionRequest_session_persist2(data) {
354+ var webView = webViewFactory.createObject(null, {});
355+ spy.target = webView;
356+
357+ webView.url = "http://foo.testsuite/tst_WebNotificationPermissionRequest.html";
358+ verify(webView.waitForLoadSucceeded());
359+
360+ spy.wait();
361+
362+ compare(webView.lastRequest.embedder, "http://foo.testsuite/");
363+
364+ data.function(webView.lastRequest);
365+
366+ verify(webView.waitFor(function() { return webView.lastStatus != -1; }));
367+ compare(webView.lastStatus, data.expected);
368+
369+ webView = webViewFactory.createObject(null, {});
370+ spy.target = webView;
371+
372+ spy.clear();
373+
374+ webView.url = "http://foo.testsuite/tst_WebNotificationPermissionRequest.html";
375+ verify(webView.waitForLoadSucceeded());
376+
377+ if (data.save) {
378+ verify(webView.waitFor(function() { return webView.lastStatus != -1; }));
379+ compare(webView.lastStatus, data.expected);
380+ } else {
381+ spy.wait();
382+ compare(webView.lastRequest.embedder, "http://foo.testsuite/");
383+ }
384+ }
385+
386+ function test_NotificationPermissionRequest_session_persist3_data() {
387+ return [
388+ // Same origin / different embedder
389+ { url1: "http://foo.testsuite/tst_WebNotificationPermissionRequest.html",
390+ url2: "http://bar.testsuite/tst_WebNotificationPermissionRequest.html" },
391+ // Same origin / embedder == allowed origin
392+ { url1: "http://foo.testsuite/tst_WebNotificationPermissionRequest.html",
393+ url2: "http://testsuite/tst_WebNotificationPermissionRequest.html" },
394+ // Different origin / different embedder
395+ { url1: "http://foo.testsuite/tst_WebNotificationPermissionRequest.html",
396+ url2: "http://bar.testsuite/tst_WebNotificationPermissionRequest.html" },
397+ ];
398+ }
399+
400+ // Verify several cases where saved permissions should not be used
401+ function test_NotificationPermissionRequest_session_persist3(data) {
402+ var webView = webViewFactory.createObject(null, {});
403+ spy.target = webView;
404+
405+ webView.url = data.url1;
406+ verify(webView.waitForLoadSucceeded());
407+
408+ spy.wait();
409+
410+ webView.lastRequest.allow();
411+
412+ verify(webView.waitFor(function() { return webView.lastStatus != -1; }));
413+ compare(webView.lastStatus, 0);
414+
415+ spy.clear();
416+ webView.lastStatus = -1;
417+
418+ webView.url = data.url2;
419+ verify(webView.waitForLoadSucceeded());
420+
421+ spy.wait();
422+ }
423+ }
424+}
425
426=== modified file 'shared/browser/oxide_browser_platform_integration.h'
427--- shared/browser/oxide_browser_platform_integration.h 2015-06-12 07:39:38 +0000
428+++ shared/browser/oxide_browser_platform_integration.h 2015-06-19 15:52:24 +0000
429@@ -96,6 +96,8 @@
430 // Get the current application state
431 virtual ApplicationState GetApplicationState();
432
433+ virtual std::string GetAppName() = 0;
434+
435 protected:
436 BrowserPlatformIntegration();
437
438
439=== modified file 'shared/browser/oxide_content_browser_client.cc'
440--- shared/browser/oxide_content_browser_client.cc 2015-06-18 15:48:50 +0000
441+++ shared/browser/oxide_content_browser_client.cc 2015-06-19 15:52:24 +0000
442@@ -52,6 +52,7 @@
443 #include "oxide_web_preferences.h"
444 #include "oxide_web_view.h"
445 #include "oxide_web_view_contents_helper.h"
446+#include "oxide_platform_notification_service.h"
447
448 #if defined(ENABLE_PLUGINS)
449 #include "content/public/browser/browser_ppapi_host.h"
450@@ -298,7 +299,14 @@
451 const std::string& application_locale,
452 BrowserPlatformIntegration* integration)
453 : application_locale_(application_locale),
454- platform_integration_(integration) {}
455+ platform_integration_(integration) {
456+ PlatformNotificationService::GetInstance()->Initialize(integration->GetAppName());
457+}
458+
459+content::PlatformNotificationService*
460+ContentBrowserClient::GetPlatformNotificationService() {
461+ return PlatformNotificationService::GetInstance();
462+}
463
464 ContentBrowserClient::~ContentBrowserClient() {}
465
466
467=== modified file 'shared/browser/oxide_content_browser_client.h'
468--- shared/browser/oxide_content_browser_client.h 2015-06-17 21:04:11 +0000
469+++ shared/browser/oxide_content_browser_client.h 2015-06-19 15:52:24 +0000
470@@ -49,6 +49,7 @@
471 private:
472 // content::ContentBrowserClient implementation
473 std::string GetApplicationLocale() final;
474+ content::PlatformNotificationService* GetPlatformNotificationService() final;
475 content::BrowserMainParts* CreateBrowserMainParts(
476 const content::MainFunctionParams& parameters) final;
477 void RenderProcessWillLaunch(content::RenderProcessHost* host) final;
478
479=== added file 'shared/browser/oxide_platform_notification_service.cc'
480--- shared/browser/oxide_platform_notification_service.cc 1970-01-01 00:00:00 +0000
481+++ shared/browser/oxide_platform_notification_service.cc 2015-06-19 15:52:24 +0000
482@@ -0,0 +1,265 @@
483+// vim:expandtab:shiftwidth=2:tabstop=2:
484+// Copyright (C) 2014 Canonical Ltd.
485+
486+// This library is free software; you can redistribute it and/or
487+// modify it under the terms of the GNU Lesser General Public
488+// License as published by the Free Software Foundation; either
489+// version 2.1 of the License, or (at your option) any later version.
490+
491+// This library is distributed in the hope that it will be useful,
492+// but WITHOUT ANY WARRANTY; without even the implied warranty of
493+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
494+// Lesser General Public License for more details.
495+
496+// You should have received a copy of the GNU Lesser General Public
497+// License along with this library; if not, write to the Free Software
498+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
499+
500+#include "oxide_platform_notification_service.h"
501+
502+#include <map>
503+#include <libnotify/notify.h>
504+#include "third_party/skia/include/core/SkImageEncoder.h"
505+#include "third_party/skia/include/core/SkBitmap.h"
506+
507+#include "base/strings/utf_string_conversions.h"
508+#include "content/public/common/platform_notification_data.h"
509+#include "content/public/browser/desktop_notification_delegate.h"
510+#include "third_party/skia/include/core/SkUnPreMultiply.h"
511+
512+#include "shared/browser/permissions/oxide_temporary_saved_permission_context.h"
513+#include "shared/browser/oxide_browser_context.h"
514+
515+namespace oxide {
516+
517+// Copy&paste from chrome/browser/ui/libgtk2ui/skia_utils_gtk2.cc
518+// to avoid dependency on full Gdk.
519+GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap) {
520+ if (bitmap.isNull()) {
521+ return nullptr;
522+ }
523+
524+ SkAutoLockPixels lock_pixels(bitmap);
525+
526+ int width = bitmap.width();
527+ int height = bitmap.height();
528+
529+ GdkPixbuf* pixbuf =
530+ gdk_pixbuf_new(GDK_COLORSPACE_RGB, // The only colorspace gtk supports.
531+ TRUE, // There is an alpha channel.
532+ 8,
533+ width,
534+ height);
535+
536+ // SkBitmaps are premultiplied, we need to unpremultiply them.
537+ const int kBytesPerPixel = 4;
538+ uint8_t* divided = gdk_pixbuf_get_pixels(pixbuf);
539+
540+ for (int y = 0, i = 0; y < height; y++) {
541+ for (int x = 0; x < width; x++) {
542+ uint32_t pixel = bitmap.getAddr32(0, y)[x];
543+
544+ int alpha = SkColorGetA(pixel);
545+ if (alpha != 0 && alpha != 255) {
546+ SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel);
547+ divided[i + 0] = SkColorGetR(unmultiplied);
548+ divided[i + 1] = SkColorGetG(unmultiplied);
549+ divided[i + 2] = SkColorGetB(unmultiplied);
550+ divided[i + 3] = alpha;
551+ } else {
552+ divided[i + 0] = SkColorGetR(pixel);
553+ divided[i + 1] = SkColorGetG(pixel);
554+ divided[i + 2] = SkColorGetB(pixel);
555+ divided[i + 3] = alpha;
556+ }
557+ i += kBytesPerPixel;
558+ }
559+ }
560+
561+ return pixbuf;
562+}
563+
564+
565+namespace {
566+
567+blink::WebNotificationPermission
568+ToNotificationPermission(TemporarySavedPermissionStatus status) {
569+ switch (status) {
570+ case TEMPORARY_SAVED_PERMISSION_STATUS_ALLOWED:
571+ return blink::WebNotificationPermissionAllowed;
572+ case TEMPORARY_SAVED_PERMISSION_STATUS_DENIED:
573+ return blink::WebNotificationPermissionDenied;
574+ default:
575+ return blink::WebNotificationPermissionDefault;
576+ };
577+}
578+
579+class DesktopNotification: public base::RefCounted<DesktopNotification> {
580+public:
581+ base::Closure* GetCancelCallback();
582+
583+ static scoped_refptr<DesktopNotification> CreateDesktopNotification(
584+ const GURL& origin, const std::string &tag);
585+ void Initialize(
586+ const base::string16& title, const base::string16& body,
587+ const SkBitmap& icon,
588+ scoped_ptr<content::DesktopNotificationDelegate> delegate);
589+private:
590+ DesktopNotification(const GURL &origin, const std::string &tag);
591+ ~DesktopNotification();
592+
593+ void CancelNotification();
594+
595+ GURL origin_;
596+ std::string tag_;
597+ base::Closure cancel_callback_;
598+ NotifyNotification *notification_;
599+ scoped_ptr<content::DesktopNotificationDelegate> delegate_;
600+
601+ static void OnClosed(NotifyNotification *notification, DesktopNotification *self);
602+ static std::map<GURL, std::map<std::string, scoped_refptr<DesktopNotification>>> table_;
603+
604+ friend class base::RefCounted<DesktopNotification>;
605+};
606+
607+std::map<GURL, std::map<std::string, scoped_refptr<DesktopNotification>>> DesktopNotification::table_;
608+
609+DesktopNotification::DesktopNotification(const GURL &origin, const std::string &tag)
610+ : origin_(origin), tag_(tag), notification_(nullptr) {
611+}
612+
613+scoped_refptr<DesktopNotification>
614+DesktopNotification::CreateDesktopNotification(const GURL& origin, const std::string &tag) {
615+ table_[origin][tag] = new DesktopNotification(origin, tag);
616+ return table_[origin][tag];
617+}
618+
619+void
620+DesktopNotification::Initialize(
621+ const base::string16& title, const base::string16& body,
622+ const SkBitmap& icon,
623+ scoped_ptr<content::DesktopNotificationDelegate> delegate) {
624+ DCHECK(!notification_);
625+
626+ delegate_ = delegate.Pass();
627+
628+ std::string t = base::UTF16ToUTF8(title);
629+ std::string b = base::UTF16ToUTF8(body);
630+
631+ notification_ = notify_notification_new(t.c_str(), b.c_str(), nullptr);
632+
633+ g_signal_connect(notification_, "closed", G_CALLBACK(OnClosed), this);
634+
635+ if (delegate_.get()) {
636+ delegate_->NotificationDisplayed();
637+ }
638+
639+ if (icon.width() && icon.height()) {
640+ GdkPixbuf *pix = GdkPixbufFromSkBitmap(icon);
641+ notify_notification_set_image_from_pixbuf(notification_, pix);
642+ g_object_unref(G_OBJECT(pix));
643+ }
644+ notify_notification_show(notification_, nullptr);
645+}
646+
647+base::Closure*
648+DesktopNotification::GetCancelCallback() {
649+ cancel_callback_ = base::Bind(&DesktopNotification::CancelNotification, this);
650+ return &cancel_callback_;
651+}
652+
653+DesktopNotification::~DesktopNotification() {
654+ g_object_unref(G_OBJECT(notification_));
655+}
656+
657+void
658+DesktopNotification::CancelNotification() {
659+ table_[origin_].erase(tag_);
660+ this->Release();
661+}
662+
663+void
664+DesktopNotification::OnClosed(NotifyNotification *notification, DesktopNotification *self) {
665+ table_[self->origin_].erase(self->tag_);
666+ if (self->delegate_.get()) {
667+ self->delegate_->NotificationClosed();
668+ }
669+ self->Release();
670+}
671+
672+}
673+
674+void
675+PlatformNotificationService::Initialize(const std::string& name) {
676+ notify_init(name.c_str());
677+}
678+
679+blink::WebNotificationPermission
680+PlatformNotificationService::CheckPermissionOnIOThread(
681+ content::ResourceContext* resource_context,
682+ const GURL& origin,
683+ int render_process_id) {
684+ TemporarySavedPermissionContext* permission_context =
685+ BrowserContextIOData::FromResourceContext(resource_context)->GetTemporarySavedPermissionContext();
686+
687+ return ToNotificationPermission(permission_context->GetPermissionStatus(
688+ TEMPORARY_SAVED_PERMISSION_TYPE_NOTIFICATIONS, origin, origin));
689+}
690+
691+blink::WebNotificationPermission
692+PlatformNotificationService::CheckPermissionOnUIThread(
693+ content::BrowserContext* browser_context,
694+ const GURL& origin,
695+ int render_process_id) {
696+ BrowserContext* context = static_cast<BrowserContext*>(browser_context);
697+ TemporarySavedPermissionContext* permission_context = context->GetTemporarySavedPermissionContext();
698+
699+ return ToNotificationPermission(permission_context->GetPermissionStatus(
700+ TEMPORARY_SAVED_PERMISSION_TYPE_NOTIFICATIONS, origin, origin));
701+}
702+
703+void
704+PlatformNotificationService::DisplayNotification(
705+ content::BrowserContext* browser_context,
706+ const GURL& origin,
707+ const SkBitmap& icon,
708+ const content::PlatformNotificationData& notification_data,
709+ scoped_ptr<content::DesktopNotificationDelegate> delegate,
710+ base::Closure* cancel_callback) {
711+
712+ scoped_refptr<DesktopNotification> notification = DesktopNotification::CreateDesktopNotification(
713+ origin, notification_data.tag);
714+ notification->Initialize(notification_data.title, notification_data.body, icon, delegate.Pass());
715+
716+ cancel_callback = notification->GetCancelCallback();
717+}
718+
719+void
720+PlatformNotificationService::DisplayPersistentNotification(
721+ content::BrowserContext* browser_context,
722+ int64 service_worker_registration_id,
723+ const GURL& origin,
724+ const SkBitmap& icon,
725+ const content::PlatformNotificationData& notification_data) {
726+ NOTIMPLEMENTED();
727+}
728+
729+void
730+PlatformNotificationService::ClosePersistentNotification(content::BrowserContext* browser_context,
731+ int64_t persistent_notification_id) {
732+ NOTIMPLEMENTED();
733+}
734+
735+bool
736+PlatformNotificationService::GetDisplayedPersistentNotifications(
737+ content::BrowserContext* browser_context,
738+ std::set<std::string>* displayed_notifications) {
739+ return false;
740+}
741+
742+PlatformNotificationService*
743+PlatformNotificationService::GetInstance() {
744+ return Singleton<PlatformNotificationService>::get();
745+}
746+
747+}
748
749=== added file 'shared/browser/oxide_platform_notification_service.h'
750--- shared/browser/oxide_platform_notification_service.h 1970-01-01 00:00:00 +0000
751+++ shared/browser/oxide_platform_notification_service.h 2015-06-19 15:52:24 +0000
752@@ -0,0 +1,77 @@
753+// vim:expandtab:shiftwidth=2:tabstop=2:
754+// Copyright (C) 2014 Canonical Ltd.
755+
756+// This library is free software; you can redistribute it and/or
757+// modify it under the terms of the GNU Lesser General Public
758+// License as published by the Free Software Foundation; either
759+// version 2.1 of the License, or (at your option) any later version.
760+
761+// This library is distributed in the hope that it will be useful,
762+// but WITHOUT ANY WARRANTY; without even the implied warranty of
763+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
764+// Lesser General Public License for more details.
765+
766+// You should have received a copy of the GNU Lesser General Public
767+// License along with this library; if not, write to the Free Software
768+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
769+
770+#ifndef _OXIDE_QT_CORE_PLATFORM_NOTIFICATION_H_
771+#define _OXIDE_QT_CORE_PLATFORM_NOTIFICATION_H_
772+
773+#include <string>
774+
775+#include "base/memory/singleton.h"
776+#include "base/values.h"
777+#include "content/public/browser/platform_notification_service.h"
778+
779+namespace oxide {
780+
781+class PlatformNotificationService: public content::PlatformNotificationService {
782+public:
783+ void Initialize(const std::string &name);
784+
785+ blink::WebNotificationPermission CheckPermissionOnIOThread(
786+ content::ResourceContext* resource_context,
787+ const GURL& origin,
788+ int render_process_id) override;
789+
790+ blink::WebNotificationPermission CheckPermissionOnUIThread(
791+ content::BrowserContext* browser_context,
792+ const GURL& origin,
793+ int render_process_id) override;
794+
795+ void DisplayNotification(
796+ content::BrowserContext* browser_context,
797+ const GURL& origin,
798+ const SkBitmap& icon,
799+ const content::PlatformNotificationData& notification_data,
800+ scoped_ptr<content::DesktopNotificationDelegate> delegate,
801+ base::Closure* cancel_callback) override;
802+
803+ void DisplayPersistentNotification(
804+ content::BrowserContext* browser_context,
805+ int64 service_worker_registration_id,
806+ const GURL& origin,
807+ const SkBitmap& icon,
808+ const content::PlatformNotificationData& notification_data) override;
809+
810+ void ClosePersistentNotification(
811+ content::BrowserContext* browser_context,
812+ int64_t persistent_notification_id) override;
813+
814+ bool GetDisplayedPersistentNotifications(
815+ content::BrowserContext* browser_context,
816+ std::set<std::string>* displayed_notifications) override;
817+
818+ static PlatformNotificationService* GetInstance();
819+private:
820+ friend struct DefaultSingletonTraits<PlatformNotificationService>;
821+ PlatformNotificationService() = default;
822+
823+ DISALLOW_COPY_AND_ASSIGN(PlatformNotificationService);
824+};
825+
826+
827+}
828+
829+#endif
830
831=== modified file 'shared/browser/oxide_web_view.cc'
832--- shared/browser/oxide_web_view.cc 2015-06-18 15:48:50 +0000
833+++ shared/browser/oxide_web_view.cc 2015-06-19 15:52:24 +0000
834@@ -1271,7 +1271,7 @@
835 DCHECK(root_frame_.get());
836
837 if (params->context && init_data_->load_params) {
838- web_contents_->GetController().LoadURLWithParams(*init_data_->load_params);
839+ web_contents_->GetController().LoadURLWithParams(*init_data_->load_params);
840 }
841
842 web_contents_->GetController().LoadIfNecessary();
843
844=== modified file 'shared/browser/permissions/oxide_permission_manager.cc'
845--- shared/browser/permissions/oxide_permission_manager.cc 2015-06-17 21:04:11 +0000
846+++ shared/browser/permissions/oxide_permission_manager.cc 2015-06-19 15:52:24 +0000
847@@ -21,6 +21,7 @@
848 #include "content/public/browser/geolocation_provider.h"
849 #include "content/public/browser/permission_type.h"
850 #include "content/public/common/permission_status.mojom.h"
851+#include "shared/browser/oxide_platform_notification_service.h"
852
853 #include "shared/browser/oxide_browser_context.h"
854
855@@ -57,6 +58,7 @@
856 bool IsPermissionTypeSupported(content::PermissionType permission) {
857 switch (permission) {
858 case content::PermissionType::GEOLOCATION:
859+ case content::PermissionType::NOTIFICATIONS:
860 return true;
861 default:
862 return false;
863@@ -68,6 +70,8 @@
864 switch (permission) {
865 case content::PermissionType::GEOLOCATION:
866 return TEMPORARY_SAVED_PERMISSION_TYPE_GEOLOCATION;
867+ case content::PermissionType::NOTIFICATIONS:
868+ return TEMPORARY_SAVED_PERMISSION_TYPE_NOTIFICATIONS;
869 default:
870 NOTREACHED();
871 // XXX(chrisccoulson): Perhaps we need __builtin_unreachable here?
872@@ -134,6 +138,7 @@
873 case content::PermissionType::GEOLOCATION:
874 return base::Bind(&RespondToGeolocationPermissionRequest,
875 wrapped_callback);
876+ case content::PermissionType::NOTIFICATIONS:
877 default:
878 return wrapped_callback;
879 }
880
881=== modified file 'shared/browser/permissions/oxide_permission_request_dispatcher.cc'
882--- shared/browser/permissions/oxide_permission_request_dispatcher.cc 2015-06-17 21:04:11 +0000
883+++ shared/browser/permissions/oxide_permission_request_dispatcher.cc 2015-06-19 15:52:24 +0000
884@@ -140,6 +140,9 @@
885 case content::PermissionType::GEOLOCATION:
886 client_->RequestGeolocationPermission(request.Pass());
887 break;
888+ case content::PermissionType::NOTIFICATIONS:
889+ client_->RequestNotificationPermission(request.Pass());
890+ break;
891 default:
892 NOTIMPLEMENTED();
893 break;
894
895=== modified file 'shared/browser/permissions/oxide_permission_request_dispatcher_client.h'
896--- shared/browser/permissions/oxide_permission_request_dispatcher_client.h 2015-06-04 08:35:28 +0000
897+++ shared/browser/permissions/oxide_permission_request_dispatcher_client.h 2015-06-19 15:52:24 +0000
898@@ -38,6 +38,9 @@
899
900 virtual void RequestMediaAccessPermission(
901 scoped_ptr<MediaAccessPermissionRequest> request) {}
902+
903+ virtual void RequestNotificationPermission(
904+ scoped_ptr<SimplePermissionRequest> request) {}
905 };
906
907 } // namespace oxide
908
909=== modified file 'shared/browser/permissions/oxide_temporary_saved_permission_context.h'
910--- shared/browser/permissions/oxide_temporary_saved_permission_context.h 2015-06-10 13:10:39 +0000
911+++ shared/browser/permissions/oxide_temporary_saved_permission_context.h 2015-06-19 15:52:24 +0000
912@@ -41,6 +41,7 @@
913 PERMISSION_TYPES_START,
914
915 TEMPORARY_SAVED_PERMISSION_TYPE_GEOLOCATION = PERMISSION_TYPES_START,
916+ TEMPORARY_SAVED_PERMISSION_TYPE_NOTIFICATIONS,
917 TEMPORARY_SAVED_PERMISSION_TYPE_MEDIA_DEVICE_MIC,
918 TEMPORARY_SAVED_PERMISSION_TYPE_MEDIA_DEVICE_CAMERA,
919
920
921=== modified file 'shared/shared.gyp'
922--- shared/shared.gyp 2015-06-18 15:48:50 +0000
923+++ shared/shared.gyp 2015-06-19 15:52:24 +0000
924@@ -17,6 +17,7 @@
925 {
926 'variables': {
927 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/oxide',
928+ 'pkg_config': 'pkg-config'
929 },
930 'conditions': [
931 ['enable_mediahub==1', {
932@@ -264,6 +265,7 @@
933 '<(DEPTH)/ui/ozone/ozone.gyp:ozone',
934 '<(DEPTH)/url/url.gyp:url_lib',
935 '<(DEPTH)/v8/tools/gyp/v8.gyp:v8',
936+ '../build/system.gyp:libnotify',
937 ],
938 'include_dirs': [
939 '..',
940@@ -357,6 +359,8 @@
941 'browser/oxide_message_pump.h',
942 'browser/oxide_network_delegate.cc',
943 'browser/oxide_network_delegate.h',
944+ 'browser/oxide_platform_notification_service.h',
945+ 'browser/oxide_platform_notification_service.cc',
946 'browser/oxide_power_save_blocker.cc',
947 'browser/oxide_power_save_blocker.h',
948 'browser/oxide_quota_permission_context.cc',

Subscribers

People subscribed via source and target branches