<rdar://problem/10336700> Add API to get rendered text image without having to select it
authormitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Nov 2011 21:46:01 +0000 (21:46 +0000)
committermitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Nov 2011 21:46:01 +0000 (21:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=71407

Reviewed by Simon Fraser.

Source/WebCore:

Test: TestWebKitAPI/Tests/mac/RenderedImageFromDOMRange.mm

* bindings/objc/DOM.mm:
(-[DOMRange renderedImageForcingBlackText:]): Added.
* bindings/objc/DOMPrivate.h:
* page/Frame.h:
* page/mac/FrameMac.mm:
(WebCore::Frame::rangeImage): Added. Sets the selection in the RenderView (only) based on the
given range and gets a selection-only rendering of the view, then restores the selection.
* rendering/RenderView.cpp:
(WebCore::RenderView::setSelection): Skip all invalidation if the repaint behavior is RepaintNothing.
(WebCore::RenderView::getSelection): Added this getter.
* rendering/RenderView.h:

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/mac/RenderedImageFromDOMRange.mm: Added.
(-[RenderedImageFromDOMRangeFrameLoadDelegate webView:didFinishLoadForFrame:]):
(TestWebKitAPI::TEST):

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

Source/WebCore/ChangeLog
Source/WebCore/bindings/objc/DOM.mm
Source/WebCore/bindings/objc/DOMPrivate.h
Source/WebCore/page/Frame.h
Source/WebCore/page/mac/FrameMac.mm
Source/WebCore/rendering/RenderView.cpp
Source/WebCore/rendering/RenderView.h
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/mac/RenderedImageFromDOMRange.mm [new file with mode: 0644]

index fab5db80f236dfdd753deea975c9fa987cae1047..59e9e84afd696398ded9dc0ca95051059f797b9b 100644 (file)
@@ -1,3 +1,24 @@
+2011-11-02  Dan Bernstein  <mitz@apple.com>
+
+        <rdar://problem/10336700> Add API to get rendered text image without having to select it
+        https://bugs.webkit.org/show_bug.cgi?id=71407
+
+        Reviewed by Simon Fraser.
+
+        Test: TestWebKitAPI/Tests/mac/RenderedImageFromDOMRange.mm
+
+        * bindings/objc/DOM.mm:
+        (-[DOMRange renderedImageForcingBlackText:]): Added.
+        * bindings/objc/DOMPrivate.h:
+        * page/Frame.h:
+        * page/mac/FrameMac.mm:
+        (WebCore::Frame::rangeImage): Added. Sets the selection in the RenderView (only) based on the
+        given range and gets a selection-only rendering of the view, then restores the selection.
+        * rendering/RenderView.cpp:
+        (WebCore::RenderView::setSelection): Skip all invalidation if the repaint behavior is RepaintNothing.
+        (WebCore::RenderView::getSelection): Added this getter.
+        * rendering/RenderView.h:
+
 2011-11-02  Tom Sepez  <tsepez@chromium.org>
 
         XSSAuditor is silent
index 55cd09344d4143b7221f130578375a23b8dc3656..70af203266ff17d769118b7ce58646adfeb85a52 100644 (file)
@@ -403,6 +403,16 @@ id <DOMEventTarget> kit(WebCore::EventTarget* eventTarget)
     return core(self)->boundingBox();
 }
 
+- (NSImage *)renderedImageForcingBlackText:(BOOL)forceBlackText
+{
+    WebCore::Range* range = core(self);
+    WebCore::Frame* frame = range->ownerDocument()->frame();
+    if (!frame)
+        return nil;
+
+    return frame->rangeImage(range, forceBlackText);
+}
+
 - (NSArray *)textRects
 {
     // FIXME: The call to updateLayoutIgnorePendingStylesheets should be moved into WebCore::Range.
index ab3b56326735cf74d2221b7526f184e046401db0..fe70bc9ae1a041a737a8bd0898f73c625c3078db 100644 (file)
@@ -45,6 +45,7 @@
 
 @interface DOMRange (DOMRangeExtensions)
 - (NSRect)boundingBox;
+- (NSImage *)renderedImageForcingBlackText:(BOOL)forceBlackText;
 - (NSArray *)lineBoxRects; // Deprecated. Use textRects instead.
 - (NSArray *)textRects;
 @end
index ada26a5bf2b691ab3e3ecf41c26fdb64650452e2..81665c6254fcfb0459b67869ae2c160e94f921c1 100644 (file)
@@ -196,6 +196,7 @@ namespace WebCore {
         
 #if PLATFORM(MAC)
         NSImage* selectionImage(bool forceBlackText = false) const;
+        NSImage* rangeImage(Range*, bool forceBlackText = false) const;
         NSImage* snapshotDragImage(Node*, NSRect* imageRect, NSRect* elementRect) const;
         NSImage* imageFromRect(NSRect) const;
 #endif
index dd85f6e628facd5eb1346e7f0d651a74a9c3a95f..234627858a63310004201afb4a39759ac503798b 100644 (file)
@@ -48,6 +48,7 @@
 #import "PlatformWheelEvent.h"
 #import "RegularExpression.h"
 #import "RenderTableCell.h"
+#import "RenderView.h"
 #import "Scrollbar.h"
 #import "SimpleFontData.h"
 #import "visible_units.h"
@@ -104,6 +105,48 @@ NSImage* Frame::selectionImage(bool forceBlackText) const
     return result;
 }
 
+NSImage *Frame::rangeImage(Range* range, bool forceBlackText) const
+{
+    m_view->setPaintBehavior(PaintBehaviorSelectionOnly | (forceBlackText ? PaintBehaviorForceBlackText : 0));
+    m_doc->updateLayout();
+    RenderView* view = contentRenderer();
+    if (!view)
+        return nil;
+
+    VisibleSelection visibleSelection(range);
+
+    if (!visibleSelection.isRange())
+        return nil;
+
+    Position start = visibleSelection.start();
+    Position candidate = start.downstream();
+    if (candidate.isCandidate())
+        start = candidate;
+    Position end = visibleSelection.end();
+    candidate = end.upstream();
+    if (candidate.isCandidate())
+        end = candidate;
+
+    if (start.isNull() || end.isNull() || visibleSelection.visibleStart() == visibleSelection.visibleEnd())
+        return nil;
+
+    RenderObject* savedStartRenderer;
+    int savedStartOffset;
+    RenderObject* savedEndRenderer;
+    int savedEndOffset;
+    view->getSelection(savedStartRenderer, savedStartOffset, savedEndRenderer, savedEndOffset);
+
+    RenderObject* startRenderer = start.deprecatedNode()->renderer();
+    RenderObject* endRenderer = end.deprecatedNode()->renderer();
+
+    view->setSelection(startRenderer, start.deprecatedEditingOffset(), endRenderer, end.deprecatedEditingOffset(), RenderView::RepaintNothing);
+    NSImage* result = imageFromRect(view->selectionBounds());
+    view->setSelection(savedStartRenderer, savedStartOffset, savedEndRenderer, savedEndOffset, RenderView::RepaintNothing);
+
+    m_view->setPaintBehavior(PaintBehaviorNormal);
+    return result;
+}
+
 NSImage* Frame::snapshotDragImage(Node* node, NSRect* imageRect, NSRect* elementRect) const
 {
     RenderObject* renderer = node->renderer();
index f421f981da267dd57d5994abc3ec1a7565d18057..7409744efa06179a45cf8e3f43ccbee50b791005 100644 (file)
@@ -488,7 +488,8 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e
         o = o->nextInPreOrder();
     }
 
-    m_layer->clearBlockSelectionGapsBounds();
+    if (blockRepaintMode != RepaintNothing)
+        m_layer->clearBlockSelectionGapsBounds();
 
     // Now that the selection state has been updated for the new objects, walk them again and
     // put them in the new objects list.
@@ -509,7 +510,7 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e
         o = o->nextInPreOrder();
     }
 
-    if (!m_frameView) {
+    if (!m_frameView || blockRepaintMode == RepaintNothing) {
         // We built the maps, but we aren't going to use them.
         // We need to delete the values, otherwise they'll all leak!
         deleteAllValues(oldSelectedObjects);
@@ -575,6 +576,14 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e
     m_frameView->endDeferredRepaints();
 }
 
+void RenderView::getSelection(RenderObject*& startRenderer, int& startOffset, RenderObject*& endRenderer, int& endOffset) const
+{
+    startRenderer = m_selectionStart;
+    startOffset = m_selectionStartPos;
+    endRenderer = m_selectionEnd;
+    endOffset = m_selectionEndPos;
+}
+
 void RenderView::clearSelection()
 {
     m_layer->repaintBlockSelectionGaps();
index 9d4e9e4307b0f9d0fd15f0c513f416c51184166f..332a8ee37e17d4b6e71cd3bc5800e2d63331ae98 100644 (file)
@@ -76,8 +76,9 @@ public:
     virtual void paint(PaintInfo&, const LayoutPoint&);
     virtual void paintBoxDecorations(PaintInfo&, const IntPoint&);
 
-    enum SelectionRepaintMode { RepaintNewXOROld, RepaintNewMinusOld };
+    enum SelectionRepaintMode { RepaintNewXOROld, RepaintNewMinusOld, RepaintNothing };
     void setSelection(RenderObject* start, int startPos, RenderObject* end, int endPos, SelectionRepaintMode = RepaintNewXOROld);
+    void getSelection(RenderObject*& startRenderer, int& startOffset, RenderObject*& endRenderer, int& endOffset) const;
     void clearSelection();
     RenderObject* selectionStart() const { return m_selectionStart; }
     RenderObject* selectionEnd() const { return m_selectionEnd; }
index c29dbd6e6aac6eb7e618962879ab7e850ae7f0a4..2bdcce5c413dca502ec3dde19d982ffe902c1c50 100644 (file)
@@ -1,3 +1,15 @@
+2011-11-02  Dan Bernstein  <mitz@apple.com>
+
+        <rdar://problem/10336700> Add API to get rendered text image without having to select it
+        https://bugs.webkit.org/show_bug.cgi?id=71407
+
+        Reviewed by Simon Fraser.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/mac/RenderedImageFromDOMRange.mm: Added.
+        (-[RenderedImageFromDOMRangeFrameLoadDelegate webView:didFinishLoadForFrame:]):
+        (TestWebKitAPI::TEST):
+
 2011-11-02  Benjamin Poulain  <benjamin@webkit.org>
 
         Make the main frame's base path more explicit in _drt_descriptionSuitableForTestResult
index bd25ad082dc5db86c5af09c7ed714eecbe0d02bb..fb3ac841da1b321714b930488d1d058a83e8d52c 100644 (file)
@@ -24,6 +24,7 @@
                33DC89141419579F00747EF7 /* LoadCanceledNoServerRedirectCallback_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33DC89131419579F00747EF7 /* LoadCanceledNoServerRedirectCallback_Bundle.cpp */; };
                33E79E06137B5FD900E32D99 /* mouse-move-listener.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 33E79E05137B5FCE00E32D99 /* mouse-move-listener.html */; };
                37200B9213A16230007A4FAD /* VectorReverse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37200B9113A16230007A4FAD /* VectorReverse.cpp */; };
+               3722C8691461E03E00C45D00 /* RenderedImageFromDOMRange.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3722C8681461E03E00C45D00 /* RenderedImageFromDOMRange.mm */; };
                3799AD3A14120A43005EB0C6 /* StringByEvaluatingJavaScriptFromString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3799AD3914120A43005EB0C6 /* StringByEvaluatingJavaScriptFromString.mm */; };
                37DC678D140D7C5000ABCCDB /* DOMRangeOfString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DC678B140D7C5000ABCCDB /* DOMRangeOfString.mm */; };
                37DC6791140D7D7600ABCCDB /* DOMRangeOfString.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 37DC678F140D7D3A00ABCCDB /* DOMRangeOfString.html */; };
                33DC89131419579F00747EF7 /* LoadCanceledNoServerRedirectCallback_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LoadCanceledNoServerRedirectCallback_Bundle.cpp; sourceTree = "<group>"; };
                33E79E05137B5FCE00E32D99 /* mouse-move-listener.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "mouse-move-listener.html"; sourceTree = "<group>"; };
                37200B9113A16230007A4FAD /* VectorReverse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VectorReverse.cpp; path = WTF/VectorReverse.cpp; sourceTree = "<group>"; };
+               3722C8681461E03E00C45D00 /* RenderedImageFromDOMRange.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RenderedImageFromDOMRange.mm; sourceTree = "<group>"; };
                3799AD3914120A43005EB0C6 /* StringByEvaluatingJavaScriptFromString.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StringByEvaluatingJavaScriptFromString.mm; sourceTree = "<group>"; };
                37DC678B140D7C5000ABCCDB /* DOMRangeOfString.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DOMRangeOfString.mm; sourceTree = "<group>"; };
                37DC678F140D7D3A00ABCCDB /* DOMRangeOfString.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DOMRangeOfString.html; sourceTree = "<group>"; };
                                37DC678B140D7C5000ABCCDB /* DOMRangeOfString.mm */,
                                C07E6CAE13FD67650038B22B /* DynamicDeviceScaleFactor.mm */,
                                939BA91614103412001A01BD /* DeviceScaleFactorOnBack.mm */,
+                               3722C8681461E03E00C45D00 /* RenderedImageFromDOMRange.mm */,
                                3799AD3914120A43005EB0C6 /* StringByEvaluatingJavaScriptFromString.mm */,
                        );
                        path = mac;
                                BC3C4C7214575B6A0025FB62 /* WKBrowsingContextLoadDelegateTest.mm in Sources */,
                                BC3C4C7F14587AA60025FB62 /* WKBrowsingContextGroupTest.mm in Sources */,
                                C0C5D3BE14598B6F00A802A6 /* GetBackingScaleFactor.mm in Sources */,
+                               3722C8691461E03E00C45D00 /* RenderedImageFromDOMRange.mm in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
diff --git a/Tools/TestWebKitAPI/Tests/mac/RenderedImageFromDOMRange.mm b/Tools/TestWebKitAPI/Tests/mac/RenderedImageFromDOMRange.mm
new file mode 100644 (file)
index 0000000..4bda167
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "PlatformUtilities.h"
+#import <WebKit/WebDocumentPrivate.h>
+#import <WebKit/DOMPrivate.h>
+#import <wtf/RetainPtr.h>
+
+@interface RenderedImageFromDOMRangeFrameLoadDelegate : NSObject {
+}
+@end
+
+static bool didFinishLoad;
+
+@implementation RenderedImageFromDOMRangeFrameLoadDelegate
+
+- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
+{
+    didFinishLoad = true;
+}
+
+@end
+
+namespace TestWebKitAPI {
+
+TEST(WebKit1, RenderedImageFromDOMRange)
+{
+    RetainPtr<WebView> webView(AdoptNS, [[WebView alloc] initWithFrame:NSMakeRect(0, 0, 120, 200) frameName:nil groupName:nil]);
+    RetainPtr<RenderedImageFromDOMRangeFrameLoadDelegate> frameLoadDelegate(AdoptNS, [RenderedImageFromDOMRangeFrameLoadDelegate new]);
+
+    webView.get().frameLoadDelegate = frameLoadDelegate.get();
+    [webView.get().mainFrame loadHTMLString:@"<div style=\"width: 100px;\">Lorem <span id=\"target\">ipsum dolor</span> sit amet</div>" baseURL:[NSURL URLWithString:@"about:blank"]];
+
+    Util::run(&didFinishLoad);
+
+    DOMDocument *document = webView.get().mainFrameDocument;
+    DOMRange *range = [document createRange];
+    [range selectNode:[document getElementById:@"target"]];
+    NSImage *actualImage = [range renderedImageForcingBlackText:YES];
+
+    [webView.get() setSelectedDOMRange:range affinity:NSSelectionAffinityDownstream];
+    id <WebDocumentView> documentView = webView.get().mainFrame.frameView.documentView;
+    NSImage *expectedImage = [(id <WebDocumentSelection>)documentView selectionImageForcingBlackText:YES];
+    EXPECT_TRUE([actualImage.TIFFRepresentation isEqual:expectedImage.TIFFRepresentation]);
+}
+
+} // namespace TestWebKitAPI