Chromium Code Reviews| Index: third_party/WebKit/Source/WebKit/chromium/src/mac/WebTextHelper.mm |
| diff --git a/third_party/WebKit/Source/WebKit/chromium/src/mac/WebTextHelper.mm b/third_party/WebKit/Source/WebKit/chromium/src/mac/WebTextHelper.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..4dbd8b501bbda2043e93ce8d8dc0666a91c773e1 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/WebKit/chromium/src/mac/WebTextHelper.mm |
| @@ -0,0 +1,257 @@ |
| +/* |
|
jam
2011/01/21 18:51:07
nit: usually we have the interface in the public d
Robert Sesek
2011/01/21 23:40:07
Ok. I won't do that now because it's easier for th
|
| + * Copyright (C) 2011 Google 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: |
| + * |
| + * * Redistributions of source code must retain the above copyright |
| + * notice, this list of conditions and the following disclaimer. |
| + * * 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. |
| + * * Neither the name of Google Inc. nor the names of its |
| + * contributors may be used to endorse or promote products derived from |
| + * this software without specific prior written permission. |
| + * |
| + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT |
| + * OWNER OR 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. |
| + */ |
| + |
| +#include "config.h" |
| +#include "WebFrameImpl.h" |
| +#include "WebTextHelper.h" |
| + |
| +#include "ColorMac.h" |
| +#include "Document.h" |
| +#include "Element.h" |
| +#include "FocusController.h" |
| +#include "Frame.h" |
| +#include "FrameView.h" |
| +#include "HitTestResult.h" |
| +#include "HTMLElement.h" |
| +#include "Node.h" |
| +#include "Range.h" |
| +#include "TextIterator.h" |
| +#include "WebRect.h" |
| +#include "WebString.h" |
| + |
| +#import <Cocoa/Cocoa.h> |
| + |
| +using namespace WebCore; |
| + |
| +namespace WebKit { |
| + |
| +// This is the internal implementation of WebTextHelper that can return WebCore |
| +// types. |
| +class WebTextHelperImpl { |
| +public: |
| + explicit WebTextHelperImpl(WebFrame* frame) : m_frame(frame) {} |
| + |
| + IntRect firstRectForWkRange(Range* range); |
| + |
| + Range* characterRangeAtPoint(const IntPoint point); |
| + |
| + void getLocationAndLengthFromRange(Range* range, NSUInteger& location, NSUInteger& length); |
| + |
| + Element* rangeScope(); |
| + |
| + WebFrameImpl* webframeimpl(); |
| + |
| +protected: |
| + WebFrame* m_frame; |
| +}; |
| + |
| +IntRect WebTextHelperImpl::firstRectForWkRange(Range* range) |
| +{ |
| + return webframeimpl()->frame()->editor()->firstRectForRange(range); |
| +} |
| + |
| +// This function is copied from /WebKit2/WebPage/mac/WebPageMac.mm. |
| +Range* WebTextHelperImpl::characterRangeAtPoint(const IntPoint point) |
| +{ |
| + Frame* frame(webframeimpl()->frame()); |
| + |
| + VisiblePosition position = frame->visiblePositionForPoint(point); |
| + if (position.isNull()) |
| + return NULL; |
| + |
| + VisiblePosition previous = position.previous(); |
| + if (previous.isNotNull()) { |
| + Range* previousCharacterRange = makeRange(previous, position).get(); |
| + IntRect rect = firstRectForWkRange(previousCharacterRange); |
| + if (rect.contains(point)) |
| + return previousCharacterRange; |
| + } |
| + |
| + VisiblePosition next = position.next(); |
| + if (next.isNotNull()) { |
| + Range* nextCharacterRange = makeRange(position, next).get(); |
| + IntRect rect = firstRectForWkRange(nextCharacterRange); |
| + if (rect.contains(point)) |
| + return nextCharacterRange; |
| + } |
| + |
| + return NULL; |
| +} |
| + |
| +// This function is copied from /WebKit2/WebPage/WebPage.cpp. |
| +void WebTextHelperImpl::getLocationAndLengthFromRange(Range* range, NSUInteger& location, NSUInteger& length) |
| +{ |
| + location = notFound; |
| + length = 0; |
| + |
| + if (!range || !range->startContainer()) |
| + return; |
| + |
| + Element* selectionRoot = range->ownerDocument()->frame()->selection()->rootEditableElement(); |
| + Element* scope = selectionRoot ? selectionRoot : range->ownerDocument()->documentElement(); |
| + |
| + // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view |
| + // that is not inside the current editable region. These checks ensure we don't produce |
| + // potentially invalid data when responding to such requests. |
| + if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope)) |
| + return; |
| + if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope)) |
| + return; |
| + |
| + RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset()); |
| + ASSERT(testRange->startContainer() == scope); |
| + location = TextIterator::rangeLength(testRange.get()); |
| + |
| + ExceptionCode ec; |
| + testRange->setEnd(range->endContainer(), range->endOffset(), ec); |
| + ASSERT(testRange->startContainer() == scope); |
| + length = TextIterator::rangeLength(testRange.get()) - location; |
| +} |
| + |
| +Element* WebTextHelperImpl::rangeScope() |
| +{ |
| + Frame* frame(webframeimpl()->frame()); |
| + Element* selectionRoot(frame->selection()->rootEditableElement()); |
| + return selectionRoot ? selectionRoot : frame->document()->documentElement(); |
| +} |
| + |
| +WebFrameImpl* WebTextHelperImpl::webframeimpl() |
| +{ |
| + return static_cast<WebFrameImpl*>(m_frame); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| + |
| +WebTextHelper::WebTextHelper(WebFrame* frame) |
| + : m_private(new WebTextHelperImpl(frame)) |
| +{ |
| +} |
| + |
| +WebTextHelper::~WebTextHelper() |
| +{ |
| + m_private.reset(0); |
| +} |
| + |
| +uint WebTextHelper::characterIndexForPoint(int x, int y) |
| +{ |
| + Frame* frame(m_private->webframeimpl()->frame()); |
| + if (!frame) |
| + return NSNotFound; |
| + |
| + IntPoint scrollPosition(frame->view()->scrollPosition()); |
| + IntPoint point(x + scrollPosition.x(), y + scrollPosition.y()); |
| + HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(point, false); |
| + frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : frame->page()->focusController()->focusedOrMainFrame(); |
| + |
| + Range* range(m_private->characterRangeAtPoint(result.point())); |
| + if (!range) |
| + return NSNotFound; |
| + |
| + NSRange nsRange = { 0 }; |
| + m_private->getLocationAndLengthFromRange(range, nsRange.location, nsRange.length); |
| + |
| + return nsRange.location; |
| +} |
| + |
| +WebKit::WebRect WebTextHelper::firstRectForRange(uint location, uint length) |
| +{ |
| + Frame* frame(m_private->webframeimpl()->frame()); |
| + if (frame->view()->needsLayout()) |
| + frame->view()->layout(); |
| + |
| + RefPtr<Range> range(TextIterator::rangeFromLocationAndLength(m_private->rangeScope(), location, length)); |
|
James Su
2011/01/21 00:02:47
range could be NULL if the given location is out o
Robert Sesek
2011/01/21 23:40:07
Taken care of the NULL dereference. But I don't th
|
| + |
| + WebKit::WebRect rect(m_private->firstRectForWkRange(range.get())); |
| + IntPoint scrollPosition(frame->view()->scrollPosition()); |
| + rect.x += scrollPosition.x(); |
| + rect.y -= scrollPosition.y(); |
| + return rect; |
| +} |
| + |
| +// This function is copied from /WebKit/mac/Misc/WebNSAttributedStringExtras.mm. |
| +WebKit::WebString WebTextHelper::substringInRange(uint location, uint length) |
| +{ |
| + Frame* frame(m_private->webframeimpl()->frame()); |
| + if (frame->view()->needsLayout()) |
| + frame->view()->layout(); |
| + |
| + RefPtr<Range> range(TextIterator::rangeFromLocationAndLength(m_private->rangeScope(), location, length)); |
|
James Su
2011/01/21 00:02:47
range could be NULL.
Robert Sesek
2011/01/21 23:40:07
Done.
|
| + |
| + NSMutableAttributedString* string = [[NSMutableAttributedString alloc] init]; |
| + NSMutableDictionary* attrs = [NSMutableDictionary dictionary]; |
| + |
| + unsigned position = 0; |
| + for (TextIterator it(range.get(), TextIteratorEntersTextControls); |
| + !it.atEnd() && [string length] < length; it.advance()) { |
| + unsigned numCharacters = it.length(); |
| + if (!numCharacters) |
| + continue; |
| + |
| + ExceptionCode exception = 0; |
| + Node* container = it.range()->startContainer(exception); |
| + RenderObject* renderer = container->renderer(); |
| + ASSERT(renderer); |
| + if (!renderer) |
| + continue; |
| + |
| + RenderStyle* style = renderer->style(); |
| + NSFont* font = style->font().primaryFont()->getNSFont(); |
| + // If the platform font can't be loaded, it's likely that the site is |
| + // using a web font. For now, just use the default font instead. |
| + // TODO(rsesek): Change the font activation flags to allow other processes |
| + // to use the font. |
| + if (!font) |
| + font = [NSFont systemFontOfSize:style->font().size()]; |
| + [attrs setObject:font forKey:NSFontAttributeName]; |
| + |
| + if (style->visitedDependentColor(CSSPropertyColor).alpha()) |
| + [attrs setObject:nsColor(style->visitedDependentColor(CSSPropertyColor)) forKey:NSForegroundColorAttributeName]; |
| + else |
| + [attrs removeObjectForKey:NSForegroundColorAttributeName]; |
| + if (style->visitedDependentColor(CSSPropertyBackgroundColor).alpha()) |
| + [attrs setObject:nsColor(style->visitedDependentColor(CSSPropertyBackgroundColor)) forKey:NSBackgroundColorAttributeName]; |
| + else |
| + [attrs removeObjectForKey:NSBackgroundColorAttributeName]; |
| + |
| + NSString* substring = |
| + [[[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(it.characters()) |
| + length:it.length() |
| + freeWhenDone:NO] autorelease]; |
| + [string replaceCharactersInRange:NSMakeRange(position, 0) |
| + withString:substring]; |
| + [string setAttributes:attrs range:NSMakeRange(position, numCharacters)]; |
| + position += numCharacters; |
| + } |
| + NSData* archivedData([NSArchiver archivedDataWithRootObject:string]); |
| + return WebKit::WebString(static_cast<const WebUChar*>([archivedData bytes]), |
| + [archivedData length]); |
| +} |
| + |
| +} // namespace WebKit |