[WRTjs] Refactor popup
[platform/framework/web/chromium-efl.git] / wrt / src / browser / wrt_web_contents.cc
1 #include "wrt/src/browser/wrt_web_contents.h"
2
3 #include "base/command_line.h"
4 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
5 #include "content/browser/web_contents/web_contents_impl.h"
6 #include "content/browser/web_contents/web_contents_view_aura.h"
7 #include "content/browser/web_contents/web_contents_view_aura_helper_efl.h"
8 #include "content/public/browser/navigation_handle.h"
9 #include "content/public/browser/render_frame_host.h"
10 #include "content/public/browser/render_view_host.h"
11 #include "content/public/browser/web_contents.h"
12 #include "content/public/common/content_switches.h"
13 #include "electron/shell/browser/web_contents_preferences.h"
14 #include "electron/shell/browser/window_list.h"
15 #include "electron/shell/common/electron_command_line.h"
16 #include "extensions/buildflags/buildflags.h"
17 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
18 #include "tizen_src/chromium_impl/content/browser/selection/selection_controller_efl.h"
19 #include "tizen_src/chromium_impl/efl/window_factory.h"
20 #include "tizen_src/chromium_impl/tizen/system_info.h"
21 #include "tizen_src/ewk/efl_integration/browser/sound_effect.h"
22 #include "tizen_src/ewk/efl_integration/public/EWebKit_internal.h"
23 #include "ui/display/screen.h"
24 #include "ui/gfx/geometry/dip_util.h"
25 #include "wrt/src/browser/native_web_runtime.h"
26 #include "wrt/src/browser/tv/wrt_select_picker_tv.h"
27 #include "wrt/src/browser/wrt_browser_context.h"
28 #include "wrt/src/browser/wrt_context_menu_controller.h"
29 #include "wrt/src/browser/wrt_native_window.h"
30 #include "wrt/src/browser/wrt_select_picker.h"
31 #include "wrt/src/browser/wrt_web_view_delegate.h"
32 #include "wrt/src/common/application_data.h"
33 #include "wrt/src/common/wrt_service_message_channel.h"
34 #include "wrt/src/service/wrt_service.h"
35 #include "wrt/src/service/wrt_service_launcher.h"
36 #include "wrt/src/service/wrt_service_manager.h"
37
38 #if BUILDFLAG(IS_TIZEN_TV)
39 #include "wrt/src/browser/tv/wrt_native_window_tv.h"
40 #endif
41
42 #if defined(TIZEN_ATK_SUPPORT)
43 #include "tizen_src/ewk/efl_integration/eweb_accessibility_util.h"
44 #endif
45
46 #if ENABLE_CHROME_EXTENSIONS
47 #include "wrt/src/browser/extensions/extension_util.h"
48 #endif
49
50 namespace wrt {
51
52 namespace {
53
54 template <typename Callback>
55 void NotifyMessageOnUIThread(const std::string& type,
56                              const std::vector<std::string>& params,
57                              Callback callback) {
58   content::GetUIThreadTaskRunner({})->PostTask(
59       FROM_HERE,
60       base::BindOnce([](const std::string& type,
61                         const std::vector<std::string>& params,
62                         Callback callback) {
63         auto& runtime = NativeWebRuntime::GetInstance();
64         bool result = runtime.NotifyMessage(type, params);
65         std::move(callback).Run(result);
66       }, type, params, std::move(callback)));
67 }
68
69 SelectPickerBase* CreateSelectPicker(
70     content::WebContents* web_contents,
71     int selected_index,
72     std::vector<blink::mojom::MenuItemPtr> items,
73     bool is_multiple_selection,
74     const gfx::Rect& bounds,
75     double item_font_size) {
76   SelectPickerBase* picker;
77   Evas_Object* view_evas =
78       WRTNativeWindow::FromWebContents(web_contents)->view_evas();
79 #if BUILDFLAG(IS_TIZEN_TV)
80   picker = new WRTSelectPickerTv(web_contents, selected_index,
81                                  is_multiple_selection, view_evas);
82   picker->SetFontSizeAsItem(item_font_size);
83 #else
84   picker = new WRTSelectPicker(web_contents, selected_index,
85                                is_multiple_selection, view_evas);
86 #endif
87   // Create two separate Elm_Genlist_Item_Class classes, because EFL cannot swap
88   // item_style at runtime.
89   picker->InitializeItemClass();
90   picker->InitializeGroupClass();
91   picker->Init(std::move(items), bounds);
92   return picker;
93 }
94
95 }  // namespace
96
97 // static
98 std::map<content::WebContents*, std::unique_ptr<WRTWebContents>>
99 WRTWebContents::map_;
100
101 // static
102 void WRTWebContents::WebContentsCreated(content::WebContents* web_contents) {
103   map_[web_contents] =
104       std::unique_ptr<WRTWebContents>(new WRTWebContents(web_contents));
105 }
106
107 // static
108 void WRTWebContents::WebContentsDeleted(content::WebContents* web_contents) {
109   map_.erase(web_contents);
110 }
111
112 // static
113 bool WRTWebContents::IsObservedWebContents(
114     content::WebContents* web_contents) {
115   return (map_.find(web_contents) != map_.end());
116 }
117
118 // static
119 WRTWebContents* WRTWebContents::FromWebContents(
120     content::WebContents* web_contents) {
121   auto it = map_.find(web_contents);
122   return (it != map_.end()) ? it->second.get() : nullptr;
123 }
124
125 // static
126 WRTWebContents* WRTWebContents::FromNativeWindow(WRTNativeWindow* window) {
127   for (auto& it : map_) {
128     auto* relay = it.first->GetUserData(
129         electron::NativeWindowRelay::UserDataKey());
130     auto* relay_window = relay ?
131         static_cast<electron::NativeWindowRelay*>(relay)->GetNativeWindow() :
132         nullptr;
133     if (window == relay_window)
134       return it.second.get();
135   }
136   return nullptr;
137 }
138
139 // static
140 content::WebContents* WRTWebContents::GetWebContents() {
141   return map_.empty() ? nullptr : map_.begin()->first;
142 }
143
144 // static
145 void WRTWebContents::BindBrowser(
146     mojo::PendingAssociatedReceiver<mojom::WRTBrowser> receiver,
147     content::RenderFrameHost* render_frame_host) {
148   auto* web_contents =
149       content::WebContents::FromRenderFrameHost(render_frame_host);
150   auto* wrt_web_contents = FromWebContents(web_contents);
151   if (!wrt_web_contents)
152     return;
153   wrt_web_contents->receivers_.Bind(render_frame_host, std::move(receiver));
154 }
155
156 content::WebContentsViewAura* WRTWebContents::GetWebContentsViewAura() const {
157   content::WebContentsImpl* wc =
158       static_cast<content::WebContentsImpl*>(web_contents());
159   return static_cast<content::WebContentsViewAura*>(wc->GetView());
160 }
161
162 content::WebContentsViewAuraHelperEfl*
163 WRTWebContents::GetWebContentsViewAuraHelper() {
164   auto* wcva = GetWebContentsViewAura();
165   return wcva ? wcva->wcva_helper() : nullptr;
166 }
167
168 #if BUILDFLAG(IS_TIZEN_TV)
169 content::RWHVAuraCommonHelperEfl* WRTWebContents::GetRWHVAEflHelper() {
170   return rwhva() ? rwhva()->aura_efl_helper() : nullptr;
171 }
172 #endif
173
174 WRTWebContents::WRTWebContents(content::WebContents* web_contents)
175     : receivers_(web_contents, this),
176       thread_("service_thread"),
177       show_ime_panel_(true),
178       ime_is_floating_(false),
179       use_keypad_without_user_action_(false) {
180   Observe(web_contents);
181
182   page_scale_factor_ = 1.0;
183   if (auto* aura_helper = GetWebContentsViewAuraHelper()) {
184     web_view_delegate_.reset(new WRTWebViewDelegate(this));
185     aura_helper->SetWebviewDelegate(web_view_delegate_.get());
186   }
187
188   static_cast<content::WebContentsImpl*>(web_contents)
189       ->set_ewk_view(efl::WindowFactory::GetHostWindow(web_contents));
190 }
191
192 WRTWebContents::~WRTWebContents() {
193   if (auto* aura_helper = GetWebContentsViewAuraHelper())
194     aura_helper->SetWebviewDelegate(nullptr);
195   web_view_delegate_.reset();
196   select_picker_.reset();
197   if (thread_.IsRunning())
198     thread_.Stop();
199 }
200
201 mojom::WRTRenderer* WRTWebContents::GetRendererInterface() {
202   auto* frame = web_contents()->GetPrimaryMainFrame();
203   if (frame) {
204     frame->GetRemoteAssociatedInterfaces()->GetInterface(&renderer_interface_);
205     return renderer_interface_.get();
206   }
207
208   return nullptr;
209 }
210
211 void WRTWebContents::RenderFrameCreated(
212     content::RenderFrameHost* render_frame_host) {
213   SetExtraCommandLineSwitches(render_frame_host);
214   SetLongPollingTimeout();
215 }
216
217 void WRTWebContents::RenderViewReady() {
218 #if BUILDFLAG(IS_TIZEN_TV)
219   auto native_window_tv = static_cast<WRTNativeWindowTV*>(
220       WRTNativeWindow::FromWebContents(web_contents()));
221   if (native_window_tv && native_window_tv->NeedSetCursorPointer())
222     return;
223
224   if (auto* aura_efl_helper = GetRWHVAEflHelper())
225     aura_efl_helper->SetCursorByClient(true);
226 #endif
227 }
228
229 void WRTWebContents::OnPageScaleFactorChanged(float page_scale_factor) {
230   page_scale_factor_ = page_scale_factor;
231   SetScaledContentSize();
232 }
233
234 void WRTWebContents::SetScaledContentSize() {
235   if (!rwhva() || !rwhva()->aura_efl_helper())
236     return;
237
238   const float device_scale_factor =
239       display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
240   gfx::SizeF scaled_contents_size = gfx::ConvertSizeToPixels(
241       contents_size_, device_scale_factor * page_scale_factor_);
242   rwhva()->aura_efl_helper()->SetScaledContentSize(scaled_contents_size);
243 }
244
245 void WRTWebContents::SetExtraCommandLineSwitches(
246     content::RenderFrameHost* render_frame_host) {
247   auto* command_line = base::CommandLine::ForCurrentProcess();
248   if (!command_line->HasSwitch(switches::kSingleProcess) || map_.size() > 1)
249     return;
250
251   auto* web_contents =
252       content::WebContents::FromRenderFrameHost(render_frame_host);
253   if (!IsObservedWebContents(web_contents))
254     return;
255
256   auto* web_preferences = electron::WebContentsPreferences::From(web_contents);
257   if (web_preferences) {
258     base::CommandLine extra(base::CommandLine::NO_PROGRAM);
259     web_preferences->AppendCommandLineSwitches(&extra, false);
260     base::CommandLine::SwitchMap switches = extra.GetSwitches();
261     switches.erase(switches::kNoZygote);
262     for (const auto& sw : switches)
263       command_line->AppendSwitchNative(sw.first, sw.second);
264     electron::ElectronCommandLine::InitializeFromCommandLine();
265   }
266 }
267
268 void WRTWebContents::SetLongPollingTimeout() {
269   auto& setting_info = ApplicationData::GetInstance().setting_info();
270   auto polling_val(setting_info.long_polling());
271   auto* renderer = GetRendererInterface();
272   if (!polling_val || !renderer)
273     return;
274
275   renderer->SetLongPollingTimeout(*polling_val);
276 }
277
278 void WRTWebContents::DidFinishNavigation(
279     content::NavigationHandle* navigation_handle) {
280   if (navigation_handle->ShouldUpdateHistory())
281     static_cast<WRTBrowserContext*>(web_contents()->GetBrowserContext())
282         ->AddVisitedURLs(navigation_handle->GetRedirectChain());
283
284 #if BUILDFLAG(IS_TIZEN_TV)
285   auto native_window_tv =
286       static_cast<WRTNativeWindowTV*>(
287           WRTNativeWindow::FromWebContents(web_contents()));
288   if (native_window_tv) {
289     native_window_tv->GetImeAndKeyPadConfig(show_ime_panel_, ime_is_floating_,
290                                             use_keypad_without_user_action_);
291     native_window_tv->DidFinishNavigation(
292         navigation_handle->GetURL().possibly_invalid_spec());
293   }
294 #endif
295
296 #if defined(TIZEN_ATK_SUPPORT)
297   static bool lazy_initialize_atk_ = true;
298   EWebAccessibilityUtil::GetInstance()->ToggleAtk(lazy_initialize_atk_);
299 #endif
300 }
301
302 void WRTWebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host,
303                                    const GURL& validated_url) {
304 #if ENABLE_CHROME_EXTENSIONS
305   if (web_contents()->GetPrimaryMainFrame() == render_frame_host)
306     extensions::util::SendExtensionActionInfo(this);
307 #endif
308   // Set focus to WebContents after load is finished. This makes sure
309   // JS focus events (eg. onfocus) are disaptched as expected.
310   web_contents()->Focus();
311 }
312
313 content::RenderWidgetHostViewAura* WRTWebContents::rwhva() const {
314   return static_cast<content::RenderWidgetHostViewAura*>(
315       web_contents()->GetRenderWidgetHostView());
316 }
317
318 Evas_Object* WRTWebContents::evas_object() {
319   return efl::WindowFactory::GetHostWindow(web_contents());
320 }
321
322 content::SelectionControllerEfl*
323 WRTWebContents::GetSelectionController() const {
324 #if defined(WRT_JS_BRINGUP)
325   return nullptr;
326 #else
327   auto view = rwhva();
328   return view ? view->GetSelectionController() : nullptr;
329 #endif
330 }
331
332 void WRTWebContents::UpdateContextMenuWithParams(
333     const content::ContextMenuParams& params) {
334   context_menu_.reset(
335       new WRTContextMenuController(*web_contents(), this));
336   if (!context_menu_->PopulateAndShowContextMenu(params)) {
337     context_menu_.reset();
338     if (GetSelectionController())
339       GetSelectionController()->HideHandles();
340   }
341 }
342
343 void WRTWebContents::ShowContextMenuInternal(
344     const content::ContextMenuParams& params) {
345   if (params.is_editable) {
346     auto* renderer = GetRendererInterface();
347     if (!renderer)
348       return;
349     renderer->QueryInputType(
350         base::BindOnce([](content::WebContents* web_contents,
351                           content::ContextMenuParams params,
352                           bool is_password_input) -> void {
353           auto* wrt_web_contents =
354               WRTWebContents::FromWebContents(web_contents);
355           if (!wrt_web_contents)
356             return;
357
358           if (is_password_input) {
359             params.form_control_type =
360                 blink::mojom::FormControlType::kInputPassword;
361           }
362           wrt_web_contents->UpdateContextMenuWithParams(params);
363         }, web_contents(), params));
364   } else {
365     UpdateContextMenuWithParams(params);
366   }
367 }
368
369 void WRTWebContents::HandleLongPressGesture(
370     const content::ContextMenuParams& params) {
371   // This menu is created in renderer process and it does not know anything about
372   // view scaling factor and it has another calling sequence, so coordinates is
373   // not updated.
374   if (!(IsMobileProfile() || IsDesktopProfile())) {
375     LOG(ERROR) << "Touch events are not supported";
376     return;
377   }
378
379   if (!rwhva() || !rwhva()->aura_efl_helper())
380     return;
381   content::ContextMenuParams convertedParams = params;
382   gfx::Point convertedPoint = rwhva()->aura_efl_helper()->ConvertPointInViewPix(
383       gfx::Point(params.x, params.y));
384   convertedParams.x = convertedPoint.x();
385   convertedParams.y = convertedPoint.y();
386
387   Evas_Coord x, y;
388   evas_object_geometry_get(evas_object(), &x, &y, 0, 0);
389   convertedParams.x += x;
390   convertedParams.y += y;
391
392   bool show_context_menu_now = true;
393   if (GetSelectionController() && GetSelectionController()->GetLongPressed()) {
394     show_context_menu_now =
395         !GetSelectionController()->HandleLongPressEvent(convertedPoint,
396                                                         convertedParams);
397   }
398   if (show_context_menu_now)
399     ShowContextMenuInternal(convertedParams);
400 }
401
402 void WRTWebContents::ShowContextMenu(
403     const content::ContextMenuParams& params) {
404   if (params.source_type == ui::MENU_SOURCE_LONG_PRESS) {
405     HandleLongPressGesture(params);
406     return;
407   }
408
409   if (!rwhva() || !rwhva()->aura_efl_helper())
410     return;
411   // This menu is created in renderer process and it does not know anything about
412   // view scaling factor and it has another calling sequence, so coordinates is
413   // not updated.
414   content::ContextMenuParams convertedParams = params;
415   gfx::Point convertedPoint = rwhva()->aura_efl_helper()->ConvertPointInViewPix(
416       gfx::Point(params.x, params.y));
417   convertedParams.x = convertedPoint.x();
418   convertedParams.y = convertedPoint.y();
419
420   Evas_Coord x, y;
421   evas_object_geometry_get(evas_object(), &x, &y, 0, 0);
422   convertedParams.x += x;
423   convertedParams.y += y;
424
425   ShowContextMenuInternal(convertedParams);
426 }
427
428 void WRTWebContents::OnSelectionRectReceived(
429     const gfx::Rect& selection_rect) const {
430   if (context_menu_)
431     context_menu_->OnSelectionRectReceived(selection_rect);
432 }
433
434 void WRTWebContents::ExecuteEditCommand(const char* command,
435                                         const char* value) {
436   if (!command)
437     return;
438
439   absl::optional<std::u16string> optional_value;
440   if (value)
441     optional_value = absl::make_optional(base::ASCIIToUTF16(value));
442
443   static_cast<content::WebContentsImpl*>(web_contents())
444       ->ExecuteEditCommand(std::string(command), optional_value);
445 }
446
447 void WRTWebContents::HideContextMenu() {
448   if (context_menu_)
449     context_menu_->HideContextMenu();
450 }
451
452 void WRTWebContents::ResetControllers() {
453   context_menu_.reset();
454   auto* selection_controller = GetSelectionController();
455   if (selection_controller)
456     selection_controller->ClearSelection();
457 }
458
459 void WRTWebContents::ShowPopupMenuImpl(
460     std::vector<blink::mojom::MenuItemPtr> items,
461     int selectedIndex,
462     bool multiple,
463     const gfx::Rect& bounds,
464     double item_font_size) {
465   if (!select_picker_) {
466     select_picker_.reset(CreateSelectPicker(web_contents(), selectedIndex,
467                                             std::move(items), multiple, bounds,
468                                             item_font_size));
469     // Picker has been shown on top of webview and the page content gets
470     // partially overlapped. Decrease viewport while showing picker.
471     AdjustViewPortHeightToPopupMenu(true /* is_popup_menu_visible */);
472     is_popup_showing_ = true;
473     ScrollFocusedNodeIntoView();
474   } else {
475     select_picker_->UpdatePickerData(selectedIndex, std::move(items), multiple);
476   }
477 #if BUILDFLAG(IS_TIZEN_TV)
478   if (auto* aura_efl_helper = GetRWHVAEflHelper())
479     aura_efl_helper->SetPopupMenuVisible(true);
480 #endif
481   select_picker_->Show();
482 }
483
484 void WRTWebContents::HidePopupMenuImpl() {
485   is_popup_showing_ = false;
486   if (!select_picker_)
487      return;
488
489
490   AdjustViewPortHeightToPopupMenu(false /* is_popup_menu_visible */);
491 #if BUILDFLAG(IS_TIZEN_TV)
492   if (auto* aura_efl_helper = GetRWHVAEflHelper()) {
493     aura_efl_helper->SetPopupMenuVisible(false);
494     aura_efl_helper->SetPopupMenuBounds(gfx::Rect(0, 0, 0, 0));
495   }
496 #endif
497   select_picker_.reset();
498 }
499
500 void WRTWebContents::AdjustViewPortHeightToPopupMenu(
501     bool is_popup_menu_visible) {
502   if (!rwhva() || !rwhva()->aura_efl_helper() || !IsMobileProfile() ||
503       NativeWebRuntime::GetInstance().IsTizenWebKitCompatibilityEnabled()) {
504     return;
505   }
506
507   int picker_height = select_picker_->GetGeometryDIP().height();
508   gfx::Rect screen_rect =
509       display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
510   gfx::Rect view_rect = rwhva()->GetViewBounds();
511   int bottom_height =
512       screen_rect.height() - (view_rect.y() + view_rect.height());
513
514   rwhva()->aura_efl_helper()->SetCustomViewportSize(
515       is_popup_menu_visible
516           ? gfx::Size(view_rect.width(),
517                       view_rect.height() - picker_height + bottom_height)
518           : gfx::Size());
519 }
520
521 void WRTWebContents::ScrollFocusedNodeIntoView() {
522   auto* renderer = GetRendererInterface();
523   if (!renderer)
524     return;
525   renderer->ScrollFocusedNodeIntoView();
526 }
527
528 void WRTWebContents::StartService(
529     const std::string& internal_id,
530     mojo::PendingRemote<mojom::WRTServiceMessageListener> remote,
531     StartServiceCallback callback) {
532   auto& app_data = ApplicationData::GetInstance();
533   auto service_id = ApplicationData::GetServiceID(internal_id);
534   if (app_data.IsGlobalServiceApp(service_id)) {
535     LOG(INFO) << "Starting global service...";
536     if (!thread_.IsRunning())
537       thread_.Start();
538
539     thread_.task_runner()->PostTask(
540         FROM_HERE,
541         base::BindOnce([](const std::string& internal_id,
542                           StartServiceCallback callback) {
543
544           auto wrt_service_launcher = WRTServiceLauncher::Get();
545           if (!wrt_service_launcher->LaunchWrtService()) {
546             std::move(callback).Run(false);
547             return;
548           }
549           if (wrt_service_launcher->IsWrtServiceReady() &&
550               wrt_service_launcher->IsWrtServiceDbusReady() &&
551               WRTService::StartService(internal_id)) {
552             std::move(callback).Run(true);
553           } else {
554             std::move(callback).Run(false);
555           }
556         }, internal_id, std::move(callback)));
557   } else {
558     LOG(INFO) << "Starting ui service...";
559     auto service_manager = WRTServiceManager::Get();
560     service_manager->SetServiceModel(WRTService::UI_DEDICATED);
561
562     auto* channel =
563         new WRTBrowserServiceMessageChannel(internal_id, std::move(remote));
564     if (!channel) {
565       LOG(ERROR) << "Fail to create message channel";
566       std::move(callback).Run(false);
567       return;
568     }
569
570     auto service_path = app_data.GetStartServiceFile();
571     std::vector<std::string> params{ internal_id, service_path };
572     NotifyMessageOnUIThread("startService", params, std::move(callback));
573   }
574 }
575
576 void WRTWebContents::StopService(const std::string& internal_id,
577                                  StopServiceCallback callback) {
578   auto& app_data = ApplicationData::GetInstance();
579   if (app_data.IsGlobalServiceApp(ApplicationData::GetServiceID(internal_id))) {
580     if (!thread_.IsRunning())
581       thread_.Start();
582
583     thread_.task_runner()->PostTask(
584         FROM_HERE,
585         base::BindOnce([](const std::string& internal_id,
586                           StopServiceCallback callback) {
587           if (!WRTService::StopService(internal_id)) {
588             std::move(callback).Run(false);
589           } else {
590             std::move(callback).Run(true);
591           }
592         }, internal_id, std::move(callback)));
593   } else {
594     std::vector<std::string> params{ internal_id };
595     NotifyMessageOnUIThread("stopService", params, std::move(callback));
596     WRTBrowserServiceMessageChannel::RemoveMessageChannel(internal_id);
597   }
598 }
599
600 void WRTWebContents::PlayLinkEffect() {
601   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
602   sound_effect::playLinkEffect();
603 }
604
605 void WRTWebContents::DidChangeContentsSize(const gfx::Size& size) {
606   contents_size_ = size;
607   SetScaledContentSize();
608 }
609
610 void WRTWebContents::DidHandleGestureEventWithContext(
611     bool is_content_editable) {
612   if (!GetSelectionController())
613     return;
614
615   GetSelectionController()->PostHandleTapGesture(is_content_editable);
616 }
617
618 }  // namespace wrt