Skip to content

Commit

Permalink
[UnifiedPDF] Cursor should update correctly over text and annotations
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=265866
rdar://118550951

Reviewed by Tim Horton.

UnifiedPDFPlugin has the plumbing in place to send cursor updates to the
UI process, but does not actually know what annotations/text (or other
PDF elements of interest) exist under a given point in a PDF page. This
patch builds up our hit-testing logic to imbibe UnifiedPDFPlugin with
this knowledge.

The logic to build up our set of PDF elements under a point is fairly
straightforward as we consult (and map) the type of the annotation at a
given point reported by PDFKit. As for text and images, which are not
annotations, we have to call into CoreGraphics' PDFPageLayout SPI that
returns a bitset indicating the presence of text and images under a
given page point. Note that we only do so for tagged PDFs, since the SPI
in question does not return any information for PDFs that are not
tagged.

* Source/WebCore/PAL/pal/spi/cg/CoreGraphicsSPI.h:

Provide declarations required to call into `CGPDFDocumentIsTaggedPDF`
and `CGPDFPageLayoutGetAreaOfInterestAtPoint`.

* Source/WebKit/Platform/spi/Cocoa/PDFKitSPI.h: Renamed from Source/WebKit/Platform/spi/ios/PDFKitSPI.h.

Move to a Cocoa subdirectory since this SPI declaration header is now
required in UnifiedPDFPlugin.mm.

Also, provide a declaration to be able to obtain a PDFPage object's
corresponding CGPDFPageLayout instance.

* Source/WebKit/Shared/Cocoa/PDFKitSoftLink.h:
* Source/WebKit/Shared/Cocoa/PDFKitSoftLink.mm:

Soft-link more PDFAnnotation-adjacent classes used for type equality
checks in our hit-testing logic.

Also, drive-by fix that sorts the PDFKit symbols alphabetically.

* Source/WebKit/WebKit.xcodeproj/project.pbxproj:

In addition to moving PDFKitSPI.h around, also added WebKit target
membership for the file.

* Source/WebKit/WebProcess/Plugins/PDF/UnifiedPDF/UnifiedPDFPlugin.h:
* Source/WebKit/WebProcess/Plugins/PDF/UnifiedPDF/UnifiedPDFPlugin.mm:
(WebKit::toWebCoreCursorType):

Some drive-by ergonomics nicety to avoid having to type out the
`UniversalPDFPlugin::` prefix.

(WebKit::UnifiedPDFPlugin::isTaggedPDF const):

Add a method to query whether the PDFDocument is tagged.

(WebKit::UnifiedPDFPlugin::pdfElementTypesForPluginPoint const):

The bulk of the logic described in the commit message is implemented
here.

* Tools/TestWebKitAPI/Configurations/Base.xcconfig:

Since PDFKitSPI.h was moved to a Cocoa subdirectory, include said path
in the cocoatouch header search path.

Canonical link: https://commits.webkit.org/271650@main
  • Loading branch information
aprotyas committed Dec 7, 2023
1 parent 722a26b commit 90ad901
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 15 deletions.
17 changes: 16 additions & 1 deletion Source/WebCore/PAL/pal/spi/cg/CoreGraphicsSPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

#include <CoreGraphics/CGContextDelegatePrivate.h>
#include <CoreGraphics/CGFontCache.h>
#include <CoreGraphics/CGPDFPageLayout.h>
#include <CoreGraphics/CGPathPrivate.h>
#include <CoreGraphics/CGShadingPrivate.h>
#include <CoreGraphics/CGStylePrivate.h>
Expand All @@ -50,7 +51,7 @@
#include <CoreGraphics/CGEventPrivate.h>
#endif

#else
#else // USE(APPLE_INTERNAL_SDK)

struct CGFontHMetrics {
int ascent;
Expand Down Expand Up @@ -237,6 +238,17 @@ typedef struct CGRenderingState *CGRenderingStateRef;
typedef struct CGGState *CGGStateRef;
typedef struct CGStyle *CGStyleRef;

#if ENABLE(UNIFIED_PDF)

typedef CF_OPTIONS(uint32_t, CGPDFAreaOfInterest) {
kCGPDFAreaText = (1 << 0),
kCGPDFAreaImage = (1 << 1),
};
typedef struct CGPDFPageLayout *CGPDFPageLayoutRef;
CGPDFAreaOfInterest CGPDFPageLayoutGetAreaOfInterestAtPoint(CGPDFPageLayoutRef, CGPoint);

#endif // ENABLE(UNIFIED_PDF)

#endif // USE(APPLE_INTERNAL_SDK)

#if PLATFORM(COCOA)
Expand Down Expand Up @@ -290,6 +302,9 @@ CGImageCachingFlags CGImageGetCachingFlags(CGImageRef);
void CGImageSetProperty(CGImageRef, CFStringRef, CFTypeRef);

CGDataProviderRef CGPDFDocumentGetDataProvider(CGPDFDocumentRef);
#if ENABLE(UNIFIED_PDF)
bool CGPDFDocumentIsTaggedPDF(CGPDFDocumentRef);
#endif // ENABLE(UNIFIED_PDF)

CGFontAntialiasingStyle CGContextGetFontAntialiasingStyle(CGContextRef);
void CGContextSetFontAntialiasingStyle(CGContextRef, CGFontAntialiasingStyle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,19 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#pragma once

#import <pal/spi/cg/CoreGraphicsSPI.h>

#if USE(APPLE_INTERNAL_SDK)

#if HAVE(PDFKIT) && PLATFORM(IOS_FAMILY)
#import <PDFKit/PDFHostViewController.h>
#endif // HAVE(PDFKIT) && PLATFORM(IOS_FAMILY)

#else

#if HAVE(PDFKIT) && PLATFORM(IOS_FAMILY)
#import "UIKitSPI.h"

@interface _UIRemoteViewController : UIViewController
Expand Down Expand Up @@ -64,5 +71,12 @@
- (void) snapshotViewRect: (CGRect) rect snapshotWidth: (NSNumber*) width afterScreenUpdates: (BOOL) afterScreenUpdates withResult: (void (^)(UIImage* image)) completion;

@end
#endif // HAVE(PDFKIT) && PLATFORM(IOS_FAMILY)

#endif
#endif // USE(APPLE_INTERNAL_SDK)

#if ENABLE(UNIFIED_PDF)
@interface PDFPage (IPI)
- (CGPDFPageLayoutRef) pageLayout;
@end
#endif // ENABLE(UNIFIED_PDF)
7 changes: 5 additions & 2 deletions Source/WebKit/Shared/Cocoa/PDFKitSoftLink.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@
SOFT_LINK_FRAMEWORK_FOR_HEADER(WebKit, PDFKit)

ALLOW_DEPRECATED_DECLARATIONS_BEGIN
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFAnnotationButtonWidget)
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFAnnotationChoiceWidget)
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFAnnotationLink)
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFAnnotationPopup)
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFAnnotationText)
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFAnnotationTextWidget)
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFDocument)
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFLayerController)
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFAnnotationTextWidget)
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFAnnotationChoiceWidget)
SOFT_LINK_CLASS_FOR_HEADER(WebKit, PDFSelection)
ALLOW_DEPRECATED_DECLARATIONS_END

Expand Down
7 changes: 5 additions & 2 deletions Source/WebKit/Shared/Cocoa/PDFKitSoftLink.mm
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@
SOFT_LINK_FRAMEWORK_FOR_SOURCE(WebKit, PDFKit)

ALLOW_DEPRECATED_DECLARATIONS_BEGIN
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFAnnotationButtonWidget)
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFAnnotationChoiceWidget)
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFAnnotationLink)
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFAnnotationPopup)
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFAnnotationText)
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFAnnotationTextWidget)
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFDocument)
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFLayerController)
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFAnnotationTextWidget)
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFAnnotationChoiceWidget)
SOFT_LINK_CLASS_FOR_SOURCE(WebKit, PDFKit, PDFSelection)
ALLOW_DEPRECATED_DECLARATIONS_END

Expand Down
4 changes: 3 additions & 1 deletion Source/WebKit/WebKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,7 @@
337822472947FBA5002106BB /* WebExtensionUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 337822452947FBA4002106BB /* WebExtensionUtilities.h */; };
337822482947FBA5002106BB /* WebExtensionUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = 337822462947FBA4002106BB /* WebExtensionUtilities.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
3378F2FD2B1FCA6A00362EF3 /* WebExtensionMatchedRuleParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 3378F2FB2B1FCA5C00362EF3 /* WebExtensionMatchedRuleParameters.h */; };
33AE61252B20F13B00E1C215 /* PDFKitSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 3178AF9720E2A7F80074DE94 /* PDFKitSPI.h */; };
33B5A80C2AFD298100A15D40 /* WebExtensionFrameParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B5A80B2AFD298100A15D40 /* WebExtensionFrameParameters.h */; };
33B5A80F2AFD5DE800A15D40 /* WebExtensionContextAPIWebNavigationCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 33B5A80E2AFD5DE800A15D40 /* WebExtensionContextAPIWebNavigationCocoa.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
33F68338293FF6F5005C63C0 /* JSWebExtensionAPIWebNavigationEvent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 33F68336293FF6F5005C63C0 /* JSWebExtensionAPIWebNavigationEvent.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
Expand Down Expand Up @@ -10512,6 +10513,7 @@
57B826402304EB3E00B72EB0 /* NearFieldSPI.h */,
3754D5441B3A29FD003A4C7F /* NSInvocationSPI.h */,
F4EB4AFC269CD23600D297AE /* OSStateSPI.h */,
3178AF9720E2A7F80074DE94 /* PDFKitSPI.h */,
5CB6AE432609799C00B6ED5A /* ReasonSPI.h */,
0E97D74C200E8FF300BF6643 /* SafeBrowsingSPI.h */,
448AC24D267135A600B28921 /* SynapseSPI.h */,
Expand Down Expand Up @@ -14654,7 +14656,6 @@
2D4AF0882044C3C4006C8817 /* FrontBoardServicesSPI.h */,
4193927728BE41C000162139 /* LSApplicationWorkspaceSPI.h */,
A13B3DA1207F39DE0090C58D /* MobileWiFiSPI.h */,
3178AF9720E2A7F80074DE94 /* PDFKitSPI.h */,
E5DEFA6726F8F42600AB68DB /* PhotosUISPI.h */,
2D279E1826955768004B3EEB /* PrototypeToolsSPI.h */,
46F38E8B2416E66D0059375A /* RunningBoardServicesSPI.h */,
Expand Down Expand Up @@ -15524,6 +15525,7 @@
A1798B43222D98DF000764BD /* PaymentAuthorizationViewController.h in Headers */,
950FECF5285027200002DE4E /* PaymentTokenContext.h in Headers */,
C1E123BA20A11573002646F4 /* PDFContextMenu.h in Headers */,
33AE61252B20F13B00E1C215 /* PDFKitSPI.h in Headers */,
5C298DA01C3DF02100470AFE /* PendingDownload.h in Headers */,
832ED18C1E2FE157006BA64A /* PerActivityStateCPUUsageSampler.h in Headers */,
7AFBD36F21E546F8005DBACB /* PersistencyUtils.h in Headers */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ class UnifiedPDFPlugin final : public PDFPluginBase, public WebCore::GraphicsLay
WebCore::IntPoint convertFromDocumentToPage(const WebCore::IntPoint&, PDFDocumentLayout::PageIndex) const;
PDFElementTypes pdfElementTypesForPluginPoint(const WebCore::IntPoint&) const;

bool isTaggedPDF() const;

PDFDocumentLayout m_documentLayout;
RefPtr<WebCore::GraphicsLayer> m_rootLayer;
RefPtr<WebCore::GraphicsLayer> m_scrollContainerLayer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#if ENABLE(UNIFIED_PDF)

#include "PDFContextMenu.h"
#include "PDFKitSPI.h"
#include "PluginView.h"
#include "WebEventConversion.h"
#include "WebEventType.h"
Expand All @@ -52,6 +53,9 @@
#include <WebCore/RenderLayerBacking.h>
#include <WebCore/RenderLayerCompositor.h>
#include <WebCore/ScrollTypes.h>
#include <pal/spi/cg/CoreGraphicsSPI.h>

#include "PDFKitSoftLink.h"

#if PLATFORM(IOS_FAMILY)
#import <UIKit/UIColor.h>
Expand Down Expand Up @@ -455,10 +459,12 @@

static WebCore::Cursor::Type toWebCoreCursorType(UnifiedPDFPlugin::PDFElementTypes pdfElementTypes, AltKeyIsActive altKeyIsActive = AltKeyIsActive::No)
{
if (pdfElementTypes.containsAny({ UnifiedPDFPlugin::PDFElementType::Link, UnifiedPDFPlugin::PDFElementType::Control, UnifiedPDFPlugin::PDFElementType::Icon }) || altKeyIsActive == AltKeyIsActive::Yes)
using PDFElementType = UnifiedPDFPlugin::PDFElementType;

if (pdfElementTypes.containsAny({ PDFElementType::Link, PDFElementType::Control, PDFElementType::Icon }) || altKeyIsActive == AltKeyIsActive::Yes)
return WebCore::Cursor::Type::Hand;

if (pdfElementTypes.containsAny({ UnifiedPDFPlugin::PDFElementType::Text, UnifiedPDFPlugin::PDFElementType::TextField }))
if (pdfElementTypes.containsAny({ PDFElementType::Text, PDFElementType::TextField }))
return WebCore::Cursor::Type::IBeam;

return WebCore::Cursor::Type::Pointer;
Expand Down Expand Up @@ -532,15 +538,44 @@
if (!maybeNearestPageIndex || *maybeNearestPageIndex >= m_documentLayout.pageCount())
return { };
auto nearestPageIndex = *maybeNearestPageIndex;
auto nearestPage = m_documentLayout.pageAtIndex(nearestPageIndex);
auto pointInPDFPageSpace = convertFromDocumentToPage(pointInDocumentSpace, nearestPageIndex);

if (auto annotation = [m_documentLayout.pageAtIndex(nearestPageIndex) annotationAtPoint:pointInPDFPageSpace]) {
// FIXME: Reason about the annotation type to construct the full set of PDFElementTypes at this page point.
UNUSED_VARIABLE(annotation);
PDFElementTypes pdfElementTypes { PDFElementType::Page };

if (auto annotation = [nearestPage annotationAtPoint:pointInPDFPageSpace]) {
pdfElementTypes.add(PDFElementType::Annotation);

if ([annotation isKindOfClass:getPDFAnnotationLinkClass()])
pdfElementTypes.add(PDFElementType::Link);

if ([annotation isKindOfClass:getPDFAnnotationPopupClass()])
pdfElementTypes.add(PDFElementType::Popup);

if ([annotation isKindOfClass:getPDFAnnotationTextClass()])
pdfElementTypes.add(PDFElementType::Icon);

if ([annotation isKindOfClass:getPDFAnnotationTextWidgetClass()] && ![annotation isReadOnly])
pdfElementTypes.add(PDFElementType::TextField);

if ([annotation isKindOfClass:getPDFAnnotationButtonWidgetClass()] && ![annotation isReadOnly])
pdfElementTypes.add(PDFElementType::Control);
}

// Default element if we could map to a PDF page at all.
return { PDFElementType::Page };
if (!isTaggedPDF())
return pdfElementTypes;

if (auto pageLayout = [nearestPage pageLayout]) {
CGPDFAreaOfInterest areaOfInterest = CGPDFPageLayoutGetAreaOfInterestAtPoint(pageLayout, pointInPDFPageSpace);
if (areaOfInterest & kCGPDFAreaText)
pdfElementTypes.add(PDFElementType::Text);
if (areaOfInterest & kCGPDFAreaImage)
pdfElementTypes.add(PDFElementType::Image);
}

// FIXME: <https://webkit.org/b/265908> Cursor updates are incorrect over text/image elements for untagged PDFs.

return pdfElementTypes;
}

bool UnifiedPDFPlugin::handleMouseEvent(const WebMouseEvent& event)
Expand Down Expand Up @@ -728,6 +763,11 @@

#endif // ENABLE(PDF_HUD)

bool UnifiedPDFPlugin::isTaggedPDF() const
{
return CGPDFDocumentIsTaggedPDF([m_pdfDocument documentRef]);
}

} // namespace WebKit

#endif // ENABLE(UNIFIED_PDF)
2 changes: 1 addition & 1 deletion Tools/TestWebKitAPI/Configurations/Base.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ ALTERNATE_HEADER_SEARCH_PATHS = $(ALTERNATE_HEADER_SEARCH_PATHS_$(SDK_VARIANT));
ALTERNATE_HEADER_SEARCH_PATHS_iosmac = $(BUILT_PRODUCTS_DIR)$(WK_ALTERNATE_FRAMEWORKS_DIR)/usr/local/include

PROJECT_HEADER_SEARCH_PATHS = $(SRCROOT)/../../Source/WebKit/Platform/cocoa $(PROJECT_HEADER_SEARCH_PATHS_$(WK_COCOA_TOUCH)) $(SRCROOT)/../TestRunnerShared/spi;
PROJECT_HEADER_SEARCH_PATHS_cocoatouch = $(inherited) $(SRCROOT)/../../Source/WebKit/Platform/spi/ios $(SRCROOT)/../../Source/WebKit/UIProcess/ios;
PROJECT_HEADER_SEARCH_PATHS_cocoatouch = $(inherited) $(SRCROOT)/../../Source/WebKit/Platform/spi/Cocoa $(SRCROOT)/../../Source/WebKit/Platform/spi/ios $(SRCROOT)/../../Source/WebKit/UIProcess/ios;

HEADER_SEARCH_PATHS = $(ALTERNATE_HEADER_SEARCH_PATHS) ${BUILT_PRODUCTS_DIR}/usr/local/include $(WEBCORE_PRIVATE_HEADERS_DIR)/ForwardingHeaders $(BUILT_PRODUCTS_DIR)/WebCoreTestSupport $(BUILT_PRODUCTS_DIR)/WebKitTestSupport ${SRCROOT} $(PROJECT_HEADER_SEARCH_PATHS);
SYSTEM_HEADER_SEARCH_PATHS = $(inherited) $(WK_PRIVATE_SDK_DIR)$(WK_ALTERNATE_WEBKIT_SDK_PATH)$(WK_LIBRARY_HEADERS_FOLDER_PATH);
Expand Down

0 comments on commit 90ad901

Please sign in to comment.