Commit 8fdb8930 authored by edchin's avatar edchin Committed by Commit Bot

[ios] Add URL drop interaction to web content

Enables dropping (through dragging) a URL into the content
area of the web view. Dropping the URL navigates the
web content to that URL.

This is controlled via a Finch feature flag.

Bug: 1099050
Change-Id: I10d1bb27eac4ce41811ace0af9a59fe12959183d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2248662
Commit-Queue: edchin <edchin@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#782733}
parent b0d1345d
......@@ -39,6 +39,11 @@
"owners": [ "msarda" ],
"expiry_milestone": 88
},
{
"name": "add-web-content-drop-interaction",
"owners": [ "edchin" ],
"expiry_milestone": 90
},
{
"name": "aggregated-ml-app-ranking",
"owners": [ "pdyson", "jiameng" ],
......
......@@ -656,6 +656,11 @@ const flags_ui::FeatureEntry kFeatureEntries[] = {
{"shared-highlighting-ios", flag_descriptions::kSharedHighlightingIOSName,
flag_descriptions::kSharedHighlightingIOSDescription, flags_ui::kOsIos,
FEATURE_VALUE_TYPE(kSharedHighlightingIOS)},
{"add-web-content-drop-interaction",
flag_descriptions::kAddWebContentDropInteractionName,
flag_descriptions::kAddWebContentDropInteractionDescription,
flags_ui::kOsIos,
FEATURE_VALUE_TYPE(web::features::kAddWebContentDropInteraction)},
};
bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
......
......@@ -11,6 +11,12 @@
namespace flag_descriptions {
const char kAddWebContentDropInteractionName[] =
"Add Web Content Drop Interaction";
const char kAddWebContentDropInteractionDescription[] =
"When enabled, adds ability to drop a URL on the web content area to "
"navigate to that URL.";
const char kAutofillCacheQueryResponsesName[] =
"Cache Autofill Query Responses";
const char kAutofillCacheQueryResponsesDescription[] =
......
......@@ -11,6 +11,11 @@
namespace flag_descriptions {
// Title and description for the flag to add a custom drop interaction to web
// content.
extern const char kAddWebContentDropInteractionName[];
extern const char kAddWebContentDropInteractionDescription[];
// Title and description for the flag to control the autofill query cache.
extern const char kAutofillCacheQueryResponsesName[];
extern const char kAutofillCacheQueryResponsesDescription[];
......
......@@ -53,6 +53,10 @@ extern const base::Feature kPreserveScrollViewProperties;
// When enabled, display an interstitial on lookalike URL navigations.
extern const base::Feature kIOSLookalikeUrlNavigationSuggestionsUI;
// When enabled, supports dropping URLs on the web content area to navigate to
// the URL.
extern const base::Feature kAddWebContentDropInteraction;
// When true, for each navigation, the default user agent is chosen by the
// WebClient GetDefaultUserAgent() method. If it is false, the mobile version
// is requested by default.
......
......@@ -45,6 +45,9 @@ const base::Feature kIOSLookalikeUrlNavigationSuggestionsUI{
"IOSLookalikeUrlNavigationSuggestionsUI",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAddWebContentDropInteraction{
"AddWebContentDropInteraction", base::FEATURE_DISABLED_BY_DEFAULT};
bool UseWebClientDefaultUserAgent() {
if (@available(iOS 13, *)) {
return base::FeatureList::IsEnabled(kUseDefaultUserAgentInWebClient);
......
......@@ -99,6 +99,7 @@ NSString* const kScriptMessageName = @"crwebinvoke";
CRWWebViewScrollViewProxyObserver,
CRWWKNavigationHandlerDelegate,
CRWWKUIHandlerDelegate,
UIDropInteractionDelegate,
WKNavigationDelegate> {
// The view used to display content. Must outlive |_webViewProxy|. The
// container view should be accessed through this property rather than
......@@ -204,6 +205,10 @@ NSString* const kScriptMessageName = @"crwebinvoke";
@property(nonatomic, strong, readonly)
CRWTouchTrackingRecognizer* touchTrackingRecognizer;
// A custom drop interaction that is added alongside the web view's default drop
// interaction.
@property(nonatomic, strong) UIDropInteraction* customDropInteraction;
// Session Information
// -------------------
// The associated NavigationManagerImpl.
......@@ -726,6 +731,10 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*);
- (void)wasShown {
self.visible = YES;
// WebKit adds a drop interaction to a subview (WKContentView) of WKWebView's
// scrollView when the web view is added to the view hierarchy.
[self addCustomURLDropInteractionIfNeeded];
}
- (void)wasHidden {
......@@ -931,6 +940,46 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*);
return _userInteractionState.IsUserInteracting(self.webView);
}
// Adds a custom drop interaction to the same subview of |self.webScrollView|
// that already has a default drop interaction.
- (void)addCustomURLDropInteractionIfNeeded {
if (!base::FeatureList::IsEnabled(
web::features::kAddWebContentDropInteraction))
return;
BOOL subviewWithDefaultInteractionFound = NO;
for (UIView* subview in self.webScrollView.subviews) {
BOOL defaultInteractionFound = NO;
BOOL customInteractionFound = NO;
for (id<UIInteraction> interaction in subview.interactions) {
if ([interaction isKindOfClass:[UIDropInteraction class]]) {
if (interaction == self.customDropInteraction) {
customInteractionFound = YES;
} else {
DCHECK(!defaultInteractionFound &&
!subviewWithDefaultInteractionFound)
<< "There should be only one default drop interaction in the "
"webScrollView.";
defaultInteractionFound = YES;
subviewWithDefaultInteractionFound = YES;
}
}
}
if (customInteractionFound) {
// The custom interaction must be added after the default drop interaction
// to work properly.
[subview removeInteraction:self.customDropInteraction];
[subview addInteraction:self.customDropInteraction];
} else if (defaultInteractionFound) {
if (!self.customDropInteraction) {
self.customDropInteraction =
[[UIDropInteraction alloc] initWithDelegate:self];
}
[subview addInteraction:self.customDropInteraction];
}
}
}
#pragma mark - End of loading
- (void)didFinishNavigation:(web::NavigationContextImpl*)context {
......@@ -967,6 +1016,13 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*);
BOOL success = !context || !context->GetError();
[self loadCompleteWithSuccess:success forContext:context];
// WebKit adds a drop interaction to a subview (WKContentView) of WKWebView's
// scrollView when a new WebProcess finishes launching. This can be loading
// the first page, navigating cross-domain, or recovering from a WebProcess
// crash. Add a custom drop interaction alongside the default drop
// interaction.
[self addCustomURLDropInteractionIfNeeded];
}
- (void)loadCompleteWithSuccess:(BOOL)loadSuccess
......@@ -1928,6 +1984,37 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*);
return [self optOutScrollsToTopForSubviews];
}
#pragma mark - UIDropInteractionDelegate
- (BOOL)dropInteraction:(UIDropInteraction*)interaction
canHandleSession:(id<UIDropSession>)session {
return session.items.count == 1U &&
[session canLoadObjectsOfClass:[NSURL class]];
}
- (UIDropProposal*)dropInteraction:(UIDropInteraction*)interaction
sessionDidUpdate:(id<UIDropSession>)session {
return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationCopy];
}
- (void)dropInteraction:(UIDropInteraction*)interaction
performDrop:(id<UIDropSession>)session {
DCHECK_EQ(1U, session.items.count);
if ([session canLoadObjectsOfClass:[NSURL class]]) {
__weak CRWWebController* weakSelf = self;
[session loadObjectsOfClass:[NSURL class]
completion:^(NSArray<NSURL*>* objects) {
GURL URL = net::GURLWithNSURL([objects firstObject]);
if (!_isBeingDestroyed && URL.is_valid()) {
web::NavigationManager::WebLoadParams params(URL);
params.transition_type = ui::PAGE_TRANSITION_TYPED;
weakSelf.webStateImpl->GetNavigationManager()
->LoadURLWithParams(params);
}
}];
}
}
#pragma mark - Testing-Only Methods
- (void)injectWebViewContentView:(CRWWebViewContentView*)webViewContentView {
......
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