Index: third_party/WebKit/Source/core/page/CreateWindow.cpp |
diff --git a/third_party/WebKit/Source/core/page/CreateWindow.cpp b/third_party/WebKit/Source/core/page/CreateWindow.cpp |
index d3a42e6f25a79cf4891a8abbea211720a194fb72..88aa2ad87b0c61d0b1801499405c0d5ef3883ee8 100644 |
--- a/third_party/WebKit/Source/core/page/CreateWindow.cpp |
+++ b/third_party/WebKit/Source/core/page/CreateWindow.cpp |
@@ -29,6 +29,7 @@ |
#include "bindings/core/v8/ExceptionState.h" |
#include "core/dom/Document.h" |
#include "core/dom/UserGestureIndicator.h" |
+#include "core/events/UIEventWithKeyState.h" |
#include "core/frame/FrameClient.h" |
#include "core/frame/LocalFrame.h" |
#include "core/frame/Settings.h" |
@@ -37,16 +38,225 @@ |
#include "core/page/ChromeClient.h" |
#include "core/page/FocusController.h" |
#include "core/page/Page.h" |
-#include "core/page/WindowFeatures.h" |
#include "core/probe/CoreProbes.h" |
+#include "platform/KeyboardCodes.h" |
#include "platform/loader/fetch/ResourceRequest.h" |
#include "platform/weborigin/KURL.h" |
#include "platform/weborigin/SecurityOrigin.h" |
#include "platform/weborigin/SecurityPolicy.h" |
+#include "public/platform/WebInputEvent.h" |
+#include "public/platform/WebKeyboardEvent.h" |
+#include "public/platform/WebMouseEvent.h" |
#include "public/platform/WebURLRequest.h" |
+#include "public/web/WebWindowFeatures.h" |
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 = *policy; |
+ NavigationPolicyFromMouseEvent(button_number, ctrl, shift, alt, meta, |
+ &user_policy); |
+ |
+ // When the input event suggests a download, but the navigation was initiated |
+ // by script, we should not override it. |
+ if (user_policy == kNavigationPolicyDownload && |
+ *policy != kNavigationPolicyIgnore) |
+ return; |
+ |
+ // User and app agree that we want a new window; let the app override the |
+ // decorations. |
+ if (user_policy == kNavigationPolicyNewWindow && |
+ *policy == kNavigationPolicyNewPopup) |
+ return; |
+ *policy = user_policy; |
+} |
+ |
+NavigationPolicy GetNavigationPolicy(const WebInputEvent* current_event, |
+ bool toolbar_visible) { |
+ // If the window features didn't enable the toolbar, or this window wasn't |
+ // created by a user gesture, show as a popup instead of a new tab. |
+ // |
+ // Note: this previously also checked that menubar, resizable, scrollbar, and |
+ // statusbar are enabled too. When no feature string is specified, these |
+ // features default to enabled (and the window opens as a new tab). However, |
+ // when a feature string is specified, any *unspecified* features default to |
+ // disabled, often causing the window to open as a popup instead. |
+ // |
+ // As specifying menubar, resizable, scrollbar, and statusbar have no effect |
+ // on the UI, just ignore them and only consider whether or not the toolbar is |
+ // enabled, which matches Firefox's behavior. |
+ NavigationPolicy policy = toolbar_visible ? kNavigationPolicyNewForegroundTab |
+ : kNavigationPolicyNewPopup; |
+ UpdatePolicyForEvent(current_event, &policy); |
+ return policy; |
+} |
+ |
+} // anonymous namespace |
+ |
+NavigationPolicy EffectiveNavigationPolicy(NavigationPolicy policy, |
+ const WebInputEvent* current_event, |
+ bool toolbar_visible) { |
+ if (policy == kNavigationPolicyIgnore) |
+ return GetNavigationPolicy(current_event, toolbar_visible); |
+ if (policy == kNavigationPolicyNewBackgroundTab && |
+ GetNavigationPolicy(current_event, toolbar_visible) != |
+ kNavigationPolicyNewBackgroundTab && |
+ !UIEventWithKeyState::NewTabModifierSetFromIsolatedWorld()) { |
+ return kNavigationPolicyNewForegroundTab; |
+ } |
+ return policy; |
+} |
+ |
+// Though isspace() considers \t and \v to be whitespace, Win IE doesn't when |
+// parsing window features. |
+static bool IsWindowFeaturesSeparator(UChar c) { |
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || |
+ c == ',' || c == '\0'; |
+} |
+ |
+WebWindowFeatures GetWindowFeaturesFromString(const String& feature_string) { |
+ WebWindowFeatures window_features; |
+ |
+ // The IE rule is: all features except for channelmode default |
+ // to YES, but if the user specifies a feature string, all features default to |
+ // NO. (There is no public standard that applies to this method.) |
+ // <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp> |
+ if (feature_string.IsEmpty()) |
+ return window_features; |
+ |
+ window_features.menu_bar_visible = false; |
+ window_features.status_bar_visible = false; |
+ window_features.tool_bar_visible = false; |
+ window_features.scrollbars_visible = false; |
+ |
+ // Tread lightly in this code -- it was specifically designed to mimic Win |
+ // IE's parsing behavior. |
+ unsigned key_begin, key_end; |
+ unsigned value_begin, value_end; |
+ |
+ String buffer = feature_string.DeprecatedLower(); |
+ unsigned length = buffer.length(); |
+ for (unsigned i = 0; i < length;) { |
+ // skip to first non-separator, but don't skip past the end of the string |
+ while (i < length && IsWindowFeaturesSeparator(buffer[i])) |
+ i++; |
+ key_begin = i; |
+ |
+ // skip to first separator |
+ while (i < length && !IsWindowFeaturesSeparator(buffer[i])) |
+ i++; |
+ key_end = i; |
+ |
+ SECURITY_DCHECK(i <= length); |
+ |
+ // skip to first '=', but don't skip past a ',' or the end of the string |
+ while (i < length && buffer[i] != '=') { |
+ if (buffer[i] == ',') |
+ break; |
+ i++; |
+ } |
+ |
+ SECURITY_DCHECK(i <= length); |
+ |
+ // Skip to first non-separator, but don't skip past a ',' or the end of the |
+ // string. |
+ while (i < length && IsWindowFeaturesSeparator(buffer[i])) { |
+ if (buffer[i] == ',') |
+ break; |
+ i++; |
+ } |
+ value_begin = i; |
+ |
+ SECURITY_DCHECK(i <= length); |
+ |
+ // skip to first separator |
+ while (i < length && !IsWindowFeaturesSeparator(buffer[i])) |
+ i++; |
+ value_end = i; |
+ |
+ SECURITY_DCHECK(i <= length); |
+ |
+ String key_string(buffer.Substring(key_begin, key_end - key_begin)); |
+ String value_string(buffer.Substring(value_begin, value_end - value_begin)); |
+ |
+ // Listing a key with no value is shorthand for key=yes |
+ int value; |
+ if (value_string.IsEmpty() || value_string == "yes") |
+ value = 1; |
+ else |
+ value = value_string.ToInt(); |
+ |
+ if (key_string == "left" || key_string == "screenx") { |
+ window_features.x_set = true; |
+ window_features.x = value; |
+ } else if (key_string == "top" || key_string == "screeny") { |
+ window_features.y_set = true; |
+ window_features.y = value; |
+ } else if (key_string == "width" || key_string == "innerwidth") { |
+ window_features.width_set = true; |
+ window_features.width = value; |
+ } else if (key_string == "height" || key_string == "innerheight") { |
+ window_features.height_set = true; |
+ window_features.height = value; |
+ } else if (key_string == "menubar") { |
+ window_features.menu_bar_visible = value; |
+ } else if (key_string == "toolbar" || key_string == "location") { |
+ window_features.tool_bar_visible |= static_cast<bool>(value); |
+ } else if (key_string == "status") { |
+ window_features.status_bar_visible = value; |
+ } else if (key_string == "scrollbars") { |
+ window_features.scrollbars_visible = value; |
+ } else if (key_string == "noopener") { |
+ window_features.noopener = true; |
+ } else if (key_string == "background") { |
+ window_features.background = true; |
+ } else if (key_string == "persistent") { |
+ window_features.persistent = true; |
+ } |
+ } |
+ |
+ return window_features; |
+} |
+ |
static Frame* ReuseExistingWindow(LocalFrame& active_frame, |
LocalFrame& lookup_frame, |
const AtomicString& frame_name, |
@@ -71,13 +281,17 @@ static Frame* ReuseExistingWindow(LocalFrame& active_frame, |
static Frame* CreateNewWindow(LocalFrame& opener_frame, |
const FrameLoadRequest& request, |
- const WindowFeatures& features, |
+ const WebWindowFeatures& features, |
NavigationPolicy policy, |
bool& created) { |
Page* old_page = opener_frame.GetPage(); |
if (!old_page) |
return nullptr; |
+ policy = EffectiveNavigationPolicy( |
+ policy, old_page->GetChromeClient().GetCurrentInputEvent(), |
+ features.tool_bar_visible); |
+ |
Page* page = old_page->GetChromeClient().CreateWindow(&opener_frame, request, |
features, policy); |
if (!page) |
@@ -92,7 +306,9 @@ static Frame* CreateNewWindow(LocalFrame& opener_frame, |
if (!EqualIgnoringASCIICase(request.FrameName(), "_blank")) |
frame.Tree().SetName(request.FrameName()); |
- page->GetChromeClient().SetWindowFeatures(features); |
+ page->SetWindowFeatures(features); |
+ |
+ frame.View()->SetCanHaveScrollbars(features.scrollbars_visible); |
// 'x' and 'y' specify the location of the window, while 'width' and 'height' |
// specify the size of the viewport. We can only resize the window, so adjust |
@@ -130,10 +346,9 @@ static Frame* CreateWindowHelper(LocalFrame& opener_frame, |
LocalFrame& active_frame, |
LocalFrame& lookup_frame, |
const FrameLoadRequest& request, |
- const WindowFeatures& features, |
+ const WebWindowFeatures& features, |
NavigationPolicy policy, |
bool& created) { |
- DCHECK(!features.dialog || request.FrameName().IsEmpty()); |
DCHECK(request.GetResourceRequest().RequestorOrigin() || |
opener_frame.GetDocument()->Url().IsEmpty()); |
DCHECK_EQ(request.GetResourceRequest().GetFrameType(), |
@@ -176,7 +391,7 @@ static Frame* CreateWindowHelper(LocalFrame& opener_frame, |
DOMWindow* CreateWindow(const String& url_string, |
const AtomicString& frame_name, |
- const WindowFeatures& window_features, |
+ const String& window_features_string, |
LocalDOMWindow& calling_window, |
LocalFrame& first_frame, |
LocalFrame& opener_frame, |
@@ -195,6 +410,9 @@ DOMWindow* CreateWindow(const String& url_string, |
return nullptr; |
} |
+ WebWindowFeatures window_features = |
+ GetWindowFeaturesFromString(window_features_string); |
+ |
FrameLoadRequest frame_request(calling_window.document(), |
ResourceRequest(completed_url), frame_name); |
frame_request.SetShouldSetOpener(window_features.noopener ? kNeverSetOpener |
@@ -231,7 +449,7 @@ DOMWindow* CreateWindow(const String& url_string, |
return nullptr; |
if (new_frame->DomWindow()->IsInsecureScriptAccess(calling_window, |
completed_url)) |
- return new_frame->DomWindow(); |
+ return window_features.noopener ? nullptr : new_frame->DomWindow(); |
// TODO(dcheng): Special case for window.open("about:blank") to ensure it |
// loads synchronously into a new window. This is our historical behavior, and |
@@ -252,7 +470,7 @@ DOMWindow* CreateWindow(const String& url_string, |
has_user_gesture ? UserGestureStatus::kActive |
: UserGestureStatus::kNone); |
} |
- return new_frame->DomWindow(); |
+ return window_features.noopener ? nullptr : new_frame->DomWindow(); |
} |
void CreateWindowForRequest(const FrameLoadRequest& request, |
@@ -273,7 +491,7 @@ void CreateWindowForRequest(const FrameLoadRequest& request, |
if (policy == kNavigationPolicyCurrentTab) |
policy = kNavigationPolicyNewForegroundTab; |
- WindowFeatures features; |
+ WebWindowFeatures features; |
features.noopener = request.GetShouldSetOpener() == kNeverSetOpener; |
bool created; |
Frame* new_frame = |