1/*
2 * Copyright (C) 2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "EditingRange.h"
28
29#include <WebCore/Frame.h>
30#include <WebCore/FrameSelection.h>
31#include <WebCore/TextIterator.h>
32#include <WebCore/VisibleUnits.h>
33
34namespace WebKit {
35
36RefPtr<WebCore::Range> EditingRange::toRange(WebCore::Frame& frame, const EditingRange& range, EditingRangeIsRelativeTo editingRangeIsRelativeTo)
37{
38 ASSERT(range.location != notFound);
39
40 // Sanitize the input, because TextIterator::rangeFromLocationAndLength takes signed integers.
41 if (range.location > INT_MAX)
42 return nullptr;
43 int length;
44 if (range.length <= INT_MAX && range.location + range.length <= INT_MAX)
45 length = static_cast<int>(range.length);
46 else
47 length = INT_MAX - range.location;
48
49 if (editingRangeIsRelativeTo == EditingRangeIsRelativeTo::EditableRoot) {
50 // Our critical assumption is that this code path is called by input methods that
51 // concentrate on a given area containing the selection.
52 // We have to do this because of text fields and textareas. The DOM for those is not
53 // directly in the document DOM, so serialization is problematic. Our solution is
54 // to use the root editable element of the selection start as the positional base.
55 // That fits with AppKit's idea of an input context.
56 return WebCore::TextIterator::rangeFromLocationAndLength(frame.selection().rootEditableElementOrDocumentElement(), static_cast<int>(range.location), length);
57 }
58
59 ASSERT(editingRangeIsRelativeTo == EditingRangeIsRelativeTo::Paragraph);
60
61 const WebCore::VisibleSelection& selection = frame.selection().selection();
62 RefPtr<WebCore::Range> selectedRange = selection.toNormalizedRange();
63 if (!selectedRange)
64 return nullptr;
65
66 RefPtr<WebCore::Range> paragraphRange = makeRange(startOfParagraph(selection.visibleStart()), selection.visibleEnd());
67 if (!paragraphRange)
68 return nullptr;
69
70 WebCore::ContainerNode& rootNode = paragraphRange.get()->startContainer().treeScope().rootNode();
71 int paragraphStartIndex = WebCore::TextIterator::rangeLength(WebCore::Range::create(rootNode.document(), &rootNode, 0, &paragraphRange->startContainer(), paragraphRange->startOffset()).ptr());
72 return WebCore::TextIterator::rangeFromLocationAndLength(&rootNode, paragraphStartIndex + static_cast<int>(range.location), length);
73}
74
75EditingRange EditingRange::fromRange(WebCore::Frame& frame, const WebCore::Range* range, EditingRangeIsRelativeTo editingRangeIsRelativeTo)
76{
77 ASSERT(editingRangeIsRelativeTo == EditingRangeIsRelativeTo::EditableRoot);
78
79 size_t location;
80 size_t length;
81 if (!range || !WebCore::TextIterator::getLocationAndLengthFromRange(frame.selection().rootEditableElementOrDocumentElement(), range, location, length))
82 return { };
83
84 EditingRange editingRange(location, length);
85 if (!editingRange.isValid())
86 return { };
87
88 return editingRange;
89}
90
91} // namespace WebKit
92
93namespace IPC {
94
95void ArgumentCoder<WebKit::EditingRange>::encode(Encoder& encoder, const WebKit::EditingRange& editingRange)
96{
97 encoder << editingRange.location;
98 encoder << editingRange.length;
99}
100
101Optional<WebKit::EditingRange> ArgumentCoder<WebKit::EditingRange>::decode(Decoder& decoder)
102{
103 WebKit::EditingRange editingRange;
104
105 if (!decoder.decode(editingRange.location))
106 return WTF::nullopt;
107 if (!decoder.decode(editingRange.length))
108 return WTF::nullopt;
109
110 return editingRange;
111}
112
113} // namespace IPC
114