Skip to content
Permalink
Browse files
[iOS] Add long press support for links in WKPDFView
https://bugs.webkit.org/show_bug.cgi?id=138357

Reviewed by Dan Bernstein.

Use WKActionSheetAssistant to show a link action sheet in response to long-pressing on a link. Have WKPDFView
conform to WKActionSheetAssistantDelegate in order to respond to the open and copy actions as well as to
provide the link's URL and position information to WKActionSheetAssistant. The long-pressed link is highlighted
for .75 seconds before the sheet is displayed in order to match UIWebPDFView.

* UIProcess/ios/WKActionSheetAssistant.h: Made protocol methods that WKPDFView doesn't implement optional.
* UIProcess/ios/WKActionSheetAssistant.mm:
(-[WKActionSheetAssistant updatePositionInformation]): Checked if delegate responds to
updatePositionInformationForActionSheetAssistant: before calling.
(-[WKActionSheetAssistant _createSheetWithElementActions:showLinkTitle:]): Checked if delegate responds to
actionSheetAssistant:willStartInteractionWithElement: before calling.
(-[WKActionSheetAssistant cleanupSheet]): Checked if delegate responds to actionSheetAssistantDidStopInteraction:
before calling.
* UIProcess/ios/WKPDFView.h: Conformed to WKActionSheetAssistantDelegate.
* UIProcess/ios/WKPDFView.mm:
(-[WKPDFView web_initWithFrame:webView:]): Instantiated a WKActionSheetAssistant and set self as its delegate.
(-[WKPDFView _highlightLinkAnnotation:forDuration:completionHandler:]): Moved highlight drawing to here from
annotation:wasTouchedAtPoint:controller: in order to be reused for long-press.
(-[WKPDFView _URLForLinkAnnotation:]): Moved URL creation to here from annotation:wasTouchedAtPoint:controller:
in order to be reused for long-press. Generated an absolute URL since this URL might go into the pasteboard.
(-[WKPDFView annotation:wasTouchedAtPoint:controller:]): Changed to call
_highlightLinkAnnotation:forDuration:completionHandler: and _URLForLinkAnnotation:.
(-[WKPDFView annotation:isBeingPressedAtPoint:controller:]): Set values on _positionInformation and called
-[WKActionSheetAssistant showLinkSheet] after showing a highlight for .75 seconds.
(-[WKPDFView positionInformation]): Returned _positionInformation.
(-[WKPDFView performAction:]): Added a UTF-8 text and URL representation of the pressed URL to the pasteboard.
(-[WKPDFView openElementAtLocation:]): Called WebPage::navigateToURLWithSimulatedClick().
(-[WKPDFView actionsForElement:defaultActions:]): Returned actions from UIClient::actionsForElement().
(-[WKPDFView _createHighlightViewWithFrame:]): Deleted.

Canonical link: https://commits.webkit.org/156216@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@175595 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
aestes committed Nov 5, 2014
1 parent f94a5bc commit a1e875c02dd91200203c049969343196eb483dfa
Showing 5 changed files with 144 additions and 29 deletions.
@@ -1,3 +1,40 @@
2014-11-04 Andy Estes <aestes@apple.com>

[iOS] Add long press support for links in WKPDFView
https://bugs.webkit.org/show_bug.cgi?id=138357

Reviewed by Dan Bernstein.

Use WKActionSheetAssistant to show a link action sheet in response to long-pressing on a link. Have WKPDFView
conform to WKActionSheetAssistantDelegate in order to respond to the open and copy actions as well as to
provide the link's URL and position information to WKActionSheetAssistant. The long-pressed link is highlighted
for .75 seconds before the sheet is displayed in order to match UIWebPDFView.

* UIProcess/ios/WKActionSheetAssistant.h: Made protocol methods that WKPDFView doesn't implement optional.
* UIProcess/ios/WKActionSheetAssistant.mm:
(-[WKActionSheetAssistant updatePositionInformation]): Checked if delegate responds to
updatePositionInformationForActionSheetAssistant: before calling.
(-[WKActionSheetAssistant _createSheetWithElementActions:showLinkTitle:]): Checked if delegate responds to
actionSheetAssistant:willStartInteractionWithElement: before calling.
(-[WKActionSheetAssistant cleanupSheet]): Checked if delegate responds to actionSheetAssistantDidStopInteraction:
before calling.
* UIProcess/ios/WKPDFView.h: Conformed to WKActionSheetAssistantDelegate.
* UIProcess/ios/WKPDFView.mm:
(-[WKPDFView web_initWithFrame:webView:]): Instantiated a WKActionSheetAssistant and set self as its delegate.
(-[WKPDFView _highlightLinkAnnotation:forDuration:completionHandler:]): Moved highlight drawing to here from
annotation:wasTouchedAtPoint:controller: in order to be reused for long-press.
(-[WKPDFView _URLForLinkAnnotation:]): Moved URL creation to here from annotation:wasTouchedAtPoint:controller:
in order to be reused for long-press. Generated an absolute URL since this URL might go into the pasteboard.
(-[WKPDFView annotation:wasTouchedAtPoint:controller:]): Changed to call
_highlightLinkAnnotation:forDuration:completionHandler: and _URLForLinkAnnotation:.
(-[WKPDFView annotation:isBeingPressedAtPoint:controller:]): Set values on _positionInformation and called
-[WKActionSheetAssistant showLinkSheet] after showing a highlight for .75 seconds.
(-[WKPDFView positionInformation]): Returned _positionInformation.
(-[WKPDFView performAction:]): Added a UTF-8 text and URL representation of the pressed URL to the pasteboard.
(-[WKPDFView openElementAtLocation:]): Called WebPage::navigateToURLWithSimulatedClick().
(-[WKPDFView actionsForElement:defaultActions:]): Returned actions from UIClient::actionsForElement().
(-[WKPDFView _createHighlightViewWithFrame:]): Deleted.

2014-11-04 Anders Carlsson <andersca@apple.com>

Try to fix the 32-bit build.
@@ -40,15 +40,18 @@ struct InteractionInformationAtPosition;
@class _WKActivatedElementInfo;
@protocol WKActionSheetDelegate;

@protocol WKActionSheetAssistantDelegate
@protocol WKActionSheetAssistantDelegate <NSObject>
@required
- (const WebKit::InteractionInformationAtPosition&)positionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant;
- (void)updatePositionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant;
- (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant performAction:(WebKit::SheetAction)action;
- (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant openElementAtLocation:(CGPoint)location;
- (RetainPtr<NSArray>)actionSheetAssistant:(WKActionSheetAssistant *)assistant decideActionsForElement:(_WKActivatedElementInfo *)element defaultActions:(RetainPtr<NSArray>)defaultActions;

@optional
- (void)updatePositionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant;
- (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant willStartInteractionWithElement:(_WKActivatedElementInfo *)element;
- (void)actionSheetAssistantDidStopInteraction:(WKActionSheetAssistant *)assistant;

@end

@interface WKActionSheetAssistant : NSObject <WKActionSheetDelegate, DDDetectionControllerInteractionDelegate>
@@ -163,7 +163,9 @@ - (CGRect)presentationRectInHostViewForSheet

- (void)updatePositionInformation
{
[_delegate.get() updatePositionInformationForActionSheetAssistant:self];
auto delegate = _delegate.get();
if ([delegate respondsToSelector:@selector(updatePositionInformationForActionSheetAssistant:)])
[delegate updatePositionInformationForActionSheetAssistant:self];
}

- (BOOL)presentSheet
@@ -236,7 +238,9 @@ - (void)_createSheetWithElementActions:(NSArray *)actions showLinkTitle:(BOOL)sh
handler:^(UIAlertAction *action) {
[self cleanupSheet];
}]];
[delegate actionSheetAssistant:self willStartInteractionWithElement:_elementInfo.get()];

if ([delegate respondsToSelector:@selector(actionSheetAssistant:willStartInteractionWithElement:)])
[delegate actionSheetAssistant:self willStartInteractionWithElement:_elementInfo.get()];
}

- (void)showImageSheet
@@ -365,7 +369,9 @@ - (void)showDataDetectorsSheet

- (void)cleanupSheet
{
[_delegate.get() actionSheetAssistantDidStopInteraction:self];
auto delegate = _delegate.get();
if ([delegate respondsToSelector:@selector(actionSheetAssistantDidStopInteraction:)])
[delegate actionSheetAssistantDidStopInteraction:self];

[_interactionSheet doneWithSheet];
[_interactionSheet setSheetDelegate:nil];
@@ -25,12 +25,13 @@

#if PLATFORM(IOS)

#import "WKActionSheetAssistant.h"
#import "WKWebViewContentProvider.h"
#import <CorePDF/UIPDFAnnotationController.h>
#import <CorePDF/UIPDFPageView.h>
#import <UIKit/UIView.h>

@interface WKPDFView : UIView <WKWebViewContentProvider, UIPDFPageViewDelegate, UIPDFAnnotationControllerDelegate>
@interface WKPDFView : UIView <WKWebViewContentProvider, UIPDFPageViewDelegate, UIPDFAnnotationControllerDelegate, WKActionSheetAssistantDelegate>

@property (nonatomic, readonly) NSString *suggestedFilename;
@property (nonatomic, readonly) CGPDFDocumentRef pdfDocument;
@@ -28,18 +28,20 @@

#if PLATFORM(IOS)

#import "APIUIClient.h"
#import "SessionState.h"
#import "WKNSURLExtras.h"
#import "WKPDFPageNumberIndicator.h"
#import "WKWebViewInternal.h"
#import "WebPageProxy.h"
#import <CorePDF/UIPDFDocument.h>
#import <CorePDF/UIPDFLinkAnnotation.h>
#import <CorePDF/UIPDFPage.h>
#import <CorePDF/UIPDFPageView.h>
#import <MobileCoreServices/UTCoreTypes.h>
#import <UIKit/UIScrollView_Private.h>
#import <WebCore/FloatRect.h>
#import <WebCore/_UIHighlightViewSPI.h>
#import <chrono>
#import <wtf/RetainPtr.h>
#import <wtf/Vector.h>

@@ -79,6 +81,9 @@ @implementation WKPDFView {

BOOL _isStartingZoom;
BOOL _isPerformingSameDocumentNavigation;

RetainPtr<WKActionSheetAssistant> _actionSheetAssistant;
WebKit::InteractionInformationAtPosition _positionInformation;
}

- (instancetype)web_initWithFrame:(CGRect)frame webView:(WKWebView *)webView
@@ -95,6 +100,9 @@ - (instancetype)web_initWithFrame:(CGRect)frame webView:(WKWebView *)webView
[_scrollView setMaximumZoomScale:pdfMaximumZoomScale];
[_scrollView setBackgroundColor:[UIColor grayColor]];

_actionSheetAssistant = adoptNS([[WKActionSheetAssistant alloc] initWithView:self]);
[_actionSheetAssistant setDelegate:self];

return self;
}

@@ -319,17 +327,41 @@ - (void)_resetZoomAnimated:(BOOL)animated
_isStartingZoom = NO;
}

- (RetainPtr<_UIHighlightView>)_createHighlightViewWithFrame:(CGRect)frame
- (void)_highlightLinkAnnotation:(UIPDFLinkAnnotation *)linkAnnotation forDuration:(NSTimeInterval)duration completionHandler:(void (^)())completionHandler
{
static const CGFloat highlightBorderRadius = 3;
static const CGFloat highlightColorComponent = 26.0 / 255;
static UIColor *highlightColor = [[UIColor alloc] initWithRed:highlightColorComponent green:highlightColorComponent blue:highlightColorComponent alpha:0.3];

RetainPtr<_UIHighlightView> highlightView = adoptNS([[_UIHighlightView alloc] initWithFrame:CGRectInset(frame, -highlightBorderRadius, -highlightBorderRadius)]);
UIPDFPageView *pageView = linkAnnotation.annotationController.pageView;
CGRect highlightViewFrame = [self convertRect:[pageView convertRectFromPDFPageSpace:linkAnnotation.Rect] fromView:pageView];
RetainPtr<_UIHighlightView> highlightView = adoptNS([[_UIHighlightView alloc] initWithFrame:CGRectInset(highlightViewFrame, -highlightBorderRadius, -highlightBorderRadius)]);
[highlightView setOpaque:NO];
[highlightView setCornerRadius:highlightBorderRadius];
[highlightView setColor:highlightColor];
return highlightView;

ASSERT(isMainThread());
[self addSubview:highlightView.get()];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, duration * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[highlightView removeFromSuperview];
completionHandler();
});
}

- (NSURL *)_URLForLinkAnnotation:(UIPDFLinkAnnotation *)linkAnnotation
{
NSURL *documentURL = _webView.URL;

if (NSURL *url = linkAnnotation.url)
return [NSURL URLWithString:url.relativeString relativeToURL:documentURL];

if (NSUInteger pageNumber = linkAnnotation.pageNumber) {
String anchorString = ASCIILiteral("#page");
anchorString.append(String::number(pageNumber));
return [NSURL _web_URLWithWTFString:anchorString relativeToURL:documentURL];
}

return nil;
}

#pragma mark UIPDFPageViewDelegate
@@ -360,35 +392,71 @@ - (void)resetZoom:(UIPDFPageView *)pageView

- (void)annotation:(UIPDFAnnotation *)annotation wasTouchedAtPoint:(CGPoint)point controller:(UIPDFAnnotationController *)controller
{
ASSERT(isMainThread());

if (![annotation isKindOfClass:[UIPDFLinkAnnotation class]])
return;

UIPDFLinkAnnotation *linkAnnotation = (UIPDFLinkAnnotation *)annotation;
String urlString;
if (NSURL *url = linkAnnotation.url)
urlString = url.absoluteString;
else if (NSUInteger pageNumber = linkAnnotation.pageNumber) {
urlString = ASCIILiteral("#page");
urlString.append(String::number(pageNumber));
}

if (urlString.isEmpty())
RetainPtr<NSURL> url = [self _URLForLinkAnnotation:linkAnnotation];
if (!url)
return;

CGPoint documentPoint = [controller.pageView convertPoint:point toView:self];
CGPoint screenPoint = [self.window convertPoint:[self convertPoint:documentPoint toView:nil] toWindow:nil];
static const int64_t dispatchOffset = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(200)).count();
RetainPtr<WKWebView> retainedWebView = _webView;

CGRect highlightViewFrame = [self convertRect:[controller.pageView convertRectFromPDFPageSpace:annotation.Rect] fromView:controller.pageView];
RetainPtr<_UIHighlightView> highlightView = [self _createHighlightViewWithFrame:highlightViewFrame];
[self addSubview:highlightView.get()];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, dispatchOffset), dispatch_get_main_queue(), ^ {
retainedWebView->_page->navigateToURLWithSimulatedClick(urlString, roundedIntPoint(documentPoint), roundedIntPoint(screenPoint));
[highlightView removeFromSuperview];
});
[self _highlightLinkAnnotation:linkAnnotation forDuration:.2 completionHandler:^{
retainedWebView->_page->navigateToURLWithSimulatedClick([url absoluteString], roundedIntPoint(documentPoint), roundedIntPoint(screenPoint));
}];
}

- (void)annotation:(UIPDFAnnotation *)annotation isBeingPressedAtPoint:(CGPoint)point controller:(UIPDFAnnotationController *)controller
{
if (![annotation isKindOfClass:[UIPDFLinkAnnotation class]])
return;

UIPDFLinkAnnotation *linkAnnotation = (UIPDFLinkAnnotation *)annotation;
NSURL *url = [self _URLForLinkAnnotation:linkAnnotation];
if (!url)
return;

_positionInformation.url = url.absoluteString;
_positionInformation.point = roundedIntPoint([controller.pageView convertPoint:point toView:self]);
_positionInformation.bounds = roundedIntRect([self convertRect:[controller.pageView convertRectFromPDFPageSpace:annotation.Rect] fromView:controller.pageView]);

[self _highlightLinkAnnotation:linkAnnotation forDuration:.75 completionHandler:^{
[_actionSheetAssistant showLinkSheet];
}];
}

#pragma mark WKActionSheetAssistantDelegate

- (const WebKit::InteractionInformationAtPosition&)positionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant
{
return _positionInformation;
}

- (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant performAction:(WebKit::SheetAction)action
{
if (action != WebKit::SheetAction::Copy)
return;

NSDictionary *representations = @{
(NSString *)kUTTypeUTF8PlainText : _positionInformation.url,
(NSString *)kUTTypeURL : [NSURL URLWithString:_positionInformation.url]
};

[UIPasteboard generalPasteboard].items = @[ representations ];
}

- (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant openElementAtLocation:(CGPoint)location
{
CGPoint screenPoint = [self.window convertPoint:[self convertPoint:location toView:nil] toWindow:nil];
_webView->_page->navigateToURLWithSimulatedClick(_positionInformation.url, roundedIntPoint(location), roundedIntPoint(screenPoint));
}

- (RetainPtr<NSArray>)actionSheetAssistant:(WKActionSheetAssistant *)assistant decideActionsForElement:(_WKActivatedElementInfo *)element defaultActions:(RetainPtr<NSArray>)defaultActions
{
return _webView->_page->uiClient().actionsForElement(element, WTF::move(defaultActions));
}

@end

0 comments on commit a1e875c

Please sign in to comment.