Reviewed by Darin Adler.
authorap@apple.com <ap@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 27 Jan 2011 19:36:10 +0000 (19:36 +0000)
committerap@apple.com <ap@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 27 Jan 2011 19:36:10 +0000 (19:36 +0000)
        https://bugs.webkit.org/show_bug.cgi?id=53197
        <rdar://problem/8895682> Make WebKit2 printing asynchronous

        <rdar://problem/8899988> REGRESSION(WebKit2): Attempting to print WHATWG HTML spec shows
        1-page blank preview

        <rdar://problem/8900078> WebKit2 printing has a separate message exchange per page when
        printing instead of printing all at once

        * UIProcess/API/mac/WKPrintingView.h: Store a lot more cached information.

        * UIProcess/API/mac/WKPrintingView.mm:
        (-[WKPrintingView _adjustPrintingMarginsForHeaderAndFooter]): Use PrintOperation stored in
        class. We generally want that now, because current operation will not be set up on other threads.
        (-[WKPrintingView _isPrintingPreview]): Added. Preview is different, because it can draw
        a placeholder - but actual printing need to wait until UI process has data.
        (-[WKPrintingView _updatePreview]): Force AppKit to update print preview when we have real
        data to replace placeholder with.
        (-[WKPrintingView _hasPageRects]): Return if page rects have already been computed.
        (-[WKPrintingView _expectedPreviewCallbackForRect:]): Find an existing request for this rect,
        if any.
        (pageDidDrawToPDF): Update preview - or if actually printing, release control to printing thread.
        (-[WKPrintingView _preparePDFDataForPrintingOnSecondaryThread]): Ask for a PDF document with
        pages the user asked to print.
        (pageDidComputePageRects): When web process returns page geometry data, we update preview to display
        a page count (which indirectly triggers a request for a preview). When actually printing,
        request a PDF right away, we'll need it later.
        (-[WKPrintingView _askPageToComputePageRects]): Ask web process for page geometry.
        (prepareDataForPrintingOnSecondaryThread): This function starts preparing all data necessary
        for actual printing on main thread.
        (-[WKPrintingView knowsPageRange:]): Changed to be async for preview, and to call main
        thread for actual printing.
        (-[WKPrintingView _pageForRect:]): Find page number for a given range, assuming that AppKit
        always asks for full pages.
        (-[WKPrintingView _drawPDFDocument:page:atPoint:]): A helper function to draw a PDF document.
        (-[WKPrintingView _drawPreview:]): Draw the whole page for preview.
        (-[WKPrintingView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:]):
        Draw the whole page for actual printing.
        (-[WKPrintingView _drawPageBorderWithSizeOnMainThread:]): Call drawPageBorderWithSize: with
        correct graphics context and print operation.
        (-[WKPrintingView drawPageBorderWithSize:]): Delegate work to main thread when called from a
        secondary one.
        (-[WKPrintingView _provideTotalScaleFactorForPrintOperation:]): Added an assertion that
        our stored NSPrintOperation isn't out of sync with reality.
        (-[WKPrintingView rectForPage:]): Handle the case where we don't have the data yet, drawing
        a placeholder.
        (-[WKPrintingView endDocument]): When page setup changes, we must reset all state.

        * UIProcess/API/mac/WKView.mm: (-[WKView printOperationWithPrintInfo:forFrame:]): Tell
        NSPrintingView which operation it's serving, so that it knows it even when current operation
        isn't set.

        * UIProcess/GenericCallback.h:
        (WebKit::ComputedPagesCallback::create):
        (WebKit::ComputedPagesCallback::~ComputedPagesCallback):
        (WebKit::ComputedPagesCallback::performCallbackWithReturnValue):
        (WebKit::ComputedPagesCallback::invalidate):
        (WebKit::ComputedPagesCallback::callbackID):
        (WebKit::ComputedPagesCallback::generateCallbackID):
        (WebKit::ComputedPagesCallback::ComputedPagesCallback):
        Added ComputedPagesCallback, which returns a vector of IntRects and a double. Hopefully,
        it will become a specialization of GenericCallback one day.

        * UIProcess/WebPageProxy.cpp:
        (WebKit::WebPageProxy::close): Added m_voidCallbacks - it looks like they were omitted
        by accident.
        (WebKit::WebPageProxy::computedPagesCallback): Added.
        (WebKit::WebPageProxy::processDidCrash): Added m_computedPagesCallbacks.
        (WebKit::WebPageProxy::computePagesForPrinting): Async now!
        (WebKit::WebPageProxy::drawRectToPDF): Ditto.
        (WebKit::WebPageProxy::drawPagesToPDF): Added.
        * UIProcess/WebPageProxy.h:

        * UIProcess/WebPageProxy.messages.in: Added ComputedPagesCallback.

        * WebProcess/WebPage/WebPage.cpp:
        (WebKit::WebPage::beginPriting): Compute page rects right away - we'll need them in
        drawPagesToPDF(), which doesn't have a PrintInfo.
        (WebKit::WebPage::computePagesForPrinting): Send async response.
        (WebKit::WebPage::drawRectToPDF): This function used to draw into original
        location at frame coordinates, and now it draws at (0, 0).
        (WebKit::WebPage::drawPagesToPDF): Added - make a multi-page PDF.
        * WebProcess/WebPage/WebPage.h:

        * WebProcess/WebPage/WebPage.messages.in: Added DrawPagesToPDF.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@76821 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
Source/WebCore/ChangeLog
Source/WebCore/WebCore.exp.in
Source/WebCore/page/PrintContext.cpp
Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/mac/WKPrintingView.h
Source/WebKit2/UIProcess/API/mac/WKPrintingView.mm
Source/WebKit2/UIProcess/API/mac/WKView.mm
Source/WebKit2/UIProcess/GenericCallback.h
Source/WebKit2/UIProcess/WebPageProxy.cpp
Source/WebKit2/UIProcess/WebPageProxy.h
Source/WebKit2/UIProcess/WebPageProxy.messages.in
Source/WebKit2/WebProcess/WebPage/WebPage.cpp
Source/WebKit2/WebProcess/WebPage/WebPage.h
Source/WebKit2/WebProcess/WebPage/WebPage.messages.in

index c13e311..d4fc183 100644 (file)
@@ -1,3 +1,15 @@
+2011-01-26  Alexey Proskuryakov  <ap@apple.com>
+
+        Reviewed by Darin Adler.
+
+        https://bugs.webkit.org/show_bug.cgi?id=53197
+        <rdar://problem/8895682> Make WebKit2 printing asynchronous
+
+        * WebCore.exp.in: Export more PrintContext methods that we didn't use on Mac before.
+
+        * page/PrintContext.cpp: (WebCore::PrintContext::spoolRect): Changed to make the same
+        transformation as spoolPages does for consistency.
+
 2011-01-27  David Grogan  <dgrogan@google.com>
 
         Reviewed by Jeremy Orlow.
index 957c492..44383b3 100644 (file)
@@ -1001,6 +1001,8 @@ __ZNK7WebCore12EventHandler20currentKeyboardEventEv
 __ZNK7WebCore12IconDatabase12databasePathEv
 __ZNK7WebCore12IconDatabase24shouldStopThreadActivityEv
 __ZNK7WebCore12IconDatabase9isEnabledEv
+__ZNK7WebCore12PrintContext8pageRectEm
+__ZNK7WebCore12PrintContext9pageCountEv
 __ZNK7WebCore12RenderObject14enclosingLayerEv
 __ZNK7WebCore12RenderObject15localToAbsoluteENS_10FloatPointEbb
 __ZNK7WebCore12RenderObject7childAtEj
index 16fd379..760b6af 100644 (file)
@@ -165,8 +165,7 @@ void PrintContext::spoolPage(GraphicsContext& ctx, int pageNumber, float width)
 void PrintContext::spoolRect(GraphicsContext& ctx, const IntRect& rect)
 {
     ctx.save();
-    ctx.scale(FloatSize(1, -1));
-    ctx.translate(0, -m_frame->view()->contentsHeight());
+    ctx.translate(-rect.x(), -rect.y());
     ctx.clip(rect);
     m_frame->view()->paintContents(&ctx, rect);
     ctx.restore();
index 3dd6ba6..62b2dd4 100644 (file)
@@ -1,3 +1,93 @@
+2011-01-26  Alexey Proskuryakov  <ap@apple.com>
+
+        Reviewed by Darin Adler.
+
+        https://bugs.webkit.org/show_bug.cgi?id=53197
+        <rdar://problem/8895682> Make WebKit2 printing asynchronous
+
+        <rdar://problem/8899988> REGRESSION(WebKit2): Attempting to print WHATWG HTML spec shows
+        1-page blank preview
+
+        <rdar://problem/8900078> WebKit2 printing has a separate message exchange per page when
+        printing instead of printing all at once
+
+        * UIProcess/API/mac/WKPrintingView.h: Store a lot more cached information.
+
+        * UIProcess/API/mac/WKPrintingView.mm:
+        (-[WKPrintingView _adjustPrintingMarginsForHeaderAndFooter]): Use PrintOperation stored in
+        class. We generally want that now, because current operation will not be set up on other threads.
+        (-[WKPrintingView _isPrintingPreview]): Added. Preview is different, because it can draw
+        a placeholder - but actual printing need to wait until UI process has data.
+        (-[WKPrintingView _updatePreview]): Force AppKit to update print preview when we have real
+        data to replace placeholder with.
+        (-[WKPrintingView _hasPageRects]): Return if page rects have already been computed.
+        (-[WKPrintingView _expectedPreviewCallbackForRect:]): Find an existing request for this rect,
+        if any.
+        (pageDidDrawToPDF): Update preview - or if actually printing, release control to printing thread.
+        (-[WKPrintingView _preparePDFDataForPrintingOnSecondaryThread]): Ask for a PDF document with
+        pages the user asked to print.
+        (pageDidComputePageRects): When web process returns page geometry data, we update preview to display
+        a page count (which indirectly triggers a request for a preview). When actually printing,
+        request a PDF right away, we'll need it later.
+        (-[WKPrintingView _askPageToComputePageRects]): Ask web process for page geometry.
+        (prepareDataForPrintingOnSecondaryThread): This function starts preparing all data necessary
+        for actual printing on main thread.
+        (-[WKPrintingView knowsPageRange:]): Changed to be async for preview, and to call main
+        thread for actual printing.
+        (-[WKPrintingView _pageForRect:]): Find page number for a given range, assuming that AppKit
+        always asks for full pages.
+        (-[WKPrintingView _drawPDFDocument:page:atPoint:]): A helper function to draw a PDF document.
+        (-[WKPrintingView _drawPreview:]): Draw the whole page for preview.
+        (-[WKPrintingView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:]):
+        Draw the whole page for actual printing.
+        (-[WKPrintingView _drawPageBorderWithSizeOnMainThread:]): Call drawPageBorderWithSize: with
+        correct graphics context and print operation.
+        (-[WKPrintingView drawPageBorderWithSize:]): Delegate work to main thread when called from a
+        secondary one.
+        (-[WKPrintingView _provideTotalScaleFactorForPrintOperation:]): Added an assertion that
+        our stored NSPrintOperation isn't out of sync with reality.
+        (-[WKPrintingView rectForPage:]): Handle the case where we don't have the data yet, drawing
+        a placeholder.
+        (-[WKPrintingView endDocument]): When page setup changes, we must reset all state.
+
+        * UIProcess/API/mac/WKView.mm: (-[WKView printOperationWithPrintInfo:forFrame:]): Tell
+        NSPrintingView which operation it's serving, so that it knows it even when current operation
+        isn't set.
+
+        * UIProcess/GenericCallback.h:
+        (WebKit::ComputedPagesCallback::create):
+        (WebKit::ComputedPagesCallback::~ComputedPagesCallback):
+        (WebKit::ComputedPagesCallback::performCallbackWithReturnValue):
+        (WebKit::ComputedPagesCallback::invalidate):
+        (WebKit::ComputedPagesCallback::callbackID):
+        (WebKit::ComputedPagesCallback::generateCallbackID):
+        (WebKit::ComputedPagesCallback::ComputedPagesCallback):
+        Added ComputedPagesCallback, which returns a vector of IntRects and a double. Hopefully,
+        it will become a specialization of GenericCallback one day.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::close): Added m_voidCallbacks - it looks like they were omitted
+        by accident.
+        (WebKit::WebPageProxy::computedPagesCallback): Added.
+        (WebKit::WebPageProxy::processDidCrash): Added m_computedPagesCallbacks.
+        (WebKit::WebPageProxy::computePagesForPrinting): Async now!
+        (WebKit::WebPageProxy::drawRectToPDF): Ditto.
+        (WebKit::WebPageProxy::drawPagesToPDF): Added.
+        * UIProcess/WebPageProxy.h:
+
+        * UIProcess/WebPageProxy.messages.in: Added ComputedPagesCallback.
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::beginPriting): Compute page rects right away - we'll need them in
+        drawPagesToPDF(), which doesn't have a PrintInfo.
+        (WebKit::WebPage::computePagesForPrinting): Send async response.
+        (WebKit::WebPage::drawRectToPDF): This function used to draw into original
+        location at frame coordinates, and now it draws at (0, 0).
+        (WebKit::WebPage::drawPagesToPDF): Added - make a multi-page PDF.
+        * WebProcess/WebPage/WebPage.h:
+
+        * WebProcess/WebPage/WebPage.messages.in: Added DrawPagesToPDF.
+
 2011-01-27  Alejandro G. Castro  <alex@igalia.com>
 
         Unreviewed GTK build fix after r76797.
index 42e2c30..10c59c4 100644 (file)
@@ -23,8 +23,8 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#import <WebCore/IntRect.h>
-#import <wtf/RefPtr.h>
+#import <WebCore/IntRectHash.h>
+#import <wtf/RetainPtr.h>
 
 @class WKPrintingViewData;
 
@@ -34,9 +34,25 @@ namespace WebKit {
 
 @interface WKPrintingView : NSView {
 @public
+    NSPrintOperation *_printOperation; // WKPrintingView is owned by the operation.
+
     RefPtr<WebKit::WebFrameProxy> _webFrame;
-    Vector<WebCore::IntRect> _webPrintingPageRects;
-    double _webTotalScaleFactorForPrinting;
+    Vector<WebCore::IntRect> _printingPageRects;
+    double _totalScaleFactorForPrinting;
+    HashMap<WebCore::IntRect, Vector<uint8_t> > _pagePreviews;
+
+    Vector<uint8_t> _printedPagesData;
+    RetainPtr<CGPDFDocumentRef> _printedPagesPDFDocument;
+
+    uint64_t _expectedComputedPagesCallback;
+    HashMap<uint64_t, WebCore::IntRect> _expectedPreviewCallbacks;
+    uint64_t _latestExpectedPreviewCallback;
+    uint64_t _expectedPrintCallback;
+    BOOL _isForcingPreviewUpdate;
+
+    BOOL _isPrintingFromSecondaryThread;
+    Mutex _printingCallbackMutex;
+    ThreadCondition _printingCallbackCondition;
 }
 
 - (id)initWithFrameProxy:(WebKit::WebFrameProxy*)frame;
index 99b8610..3508c10 100644 (file)
@@ -27,6 +27,7 @@
 
 #import "Logging.h"
 #import "PrintInfo.h"
+#import "WebData.h"
 #import "WebPageProxy.h"
 
 @interface NSView (WebNSViewDetails)
@@ -39,6 +40,8 @@ using namespace WebCore;
 NSString * const WebKitOriginalTopPrintingMarginKey = @"WebKitOriginalTopMargin";
 NSString * const WebKitOriginalBottomPrintingMarginKey = @"WebKitOriginalBottomMargin";
 
+NSString * const NSPrintInfoDidChangeNotification = @"NSPrintInfoDidChange";
+
 @implementation WKPrintingView
 
 - (id)initWithFrameProxy:(WebFrameProxy*)frame
@@ -59,8 +62,7 @@ NSString * const WebKitOriginalBottomPrintingMarginKey = @"WebKitOriginalBottomM
 
 - (void)_adjustPrintingMarginsForHeaderAndFooter
 {
-    NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
-    NSPrintInfo *info = [printOperation printInfo];
+    NSPrintInfo *info = [_printOperation printInfo];
     NSMutableDictionary *infoDictionary = [info dictionary];
 
     // We need to modify the top and bottom margins in the NSPrintInfo to account for the space needed by the
@@ -88,40 +90,236 @@ NSString * const WebKitOriginalBottomPrintingMarginKey = @"WebKitOriginalBottomM
     [info setBottomMargin:originalBottomMargin + _webFrame->page()->footerHeight(_webFrame.get()) * scale];
 }
 
-- (BOOL)knowsPageRange:(NSRangePointer)range
+- (BOOL)_isPrintingPreview
 {
-    LOG(View, "knowsPageRange:");
-    [self _adjustPrintingMarginsForHeaderAndFooter];
+    // <rdar://problem/8901041> Please add an API returning whether the current print operation is for preview.
+#if !defined(BUILDING_ON_SNOW_LEOPARD)
+    return [_printOperation preferredRenderingQuality] == NSPrintRenderingQualityResponsive;
+#else
+    // Assuming that if NSPrintOperation is allowed to spawn a thread for printing, it will. Print preview doesn't spawn a thread.
+    return !_isPrintingFromSecondaryThread;
+#endif
+}
+
+- (void)_updatePreview
+{
+    // <rdar://problem/8900923> Please add an API to force print preview update.
+    _isForcingPreviewUpdate = YES;
+    [[NSNotificationCenter defaultCenter] postNotificationName:NSPrintInfoDidChangeNotification object:nil];
+    _isForcingPreviewUpdate = NO;
+}
+
+- (BOOL)_hasPageRects
+{
+    // WebCore always prints at least one page.
+    return !_printingPageRects.isEmpty();
+}
+
+- (uint64_t)_expectedPreviewCallbackForRect:(const IntRect&)rect
+{
+    for (HashMap<uint64_t, WebCore::IntRect>::iterator iter = _expectedPreviewCallbacks.begin(); iter != _expectedPreviewCallbacks.end(); ++iter) {
+        if (iter->second  == rect)
+            return iter->first;
+    }
+    return 0;
+}
+
+struct IPCCallbackContext {
+    RetainPtr<WKPrintingView> view;
+    uint64_t callbackID;
+};
+
+static void pageDidDrawToPDF(WKDataRef dataRef, WKErrorRef, void* untypedContext)
+{
+    ASSERT(isMainThread());
+
+    OwnPtr<IPCCallbackContext> context = adoptPtr(static_cast<IPCCallbackContext*>(untypedContext));
+    WKPrintingView *view = context->view.get();
+    WebData* data = toImpl(dataRef);
+
+    if (context->callbackID == view->_expectedPrintCallback) {
+        ASSERT(![view _isPrintingPreview]);
+        ASSERT(view->_printedPagesData.isEmpty());
+        ASSERT(!view->_printedPagesPDFDocument);
+        if (data)
+            view->_printedPagesData.append(data->bytes(), data->size());
+        view->_expectedPrintCallback = 0;
+        view->_printingCallbackCondition.signal();
+    } else {
+        // If the user has already changed print setup, then this response is obsolete. And this callback is not in response to the latest request,
+        // then the user has already moved to another page - we'll cache the response, but won't draw it.
+        HashMap<uint64_t, WebCore::IntRect>::iterator iter = view->_expectedPreviewCallbacks.find(context->callbackID);
+        if (iter != view->_expectedPreviewCallbacks.end()) {
+            ASSERT([view _isPrintingPreview]);
+
+            pair<HashMap<WebCore::IntRect, Vector<uint8_t> >::iterator, bool> entry = view->_pagePreviews.add(iter->second, Vector<uint8_t>());
+            entry.first->second.append(data->bytes(), data->size());
+
+            bool receivedResponseToLatestRequest = view->_latestExpectedPreviewCallback == context->callbackID;
+            view->_latestExpectedPreviewCallback = 0;
+            view->_expectedPreviewCallbacks.remove(context->callbackID);
+            if (receivedResponseToLatestRequest)
+                [view _updatePreview];
+        }
+    }
+}
+
+- (void)_preparePDFDataForPrintingOnSecondaryThread
+{
+    ASSERT(isMainThread());
+
+    if (!_webFrame->page()) {
+        _printingCallbackCondition.signal();
+        return;
+    }
+
+    MutexLocker lock(_printingCallbackMutex);
+
+    ASSERT([self _hasPageRects]);
+    ASSERT(_printedPagesData.isEmpty());
+    ASSERT(!_printedPagesPDFDocument);
+    ASSERT(!_expectedPrintCallback);
+
+    unsigned firstPage = [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintFirstPage] unsignedIntValue];
+    unsigned lastPage = [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintLastPage] unsignedIntValue];
+    if (lastPage > _printingPageRects.size()) // Last page is NSIntegerMax if not set by the user.
+        lastPage = _printingPageRects.size();
 
-    _webFrame->page()->computePagesForPrinting(_webFrame.get(), PrintInfo([[NSPrintOperation currentOperation] printInfo]), _webPrintingPageRects, _webTotalScaleFactorForPrinting);
+    ASSERT(firstPage > 0);
+    ASSERT(firstPage <= lastPage);
+    LOG(View, "WKPrintingView requesting PDF data for pages %u...%u", firstPage, lastPage);
 
-    *range = NSMakeRange(1, _webPrintingPageRects.size());
+    // Return to printing mode if we're already back to screen (e.g. due to window resizing).
+    _webFrame->page()->beginPrinting(_webFrame.get(), PrintInfo([_printOperation printInfo]));
+
+    IPCCallbackContext* context = new IPCCallbackContext;
+    RefPtr<DataCallback> callback = DataCallback::create(context, pageDidDrawToPDF);
+    _expectedPrintCallback = callback->callbackID();
+
+    context->view = self;
+    context->callbackID = callback->callbackID();
+
+    _webFrame->page()->drawPagesToPDF(_webFrame.get(), firstPage - 1, lastPage - firstPage + 1, callback.get());
+}
+
+static void pageDidComputePageRects(const Vector<WebCore::IntRect>& pageRects, double totalScaleFactorForPrinting, WKErrorRef, void* untypedContext)
+{
+    ASSERT(isMainThread());
+
+    OwnPtr<IPCCallbackContext> context = adoptPtr(static_cast<IPCCallbackContext*>(untypedContext));
+    WKPrintingView *view = context->view.get();
+
+    // If the user has already changed print setup, then this response is obsolete.
+    if (context->callbackID == view->_expectedComputedPagesCallback) {
+        ASSERT(isMainThread());
+        ASSERT(view->_expectedPreviewCallbacks.isEmpty());
+        ASSERT(!view->_latestExpectedPreviewCallback);
+        ASSERT(!view->_expectedPrintCallback);
+        ASSERT(view->_pagePreviews.isEmpty());
+        view->_expectedComputedPagesCallback = 0;
+
+        view->_printingPageRects = pageRects;
+        view->_totalScaleFactorForPrinting = totalScaleFactorForPrinting;
+
+        if ([view _isPrintingPreview]) {
+            // Show page count, and ask for an actual image to replace placeholder.
+            [view _updatePreview];
+        } else {
+            // When printing, request everything we'll need beforehand.
+            [view _preparePDFDataForPrintingOnSecondaryThread];
+        }
+    }
+}
+
+- (BOOL)_askPageToComputePageRects
+{
+    ASSERT(isMainThread());
+
+    if (!_webFrame->page())
+        return NO;
+
+    ASSERT(!_expectedComputedPagesCallback);
+
+    IPCCallbackContext* context = new IPCCallbackContext;
+    RefPtr<ComputedPagesCallback> callback = ComputedPagesCallback::create(context, pageDidComputePageRects);
+    _expectedComputedPagesCallback = callback->callbackID();
+    context->view = self;
+    context->callbackID = _expectedComputedPagesCallback;
+
+    _webFrame->page()->computePagesForPrinting(_webFrame.get(), PrintInfo([_printOperation printInfo]), callback.release());
     return YES;
 }
 
-// Take over printing. AppKit applies incorrect clipping, and doesn't print pages beyond the first one.
-- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
+static void prepareDataForPrintingOnSecondaryThread(void* untypedContext)
 {
-    LOG(View, "Printing rect x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
+    ASSERT(isMainThread());
 
-    ASSERT(self == visibleView);
+    WKPrintingView *view = static_cast<WKPrintingView *>(untypedContext);
+    MutexLocker lock(view->_printingCallbackMutex);
 
-    _webFrame->page()->beginPrinting(_webFrame.get(), PrintInfo([[NSPrintOperation currentOperation] printInfo]));
+    // We may have received page rects while a message to call this function traveled from secondary thread to main one.
+    if ([view _hasPageRects]) {
+        [view _preparePDFDataForPrintingOnSecondaryThread];
+        return;
+    }
 
-    // FIXME: This is optimized for print preview. Get the whole document at once when actually printing.
-    Vector<uint8_t> pdfData;
-    _webFrame->page()->drawRectToPDF(_webFrame.get(), IntRect(rect), pdfData);
+    // A request for pages has already been made, just wait for it to finish.
+    if (view->_expectedComputedPagesCallback)
+        return;
 
-    RetainPtr<CGDataProviderRef> pdfDataProvider(AdoptCF, CGDataProviderCreateWithData(0, pdfData.data(), pdfData.size(), 0));
-    RetainPtr<CGPDFDocumentRef> pdfDocument(AdoptCF, CGPDFDocumentCreateWithProvider(pdfDataProvider.get()));
+    [view _askPageToComputePageRects];
+}
+
+- (BOOL)knowsPageRange:(NSRangePointer)range
+{
+    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");
+    ASSERT(_printOperation == [NSPrintOperation currentOperation]);
+
+    // Assuming that once we switch to printing from a secondary thread, we don't go back.
+    ASSERT(!_isPrintingFromSecondaryThread || !isMainThread());
+    if (!isMainThread())
+        _isPrintingFromSecondaryThread = YES;
+
+    [self _adjustPrintingMarginsForHeaderAndFooter];
+
+    if ([self _hasPageRects])
+        *range = NSMakeRange(1, _printingPageRects.size());
+    else if (!isMainThread()) {
+        ASSERT(![self _isPrintingPreview]);
+        MutexLocker lock(_printingCallbackMutex);
+        callOnMainThread(prepareDataForPrintingOnSecondaryThread, self);
+        _printingCallbackCondition.wait(_printingCallbackMutex);
+        *range = NSMakeRange(1, _printingPageRects.size());
+    } else {
+        ASSERT([self _isPrintingPreview]);
+
+        [self _askPageToComputePageRects];
+
+        *range = NSMakeRange(1, NSIntegerMax);
+    }
+    return YES;
+}
+
+- (unsigned)_pageForRect:(NSRect)rect
+{
+    // Assuming that rect exactly matches one of the pages.
+    for (size_t i = 0; i < _printingPageRects.size(); ++i) {
+        if (rect.origin.y == _printingPageRects[i].y())
+            return i + 1;
+    }
+    return 0; // Invalid page number.
+}
+
+- (void)_drawPDFDocument:(CGPDFDocumentRef)pdfDocument page:(unsigned)page atPoint:(NSPoint)point
+{
     if (!pdfDocument) {
-        LOG_ERROR("Couldn't create a PDF document with data passed for printing");
+        LOG_ERROR("Couldn't create a PDF document with data passed for preview");
         return;
     }
 
-    CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdfDocument.get(), 1);
+    CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdfDocument, page);
     if (!pdfPage) {
-        LOG_ERROR("Printing data doesn't have page 1");
+        LOG_ERROR("Preview data doesn't have page %d", page);
         return;
     }
 
@@ -131,19 +329,120 @@ NSString * const WebKitOriginalBottomPrintingMarginKey = @"WebKitOriginalBottomM
     CGContextSaveGState(context);
     // Flip the destination.
     CGContextScaleCTM(context, 1, -1);
-    CGContextTranslateCTM(context, 0, -CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox).size.height);
+    CGContextTranslateCTM(context, point.x, -point.y -CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox).size.height);
     CGContextDrawPDFPage(context, pdfPage);
     CGContextRestoreGState(context);
 }
 
+- (void)_drawPreview:(NSRect)nsRect
+{
+    ASSERT(isMainThread());
+
+    IntRect rect(nsRect);
+    HashMap<WebCore::IntRect, Vector<uint8_t> >::iterator pagePreviewIterator = _pagePreviews.find(rect);
+    if (pagePreviewIterator == _pagePreviews.end())  {
+        // It's too early to ask for page preview if we don't even know page size and scale.
+        if ([self _hasPageRects]) {
+            if (uint64_t existingCallback = [self _expectedPreviewCallbackForRect:rect]) {
+                // We've already asked for a preview of this page, and are waiting for response.
+                // There is no need to ask again.
+                _latestExpectedPreviewCallback = existingCallback;
+            } else {
+                // Preview isn't available yet, request it asynchronously.
+                if (!_webFrame->page())
+                    return;
+
+                // Return to printing mode if we're already back to screen (e.g. due to window resizing).
+                _webFrame->page()->beginPrinting(_webFrame.get(), PrintInfo([_printOperation printInfo]));
+
+                IPCCallbackContext* context = new IPCCallbackContext;
+                RefPtr<DataCallback> callback = DataCallback::create(context, pageDidDrawToPDF);
+                _latestExpectedPreviewCallback = callback->callbackID();
+                _expectedPreviewCallbacks.add(_latestExpectedPreviewCallback, rect);
+
+                context->view = self;
+                context->callbackID = callback->callbackID();
+
+                _webFrame->page()->drawRectToPDF(_webFrame.get(), rect, callback.get());
+                return;
+            }
+        }
+
+        // FIXME: Draw a placeholder
+        return;
+    }
+
+    const Vector<uint8_t>& pdfData = pagePreviewIterator->second;
+    RetainPtr<CGDataProviderRef> pdfDataProvider(AdoptCF, CGDataProviderCreateWithData(0, pdfData.data(), pdfData.size(), 0));
+    RetainPtr<CGPDFDocumentRef> pdfDocument(AdoptCF, CGPDFDocumentCreateWithProvider(pdfDataProvider.get()));
+
+    [self _drawPDFDocument:pdfDocument.get() page:1 atPoint:NSMakePoint(nsRect.origin.x, nsRect.origin.y)];
+}
+
+// Take over printing. AppKit applies incorrect clipping, and doesn't print pages beyond the first one.
+- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)nsRect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
+{
+    LOG(View, "WKPrintingView printing rect x:%g, y:%g, width:%g, height:%g", nsRect.origin.x, nsRect.origin.y, nsRect.size.width, nsRect.size.height);
+
+    ASSERT(_printOperation == [NSPrintOperation currentOperation]);
+    ASSERT(self == visibleView);
+
+    if (!_webFrame->page())
+        return;
+
+    if ([self _isPrintingPreview]) {
+        [self _drawPreview:nsRect];
+        return;
+    }
+
+    ASSERT(!isMainThread());
+    ASSERT(!_printedPagesData.isEmpty()); // Prepared by knowsPageRange:
+
+    if (!_printedPagesPDFDocument) {
+        RetainPtr<CGDataProviderRef> pdfDataProvider(AdoptCF, CGDataProviderCreateWithData(0, _printedPagesData.data(), _printedPagesData.size(), 0));
+        _printedPagesPDFDocument.adoptCF(CGPDFDocumentCreateWithProvider(pdfDataProvider.get()));
+    }
+
+    [self _drawPDFDocument:_printedPagesPDFDocument.get() page:[self _pageForRect:nsRect] atPoint:NSMakePoint(nsRect.origin.x, nsRect.origin.y)];
+}
+
+- (void)_drawPageBorderWithSizeOnMainThread:(NSSize)borderSize
+{
+    ASSERT(isMainThread());
+
+    // When printing from a secondary thread, the main thread doesn't have graphics context and printing operation set up.
+    NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
+    [NSGraphicsContext setCurrentContext:[_printOperation context]];
+
+    ASSERT(![NSPrintOperation currentOperation]);
+    [NSPrintOperation setCurrentOperation:_printOperation];
+
+    [self drawPageBorderWithSize:borderSize];
+
+    [NSPrintOperation setCurrentOperation:nil];
+    [NSGraphicsContext setCurrentContext:currentContext];
+}
+
 - (void)drawPageBorderWithSize:(NSSize)borderSize
 {
-    ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));    
+    ASSERT(NSEqualSizes(borderSize, [[_printOperation printInfo] paperSize]));    
+    ASSERT(_printOperation == [NSPrintOperation currentOperation]);
+
+    if (!isMainThread()) {
+        // Don't call the client from a secondary thread.
+        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[WKPrintingView instanceMethodSignatureForSelector:@selector(_drawPageBorderWithSizeOnMainThread:)]];
+        [invocation setSelector:@selector(_drawPageBorderWithSizeOnMainThread:)];
+        [invocation setArgument:&borderSize atIndex:2];
+        [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:self waitUntilDone:YES];
+        return;
+    }
+
+    if (!_webFrame->page())
+        return;
 
     // The header and footer rect height scales with the page, but the width is always
     // all the way across the printed page (inset by printing margins).
-    NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
-    NSPrintInfo *printInfo = [printOperation printInfo];
+    NSPrintInfo *printInfo = [_printOperation printInfo];
     CGFloat scale = [printInfo scalingFactor];
     NSSize paperSize = [printInfo paperSize];
     CGFloat headerFooterLeft = [printInfo leftMargin] / scale;
@@ -168,14 +467,43 @@ NSString * const WebKitOriginalBottomPrintingMarginKey = @"WebKitOriginalBottomM
 // if AppKit makes it SPI/API.
 - (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation 
 {
-    return _webTotalScaleFactorForPrinting;
+    ASSERT(_printOperation == printOperation);
+    return _totalScaleFactorForPrinting;
 }
 
 // Return the drawing rectangle for a particular page number
 - (NSRect)rectForPage:(NSInteger)page
 {
-    LOG(View, "rectForPage:%d -> x %d, y %d, width %d, height %d\n", (int)page, _webPrintingPageRects[page - 1].x(), _webPrintingPageRects[page - 1].y(), _webPrintingPageRects[page - 1].width(), _webPrintingPageRects[page - 1].height());
-    return _webPrintingPageRects[page - 1];
+    ASSERT(_printOperation == [NSPrintOperation currentOperation]);
+    if (![self _hasPageRects]) {
+        LOG(View, "-[WKPrintingView rectForPage:%d] - data is not yet available", (int)page);
+        // We must be still calculating the page range.
+        ASSERT(_expectedComputedPagesCallback);
+        return NSMakeRect(0, 0, 1, 1);
+    }
+    LOG(View, "-[WKPrintingView rectForPage:%d] -> x %d, y %d, width %d, height %d", (int)page, _printingPageRects[page - 1].x(), _printingPageRects[page - 1].y(), _printingPageRects[page - 1].width(), _printingPageRects[page - 1].height());
+    return _printingPageRects[page - 1];
 }
 
+- (void)endDocument
+{
+    ASSERT(_printOperation == [NSPrintOperation currentOperation]);
+
+    // Forcing preview update gets us here, but page setup hasn't actually changed.
+    if (_isForcingPreviewUpdate)
+        return;
+
+    LOG(View, "-[WKPrintingView endDocument] - clearing cached data");
+
+    // Both existing data and pending responses are now obsolete.
+    _printingPageRects.clear();
+    _totalScaleFactorForPrinting = 1;
+    _pagePreviews.clear();
+    _printedPagesData.clear();
+    _printedPagesPDFDocument = nullptr;
+    _expectedComputedPagesCallback = 0;
+    _expectedPreviewCallbacks.clear();
+    _latestExpectedPreviewCallback = 0;
+    _expectedPrintCallback = 0;
+}
 @end
index 9432e33..fb06fd3 100644 (file)
@@ -1391,7 +1391,10 @@ static void drawPageBackground(CGContextRef context, WebPageProxy* page, const I
     } else {
         RetainPtr<WKPrintingView> printingView(AdoptNS, [[WKPrintingView alloc] initWithFrameProxy:toImpl(frameRef)]);
         // NSPrintOperation takes ownership of the view.
-        return [NSPrintOperation printOperationWithView:printingView.get()];
+        NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get()];
+        [printOperation setCanSpawnSeparateThread:YES];
+        printingView->_printOperation = printOperation;
+        return printOperation;
     }
 }
 
index 010ce04..c34414a 100644 (file)
@@ -89,6 +89,61 @@ private:
     uint64_t m_callbackID;
 };
 
+// FIXME: Make a version of GenericCallback with two arguments, and define ComputedPagesCallback as a specialization.
+class ComputedPagesCallback : public RefCounted<ComputedPagesCallback> {
+public:
+    typedef void (*CallbackFunction)(const Vector<WebCore::IntRect>&, double, WKErrorRef, void*);
+
+    static PassRefPtr<ComputedPagesCallback> create(void* context, CallbackFunction callback)
+    {
+        return adoptRef(new ComputedPagesCallback(context, callback));
+    }
+
+    ~ComputedPagesCallback()
+    {
+        ASSERT(!m_callback);
+    }
+
+    void performCallbackWithReturnValue(const Vector<WebCore::IntRect>& returnValue1, double returnValue2)
+    {
+        ASSERT(m_callback);
+
+        m_callback(returnValue1, returnValue2, 0, m_context);
+
+        m_callback = 0;
+    }
+    
+    void invalidate()
+    {
+        ASSERT(m_callback);
+
+        RefPtr<WebError> error = WebError::create();
+        m_callback(Vector<WebCore::IntRect>(), 0, toAPI(error.get()), m_context);
+        
+        m_callback = 0;
+    }
+
+    uint64_t callbackID() const { return m_callbackID; }
+
+private:
+    static uint64_t generateCallbackID()
+    {
+        static uint64_t uniqueCallbackID = 1;
+        return uniqueCallbackID++;
+    }
+
+    ComputedPagesCallback(void* context, CallbackFunction callback)
+        : m_context(context)
+        , m_callback(callback)
+        , m_callbackID(generateCallbackID())
+    {
+    }
+
+    void* m_context;
+    CallbackFunction m_callback;
+    uint64_t m_callbackID;
+};
+
 template<typename APIReturnValueType, typename InternalReturnValueType = typename APITypeInfo<APIReturnValueType>::ImplType>
 class GenericCallback : public RefCounted<GenericCallback<APIReturnValueType, InternalReturnValueType> > {
 public:
index 02e598a..f24905d 100644 (file)
@@ -275,8 +275,10 @@ void WebPageProxy::close()
 
     m_toolTip = String();
 
+    invalidateCallbackMap(m_voidCallbacks);
     invalidateCallbackMap(m_dataCallbacks);
     invalidateCallbackMap(m_stringCallbacks);
+    invalidateCallbackMap(m_computedPagesCallbacks);
 
     Vector<WebEditCommandProxy*> editCommandVector;
     copyToVector(m_editCommandSet, editCommandVector);
@@ -2208,6 +2210,17 @@ void WebPageProxy::stringCallback(const String& resultString, uint64_t callbackI
     callback->performCallbackWithReturnValue(resultString.impl());
 }
 
+void WebPageProxy::computedPagesCallback(const Vector<WebCore::IntRect>& pageRects, double totalScaleFactorForPrinting, uint64_t callbackID)
+{
+    RefPtr<ComputedPagesCallback> callback = m_computedPagesCallbacks.take(callbackID);
+    if (!callback) {
+        // FIXME: Log error or assert.
+        return;
+    }
+
+    callback->performCallbackWithReturnValue(pageRects, totalScaleFactorForPrinting);
+}
+
 #if PLATFORM(MAC)
 void WebPageProxy::sendAccessibilityPresenterToken(const CoreIPC::DataReference& token)
 {
@@ -2298,6 +2311,7 @@ void WebPageProxy::processDidCrash()
     invalidateCallbackMap(m_voidCallbacks);
     invalidateCallbackMap(m_dataCallbacks);
     invalidateCallbackMap(m_stringCallbacks);
+    invalidateCallbackMap(m_computedPagesCallbacks);
 
     Vector<WebEditCommandProxy*> editCommandVector;
     copyToVector(m_editCommandSet, editCommandVector);
@@ -2464,17 +2478,27 @@ void WebPageProxy::endPrinting()
     process()->send(Messages::WebPage::EndPrinting(), m_pageID);
 }
 
-void WebPageProxy::computePagesForPrinting(WebFrameProxy* frame, const PrintInfo& printInfo, Vector<WebCore::IntRect>& resultPageRects, double& resultTotalScaleFactorForPrinting)
+void WebPageProxy::computePagesForPrinting(WebFrameProxy* frame, const PrintInfo& printInfo, PassRefPtr<ComputedPagesCallback> callback)
 {
-    // Layout for printing can take a long time, but we need to have the answer.
-    process()->sendSync(Messages::WebPage::ComputePagesForPrinting(frame->frameID(), printInfo), Messages::WebPage::ComputePagesForPrinting::Reply(resultPageRects, resultTotalScaleFactorForPrinting), m_pageID);
+    uint64_t callbackID = callback->callbackID();
+    m_computedPagesCallbacks.set(callbackID, callback.get());
+    m_isInPrintingMode = true;
+    process()->send(Messages::WebPage::ComputePagesForPrinting(frame->frameID(), printInfo, callbackID), m_pageID);
 }
 
 #if PLATFORM(MAC)
-void WebPageProxy::drawRectToPDF(WebFrameProxy* frame, const IntRect& rect, Vector<uint8_t>& pdfData)
+void WebPageProxy::drawRectToPDF(WebFrameProxy* frame, const IntRect& rect, PassRefPtr<DataCallback> callback)
 {
-    // Printing can take a long time, but we need to have the answer.
-    process()->sendSync(Messages::WebPage::DrawRectToPDF(frame->frameID(), rect), Messages::WebPage::DrawRectToPDF::Reply(pdfData), m_pageID);
+    uint64_t callbackID = callback->callbackID();
+    m_dataCallbacks.set(callbackID, callback.get());
+    process()->send(Messages::WebPage::DrawRectToPDF(frame->frameID(), rect, callbackID), m_pageID);
+}
+
+void WebPageProxy::drawPagesToPDF(WebFrameProxy* frame, uint32_t first, uint32_t count, PassRefPtr<DataCallback> callback)
+{
+    uint64_t callbackID = callback->callbackID();
+    m_dataCallbacks.set(callbackID, callback.get());
+    process()->send(Messages::WebPage::DrawPagesToPDF(frame->frameID(), first, count, callbackID), m_pageID);
 }
 #endif
 
index 4228024..7caf9d9 100644 (file)
@@ -366,9 +366,10 @@ public:
 
     void beginPrinting(WebFrameProxy*, const PrintInfo&);
     void endPrinting();
-    void computePagesForPrinting(WebFrameProxy*, const PrintInfo&, Vector<WebCore::IntRect>& resultPageRects, double& resultTotalScaleFactorForPrinting);
+    void computePagesForPrinting(WebFrameProxy*, const PrintInfo&, PassRefPtr<ComputedPagesCallback>);
 #if PLATFORM(MAC)
-    void drawRectToPDF(WebFrameProxy*, const WebCore::IntRect&, Vector<uint8_t>& pdfData);
+    void drawRectToPDF(WebFrameProxy*, const WebCore::IntRect&, PassRefPtr<DataCallback>);
+    void drawPagesToPDF(WebFrameProxy*, uint32_t first, uint32_t count, PassRefPtr<DataCallback>);
 #endif
 
 private:
@@ -523,6 +524,7 @@ private:
     void voidCallback(uint64_t);
     void dataCallback(const CoreIPC::DataReference&, uint64_t);
     void stringCallback(const String&, uint64_t);
+    void computedPagesCallback(const Vector<WebCore::IntRect>&, double totalScaleFactorForPrinting, uint64_t);
 
     void focusedFrameChanged(uint64_t frameID);
     void frameSetLargestFrameChanged(uint64_t frameID);
@@ -570,6 +572,7 @@ private:
     HashMap<uint64_t, RefPtr<VoidCallback> > m_voidCallbacks;
     HashMap<uint64_t, RefPtr<DataCallback> > m_dataCallbacks;
     HashMap<uint64_t, RefPtr<StringCallback> > m_stringCallbacks;
+    HashMap<uint64_t, RefPtr<ComputedPagesCallback> > m_computedPagesCallbacks;
 
     HashSet<WebEditCommandProxy*> m_editCommandSet;
 
index ea16cc4..a48ca8f 100644 (file)
@@ -116,6 +116,7 @@ messages -> WebPageProxy {
     VoidCallback(uint64_t callbackID)
     DataCallback(CoreIPC::DataReference resultData, uint64_t callbackID)
     StringCallback(WTF::String resultString, uint64_t callbackID)
+    ComputedPagesCallback(Vector<WebCore::IntRect> pageRects, double totalScaleFactorForPrinting, uint64_t callbackID)
 
     DidReceiveAccessibilityPageToken(CoreIPC::DataReference data)
 
index 686195b..b4df4df 100644 (file)
@@ -1784,6 +1784,9 @@ void WebPage::beginPrinting(uint64_t frameID, const PrintInfo& printInfo)
         m_printContext = adoptPtr(new PrintContext(coreFrame));
 
     m_printContext->begin(printInfo.availablePaperWidth, printInfo.availablePaperHeight);
+
+    float fullPageHeight;
+    m_printContext->computePageRects(FloatRect(0, 0, printInfo.availablePaperWidth, printInfo.availablePaperHeight), 0, 0, printInfo.pageSetupScaleFactor, fullPageHeight, true);
 }
 
 void WebPage::endPrinting()
@@ -1791,57 +1794,90 @@ void WebPage::endPrinting()
     m_printContext = nullptr;
 }
 
-void WebPage::computePagesForPrinting(uint64_t frameID, const PrintInfo& printInfo, Vector<IntRect>& resultPageRects, double& resultTotalScaleFactorForPrinting)
+void WebPage::computePagesForPrinting(uint64_t frameID, const PrintInfo& printInfo, uint64_t callbackID)
 {
-    beginPrinting(frameID, printInfo);
+    Vector<IntRect> resultPageRects;
+    double resultTotalScaleFactorForPrinting = 1;
 
-    WebFrame* frame = WebProcess::shared().webFrame(frameID);
-    if (!frame)
-        return;
-
-    float fullPageHeight;
-    m_printContext->computePageRects(FloatRect(0, 0, printInfo.availablePaperWidth, printInfo.availablePaperHeight), 0, 0, printInfo.pageSetupScaleFactor, fullPageHeight, true);
+    beginPrinting(frameID, printInfo);
 
-    resultTotalScaleFactorForPrinting = m_printContext->computeAutomaticScaleFactor(printInfo.availablePaperWidth) * printInfo.pageSetupScaleFactor;
-    resultPageRects = m_printContext->pageRects();
+    if (m_printContext) {
+        resultPageRects = m_printContext->pageRects();
+        resultTotalScaleFactorForPrinting = m_printContext->computeAutomaticScaleFactor(printInfo.availablePaperWidth) * printInfo.pageSetupScaleFactor;
+    }
 
     // If we're asked to print, we should actually print at least a blank page.
     if (resultPageRects.isEmpty())
         resultPageRects.append(IntRect(0, 0, 1, 1));
+
+    send(Messages::WebPageProxy::ComputedPagesCallback(resultPageRects, resultTotalScaleFactorForPrinting, callbackID));
 }
 
 #if PLATFORM(MAC)
 // FIXME: Find a better place for Mac specific code.
-void WebPage::drawRectToPDF(uint64_t frameID, const WebCore::IntRect& rect, Vector<uint8_t>& pdfData)
+void WebPage::drawRectToPDF(uint64_t frameID, const WebCore::IntRect& rect, uint64_t callbackID)
 {
     WebFrame* frame = WebProcess::shared().webFrame(frameID);
-    if (!frame)
-        return;
+    Frame* coreFrame = frame ? frame->coreFrame() : 0;
 
-    Frame* coreFrame = frame->coreFrame();
-    if (!coreFrame)
-        return;
+    RetainPtr<CFMutableDataRef> pdfPageData(AdoptCF, CFDataCreateMutable(0, 0));
+
+    if (coreFrame) {
+        ASSERT(coreFrame->document()->printing());
+
+        // FIXME: Use CGDataConsumerCreate with callbacks to avoid copying the data.
+        RetainPtr<CGDataConsumerRef> pdfDataConsumer(AdoptCF, CGDataConsumerCreateWithCFData(pdfPageData.get()));
+
+        CGRect mediaBox = CGRectMake(0, 0, rect.width(), rect.height());
+        RetainPtr<CGContextRef> context(AdoptCF, CGPDFContextCreate(pdfDataConsumer.get(), &mediaBox, 0));
+        RetainPtr<CFDictionaryRef> pageInfo(AdoptCF, CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+        CGPDFContextBeginPage(context.get(), pageInfo.get());
+
+        GraphicsContext ctx(context.get());
+        ctx.scale(FloatSize(1, -1));
+        ctx.translate(0, -rect.height());
+        m_printContext->spoolRect(ctx, rect);
+
+        CGPDFContextEndPage(context.get());
+        CGPDFContextClose(context.get());
+    }
 
-    ASSERT(coreFrame->document()->printing());
+    send(Messages::WebPageProxy::DataCallback(CoreIPC::DataReference(CFDataGetBytePtr(pdfPageData.get()), CFDataGetLength(pdfPageData.get())), callbackID));
+}
+
+void WebPage::drawPagesToPDF(uint64_t frameID, uint32_t first, uint32_t count, uint64_t callbackID)
+{
+    WebFrame* frame = WebProcess::shared().webFrame(frameID);
+    Frame* coreFrame = frame ? frame->coreFrame() : 0;
 
     RetainPtr<CFMutableDataRef> pdfPageData(AdoptCF, CFDataCreateMutable(0, 0));
 
-    // FIXME: Use CGDataConsumerCreate with callbacks to avoid copying the data.
-    RetainPtr<CGDataConsumerRef> pdfDataConsumer(AdoptCF, CGDataConsumerCreateWithCFData(pdfPageData.get()));
+    if (coreFrame) {
+        ASSERT(coreFrame->document()->printing());
 
-    CGRect mediaBox = CGRectMake(0, 0, frame->size().width(), frame->size().height());
-    RetainPtr<CGContextRef> context(AdoptCF, CGPDFContextCreate(pdfDataConsumer.get(), &mediaBox, 0));
-    RetainPtr<CFDictionaryRef> pageInfo(AdoptCF, CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
-    CGPDFContextBeginPage(context.get(), pageInfo.get());
+        // FIXME: Use CGDataConsumerCreate with callbacks to avoid copying the data.
+        RetainPtr<CGDataConsumerRef> pdfDataConsumer(AdoptCF, CGDataConsumerCreateWithCFData(pdfPageData.get()));
 
-    GraphicsContext ctx(context.get());
-    m_printContext->spoolRect(ctx, rect);
+        CGRect mediaBox = m_printContext->pageRect(0);
+        RetainPtr<CGContextRef> context(AdoptCF, CGPDFContextCreate(pdfDataConsumer.get(), &mediaBox, 0));
+        for (uint32_t page = first; page < first + count; ++page) {
+            if (page > m_printContext->pageCount())
+                break;
 
-    CGPDFContextEndPage(context.get());
-    CGPDFContextClose(context.get());
+            RetainPtr<CFDictionaryRef> pageInfo(AdoptCF, CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+            CGPDFContextBeginPage(context.get(), pageInfo.get());
+
+            GraphicsContext ctx(context.get());
+            ctx.scale(FloatSize(1, -1));
+            ctx.translate(0, -m_printContext->pageRect(page).height());
+            m_printContext->spoolPage(ctx, page, m_printContext->pageRect(page).width());
+
+            CGPDFContextEndPage(context.get());
+        }
+        CGPDFContextClose(context.get());
+    }
 
-    pdfData.resize(CFDataGetLength(pdfPageData.get()));
-    CFDataGetBytes(pdfPageData.get(), CFRangeMake(0, pdfData.size()), pdfData.data());
+    send(Messages::WebPageProxy::DataCallback(CoreIPC::DataReference(CFDataGetBytePtr(pdfPageData.get()), CFDataGetLength(pdfPageData.get())), callbackID));
 }
 #endif
 
index a465327..3dae655 100644 (file)
@@ -309,9 +309,10 @@ public:
 
     void beginPrinting(uint64_t frameID, const PrintInfo&);
     void endPrinting();
-    void computePagesForPrinting(uint64_t frameID, const PrintInfo&, Vector<WebCore::IntRect>& resultPageRects, double& resultTotalScaleFactorForPrinting);
+    void computePagesForPrinting(uint64_t frameID, const PrintInfo&, uint64_t callbackID);
 #if PLATFORM(MAC)
-    void drawRectToPDF(uint64_t frameID, const WebCore::IntRect&, Vector<uint8_t>& pdfData);
+    void drawRectToPDF(uint64_t frameID, const WebCore::IntRect&, uint64_t callbackID);
+    void drawPagesToPDF(uint64_t frameID, uint32_t first, uint32_t count, uint64_t callbackID);
 #endif
 
     bool mainFrameHasCustomRepresentation() const;
index 5d02d31..afd4c72 100644 (file)
@@ -139,11 +139,12 @@ messages -> WebPage {
     SetWindowResizerSize(WebCore::IntSize intersectsView)
 
     # Printing.
-    BeginPrinting(uint64_t frameID, WebKit::PrintInfo printInfo);
+    BeginPrinting(uint64_t frameID, WebKit::PrintInfo printInfo)
     EndPrinting();
-    ComputePagesForPrinting(uint64_t frameID, WebKit::PrintInfo printInfo) -> (Vector<WebCore::IntRect> pageRects, double totalScaleFactorForPrinting)
+    ComputePagesForPrinting(uint64_t frameID, WebKit::PrintInfo printInfo, uint64_t callbackID)
 #if PLATFORM(MAC)
-    DrawRectToPDF(uint64_t frameID, WebCore::IntRect rect) -> (Vector<uint8_t> pdfData)
+    DrawRectToPDF(uint64_t frameID, WebCore::IntRect rect, uint64_t callbackID)
+    DrawPagesToPDF(uint64_t frameID, uint32_t first, uint32_t count, uint64_t callbackID)
 #endif
 
     // FIXME: This a dummy message, to avoid breaking the build for platforms that don't require