1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "third_party/blink/renderer/core/loader/navigation_policy.h"
32 
33 #include "build/build_config.h"
34 #include "third_party/blink/public/common/input/web_keyboard_event.h"
35 #include "third_party/blink/public/common/input/web_mouse_event.h"
36 #include "third_party/blink/public/web/web_navigation_policy.h"
37 #include "third_party/blink/public/web/web_window_features.h"
38 #include "third_party/blink/renderer/core/events/current_input_event.h"
39 #include "third_party/blink/renderer/core/events/gesture_event.h"
40 #include "third_party/blink/renderer/core/events/keyboard_event.h"
41 #include "third_party/blink/renderer/core/events/mouse_event.h"
42 #include "third_party/blink/renderer/core/events/ui_event_with_key_state.h"
43 #include "third_party/blink/renderer/platform/keyboard_codes.h"
44 #include "third_party/blink/renderer/platform/wtf/assertions.h"
45 
46 namespace blink {
47 
48 namespace {
49 
NavigationPolicyFromEventModifiers(int16_t button,bool ctrl,bool shift,bool alt,bool meta)50 NavigationPolicy NavigationPolicyFromEventModifiers(int16_t button,
51                                                     bool ctrl,
52                                                     bool shift,
53                                                     bool alt,
54                                                     bool meta) {
55 #if defined(OS_MAC)
56   const bool new_tab_modifier = (button == 1) || meta;
57 #else
58   const bool new_tab_modifier = (button == 1) || ctrl;
59 #endif
60   if (!new_tab_modifier && !shift && !alt)
61     return kNavigationPolicyCurrentTab;
62 
63   if (new_tab_modifier) {
64     if (shift)
65       return kNavigationPolicyNewForegroundTab;
66     else
67       return kNavigationPolicyNewBackgroundTab;
68   } else {
69     if (shift)
70       return kNavigationPolicyNewWindow;
71     else
72       return kNavigationPolicyDownload;
73   }
74   return kNavigationPolicyCurrentTab;
75 }
76 
NavigationPolicyFromEventInternal(const Event * event)77 NavigationPolicy NavigationPolicyFromEventInternal(const Event* event) {
78   if (!event)
79     return kNavigationPolicyCurrentTab;
80 
81   if (const auto* mouse_event = DynamicTo<MouseEvent>(event)) {
82     return NavigationPolicyFromEventModifiers(
83         mouse_event->button(), mouse_event->ctrlKey(), mouse_event->shiftKey(),
84         mouse_event->altKey(), mouse_event->metaKey());
85   } else if (const KeyboardEvent* key_event = DynamicTo<KeyboardEvent>(event)) {
86     // The click is simulated when triggering the keypress event.
87     return NavigationPolicyFromEventModifiers(
88         0, key_event->ctrlKey(), key_event->shiftKey(), key_event->altKey(),
89         key_event->metaKey());
90   } else if (const auto* gesture_event = DynamicTo<GestureEvent>(event)) {
91     // The click is simulated when triggering the gesture-tap event
92     return NavigationPolicyFromEventModifiers(
93         0, gesture_event->ctrlKey(), gesture_event->shiftKey(),
94         gesture_event->altKey(), gesture_event->metaKey());
95   }
96   return kNavigationPolicyCurrentTab;
97 }
98 
NavigationPolicyFromCurrentEvent()99 NavigationPolicy NavigationPolicyFromCurrentEvent() {
100   const WebInputEvent* event = CurrentInputEvent::Get();
101   if (!event)
102     return kNavigationPolicyCurrentTab;
103 
104   int16_t button = 0;
105   if (event->GetType() == WebInputEvent::Type::kMouseUp) {
106     const WebMouseEvent* mouse_event = static_cast<const WebMouseEvent*>(event);
107 
108     switch (mouse_event->button) {
109       case WebMouseEvent::Button::kLeft:
110         button = 0;
111         break;
112       case WebMouseEvent::Button::kMiddle:
113         button = 1;
114         break;
115       case WebMouseEvent::Button::kRight:
116         button = 2;
117         break;
118       default:
119         return kNavigationPolicyCurrentTab;
120     }
121   } else if ((WebInputEvent::IsKeyboardEventType(event->GetType()) &&
122               static_cast<const WebKeyboardEvent*>(event)->windows_key_code ==
123                   VKEY_RETURN) ||
124              WebInputEvent::IsGestureEventType(event->GetType())) {
125     // Keyboard and gesture events can simulate mouse events.
126     button = 0;
127   } else {
128     return kNavigationPolicyCurrentTab;
129   }
130 
131   return NavigationPolicyFromEventModifiers(
132       button, event->GetModifiers() & WebInputEvent::kControlKey,
133       event->GetModifiers() & WebInputEvent::kShiftKey,
134       event->GetModifiers() & WebInputEvent::kAltKey,
135       event->GetModifiers() & WebInputEvent::kMetaKey);
136 }
137 
138 }  // namespace
139 
NavigationPolicyFromEvent(const Event * event)140 NavigationPolicy NavigationPolicyFromEvent(const Event* event) {
141   NavigationPolicy event_policy = NavigationPolicyFromEventInternal(event);
142   NavigationPolicy input_policy = NavigationPolicyFromCurrentEvent();
143 
144   if (event_policy == kNavigationPolicyDownload &&
145       input_policy != kNavigationPolicyDownload) {
146     // No downloads from synthesized events without user intention.
147     return kNavigationPolicyCurrentTab;
148   }
149 
150   if (event_policy == kNavigationPolicyNewBackgroundTab &&
151       input_policy != kNavigationPolicyNewBackgroundTab &&
152       !UIEventWithKeyState::NewTabModifierSetFromIsolatedWorld()) {
153     // No "tab-unders" from synthesized events without user intention.
154     // Events originating from an isolated world are exempt.
155     return kNavigationPolicyNewForegroundTab;
156   }
157 
158   return event_policy;
159 }
160 
NavigationPolicyForCreateWindow(const WebWindowFeatures & features)161 NavigationPolicy NavigationPolicyForCreateWindow(
162     const WebWindowFeatures& features) {
163   // If our default configuration was modified by a script or wasn't
164   // created by a user gesture, then show as a popup. Else, let this
165   // new window be opened as a toplevel window.
166   bool as_popup = !features.tool_bar_visible || !features.status_bar_visible ||
167                   !features.scrollbars_visible || !features.menu_bar_visible ||
168                   !features.resizable;
169   NavigationPolicy app_policy =
170       as_popup ? kNavigationPolicyNewPopup : kNavigationPolicyNewForegroundTab;
171   NavigationPolicy user_policy = NavigationPolicyFromCurrentEvent();
172 
173   if (user_policy == kNavigationPolicyNewWindow &&
174       app_policy == kNavigationPolicyNewPopup) {
175     // User and app agree that we want a new window; let the app override the
176     // decorations.
177     return app_policy;
178   }
179 
180   if (user_policy == kNavigationPolicyCurrentTab) {
181     // User doesn't want a specific policy, use app policy instead.
182     return app_policy;
183   }
184 
185   if (user_policy == kNavigationPolicyDownload) {
186     // When the input event suggests a download, but the navigation was
187     // initiated by script, we should not override it.
188     return app_policy;
189   }
190 
191   return user_policy;
192 }
193 
194 STATIC_ASSERT_ENUM(kWebNavigationPolicyDownload, kNavigationPolicyDownload);
195 STATIC_ASSERT_ENUM(kWebNavigationPolicyCurrentTab, kNavigationPolicyCurrentTab);
196 STATIC_ASSERT_ENUM(kWebNavigationPolicyNewBackgroundTab,
197                    kNavigationPolicyNewBackgroundTab);
198 STATIC_ASSERT_ENUM(kWebNavigationPolicyNewForegroundTab,
199                    kNavigationPolicyNewForegroundTab);
200 STATIC_ASSERT_ENUM(kWebNavigationPolicyNewWindow, kNavigationPolicyNewWindow);
201 STATIC_ASSERT_ENUM(kWebNavigationPolicyNewPopup, kNavigationPolicyNewPopup);
202 
203 }  // namespace blink
204