<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[166896] trunk/Source/WebKit2</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/166896">166896</a></dd>
<dt>Author</dt> <dd>timothy_horton@apple.com</dd>
<dt>Date</dt> <dd>2014-04-07 17:07:45 -0700 (Mon, 07 Apr 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>[iOS WebKit2] WKPDFView fades in tiles, PDFs load very flashily
https://bugs.webkit.org/show_bug.cgi?id=131325
&lt;rdar://problem/16382960&gt;

Reviewed by Anders Carlsson.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView scrollViewDidScroll:]):
Forward scroll events to the current custom content provider.

* UIProcess/Cocoa/WKWebViewContentProvider.h:
Make content providers UIScrollViewDelegates (though we only forward didScroll for now).

* UIProcess/ios/WKPDFView.mm:
(-[WKPDFView web_setContentProviderData:suggestedFilename:]):
(-[WKPDFView web_setMinimumSize:]):
(-[WKPDFView web_setScrollView:]):
(-[WKPDFView scrollViewDidScroll:]):
(-[WKPDFView _revalidateViews]):
(-[WKPDFView _computePageAndDocumentFrames]):
(-[WKPDFView layoutViews]): Deleted.
Keep track of each UIPDFPage, UIPDFPageView, and its frame (in unscaled/document coordinates).
Compute all the rects up front (and re-compute upon rotation, loading, etc.).
When scrolling, find all the pages that intersect the view (inflated by 1.5x in each
vertical direction), and ensure that pages that are inside that rect have
UIPDFPageViews installed, and ones that are outside that rect do not.
Use setUseBackingLayer to ensure that we have a low-resolution layer behind the
tiled layer to avoid flashiness.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICocoaWKWebViewmm">trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessCocoaWKWebViewContentProviderh">trunk/Source/WebKit2/UIProcess/Cocoa/WKWebViewContentProvider.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosWKPDFViewmm">trunk/Source/WebKit2/UIProcess/ios/WKPDFView.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (166895 => 166896)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2014-04-08 00:04:20 UTC (rev 166895)
+++ trunk/Source/WebKit2/ChangeLog        2014-04-08 00:07:45 UTC (rev 166896)
</span><span class="lines">@@ -1,3 +1,34 @@
</span><ins>+2014-04-07  Tim Horton  &lt;timothy_horton@apple.com&gt;
+
+        [iOS WebKit2] WKPDFView fades in tiles, PDFs load very flashily
+        https://bugs.webkit.org/show_bug.cgi?id=131325
+        &lt;rdar://problem/16382960&gt;
+
+        Reviewed by Anders Carlsson.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView scrollViewDidScroll:]):
+        Forward scroll events to the current custom content provider.
+
+        * UIProcess/Cocoa/WKWebViewContentProvider.h:
+        Make content providers UIScrollViewDelegates (though we only forward didScroll for now).
+
+        * UIProcess/ios/WKPDFView.mm:
+        (-[WKPDFView web_setContentProviderData:suggestedFilename:]):
+        (-[WKPDFView web_setMinimumSize:]):
+        (-[WKPDFView web_setScrollView:]):
+        (-[WKPDFView scrollViewDidScroll:]):
+        (-[WKPDFView _revalidateViews]):
+        (-[WKPDFView _computePageAndDocumentFrames]):
+        (-[WKPDFView layoutViews]): Deleted.
+        Keep track of each UIPDFPage, UIPDFPageView, and its frame (in unscaled/document coordinates).
+        Compute all the rects up front (and re-compute upon rotation, loading, etc.).
+        When scrolling, find all the pages that intersect the view (inflated by 1.5x in each
+        vertical direction), and ensure that pages that are inside that rect have
+        UIPDFPageViews installed, and ones that are outside that rect do not.
+        Use setUseBackingLayer to ensure that we have a low-resolution layer behind the
+        tiled layer to avoid flashiness.
+
</ins><span class="cx"> 2014-04-07  Dan Bernstein  &lt;mitz@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Add API::InjectedBundle::FormClient
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICocoaWKWebViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm (166895 => 166896)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm        2014-04-08 00:04:20 UTC (rev 166895)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm        2014-04-08 00:07:45 UTC (rev 166896)
</span><span class="lines">@@ -656,6 +656,9 @@
</span><span class="cx"> 
</span><span class="cx"> - (void)scrollViewDidScroll:(UIScrollView *)scrollView
</span><span class="cx"> {
</span><ins>+    if (![self usesStandardContentView])
+        [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
+
</ins><span class="cx">     [self _updateVisibleContentRects];
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessCocoaWKWebViewContentProviderh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/Cocoa/WKWebViewContentProvider.h (166895 => 166896)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/Cocoa/WKWebViewContentProvider.h        2014-04-08 00:04:20 UTC (rev 166895)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/WKWebViewContentProvider.h        2014-04-08 00:07:45 UTC (rev 166896)
</span><span class="lines">@@ -32,10 +32,11 @@
</span><span class="cx"> @class NSData;
</span><span class="cx"> @class UIScrollView;
</span><span class="cx"> @protocol NSObject;
</span><ins>+@protocol UIScrollViewDelegate;
</ins><span class="cx"> struct CGSize;
</span><span class="cx"> 
</span><del>-// FIXME: This should be API.
-@protocol WKWebViewContentProvider &lt;NSObject&gt;
</del><ins>+// FIXME: This should be API (and probably should not be a UIScrollViewDelegate).
+@protocol WKWebViewContentProvider &lt;NSObject, UIScrollViewDelegate&gt;
</ins><span class="cx"> 
</span><span class="cx"> - (void)web_setContentProviderData:(NSData *)data suggestedFilename:(NSString *)filename;
</span><span class="cx"> - (void)web_setMinimumSize:(CGSize)size;
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosWKPDFViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/WKPDFView.mm (166895 => 166896)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/WKPDFView.mm        2014-04-08 00:04:20 UTC (rev 166895)
+++ trunk/Source/WebKit2/UIProcess/ios/WKPDFView.mm        2014-04-08 00:07:45 UTC (rev 166896)
</span><span class="lines">@@ -31,17 +31,31 @@
</span><span class="cx"> #import &lt;CoreGraphics/CGPDFDocumentPrivate.h&gt;
</span><span class="cx"> #import &lt;CorePDF/UIPDFDocument.h&gt;
</span><span class="cx"> #import &lt;CorePDF/UIPDFPageView.h&gt;
</span><ins>+#import &lt;WebCore/FloatRect.h&gt;
</ins><span class="cx"> #import &lt;wtf/RetainPtr.h&gt;
</span><span class="cx"> #import &lt;wtf/Vector.h&gt;
</span><span class="cx"> 
</span><ins>+using namespace WebCore;
+
</ins><span class="cx"> const CGFloat pdfPageMargin = 8;
</span><span class="cx"> const CGFloat pdfMinimumZoomScale = 1;
</span><span class="cx"> const CGFloat pdfMaximumZoomScale = 5;
</span><span class="cx"> 
</span><ins>+const float overdrawHeightMultiplier = 1.5;
+
+typedef struct {
+    CGRect frame;
+    RetainPtr&lt;UIPDFPageView&gt; view;
+    RetainPtr&lt;UIPDFPage&gt; page;
+} PDFPageInfo;
+
</ins><span class="cx"> @implementation WKPDFView {
</span><span class="cx">     RetainPtr&lt;UIPDFDocument&gt; _pdfDocument;
</span><span class="cx">     RetainPtr&lt;NSString&gt; _suggestedFilename;
</span><del>-    Vector&lt;UIPDFPageView*&gt; _pageViews;
</del><ins>+
+    Vector&lt;PDFPageInfo&gt; _pages;
+    CGRect _documentFrame;
+
</ins><span class="cx">     CGSize _minimumSize;
</span><span class="cx">     UIScrollView *_scrollView;
</span><span class="cx"> }
</span><span class="lines">@@ -70,67 +84,104 @@
</span><span class="cx"> {
</span><span class="cx">     _suggestedFilename = adoptNS([filename copy]);
</span><span class="cx"> 
</span><del>-    for (auto&amp; pageView : _pageViews)
-        [pageView removeFromSuperview];
</del><ins>+    for (auto&amp; page : _pages)
+        [page.view removeFromSuperview];
</ins><span class="cx"> 
</span><del>-    _pageViews.clear();
</del><ins>+    _pages.clear();
</ins><span class="cx"> 
</span><span class="cx">     RetainPtr&lt;CGDataProvider&gt; dataProvider = adoptCF(CGDataProviderCreateWithCFData((CFDataRef)data));
</span><span class="cx">     RetainPtr&lt;CGPDFDocumentRef&gt; cgPDFDocument = adoptCF(CGPDFDocumentCreateWithProvider(dataProvider.get()));
</span><span class="cx">     _pdfDocument = adoptNS([[UIPDFDocument alloc] initWithCGPDFDocument:cgPDFDocument.get()]);
</span><span class="cx"> 
</span><del>-    for (NSUInteger page = 0; page &lt; [_pdfDocument numberOfPages]; ++page) {
-        UIPDFPage *pdfPage = [_pdfDocument pageAtIndex:page];
-
-        if (!pdfPage)
-            continue;
-
-        // FIXME: We should create and destroy views instead of depending on tiling.
-        RetainPtr&lt;UIPDFPageView&gt; pageView = adoptNS([[UIPDFPageView alloc] initWithPage:pdfPage tiledContent:YES]);
-        [self addSubview:pageView.get()];
-        _pageViews.append(pageView.get());
-
-        [pageView contentLayer].contentsScale = self.window.screen.scale;
-    }
-
-    [self layoutViews];
</del><ins>+    [self _computePageAndDocumentFrames];
+    [self _revalidateViews];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> - (void)web_setMinimumSize:(CGSize)size
</span><span class="cx"> {
</span><span class="cx">     _minimumSize = size;
</span><span class="cx"> 
</span><del>-    [self layoutViews];
</del><ins>+    [self _computePageAndDocumentFrames];
+    [self _revalidateViews];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> - (void)web_setScrollView:(UIScrollView *)scrollView
</span><span class="cx"> {
</span><span class="cx">     _scrollView = scrollView;
</span><ins>+
+    [_scrollView setMinimumZoomScale:pdfMinimumZoomScale];
+    [_scrollView setMaximumZoomScale:pdfMaximumZoomScale];
+    [_scrollView setContentSize:_documentFrame.size];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (void)layoutViews
</del><ins>+- (void)scrollViewDidScroll:(UIScrollView *)scrollView
</ins><span class="cx"> {
</span><ins>+    if (scrollView.isZoomBouncing)
+        return;
+
+    [self _revalidateViews];
+}
+
+- (void)_revalidateViews
+{
+    CGRect targetRect = [_scrollView convertRect:_scrollView.bounds toView:self];
+
+    // We apply overdraw after applying scale in order to avoid excessive
+    // memory use caused by scaling the overdraw.
+    targetRect = CGRectInset(targetRect, 0, -targetRect.size.height * overdrawHeightMultiplier);
+
+    for (auto&amp; pageInfo : _pages) {
+        if (!CGRectIntersectsRect(pageInfo.frame, targetRect)) {
+            [pageInfo.view removeFromSuperview];
+            pageInfo.view = nullptr;
+            continue;
+        }
+
+        if (pageInfo.view)
+            continue;
+
+        pageInfo.view = adoptNS([[UIPDFPageView alloc] initWithPage:pageInfo.page.get() tiledContent:YES]);
+        [pageInfo.view setUseBackingLayer:YES];
+        [self addSubview:pageInfo.view.get()];
+
+        [pageInfo.view setFrame:pageInfo.frame];
+        [pageInfo.view contentLayer].contentsScale = self.window.screen.scale;
+    }
+}
+
+- (void)_computePageAndDocumentFrames
+{
+    NSUInteger pageCount = [_pdfDocument numberOfPages];
+
+    for (auto&amp; pageInfo : _pages)
+        [pageInfo.view removeFromSuperview];
+
+    _pages.clear();
+    _pages.reserveCapacity(pageCount);
+
</ins><span class="cx">     CGRect pageFrame = CGRectMake(0, 0, _minimumSize.width, _minimumSize.height);
</span><ins>+    for (NSUInteger pageNumber = 0; pageNumber &lt; pageCount; ++pageNumber) {
+        UIPDFPage *page = [_pdfDocument pageAtIndex:pageNumber];
+        if (!page)
+            continue;
</ins><span class="cx"> 
</span><del>-    for (auto&amp; pageView : _pageViews) {
-        CGSize pageSize = [pageView.page size];
-
</del><ins>+        CGSize pageSize = [page size];
</ins><span class="cx">         pageFrame.size.height = pageSize.height / pageSize.width * pageFrame.size.width;
</span><del>-
</del><span class="cx">         CGRect pageFrameWithMarginApplied = CGRectInset(pageFrame, pdfPageMargin, pdfPageMargin);
</span><del>-        [pageView setFrame:pageFrameWithMarginApplied];
</del><span class="cx"> 
</span><ins>+        PDFPageInfo pageInfo;
+        pageInfo.page = page;
+        pageInfo.frame = pageFrameWithMarginApplied;
+        _pages.append(pageInfo);
</ins><span class="cx">         pageFrame.origin.y += pageFrame.size.height - pdfPageMargin;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    CGRect newFrame = [self frame];
-    newFrame.size.width = _minimumSize.width;
-    newFrame.size.height = std::max(pageFrame.origin.y + pdfPageMargin, _minimumSize.height);
-    [self setFrame:newFrame];
</del><ins>+    _documentFrame = [self frame];
+    _documentFrame.size.width = _minimumSize.width;
+    _documentFrame.size.height = std::max(pageFrame.origin.y + pdfPageMargin, _minimumSize.height);
</ins><span class="cx"> 
</span><del>-    [_scrollView setContentSize:newFrame.size];
-    [_scrollView setMinimumZoomScale:pdfMinimumZoomScale];
-    [_scrollView setMaximumZoomScale:pdfMaximumZoomScale];
</del><ins>+    [self setFrame:_documentFrame];
+    [_scrollView setContentSize:_documentFrame.size];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> @end
</span></span></pre>
</div>
</div>

</body>
</html>