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