Home | History | Annotate | Download | only in mac
      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 #import "Logging.h"
     30 #import "PrintInfo.h"
     31 #import "WebData.h"
     32 #import "WebPageProxy.h"
     33 
     34 using namespace WebKit;
     35 using namespace WebCore;
     36 
     37 NSString * const WebKitOriginalTopPrintingMarginKey = @"WebKitOriginalTopMargin";
     38 NSString * const WebKitOriginalBottomPrintingMarginKey = @"WebKitOriginalBottomMargin";
     39 
     40 NSString * const NSPrintInfoDidChangeNotification = @"NSPrintInfoDidChange";
     41 
     42 static BOOL isForcingPreviewUpdate;
     43 
     44 @implementation WKPrintingView
     45 
     46 - (id)initWithFrameProxy:(WebKit::WebFrameProxy*)frame view:(NSView *)wkView
     47 {
     48     self = [super init]; // No frame rect to pass to NSView.
     49     if (!self)
     50         return nil;
     51 
     52     _webFrame = frame;
     53     _wkView = wkView;
     54 
     55     return self;
     56 }
     57 
     58 - (BOOL)isFlipped
     59 {
     60     return YES;
     61 }
     62 
     63 - (void)_setAutodisplay:(BOOL)newState
     64 {
     65     if (!newState && [[_wkView.get() window] isAutodisplay])
     66         [_wkView.get() displayIfNeeded];
     67 
     68     [[_wkView.get() window] setAutodisplay:newState];
     69 
     70     // For some reason, painting doesn't happen for a long time without this call, <rdar://problem/8975229>.
     71     if (newState)
     72         [_wkView.get() displayIfNeeded];
     73 }
     74 
     75 
     76 - (void)_suspendAutodisplay
     77 {
     78     // A drawRect: call on WKView causes a switch to screen mode, which is slow due to relayout, and we want to avoid that.
     79     // Disabling autodisplay will prevent random updates from causing this, but resizing the window will still work.
     80     if (_autodisplayResumeTimer) {
     81         [_autodisplayResumeTimer invalidate];
     82         _autodisplayResumeTimer = nil;
     83     } else
     84         [self _setAutodisplay:NO];
     85 }
     86 
     87 - (void)_delayedResumeAutodisplayTimerFired
     88 {
     89     ASSERT(isMainThread());
     90 
     91     _autodisplayResumeTimer = nil;
     92     [self _setAutodisplay:YES];
     93 }
     94 
     95 - (void)_delayedResumeAutodisplay
     96 {
     97     // AppKit calls endDocument/beginDocument when print option change. We don't want to switch between print and screen mode just for that,
     98     // and enabling autodisplay may result in switching into screen mode. So, autodisplay is only resumed on next run loop iteration.
     99     if (!_autodisplayResumeTimer) {
    100         _autodisplayResumeTimer = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(_delayedResumeAutodisplayTimerFired) userInfo:nil repeats:NO];
    101         // The timer must be scheduled on main thread, because printing thread may finish before it fires.
    102         [[NSRunLoop mainRunLoop] addTimer:_autodisplayResumeTimer forMode:NSDefaultRunLoopMode];
    103     }
    104 }
    105 
    106 - (void)_adjustPrintingMarginsForHeaderAndFooter
    107 {
    108     NSPrintInfo *info = [_printOperation printInfo];
    109     NSMutableDictionary *infoDictionary = [info dictionary];
    110 
    111     // We need to modify the top and bottom margins in the NSPrintInfo to account for the space needed by the
    112     // header and footer. Because this method can be called more than once on the same NSPrintInfo (see 5038087),
    113     // we stash away the unmodified top and bottom margins the first time this method is called, and we read from
    114     // those stashed-away values on subsequent calls.
    115     double originalTopMargin;
    116     double originalBottomMargin;
    117     NSNumber *originalTopMarginNumber = [infoDictionary objectForKey:WebKitOriginalTopPrintingMarginKey];
    118     if (!originalTopMarginNumber) {
    119         ASSERT(![infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey]);
    120         originalTopMargin = [info topMargin];
    121         originalBottomMargin = [info bottomMargin];
    122         [infoDictionary setObject:[NSNumber numberWithDouble:originalTopMargin] forKey:WebKitOriginalTopPrintingMarginKey];
    123         [infoDictionary setObject:[NSNumber numberWithDouble:originalBottomMargin] forKey:WebKitOriginalBottomPrintingMarginKey];
    124     } else {
    125         ASSERT([originalTopMarginNumber isKindOfClass:[NSNumber class]]);
    126         ASSERT([[infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey] isKindOfClass:[NSNumber class]]);
    127         originalTopMargin = [originalTopMarginNumber doubleValue];
    128         originalBottomMargin = [[infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey] doubleValue];
    129     }
    130 
    131     CGFloat scale = [info scalingFactor];
    132     [info setTopMargin:originalTopMargin + _webFrame->page()->headerHeight(_webFrame.get()) * scale];
    133     [info setBottomMargin:originalBottomMargin + _webFrame->page()->footerHeight(_webFrame.get()) * scale];
    134 }
    135 
    136 - (BOOL)_isPrintingPreview
    137 {
    138     // <rdar://problem/8901041> Please add an API returning whether the current print operation is for preview.
    139     // Assuming that if NSPrintOperation is allowed to spawn a thread for printing, it will. Print preview doesn't spawn a thread.
    140     return !_isPrintingFromSecondaryThread;
    141 }
    142 
    143 - (void)_updatePreview
    144 {
    145     // <rdar://problem/8900923> Please add an API to force print preview update.
    146     ASSERT(!isForcingPreviewUpdate);
    147     isForcingPreviewUpdate = YES;
    148     [[NSNotificationCenter defaultCenter] postNotificationName:NSPrintInfoDidChangeNotification object:nil];
    149     isForcingPreviewUpdate = NO;
    150 }
    151 
    152 - (BOOL)_hasPageRects
    153 {
    154     // WebCore always prints at least one page.
    155     return !_printingPageRects.isEmpty();
    156 }
    157 
    158 - (NSUInteger)_firstPrintedPageNumber
    159 {
    160     // Need to directly access the dictionary because -[NSPrintOperation pageRange] verifies pagination, potentially causing recursion.
    161     return [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintFirstPage] unsignedIntegerValue];
    162 }
    163 
    164 - (NSUInteger)_lastPrintedPageNumber
    165 {
    166     ASSERT([self _hasPageRects]);
    167 
    168     // Need to directly access the dictionary because -[NSPrintOperation pageRange] verifies pagination, potentially causing recursion.
    169     NSUInteger firstPage = [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintFirstPage] unsignedIntegerValue];
    170     NSUInteger lastPage = [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintLastPage] unsignedIntegerValue];
    171     if (lastPage - firstPage >= _printingPageRects.size())
    172         return _printingPageRects.size();
    173     return lastPage;
    174 }
    175 
    176 - (uint64_t)_expectedPreviewCallbackForRect:(const IntRect&)rect
    177 {
    178     for (HashMap<uint64_t, WebCore::IntRect>::iterator iter = _expectedPreviewCallbacks.begin(); iter != _expectedPreviewCallbacks.end(); ++iter) {
    179         if (iter->second  == rect)
    180             return iter->first;
    181     }
    182     return 0;
    183 }
    184 
    185 struct IPCCallbackContext {
    186     RetainPtr<WKPrintingView> view;
    187     uint64_t callbackID;
    188 };
    189 
    190 static void pageDidDrawToPDF(WKDataRef dataRef, WKErrorRef, void* untypedContext)
    191 {
    192     ASSERT(isMainThread());
    193 
    194     OwnPtr<IPCCallbackContext> context = adoptPtr(static_cast<IPCCallbackContext*>(untypedContext));
    195     WKPrintingView *view = context->view.get();
    196     WebData* data = toImpl(dataRef);
    197 
    198     if (context->callbackID == view->_expectedPrintCallback) {
    199         ASSERT(![view _isPrintingPreview]);
    200         ASSERT(view->_printedPagesData.isEmpty());
    201         ASSERT(!view->_printedPagesPDFDocument);
    202         if (data)
    203             view->_printedPagesData.append(data->bytes(), data->size());
    204         view->_expectedPrintCallback = 0;
    205         view->_printingCallbackCondition.signal();
    206     } else {
    207         // If the user has already changed print setup, then this response is obsolete. And this callback is not in response to the latest request,
    208         // then the user has already moved to another page - we'll cache the response, but won't draw it.
    209         HashMap<uint64_t, WebCore::IntRect>::iterator iter = view->_expectedPreviewCallbacks.find(context->callbackID);
    210         if (iter != view->_expectedPreviewCallbacks.end()) {
    211             ASSERT([view _isPrintingPreview]);
    212 
    213             if (data) {
    214                 pair<HashMap<WebCore::IntRect, Vector<uint8_t> >::iterator, bool> entry = view->_pagePreviews.add(iter->second, Vector<uint8_t>());
    215                 entry.first->second.append(data->bytes(), data->size());
    216             }
    217             view->_expectedPreviewCallbacks.remove(context->callbackID);
    218             bool receivedResponseToLatestRequest = view->_latestExpectedPreviewCallback == context->callbackID;
    219             if (receivedResponseToLatestRequest) {
    220                 view->_latestExpectedPreviewCallback = 0;
    221                 [view _updatePreview];
    222             }
    223         }
    224     }
    225 }
    226 
    227 - (void)_preparePDFDataForPrintingOnSecondaryThread
    228 {
    229     ASSERT(isMainThread());
    230 
    231     if (!_webFrame->page()) {
    232         _printingCallbackCondition.signal();
    233         return;
    234     }
    235 
    236     MutexLocker lock(_printingCallbackMutex);
    237 
    238     ASSERT([self _hasPageRects]);
    239     ASSERT(_printedPagesData.isEmpty());
    240     ASSERT(!_printedPagesPDFDocument);
    241     ASSERT(!_expectedPrintCallback);
    242 
    243     NSUInteger firstPage = [self _firstPrintedPageNumber];
    244     NSUInteger lastPage = [self _lastPrintedPageNumber];
    245 
    246     ASSERT(firstPage > 0);
    247     ASSERT(firstPage <= lastPage);
    248     LOG(View, "WKPrintingView requesting PDF data for pages %u...%u", firstPage, lastPage);
    249 
    250     // Return to printing mode if we're already back to screen (e.g. due to window resizing).
    251     _webFrame->page()->beginPrinting(_webFrame.get(), PrintInfo([_printOperation printInfo]));
    252 
    253     IPCCallbackContext* context = new IPCCallbackContext;
    254     RefPtr<DataCallback> callback = DataCallback::create(context, pageDidDrawToPDF);
    255     _expectedPrintCallback = callback->callbackID();
    256 
    257     context->view = self;
    258     context->callbackID = callback->callbackID();
    259 
    260     _webFrame->page()->drawPagesToPDF(_webFrame.get(), firstPage - 1, lastPage - firstPage + 1, callback.get());
    261 }
    262 
    263 static void pageDidComputePageRects(const Vector<WebCore::IntRect>& pageRects, double totalScaleFactorForPrinting, WKErrorRef, void* untypedContext)
    264 {
    265     ASSERT(isMainThread());
    266 
    267     OwnPtr<IPCCallbackContext> context = adoptPtr(static_cast<IPCCallbackContext*>(untypedContext));
    268     WKPrintingView *view = context->view.get();
    269 
    270     // If the user has already changed print setup, then this response is obsolete.
    271     if (context->callbackID == view->_expectedComputedPagesCallback) {
    272         ASSERT(isMainThread());
    273         ASSERT(view->_expectedPreviewCallbacks.isEmpty());
    274         ASSERT(!view->_latestExpectedPreviewCallback);
    275         ASSERT(!view->_expectedPrintCallback);
    276         ASSERT(view->_pagePreviews.isEmpty());
    277         view->_expectedComputedPagesCallback = 0;
    278 
    279         view->_printingPageRects = pageRects;
    280         view->_totalScaleFactorForPrinting = totalScaleFactorForPrinting;
    281 
    282         // Sanitize a response coming from the Web process.
    283         if (view->_printingPageRects.isEmpty())
    284             view->_printingPageRects.append(IntRect(0, 0, 1, 1));
    285         if (view->_totalScaleFactorForPrinting <= 0)
    286             view->_totalScaleFactorForPrinting = 1;
    287 
    288         const IntRect& lastPrintingPageRect = view->_printingPageRects[view->_printingPageRects.size() - 1];
    289         NSRect newFrameSize = NSMakeRect(0, 0,
    290             ceil(lastPrintingPageRect.maxX() * view->_totalScaleFactorForPrinting),
    291             ceil(lastPrintingPageRect.maxY() * view->_totalScaleFactorForPrinting));
    292         LOG(View, "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);
    293         [view setFrame:newFrameSize];
    294 
    295         if ([view _isPrintingPreview]) {
    296             // Show page count, and ask for an actual image to replace placeholder.
    297             [view _updatePreview];
    298         } else {
    299             // When printing, request everything we'll need beforehand.
    300             [view _preparePDFDataForPrintingOnSecondaryThread];
    301         }
    302     }
    303 }
    304 
    305 - (BOOL)_askPageToComputePageRects
    306 {
    307     ASSERT(isMainThread());
    308 
    309     if (!_webFrame->page())
    310         return NO;
    311 
    312     ASSERT(!_expectedComputedPagesCallback);
    313 
    314     IPCCallbackContext* context = new IPCCallbackContext;
    315     RefPtr<ComputedPagesCallback> callback = ComputedPagesCallback::create(context, pageDidComputePageRects);
    316     _expectedComputedPagesCallback = callback->callbackID();
    317     context->view = self;
    318     context->callbackID = _expectedComputedPagesCallback;
    319 
    320     _webFrame->page()->computePagesForPrinting(_webFrame.get(), PrintInfo([_printOperation printInfo]), callback.release());
    321     return YES;
    322 }
    323 
    324 static void prepareDataForPrintingOnSecondaryThread(void* untypedContext)
    325 {
    326     ASSERT(isMainThread());
    327 
    328     WKPrintingView *view = static_cast<WKPrintingView *>(untypedContext);
    329     MutexLocker lock(view->_printingCallbackMutex);
    330 
    331     // We may have received page rects while a message to call this function traveled from secondary thread to main one.
    332     if ([view _hasPageRects]) {
    333         [view _preparePDFDataForPrintingOnSecondaryThread];
    334         return;
    335     }
    336 
    337     // A request for pages has already been made, just wait for it to finish.
    338     if (view->_expectedComputedPagesCallback)
    339         return;
    340 
    341     [view _askPageToComputePageRects];
    342 }
    343 
    344 - (BOOL)knowsPageRange:(NSRangePointer)range
    345 {
    346     LOG(View, "-[WKPrintingView %p knowsPageRange:], %s, %s", self, [self _hasPageRects] ? "print data is available" : "print data is not available yet", isMainThread() ? "on main thread" : "on secondary thread");
    347     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
    348 
    349     // Assuming that once we switch to printing from a secondary thread, we don't go back.
    350     ASSERT(!_isPrintingFromSecondaryThread || !isMainThread());
    351     if (!isMainThread())
    352         _isPrintingFromSecondaryThread = YES;
    353 
    354     if (!_webFrame->page()) {
    355         *range = NSMakeRange(1, NSIntegerMax);
    356         return YES;
    357     }
    358 
    359     [self _suspendAutodisplay];
    360 
    361     [self _adjustPrintingMarginsForHeaderAndFooter];
    362 
    363     if ([self _hasPageRects])
    364         *range = NSMakeRange(1, _printingPageRects.size());
    365     else if (!isMainThread()) {
    366         ASSERT(![self _isPrintingPreview]);
    367         MutexLocker lock(_printingCallbackMutex);
    368         callOnMainThread(prepareDataForPrintingOnSecondaryThread, self);
    369         _printingCallbackCondition.wait(_printingCallbackMutex);
    370         *range = NSMakeRange(1, _printingPageRects.size());
    371     } else {
    372         ASSERT([self _isPrintingPreview]);
    373 
    374         // If a request for pages hasn't already been made, make it now.
    375         if (!_expectedComputedPagesCallback)
    376             [self _askPageToComputePageRects];
    377 
    378         *range = NSMakeRange(1, NSIntegerMax);
    379     }
    380     return YES;
    381 }
    382 
    383 - (unsigned)_pageForRect:(NSRect)rect
    384 {
    385     // Assuming that rect exactly matches one of the pages.
    386     for (size_t i = 0; i < _printingPageRects.size(); ++i) {
    387         IntRect currentRect(_printingPageRects[i]);
    388         currentRect.scale(_totalScaleFactorForPrinting);
    389         if (rect.origin.y == currentRect.y() && rect.origin.x == currentRect.x())
    390             return i + 1;
    391     }
    392     ASSERT_NOT_REACHED();
    393     return 0; // Invalid page number.
    394 }
    395 
    396 - (void)_drawPDFDocument:(CGPDFDocumentRef)pdfDocument page:(unsigned)page atPoint:(NSPoint)point
    397 {
    398     if (!pdfDocument) {
    399         LOG_ERROR("Couldn't create a PDF document with data passed for preview");
    400         return;
    401     }
    402 
    403     CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdfDocument, page);
    404     if (!pdfPage) {
    405         LOG_ERROR("Preview data doesn't have page %d", page);
    406         return;
    407     }
    408 
    409     NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
    410     CGContextRef context = static_cast<CGContextRef>([nsGraphicsContext graphicsPort]);
    411 
    412     CGContextSaveGState(context);
    413     CGContextTranslateCTM(context, point.x, point.y);
    414     CGContextScaleCTM(context, _totalScaleFactorForPrinting, -_totalScaleFactorForPrinting);
    415     CGContextTranslateCTM(context, 0, -CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox).size.height);
    416     CGContextDrawPDFPage(context, pdfPage);
    417     CGContextRestoreGState(context);
    418 }
    419 
    420 - (void)_drawPreview:(NSRect)nsRect
    421 {
    422     ASSERT(isMainThread());
    423 
    424     IntRect rect(nsRect);
    425     rect.scale(1 / _totalScaleFactorForPrinting);
    426     HashMap<WebCore::IntRect, Vector<uint8_t> >::iterator pagePreviewIterator = _pagePreviews.find(rect);
    427     if (pagePreviewIterator == _pagePreviews.end())  {
    428         // It's too early to ask for page preview if we don't even know page size and scale.
    429         if ([self _hasPageRects]) {
    430             if (uint64_t existingCallback = [self _expectedPreviewCallbackForRect:rect]) {
    431                 // We've already asked for a preview of this page, and are waiting for response.
    432                 // There is no need to ask again.
    433                 _latestExpectedPreviewCallback = existingCallback;
    434             } else {
    435                 // Preview isn't available yet, request it asynchronously.
    436                 if (!_webFrame->page())
    437                     return;
    438 
    439                 // Return to printing mode if we're already back to screen (e.g. due to window resizing).
    440                 _webFrame->page()->beginPrinting(_webFrame.get(), PrintInfo([_printOperation printInfo]));
    441 
    442                 IPCCallbackContext* context = new IPCCallbackContext;
    443                 RefPtr<DataCallback> callback = DataCallback::create(context, pageDidDrawToPDF);
    444                 _latestExpectedPreviewCallback = callback->callbackID();
    445                 _expectedPreviewCallbacks.add(_latestExpectedPreviewCallback, rect);
    446 
    447                 context->view = self;
    448                 context->callbackID = callback->callbackID();
    449 
    450                 _webFrame->page()->drawRectToPDF(_webFrame.get(), rect, callback.get());
    451                 return;
    452             }
    453         }
    454 
    455         // FIXME: Draw a placeholder
    456         return;
    457     }
    458 
    459     const Vector<uint8_t>& pdfData = pagePreviewIterator->second;
    460     RetainPtr<CGDataProviderRef> pdfDataProvider(AdoptCF, CGDataProviderCreateWithData(0, pdfData.data(), pdfData.size(), 0));
    461     RetainPtr<CGPDFDocumentRef> pdfDocument(AdoptCF, CGPDFDocumentCreateWithProvider(pdfDataProvider.get()));
    462 
    463     [self _drawPDFDocument:pdfDocument.get() page:1 atPoint:NSMakePoint(nsRect.origin.x, nsRect.origin.y)];
    464 }
    465 
    466 - (void)drawRect:(NSRect)nsRect
    467 {
    468     LOG(View, "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" : "");
    469 
    470     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
    471 
    472     if (!_webFrame->page())
    473         return;
    474 
    475     if ([self _isPrintingPreview]) {
    476         [self _drawPreview:nsRect];
    477         return;
    478     }
    479 
    480     ASSERT(!isMainThread());
    481     ASSERT(!_printedPagesData.isEmpty()); // Prepared by knowsPageRange:
    482 
    483     if (!_printedPagesPDFDocument) {
    484         RetainPtr<CGDataProviderRef> pdfDataProvider(AdoptCF, CGDataProviderCreateWithData(0, _printedPagesData.data(), _printedPagesData.size(), 0));
    485         _printedPagesPDFDocument.adoptCF(CGPDFDocumentCreateWithProvider(pdfDataProvider.get()));
    486     }
    487 
    488     unsigned printedPageNumber = [self _pageForRect:nsRect] - [self _firstPrintedPageNumber] + 1;
    489     [self _drawPDFDocument:_printedPagesPDFDocument.get() page:printedPageNumber atPoint:NSMakePoint(nsRect.origin.x, nsRect.origin.y)];
    490 }
    491 
    492 - (void)_drawPageBorderWithSizeOnMainThread:(NSSize)borderSize
    493 {
    494     ASSERT(isMainThread());
    495 
    496     // When printing from a secondary thread, the main thread doesn't have graphics context and printing operation set up.
    497     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
    498     [NSGraphicsContext setCurrentContext:[_printOperation context]];
    499 
    500     ASSERT(![NSPrintOperation currentOperation]);
    501     [NSPrintOperation setCurrentOperation:_printOperation];
    502 
    503     [self drawPageBorderWithSize:borderSize];
    504 
    505     [NSPrintOperation setCurrentOperation:nil];
    506     [NSGraphicsContext setCurrentContext:currentContext];
    507 }
    508 
    509 - (void)drawPageBorderWithSize:(NSSize)borderSize
    510 {
    511     ASSERT(NSEqualSizes(borderSize, [[_printOperation printInfo] paperSize]));
    512     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
    513 
    514     if (!isMainThread()) {
    515         // Don't call the client from a secondary thread.
    516         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[WKPrintingView instanceMethodSignatureForSelector:@selector(_drawPageBorderWithSizeOnMainThread:)]];
    517         [invocation setSelector:@selector(_drawPageBorderWithSizeOnMainThread:)];
    518         [invocation setArgument:&borderSize atIndex:2];
    519         [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:self waitUntilDone:YES];
    520         return;
    521     }
    522 
    523     if (!_webFrame->page())
    524         return;
    525 
    526     // The header and footer rect height scales with the page, but the width is always
    527     // all the way across the printed page (inset by printing margins).
    528     NSPrintInfo *printInfo = [_printOperation printInfo];
    529     CGFloat scale = [printInfo scalingFactor];
    530     NSSize paperSize = [printInfo paperSize];
    531     CGFloat headerFooterLeft = [printInfo leftMargin] / scale;
    532     CGFloat headerFooterWidth = (paperSize.width - ([printInfo leftMargin] + [printInfo rightMargin])) / scale;
    533     NSRect footerRect = NSMakeRect(headerFooterLeft, [printInfo bottomMargin] / scale - _webFrame->page()->footerHeight(_webFrame.get()), headerFooterWidth, _webFrame->page()->footerHeight(_webFrame.get()));
    534     NSRect headerRect = NSMakeRect(headerFooterLeft, (paperSize.height - [printInfo topMargin]) / scale, headerFooterWidth, _webFrame->page()->headerHeight(_webFrame.get()));
    535 
    536     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
    537     [currentContext saveGraphicsState];
    538     NSRectClip(headerRect);
    539     _webFrame->page()->drawHeader(_webFrame.get(), headerRect);
    540     [currentContext restoreGraphicsState];
    541 
    542     [currentContext saveGraphicsState];
    543     NSRectClip(footerRect);
    544     _webFrame->page()->drawFooter(_webFrame.get(), footerRect);
    545     [currentContext restoreGraphicsState];
    546 }
    547 
    548 - (NSRect)rectForPage:(NSInteger)page
    549 {
    550     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
    551     if (![self _hasPageRects]) {
    552         LOG(View, "-[WKPrintingView %p rectForPage:%d] - data is not yet available", self, (int)page);
    553         if (!_webFrame->page()) {
    554             // We may have not told AppKit how many pages there are, so it will try to print until a null rect is returned.
    555             return NSMakeRect(0, 0, 0, 0);
    556         }
    557         // We must be still calculating the page range.
    558         ASSERT(_expectedComputedPagesCallback);
    559         return NSMakeRect(0, 0, 1, 1);
    560     }
    561 
    562     // If Web process crashes while computing page rects, we never tell AppKit how many pages there are.
    563     // Returning a null rect prevents selecting non-existent pages in preview dialog.
    564     if (static_cast<unsigned>(page) > _printingPageRects.size()) {
    565         ASSERT(!_webFrame->page());
    566         return NSMakeRect(0, 0, 0, 0);
    567     }
    568 
    569     IntRect rect = _printingPageRects[page - 1];
    570     rect.scale(_totalScaleFactorForPrinting);
    571     LOG(View, "-[WKPrintingView %p rectForPage:%d] -> x %d, y %d, width %d, height %d", self, (int)page, rect.x(), rect.y(), rect.width(), rect.height());
    572     return rect;
    573 }
    574 
    575 // Temporary workaround for <rdar://problem/8944535>. Force correct printout positioning.
    576 - (NSPoint)locationOfPrintRect:(NSRect)aRect
    577 {
    578     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
    579     return NSMakePoint([[_printOperation printInfo] leftMargin], [[_printOperation printInfo] bottomMargin]);
    580 }
    581 
    582 - (void)beginDocument
    583 {
    584     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
    585 
    586     // Forcing preview update gets us here, but page setup hasn't actually changed.
    587     if (isForcingPreviewUpdate)
    588         return;
    589 
    590     LOG(View, "-[WKPrintingView %p beginDocument]", self);
    591 
    592     [super beginDocument];
    593 
    594     [self _suspendAutodisplay];
    595 }
    596 
    597 - (void)endDocument
    598 {
    599     ASSERT(_printOperation == [NSPrintOperation currentOperation]);
    600 
    601     // Forcing preview update gets us here, but page setup hasn't actually changed.
    602     if (isForcingPreviewUpdate)
    603         return;
    604 
    605     LOG(View, "-[WKPrintingView %p endDocument] - clearing cached data", self);
    606 
    607     // Both existing data and pending responses are now obsolete.
    608     _printingPageRects.clear();
    609     _totalScaleFactorForPrinting = 1;
    610     _pagePreviews.clear();
    611     _printedPagesData.clear();
    612     _printedPagesPDFDocument = nullptr;
    613     _expectedComputedPagesCallback = 0;
    614     _expectedPreviewCallbacks.clear();
    615     _latestExpectedPreviewCallback = 0;
    616     _expectedPrintCallback = 0;
    617 
    618     [self _delayedResumeAutodisplay];
    619 
    620     [super endDocument];
    621 }
    622 @end
    623