| OLD | NEW |
| (Empty) |
| 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 "core/page/DOMSelection.h" | |
| 33 | |
| 34 #include "bindings/core/v8/ExceptionMessages.h" | |
| 35 #include "bindings/core/v8/ExceptionState.h" | |
| 36 #include "bindings/core/v8/ExceptionStatePlaceholder.h" | |
| 37 #include "core/dom/Document.h" | |
| 38 #include "core/dom/ExceptionCode.h" | |
| 39 #include "core/dom/Node.h" | |
| 40 #include "core/dom/Range.h" | |
| 41 #include "core/dom/TreeScope.h" | |
| 42 #include "core/editing/FrameSelection.h" | |
| 43 #include "core/editing/TextIterator.h" | |
| 44 #include "core/editing/htmlediting.h" | |
| 45 #include "core/frame/LocalFrame.h" | |
| 46 #include "wtf/text/WTFString.h" | |
| 47 | |
| 48 namespace blink { | |
| 49 | |
| 50 static Node* selectionShadowAncestor(LocalFrame* frame) | |
| 51 { | |
| 52 Node* node = frame->selection().selection().base().anchorNode(); | |
| 53 if (!node) | |
| 54 return 0; | |
| 55 | |
| 56 if (!node->isInShadowTree()) | |
| 57 return 0; | |
| 58 | |
| 59 return frame->document()->ancestorInThisScope(node); | |
| 60 } | |
| 61 | |
| 62 DOMSelection::DOMSelection(const TreeScope* treeScope) | |
| 63 : DOMWindowProperty(treeScope->rootNode().document().frame()) | |
| 64 , m_treeScope(treeScope) | |
| 65 { | |
| 66 ScriptWrappable::init(this); | |
| 67 } | |
| 68 | |
| 69 void DOMSelection::clearTreeScope() | |
| 70 { | |
| 71 m_treeScope = nullptr; | |
| 72 } | |
| 73 | |
| 74 const VisibleSelection& DOMSelection::visibleSelection() const | |
| 75 { | |
| 76 ASSERT(m_frame); | |
| 77 return m_frame->selection().selection(); | |
| 78 } | |
| 79 | |
| 80 static Position anchorPosition(const VisibleSelection& selection) | |
| 81 { | |
| 82 Position anchor = selection.isBaseFirst() ? selection.start() : selection.en
d(); | |
| 83 return anchor.parentAnchoredEquivalent(); | |
| 84 } | |
| 85 | |
| 86 static Position focusPosition(const VisibleSelection& selection) | |
| 87 { | |
| 88 Position focus = selection.isBaseFirst() ? selection.end() : selection.start
(); | |
| 89 return focus.parentAnchoredEquivalent(); | |
| 90 } | |
| 91 | |
| 92 static Position basePosition(const VisibleSelection& selection) | |
| 93 { | |
| 94 return selection.base().parentAnchoredEquivalent(); | |
| 95 } | |
| 96 | |
| 97 static Position extentPosition(const VisibleSelection& selection) | |
| 98 { | |
| 99 return selection.extent().parentAnchoredEquivalent(); | |
| 100 } | |
| 101 | |
| 102 Node* DOMSelection::anchorNode() const | |
| 103 { | |
| 104 if (!m_frame) | |
| 105 return 0; | |
| 106 | |
| 107 return shadowAdjustedNode(anchorPosition(visibleSelection())); | |
| 108 } | |
| 109 | |
| 110 int DOMSelection::anchorOffset() const | |
| 111 { | |
| 112 if (!m_frame) | |
| 113 return 0; | |
| 114 | |
| 115 return shadowAdjustedOffset(anchorPosition(visibleSelection())); | |
| 116 } | |
| 117 | |
| 118 Node* DOMSelection::focusNode() const | |
| 119 { | |
| 120 if (!m_frame) | |
| 121 return 0; | |
| 122 | |
| 123 return shadowAdjustedNode(focusPosition(visibleSelection())); | |
| 124 } | |
| 125 | |
| 126 int DOMSelection::focusOffset() const | |
| 127 { | |
| 128 if (!m_frame) | |
| 129 return 0; | |
| 130 | |
| 131 return shadowAdjustedOffset(focusPosition(visibleSelection())); | |
| 132 } | |
| 133 | |
| 134 Node* DOMSelection::baseNode() const | |
| 135 { | |
| 136 if (!m_frame) | |
| 137 return 0; | |
| 138 | |
| 139 return shadowAdjustedNode(basePosition(visibleSelection())); | |
| 140 } | |
| 141 | |
| 142 int DOMSelection::baseOffset() const | |
| 143 { | |
| 144 if (!m_frame) | |
| 145 return 0; | |
| 146 | |
| 147 return shadowAdjustedOffset(basePosition(visibleSelection())); | |
| 148 } | |
| 149 | |
| 150 Node* DOMSelection::extentNode() const | |
| 151 { | |
| 152 if (!m_frame) | |
| 153 return 0; | |
| 154 | |
| 155 return shadowAdjustedNode(extentPosition(visibleSelection())); | |
| 156 } | |
| 157 | |
| 158 int DOMSelection::extentOffset() const | |
| 159 { | |
| 160 if (!m_frame) | |
| 161 return 0; | |
| 162 | |
| 163 return shadowAdjustedOffset(extentPosition(visibleSelection())); | |
| 164 } | |
| 165 | |
| 166 bool DOMSelection::isCollapsed() const | |
| 167 { | |
| 168 if (!m_frame || selectionShadowAncestor(m_frame)) | |
| 169 return true; | |
| 170 return !m_frame->selection().isRange(); | |
| 171 } | |
| 172 | |
| 173 String DOMSelection::type() const | |
| 174 { | |
| 175 if (!m_frame) | |
| 176 return String(); | |
| 177 | |
| 178 FrameSelection& selection = m_frame->selection(); | |
| 179 | |
| 180 // This is a WebKit DOM extension, incompatible with an IE extension | |
| 181 // IE has this same attribute, but returns "none", "text" and "control" | |
| 182 // http://msdn.microsoft.com/en-us/library/ms534692(VS.85).aspx | |
| 183 if (selection.isNone()) | |
| 184 return "None"; | |
| 185 if (selection.isCaret()) | |
| 186 return "Caret"; | |
| 187 return "Range"; | |
| 188 } | |
| 189 | |
| 190 int DOMSelection::rangeCount() const | |
| 191 { | |
| 192 if (!m_frame) | |
| 193 return 0; | |
| 194 return m_frame->selection().isNone() ? 0 : 1; | |
| 195 } | |
| 196 | |
| 197 void DOMSelection::collapse(Node* node, int offset, ExceptionState& exceptionSta
te) | |
| 198 { | |
| 199 ASSERT(node); | |
| 200 if (!m_frame) | |
| 201 return; | |
| 202 | |
| 203 if (offset < 0) { | |
| 204 exceptionState.throwDOMException(IndexSizeError, String::number(offset)
+ " is not a valid offset."); | |
| 205 return; | |
| 206 } | |
| 207 | |
| 208 if (!isValidForPosition(node)) | |
| 209 return; | |
| 210 RefPtrWillBeRawPtr<Range> range = Range::create(node->document()); | |
| 211 range->setStart(node, offset, exceptionState); | |
| 212 if (exceptionState.hadException()) | |
| 213 return; | |
| 214 range->setEnd(node, offset, exceptionState); | |
| 215 if (exceptionState.hadException()) | |
| 216 return; | |
| 217 m_frame->selection().setSelectedRange(range.get(), DOWNSTREAM, m_frame->sele
ction().isDirectional() ? FrameSelection::Directional : FrameSelection::NonDirec
tional); | |
| 218 } | |
| 219 | |
| 220 void DOMSelection::collapse(Node* node, ExceptionState& exceptionState) | |
| 221 { | |
| 222 collapse(node, 0, exceptionState); | |
| 223 } | |
| 224 | |
| 225 void DOMSelection::collapseToEnd(ExceptionState& exceptionState) | |
| 226 { | |
| 227 if (!m_frame) | |
| 228 return; | |
| 229 | |
| 230 const VisibleSelection& selection = m_frame->selection().selection(); | |
| 231 | |
| 232 if (selection.isNone()) { | |
| 233 exceptionState.throwDOMException(InvalidStateError, "there is no selecti
on."); | |
| 234 return; | |
| 235 } | |
| 236 | |
| 237 m_frame->selection().moveTo(VisiblePosition(selection.end(), DOWNSTREAM)); | |
| 238 } | |
| 239 | |
| 240 void DOMSelection::collapseToStart(ExceptionState& exceptionState) | |
| 241 { | |
| 242 if (!m_frame) | |
| 243 return; | |
| 244 | |
| 245 const VisibleSelection& selection = m_frame->selection().selection(); | |
| 246 | |
| 247 if (selection.isNone()) { | |
| 248 exceptionState.throwDOMException(InvalidStateError, "there is no selecti
on."); | |
| 249 return; | |
| 250 } | |
| 251 | |
| 252 m_frame->selection().moveTo(VisiblePosition(selection.start(), DOWNSTREAM)); | |
| 253 } | |
| 254 | |
| 255 void DOMSelection::empty() | |
| 256 { | |
| 257 if (!m_frame) | |
| 258 return; | |
| 259 m_frame->selection().clear(); | |
| 260 } | |
| 261 | |
| 262 void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extent
Node, int extentOffset, ExceptionState& exceptionState) | |
| 263 { | |
| 264 if (!m_frame) | |
| 265 return; | |
| 266 | |
| 267 if (baseOffset < 0) { | |
| 268 exceptionState.throwDOMException(IndexSizeError, String::number(baseOffs
et) + " is not a valid base offset."); | |
| 269 return; | |
| 270 } | |
| 271 | |
| 272 if (extentOffset < 0) { | |
| 273 exceptionState.throwDOMException(IndexSizeError, String::number(extentOf
fset) + " is not a valid extent offset."); | |
| 274 return; | |
| 275 } | |
| 276 | |
| 277 if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode)) | |
| 278 return; | |
| 279 | |
| 280 // FIXME: Eliminate legacy editing positions | |
| 281 VisiblePosition visibleBase = VisiblePosition(createLegacyEditingPosition(ba
seNode, baseOffset), DOWNSTREAM); | |
| 282 VisiblePosition visibleExtent = VisiblePosition(createLegacyEditingPosition(
extentNode, extentOffset), DOWNSTREAM); | |
| 283 | |
| 284 m_frame->selection().moveTo(visibleBase, visibleExtent); | |
| 285 } | |
| 286 | |
| 287 void DOMSelection::modify(const String& alterString, const String& directionStri
ng, const String& granularityString) | |
| 288 { | |
| 289 if (!m_frame) | |
| 290 return; | |
| 291 | |
| 292 FrameSelection::EAlteration alter; | |
| 293 if (equalIgnoringCase(alterString, "extend")) | |
| 294 alter = FrameSelection::AlterationExtend; | |
| 295 else if (equalIgnoringCase(alterString, "move")) | |
| 296 alter = FrameSelection::AlterationMove; | |
| 297 else | |
| 298 return; | |
| 299 | |
| 300 SelectionDirection direction; | |
| 301 if (equalIgnoringCase(directionString, "forward")) | |
| 302 direction = DirectionForward; | |
| 303 else if (equalIgnoringCase(directionString, "backward")) | |
| 304 direction = DirectionBackward; | |
| 305 else if (equalIgnoringCase(directionString, "left")) | |
| 306 direction = DirectionLeft; | |
| 307 else if (equalIgnoringCase(directionString, "right")) | |
| 308 direction = DirectionRight; | |
| 309 else | |
| 310 return; | |
| 311 | |
| 312 TextGranularity granularity; | |
| 313 if (equalIgnoringCase(granularityString, "character")) | |
| 314 granularity = CharacterGranularity; | |
| 315 else if (equalIgnoringCase(granularityString, "word")) | |
| 316 granularity = WordGranularity; | |
| 317 else if (equalIgnoringCase(granularityString, "sentence")) | |
| 318 granularity = SentenceGranularity; | |
| 319 else if (equalIgnoringCase(granularityString, "line")) | |
| 320 granularity = LineGranularity; | |
| 321 else if (equalIgnoringCase(granularityString, "paragraph")) | |
| 322 granularity = ParagraphGranularity; | |
| 323 else if (equalIgnoringCase(granularityString, "lineboundary")) | |
| 324 granularity = LineBoundary; | |
| 325 else if (equalIgnoringCase(granularityString, "sentenceboundary")) | |
| 326 granularity = SentenceBoundary; | |
| 327 else if (equalIgnoringCase(granularityString, "paragraphboundary")) | |
| 328 granularity = ParagraphBoundary; | |
| 329 else if (equalIgnoringCase(granularityString, "documentboundary")) | |
| 330 granularity = DocumentBoundary; | |
| 331 else | |
| 332 return; | |
| 333 | |
| 334 m_frame->selection().modify(alter, direction, granularity); | |
| 335 } | |
| 336 | |
| 337 void DOMSelection::extend(Node* node, int offset, ExceptionState& exceptionState
) | |
| 338 { | |
| 339 ASSERT(node); | |
| 340 | |
| 341 if (!m_frame) | |
| 342 return; | |
| 343 | |
| 344 if (offset < 0) { | |
| 345 exceptionState.throwDOMException(IndexSizeError, String::number(offset)
+ " is not a valid offset."); | |
| 346 return; | |
| 347 } | |
| 348 if (offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node-
>countChildren())) { | |
| 349 exceptionState.throwDOMException(IndexSizeError, String::number(offset)
+ " is larger than the given node's length."); | |
| 350 return; | |
| 351 } | |
| 352 | |
| 353 if (!isValidForPosition(node)) | |
| 354 return; | |
| 355 | |
| 356 // FIXME: Eliminate legacy editing positions | |
| 357 m_frame->selection().setExtent(VisiblePosition(createLegacyEditingPosition(n
ode, offset), DOWNSTREAM)); | |
| 358 } | |
| 359 | |
| 360 void DOMSelection::extend(Node* node, ExceptionState& exceptionState) | |
| 361 { | |
| 362 // This default value implementation differs from the spec, which says |offs
et| is not optional. | |
| 363 // FIXME: Specify this default value in Selection.idl. | |
| 364 extend(node, 0, exceptionState); | |
| 365 } | |
| 366 | |
| 367 PassRefPtrWillBeRawPtr<Range> DOMSelection::getRangeAt(int index, ExceptionState
& exceptionState) | |
| 368 { | |
| 369 if (!m_frame) | |
| 370 return nullptr; | |
| 371 | |
| 372 if (index < 0 || index >= rangeCount()) { | |
| 373 exceptionState.throwDOMException(IndexSizeError, String::number(index) +
" is not a valid index."); | |
| 374 return nullptr; | |
| 375 } | |
| 376 | |
| 377 // If you're hitting this, you've added broken multi-range selection support | |
| 378 ASSERT(rangeCount() == 1); | |
| 379 | |
| 380 if (Node* shadowAncestor = selectionShadowAncestor(m_frame)) { | |
| 381 ASSERT(!shadowAncestor->isShadowRoot()); | |
| 382 ContainerNode* container = shadowAncestor->parentOrShadowHostNode(); | |
| 383 int offset = shadowAncestor->nodeIndex(); | |
| 384 return Range::create(shadowAncestor->document(), container, offset, cont
ainer, offset); | |
| 385 } | |
| 386 | |
| 387 return m_frame->selection().firstRange(); | |
| 388 } | |
| 389 | |
| 390 void DOMSelection::removeAllRanges() | |
| 391 { | |
| 392 if (!m_frame) | |
| 393 return; | |
| 394 m_frame->selection().clear(); | |
| 395 } | |
| 396 | |
| 397 void DOMSelection::addRange(Range* newRange) | |
| 398 { | |
| 399 if (!m_frame) | |
| 400 return; | |
| 401 | |
| 402 // FIXME: Should we throw DOMException for error cases below? | |
| 403 if (!newRange) { | |
| 404 addConsoleError("The given range is null."); | |
| 405 return; | |
| 406 } | |
| 407 | |
| 408 if (!newRange->startContainer()) { | |
| 409 addConsoleError("The given range has no container. Perhaps 'detach()' ha
s been invoked on it?"); | |
| 410 return; | |
| 411 } | |
| 412 | |
| 413 FrameSelection& selection = m_frame->selection(); | |
| 414 | |
| 415 if (selection.isNone()) { | |
| 416 selection.setSelectedRange(newRange, VP_DEFAULT_AFFINITY); | |
| 417 return; | |
| 418 } | |
| 419 | |
| 420 RefPtrWillBeRawPtr<Range> originalRange = selection.firstRange(); | |
| 421 | |
| 422 if (originalRange->startContainer()->document() != newRange->startContainer(
)->document()) { | |
| 423 addConsoleError("The given range does not belong to the current selectio
n's document."); | |
| 424 return; | |
| 425 } | |
| 426 if (originalRange->startContainer()->treeScope() != newRange->startContainer
()->treeScope()) { | |
| 427 addConsoleError("The given range and the current selection belong to two
different document fragments."); | |
| 428 return; | |
| 429 } | |
| 430 | |
| 431 if (originalRange->compareBoundaryPoints(Range::START_TO_END, newRange, ASSE
RT_NO_EXCEPTION) < 0 | |
| 432 || newRange->compareBoundaryPoints(Range::START_TO_END, originalRange.ge
t(), ASSERT_NO_EXCEPTION) < 0) { | |
| 433 addConsoleError("Discontiguous selection is not supported."); | |
| 434 return; | |
| 435 } | |
| 436 | |
| 437 // FIXME: "Merge the ranges if they intersect" is Blink-specific behavior; o
ther browsers supporting discontiguous | |
| 438 // selection (obviously) keep each Range added and return it in getRangeAt()
. But it's unclear if we can really | |
| 439 // do the same, since we don't support discontiguous selection. Further disc
ussions at | |
| 440 // <https://code.google.com/p/chromium/issues/detail?id=353069>. | |
| 441 | |
| 442 Range* start = originalRange->compareBoundaryPoints(Range::START_TO_START, n
ewRange, ASSERT_NO_EXCEPTION) < 0 ? originalRange.get() : newRange; | |
| 443 Range* end = originalRange->compareBoundaryPoints(Range::END_TO_END, newRang
e, ASSERT_NO_EXCEPTION) < 0 ? newRange : originalRange.get(); | |
| 444 RefPtrWillBeRawPtr<Range> merged = Range::create(originalRange->startContain
er()->document(), start->startContainer(), start->startOffset(), end->endContain
er(), end->endOffset()); | |
| 445 EAffinity affinity = selection.selection().affinity(); | |
| 446 selection.setSelectedRange(merged.get(), affinity); | |
| 447 } | |
| 448 | |
| 449 void DOMSelection::deleteFromDocument() | |
| 450 { | |
| 451 if (!m_frame) | |
| 452 return; | |
| 453 | |
| 454 FrameSelection& selection = m_frame->selection(); | |
| 455 | |
| 456 if (selection.isNone()) | |
| 457 return; | |
| 458 | |
| 459 RefPtrWillBeRawPtr<Range> selectedRange = selection.selection().toNormalized
Range(); | |
| 460 if (!selectedRange) | |
| 461 return; | |
| 462 | |
| 463 selectedRange->deleteContents(ASSERT_NO_EXCEPTION); | |
| 464 | |
| 465 setBaseAndExtent(selectedRange->startContainer(), selectedRange->startOffset
(), selectedRange->startContainer(), selectedRange->startOffset(), ASSERT_NO_EXC
EPTION); | |
| 466 } | |
| 467 | |
| 468 bool DOMSelection::containsNode(const Node* n, bool allowPartial) const | |
| 469 { | |
| 470 if (!m_frame) | |
| 471 return false; | |
| 472 | |
| 473 FrameSelection& selection = m_frame->selection(); | |
| 474 | |
| 475 if (!n || m_frame->document() != n->document() || selection.isNone()) | |
| 476 return false; | |
| 477 | |
| 478 unsigned nodeIndex = n->nodeIndex(); | |
| 479 RefPtrWillBeRawPtr<Range> selectedRange = selection.selection().toNormalized
Range(); | |
| 480 | |
| 481 ContainerNode* parentNode = n->parentNode(); | |
| 482 if (!parentNode) | |
| 483 return false; | |
| 484 | |
| 485 TrackExceptionState exceptionState; | |
| 486 bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex,
selectedRange->startContainer(), selectedRange->startOffset(), exceptionState)
>= 0 && !exceptionState.hadException() | |
| 487 && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange
->endContainer(), selectedRange->endOffset(), exceptionState) <= 0 && !exception
State.hadException(); | |
| 488 if (exceptionState.hadException()) | |
| 489 return false; | |
| 490 if (nodeFullySelected) | |
| 491 return true; | |
| 492 | |
| 493 bool nodeFullyUnselected = (Range::compareBoundaryPoints(parentNode, nodeInd
ex, selectedRange->endContainer(), selectedRange->endOffset(), exceptionState) >
0 && !exceptionState.hadException()) | |
| 494 || (Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRang
e->startContainer(), selectedRange->startOffset(), exceptionState) < 0 && !excep
tionState.hadException()); | |
| 495 ASSERT(!exceptionState.hadException()); | |
| 496 if (nodeFullyUnselected) | |
| 497 return false; | |
| 498 | |
| 499 return allowPartial || n->isTextNode(); | |
| 500 } | |
| 501 | |
| 502 void DOMSelection::selectAllChildren(Node* n, ExceptionState& exceptionState) | |
| 503 { | |
| 504 if (!n) | |
| 505 return; | |
| 506 | |
| 507 // This doesn't (and shouldn't) select text node characters. | |
| 508 setBaseAndExtent(n, 0, n, n->countChildren(), exceptionState); | |
| 509 } | |
| 510 | |
| 511 String DOMSelection::toString() | |
| 512 { | |
| 513 if (!m_frame) | |
| 514 return String(); | |
| 515 | |
| 516 return plainText(m_frame->selection().selection().toNormalizedRange().get())
; | |
| 517 } | |
| 518 | |
| 519 Node* DOMSelection::shadowAdjustedNode(const Position& position) const | |
| 520 { | |
| 521 if (position.isNull()) | |
| 522 return 0; | |
| 523 | |
| 524 Node* containerNode = position.containerNode(); | |
| 525 Node* adjustedNode = m_treeScope->ancestorInThisScope(containerNode); | |
| 526 | |
| 527 if (!adjustedNode) | |
| 528 return 0; | |
| 529 | |
| 530 if (containerNode == adjustedNode) | |
| 531 return containerNode; | |
| 532 | |
| 533 ASSERT(!adjustedNode->isShadowRoot()); | |
| 534 return adjustedNode->parentOrShadowHostNode(); | |
| 535 } | |
| 536 | |
| 537 int DOMSelection::shadowAdjustedOffset(const Position& position) const | |
| 538 { | |
| 539 if (position.isNull()) | |
| 540 return 0; | |
| 541 | |
| 542 Node* containerNode = position.containerNode(); | |
| 543 Node* adjustedNode = m_treeScope->ancestorInThisScope(containerNode); | |
| 544 | |
| 545 if (!adjustedNode) | |
| 546 return 0; | |
| 547 | |
| 548 if (containerNode == adjustedNode) | |
| 549 return position.computeOffsetInContainerNode(); | |
| 550 | |
| 551 return adjustedNode->nodeIndex(); | |
| 552 } | |
| 553 | |
| 554 bool DOMSelection::isValidForPosition(Node* node) const | |
| 555 { | |
| 556 ASSERT(m_frame); | |
| 557 if (!node) | |
| 558 return true; | |
| 559 return node->document() == m_frame->document(); | |
| 560 } | |
| 561 | |
| 562 void DOMSelection::addConsoleError(const String& message) | |
| 563 { | |
| 564 if (m_treeScope) | |
| 565 m_treeScope->document().addConsoleMessage(JSMessageSource, ErrorMessageL
evel, message); | |
| 566 } | |
| 567 | |
| 568 } // namespace blink | |
| OLD | NEW |