7b0b38b7454461c7a279a75373e6589ede16352a
[WebKit-https.git] / Source / WebKit / UIProcess / mac / WKPrintingView.mm
1 /*
2  * Copyright (C) 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKPrintingView.h"
28
29 #if PLATFORM(MAC)
30
31 #import "APIData.h"
32 #import "Logging.h"
33 #import "PDFKitImports.h"
34 #import "PrintInfo.h"
35 #import "ShareableBitmap.h"
36 #import "WebPageProxy.h"
37 #import <PDFKit/PDFKit.h>
38 #import <WebCore/GraphicsContext.h>
39 #import <WebCore/LocalDefaultSystemAppearance.h>
40 #import <WebCore/WebCoreObjCExtras.h>
41 #import <wtf/RunLoop.h>
42
43 NSString * const WebKitOriginalTopPrintingMarginKey = @"WebKitOriginalTopMargin";
44 NSString * const WebKitOriginalBottomPrintingMarginKey = @"WebKitOriginalBottomMargin";
45
46 NSString * const NSPrintInfoDidChangeNotification = @"NSPrintInfoDidChange";
47
48 static BOOL isForcingPreviewUpdate;
49
50 @implementation WKPrintingView
51
52 - (id)initWithFrameProxy:(WebKit::WebFrameProxy&)frame view:(NSView *)wkView
53 {
54     self = [super init]; // No frame rect to pass to NSView.
55     if (!self)
56         return nil;
57
58     _webFrame = &frame;
59     _wkView = wkView;
60
61     return self;
62 }
63
64 - (void)dealloc
65 {
66     if (WebCoreObjCScheduleDeallocateOnMainThread([WKPrintingView class], self))
67         return;
68
69     [super dealloc];
70 }
71
72 - (BOOL)isFlipped
73 {
74     return YES;
75 }
76
77 - (void)_setAutodisplay:(BOOL)newState
78 {
79 #pragma clang diagnostic push
80 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
81     if (!newState && [[_wkView window] isAutodisplay])
82 #pragma clang diagnostic pop
83         [_wkView displayIfNeeded];
84     
85 #pragma clang diagnostic push
86 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
87     [[_wkView window] setAutodisplay:newState];
88 #pragma clang diagnostic pop
89
90     // For some reason, painting doesn't happen for a long time without this call, <rdar://problem/8975229>.
91     if (newState)
92         [_wkView displayIfNeeded];
93 }
94
95
96 - (void)_suspendAutodisplay
97 {
98     // A drawRect: call on WKView causes a switch to screen mode, which is slow due to relayout, and we want to avoid that.
99     // Disabling autodisplay will prevent random updates from causing this, but resizing the window will still work.
100     if (_autodisplayResumeTimer) {
101         [_autodisplayResumeTimer invalidate];
102         _autodisplayResumeTimer = nil;
103     } else
104         [self _setAutodisplay:NO];
105 }
106
107 - (void)_delayedResumeAutodisplayTimerFired
108 {
109     ASSERT(RunLoop::isMain());
110     
111     _autodisplayResumeTimer = nil;
112     [self _setAutodisplay:YES];
113
114     // Enabling autodisplay normally implicitly calls endPrinting() via -[WKView drawRect:], but not when content is in accelerated compositing mode.
115     if (_webFrame->page())
116         _webFrame->page()->endPrinting();
117 }
118
119 - (void)_delayedResumeAutodisplay
120 {
121     // AppKit calls endDocument/beginDocument when print option change. We don't want to switch between print and screen mode just for that,
122     // and enabling autodisplay may result in switching into screen mode. So, autodisplay is only resumed on next run loop iteration.
123     if (!_autodisplayResumeTimer) {
124         _autodisplayResumeTimer = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(_delayedResumeAutodisplayTimerFired) userInfo:nil repeats:NO];
125         // The timer must be scheduled on main thread, because printing thread may finish before it fires.
126         [[NSRunLoop mainRunLoop] addTimer:_autodisplayResumeTimer forMode:NSDefaultRunLoopMode];
127     }
128 }
129
130 - (void)_adjustPrintingMarginsForHeaderAndFooter
131 {
132     ASSERT(RunLoop::isMain()); // This function calls the client, which should only be done on main thread.
133
134     NSPrintInfo *info = [_printOperation printInfo];
135     NSMutableDictionary *infoDictionary = [info dictionary];
136
137     // We need to modify the top and bottom margins in the NSPrintInfo to account for the space needed by the
138     // header and footer. Because this method can be called more than once on the same NSPrintInfo (see 5038087),
139     // we stash away the unmodified top and bottom margins the first time this method is called, and we read from
140     // those stashed-away values on subsequent calls.
141     double originalTopMargin;
142     double originalBottomMargin;
143     NSNumber *originalTopMarginNumber = [infoDictionary objectForKey:WebKitOriginalTopPrintingMarginKey];
144     if (!originalTopMarginNumber) {
145         ASSERT(![infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey]);
146         originalTopMargin = [info topMargin];
147         originalBottomMargin = [info bottomMargin];
148         [infoDictionary setObject:[NSNumber numberWithDouble:originalTopMargin] forKey:WebKitOriginalTopPrintingMarginKey];
149         [infoDictionary setObject:[NSNumber numberWithDouble:originalBottomMargin] forKey:WebKitOriginalBottomPrintingMarginKey];
150     } else {
151         ASSERT([originalTopMarginNumber isKindOfClass:[NSNumber class]]);
152         ASSERT([[infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey] isKindOfClass:[NSNumber class]]);
153         originalTopMargin = [originalTopMarginNumber doubleValue];
154         originalBottomMargin = [[infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey] doubleValue];
155     }
156     
157     CGFloat scale = [info scalingFactor];
158     [info setTopMargin:originalTopMargin + _webFrame->page()->headerHeight(*_webFrame) * scale];
159     [info setBottomMargin:originalBottomMargin + _webFrame->page()->footerHeight(*_webFrame) * scale];
160 }
161
162 - (BOOL)_isPrintingPreview
163 {
164     // <rdar://problem/8901041> Please add an API returning whether the current print operation is for preview.
165     // Assuming that if NSPrintOperation is allowed to spawn a thread for printing, it will. Print preview doesn't spawn a thread.
166     return !_isPrintingFromSecondaryThread;
167 }
168
169 - (void)_updatePreview
170 {
171     // <rdar://problem/8900923> Please add an API to force print preview update.
172     ASSERT(!isForcingPreviewUpdate);
173     isForcingPreviewUpdate = YES;
174     [[NSNotificationCenter defaultCenter] postNotificationName:NSPrintInfoDidChangeNotification object:nil];
175     isForcingPreviewUpdate = NO;
176 }
177
178 - (BOOL)_hasPageRects
179 {
180     // WebCore always prints at least one page.
181     return !_printingPageRects.isEmpty();
182 }
183
184 - (NSUInteger)_firstPrintedPageNumber
185 {
186     // Need to directly access the dictionary because -[NSPrintOperation pageRange] verifies pagination, potentially causing recursion.
187     return [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintFirstPage] unsignedIntegerValue];
188 }
189
190 - (NSUInteger)_lastPrintedPageNumber
191 {
192     ASSERT([self _hasPageRects]);
193
194     // Need to directly access the dictionary because -[NSPrintOperation pageRange] verifies pagination, potentially causing recursion.
195     NSUInteger firstPage = [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintFirstPage] unsignedIntegerValue];
196     NSUInteger lastPage = [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintLastPage] unsignedIntegerValue];
197     if (lastPage - firstPage >= _printingPageRects.size())
198         return _printingPageRects.size();
199     return lastPage;
200 }
201
202 - (uint64_t)_expectedPreviewCallbackForRect:(const WebCore::IntRect&)rect
203 {
204     for (HashMap<uint64_t, WebCore::IntRect>::iterator iter = _expectedPreviewCallbacks.begin(); iter != _expectedPreviewCallbacks.end(); ++iter) {
205         if (iter->value  == rect)
206             return iter->key;
207     }
208     return 0;
209 }
210
211 struct IPCCallbackContext {
212     RetainPtr<WKPrintingView> view;
213     uint64_t callbackID;
214 };
215
216 static void pageDidDrawToImage(const WebKit::ShareableBitmap::Handle& imageHandle, IPCCallbackContext* context)
217 {
218     ASSERT(RunLoop::isMain());
219
220     WKPrintingView *view = context->view.get();
221
222     // If the user has already changed print setup, then this response is obsolete. And if this callback is not in response to the latest request,
223     // then the user has already moved to another page - we'll cache the response, but won't draw it.
224     HashMap<uint64_t, WebCore::IntRect>::iterator iter = view->_expectedPreviewCallbacks.find(context->callbackID);
225     if (iter != view->_expectedPreviewCallbacks.end()) {
226         ASSERT([view _isPrintingPreview]);
227
228         if (!imageHandle.isNull()) {
229             auto image = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
230
231             if (image)
232                 view->_pagePreviews.add(iter->value, image);
233         }
234
235         view->_expectedPreviewCallbacks.remove(context->callbackID);
236         bool receivedResponseToLatestRequest = view->_latestExpectedPreviewCallback == context->callbackID;
237         if (receivedResponseToLatestRequest) {
238             view->_latestExpectedPreviewCallback = 0;
239             [view _updatePreview];
240         }
241     }
242 }
243
244 - (void)_preparePDFDataForPrintingOnSecondaryThread
245 {
246     ASSERT(RunLoop::isMain());
247
248     if (!_webFrame->page()) {
249         _printingCallbackCondition.notifyOne();
250         return;
251     }
252
253     std::lock_guard<Lock> lock(_printingCallbackMutex);
254
255     ASSERT([self _hasPageRects]);
256     ASSERT(_printedPagesData.isEmpty());
257     ASSERT(!_printedPagesPDFDocument);
258     ASSERT(!_expectedPrintCallback);
259
260     NSUInteger firstPage = [self _firstPrintedPageNumber];
261     NSUInteger lastPage = [self _lastPrintedPageNumber];
262
263     ASSERT(firstPage > 0);
264     ASSERT(firstPage <= lastPage);
265     LOG(Printing, "WKPrintingView requesting PDF data for pages %u...%u", firstPage, lastPage);
266
267     WebKit::PrintInfo printInfo([_printOperation printInfo]);
268     // Return to printing mode if we're already back to screen (e.g. due to window resizing).
269     _webFrame->page()->beginPrinting(_webFrame.get(), printInfo);
270
271     IPCCallbackContext* context = new IPCCallbackContext;
272     auto callback = WebKit::DataCallback::create([context](API::Data* data, WebKit::CallbackBase::Error) {
273         ASSERT(RunLoop::isMain());
274
275         std::unique_ptr<IPCCallbackContext> contextDeleter(context);
276         WKPrintingView *view = context->view.get();
277
278         if (context->callbackID == view->_expectedPrintCallback) {
279             ASSERT(![view _isPrintingPreview]);
280             ASSERT(view->_printedPagesData.isEmpty());
281             ASSERT(!view->_printedPagesPDFDocument);
282             if (data)
283                 view->_printedPagesData.append(data->bytes(), data->size());
284             view->_expectedPrintCallback = 0;
285             view->_printingCallbackCondition.notifyOne();
286         }
287     });
288     _expectedPrintCallback = callback->callbackID().toInteger();
289
290     context->view = self;
291     context->callbackID = callback->callbackID().toInteger();
292
293     _webFrame->page()->drawPagesToPDF(_webFrame.get(), printInfo, firstPage - 1, lastPage - firstPage + 1, WTFMove(callback));
294 }
295
296 static void pageDidComputePageRects(const Vector<WebCore::IntRect>& pageRects, double totalScaleFactorForPrinting, IPCCallbackContext* context)
297 {
298     ASSERT(RunLoop::isMain());
299
300     WKPrintingView *view = context->view.get();
301
302     // If the user has already changed print setup, then this response is obsolete.
303     if (context->callbackID == view->_expectedComputedPagesCallback) {
304         ASSERT(RunLoop::isMain());
305         ASSERT(view->_expectedPreviewCallbacks.isEmpty());
306         ASSERT(!view->_latestExpectedPreviewCallback);
307         ASSERT(!view->_expectedPrintCallback);
308         ASSERT(view->_pagePreviews.isEmpty());
309         view->_expectedComputedPagesCallback = 0;
310
311         view->_printingPageRects = pageRects;
312         view->_totalScaleFactorForPrinting = totalScaleFactorForPrinting;
313
314         // Sanitize a response coming from the Web process.
315         if (view->_printingPageRects.isEmpty())
316             view->_printingPageRects.append(WebCore::IntRect(0, 0, 1, 1));
317         if (view->_totalScaleFactorForPrinting <= 0)
318             view->_totalScaleFactorForPrinting = 1;
319
320         const WebCore::IntRect& lastPrintingPageRect = view->_printingPageRects[view->_printingPageRects.size() - 1];
321         NSRect newFrameSize = NSMakeRect(0, 0, 
322             ceil(lastPrintingPageRect.maxX() * view->_totalScaleFactorForPrinting), 
323             ceil(lastPrintingPageRect.maxY() * view->_totalScaleFactorForPrinting));
324         LOG(Printing, "WKPrintingView setting frame size to x:%g y:%g width:%g height:%g", newFrameSize.origin.x, newFrameSize.origin.y, newFrameSize.size.width, newFrameSize.size.height);
325         [view setFrame:newFrameSize];
326
327         if ([view _isPrintingPreview]) {
328             // Show page count, and ask for an actual image to replace placeholder.
329             [view _updatePreview];
330         } else {
331             // When printing, request everything we'll need beforehand.
332             [view _preparePDFDataForPrintingOnSecondaryThread];
333         }
334     }
335 }
336
337 - (BOOL)_askPageToComputePageRects
338 {
339     ASSERT(RunLoop::isMain());
340
341     if (!_webFrame->page())
342         return NO;
343
344     ASSERT(!_expectedComputedPagesCallback);
345
346     IPCCallbackContext* context = new IPCCallbackContext;
347     auto callback = WebKit::ComputedPagesCallback::create([context](const Vector<WebCore::IntRect>& pageRects, double totalScaleFactorForPrinting, WebKit::CallbackBase::Error) {
348         std::unique_ptr<IPCCallbackContext> contextDeleter(context);
349         pageDidComputePageRects(pageRects, totalScaleFactorForPrinting, context);
350     });
351     _expectedComputedPagesCallback = callback->callbackID().toInteger();
352     context->view = self;
353     context->callbackID = _expectedComputedPagesCallback;
354
355     _webFrame->page()->computePagesForPrinting(_webFrame.get(), WebKit::PrintInfo([_printOperation printInfo]), WTFMove(callback));
356     return YES;
357 }
358
359 static void prepareDataForPrintingOnSecondaryThread(WKPrintingView *view)
360 {
361     ASSERT(RunLoop::isMain());
362
363     std::lock_guard<Lock> lock(view->_printingCallbackMutex);
364
365     // We may have received page rects while a message to call this function traveled from secondary thread to main one.
366     if ([view _hasPageRects]) {
367         [view _preparePDFDataForPrintingOnSecondaryThread];
368         return;
369     }
370
371     // A request for pages has already been made, just wait for it to finish.
372     if (view->_expectedComputedPagesCallback)
373         return;
374
375     [view _askPageToComputePageRects];
376 }
377
378 - (BOOL)knowsPageRange:(NSRangePointer)range
379 {
380     LOG(Printing, "-[WKPrintingView %p knowsPageRange:], %s, %s", self, [self _hasPageRects] ? "print data is available" : "print data is not available yet", RunLoop::isMain() ? "on main thread" : "on secondary thread");
381     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
382
383     // Assuming that once we switch to printing from a secondary thread, we don't go back.
384     ASSERT(!_isPrintingFromSecondaryThread || !RunLoop::isMain());
385     if (!RunLoop::isMain())
386         _isPrintingFromSecondaryThread = YES;
387
388     if (!_webFrame->page()) {
389         *range = NSMakeRange(1, NSIntegerMax);
390         return YES;
391     }
392
393     [self _suspendAutodisplay];
394     
395     [self performSelectorOnMainThread:@selector(_adjustPrintingMarginsForHeaderAndFooter) withObject:nil waitUntilDone:YES];
396
397     if ([self _hasPageRects])
398         *range = NSMakeRange(1, _printingPageRects.size());
399     else if (!RunLoop::isMain()) {
400         ASSERT(![self _isPrintingPreview]);
401         std::unique_lock<Lock> lock(_printingCallbackMutex);
402
403         RunLoop::main().dispatch([self] {
404             prepareDataForPrintingOnSecondaryThread(self);
405         });
406
407         _printingCallbackCondition.wait(lock);
408         *range = NSMakeRange(1, _printingPageRects.size());
409     } else {
410         ASSERT([self _isPrintingPreview]);
411
412         // If a request for pages hasn't already been made, make it now.
413         if (!_expectedComputedPagesCallback)
414             [self _askPageToComputePageRects];
415
416         *range = NSMakeRange(1, NSIntegerMax);
417     }
418     return YES;
419 }
420
421 - (unsigned)_pageForRect:(NSRect)rect
422 {
423     // Assuming that rect exactly matches one of the pages.
424     for (size_t i = 0; i < _printingPageRects.size(); ++i) {
425         WebCore::IntRect currentRect(_printingPageRects[i]);
426         currentRect.scale(_totalScaleFactorForPrinting);
427         if (rect.origin.y == currentRect.y() && rect.origin.x == currentRect.x())
428             return i + 1;
429     }
430     ASSERT_NOT_REACHED();
431     return 0; // Invalid page number.
432 }
433
434 static CFStringRef linkDestinationName(PDFDocument *document, PDFDestination *destination)
435 {
436     return (CFStringRef)[NSString stringWithFormat:@"%lu-%f-%f", (unsigned long)[document indexForPage:destination.page], destination.point.x, destination.point.y];
437 }
438
439 - (void)_drawPDFDocument:(PDFDocument *)pdfDocument page:(unsigned)page atPoint:(NSPoint)point
440 {
441     if (!pdfDocument) {
442         LOG_ERROR("Couldn't create a PDF document with data passed for preview");
443         return;
444     }
445
446     PDFPage *pdfPage;
447     @try {
448         pdfPage = [pdfDocument pageAtIndex:page];
449     } @catch (id exception) {
450         LOG_ERROR("Preview data doesn't have page %d: %@", page, exception);
451         return;
452     }
453
454     NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
455 #pragma clang diagnostic push
456 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
457     CGContextRef context = static_cast<CGContextRef>([nsGraphicsContext graphicsPort]);
458 #pragma clang diagnostic pop
459
460     CGContextSaveGState(context);
461     CGContextTranslateCTM(context, point.x, point.y);
462     CGContextScaleCTM(context, _totalScaleFactorForPrinting, -_totalScaleFactorForPrinting);
463     CGContextTranslateCTM(context, 0, -[pdfPage boundsForBox:kPDFDisplayBoxMediaBox].size.height);
464 #pragma clang diagnostic push
465 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
466     [pdfPage drawWithBox:kPDFDisplayBoxMediaBox];
467 #pragma clang diagnostic pop
468
469     CGAffineTransform transform = CGContextGetCTM(context);
470
471     for (const auto& destination : _linkDestinationsPerPage[page]) {
472         CGPoint destinationPoint = CGPointApplyAffineTransform(NSPointToCGPoint([destination point]), transform);
473         CGPDFContextAddDestinationAtPoint(context, linkDestinationName(pdfDocument, destination.get()), destinationPoint);
474     }
475
476     for (PDFAnnotation *annotation in [pdfPage annotations]) {
477         if (![annotation isKindOfClass:WebKit::pdfAnnotationLinkClass()])
478             continue;
479
480 #pragma clang diagnostic push
481 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
482         PDFAnnotationLink *linkAnnotation = (PDFAnnotationLink *)annotation;
483 #pragma clang diagnostic pop
484         NSURL *url = [linkAnnotation URL];
485         CGRect transformedRect = CGRectApplyAffineTransform(NSRectToCGRect([linkAnnotation bounds]), transform);
486
487         if (!url) {
488             PDFDestination *destination = [linkAnnotation destination];
489             if (!destination)
490                 continue;
491             CGPDFContextSetDestinationForRect(context, linkDestinationName(pdfDocument, destination), transformedRect);
492
493             continue;
494         }
495
496         CGPDFContextSetURLForRect(context, (CFURLRef)url, transformedRect);
497     }
498
499     CGContextRestoreGState(context);
500 }
501
502 - (void)_drawPreview:(NSRect)nsRect
503 {
504     ASSERT(RunLoop::isMain());
505
506     WebCore::IntRect scaledPrintingRect(nsRect);
507     scaledPrintingRect.scale(1 / _totalScaleFactorForPrinting);
508     WebCore::IntSize imageSize(nsRect.size);
509     imageSize.scale(_webFrame->page()->deviceScaleFactor());
510     HashMap<WebCore::IntRect, RefPtr<WebKit::ShareableBitmap>>::iterator pagePreviewIterator = _pagePreviews.find(scaledPrintingRect);
511     if (pagePreviewIterator == _pagePreviews.end())  {
512         // It's too early to ask for page preview if we don't even know page size and scale.
513         if ([self _hasPageRects]) {
514             if (uint64_t existingCallback = [self _expectedPreviewCallbackForRect:scaledPrintingRect]) {
515                 // We've already asked for a preview of this page, and are waiting for response.
516                 // There is no need to ask again.
517                 _latestExpectedPreviewCallback = existingCallback;
518             } else {
519                 // Preview isn't available yet, request it asynchronously.
520                 if (!_webFrame->page())
521                     return;
522
523                 // Return to printing mode if we're already back to screen (e.g. due to window resizing).
524                 _webFrame->page()->beginPrinting(_webFrame.get(), WebKit::PrintInfo([_printOperation printInfo]));
525
526                 IPCCallbackContext* context = new IPCCallbackContext;
527                 auto callback = WebKit::ImageCallback::create([context](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error) {
528                     std::unique_ptr<IPCCallbackContext> contextDeleter(context);
529                     pageDidDrawToImage(imageHandle, context);
530                 });
531                 _latestExpectedPreviewCallback = callback->callbackID().toInteger();
532                 _expectedPreviewCallbacks.add(_latestExpectedPreviewCallback, scaledPrintingRect);
533
534                 context->view = self;
535                 context->callbackID = callback->callbackID().toInteger();
536
537                 _webFrame->page()->drawRectToImage(_webFrame.get(), WebKit::PrintInfo([_printOperation printInfo]), scaledPrintingRect, imageSize, WTFMove(callback));
538                 return;
539             }
540         }
541
542         // FIXME: Draw a placeholder
543         return;
544     }
545
546     RefPtr<WebKit::ShareableBitmap> bitmap = pagePreviewIterator->value;
547 #pragma clang diagnostic push
548 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
549     CGContextRef cgContext = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
550 #pragma clang diagnostic pop
551
552     WebCore::GraphicsContext context(cgContext);
553     WebCore::GraphicsContextStateSaver stateSaver(context);
554
555     bitmap->paint(context, _webFrame->page()->deviceScaleFactor(), WebCore::IntPoint(nsRect.origin), bitmap->bounds());
556 }
557
558 - (void)drawRect:(NSRect)nsRect
559 {
560     LOG(Printing, "WKPrintingView %p printing rect x:%g, y:%g, width:%g, height:%g%s", self, nsRect.origin.x, nsRect.origin.y, nsRect.size.width, nsRect.size.height, [self _isPrintingPreview] ? " for preview" : "");
561
562     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
563
564     auto* page = _webFrame->page();
565     if (!page)
566         return;
567
568     WebCore::LocalDefaultSystemAppearance localAppearance(page->useSystemAppearance(), page->useDarkAppearance());
569
570     if ([self _isPrintingPreview]) {
571         [self _drawPreview:nsRect];
572         return;
573     }
574
575     ASSERT(!RunLoop::isMain());
576     ASSERT(!_printedPagesData.isEmpty()); // Prepared by knowsPageRange:
577
578     if (!_printedPagesPDFDocument) {
579         RetainPtr<NSData> pdfData = adoptNS([[NSData alloc] initWithBytes:_printedPagesData.data() length:_printedPagesData.size()]);
580         _printedPagesPDFDocument = adoptNS([[WebKit::pdfDocumentClass() alloc] initWithData:pdfData.get()]);
581
582         unsigned pageCount = [_printedPagesPDFDocument pageCount];
583         _linkDestinationsPerPage.clear();
584         _linkDestinationsPerPage.resize(pageCount);
585         for (unsigned i = 0; i < pageCount; i++) {
586             PDFPage *page = [_printedPagesPDFDocument pageAtIndex:i];
587             for (PDFAnnotation *annotation in page.annotations) {
588                 if (![annotation isKindOfClass:WebKit::pdfAnnotationLinkClass()])
589                     continue;
590
591 #pragma clang diagnostic push
592 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
593                 PDFAnnotationLink *linkAnnotation = (PDFAnnotationLink *)annotation;
594 #pragma clang diagnostic pop
595                 if (linkAnnotation.URL)
596                     continue;
597
598                 PDFDestination *destination = linkAnnotation.destination;
599                 if (!destination)
600                     continue;
601
602                 unsigned destinationPageIndex = [_printedPagesPDFDocument indexForPage:destination.page];
603                 _linkDestinationsPerPage[destinationPageIndex].append(destination);
604             }
605         }
606     }
607
608     unsigned printedPageNumber = [self _pageForRect:nsRect] - [self _firstPrintedPageNumber];
609     [self _drawPDFDocument:_printedPagesPDFDocument.get() page:printedPageNumber atPoint:NSMakePoint(nsRect.origin.x, nsRect.origin.y)];
610 }
611
612 - (void)_drawPageBorderWithSizeOnMainThread:(NSSize)borderSize
613 {
614     ASSERT(RunLoop::isMain());
615
616     // When printing from a secondary thread, the main thread doesn't have graphics context and printing operation set up.
617     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
618     [NSGraphicsContext setCurrentContext:[_printOperation context]];
619
620     ASSERT(![NSPrintOperation currentOperation]);
621     [NSPrintOperation setCurrentOperation:_printOperation];
622
623     [self drawPageBorderWithSize:borderSize];
624
625     [NSPrintOperation setCurrentOperation:nil];
626     [NSGraphicsContext setCurrentContext:currentContext];
627 }
628
629 - (void)drawPageBorderWithSize:(NSSize)borderSize
630 {
631     ASSERT(NSEqualSizes(borderSize, [[_printOperation printInfo] paperSize]));    
632     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
633
634     if (!RunLoop::isMain()) {
635         // Don't call the client from a secondary thread.
636         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[WKPrintingView instanceMethodSignatureForSelector:@selector(_drawPageBorderWithSizeOnMainThread:)]];
637         [invocation setSelector:@selector(_drawPageBorderWithSizeOnMainThread:)];
638         [invocation setArgument:&borderSize atIndex:2];
639         [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:self waitUntilDone:YES];
640         return;
641     }
642
643     if (!_webFrame->page())
644         return;
645
646     // The header and footer rect height scales with the page, but the width is always
647     // all the way across the printed page (inset by printing margins).
648     NSPrintInfo *printInfo = [_printOperation printInfo];
649     CGFloat scale = [printInfo scalingFactor];
650     NSSize paperSize = [printInfo paperSize];
651     CGFloat headerFooterLeft = [printInfo leftMargin] / scale;
652     CGFloat headerFooterWidth = (paperSize.width - ([printInfo leftMargin] + [printInfo rightMargin])) / scale;
653     NSRect footerRect = NSMakeRect(headerFooterLeft, [printInfo bottomMargin] / scale - _webFrame->page()->footerHeight(*_webFrame), headerFooterWidth, _webFrame->page()->footerHeight(*_webFrame));
654     NSRect headerRect = NSMakeRect(headerFooterLeft, (paperSize.height - [printInfo topMargin]) / scale, headerFooterWidth, _webFrame->page()->headerHeight(*_webFrame));
655
656     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
657     [currentContext saveGraphicsState];
658     NSRectClip(headerRect);
659     _webFrame->page()->drawHeader(*_webFrame, headerRect);
660     [currentContext restoreGraphicsState];
661
662     [currentContext saveGraphicsState];
663     NSRectClip(footerRect);
664     _webFrame->page()->drawFooter(*_webFrame, footerRect);
665     [currentContext restoreGraphicsState];
666 }
667
668 - (NSRect)rectForPage:(NSInteger)page
669 {
670     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
671     if (![self _hasPageRects]) {
672         LOG(Printing, "-[WKPrintingView %p rectForPage:%d] - data is not yet available", self, (int)page);
673         if (!_webFrame->page()) {
674             // We may have not told AppKit how many pages there are, so it will try to print until a null rect is returned.
675             return NSMakeRect(0, 0, 0, 0);
676         }
677         // We must be still calculating the page range.
678         ASSERT(_expectedComputedPagesCallback);
679         return NSMakeRect(0, 0, 1, 1);
680     }
681
682     // If Web process crashes while computing page rects, we never tell AppKit how many pages there are.
683     // Returning a null rect prevents selecting non-existent pages in preview dialog.
684     if (static_cast<unsigned>(page) > _printingPageRects.size()) {
685         ASSERT(!_webFrame->page());
686         return NSMakeRect(0, 0, 0, 0);
687     }
688
689     WebCore::IntRect rect = _printingPageRects[page - 1];
690     rect.scale(_totalScaleFactorForPrinting);
691     LOG(Printing, "-[WKPrintingView %p rectForPage:%d] -> x %d, y %d, width %d, height %d", self, (int)page, rect.x(), rect.y(), rect.width(), rect.height());
692     return rect;
693 }
694
695 // Temporary workaround for <rdar://problem/8944535>. Force correct printout positioning.
696 - (NSPoint)locationOfPrintRect:(NSRect)aRect
697 {
698     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
699     return NSMakePoint([[_printOperation printInfo] leftMargin], [[_printOperation printInfo] bottomMargin]);
700 }
701
702 - (void)beginDocument
703 {
704     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
705
706     // Forcing preview update gets us here, but page setup hasn't actually changed.
707     if (isForcingPreviewUpdate)
708         return;
709
710     LOG(Printing, "-[WKPrintingView %p beginDocument]", self);
711
712     [super beginDocument];
713
714     [self _suspendAutodisplay];
715 }
716
717 - (void)endDocument
718 {
719     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
720
721     // Forcing preview update gets us here, but page setup hasn't actually changed.
722     if (isForcingPreviewUpdate)
723         return;
724
725     LOG(Printing, "-[WKPrintingView %p endDocument] - clearing cached data", self);
726
727     // Both existing data and pending responses are now obsolete.
728     _printingPageRects.clear();
729     _totalScaleFactorForPrinting = 1;
730     _pagePreviews.clear();
731     _printedPagesData.clear();
732     _printedPagesPDFDocument = nullptr;
733     _expectedComputedPagesCallback = 0;
734     _expectedPreviewCallbacks.clear();
735     _latestExpectedPreviewCallback = 0;
736     _expectedPrintCallback = 0;
737
738     [self _delayedResumeAutodisplay];
739     
740     [super endDocument];
741 }
742 @end
743
744 #endif // PLATFORM(MAC)