Commit 323d55f7 authored by Dmitry Gozman's avatar Dmitry Gozman Committed by Commit Bot

Move more NavigationPolicy decisions to navigation_policy.cc

As a side effect, this makes ctrl+click on a submit button open
a new background tab - similar to webkit and clicking on a link.

Also covered ctrl+click functionality with tests.

Bug: 849055
Change-Id: I344df4821a7c840129d239023d4a500a42e8f80b
Reviewed-on: https://chromium-review.googlesource.com/1100107Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Commit-Queue: Dmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567422}
parent 03018770
Default policy for navigation to 'notify-done.html' is 'new background tab'
Default policy for navigation to 'notify-done.html' is 'current tab'
Tests that hitting ctrl-click on a link results in a new background tab.
link
<!doctype html>
<script>
function test()
{
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.dumpNavigationPolicy();
testRunner.waitUntilDone();
var isMac = navigator.userAgent.search(/\bMac OS X\b/) != -1;
var a = document.querySelector("#link");
eventSender.mouseMoveTo(a.offsetLeft + 10, a.offsetTop + 10);
eventSender.mouseDown(0, [isMac ? 'metaKey' : 'ctrlKey']);
eventSender.mouseUp(0, [isMac ? 'metaKey' : 'ctrlKey']);
}
}
</script>
<body onload="test()">
<p>Tests that hitting ctrl-click on a link results in a new background tab.</p>
<a href="resources/notify-done.html" id="link">link</a>
</body>
Default policy for navigation to 'notify-done.html' is 'new background tab'
Default policy for navigation to 'notify-done.html' is 'current tab'
Tests that ctrl-clicking on a submit button results in a new background tab.
<!doctype html>
<script>
function test()
{
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.dumpNavigationPolicy();
testRunner.waitUntilDone();
var isMac = navigator.userAgent.search(/\bMac OS X\b/) != -1;
var submit = document.querySelector("#submit");
eventSender.mouseMoveTo(submit.offsetLeft + 10, submit.offsetTop + 10);
eventSender.mouseDown(0, [isMac ? 'metaKey' : 'ctrlKey']);
eventSender.mouseUp(0, [isMac ? 'metaKey' : 'ctrlKey']);
}
}
</script>
<body onload="test()">
<p>Tests that ctrl-clicking on a submit button results in a new background tab.</p>
<form action="resources/notify-done.html">
<input type="text" name="foo" value="bar" />
<input type="submit" id="submit" />
</form>
</body>
Default policy for navigation to 'notify-done.html' is 'new foreground tab'
Default policy for navigation to 'notify-done.html' is 'current tab'
Tests that synthesizing ctrl-click on a submit button does not result in a new background tab.
<!doctype html>
<script>
function test()
{
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.dumpNavigationPolicy();
testRunner.waitUntilDone();
var isMac = navigator.userAgent.search(/\bMac OS X\b/) != -1;
var submit = document.querySelector("#submit");
var evt = new MouseEvent("click", isMac ? { metaKey: true } : { ctrlKey: true });
submit.dispatchEvent(evt);
}
}
</script>
<body onload="test()">
<p>Tests that synthesizing ctrl-click on a submit button does not result in a new background tab.</p>
<form action="resources/notify-done.html">
<input type="text" name="foo" value="bar" />
<input type="submit" id="submit" />
</form>
</body>
Default policy for navigation to 'notify-done.html' is 'new foreground tab'
Default policy for navigation to 'notify-done.html' is 'current tab'
Tests that synthesizing ctrl-click does not result in a new background tab.
link
<!doctype html>
<script>
function test()
{
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.dumpNavigationPolicy();
testRunner.waitUntilDone();
var isMac = navigator.userAgent.search(/\bMac OS X\b/) != -1;
var a = document.querySelector("#link");
var evt = new MouseEvent("click", isMac ? { metaKey: true } : { ctrlKey: true });
a.dispatchEvent(evt);
}
}
</script>
<body onload="test()">
<p>Tests that synthesizing ctrl-click does not result in a new background tab.</p>
<a href="resources/notify-done.html" id="link">link</a>
</body>
......@@ -62,7 +62,6 @@
#include "third_party/blink/renderer/core/events/current_input_event.h"
#include "third_party/blink/renderer/core/events/message_event.h"
#include "third_party/blink/renderer/core/events/mouse_event.h"
#include "third_party/blink/renderer/core/events/ui_event_with_key_state.h"
#include "third_party/blink/renderer/core/exported/shared_worker_repository_client_impl.h"
#include "third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h"
#include "third_party/blink/renderer/core/exported/web_document_loader_impl.h"
......@@ -469,45 +468,6 @@ void LocalFrameClientImpl::DispatchDidChangeThemeColor() {
web_frame_->Client()->DidChangeThemeColor();
}
static bool AllowCreatingBackgroundTabs() {
const WebInputEvent* input_event = CurrentInputEvent::Get();
if (!input_event || (input_event->GetType() != WebInputEvent::kMouseUp &&
(input_event->GetType() != WebInputEvent::kRawKeyDown &&
input_event->GetType() != WebInputEvent::kKeyDown) &&
input_event->GetType() != WebInputEvent::kGestureTap))
return false;
unsigned short button_number;
if (WebInputEvent::IsMouseEventType(input_event->GetType())) {
const WebMouseEvent* mouse_event =
static_cast<const WebMouseEvent*>(input_event);
switch (mouse_event->button) {
case WebMouseEvent::Button::kLeft:
button_number = 0;
break;
case WebMouseEvent::Button::kMiddle:
button_number = 1;
break;
case WebMouseEvent::Button::kRight:
button_number = 2;
break;
default:
return false;
}
} else {
// The click is simulated when triggering the keypress event.
button_number = 0;
}
bool ctrl = input_event->GetModifiers() & WebMouseEvent::kControlKey;
bool shift = input_event->GetModifiers() & WebMouseEvent::kShiftKey;
bool alt = input_event->GetModifiers() & WebMouseEvent::kAltKey;
bool meta = input_event->GetModifiers() & WebMouseEvent::kMetaKey;
return NavigationPolicyFromMouseEvent(button_number, ctrl, shift, alt,
meta) ==
kNavigationPolicyNewBackgroundTab;
}
NavigationPolicy LocalFrameClientImpl::DecidePolicyForNavigation(
const ResourceRequest& request,
Document* origin_document,
......@@ -524,11 +484,6 @@ NavigationPolicy LocalFrameClientImpl::DecidePolicyForNavigation(
if (!web_frame_->Client())
return kNavigationPolicyIgnore;
if (policy == kNavigationPolicyNewBackgroundTab &&
!AllowCreatingBackgroundTabs() &&
!UIEventWithKeyState::NewTabModifierSetFromIsolatedWorld())
policy = kNavigationPolicyNewForegroundTab;
WebDocumentLoaderImpl* web_document_loader =
WebDocumentLoaderImpl::FromDocumentLoader(document_loader);
......
......@@ -31,22 +31,28 @@
#include "third_party/blink/renderer/core/loader/navigation_policy.h"
#include "build/build_config.h"
#include "third_party/blink/public/platform/web_keyboard_event.h"
#include "third_party/blink/public/platform/web_mouse_event.h"
#include "third_party/blink/public/web/web_navigation_policy.h"
#include "third_party/blink/public/web/web_window_features.h"
#include "third_party/blink/renderer/core/events/current_input_event.h"
#include "third_party/blink/renderer/core/events/gesture_event.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
#include "third_party/blink/renderer/core/events/mouse_event.h"
#include "third_party/blink/renderer/core/events/ui_event_with_key_state.h"
#include "third_party/blink/renderer/core/page/create_window.h"
#include "third_party/blink/renderer/platform/keyboard_codes.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
namespace blink {
NavigationPolicy NavigationPolicyFromMouseEvent(unsigned short button,
bool ctrl,
bool shift,
bool alt,
bool meta) {
namespace {
NavigationPolicy NavigationPolicyFromEventModifiers(unsigned short button,
bool ctrl,
bool shift,
bool alt,
bool meta) {
#if defined(OS_MACOSX)
const bool new_tab_modifier = (button == 1) || meta;
#else
......@@ -69,27 +75,25 @@ NavigationPolicy NavigationPolicyFromMouseEvent(unsigned short button,
return kNavigationPolicyCurrentTab;
}
namespace {
NavigationPolicy NavigationPolicyFromEventInternal(Event* event) {
if (!event)
return kNavigationPolicyCurrentTab;
if (event->IsMouseEvent()) {
MouseEvent* mouse_event = ToMouseEvent(event);
return NavigationPolicyFromMouseEvent(
return NavigationPolicyFromEventModifiers(
mouse_event->button(), mouse_event->ctrlKey(), mouse_event->shiftKey(),
mouse_event->altKey(), mouse_event->metaKey());
} else if (event->IsKeyboardEvent()) {
// The click is simulated when triggering the keypress event.
KeyboardEvent* key_event = ToKeyboardEvent(event);
return NavigationPolicyFromMouseEvent(
return NavigationPolicyFromEventModifiers(
0, key_event->ctrlKey(), key_event->shiftKey(), key_event->altKey(),
key_event->metaKey());
} else if (event->IsGestureEvent()) {
// The click is simulated when triggering the gesture-tap event
GestureEvent* gesture_event = ToGestureEvent(event);
return NavigationPolicyFromMouseEvent(
return NavigationPolicyFromEventModifiers(
0, gesture_event->ctrlKey(), gesture_event->shiftKey(),
gesture_event->altKey(), gesture_event->metaKey());
}
......@@ -99,15 +103,63 @@ NavigationPolicy NavigationPolicyFromEventInternal(Event* event) {
} // namespace
NavigationPolicy NavigationPolicyFromEvent(Event* event) {
NavigationPolicy policy = NavigationPolicyFromEventInternal(event);
// TODO(dgozman): move navigation policy helpers from CreateWindow here.
if (policy == kNavigationPolicyDownload &&
EffectiveNavigationPolicy(policy, CurrentInputEvent::Get(),
WebWindowFeatures()) !=
kNavigationPolicyDownload) {
NavigationPolicy event_policy = NavigationPolicyFromEventInternal(event);
NavigationPolicy input_policy =
NavigationPolicyFromEvent(CurrentInputEvent::Get());
if (event_policy == kNavigationPolicyDownload &&
input_policy != kNavigationPolicyDownload) {
// No downloads from synthesized events without user intention.
return kNavigationPolicyCurrentTab;
}
if (event_policy == kNavigationPolicyNewBackgroundTab &&
input_policy != kNavigationPolicyNewBackgroundTab &&
!UIEventWithKeyState::NewTabModifierSetFromIsolatedWorld()) {
// No "tab-unders" from synthesized events without user intention.
// Events originating from an isolated world are exempt.
return kNavigationPolicyNewForegroundTab;
}
return event_policy;
}
NavigationPolicy NavigationPolicyFromEvent(const WebInputEvent* event) {
if (!event)
return kNavigationPolicyCurrentTab;
unsigned short button = 0;
if (event->GetType() == WebInputEvent::kMouseUp) {
const WebMouseEvent* mouse_event = static_cast<const WebMouseEvent*>(event);
switch (mouse_event->button) {
case WebMouseEvent::Button::kLeft:
button = 0;
break;
case WebMouseEvent::Button::kMiddle:
button = 1;
break;
case WebMouseEvent::Button::kRight:
button = 2;
break;
default:
return kNavigationPolicyCurrentTab;
}
} else if ((WebInputEvent::IsKeyboardEventType(event->GetType()) &&
static_cast<const WebKeyboardEvent*>(event)->windows_key_code ==
VKEY_RETURN) ||
WebInputEvent::IsGestureEventType(event->GetType())) {
// Keyboard and gesture events can simulate mouse events.
button = 0;
} else {
return kNavigationPolicyCurrentTab;
}
return policy;
return NavigationPolicyFromEventModifiers(
button, event->GetModifiers() & WebInputEvent::kControlKey,
event->GetModifiers() & WebInputEvent::kShiftKey,
event->GetModifiers() & WebInputEvent::kAltKey,
event->GetModifiers() & WebInputEvent::kMetaKey);
}
STATIC_ASSERT_ENUM(kWebNavigationPolicyIgnore, kNavigationPolicyIgnore);
......
......@@ -36,6 +36,7 @@
namespace blink {
class Event;
class WebInputEvent;
enum NavigationPolicy {
kNavigationPolicyIgnore,
......@@ -49,14 +50,16 @@ enum NavigationPolicy {
kNavigationPolicyHandledByClientForInitialHistory,
};
// Returns a NavigationPolicy to use for starting a navigation
// based on the Event. This function takes care of some security checks,
// ensuring that synthesized events cannot trigger arbitrary downloads
// or new tabs without user intention coming from a real input event.
CORE_EXPORT NavigationPolicy NavigationPolicyFromEvent(Event*);
CORE_EXPORT NavigationPolicy
NavigationPolicyFromMouseEvent(unsigned short button,
bool ctrl,
bool shift,
bool alt,
bool meta);
// This is a helper method which returns policy for a real input event
// from the user.
// TODO(dgozman): this function should be gone soon.
CORE_EXPORT NavigationPolicy NavigationPolicyFromEvent(const WebInputEvent*);
} // namespace blink
......
......@@ -28,8 +28,6 @@
#include "services/network/public/mojom/request_context_frame_type.mojom-blink.h"
#include "third_party/blink/public/platform/web_input_event.h"
#include "third_party/blink/public/platform/web_keyboard_event.h"
#include "third_party/blink/public/platform/web_mouse_event.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/web/web_window_features.h"
#include "third_party/blink/renderer/bindings/core/v8/exception_state.h"
......@@ -46,7 +44,6 @@
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/platform/keyboard_codes.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
......@@ -54,64 +51,9 @@
namespace blink {
namespace {
void UpdatePolicyForEvent(const WebInputEvent* input_event,
NavigationPolicy* policy) {
if (!input_event)
return;
unsigned short button_number = 0;
if (input_event->GetType() == WebInputEvent::kMouseUp) {
const WebMouseEvent* mouse_event =
static_cast<const WebMouseEvent*>(input_event);
switch (mouse_event->button) {
case WebMouseEvent::Button::kLeft:
button_number = 0;
break;
case WebMouseEvent::Button::kMiddle:
button_number = 1;
break;
case WebMouseEvent::Button::kRight:
button_number = 2;
break;
default:
return;
}
} else if ((WebInputEvent::IsKeyboardEventType(input_event->GetType()) &&
static_cast<const WebKeyboardEvent*>(input_event)
->windows_key_code == VKEY_RETURN) ||
WebInputEvent::IsGestureEventType(input_event->GetType())) {
// Keyboard and gesture events can simulate mouse events.
button_number = 0;
} else {
return;
}
bool ctrl = input_event->GetModifiers() & WebInputEvent::kControlKey;
bool shift = input_event->GetModifiers() & WebInputEvent::kShiftKey;
bool alt = input_event->GetModifiers() & WebInputEvent::kAltKey;
bool meta = input_event->GetModifiers() & WebInputEvent::kMetaKey;
NavigationPolicy user_policy =
NavigationPolicyFromMouseEvent(button_number, ctrl, shift, alt, meta);
// User and app agree that we want a new window; let the app override the
// decorations.
if (user_policy == kNavigationPolicyNewWindow &&
*policy == kNavigationPolicyNewPopup)
return;
// User wants a specific policy, use it over app policy.
if (user_policy != kNavigationPolicyCurrentTab)
*policy = user_policy;
}
} // anonymous namespace
// Check that the desired NavigationPolicy |policy| is compatible with the
// observed input event |current_event|.
// TODO(dgozman): move this to navigation_policy.cc.
NavigationPolicy EffectiveNavigationPolicy(NavigationPolicy policy,
const WebInputEvent* current_event,
const WebWindowFeatures& features) {
......@@ -121,9 +63,19 @@ NavigationPolicy EffectiveNavigationPolicy(NavigationPolicy policy,
bool as_popup = !features.tool_bar_visible || !features.status_bar_visible ||
!features.scrollbars_visible || !features.menu_bar_visible ||
!features.resizable;
NavigationPolicy user_policy =
NavigationPolicy app_policy =
as_popup ? kNavigationPolicyNewPopup : kNavigationPolicyNewForegroundTab;
UpdatePolicyForEvent(current_event, &user_policy);
NavigationPolicy user_policy = NavigationPolicyFromEvent(current_event);
if (user_policy == kNavigationPolicyNewWindow &&
app_policy == kNavigationPolicyNewPopup) {
// User and app agree that we want a new window; let the app override the
// decorations.
user_policy = app_policy;
} else if (user_policy == kNavigationPolicyCurrentTab) {
// User doesn't want a specific policy, use app policy instead.
user_policy = app_policy;
}
if (policy == kNavigationPolicyIgnore) {
// When the input event suggests a download, but the navigation was
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment