~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/page/DOMSelection.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
 
3
 * Copyright (C) 2012 Google Inc. All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * 1.  Redistributions of source code must retain the above copyright
 
10
 *     notice, this list of conditions and the following disclaimer. 
 
11
 * 2.  Redistributions in binary form must reproduce the above copyright
 
12
 *     notice, this list of conditions and the following disclaimer in the
 
13
 *     documentation and/or other materials provided with the distribution. 
 
14
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 
15
 *     its contributors may be used to endorse or promote products derived
 
16
 *     from this software without specific prior written permission. 
 
17
 *
 
18
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
21
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
27
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
28
 */
 
29
 
 
30
 
 
31
#include "config.h"
 
32
#include "DOMSelection.h"
 
33
 
 
34
#include "Document.h"
 
35
#include "ExceptionCode.h"
 
36
#include "Frame.h"
 
37
#include "FrameSelection.h"
 
38
#include "Node.h"
 
39
#include "Range.h"
 
40
#include "TextIterator.h"
 
41
#include "TreeScope.h"
 
42
#include "htmlediting.h"
 
43
#include <wtf/text/WTFString.h>
 
44
 
 
45
namespace WebCore {
 
46
 
 
47
static Node* selectionShadowAncestor(Frame* frame)
 
48
{
 
49
    Node* node = frame->selection()->selection().base().anchorNode();
 
50
    if (!node)
 
51
        return 0;
 
52
 
 
53
    if (!node->isInShadowTree())
 
54
        return 0;
 
55
 
 
56
    return frame->document()->ancestorInThisScope(node);
 
57
}
 
58
 
 
59
DOMSelection::DOMSelection(const TreeScope* treeScope)
 
60
    : DOMWindowProperty(treeScope->rootNode()->document()->frame())
 
61
    , m_treeScope(treeScope)
 
62
{
 
63
}
 
64
 
 
65
void DOMSelection::clearTreeScope()
 
66
{
 
67
    m_treeScope = 0;
 
68
}
 
69
 
 
70
const VisibleSelection& DOMSelection::visibleSelection() const
 
71
{
 
72
    ASSERT(m_frame);
 
73
    return m_frame->selection()->selection();
 
74
}
 
75
 
 
76
static Position anchorPosition(const VisibleSelection& selection)
 
77
{
 
78
    Position anchor = selection.isBaseFirst() ? selection.start() : selection.end();
 
79
    return anchor.parentAnchoredEquivalent();
 
80
}
 
81
 
 
82
static Position focusPosition(const VisibleSelection& selection)
 
83
{
 
84
    Position focus = selection.isBaseFirst() ? selection.end() : selection.start();
 
85
    return focus.parentAnchoredEquivalent();
 
86
}
 
87
 
 
88
static Position basePosition(const VisibleSelection& selection)
 
89
{
 
90
    return selection.base().parentAnchoredEquivalent();
 
91
}
 
92
 
 
93
static Position extentPosition(const VisibleSelection& selection)
 
94
{
 
95
    return selection.extent().parentAnchoredEquivalent();
 
96
}
 
97
 
 
98
Node* DOMSelection::anchorNode() const
 
99
{
 
100
    if (!m_frame)
 
101
        return 0;
 
102
 
 
103
    return shadowAdjustedNode(anchorPosition(visibleSelection()));
 
104
}
 
105
 
 
106
int DOMSelection::anchorOffset() const
 
107
{
 
108
    if (!m_frame)
 
109
        return 0;
 
110
 
 
111
    return shadowAdjustedOffset(anchorPosition(visibleSelection()));
 
112
}
 
113
 
 
114
Node* DOMSelection::focusNode() const
 
115
{
 
116
    if (!m_frame)
 
117
        return 0;
 
118
 
 
119
    return shadowAdjustedNode(focusPosition(visibleSelection()));
 
120
}
 
121
 
 
122
int DOMSelection::focusOffset() const
 
123
{
 
124
    if (!m_frame)
 
125
        return 0;
 
126
 
 
127
    return shadowAdjustedOffset(focusPosition(visibleSelection()));
 
128
}
 
129
 
 
130
Node* DOMSelection::baseNode() const
 
131
{
 
132
    if (!m_frame)
 
133
        return 0;
 
134
 
 
135
    return shadowAdjustedNode(basePosition(visibleSelection()));
 
136
}
 
137
 
 
138
int DOMSelection::baseOffset() const
 
139
{
 
140
    if (!m_frame)
 
141
        return 0;
 
142
 
 
143
    return shadowAdjustedOffset(basePosition(visibleSelection()));
 
144
}
 
145
 
 
146
Node* DOMSelection::extentNode() const
 
147
{
 
148
    if (!m_frame)
 
149
        return 0;
 
150
 
 
151
    return shadowAdjustedNode(extentPosition(visibleSelection()));
 
152
}
 
153
 
 
154
int DOMSelection::extentOffset() const
 
155
{
 
156
    if (!m_frame)
 
157
        return 0;
 
158
 
 
159
    return shadowAdjustedOffset(extentPosition(visibleSelection()));
 
160
}
 
161
 
 
162
bool DOMSelection::isCollapsed() const
 
163
{
 
164
    if (!m_frame || selectionShadowAncestor(m_frame))
 
165
        return true;
 
166
    return !m_frame->selection()->isRange();
 
167
}
 
168
 
 
169
String DOMSelection::type() const
 
170
{
 
171
    if (!m_frame)
 
172
        return String();
 
173
 
 
174
    FrameSelection* selection = m_frame->selection();
 
175
 
 
176
    // This is a WebKit DOM extension, incompatible with an IE extension
 
177
    // IE has this same attribute, but returns "none", "text" and "control"
 
178
    // http://msdn.microsoft.com/en-us/library/ms534692(VS.85).aspx
 
179
    if (selection->isNone())
 
180
        return "None";
 
181
    if (selection->isCaret())
 
182
        return "Caret";
 
183
    return "Range";
 
184
}
 
185
 
 
186
int DOMSelection::rangeCount() const
 
187
{
 
188
    if (!m_frame)
 
189
        return 0;
 
190
    return m_frame->selection()->isNone() ? 0 : 1;
 
191
}
 
192
 
 
193
void DOMSelection::collapse(Node* node, int offset, ExceptionCode& ec)
 
194
{
 
195
    if (!m_frame)
 
196
        return;
 
197
 
 
198
    if (offset < 0) {
 
199
        ec = INDEX_SIZE_ERR;
 
200
        return;
 
201
    }
 
202
 
 
203
    if (!isValidForPosition(node))
 
204
        return;
 
205
 
 
206
    // FIXME: Eliminate legacy editing positions
 
207
    m_frame->selection()->moveTo(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM));
 
208
}
 
209
 
 
210
void DOMSelection::collapseToEnd(ExceptionCode& ec)
 
211
{
 
212
    if (!m_frame)
 
213
        return;
 
214
 
 
215
    const VisibleSelection& selection = m_frame->selection()->selection();
 
216
 
 
217
    if (selection.isNone()) {
 
218
        ec = INVALID_STATE_ERR;
 
219
        return;
 
220
    }
 
221
 
 
222
    m_frame->selection()->moveTo(VisiblePosition(selection.end(), DOWNSTREAM));
 
223
}
 
224
 
 
225
void DOMSelection::collapseToStart(ExceptionCode& ec)
 
226
{
 
227
    if (!m_frame)
 
228
        return;
 
229
 
 
230
    const VisibleSelection& selection = m_frame->selection()->selection();
 
231
 
 
232
    if (selection.isNone()) {
 
233
        ec = INVALID_STATE_ERR;
 
234
        return;
 
235
    }
 
236
 
 
237
    m_frame->selection()->moveTo(VisiblePosition(selection.start(), DOWNSTREAM));
 
238
}
 
239
 
 
240
void DOMSelection::empty()
 
241
{
 
242
    if (!m_frame)
 
243
        return;
 
244
    m_frame->selection()->clear();
 
245
}
 
246
 
 
247
void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode& ec)
 
248
{
 
249
    if (!m_frame)
 
250
        return;
 
251
 
 
252
    if (baseOffset < 0 || extentOffset < 0) {
 
253
        ec = INDEX_SIZE_ERR;
 
254
        return;
 
255
    }
 
256
 
 
257
    if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode))
 
258
        return;
 
259
 
 
260
    // FIXME: Eliminate legacy editing positions
 
261
    VisiblePosition visibleBase = VisiblePosition(createLegacyEditingPosition(baseNode, baseOffset), DOWNSTREAM);
 
262
    VisiblePosition visibleExtent = VisiblePosition(createLegacyEditingPosition(extentNode, extentOffset), DOWNSTREAM);
 
263
 
 
264
    m_frame->selection()->moveTo(visibleBase, visibleExtent);
 
265
}
 
266
 
 
267
void DOMSelection::setPosition(Node* node, int offset, ExceptionCode& ec)
 
268
{
 
269
    if (!m_frame)
 
270
        return;
 
271
    if (offset < 0) {
 
272
        ec = INDEX_SIZE_ERR;
 
273
        return;
 
274
    }
 
275
 
 
276
    if (!isValidForPosition(node))
 
277
        return;
 
278
 
 
279
    // FIXME: Eliminate legacy editing positions
 
280
    m_frame->selection()->moveTo(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM));
 
281
}
 
282
 
 
283
void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString)
 
284
{
 
285
    if (!m_frame)
 
286
        return;
 
287
 
 
288
    FrameSelection::EAlteration alter;
 
289
    if (equalIgnoringCase(alterString, "extend"))
 
290
        alter = FrameSelection::AlterationExtend;
 
291
    else if (equalIgnoringCase(alterString, "move"))
 
292
        alter = FrameSelection::AlterationMove;
 
293
    else
 
294
        return;
 
295
 
 
296
    SelectionDirection direction;
 
297
    if (equalIgnoringCase(directionString, "forward"))
 
298
        direction = DirectionForward;
 
299
    else if (equalIgnoringCase(directionString, "backward"))
 
300
        direction = DirectionBackward;
 
301
    else if (equalIgnoringCase(directionString, "left"))
 
302
        direction = DirectionLeft;
 
303
    else if (equalIgnoringCase(directionString, "right"))
 
304
        direction = DirectionRight;
 
305
    else
 
306
        return;
 
307
 
 
308
    TextGranularity granularity;
 
309
    if (equalIgnoringCase(granularityString, "character"))
 
310
        granularity = CharacterGranularity;
 
311
    else if (equalIgnoringCase(granularityString, "word"))
 
312
        granularity = WordGranularity;
 
313
    else if (equalIgnoringCase(granularityString, "sentence"))
 
314
        granularity = SentenceGranularity;
 
315
    else if (equalIgnoringCase(granularityString, "line"))
 
316
        granularity = LineGranularity;
 
317
    else if (equalIgnoringCase(granularityString, "paragraph"))
 
318
        granularity = ParagraphGranularity;
 
319
    else if (equalIgnoringCase(granularityString, "lineboundary"))
 
320
        granularity = LineBoundary;
 
321
    else if (equalIgnoringCase(granularityString, "sentenceboundary"))
 
322
        granularity = SentenceBoundary;
 
323
    else if (equalIgnoringCase(granularityString, "paragraphboundary"))
 
324
        granularity = ParagraphBoundary;
 
325
    else if (equalIgnoringCase(granularityString, "documentboundary"))
 
326
        granularity = DocumentBoundary;
 
327
    else
 
328
        return;
 
329
 
 
330
    m_frame->selection()->modify(alter, direction, granularity);
 
331
}
 
332
 
 
333
void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec)
 
334
{
 
335
    if (!m_frame)
 
336
        return;
 
337
 
 
338
    if (!node) {
 
339
        ec = TYPE_MISMATCH_ERR;
 
340
        return;
 
341
    }
 
342
 
 
343
    if (offset < 0 || offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->childNodeCount())) {
 
344
        ec = INDEX_SIZE_ERR;
 
345
        return;
 
346
    }
 
347
 
 
348
    if (!isValidForPosition(node))
 
349
        return;
 
350
 
 
351
    // FIXME: Eliminate legacy editing positions
 
352
    m_frame->selection()->setExtent(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM));
 
353
}
 
354
 
 
355
PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionCode& ec)
 
356
{
 
357
    if (!m_frame)
 
358
        return 0;
 
359
 
 
360
    if (index < 0 || index >= rangeCount()) {
 
361
        ec = INDEX_SIZE_ERR;
 
362
        return 0;
 
363
    }
 
364
 
 
365
    // If you're hitting this, you've added broken multi-range selection support
 
366
    ASSERT(rangeCount() == 1);
 
367
 
 
368
    if (Node* shadowAncestor = selectionShadowAncestor(m_frame)) {
 
369
        ContainerNode* container = shadowAncestor->parentNodeGuaranteedHostFree();
 
370
        int offset = shadowAncestor->nodeIndex();
 
371
        return Range::create(shadowAncestor->document(), container, offset, container, offset);
 
372
    }
 
373
 
 
374
    const VisibleSelection& selection = m_frame->selection()->selection();
 
375
    return selection.firstRange();
 
376
}
 
377
 
 
378
void DOMSelection::removeAllRanges()
 
379
{
 
380
    if (!m_frame)
 
381
        return;
 
382
    m_frame->selection()->clear();
 
383
}
 
384
 
 
385
void DOMSelection::addRange(Range* r)
 
386
{
 
387
    if (!m_frame)
 
388
        return;
 
389
    if (!r)
 
390
        return;
 
391
 
 
392
    FrameSelection* selection = m_frame->selection();
 
393
 
 
394
    if (selection->isNone()) {
 
395
        selection->setSelection(VisibleSelection(r));
 
396
        return;
 
397
    }
 
398
 
 
399
    RefPtr<Range> range = selection->selection().toNormalizedRange();
 
400
    ExceptionCode ec = 0;
 
401
    if (r->compareBoundaryPoints(Range::START_TO_START, range.get(), ec) == -1) {
 
402
        // We don't support discontiguous selection. We don't do anything if r and range don't intersect.
 
403
        if (r->compareBoundaryPoints(Range::START_TO_END, range.get(), ec) > -1) {
 
404
            if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1)
 
405
                // The original range and r intersect.
 
406
                selection->setSelection(VisibleSelection(r->startPosition(), range->endPosition(), DOWNSTREAM));
 
407
            else
 
408
                // r contains the original range.
 
409
                selection->setSelection(VisibleSelection(r));
 
410
        }
 
411
    } else {
 
412
        // We don't support discontiguous selection. We don't do anything if r and range don't intersect.
 
413
        if (r->compareBoundaryPoints(Range::END_TO_START, range.get(), ec) < 1 && !ec) {
 
414
            if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1)
 
415
                // The original range contains r.
 
416
                selection->setSelection(VisibleSelection(range.get()));
 
417
            else
 
418
                // The original range and r intersect.
 
419
                selection->setSelection(VisibleSelection(range->startPosition(), r->endPosition(), DOWNSTREAM));
 
420
        }
 
421
    }
 
422
}
 
423
 
 
424
void DOMSelection::deleteFromDocument()
 
425
{
 
426
    if (!m_frame)
 
427
        return;
 
428
 
 
429
    FrameSelection* selection = m_frame->selection();
 
430
 
 
431
    if (selection->isNone())
 
432
        return;
 
433
 
 
434
    if (isCollapsed())
 
435
        selection->modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity);
 
436
 
 
437
    RefPtr<Range> selectedRange = selection->selection().toNormalizedRange();
 
438
    if (!selectedRange)
 
439
        return;
 
440
 
 
441
    ExceptionCode ec = 0;
 
442
    selectedRange->deleteContents(ec);
 
443
    ASSERT(!ec);
 
444
 
 
445
    setBaseAndExtent(selectedRange->startContainer(ec), selectedRange->startOffset(ec), selectedRange->startContainer(ec), selectedRange->startOffset(ec), ec);
 
446
    ASSERT(!ec);
 
447
}
 
448
 
 
449
bool DOMSelection::containsNode(const Node* n, bool allowPartial) const
 
450
{
 
451
    if (!m_frame)
 
452
        return false;
 
453
 
 
454
    FrameSelection* selection = m_frame->selection();
 
455
 
 
456
    if (!n || m_frame->document() != n->document() || selection->isNone())
 
457
        return false;
 
458
 
 
459
    ContainerNode* parentNode = n->parentNode();
 
460
    unsigned nodeIndex = n->nodeIndex();
 
461
    RefPtr<Range> selectedRange = selection->selection().toNormalizedRange();
 
462
 
 
463
    if (!parentNode)
 
464
        return false;
 
465
 
 
466
    ExceptionCode ec = 0;
 
467
    bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(ec), selectedRange->startOffset(ec), ec) >= 0 && !ec
 
468
        && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(ec), selectedRange->endOffset(ec), ec) <= 0 && !ec;
 
469
    ASSERT(!ec);
 
470
    if (nodeFullySelected)
 
471
        return true;
 
472
 
 
473
    bool nodeFullyUnselected = (Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(ec), selectedRange->endOffset(ec), ec) > 0 && !ec)
 
474
        || (Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(ec), selectedRange->startOffset(ec), ec) < 0 && !ec);
 
475
    ASSERT(!ec);
 
476
    if (nodeFullyUnselected)
 
477
        return false;
 
478
 
 
479
    return allowPartial || n->isTextNode();
 
480
}
 
481
 
 
482
void DOMSelection::selectAllChildren(Node* n, ExceptionCode& ec)
 
483
{
 
484
    if (!n)
 
485
        return;
 
486
 
 
487
    // This doesn't (and shouldn't) select text node characters.
 
488
    setBaseAndExtent(n, 0, n, n->childNodeCount(), ec);
 
489
}
 
490
 
 
491
String DOMSelection::toString()
 
492
{
 
493
    if (!m_frame)
 
494
        return String();
 
495
 
 
496
    return plainText(m_frame->selection()->selection().toNormalizedRange().get());
 
497
}
 
498
 
 
499
Node* DOMSelection::shadowAdjustedNode(const Position& position) const
 
500
{
 
501
    if (position.isNull())
 
502
        return 0;
 
503
 
 
504
    Node* containerNode = position.containerNode();
 
505
    Node* adjustedNode = m_treeScope->ancestorInThisScope(containerNode);
 
506
 
 
507
    if (!adjustedNode)
 
508
        return 0;
 
509
 
 
510
    if (containerNode == adjustedNode)
 
511
        return containerNode;
 
512
 
 
513
    return adjustedNode->parentNodeGuaranteedHostFree();
 
514
}
 
515
 
 
516
int DOMSelection::shadowAdjustedOffset(const Position& position) const
 
517
{
 
518
    if (position.isNull())
 
519
        return 0;
 
520
 
 
521
    Node* containerNode = position.containerNode();
 
522
    Node* adjustedNode = m_treeScope->ancestorInThisScope(containerNode);
 
523
 
 
524
    if (!adjustedNode)
 
525
        return 0;
 
526
 
 
527
    if (containerNode == adjustedNode)
 
528
        return position.computeOffsetInContainerNode();
 
529
 
 
530
    return adjustedNode->nodeIndex();
 
531
}
 
532
 
 
533
bool DOMSelection::isValidForPosition(Node* node) const
 
534
{
 
535
    ASSERT(m_frame);
 
536
    if (!node)
 
537
        return true;
 
538
    return node->document() == m_frame->document();
 
539
}
 
540
 
 
541
} // namespace WebCore