Merge lp:~osomon/oxide/context-menu into lp:~oxide-developers/oxide/oxide.trunk

Proposed by Olivier Tilloy on 2015-04-24
Status: Merged
Merged at revision: 1094
Proposed branch: lp:~osomon/oxide/context-menu
Merge into: lp:~oxide-developers/oxide/oxide.trunk
Diff against target: 1819 lines (+1406/-5)
27 files modified
qt/core/browser/oxide_qt_web_context_menu.cc (+183/-0)
qt/core/browser/oxide_qt_web_context_menu.h (+73/-0)
qt/core/browser/oxide_qt_web_view.cc (+35/-0)
qt/core/browser/oxide_qt_web_view.h (+5/-0)
qt/core/core.gyp (+4/-0)
qt/core/glue/oxide_qt_web_context_menu_proxy.h (+35/-0)
qt/core/glue/oxide_qt_web_context_menu_proxy_client.h (+92/-0)
qt/core/glue/oxide_qt_web_view_proxy.h (+12/-0)
qt/core/glue/oxide_qt_web_view_proxy_client.h (+4/-0)
qt/quick/CMakeLists.txt (+2/-1)
qt/quick/api/oxideqquickwebview.cc (+55/-0)
qt/quick/api/oxideqquickwebview_p.h (+58/-1)
qt/quick/api/oxideqquickwebview_p_p.h (+4/-1)
qt/quick/oxide_qquick_web_context_menu.cc (+311/-0)
qt/quick/oxide_qquick_web_context_menu.h (+61/-0)
qt/tests/qmltests/api/cof.svg (+27/-0)
qt/tests/qmltests/api/tst_WebView_contextMenu.html (+13/-0)
qt/tests/qmltests/api/tst_WebView_contextMenu.qml (+215/-0)
shared/browser/oxide_web_contents_view.cc (+7/-1)
shared/browser/oxide_web_contents_view.h (+3/-1)
shared/browser/oxide_web_context_menu.cc (+105/-0)
shared/browser/oxide_web_context_menu.h (+73/-0)
shared/browser/oxide_web_view.cc (+11/-0)
shared/browser/oxide_web_view.h (+4/-0)
shared/browser/oxide_web_view_client.cc (+6/-0)
shared/browser/oxide_web_view_client.h (+6/-0)
shared/shared.gyp (+2/-0)
To merge this branch: bzr merge lp:~osomon/oxide/context-menu
Reviewer Review Type Date Requested Status
Chris Coulson 2015-04-24 Approve on 2015-05-26
Review via email: mp+257351@code.launchpad.net

Commit Message

Support for context menus.

To post a comment you must log in.
lp:~osomon/oxide/context-menu updated on 2015-05-15
1036. By Olivier Tilloy on 2015-05-15

Merge the latest changes from trunk and resolve a conflict.

Chris Coulson (chrisccoulson) wrote :

Thanks for working on this - I've added a few comments inline.

In addition to that, there's a few use cases that are missing:
- Open link / image / audio / video in new tab / window don't have any way to pass referrer information when appropriate. I'm not sure how best to handle that.
- Copy image to clipboard (for image + canvas)
- Controlling the media element (play / pause / mute)
- Save page (there should be a WebView API for this)
- Reload frame (there should be a WebView API for this).

It's ok to leave these cases unimplemented for this iteration, but we should have follow up bugs for them.

Out of interest - for the browser implementation, have you had a look at the logic in Chromium (particularly components/renderer_context_menu/context_menu_content_type.cc and chrome/browser/renderer_context_menu/render_view_context_menu.cc)?

review: Needs Fixing
lp:~osomon/oxide/context-menu updated on 2015-05-22
1037. By Olivier Tilloy on 2015-05-22

Merge the latest changes from trunk and resolve a couple of conflicts.

1038. By Olivier Tilloy on 2015-05-22

Use DISALLOW_COPY_AND_ASSIGN instead of DISALLOW_IMPLICIT_CONSTRUCTORS.

1039. By Olivier Tilloy on 2015-05-22

QPoint → QPointF

1040. By Olivier Tilloy on 2015-05-22

Remove unused frameCharset property.

1041. By Olivier Tilloy on 2015-05-22

Remove unused suggestedFilename property.

1042. By Olivier Tilloy on 2015-05-22

Convert can* properties to edit flags.

1043. By Olivier Tilloy on 2015-05-22

Add mediaType property.

1044. By Olivier Tilloy on 2015-05-22

Add unit tests for audio and video media types.

1045. By Olivier Tilloy on 2015-05-22

Refactor context menu unit tests to be data-driven.

1046. By Olivier Tilloy on 2015-05-22

Add mediaFlags property.

1047. By Olivier Tilloy on 2015-05-22

Add WebView.executeEditingCommand() API.

1048. By Olivier Tilloy on 2015-05-22

Rename saveImage() to saveMedia().

1049. By Olivier Tilloy on 2015-05-22

Do not store a reference to the active context menu, this is unused.

Olivier Tilloy (osomon) wrote :

Addressed all your comments but that one:

> It doesn't look like ContextMenuParams::link_text is used in Chrome.
> Does it need to be exposed here?

It’s not used, but it sounds like it could potentially be useful to embedders (for e.g. sharing a URL and associated text), and since it’s not really cluttering the API I left it there. I’ll remove it if you insist though :)

lp:~osomon/oxide/context-menu updated on 2015-05-22
1050. By Olivier Tilloy on 2015-05-22

Merge the latest changes from trunk and resolve a conflict.

Chris Coulson (chrisccoulson) wrote :

Thanks. There's just a couple of minor comments inline, but other than that, this looks ok

review: Approve
lp:~osomon/oxide/context-menu updated on 2015-05-26
1051. By Olivier Tilloy on 2015-05-26

Rename EditFlags to EditCapabilityFlags.

1052. By Olivier Tilloy on 2015-05-26

Rename MediaFlags to MediaStatusFlags.

1053. By Olivier Tilloy on 2015-05-26

Remove unused code.

1054. By Olivier Tilloy on 2015-05-26

Overlooked one rename.

1055. By Olivier Tilloy on 2015-05-26

Forgot a few more renames in tests.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'qt/core/browser/oxide_qt_web_context_menu.cc'
2--- qt/core/browser/oxide_qt_web_context_menu.cc 1970-01-01 00:00:00 +0000
3+++ qt/core/browser/oxide_qt_web_context_menu.cc 2015-05-26 19:54:24 +0000
4@@ -0,0 +1,183 @@
5+// vim:expandtab:shiftwidth=2:tabstop=2:
6+// Copyright (C) 2015 Canonical Ltd.
7+
8+// This library is free software; you can redistribute it and/or
9+// modify it under the terms of the GNU Lesser General Public
10+// License as published by the Free Software Foundation; either
11+// version 2.1 of the License, or (at your option) any later version.
12+
13+// This library is distributed in the hope that it will be useful,
14+// but WITHOUT ANY WARRANTY; without even the implied warranty of
15+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+// Lesser General Public License for more details.
17+
18+// You should have received a copy of the GNU Lesser General Public
19+// License along with this library; if not, write to the Free Software
20+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+
22+#include "oxide_qt_web_context_menu.h"
23+
24+#include <QPoint>
25+#include <QString>
26+#include <QUrl>
27+
28+#include "base/strings/utf_string_conversions.h"
29+
30+#include "qt/core/glue/oxide_qt_web_context_menu_proxy.h"
31+
32+namespace oxide {
33+namespace qt {
34+
35+WebContextMenu::~WebContextMenu() {}
36+
37+void WebContextMenu::Show() {
38+ proxy_->Show();
39+}
40+
41+void WebContextMenu::Hide() {
42+ proxy_->Hide();
43+}
44+
45+MediaType WebContextMenu::mediaType() const {
46+ switch (params_.media_type) {
47+ case blink::WebContextMenuData::MediaTypeNone:
48+ case blink::WebContextMenuData::MediaTypeFile:
49+ return MEDIA_TYPE_NONE;
50+ case blink::WebContextMenuData::MediaTypeImage:
51+ return MEDIA_TYPE_IMAGE;
52+ case blink::WebContextMenuData::MediaTypeVideo:
53+ return MEDIA_TYPE_VIDEO;
54+ case blink::WebContextMenuData::MediaTypeAudio:
55+ return MEDIA_TYPE_AUDIO;
56+ case blink::WebContextMenuData::MediaTypeCanvas:
57+ return MEDIA_TYPE_CANVAS;
58+ case blink::WebContextMenuData::MediaTypePlugin:
59+ return MEDIA_TYPE_PLUGIN;
60+ default:
61+ Q_UNREACHABLE();
62+ }
63+}
64+
65+QPoint WebContextMenu::position() const {
66+ return QPoint(params_.x, params_.y);
67+}
68+
69+QUrl WebContextMenu::linkUrl() const {
70+ return QUrl(QString::fromStdString(params_.link_url.spec()));
71+}
72+
73+QString WebContextMenu::linkText() const {
74+ return QString::fromStdString(base::UTF16ToUTF8(params_.link_text));
75+}
76+
77+QUrl WebContextMenu::unfilteredLinkUrl() const {
78+ return QUrl(QString::fromStdString(params_.unfiltered_link_url.spec()));
79+}
80+
81+QUrl WebContextMenu::srcUrl() const {
82+ return QUrl(QString::fromStdString(params_.src_url.spec()));
83+}
84+
85+bool WebContextMenu::hasImageContents() const {
86+ return params_.has_image_contents;
87+}
88+
89+QUrl WebContextMenu::pageUrl() const {
90+ return QUrl(QString::fromStdString(params_.page_url.spec()));
91+}
92+
93+QUrl WebContextMenu::frameUrl() const {
94+ return QUrl(QString::fromStdString(params_.frame_url.spec()));
95+}
96+
97+QString WebContextMenu::selectionText() const {
98+ return QString::fromStdString(base::UTF16ToUTF8(params_.selection_text));
99+}
100+
101+bool WebContextMenu::isEditable() const {
102+ return params_.is_editable;
103+}
104+
105+void WebContextMenu::cancel() {
106+ Close();
107+}
108+
109+int WebContextMenu::editFlags() const {
110+ int flags = NO_CAPABILITY;
111+ if (params_.edit_flags & blink::WebContextMenuData::CanUndo) {
112+ flags |= UNDO_CAPABILITY;
113+ }
114+ if (params_.edit_flags & blink::WebContextMenuData::CanRedo) {
115+ flags |= REDO_CAPABILITY;
116+ }
117+ if (params_.edit_flags & blink::WebContextMenuData::CanCut) {
118+ flags |= CUT_CAPABILITY;
119+ }
120+ if (params_.edit_flags & blink::WebContextMenuData::CanCopy) {
121+ flags |= COPY_CAPABILITY;
122+ }
123+ if (params_.edit_flags & blink::WebContextMenuData::CanPaste) {
124+ flags |= PASTE_CAPABILITY;
125+ }
126+ if (params_.edit_flags & blink::WebContextMenuData::CanDelete) {
127+ flags |= ERASE_CAPABILITY;
128+ }
129+ if (params_.edit_flags & blink::WebContextMenuData::CanSelectAll) {
130+ flags |= SELECT_ALL_CAPABILITY;
131+ }
132+ return flags;
133+}
134+
135+int WebContextMenu::mediaFlags() const {
136+ int flags = MEDIA_STATUS_NONE;
137+ if (params_.media_flags & blink::WebContextMenuData::MediaInError) {
138+ flags |= MEDIA_STATUS_IN_ERROR;
139+ }
140+ if (params_.media_flags & blink::WebContextMenuData::MediaPaused) {
141+ flags |= MEDIA_STATUS_PAUSED;
142+ }
143+ if (params_.media_flags & blink::WebContextMenuData::MediaMuted) {
144+ flags |= MEDIA_STATUS_MUTED;
145+ }
146+ if (params_.media_flags & blink::WebContextMenuData::MediaLoop) {
147+ flags |= MEDIA_STATUS_LOOP;
148+ }
149+ if (params_.media_flags & blink::WebContextMenuData::MediaCanSave) {
150+ flags |= MEDIA_STATUS_CAN_SAVE;
151+ }
152+ if (params_.media_flags & blink::WebContextMenuData::MediaHasAudio) {
153+ flags |= MEDIA_STATUS_HAS_AUDIO;
154+ }
155+ if (params_.media_flags & blink::WebContextMenuData::MediaCanToggleControls) {
156+ flags |= MEDIA_STATUS_CAN_TOGGLE_CONTROLS;
157+ }
158+ if (params_.media_flags & blink::WebContextMenuData::MediaControls) {
159+ flags |= MEDIA_STATUS_CONTROLS;
160+ }
161+ if (params_.media_flags & blink::WebContextMenuData::MediaCanPrint) {
162+ flags |= MEDIA_STATUS_CAN_PRINT;
163+ }
164+ if (params_.media_flags & blink::WebContextMenuData::MediaCanRotate) {
165+ flags |= MEDIA_STATUS_CAN_ROTATE;
166+ }
167+ return flags;
168+}
169+
170+void WebContextMenu::saveLink() const {
171+ SaveLink();
172+}
173+
174+void WebContextMenu::saveMedia() const {
175+ SaveMedia();
176+}
177+
178+WebContextMenu::WebContextMenu(content::RenderFrameHost* rfh,
179+ const content::ContextMenuParams& params)
180+ : oxide::WebContextMenu(rfh, params) {}
181+
182+void WebContextMenu::SetProxy(WebContextMenuProxy* proxy) {
183+ proxy_.reset(proxy);
184+}
185+
186+} // namespace qt
187+} // namespace oxide
188
189=== added file 'qt/core/browser/oxide_qt_web_context_menu.h'
190--- qt/core/browser/oxide_qt_web_context_menu.h 1970-01-01 00:00:00 +0000
191+++ qt/core/browser/oxide_qt_web_context_menu.h 2015-05-26 19:54:24 +0000
192@@ -0,0 +1,73 @@
193+// vim:expandtab:shiftwidth=2:tabstop=2:
194+// Copyright (C) 2015 Canonical Ltd.
195+
196+// This library is free software; you can redistribute it and/or
197+// modify it under the terms of the GNU Lesser General Public
198+// License as published by the Free Software Foundation; either
199+// version 2.1 of the License, or (at your option) any later version.
200+
201+// This library is distributed in the hope that it will be useful,
202+// but WITHOUT ANY WARRANTY; without even the implied warranty of
203+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
204+// Lesser General Public License for more details.
205+
206+// You should have received a copy of the GNU Lesser General Public
207+// License along with this library; if not, write to the Free Software
208+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
209+
210+#ifndef _OXIDE_QT_CORE_BROWSER_WEB_CONTEXT_MENU_H_
211+#define _OXIDE_QT_CORE_BROWSER_WEB_CONTEXT_MENU_H_
212+
213+#include "base/macros.h"
214+#include "base/memory/scoped_ptr.h"
215+
216+#include "qt/core/glue/oxide_qt_web_context_menu_proxy_client.h"
217+#include "shared/browser/oxide_web_context_menu.h"
218+
219+namespace oxide {
220+namespace qt {
221+
222+class WebContextMenuProxy;
223+
224+class WebContextMenu : public oxide::WebContextMenu,
225+ public WebContextMenuProxyClient {
226+ public:
227+ WebContextMenu(content::RenderFrameHost* rfh,
228+ const content::ContextMenuParams& params);
229+
230+ void SetProxy(WebContextMenuProxy* proxy);
231+
232+ private:
233+ ~WebContextMenu() override;
234+
235+ // oxide::WebContextMenu implementation
236+ void Show() override;
237+ void Hide() override;
238+
239+ // WebContextMenuProxyClient implementation
240+ MediaType mediaType() const override;
241+ QPoint position() const override;
242+ QUrl linkUrl() const override;
243+ QString linkText() const override;
244+ QUrl unfilteredLinkUrl() const override;
245+ QUrl srcUrl() const override;
246+ bool hasImageContents() const override;
247+ QUrl pageUrl() const override;
248+ QUrl frameUrl() const override;
249+ QString selectionText() const override;
250+ bool isEditable() const override;
251+ void cancel() override;
252+ int editFlags() const override;
253+ int mediaFlags() const override;
254+ void saveLink() const override;
255+ void saveMedia() const override;
256+
257+ scoped_ptr<WebContextMenuProxy> proxy_;
258+
259+ DISALLOW_COPY_AND_ASSIGN(WebContextMenu);
260+};
261+
262+} // namespace qt
263+} // namespace oxide
264+
265+#endif // _OXIDE_QT_CORE_BROWSER_WEB_CONTEXT_MENU_H_
266
267=== modified file 'qt/core/browser/oxide_qt_web_view.cc'
268--- qt/core/browser/oxide_qt_web_view.cc 2015-05-22 16:06:07 +0000
269+++ qt/core/browser/oxide_qt_web_view.cc 2015-05-26 19:54:24 +0000
270@@ -85,6 +85,7 @@
271 #include "oxide_qt_javascript_dialog.h"
272 #include "oxide_qt_script_message_handler.h"
273 #include "oxide_qt_web_context.h"
274+#include "oxide_qt_web_context_menu.h"
275 #include "oxide_qt_web_frame.h"
276 #include "oxide_qt_web_popup_menu.h"
277 #include "oxide_qt_web_preferences.h"
278@@ -796,6 +797,14 @@
279 return WebFrame::FromProxyHandle(handle);
280 }
281
282+oxide::WebContextMenu* WebView::CreateContextMenu(
283+ content::RenderFrameHost* rfh,
284+ const content::ContextMenuParams& params) {
285+ WebContextMenu* menu = new WebContextMenu(rfh, params);
286+ menu->SetProxy(client_->CreateWebContextMenu(menu));
287+ return menu;
288+}
289+
290 oxide::WebPopupMenu* WebView::CreatePopupMenu(content::RenderFrameHost* rfh) {
291 WebPopupMenu* menu = new WebPopupMenu(rfh);
292 menu->SetProxy(client_->CreateWebPopupMenu(menu));
293@@ -1522,6 +1531,32 @@
294 }
295 }
296
297+void WebView::executeEditingCommand(EditingCommands command) const {
298+ content::WebContents* contents = view_->GetWebContents();
299+ if (!contents) {
300+ return;
301+ }
302+
303+ switch (command) {
304+ case EDITING_COMMAND_UNDO:
305+ return contents->Undo();
306+ case EDITING_COMMAND_REDO:
307+ return contents->Redo();
308+ case EDITING_COMMAND_CUT:
309+ return contents->Cut();
310+ case EDITING_COMMAND_COPY:
311+ return contents->Copy();
312+ case EDITING_COMMAND_PASTE:
313+ return contents->Paste();
314+ case EDITING_COMMAND_ERASE:
315+ return contents->Delete();
316+ case EDITING_COMMAND_SELECT_ALL:
317+ return contents->SelectAll();
318+ default:
319+ NOTREACHED();
320+ }
321+}
322+
323 WebView::WebView(WebViewProxyClient* client)
324 : view_(new oxide::WebView(this)),
325 client_(client),
326
327=== modified file 'qt/core/browser/oxide_qt_web_view.h'
328--- qt/core/browser/oxide_qt_web_view.h 2015-05-22 16:06:07 +0000
329+++ qt/core/browser/oxide_qt_web_view.h 2015-05-26 19:54:24 +0000
330@@ -148,6 +148,9 @@
331 bool user_gesture) override;
332 oxide::WebFrame* CreateWebFrame(
333 content::RenderFrameHost* render_frame_host) override;
334+ oxide::WebContextMenu* CreateContextMenu(
335+ content::RenderFrameHost* rfh,
336+ const content::ContextMenuParams& params) override;
337 oxide::WebPopupMenu* CreatePopupMenu(content::RenderFrameHost* rfh) override;
338 oxide::WebView* CreateNewWebView(const gfx::Rect& initial_pos,
339 WindowOpenDisposition disposition) override;
340@@ -269,6 +272,8 @@
341
342 WebProcessStatus webProcessStatus() const override;
343
344+ void executeEditingCommand(EditingCommands command) const override;
345+
346 scoped_ptr<oxide::WebView> view_;
347
348 WebViewProxyClient* client_;
349
350=== modified file 'qt/core/core.gyp'
351--- qt/core/core.gyp 2015-05-25 13:33:47 +0000
352+++ qt/core/core.gyp 2015-05-26 19:54:24 +0000
353@@ -90,6 +90,8 @@
354 'browser/oxide_qt_user_script.h',
355 'browser/oxide_qt_web_context.cc',
356 'browser/oxide_qt_web_context.h',
357+ 'browser/oxide_qt_web_context_menu.cc',
358+ 'browser/oxide_qt_web_context_menu.h',
359 'browser/oxide_qt_web_frame.cc',
360 'browser/oxide_qt_web_frame.h',
361 'browser/oxide_qt_web_popup_menu.cc',
362@@ -121,6 +123,8 @@
363 'glue/oxide_qt_user_script_proxy.cc',
364 'glue/oxide_qt_user_script_proxy.h',
365 'glue/oxide_qt_user_script_proxy_client.h',
366+ 'glue/oxide_qt_web_context_menu_proxy.h',
367+ 'glue/oxide_qt_web_context_menu_proxy_client.h',
368 'glue/oxide_qt_web_context_proxy.cc',
369 'glue/oxide_qt_web_context_proxy.h',
370 'glue/oxide_qt_web_context_proxy_client.h',
371
372=== added file 'qt/core/glue/oxide_qt_web_context_menu_proxy.h'
373--- qt/core/glue/oxide_qt_web_context_menu_proxy.h 1970-01-01 00:00:00 +0000
374+++ qt/core/glue/oxide_qt_web_context_menu_proxy.h 2015-05-26 19:54:24 +0000
375@@ -0,0 +1,35 @@
376+// vim:expandtab:shiftwidth=2:tabstop=2:
377+// Copyright (C) 2015 Canonical Ltd.
378+
379+// This library is free software; you can redistribute it and/or
380+// modify it under the terms of the GNU Lesser General Public
381+// License as published by the Free Software Foundation; either
382+// version 2.1 of the License, or (at your option) any later version.
383+
384+// This library is distributed in the hope that it will be useful,
385+// but WITHOUT ANY WARRANTY; without even the implied warranty of
386+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
387+// Lesser General Public License for more details.
388+
389+// You should have received a copy of the GNU Lesser General Public
390+// License along with this library; if not, write to the Free Software
391+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
392+
393+#ifndef _OXIDE_QT_CORE_GLUE_WEB_CONTEXT_MENU_PROXY_H_
394+#define _OXIDE_QT_CORE_GLUE_WEB_CONTEXT_MENU_PROXY_H_
395+
396+namespace oxide {
397+namespace qt {
398+
399+class WebContextMenuProxy {
400+ public:
401+ virtual ~WebContextMenuProxy() {}
402+
403+ virtual void Show() = 0;
404+ virtual void Hide() = 0;
405+};
406+
407+} // namespace qt
408+} // namespace oxide
409+
410+#endif // _OXIDE_QT_CORE_GLUE_WEB_CONTEXT_MENU_PROXY_H_
411
412=== added file 'qt/core/glue/oxide_qt_web_context_menu_proxy_client.h'
413--- qt/core/glue/oxide_qt_web_context_menu_proxy_client.h 1970-01-01 00:00:00 +0000
414+++ qt/core/glue/oxide_qt_web_context_menu_proxy_client.h 2015-05-26 19:54:24 +0000
415@@ -0,0 +1,92 @@
416+// vim:expandtab:shiftwidth=2:tabstop=2:
417+// Copyright (C) 2015 Canonical Ltd.
418+
419+// This library is free software; you can redistribute it and/or
420+// modify it under the terms of the GNU Lesser General Public
421+// License as published by the Free Software Foundation; either
422+// version 2.1 of the License, or (at your option) any later version.
423+
424+// This library is distributed in the hope that it will be useful,
425+// but WITHOUT ANY WARRANTY; without even the implied warranty of
426+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
427+// Lesser General Public License for more details.
428+
429+// You should have received a copy of the GNU Lesser General Public
430+// License along with this library; if not, write to the Free Software
431+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
432+
433+#ifndef _OXIDE_QT_CORE_GLUE_WEB_CONTEXT_MENU_PROXY_CLIENT_H_
434+#define _OXIDE_QT_CORE_GLUE_WEB_CONTEXT_MENU_PROXY_CLIENT_H_
435+
436+#include <QPoint>
437+#include <QString>
438+#include <QtGlobal>
439+#include <QUrl>
440+
441+namespace oxide {
442+namespace qt {
443+
444+enum EditCapabilityFlags {
445+ NO_CAPABILITY = 0,
446+ UNDO_CAPABILITY = 1 << 0,
447+ REDO_CAPABILITY = 1 << 1,
448+ CUT_CAPABILITY = 1 << 2,
449+ COPY_CAPABILITY = 1 << 3,
450+ PASTE_CAPABILITY = 1 << 4,
451+ ERASE_CAPABILITY = 1 << 5,
452+ SELECT_ALL_CAPABILITY = 1 << 6
453+};
454+
455+enum MediaType {
456+ MEDIA_TYPE_NONE,
457+ MEDIA_TYPE_IMAGE,
458+ MEDIA_TYPE_VIDEO,
459+ MEDIA_TYPE_AUDIO,
460+ MEDIA_TYPE_CANVAS,
461+ MEDIA_TYPE_PLUGIN
462+};
463+
464+enum MediaStatusFlags {
465+ MEDIA_STATUS_NONE = 0,
466+ MEDIA_STATUS_IN_ERROR = 1 << 0,
467+ MEDIA_STATUS_PAUSED = 1 << 1,
468+ MEDIA_STATUS_MUTED = 1 << 2,
469+ MEDIA_STATUS_LOOP = 1 << 3,
470+ MEDIA_STATUS_CAN_SAVE = 1 << 4,
471+ MEDIA_STATUS_HAS_AUDIO = 1 << 5,
472+ MEDIA_STATUS_CAN_TOGGLE_CONTROLS = 1 << 6,
473+ MEDIA_STATUS_CONTROLS = 1 << 7,
474+ MEDIA_STATUS_CAN_PRINT = 1 << 8,
475+ MEDIA_STATUS_CAN_ROTATE = 1 << 9
476+};
477+
478+class WebContextMenuProxyClient {
479+ public:
480+ virtual ~WebContextMenuProxyClient() {}
481+
482+ virtual MediaType mediaType() const = 0;
483+ virtual QPoint position() const = 0;
484+ virtual QUrl linkUrl() const = 0;
485+ virtual QString linkText() const = 0;
486+ virtual QUrl unfilteredLinkUrl() const = 0;
487+ virtual QUrl srcUrl() const = 0;
488+ virtual bool hasImageContents() const = 0;
489+ virtual QUrl pageUrl() const = 0;
490+ virtual QUrl frameUrl() const = 0;
491+ virtual QString selectionText() const = 0;
492+ virtual bool isEditable() const = 0;
493+
494+ virtual void cancel() = 0;
495+
496+ virtual int editFlags() const = 0;
497+
498+ virtual int mediaFlags() const = 0;
499+
500+ virtual void saveLink() const = 0;
501+ virtual void saveMedia() const = 0;
502+};
503+
504+} // namespace qt
505+} // namespace oxide
506+
507+#endif // _OXIDE_QT_CORE_GLUE_WEB_CONTEXT_MENU_PROXY_CLIENT_H_
508
509=== modified file 'qt/core/glue/oxide_qt_web_view_proxy.h'
510--- qt/core/glue/oxide_qt_web_view_proxy.h 2015-05-19 10:08:59 +0000
511+++ qt/core/glue/oxide_qt_web_view_proxy.h 2015-05-26 19:54:24 +0000
512@@ -83,6 +83,16 @@
513 WEB_PROCESS_CRASHED
514 };
515
516+enum EditingCommands {
517+ EDITING_COMMAND_UNDO,
518+ EDITING_COMMAND_REDO,
519+ EDITING_COMMAND_CUT,
520+ EDITING_COMMAND_COPY,
521+ EDITING_COMMAND_PASTE,
522+ EDITING_COMMAND_ERASE,
523+ EDITING_COMMAND_SELECT_ALL
524+};
525+
526 class CompositorFrameHandle {
527 public:
528 virtual ~CompositorFrameHandle() {}
529@@ -215,6 +225,8 @@
530 virtual void locationBarHide(bool animate) = 0;
531
532 virtual WebProcessStatus webProcessStatus() const = 0;
533+
534+ virtual void executeEditingCommand(EditingCommands command) const = 0;
535 };
536
537 OXIDE_Q_DECL_PROXY_HANDLE(WebViewProxy);
538
539=== modified file 'qt/core/glue/oxide_qt_web_view_proxy_client.h'
540--- qt/core/glue/oxide_qt_web_view_proxy_client.h 2015-05-19 10:08:59 +0000
541+++ qt/core/glue/oxide_qt_web_view_proxy_client.h 2015-05-26 19:54:24 +0000
542@@ -47,6 +47,8 @@
543 class FilePickerProxy;
544 class FilePickerProxyClient;
545 class JavaScriptDialogProxy;
546+class WebContextMenuProxy;
547+class WebContextMenuProxyClient;
548 class WebFrameProxy;
549 class WebPopupMenuProxy;
550 class WebPopupMenuProxyClient;
551@@ -71,6 +73,8 @@
552
553 virtual QObject* GetApiHandle() = 0;
554
555+ virtual WebContextMenuProxy* CreateWebContextMenu(
556+ WebContextMenuProxyClient* client) = 0;
557 virtual WebPopupMenuProxy* CreateWebPopupMenu(
558 WebPopupMenuProxyClient* client) = 0;
559 virtual JavaScriptDialogProxy* CreateJavaScriptDialog(
560
561=== modified file 'qt/quick/CMakeLists.txt'
562--- qt/quick/CMakeLists.txt 2015-04-09 16:41:51 +0000
563+++ qt/quick/CMakeLists.txt 2015-05-26 19:54:24 +0000
564@@ -1,6 +1,6 @@
565 # vim:expandtab:shiftwidth=2:tabstop=2:
566
567-# Copyright (C) 2014 Canonical Ltd.
568+# Copyright (C) 2014-2015 Canonical Ltd.
569
570 # This library is free software; you can redistribute it and/or
571 # modify it under the terms of the GNU Lesser General Public
572@@ -45,6 +45,7 @@
573 oxide_qquick_javascript_dialog.cc
574 oxide_qquick_prompt_dialog.cc
575 oxide_qquick_software_frame_node.cc
576+ oxide_qquick_web_context_menu.cc
577 oxide_qquick_web_popup_menu.cc
578 ${MOC_EXTRA})
579
580
581=== modified file 'qt/quick/api/oxideqquickwebview.cc'
582--- qt/quick/api/oxideqquickwebview.cc 2015-05-19 10:08:59 +0000
583+++ qt/quick/api/oxideqquickwebview.cc 2015-05-26 19:54:24 +0000
584@@ -55,6 +55,7 @@
585 #include "qt/quick/oxide_qquick_init.h"
586 #include "qt/quick/oxide_qquick_prompt_dialog.h"
587 #include "qt/quick/oxide_qquick_software_frame_node.h"
588+#include "qt/quick/oxide_qquick_web_context_menu.h"
589 #include "qt/quick/oxide_qquick_web_popup_menu.h"
590
591 #include "oxideqquicklocationbarcontroller_p.h"
592@@ -225,6 +226,7 @@
593 load_progress_(0),
594 constructed_(false),
595 navigation_history_(view),
596+ context_menu_(nullptr),
597 popup_menu_(nullptr),
598 alert_dialog_(nullptr),
599 confirm_dialog_(nullptr),
600@@ -270,6 +272,13 @@
601 return q;
602 }
603
604+oxide::qt::WebContextMenuProxy* OxideQQuickWebViewPrivate::CreateWebContextMenu(
605+ oxide::qt::WebContextMenuProxyClient* client) {
606+ Q_Q(OxideQQuickWebView);
607+
608+ return new oxide::qquick::WebContextMenu(q, client);
609+}
610+
611 oxide::qt::WebPopupMenuProxy* OxideQQuickWebViewPrivate::CreateWebPopupMenu(
612 oxide::qt::WebPopupMenuProxyClient* client) {
613 Q_Q(OxideQQuickWebView);
614@@ -1387,6 +1396,23 @@
615 d)->proxy()->compositorFrameScrollOffsetPix().y();
616 }
617
618+QQmlComponent* OxideQQuickWebView::contextMenu() const {
619+ Q_D(const OxideQQuickWebView);
620+
621+ return d->context_menu_;
622+}
623+
624+void OxideQQuickWebView::setContextMenu(QQmlComponent* context_menu) {
625+ Q_D(OxideQQuickWebView);
626+
627+ if (d->context_menu_ == context_menu) {
628+ return;
629+ }
630+
631+ d->context_menu_ = context_menu;
632+ emit contextMenuChanged();
633+}
634+
635 QQmlComponent* OxideQQuickWebView::popupMenu() const {
636 Q_D(const OxideQQuickWebView);
637
638@@ -1697,6 +1723,35 @@
639 return new OxideQQuickWebViewAttached(object);
640 }
641
642+void OxideQQuickWebView::executeEditingCommand(EditingCommands command) const {
643+ Q_D(const OxideQQuickWebView);
644+
645+ Q_STATIC_ASSERT(
646+ EditingCommandUndo ==
647+ static_cast<EditingCommands>(oxide::qt::EDITING_COMMAND_UNDO));
648+ Q_STATIC_ASSERT(
649+ EditingCommandRedo ==
650+ static_cast<EditingCommands>(oxide::qt::EDITING_COMMAND_REDO));
651+ Q_STATIC_ASSERT(
652+ EditingCommandCut ==
653+ static_cast<EditingCommands>(oxide::qt::EDITING_COMMAND_CUT));
654+ Q_STATIC_ASSERT(
655+ EditingCommandCopy ==
656+ static_cast<EditingCommands>(oxide::qt::EDITING_COMMAND_COPY));
657+ Q_STATIC_ASSERT(
658+ EditingCommandPaste ==
659+ static_cast<EditingCommands>(oxide::qt::EDITING_COMMAND_PASTE));
660+ Q_STATIC_ASSERT(
661+ EditingCommandErase ==
662+ static_cast<EditingCommands>(oxide::qt::EDITING_COMMAND_ERASE));
663+ Q_STATIC_ASSERT(
664+ EditingCommandSelectAll ==
665+ static_cast<EditingCommands>(oxide::qt::EDITING_COMMAND_SELECT_ALL));
666+
667+ d->proxy()->executeEditingCommand(
668+ static_cast<oxide::qt::EditingCommands>(command));
669+}
670+
671 void OxideQQuickWebView::goBack() {
672 Q_D(OxideQQuickWebView);
673
674
675=== modified file 'qt/quick/api/oxideqquickwebview_p.h'
676--- qt/quick/api/oxideqquickwebview_p.h 2015-05-19 10:08:59 +0000
677+++ qt/quick/api/oxideqquickwebview_p.h 2015-05-26 19:54:24 +0000
678@@ -1,5 +1,5 @@
679 // vim:expandtab:shiftwidth=2:tabstop=2:
680-// Copyright (C) 2013 Canonical Ltd.
681+// Copyright (C) 2013-2015 Canonical Ltd.
682
683 // This library is free software; you can redistribute it and/or
684 // modify it under the terms of the GNU Lesser General Public
685@@ -71,7 +71,11 @@
686 Q_OBJECT
687
688 Q_FLAGS(ContentType)
689+ Q_FLAGS(EditCapabilities)
690+ Q_FLAGS(MediaStatus)
691+ Q_ENUMS(EditingCommands);
692 Q_ENUMS(LogMessageSeverityLevel);
693+ Q_ENUMS(MediaType);
694 Q_ENUMS(RestoreType);
695 Q_ENUMS(WebProcessStatus);
696
697@@ -94,6 +98,7 @@
698 Q_PROPERTY(qreal contentX READ contentX NOTIFY contentXChanged)
699 Q_PROPERTY(qreal contentY READ contentY NOTIFY contentYChanged)
700
701+ Q_PROPERTY(QQmlComponent* contextMenu READ contextMenu WRITE setContextMenu NOTIFY contextMenuChanged REVISION 4)
702 Q_PROPERTY(QQmlComponent* popupMenu READ popupMenu WRITE setPopupMenu NOTIFY popupMenuChanged)
703
704 Q_PROPERTY(QQmlComponent* alertDialog READ alertDialog WRITE setAlertDialog NOTIFY alertDialogChanged)
705@@ -163,6 +168,52 @@
706 WebProcessCrashed
707 };
708
709+ enum EditCapabilityFlags {
710+ NoCapability = 0,
711+ UndoCapability = 1 << 0,
712+ RedoCapability = 1 << 1,
713+ CutCapability = 1 << 2,
714+ CopyCapability = 1 << 3,
715+ PasteCapability = 1 << 4,
716+ EraseCapability = 1 << 5,
717+ SelectAllCapability = 1 << 6
718+ };
719+ Q_DECLARE_FLAGS(EditCapabilities, EditCapabilityFlags)
720+
721+ enum MediaType {
722+ MediaTypeNone,
723+ MediaTypeImage,
724+ MediaTypeVideo,
725+ MediaTypeAudio,
726+ MediaTypeCanvas,
727+ MediaTypePlugin
728+ };
729+
730+ enum MediaStatusFlags {
731+ MediaStatusNone = 0,
732+ MediaStatusInError = 1 << 0,
733+ MediaStatusPaused = 1 << 1,
734+ MediaStatusMuted = 1 << 2,
735+ MediaStatusLoop = 1 << 3,
736+ MediaStatusCanSave = 1 << 4,
737+ MediaStatusHasAudio = 1 << 5,
738+ MediaStatusCanToggleControls = 1 << 6,
739+ MediaStatusControls = 1 << 7,
740+ MediaStatusCanPrint = 1 << 8,
741+ MediaStatusCanRotate = 1 << 9
742+ };
743+ Q_DECLARE_FLAGS(MediaStatus, MediaStatusFlags)
744+
745+ enum EditingCommands {
746+ EditingCommandUndo,
747+ EditingCommandRedo,
748+ EditingCommandCut,
749+ EditingCommandCopy,
750+ EditingCommandPaste,
751+ EditingCommandErase,
752+ EditingCommandSelectAll
753+ };
754+
755 void componentComplete();
756
757 QUrl url() const;
758@@ -198,6 +249,9 @@
759 qreal contentX() const;
760 qreal contentY() const;
761
762+ QQmlComponent* contextMenu() const;
763+ void setContextMenu(QQmlComponent* context_menu);
764+
765 QQmlComponent* popupMenu() const;
766 void setPopupMenu(QQmlComponent* popup_menu);
767
768@@ -244,6 +298,8 @@
769
770 OxideQFindController* findController() const;
771
772+ Q_REVISION(4) Q_INVOKABLE void executeEditingCommand(EditingCommands command) const;
773+
774 public Q_SLOTS:
775 void goBack();
776 void goForward();
777@@ -269,6 +325,7 @@
778 void rootFrameChanged();
779 void frameAdded(OxideQQuickWebFrame* frame);
780 void frameRemoved(OxideQQuickWebFrame* frame);
781+ Q_REVISION(4) void contextMenuChanged();
782 void popupMenuChanged();
783 void alertDialogChanged();
784 void confirmDialogChanged();
785
786=== modified file 'qt/quick/api/oxideqquickwebview_p_p.h'
787--- qt/quick/api/oxideqquickwebview_p_p.h 2015-05-19 10:08:59 +0000
788+++ qt/quick/api/oxideqquickwebview_p_p.h 2015-05-26 19:54:24 +0000
789@@ -1,5 +1,5 @@
790 // vim:expandtab:shiftwidth=2:tabstop=2:
791-// Copyright (C) 2013 Canonical Ltd.
792+// Copyright (C) 2013-2015 Canonical Ltd.
793
794 // This library is free software; you can redistribute it and/or
795 // modify it under the terms of the GNU Lesser General Public
796@@ -84,6 +84,8 @@
797 // oxide::qt::WebViewProxyClient implementation
798 void Initialized() override;
799 QObject* GetApiHandle() override;
800+ oxide::qt::WebContextMenuProxy* CreateWebContextMenu(
801+ oxide::qt::WebContextMenuProxyClient* client) override;
802 oxide::qt::WebPopupMenuProxy* CreateWebPopupMenu(
803 oxide::qt::WebPopupMenuProxyClient* client) override;
804 oxide::qt::JavaScriptDialogProxy* CreateJavaScriptDialog(
805@@ -176,6 +178,7 @@
806 int load_progress_;
807 QUrl icon_;
808 OxideQQuickNavigationHistory navigation_history_;
809+ QQmlComponent* context_menu_;
810 QQmlComponent* popup_menu_;
811 QQmlComponent* alert_dialog_;
812 QQmlComponent* confirm_dialog_;
813
814=== added file 'qt/quick/oxide_qquick_web_context_menu.cc'
815--- qt/quick/oxide_qquick_web_context_menu.cc 1970-01-01 00:00:00 +0000
816+++ qt/quick/oxide_qquick_web_context_menu.cc 2015-05-26 19:54:24 +0000
817@@ -0,0 +1,311 @@
818+// vim:expandtab:shiftwidth=2:tabstop=2:
819+// Copyright (C) 2015 Canonical Ltd.
820+
821+// This library is free software; you can redistribute it and/or
822+// modify it under the terms of the GNU Lesser General Public
823+// License as published by the Free Software Foundation; either
824+// version 2.1 of the License, or (at your option) any later version.
825+
826+// This library is distributed in the hope that it will be useful,
827+// but WITHOUT ANY WARRANTY; without even the implied warranty of
828+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
829+// Lesser General Public License for more details.
830+
831+// You should have received a copy of the GNU Lesser General Public
832+// License along with this library; if not, write to the Free Software
833+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
834+
835+#include "oxide_qquick_web_context_menu.h"
836+
837+#include <QPointF>
838+#include <QObject>
839+#include <QQmlComponent>
840+#include <QQmlContext>
841+#include <QQmlEngine>
842+#include <QQuickItem>
843+#include <QtDebug>
844+#include <QUrl>
845+
846+#include "qt/core/glue/oxide_qt_web_context_menu_proxy_client.h"
847+#include "qt/quick/api/oxideqquickwebview_p.h"
848+#include "qt/quick/api/oxideqquickwebview_p_p.h"
849+
850+namespace oxide {
851+namespace qquick {
852+
853+namespace {
854+
855+class ContextMenuContext : public QObject {
856+ Q_OBJECT
857+
858+ Q_PROPERTY(OxideQQuickWebView::MediaType mediaType READ mediaType CONSTANT FINAL)
859+ Q_PROPERTY(QPointF position READ position CONSTANT FINAL)
860+ Q_PROPERTY(QUrl linkUrl READ linkUrl CONSTANT FINAL)
861+ Q_PROPERTY(QString linkText READ linkText CONSTANT FINAL)
862+ Q_PROPERTY(QUrl unfilteredLinkUrl READ unfilteredLinkUrl CONSTANT FINAL)
863+ Q_PROPERTY(QUrl srcUrl READ srcUrl CONSTANT FINAL)
864+ Q_PROPERTY(bool hasImageContents READ hasImageContents CONSTANT FINAL)
865+ Q_PROPERTY(QUrl pageUrl READ pageUrl CONSTANT FINAL)
866+ Q_PROPERTY(QUrl frameUrl READ frameUrl CONSTANT FINAL)
867+ Q_PROPERTY(QString selectionText READ selectionText CONSTANT FINAL)
868+ Q_PROPERTY(bool isEditable READ isEditable CONSTANT FINAL)
869+ Q_PROPERTY(OxideQQuickWebView::EditCapabilities editFlags READ editFlags CONSTANT FINAL)
870+ Q_PROPERTY(OxideQQuickWebView::MediaStatus mediaFlags READ mediaFlags CONSTANT FINAL)
871+
872+ public:
873+ virtual ~ContextMenuContext() {}
874+ ContextMenuContext(oxide::qt::WebContextMenuProxyClient* client);
875+
876+ OxideQQuickWebView::MediaType mediaType() const;
877+ QPointF position() const;
878+ QUrl linkUrl() const;
879+ QString linkText() const;
880+ QUrl unfilteredLinkUrl() const;
881+ QUrl srcUrl() const;
882+ bool hasImageContents() const;
883+ QUrl pageUrl() const;
884+ QUrl frameUrl() const;
885+ QString selectionText() const;
886+ bool isEditable() const;
887+ OxideQQuickWebView::EditCapabilities editFlags() const;
888+ OxideQQuickWebView::MediaStatus mediaFlags() const;
889+
890+ Q_INVOKABLE void saveLink() const;
891+ Q_INVOKABLE void saveMedia() const;
892+
893+ Q_INVOKABLE void close();
894+
895+ private:
896+ oxide::qt::WebContextMenuProxyClient* client_;
897+};
898+
899+ContextMenuContext::ContextMenuContext(
900+ oxide::qt::WebContextMenuProxyClient* client) :
901+ client_(client) {}
902+
903+OxideQQuickWebView::MediaType ContextMenuContext::mediaType() const {
904+ Q_STATIC_ASSERT(
905+ OxideQQuickWebView::MediaTypeNone ==
906+ static_cast<OxideQQuickWebView::MediaType>(oxide::qt::MEDIA_TYPE_NONE));
907+ Q_STATIC_ASSERT(
908+ OxideQQuickWebView::MediaTypeImage ==
909+ static_cast<OxideQQuickWebView::MediaType>(oxide::qt::MEDIA_TYPE_IMAGE));
910+ Q_STATIC_ASSERT(
911+ OxideQQuickWebView::MediaTypeVideo ==
912+ static_cast<OxideQQuickWebView::MediaType>(oxide::qt::MEDIA_TYPE_VIDEO));
913+ Q_STATIC_ASSERT(
914+ OxideQQuickWebView::MediaTypeAudio ==
915+ static_cast<OxideQQuickWebView::MediaType>(oxide::qt::MEDIA_TYPE_AUDIO));
916+ Q_STATIC_ASSERT(
917+ OxideQQuickWebView::MediaTypeCanvas ==
918+ static_cast<OxideQQuickWebView::MediaType>(oxide::qt::MEDIA_TYPE_CANVAS));
919+ Q_STATIC_ASSERT(
920+ OxideQQuickWebView::MediaTypePlugin ==
921+ static_cast<OxideQQuickWebView::MediaType>(oxide::qt::MEDIA_TYPE_PLUGIN));
922+
923+ return static_cast<OxideQQuickWebView::MediaType>(client_->mediaType());
924+}
925+
926+QPointF ContextMenuContext::position() const {
927+ return client_->position();
928+}
929+
930+QUrl ContextMenuContext::linkUrl() const {
931+ return client_->linkUrl();
932+}
933+
934+QString ContextMenuContext::linkText() const {
935+ return client_->linkText();
936+}
937+
938+QUrl ContextMenuContext::unfilteredLinkUrl() const {
939+ return client_->unfilteredLinkUrl();
940+}
941+
942+QUrl ContextMenuContext::srcUrl() const {
943+ return client_->srcUrl();
944+}
945+
946+bool ContextMenuContext::hasImageContents() const {
947+ return client_->hasImageContents();
948+}
949+
950+QUrl ContextMenuContext::pageUrl() const {
951+ return client_->pageUrl();
952+}
953+
954+QUrl ContextMenuContext::frameUrl() const {
955+ return client_->frameUrl();
956+}
957+
958+QString ContextMenuContext::selectionText() const {
959+ return client_->selectionText();
960+}
961+
962+bool ContextMenuContext::isEditable() const {
963+ return client_->isEditable();
964+}
965+
966+OxideQQuickWebView::EditCapabilities ContextMenuContext::editFlags() const {
967+ Q_STATIC_ASSERT(
968+ OxideQQuickWebView::NoCapability ==
969+ static_cast<OxideQQuickWebView::EditCapabilityFlags>(oxide::qt::NO_CAPABILITY));
970+ Q_STATIC_ASSERT(
971+ OxideQQuickWebView::UndoCapability ==
972+ static_cast<OxideQQuickWebView::EditCapabilityFlags>(oxide::qt::UNDO_CAPABILITY));
973+ Q_STATIC_ASSERT(
974+ OxideQQuickWebView::RedoCapability ==
975+ static_cast<OxideQQuickWebView::EditCapabilityFlags>(oxide::qt::REDO_CAPABILITY));
976+ Q_STATIC_ASSERT(
977+ OxideQQuickWebView::CutCapability ==
978+ static_cast<OxideQQuickWebView::EditCapabilityFlags>(oxide::qt::CUT_CAPABILITY));
979+ Q_STATIC_ASSERT(
980+ OxideQQuickWebView::CopyCapability ==
981+ static_cast<OxideQQuickWebView::EditCapabilityFlags>(oxide::qt::COPY_CAPABILITY));
982+ Q_STATIC_ASSERT(
983+ OxideQQuickWebView::PasteCapability ==
984+ static_cast<OxideQQuickWebView::EditCapabilityFlags>(oxide::qt::PASTE_CAPABILITY));
985+ Q_STATIC_ASSERT(
986+ OxideQQuickWebView::EraseCapability ==
987+ static_cast<OxideQQuickWebView::EditCapabilityFlags>(oxide::qt::ERASE_CAPABILITY));
988+ Q_STATIC_ASSERT(
989+ OxideQQuickWebView::SelectAllCapability ==
990+ static_cast<OxideQQuickWebView::EditCapabilityFlags>(oxide::qt::SELECT_ALL_CAPABILITY));
991+
992+ return static_cast<OxideQQuickWebView::EditCapabilities>(client_->editFlags());
993+}
994+
995+OxideQQuickWebView::MediaStatus ContextMenuContext::mediaFlags() const {
996+ Q_STATIC_ASSERT(
997+ OxideQQuickWebView::MediaStatusNone ==
998+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_NONE));
999+ Q_STATIC_ASSERT(
1000+ OxideQQuickWebView::MediaStatusInError ==
1001+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_IN_ERROR));
1002+ Q_STATIC_ASSERT(
1003+ OxideQQuickWebView::MediaStatusPaused ==
1004+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_PAUSED));
1005+ Q_STATIC_ASSERT(
1006+ OxideQQuickWebView::MediaStatusMuted ==
1007+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_MUTED));
1008+ Q_STATIC_ASSERT(
1009+ OxideQQuickWebView::MediaStatusLoop ==
1010+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_LOOP));
1011+ Q_STATIC_ASSERT(
1012+ OxideQQuickWebView::MediaStatusCanSave ==
1013+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_CAN_SAVE));
1014+ Q_STATIC_ASSERT(
1015+ OxideQQuickWebView::MediaStatusHasAudio ==
1016+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_HAS_AUDIO));
1017+ Q_STATIC_ASSERT(
1018+ OxideQQuickWebView::MediaStatusCanToggleControls ==
1019+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_CAN_TOGGLE_CONTROLS));
1020+ Q_STATIC_ASSERT(
1021+ OxideQQuickWebView::MediaStatusControls ==
1022+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_CONTROLS));
1023+ Q_STATIC_ASSERT(
1024+ OxideQQuickWebView::MediaStatusCanPrint ==
1025+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_CAN_PRINT));
1026+ Q_STATIC_ASSERT(
1027+ OxideQQuickWebView::MediaStatusCanRotate ==
1028+ static_cast<OxideQQuickWebView::MediaStatusFlags>(oxide::qt::MEDIA_STATUS_CAN_ROTATE));
1029+
1030+ return static_cast<OxideQQuickWebView::MediaStatus>(client_->mediaFlags());
1031+}
1032+
1033+void ContextMenuContext::saveLink() const {
1034+ if (linkUrl().isValid()) {
1035+ client_->saveLink();
1036+ } else {
1037+ qWarning() << "ContextMenuContext::saveLink(): not a valid link";
1038+ }
1039+}
1040+
1041+void ContextMenuContext::saveMedia() const {
1042+ OxideQQuickWebView::MediaType media_type = mediaType();
1043+ if ((media_type == OxideQQuickWebView::MediaTypeImage) ||
1044+ (media_type == OxideQQuickWebView::MediaTypeCanvas)) {
1045+ if (!hasImageContents()) {
1046+ qWarning() << "ContextMenuContext::saveMedia(): image has no contents";
1047+ return;
1048+ }
1049+ } else if ((media_type == OxideQQuickWebView::MediaTypeVideo) ||
1050+ (media_type == OxideQQuickWebView::MediaTypeAudio)) {
1051+ if (!mediaFlags().testFlag(OxideQQuickWebView::MediaStatusCanSave)) {
1052+ qWarning() << "ContextMenuContext::saveMedia(): cannot save media source";
1053+ return;
1054+ }
1055+ } else {
1056+ qWarning() << "ContextMenuContext::saveMedia(): invalid content";
1057+ return;
1058+ }
1059+
1060+ client_->saveMedia();
1061+}
1062+
1063+void ContextMenuContext::close() {
1064+ client_->cancel();
1065+}
1066+
1067+} // namespace
1068+
1069+void WebContextMenu::Show() {
1070+ if (!view_) {
1071+ qWarning() << "WebContextMenu::Show: Can't show after the view has gone";
1072+ client_->cancel();
1073+ return;
1074+ }
1075+
1076+ QQmlComponent* component = view_->contextMenu();
1077+ if (!component) {
1078+ qWarning() <<
1079+ "WebContextMenu::Show: Content requested a context menu, but the "
1080+ "application hasn't provided one";
1081+ client_->cancel();
1082+ return;
1083+ }
1084+
1085+ ContextMenuContext* contextObject = new ContextMenuContext(client_);
1086+
1087+ QQmlContext* baseContext = component->creationContext();
1088+ if (!baseContext) {
1089+ baseContext = QQmlEngine::contextForObject(view_);
1090+ }
1091+ context_.reset(new QQmlContext(baseContext));
1092+
1093+ context_->setContextProperty(QLatin1String("model"), contextObject);
1094+ context_->setContextObject(contextObject);
1095+ contextObject->setParent(context_.data());
1096+
1097+ item_.reset(qobject_cast<QQuickItem*>(
1098+ component->beginCreate(context_.data())));
1099+ if (!item_) {
1100+ qWarning() <<
1101+ "WebContextMenu::Show: Failed to create instance of Qml context menu component";
1102+ client_->cancel();
1103+ return;
1104+ }
1105+
1106+ OxideQQuickWebViewPrivate::get(view_)->addAttachedPropertyTo(item_.data());
1107+ item_->setParentItem(view_);
1108+
1109+ component->completeCreate();
1110+}
1111+
1112+void WebContextMenu::Hide() {
1113+ if (item_) {
1114+ item_->setVisible(false);
1115+ }
1116+}
1117+
1118+WebContextMenu::WebContextMenu(OxideQQuickWebView* view,
1119+ oxide::qt::WebContextMenuProxyClient* client)
1120+ : client_(client),
1121+ view_(view) {}
1122+
1123+WebContextMenu::~WebContextMenu() {}
1124+
1125+} // namespace qquick
1126+} // namespace oxide
1127+
1128+#include "oxide_qquick_web_context_menu.moc"
1129
1130=== added file 'qt/quick/oxide_qquick_web_context_menu.h'
1131--- qt/quick/oxide_qquick_web_context_menu.h 1970-01-01 00:00:00 +0000
1132+++ qt/quick/oxide_qquick_web_context_menu.h 2015-05-26 19:54:24 +0000
1133@@ -0,0 +1,61 @@
1134+// vim:expandtab:shiftwidth=2:tabstop=2:
1135+// Copyright (C) 2015 Canonical Ltd.
1136+
1137+// This library is free software; you can redistribute it and/or
1138+// modify it under the terms of the GNU Lesser General Public
1139+// License as published by the Free Software Foundation; either
1140+// version 2.1 of the License, or (at your option) any later version.
1141+
1142+// This library is distributed in the hope that it will be useful,
1143+// but WITHOUT ANY WARRANTY; without even the implied warranty of
1144+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1145+// Lesser General Public License for more details.
1146+
1147+// You should have received a copy of the GNU Lesser General Public
1148+// License along with this library; if not, write to the Free Software
1149+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1150+
1151+#ifndef _OXIDE_QT_QUICK_WEB_CONTEXT_MENU_H_
1152+#define _OXIDE_QT_QUICK_WEB_CONTEXT_MENU_H_
1153+
1154+#include <QPointer>
1155+#include <QScopedPointer>
1156+
1157+#include "qt/core/glue/oxide_qt_web_context_menu_proxy.h"
1158+
1159+class OxideQQuickWebView;
1160+
1161+QT_BEGIN_NAMESPACE
1162+class QQmlContext;
1163+class QQuickItem;
1164+QT_END_NAMESPACE
1165+
1166+namespace oxide {
1167+
1168+namespace qt {
1169+class WebContextMenuProxyClient;
1170+}
1171+
1172+namespace qquick {
1173+
1174+class WebContextMenu : public oxide::qt::WebContextMenuProxy {
1175+ public:
1176+ WebContextMenu(OxideQQuickWebView* view,
1177+ oxide::qt::WebContextMenuProxyClient* client);
1178+ ~WebContextMenu() override;
1179+
1180+ private:
1181+ // oxide::qt::WebContextMenuProxy implementation
1182+ void Show() override;
1183+ void Hide() override;
1184+
1185+ oxide::qt::WebContextMenuProxyClient* client_;
1186+ QPointer<OxideQQuickWebView> view_;
1187+ QScopedPointer<QQuickItem> item_;
1188+ QScopedPointer<QQmlContext> context_;
1189+};
1190+
1191+} // namespace qquick
1192+} // namespace oxide
1193+
1194+#endif // _OXIDE_QT_QUICK_WEB_CONTEXT_MENU_H_
1195
1196=== added file 'qt/tests/qmltests/api/buddha.mp4'
1197Binary files qt/tests/qmltests/api/buddha.mp4 1970-01-01 00:00:00 +0000 and qt/tests/qmltests/api/buddha.mp4 2015-05-26 19:54:24 +0000 differ
1198=== added file 'qt/tests/qmltests/api/cof.svg'
1199--- qt/tests/qmltests/api/cof.svg 1970-01-01 00:00:00 +0000
1200+++ qt/tests/qmltests/api/cof.svg 2015-05-26 19:54:24 +0000
1201@@ -0,0 +1,27 @@
1202+<?xml version="1.0" encoding="utf-8"?>
1203+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In -->
1204+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
1205+ <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
1206+]>
1207+<svg version="1.1"
1208+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
1209+ x="0px" y="0px" width="285px" height="285px" viewBox="-0.866 -0.866 285 285" enable-background="new -0.866 -0.866 285 285"
1210+ xml:space="preserve">
1211+<defs>
1212+</defs>
1213+<path fill="#DD4814" d="M283.465,141.734c0,78.273-63.457,141.73-141.734,141.73S0,220.008,0,141.734C0,63.455,63.453,0,141.73,0
1214+ S283.465,63.455,283.465,141.734z"/>
1215+<path fill="#FFFFFF" d="M45.356,122.812c-10.453,0-18.923,8.47-18.923,18.923c0,10.449,8.47,18.92,18.923,18.92
1216+ c10.449,0,18.92-8.471,18.92-18.92C64.276,131.281,55.806,122.812,45.356,122.812z M180.463,208.814
1217+ c-9.051,5.225-12.149,16.793-6.926,25.84c5.226,9.051,16.793,12.151,25.844,6.926c9.048-5.224,12.148-16.792,6.923-25.842
1218+ C201.08,206.691,189.511,203.59,180.463,208.814z M86.458,141.732c0-18.701,9.293-35.219,23.504-45.221L96.128,73.338
1219+ c-16.56,11.064-28.878,27.978-33.995,47.788c5.977,4.872,9.796,12.291,9.796,20.608c0,8.315-3.819,15.734-9.797,20.605
1220+ c5.116,19.812,17.435,36.726,33.995,47.789l13.835-23.175C95.751,176.953,86.458,160.436,86.458,141.732z M141.733,86.457
1221+ c28.877,0,52.564,22.141,55.047,50.373l26.968-0.394c-1.327-20.844-10.432-39.562-24.425-53.319
1222+ c-7.194,2.718-15.505,2.306-22.688-1.842c-7.192-4.152-11.705-11.156-12.941-18.757c-6.992-1.935-14.351-2.99-21.96-2.99
1223+ c-13.086,0-25.449,3.072-36.431,8.512l13.146,23.56C125.526,88.307,133.412,86.457,141.733,86.457z M141.733,197.008
1224+ c-8.322,0-16.207-1.85-23.285-5.143L105.3,215.427c10.983,5.438,23.347,8.511,36.433,8.511c7.609,0,14.968-1.055,21.961-2.99
1225+ c1.236-7.601,5.75-14.605,12.943-18.76c7.183-4.146,15.494-4.558,22.688-1.839c13.992-13.758,23.097-32.476,24.422-53.32
1226+ l-26.968-0.394C194.298,174.871,170.61,197.008,141.733,197.008z M180.46,74.649c9.05,5.227,20.619,2.126,25.842-6.921
1227+ c5.226-9.051,2.128-20.619-6.923-25.845c-9.049-5.224-20.617-2.124-25.843,6.927C168.312,57.857,171.412,69.426,180.46,74.649z"/>
1228+</svg>
1229
1230=== added file 'qt/tests/qmltests/api/fire.oga'
1231Binary files qt/tests/qmltests/api/fire.oga 1970-01-01 00:00:00 +0000 and qt/tests/qmltests/api/fire.oga 2015-05-26 19:54:24 +0000 differ
1232=== added file 'qt/tests/qmltests/api/tst_WebView_contextMenu.html'
1233--- qt/tests/qmltests/api/tst_WebView_contextMenu.html 1970-01-01 00:00:00 +0000
1234+++ qt/tests/qmltests/api/tst_WebView_contextMenu.html 2015-05-26 19:54:24 +0000
1235@@ -0,0 +1,13 @@
1236+<html>
1237+<body>
1238+ <div id="text">some text</div>
1239+ <a id="hyperlink" href="empty.html" title="hyper-link">super-link</a>
1240+ <img id="image" src="cof.svg" alt="image" width="20px" height="20px" />
1241+ <canvas id="canvas" width="20px" height="20px"></canvas>
1242+ <textarea id="editable" rows="2" cols="10">text area</textarea>
1243+ <iframe id="iframe" src="empty.html" style="border: 0; width: 20px; height:20px;"></iframe>
1244+ <a href="empty.html"><img id="imagelink" src="cof.svg" width="20px" height="20px" /></a>
1245+ <video id="video" width="20px" height="20px" src="buddha.mp4" loop="true"></video>
1246+ <audio id="audio" src="fire.oga" controls="controls" style="width: 20px; height:20px;"></audio>
1247+</body>
1248+</html>
1249
1250=== added file 'qt/tests/qmltests/api/tst_WebView_contextMenu.qml'
1251--- qt/tests/qmltests/api/tst_WebView_contextMenu.qml 1970-01-01 00:00:00 +0000
1252+++ qt/tests/qmltests/api/tst_WebView_contextMenu.qml 2015-05-26 19:54:24 +0000
1253@@ -0,0 +1,215 @@
1254+import QtQuick 2.0
1255+import QtTest 1.0
1256+import com.canonical.Oxide 1.8
1257+import com.canonical.Oxide.Testing 1.0
1258+
1259+TestWebView {
1260+ id: webView
1261+ focus: true
1262+ width: 200
1263+ height: 200
1264+
1265+ property var currentContextMenu: null
1266+ property url downloadUrl
1267+ property string downloadReferrer
1268+
1269+ contextMenu: Item {
1270+ property var contextModel: model
1271+ Component.onCompleted: WebView.view.currentContextMenu = this;
1272+ Component.onDestruction: WebView.view.currentContextMenu = null;
1273+ }
1274+
1275+ function waitForContextMenu() {
1276+ return waitFor(function() { return currentContextMenu != null; });
1277+ }
1278+
1279+ onDownloadRequested: {
1280+ downloadUrl = request.url;
1281+ downloadReferrer = request.referrer;
1282+ }
1283+
1284+ SignalSpy {
1285+ id: spy
1286+ target: webView
1287+ signalName: "downloadRequested"
1288+ }
1289+
1290+ TestCase {
1291+ name: "WebView_contextMenu"
1292+ when: windowShown
1293+
1294+ function init() {
1295+ webView.currentContextMenu = null;
1296+
1297+ webView.url = "http://testsuite/tst_WebView_contextMenu.html";
1298+ verify(webView.waitForLoadSucceeded(),
1299+ "Timed out waiting for successful load");
1300+
1301+ spy.clear();
1302+ }
1303+
1304+ function cleanup() {
1305+ webView.currentContextMenu.contextModel.close()
1306+ tryCompare(webView, "currentContextMenu", null);
1307+ }
1308+
1309+ function invokeContextMenu(id) {
1310+ var r = webView.getTestApi().getBoundingClientRectForSelector("#" + id);
1311+ var x = r.x + r.width / 2;
1312+ var y = r.y + r.height / 2;
1313+ mouseClick(webView, x, y, Qt.RightButton);
1314+ verify(webView.waitForContextMenu(),
1315+ "Timed out waiting for context menu to show");
1316+ var model = webView.currentContextMenu.contextModel;
1317+ compare(model.position, Qt.point(x, y));
1318+ compare(model.pageUrl, "http://testsuite/tst_WebView_contextMenu.html");
1319+ }
1320+
1321+ function test_WebView_contextMenu_properties_data() {
1322+ return [
1323+ { id: "text", mediaType: WebView.MediaTypeNone, linkUrl: "", linkText: "", srcUrl: "", frameUrl: "", isEditable: false },
1324+ { id: "hyperlink", mediaType: WebView.MediaTypeNone, linkUrl: "http://testsuite/empty.html", linkText: "super-link", srcUrl: "", frameUrl: "", isEditable: false },
1325+ { id: "image", mediaType: WebView.MediaTypeImage, linkUrl: "", linkText: "", srcUrl: "http://testsuite/cof.svg", frameUrl: "", isEditable: false },
1326+ { id: "canvas", mediaType: WebView.MediaTypeCanvas, linkUrl: "", linkText: "", srcUrl: "", frameUrl: "", isEditable: false },
1327+ { id: "editable", mediaType: WebView.MediaTypeNone, linkUrl: "", linkText: "", srcUrl: "", frameUrl: "", isEditable: true },
1328+ { id: "imagelink", mediaType: WebView.MediaTypeImage, linkUrl: "http://testsuite/empty.html", linkText: "", srcUrl: "http://testsuite/cof.svg", frameUrl: "", isEditable: false },
1329+ { id: "iframe", mediaType: WebView.MediaTypeNone, linkUrl: "", linkText: "", srcUrl: "", frameUrl: "http://testsuite/empty.html", isEditable: false },
1330+ { id: "video", mediaType: WebView.MediaTypeVideo, linkUrl: "", linkText: "", srcUrl: "http://testsuite/buddha.mp4", frameUrl: "", isEditable: false },
1331+ { id: "audio", mediaType: WebView.MediaTypeAudio, linkUrl: "", linkText: "", srcUrl: "http://testsuite/fire.oga", frameUrl: "", isEditable: false },
1332+ ];
1333+ }
1334+
1335+ function test_WebView_contextMenu_properties(data) {
1336+ invokeContextMenu(data.id);
1337+ var model = webView.currentContextMenu.contextModel;
1338+ compare(model.mediaType, data.mediaType);
1339+ compare(model.linkUrl, data.linkUrl);
1340+ compare(model.linkText, data.linkText);
1341+ compare(model.srcUrl, data.srcUrl);
1342+ if ((model.mediaType == WebView.MediaTypeImage) ||
1343+ (model.mediaType == WebView.MediaTypeCanvas)) {
1344+ verify(model.hasImageContents);
1345+ }
1346+ compare(model.frameUrl, data.frameUrl);
1347+ compare(model.isEditable, data.isEditable);
1348+ }
1349+
1350+ function test_WebView_contextMenu_saveLink() {
1351+ invokeContextMenu("hyperlink");
1352+ var model = webView.currentContextMenu.contextModel;
1353+ model.saveLink();
1354+ spy.wait();
1355+ compare(webView.downloadUrl, "http://testsuite/empty.html");
1356+ compare(webView.downloadReferrer,
1357+ "http://testsuite/tst_WebView_contextMenu.html");
1358+ }
1359+
1360+ function test_WebView_contextMenu_saveMedia_data() {
1361+ return [
1362+ { id: "image", url: "http://testsuite/cof.svg", referrer: "http://testsuite/tst_WebView_contextMenu.html" },
1363+ { id: "canvas", url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAKElEQVQ4T2NkoDJgpLJ5DKMGUh6io2E4GoZkhMBosiEj0NC0jMAwBABIxgAVO+SUsAAAAABJRU5ErkJggg==", referrer: "" },
1364+ { id: "video", url: "http://testsuite/buddha.mp4", referrer: "http://testsuite/tst_WebView_contextMenu.html" },
1365+ { id: "audio", url: "http://testsuite/fire.oga", referrer: "http://testsuite/tst_WebView_contextMenu.html" },
1366+ ];
1367+ }
1368+
1369+ function test_WebView_contextMenu_saveMedia(data) {
1370+ invokeContextMenu(data.id);
1371+ var model = webView.currentContextMenu.contextModel;
1372+ model.saveMedia();
1373+ spy.wait();
1374+ compare(webView.downloadUrl, data.url);
1375+ compare(webView.downloadReferrer, data.referrer);
1376+ }
1377+
1378+ function test_WebView_contextMenu_editable() {
1379+ invokeContextMenu("editable");
1380+ var model = webView.currentContextMenu.contextModel;
1381+ compare(model.selectionText, "");
1382+ verify(!(model.editFlags & WebView.UndoCapability));
1383+ verify(!(model.editFlags & WebView.RedoCapability));
1384+ verify(!(model.editFlags & WebView.CutCapability));
1385+ verify(!(model.editFlags & WebView.CopyCapability));
1386+ verify(!(model.editFlags & WebView.EraseCapability));
1387+ verify(model.editFlags & WebView.SelectAllCapability);
1388+ webView.executeEditingCommand(WebView.EditingCommandSelectAll);
1389+ cleanup();
1390+
1391+ invokeContextMenu("editable");
1392+ model = webView.currentContextMenu.contextModel;
1393+ compare(model.selectionText, "text area");
1394+ verify(!(model.editFlags & WebView.UndoCapability));
1395+ verify(!(model.editFlags & WebView.RedoCapability));
1396+ verify(model.editFlags & WebView.CutCapability);
1397+ verify(model.editFlags & WebView.CopyCapability);
1398+ verify(model.editFlags & WebView.EraseCapability);
1399+ verify(model.editFlags & WebView.SelectAllCapability);
1400+ webView.executeEditingCommand(WebView.EditingCommandErase);
1401+ cleanup();
1402+ var r = webView.getTestApi().evaluateCode(
1403+ "document.querySelector(\"#editable\").value");
1404+ compare(r, "");
1405+
1406+ invokeContextMenu("editable");
1407+ model = webView.currentContextMenu.contextModel;
1408+ compare(model.selectionText, "");
1409+ verify(model.editFlags & WebView.UndoCapability);
1410+ verify(!(model.editFlags & WebView.RedoCapability));
1411+ webView.executeEditingCommand(WebView.EditingCommandUndo);
1412+ cleanup();
1413+ var r = webView.getTestApi().evaluateCode(
1414+ "document.querySelector(\"#editable\").value");
1415+ compare(r, "text area");
1416+
1417+ invokeContextMenu("editable");
1418+ model = webView.currentContextMenu.contextModel;
1419+ compare(model.selectionText, "text area");
1420+ verify(!(model.editFlags & WebView.UndoCapability));
1421+ verify(model.editFlags & WebView.RedoCapability);
1422+ webView.executeEditingCommand(WebView.EditingCommandRedo);
1423+ var r = webView.getTestApi().evaluateCode(
1424+ "document.querySelector(\"#editable\").value");
1425+ compare(r, "");
1426+
1427+ // TODO: once clipboard support is implemented
1428+ // (https://launchpad.net/bugs/1301419), test cut/copy/paste
1429+ }
1430+
1431+ function test_WebView_contextMenu_saveLink_saveMedia() {
1432+ invokeContextMenu("imagelink");
1433+ var model = webView.currentContextMenu.contextModel;
1434+
1435+ model.saveLink();
1436+ spy.wait();
1437+ compare(webView.downloadUrl, "http://testsuite/empty.html");
1438+ compare(webView.downloadReferrer,
1439+ "http://testsuite/tst_WebView_contextMenu.html");
1440+
1441+ model.saveMedia();
1442+ spy.wait();
1443+ compare(webView.downloadUrl, "http://testsuite/cof.svg");
1444+ compare(webView.downloadReferrer,
1445+ "http://testsuite/tst_WebView_contextMenu.html");
1446+ }
1447+
1448+ function test_WebView_contextMenu_mediaFlags_data() {
1449+ return [
1450+ { id: "video", loop: true, canSave: true, hasAudio: false, controls: false },
1451+ { id: "audio", loop: false, canSave: true, hasAudio: true, controls: true },
1452+ ];
1453+ }
1454+
1455+ function test_WebView_contextMenu_mediaFlags(data) {
1456+ invokeContextMenu(data.id);
1457+ var model = webView.currentContextMenu.contextModel;
1458+ compare(model.mediaFlags & WebView.MediaStatusLoop,
1459+ data.loop ? WebView.MediaStatusLoop : WebView.MediaStatusNone);
1460+ compare(model.mediaFlags & WebView.MediaStatusCanSave,
1461+ data.canSave ? WebView.MediaStatusCanSave : WebView.MediaStatusNone);
1462+ compare(model.mediaFlags & WebView.MediaStatusHasAudio,
1463+ data.hasAudio ? WebView.MediaStatusHasAudio : WebView.MediaStatusNone);
1464+ compare(model.mediaFlags & WebView.MediaStatusControls,
1465+ data.controls ? WebView.MediaStatusControls : WebView.MediaStatusNone);
1466+ }
1467+ }
1468+}
1469
1470=== modified file 'shared/browser/oxide_web_contents_view.cc'
1471--- shared/browser/oxide_web_contents_view.cc 2015-02-24 20:27:57 +0000
1472+++ shared/browser/oxide_web_contents_view.cc 2015-05-26 19:54:24 +0000
1473@@ -1,5 +1,5 @@
1474 // vim:expandtab:shiftwidth=2:tabstop=2:
1475-// Copyright (C) 2013 Canonical Ltd.
1476+// Copyright (C) 2013-2015 Canonical Ltd.
1477
1478 // This library is free software; you can redistribute it and/or
1479 // modify it under the terms of the GNU Lesser General Public
1480@@ -137,6 +137,12 @@
1481
1482 void WebContentsView::SetOverscrollControllerEnabled(bool enabled) {}
1483
1484+void WebContentsView::ShowContextMenu(
1485+ content::RenderFrameHost* render_frame_host,
1486+ const content::ContextMenuParams& params) {
1487+ GetWebView()->ShowContextMenu(render_frame_host, params);
1488+}
1489+
1490 void WebContentsView::ShowPopupMenu(
1491 content::RenderFrameHost* render_frame_host,
1492 const gfx::Rect& bounds,
1493
1494=== modified file 'shared/browser/oxide_web_contents_view.h'
1495--- shared/browser/oxide_web_contents_view.h 2014-11-04 23:00:07 +0000
1496+++ shared/browser/oxide_web_contents_view.h 2015-05-26 19:54:24 +0000
1497@@ -1,5 +1,5 @@
1498 // vim:expandtab:shiftwidth=2:tabstop=2:
1499-// Copyright (C) 2013 Canonical Ltd.
1500+// Copyright (C) 2013-2015 Canonical Ltd.
1501
1502 // This library is free software; you can redistribute it and/or
1503 // modify it under the terms of the GNU Lesser General Public
1504@@ -71,6 +71,8 @@
1505 void SetOverscrollControllerEnabled(bool enabled) final;
1506
1507 // content::RenderViewHostDelegateView
1508+ void ShowContextMenu(content::RenderFrameHost* render_frame_host,
1509+ const content::ContextMenuParams& params) final;
1510 void ShowPopupMenu(content::RenderFrameHost* render_frame_host,
1511 const gfx::Rect& bounds,
1512 int item_height,
1513
1514=== added file 'shared/browser/oxide_web_context_menu.cc'
1515--- shared/browser/oxide_web_context_menu.cc 1970-01-01 00:00:00 +0000
1516+++ shared/browser/oxide_web_context_menu.cc 2015-05-26 19:54:24 +0000
1517@@ -0,0 +1,105 @@
1518+// vim:expandtab:shiftwidth=2:tabstop=2:
1519+// Copyright (C) 2015 Canonical Ltd.
1520+
1521+// This library is free software; you can redistribute it and/or
1522+// modify it under the terms of the GNU Lesser General Public
1523+// License as published by the Free Software Foundation; either
1524+// version 2.1 of the License, or (at your option) any later version.
1525+
1526+// This library is distributed in the hope that it will be useful,
1527+// but WITHOUT ANY WARRANTY; without even the implied warranty of
1528+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1529+// Lesser General Public License for more details.
1530+
1531+// You should have received a copy of the GNU Lesser General Public
1532+// License along with this library; if not, write to the Free Software
1533+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1534+
1535+#include "oxide_web_context_menu.h"
1536+
1537+#include "base/logging.h"
1538+#include "content/browser/frame_host/render_frame_host_impl.h"
1539+#include "content/public/browser/browser_context.h"
1540+#include "content/public/browser/browser_thread.h"
1541+#include "content/public/browser/download_manager.h"
1542+#include "content/public/browser/render_frame_host.h"
1543+#include "content/public/browser/render_view_host.h"
1544+#include "content/public/browser/web_contents.h"
1545+
1546+#include "oxide_web_contents_view.h"
1547+
1548+namespace {
1549+
1550+const GURL& GetDocumentURL(const content::ContextMenuParams& params) {
1551+ return params.frame_url.is_empty() ? params.page_url : params.frame_url;
1552+}
1553+
1554+content::Referrer CreateSaveAsReferrer(
1555+ const GURL& url,
1556+ const content::ContextMenuParams& params) {
1557+ const GURL& referring_url = GetDocumentURL(params);
1558+ return content::Referrer::SanitizeForRequest(
1559+ url,
1560+ content::Referrer(referring_url.GetAsReferrer(), params.referrer_policy));
1561+}
1562+
1563+} // namespace
1564+
1565+namespace oxide {
1566+
1567+void WebContextMenu::RenderFrameDeleted(content::RenderFrameHost* rfh) {
1568+ if (rfh != render_frame_host_) {
1569+ return;
1570+ }
1571+
1572+ Close();
1573+}
1574+
1575+void WebContextMenu::Hide() {}
1576+
1577+WebContextMenu::WebContextMenu(content::RenderFrameHost* rfh,
1578+ const content::ContextMenuParams& params)
1579+ : content::WebContentsObserver(content::WebContents::FromRenderFrameHost(rfh)),
1580+ params_(params),
1581+ render_frame_host_(static_cast<content::RenderFrameHostImpl *>(rfh)) {}
1582+
1583+WebContextMenu::~WebContextMenu() {
1584+ DCHECK(!render_frame_host_);
1585+}
1586+
1587+void WebContextMenu::Close() {
1588+ Hide();
1589+ render_frame_host_ = nullptr;
1590+ content::BrowserThread::DeleteSoon(
1591+ content::BrowserThread::UI, FROM_HERE, this);
1592+}
1593+
1594+void WebContextMenu::SaveLink() const {
1595+ content::BrowserContext* context = web_contents()->GetBrowserContext();
1596+ content::DownloadManager* dlm =
1597+ content::BrowserContext::GetDownloadManager(context);
1598+ const GURL& url = params_.link_url;
1599+ scoped_ptr<content::DownloadUrlParameters> dl_params(
1600+ content::DownloadUrlParameters::FromWebContents(web_contents(), url));
1601+ dl_params->set_referrer(CreateSaveAsReferrer(url, params_));
1602+ dl_params->set_referrer_encoding(params_.frame_charset);
1603+ dl_params->set_suggested_name(params_.suggested_filename);
1604+ dl_params->set_prompt(true);
1605+ dlm->DownloadUrl(dl_params.Pass());
1606+}
1607+
1608+void WebContextMenu::SaveMedia() const {
1609+ bool is_large_data_url =
1610+ params_.has_image_contents && params_.src_url.is_empty();
1611+ if ((params_.media_type == blink::WebContextMenuData::MediaTypeCanvas) ||
1612+ ((params_.media_type == blink::WebContextMenuData::MediaTypeImage) &&
1613+ is_large_data_url)) {
1614+ render_frame_host_->GetRenderViewHost()->SaveImageAt(params_.x, params_.y);
1615+ } else {
1616+ const GURL& url = params_.src_url;
1617+ content::Referrer referrer = CreateSaveAsReferrer(url, params_);
1618+ web_contents()->SaveFrame(url, referrer);
1619+ }
1620+}
1621+
1622+} // namespace oxide
1623
1624=== added file 'shared/browser/oxide_web_context_menu.h'
1625--- shared/browser/oxide_web_context_menu.h 1970-01-01 00:00:00 +0000
1626+++ shared/browser/oxide_web_context_menu.h 2015-05-26 19:54:24 +0000
1627@@ -0,0 +1,73 @@
1628+// vim:expandtab:shiftwidth=2:tabstop=2:
1629+// Copyright (C) 2015 Canonical Ltd.
1630+
1631+// This library is free software; you can redistribute it and/or
1632+// modify it under the terms of the GNU Lesser General Public
1633+// License as published by the Free Software Foundation; either
1634+// version 2.1 of the License, or (at your option) any later version.
1635+
1636+// This library is distributed in the hope that it will be useful,
1637+// but WITHOUT ANY WARRANTY; without even the implied warranty of
1638+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1639+// Lesser General Public License for more details.
1640+
1641+// You should have received a copy of the GNU Lesser General Public
1642+// License along with this library; if not, write to the Free Software
1643+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1644+
1645+#ifndef _OXIDE_SHARED_BROWSER_WEB_CONTEXT_MENU_H_
1646+#define _OXIDE_SHARED_BROWSER_WEB_CONTEXT_MENU_H_
1647+
1648+#include "base/macros.h"
1649+#include "base/memory/weak_ptr.h"
1650+#include "content/public/browser/web_contents_observer.h"
1651+#include "content/public/common/context_menu_params.h"
1652+
1653+class GURL;
1654+
1655+namespace base {
1656+template <typename T> class DeleteHelper;
1657+}
1658+
1659+namespace content {
1660+class ContextMenuParams;
1661+class RenderFrameHost;
1662+class RenderFrameHostImpl;
1663+}
1664+
1665+namespace gfx {
1666+class Rect;
1667+}
1668+
1669+namespace oxide {
1670+
1671+class WebContextMenu : public content::WebContentsObserver {
1672+ public:
1673+ virtual void Show() = 0;
1674+ void Close();
1675+
1676+ protected:
1677+ friend class base::DeleteHelper<WebContextMenu>;
1678+
1679+ WebContextMenu(content::RenderFrameHost* rfh,
1680+ const content::ContextMenuParams& params);
1681+ virtual ~WebContextMenu();
1682+
1683+ content::ContextMenuParams params_;
1684+
1685+ void SaveLink() const;
1686+ void SaveMedia() const;
1687+
1688+ private:
1689+ void RenderFrameDeleted(content::RenderFrameHost* rfh) final;
1690+
1691+ virtual void Hide();
1692+
1693+ content::RenderFrameHostImpl* render_frame_host_;
1694+
1695+ DISALLOW_COPY_AND_ASSIGN(WebContextMenu);
1696+};
1697+
1698+} // namespace oxide
1699+
1700+#endif // _OXIDE_SHARED_BROWSER_WEB_CONTEXT_MENU_H_
1701
1702=== modified file 'shared/browser/oxide_web_view.cc'
1703--- shared/browser/oxide_web_view.cc 2015-05-22 16:06:07 +0000
1704+++ shared/browser/oxide_web_view.cc 2015-05-26 19:54:24 +0000
1705@@ -86,6 +86,7 @@
1706 #include "oxide_render_widget_host_view.h"
1707 #include "oxide_web_contents_unloader.h"
1708 #include "oxide_web_contents_view.h"
1709+#include "oxide_web_context_menu.h"
1710 #include "oxide_web_frame.h"
1711 #include "oxide_web_popup_menu.h"
1712 #include "oxide_web_preferences.h"
1713@@ -1960,6 +1961,16 @@
1714 web_contents_->DispatchBeforeUnload(false);
1715 }
1716
1717+void WebView::ShowContextMenu(content::RenderFrameHost* render_frame_host,
1718+ const content::ContextMenuParams& params) {
1719+ WebContextMenu* menu = client_->CreateContextMenu(render_frame_host, params);
1720+ if (!menu) {
1721+ return;
1722+ }
1723+
1724+ menu->Show();
1725+}
1726+
1727 void WebView::ShowPopupMenu(content::RenderFrameHost* render_frame_host,
1728 const gfx::Rect& bounds,
1729 int selected_item,
1730
1731=== modified file 'shared/browser/oxide_web_view.h'
1732--- shared/browser/oxide_web_view.h 2015-05-22 16:06:07 +0000
1733+++ shared/browser/oxide_web_view.h 2015-05-26 19:54:24 +0000
1734@@ -66,6 +66,7 @@
1735
1736 namespace content {
1737
1738+struct ContextMenuParams;
1739 struct MenuItem;
1740 class NativeWebKeyboardEvent;
1741 class NotificationRegistrar;
1742@@ -100,6 +101,7 @@
1743 class FilePicker;
1744 class JavaScriptDialog;
1745 class RenderWidgetHostView;
1746+class WebContextMenu;
1747 class WebFrame;
1748 class WebPopupMenu;
1749 class WebPreferences;
1750@@ -270,6 +272,8 @@
1751
1752 void PrepareToClose();
1753
1754+ void ShowContextMenu(content::RenderFrameHost* render_frame_host,
1755+ const content::ContextMenuParams& params);
1756 void ShowPopupMenu(content::RenderFrameHost* render_frame_host,
1757 const gfx::Rect& bounds,
1758 int selected_item,
1759
1760=== modified file 'shared/browser/oxide_web_view_client.cc'
1761--- shared/browser/oxide_web_view_client.cc 2015-05-22 16:06:07 +0000
1762+++ shared/browser/oxide_web_view_client.cc 2015-05-26 19:54:24 +0000
1763@@ -123,6 +123,12 @@
1764 return nullptr;
1765 }
1766
1767+WebContextMenu* WebViewClient::CreateContextMenu(
1768+ content::RenderFrameHost* rfh,
1769+ const content::ContextMenuParams& params) {
1770+ return nullptr;
1771+}
1772+
1773 WebPopupMenu* WebViewClient::CreatePopupMenu(
1774 content::RenderFrameHost* rfh) {
1775 return nullptr;
1776
1777=== modified file 'shared/browser/oxide_web_view_client.h'
1778--- shared/browser/oxide_web_view_client.h 2015-05-22 16:06:07 +0000
1779+++ shared/browser/oxide_web_view_client.h 2015-05-26 19:54:24 +0000
1780@@ -36,6 +36,7 @@
1781 }
1782
1783 namespace content {
1784+struct ContextMenuParams;
1785 class NativeWebKeyboardEvent;
1786 class RenderFrameHost;
1787 class RenderViewHost;
1788@@ -50,6 +51,7 @@
1789 class MediaAccessPermissionRequest;
1790 class SecurityStatus;
1791 class SimplePermissionRequest;
1792+class WebContextMenu;
1793 class WebFrame;
1794 class WebPopupMenu;
1795 class WebView;
1796@@ -166,6 +168,10 @@
1797 virtual WebFrame* CreateWebFrame(
1798 content::RenderFrameHost* render_frame_host);
1799
1800+ virtual WebContextMenu* CreateContextMenu(
1801+ content::RenderFrameHost* rfh,
1802+ const content::ContextMenuParams& params);
1803+
1804 virtual WebPopupMenu* CreatePopupMenu(content::RenderFrameHost* rfh);
1805
1806 virtual WebView* CreateNewWebView(const gfx::Rect& initial_pos,
1807
1808=== modified file 'shared/shared.gyp'
1809--- shared/shared.gyp 2015-05-21 21:01:36 +0000
1810+++ shared/shared.gyp 2015-05-26 19:54:24 +0000
1811@@ -387,6 +387,8 @@
1812 'browser/oxide_web_contents_unloader.h',
1813 'browser/oxide_web_contents_view.cc',
1814 'browser/oxide_web_contents_view.h',
1815+ 'browser/oxide_web_context_menu.cc',
1816+ 'browser/oxide_web_context_menu.h',
1817 'browser/oxide_web_frame.cc',
1818 'browser/oxide_web_frame.h',
1819 'browser/oxide_web_popup_menu.cc',

Subscribers

People subscribed via source and target branches