| OLD | NEW | 
|---|
| 1 /* | 1 /* | 
| 2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. | 2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. | 
| 3  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) | 3  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) | 
| 4  * | 4  * | 
| 5  * Redistribution and use in source and binary forms, with or without | 5  * Redistribution and use in source and binary forms, with or without | 
| 6  * modification, are permitted provided that the following conditions | 6  * modification, are permitted provided that the following conditions | 
| 7  * are met: | 7  * are met: | 
| 8  * 1. Redistributions of source code must retain the above copyright | 8  * 1. Redistributions of source code must retain the above copyright | 
| 9  *    notice, this list of conditions and the following disclaimer. | 9  *    notice, this list of conditions and the following disclaimer. | 
| 10  * 2. Redistributions in binary form must reproduce the above copyright | 10  * 2. Redistributions in binary form must reproduce the above copyright | 
| (...skipping 853 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 864         startRoot->dispatchEvent(Event::create(eventNames().webkitEditableConten
      tChangedEvent, false, false), ec); | 864         startRoot->dispatchEvent(Event::create(eventNames().webkitEditableConten
      tChangedEvent, false, false), ec); | 
| 865     if (endRoot && endRoot != startRoot) | 865     if (endRoot && endRoot != startRoot) | 
| 866         endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentC
      hangedEvent, false, false), ec); | 866         endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentC
      hangedEvent, false, false), ec); | 
| 867 } | 867 } | 
| 868 | 868 | 
| 869 void Editor::appliedEditing(PassRefPtr<EditCommand> cmd) | 869 void Editor::appliedEditing(PassRefPtr<EditCommand> cmd) | 
| 870 { | 870 { | 
| 871     dispatchEditableContentChangedEvents(*cmd); | 871     dispatchEditableContentChangedEvents(*cmd); | 
| 872 | 872 | 
| 873     VisibleSelection newSelection(cmd->endingSelection()); | 873     VisibleSelection newSelection(cmd->endingSelection()); | 
| 874     // If there is no selection change, don't bother sending shouldChangeSelecti
      on, but still call setSelection, |  | 
| 875     // because there is work that it must do in this situation. |  | 
| 876     // The old selection can be invalid here and calling shouldChangeSelection c
      an produce some strange calls. |  | 
| 877     // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ra
      nges for selections that are no longer valid |  | 
| 878     // Don't clear the typing style with this selection change.  We do those thi
      ngs elsewhere if necessary. | 874     // Don't clear the typing style with this selection change.  We do those thi
      ngs elsewhere if necessary. | 
| 879     if (newSelection == m_frame->selection()->selection() || m_frame->shouldChan
      geSelection(newSelection)) | 875     changeSelectionAfterCommand(newSelection, false, false, cmd.get()); | 
| 880         m_frame->selection()->setSelection(newSelection, false, false); |  | 
| 881 | 876 | 
| 882     if (!cmd->preservesTypingStyle()) | 877     if (!cmd->preservesTypingStyle()) | 
| 883         m_frame->setTypingStyle(0); | 878         m_frame->setTypingStyle(0); | 
| 884 | 879 | 
| 885     // Command will be equal to last edit command only in the case of typing | 880     // Command will be equal to last edit command only in the case of typing | 
| 886     if (m_lastEditCommand.get() == cmd) | 881     if (m_lastEditCommand.get() == cmd) | 
| 887         ASSERT(cmd->isTypingCommand()); | 882         ASSERT(cmd->isTypingCommand()); | 
| 888     else { | 883     else { | 
| 889         // Only register a new undo command if the command passed in is | 884         // Only register a new undo command if the command passed in is | 
| 890         // different from the last command | 885         // different from the last command | 
| 891         m_lastEditCommand = cmd; | 886         m_lastEditCommand = cmd; | 
| 892         if (client()) | 887         if (client()) | 
| 893             client()->registerCommandForUndo(m_lastEditCommand); | 888             client()->registerCommandForUndo(m_lastEditCommand); | 
| 894     } | 889     } | 
| 895     respondToChangedContents(newSelection); | 890     respondToChangedContents(newSelection); | 
| 896 } | 891 } | 
| 897 | 892 | 
| 898 void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd) | 893 void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd) | 
| 899 { | 894 { | 
| 900     dispatchEditableContentChangedEvents(*cmd); | 895     dispatchEditableContentChangedEvents(*cmd); | 
| 901 | 896 | 
| 902     VisibleSelection newSelection(cmd->startingSelection()); | 897     VisibleSelection newSelection(cmd->startingSelection()); | 
| 903     // If there is no selection change, don't bother sending shouldChangeSelecti
      on, but still call setSelection, | 898     changeSelectionAfterCommand(newSelection, true, true, cmd.get()); | 
| 904     // because there is work that it must do in this situation. |  | 
| 905     // The old selection can be invalid here and calling shouldChangeSelection c
      an produce some strange calls. |  | 
| 906     // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ra
      nges for selections that are no longer valid |  | 
| 907     if (newSelection == m_frame->selection()->selection() || m_frame->shouldChan
      geSelection(newSelection)) |  | 
| 908         m_frame->selection()->setSelection(newSelection, true); |  | 
| 909 | 899 | 
| 910     m_lastEditCommand = 0; | 900     m_lastEditCommand = 0; | 
| 911     if (client()) | 901     if (client()) | 
| 912         client()->registerCommandForRedo(cmd); | 902         client()->registerCommandForRedo(cmd); | 
| 913     respondToChangedContents(newSelection); | 903     respondToChangedContents(newSelection); | 
| 914 } | 904 } | 
| 915 | 905 | 
| 916 void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd) | 906 void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd) | 
| 917 { | 907 { | 
| 918     dispatchEditableContentChangedEvents(*cmd); | 908     dispatchEditableContentChangedEvents(*cmd); | 
| 919 | 909 | 
| 920     VisibleSelection newSelection(cmd->endingSelection()); | 910     VisibleSelection newSelection(cmd->endingSelection()); | 
| 921     // If there is no selection change, don't bother sending shouldChangeSelecti
      on, but still call setSelection, | 911     changeSelectionAfterCommand(newSelection, true, true, cmd.get()); | 
| 922     // because there is work that it must do in this situation. |  | 
| 923     // The old selection can be invalid here and calling shouldChangeSelection c
      an produce some strange calls. |  | 
| 924     // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ra
      nges for selections that are no longer valid |  | 
| 925     if (newSelection == m_frame->selection()->selection() || m_frame->shouldChan
      geSelection(newSelection)) |  | 
| 926         m_frame->selection()->setSelection(newSelection, true); |  | 
| 927 | 912 | 
| 928     m_lastEditCommand = 0; | 913     m_lastEditCommand = 0; | 
| 929     if (client()) | 914     if (client()) | 
| 930         client()->registerCommandForUndo(cmd); | 915         client()->registerCommandForUndo(cmd); | 
| 931     respondToChangedContents(newSelection); | 916     respondToChangedContents(newSelection); | 
| 932 } | 917 } | 
| 933 | 918 | 
| 934 Editor::Editor(Frame* frame) | 919 Editor::Editor(Frame* frame) | 
| 935     : m_frame(frame) | 920     : m_frame(frame) | 
| 936     , m_deleteButtonController(new DeleteButtonController(frame)) | 921     , m_deleteButtonController(new DeleteButtonController(frame)) | 
| (...skipping 607 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1544         // These results were all between the start of the paragraph and the sta
      rt of the search range; look | 1529         // These results were all between the start of the paragraph and the sta
      rt of the search range; look | 
| 1545         // beyond this phrase. | 1530         // beyond this phrase. | 
| 1546         startOffset = badGrammarPhraseLocation + badGrammarPhraseLength; | 1531         startOffset = badGrammarPhraseLocation + badGrammarPhraseLength; | 
| 1547     } | 1532     } | 
| 1548 | 1533 | 
| 1549     return firstBadGrammarPhrase; | 1534     return firstBadGrammarPhrase; | 
| 1550 } | 1535 } | 
| 1551 | 1536 | 
| 1552 #endif /* not BUILDING_ON_TIGER */ | 1537 #endif /* not BUILDING_ON_TIGER */ | 
| 1553 | 1538 | 
|  | 1539 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD
      ) | 
|  | 1540 | 
|  | 1541 static String findFirstMisspellingOrBadGrammarInRange(EditorClient* client, Rang
      e* searchRange, bool checkGrammar, bool& outIsSpelling, int& outFirstFoundOffset
      , GrammarDetail& outGrammarDetail) | 
|  | 1542 { | 
|  | 1543     ASSERT_ARG(client, client); | 
|  | 1544     ASSERT_ARG(searchRange, searchRange); | 
|  | 1545 | 
|  | 1546     String firstFoundItem; | 
|  | 1547     String misspelledWord; | 
|  | 1548     String badGrammarPhrase; | 
|  | 1549     ExceptionCode ec = 0; | 
|  | 1550 | 
|  | 1551     // Initialize out parameters; these will be updated if we find something to 
      return. | 
|  | 1552     outIsSpelling = true; | 
|  | 1553     outFirstFoundOffset = 0; | 
|  | 1554     outGrammarDetail.location = -1; | 
|  | 1555     outGrammarDetail.length = 0; | 
|  | 1556     outGrammarDetail.guesses.clear(); | 
|  | 1557     outGrammarDetail.userDescription = ""; | 
|  | 1558 | 
|  | 1559     // Expand the search range to encompass entire paragraphs, since text checki
      ng needs that much context. | 
|  | 1560     // Determine the character offset from the start of the paragraph to the sta
      rt of the original search range, | 
|  | 1561     // since we will want to ignore results in this area. | 
|  | 1562     RefPtr<Range> paragraphRange = searchRange->cloneRange(ec); | 
|  | 1563     setStart(paragraphRange.get(), startOfParagraph(searchRange->startPosition()
      )); | 
|  | 1564     int totalRangeLength = TextIterator::rangeLength(paragraphRange.get()); | 
|  | 1565     setEnd(paragraphRange.get(), endOfParagraph(searchRange->startPosition())); | 
|  | 1566 | 
|  | 1567     RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(e
      c)->document(), paragraphRange->startPosition(), searchRange->startPosition()); | 
|  | 1568     int searchRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get()); | 
|  | 1569     int totalLengthProcessed = 0; | 
|  | 1570 | 
|  | 1571     bool firstIteration = true; | 
|  | 1572     bool lastIteration = false; | 
|  | 1573     while (totalLengthProcessed < totalRangeLength) { | 
|  | 1574         // Iterate through the search range by paragraphs, checking each one for
       spelling and grammar. | 
|  | 1575         int currentLength = TextIterator::rangeLength(paragraphRange.get()); | 
|  | 1576         int currentStartOffset = firstIteration ? searchRangeStartOffset : 0; | 
|  | 1577         int currentEndOffset = currentLength; | 
|  | 1578         if (inSameParagraph(paragraphRange->startPosition(), searchRange->endPos
      ition())) { | 
|  | 1579             // Determine the character offset from the end of the original searc
      h range to the end of the paragraph, | 
|  | 1580             // since we will want to ignore results in this area. | 
|  | 1581             RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->start
      Container(ec)->document(), paragraphRange->startPosition(), searchRange->endPosi
      tion()); | 
|  | 1582             currentEndOffset = TextIterator::rangeLength(endOffsetAsRange.get())
      ; | 
|  | 1583             lastIteration = true; | 
|  | 1584         } | 
|  | 1585         if (currentStartOffset < currentEndOffset) { | 
|  | 1586             String paragraphString = plainText(paragraphRange.get()); | 
|  | 1587             if (paragraphString.length() > 0) { | 
|  | 1588                 bool foundGrammar = false; | 
|  | 1589                 int spellingLocation = 0; | 
|  | 1590                 int grammarPhraseLocation = 0; | 
|  | 1591                 int grammarDetailLocation = 0; | 
|  | 1592                 unsigned grammarDetailIndex = 0; | 
|  | 1593 | 
|  | 1594                 Vector<TextCheckingResult> results; | 
|  | 1595                 client->checkSpellingAndGrammarOfParagraph(paragraphString.chara
      cters(), paragraphString.length(), checkGrammar, results); | 
|  | 1596 | 
|  | 1597                 for (unsigned i = 0; i < results.size(); i++) { | 
|  | 1598                     const TextCheckingResult* result = &results[i]; | 
|  | 1599                     if (result->resultType == 1 && result->location >= currentSt
      artOffset && result->location + result->length <= currentEndOffset) { | 
|  | 1600                         ASSERT(result->length > 0 && result->location >= 0); | 
|  | 1601                         spellingLocation = result->location; | 
|  | 1602                         misspelledWord = paragraphString.substring(result->locat
      ion, result->length); | 
|  | 1603                         ASSERT(misspelledWord.length() != 0); | 
|  | 1604                         break; | 
|  | 1605                     } else if (checkGrammar && result->resultType == 2 && result
      ->location < currentEndOffset && result->location + result->length > currentStar
      tOffset) { | 
|  | 1606                         ASSERT(result->length > 0 && result->location >= 0); | 
|  | 1607                         // We can't stop after the first grammar result, since t
      here might still be a spelling result after | 
|  | 1608                         // it begins but before the first detail in it, but we c
      an stop if we find a second grammar result. | 
|  | 1609                         if (foundGrammar) break; | 
|  | 1610                         for (unsigned j = 0; j < result->details.size(); j++) { | 
|  | 1611                             const GrammarDetail* detail = &result->details[j]; | 
|  | 1612                             ASSERT(detail->length > 0 && detail->location >= 0); | 
|  | 1613                             if (result->location + detail->location >= currentSt
      artOffset && result->location + detail->location + detail->length <= currentEndO
      ffset && (!foundGrammar || result->location + detail->location < grammarDetailLo
      cation)) { | 
|  | 1614                                 grammarDetailIndex = j; | 
|  | 1615                                 grammarDetailLocation = result->location + detai
      l->location; | 
|  | 1616                                 foundGrammar = true; | 
|  | 1617                             } | 
|  | 1618                         } | 
|  | 1619                         if (foundGrammar) { | 
|  | 1620                             grammarPhraseLocation = result->location; | 
|  | 1621                             outGrammarDetail = result->details[grammarDetailInde
      x]; | 
|  | 1622                             badGrammarPhrase = paragraphString.substring(result-
      >location, result->length); | 
|  | 1623                             ASSERT(badGrammarPhrase.length() != 0); | 
|  | 1624                         } | 
|  | 1625                     } | 
|  | 1626                 } | 
|  | 1627 | 
|  | 1628                 if (!misspelledWord.isEmpty() && (!checkGrammar || badGrammarPhr
      ase.isEmpty() || spellingLocation <= grammarDetailLocation)) { | 
|  | 1629                     int spellingOffset = spellingLocation - currentStartOffset; | 
|  | 1630                     if (!firstIteration) { | 
|  | 1631                         RefPtr<Range> paragraphOffsetAsRange = Range::create(par
      agraphRange->startContainer(ec)->document(), searchRange->startPosition(), parag
      raphRange->startPosition()); | 
|  | 1632                         spellingOffset += TextIterator::rangeLength(paragraphOff
      setAsRange.get()); | 
|  | 1633                     } | 
|  | 1634                     outIsSpelling = true; | 
|  | 1635                     outFirstFoundOffset = spellingOffset; | 
|  | 1636                     firstFoundItem = misspelledWord; | 
|  | 1637                     break; | 
|  | 1638                 } else if (checkGrammar && !badGrammarPhrase.isEmpty()) { | 
|  | 1639                     int grammarPhraseOffset = grammarPhraseLocation - currentSta
      rtOffset; | 
|  | 1640                     if (!firstIteration) { | 
|  | 1641                         RefPtr<Range> paragraphOffsetAsRange = Range::create(par
      agraphRange->startContainer(ec)->document(), searchRange->startPosition(), parag
      raphRange->startPosition()); | 
|  | 1642                         grammarPhraseOffset += TextIterator::rangeLength(paragra
      phOffsetAsRange.get()); | 
|  | 1643                     } | 
|  | 1644                     outIsSpelling = false; | 
|  | 1645                     outFirstFoundOffset = grammarPhraseOffset; | 
|  | 1646                     firstFoundItem = badGrammarPhrase; | 
|  | 1647                     break; | 
|  | 1648                 } | 
|  | 1649             } | 
|  | 1650         } | 
|  | 1651         if (lastIteration || totalLengthProcessed + currentLength >= totalRangeL
      ength) | 
|  | 1652             break; | 
|  | 1653         setStart(paragraphRange.get(), startOfNextParagraph(paragraphRange->endP
      osition())); | 
|  | 1654         setEnd(paragraphRange.get(), endOfParagraph(paragraphRange->startPositio
      n())); | 
|  | 1655         firstIteration = false; | 
|  | 1656         totalLengthProcessed += currentLength; | 
|  | 1657     } | 
|  | 1658     return firstFoundItem; | 
|  | 1659 } | 
|  | 1660 | 
|  | 1661 #endif | 
|  | 1662 | 
| 1554 void Editor::advanceToNextMisspelling(bool startBeforeSelection) | 1663 void Editor::advanceToNextMisspelling(bool startBeforeSelection) | 
| 1555 { | 1664 { | 
| 1556     ExceptionCode ec = 0; | 1665     ExceptionCode ec = 0; | 
| 1557 | 1666 | 
| 1558     // The basic approach is to search in two phases - from the selection end to
       the end of the doc, and | 1667     // The basic approach is to search in two phases - from the selection end to
       the end of the doc, and | 
| 1559     // then we wrap and search from the doc start to (approximately) where we st
      arted. | 1668     // then we wrap and search from the doc start to (approximately) where we st
      arted. | 
| 1560 | 1669 | 
| 1561     // Start at the end of the selection, search to edge of document.  Starting 
      at the selection end makes | 1670     // Start at the end of the selection, search to edge of document.  Starting 
      at the selection end makes | 
| 1562     // repeated "check spelling" commands work. | 1671     // repeated "check spelling" commands work. | 
| 1563     VisibleSelection selection(frame()->selection()->selection()); | 1672     VisibleSelection selection(frame()->selection()->selection()); | 
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1611     if (!client()) | 1720     if (!client()) | 
| 1612         return; | 1721         return; | 
| 1613 | 1722 | 
| 1614     // We go to the end of our first range instead of the start of it, just to b
      e sure | 1723     // We go to the end of our first range instead of the start of it, just to b
      e sure | 
| 1615     // we don't get foiled by any word boundary problems at the start.  It means
       we might | 1724     // we don't get foiled by any word boundary problems at the start.  It means
       we might | 
| 1616     // do a tiny bit more searching. | 1725     // do a tiny bit more searching. | 
| 1617     Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec); | 1726     Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec); | 
| 1618     int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec); | 1727     int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec); | 
| 1619 | 1728 | 
| 1620     int misspellingOffset; | 1729     int misspellingOffset; | 
|  | 1730 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD
      ) | 
|  | 1731     RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec); | 
|  | 1732     String misspelledWord; | 
|  | 1733     String badGrammarPhrase; | 
|  | 1734     int grammarPhraseOffset = 0; | 
|  | 1735     bool isSpelling = true; | 
|  | 1736     int foundOffset = 0; | 
|  | 1737     GrammarDetail grammarDetail; | 
|  | 1738     String foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellin
      gSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammar
      Detail); | 
|  | 1739     if (isSpelling) { | 
|  | 1740         misspelledWord = foundItem; | 
|  | 1741         misspellingOffset = foundOffset; | 
|  | 1742     } else { | 
|  | 1743         badGrammarPhrase = foundItem; | 
|  | 1744         grammarPhraseOffset = foundOffset; | 
|  | 1745     } | 
|  | 1746 #else | 
| 1621     String misspelledWord = findFirstMisspellingInRange(client(), spellingSearch
      Range.get(), misspellingOffset, false); | 1747     String misspelledWord = findFirstMisspellingInRange(client(), spellingSearch
      Range.get(), misspellingOffset, false); | 
| 1622 | 1748 | 
| 1623     String badGrammarPhrase; | 1749     String badGrammarPhrase; | 
| 1624 | 1750 | 
| 1625 #ifndef BUILDING_ON_TIGER | 1751 #ifndef BUILDING_ON_TIGER | 
| 1626     int grammarPhraseOffset = 0; | 1752     int grammarPhraseOffset = 0; | 
| 1627     GrammarDetail grammarDetail; | 1753     GrammarDetail grammarDetail; | 
| 1628 | 1754 | 
| 1629     // Search for bad grammar that occurs prior to the next misspelled word (if 
      any) | 1755     // Search for bad grammar that occurs prior to the next misspelled word (if 
      any) | 
| 1630     RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec); | 1756     RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec); | 
| 1631     if (!misspelledWord.isEmpty()) { | 1757     if (!misspelledWord.isEmpty()) { | 
| 1632         // Stop looking at start of next misspelled word | 1758         // Stop looking at start of next misspelled word | 
| 1633         CharacterIterator chars(grammarSearchRange.get()); | 1759         CharacterIterator chars(grammarSearchRange.get()); | 
| 1634         chars.advance(misspellingOffset); | 1760         chars.advance(misspellingOffset); | 
| 1635         grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.rang
      e()->startOffset(ec), ec); | 1761         grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.rang
      e()->startOffset(ec), ec); | 
| 1636     } | 1762     } | 
| 1637 | 1763 | 
| 1638     if (isGrammarCheckingEnabled()) | 1764     if (isGrammarCheckingEnabled()) | 
| 1639         badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRan
      ge.get(), grammarDetail, grammarPhraseOffset, false); | 1765         badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRan
      ge.get(), grammarDetail, grammarPhraseOffset, false); | 
| 1640 #endif | 1766 #endif | 
|  | 1767 #endif | 
| 1641 | 1768 | 
| 1642     // If we found neither bad grammar nor a misspelled word, wrap and try again
       (but don't bother if we started at the beginning of the | 1769     // If we found neither bad grammar nor a misspelled word, wrap and try again
       (but don't bother if we started at the beginning of the | 
| 1643     // block rather than at a selection). | 1770     // block rather than at a selection). | 
| 1644     if (startedWithSelection && !misspelledWord && !badGrammarPhrase) { | 1771     if (startedWithSelection && !misspelledWord && !badGrammarPhrase) { | 
| 1645         spellingSearchRange->setStart(topNode, 0, ec); | 1772         spellingSearchRange->setStart(topNode, 0, ec); | 
| 1646         // going until the end of the very first chunk we tested is far enough | 1773         // going until the end of the very first chunk we tested is far enough | 
| 1647         spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfter
      Wrap, ec); | 1774         spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfter
      Wrap, ec); | 
| 1648 | 1775 | 
|  | 1776 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD
      ) | 
|  | 1777         grammarSearchRange = spellingSearchRange->cloneRange(ec); | 
|  | 1778         foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSe
      archRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDet
      ail); | 
|  | 1779         if (isSpelling) { | 
|  | 1780             misspelledWord = foundItem; | 
|  | 1781             misspellingOffset = foundOffset; | 
|  | 1782         } else { | 
|  | 1783             badGrammarPhrase = foundItem; | 
|  | 1784             grammarPhraseOffset = foundOffset; | 
|  | 1785         } | 
|  | 1786 #else | 
| 1649         misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRan
      ge.get(), misspellingOffset, false); | 1787         misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRan
      ge.get(), misspellingOffset, false); | 
| 1650 | 1788 | 
| 1651 #ifndef BUILDING_ON_TIGER | 1789 #ifndef BUILDING_ON_TIGER | 
| 1652         grammarSearchRange = spellingSearchRange->cloneRange(ec); | 1790         grammarSearchRange = spellingSearchRange->cloneRange(ec); | 
| 1653         if (!misspelledWord.isEmpty()) { | 1791         if (!misspelledWord.isEmpty()) { | 
| 1654             // Stop looking at start of next misspelled word | 1792             // Stop looking at start of next misspelled word | 
| 1655             CharacterIterator chars(grammarSearchRange.get()); | 1793             CharacterIterator chars(grammarSearchRange.get()); | 
| 1656             chars.advance(misspellingOffset); | 1794             chars.advance(misspellingOffset); | 
| 1657             grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.
      range()->startOffset(ec), ec); | 1795             grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.
      range()->startOffset(ec), ec); | 
| 1658         } | 1796         } | 
| 1659         if (isGrammarCheckingEnabled()) | 1797         if (isGrammarCheckingEnabled()) | 
| 1660             badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearc
      hRange.get(), grammarDetail, grammarPhraseOffset, false); | 1798             badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearc
      hRange.get(), grammarDetail, grammarPhraseOffset, false); | 
| 1661 #endif | 1799 #endif | 
|  | 1800 #endif | 
| 1662     } | 1801     } | 
| 1663 | 1802 | 
| 1664     if (!badGrammarPhrase.isEmpty()) { | 1803     if (!badGrammarPhrase.isEmpty()) { | 
| 1665 #ifdef BUILDING_ON_TIGER | 1804 #ifdef BUILDING_ON_TIGER | 
| 1666         ASSERT_NOT_REACHED(); | 1805         ASSERT_NOT_REACHED(); | 
| 1667 #else | 1806 #else | 
| 1668         // We found bad grammar. Since we only searched for bad grammar up to th
      e first misspelled word, the bad grammar | 1807         // We found bad grammar. Since we only searched for bad grammar up to th
      e first misspelled word, the bad grammar | 
| 1669         // takes precedence and we ignore any potential misspelled word. Select 
      the grammar detail, update the spelling | 1808         // takes precedence and we ignore any potential misspelled word. Select 
      the grammar detail, update the spelling | 
| 1670         // panel, and store a marker so we draw the green squiggle later. | 1809         // panel, and store a marker so we draw the green squiggle later. | 
| 1671 | 1810 | 
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1793 { | 1932 { | 
| 1794     String selectedString = frame()->selectedText(); | 1933     String selectedString = frame()->selectedText(); | 
| 1795     ASSERT(selectedString.length() != 0); | 1934     ASSERT(selectedString.length() != 0); | 
| 1796 | 1935 | 
| 1797     Vector<String> guesses; | 1936     Vector<String> guesses; | 
| 1798     if (client()) | 1937     if (client()) | 
| 1799         client()->getGuessesForWord(selectedString, guesses); | 1938         client()->getGuessesForWord(selectedString, guesses); | 
| 1800     return guesses; | 1939     return guesses; | 
| 1801 } | 1940 } | 
| 1802 | 1941 | 
|  | 1942 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD
      ) | 
|  | 1943 | 
|  | 1944 static Vector<String> guessesForMisspelledOrUngrammaticalRange(EditorClient* cli
      ent, Range *range, bool checkGrammar, bool& misspelled, bool& ungrammatical) | 
|  | 1945 { | 
|  | 1946     Vector<String> guesses; | 
|  | 1947     ExceptionCode ec; | 
|  | 1948     misspelled = false; | 
|  | 1949     ungrammatical = false; | 
|  | 1950 | 
|  | 1951     if (!client || !range || range->collapsed(ec)) | 
|  | 1952         return guesses; | 
|  | 1953 | 
|  | 1954     // Expand the range to encompass entire paragraphs, since text checking need
      s that much context. | 
|  | 1955     int rangeStartOffset; | 
|  | 1956     String paragraphString; | 
|  | 1957     RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(range, rangeSta
      rtOffset, paragraphString); | 
|  | 1958     int rangeLength = TextIterator::rangeLength(range); | 
|  | 1959     if (rangeLength == 0 || paragraphString.length() == 0) | 
|  | 1960         return guesses; | 
|  | 1961 | 
|  | 1962     Vector<TextCheckingResult> results; | 
|  | 1963     client->checkSpellingAndGrammarOfParagraph(paragraphString.characters(), par
      agraphString.length(), checkGrammar, results); | 
|  | 1964 | 
|  | 1965     for (unsigned i = 0; i < results.size(); i++) { | 
|  | 1966         const TextCheckingResult* result = &results[i]; | 
|  | 1967         if (result->resultType == 1 && result->location == rangeStartOffset && r
      esult->length == rangeLength) { | 
|  | 1968             String misspelledWord = paragraphString.substring(rangeStartOffset, 
      rangeLength); | 
|  | 1969             ASSERT(misspelledWord.length() != 0); | 
|  | 1970             client->getGuessesForWord(misspelledWord, guesses); | 
|  | 1971             client->updateSpellingUIWithMisspelledWord(misspelledWord); | 
|  | 1972             misspelled = true; | 
|  | 1973             return guesses; | 
|  | 1974         } | 
|  | 1975     } | 
|  | 1976 | 
|  | 1977     if (!checkGrammar) | 
|  | 1978         return guesses; | 
|  | 1979 | 
|  | 1980     for (unsigned i = 0; i < results.size(); i++) { | 
|  | 1981         const TextCheckingResult* result = &results[i]; | 
|  | 1982         if (result->resultType == 2 && result->location <= rangeStartOffset && r
      esult->location + result->length >= rangeStartOffset + rangeLength) { | 
|  | 1983             for (unsigned j = 0; j < result->details.size(); j++) { | 
|  | 1984                 const GrammarDetail* detail = &result->details[j]; | 
|  | 1985                 ASSERT(detail->length > 0 && detail->location >= 0); | 
|  | 1986                 if (result->location + detail->location == rangeStartOffset && d
      etail->length == rangeLength) { | 
|  | 1987                     String badGrammarPhrase = paragraphString.substring(result->
      location, result->length); | 
|  | 1988                     ASSERT(badGrammarPhrase.length() != 0); | 
|  | 1989                     for (unsigned k = 0; k < detail->guesses.size(); k++) | 
|  | 1990                         guesses.append(detail->guesses[k]); | 
|  | 1991                     client->updateSpellingUIWithGrammarString(badGrammarPhrase, 
      *detail); | 
|  | 1992                     ungrammatical = true; | 
|  | 1993                     return guesses; | 
|  | 1994                 } | 
|  | 1995             } | 
|  | 1996         } | 
|  | 1997     } | 
|  | 1998     return guesses; | 
|  | 1999 } | 
|  | 2000 | 
|  | 2001 #endif | 
|  | 2002 | 
|  | 2003 Vector<String> Editor::guessesForMisspelledOrUngrammaticalSelection(bool& misspe
      lled, bool& ungrammatical) | 
|  | 2004 { | 
|  | 2005 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD
      ) | 
|  | 2006     return guessesForMisspelledOrUngrammaticalRange(client(), frame()->selection
      ()->toNormalizedRange().get(), isGrammarCheckingEnabled(), misspelled, ungrammat
      ical); | 
|  | 2007 #else | 
|  | 2008     misspelled = isSelectionMisspelled(); | 
|  | 2009     if (misspelled) { | 
|  | 2010         ungrammatical = false; | 
|  | 2011         return guessesForMisspelledSelection(); | 
|  | 2012     } | 
|  | 2013     if (isGrammarCheckingEnabled() && isSelectionUngrammatical()) { | 
|  | 2014         ungrammatical = true; | 
|  | 2015         return guessesForUngrammaticalSelection(); | 
|  | 2016     } | 
|  | 2017     ungrammatical = false; | 
|  | 2018     return Vector<String>(); | 
|  | 2019 #endif | 
|  | 2020 } | 
|  | 2021 | 
| 1803 void Editor::showSpellingGuessPanel() | 2022 void Editor::showSpellingGuessPanel() | 
| 1804 { | 2023 { | 
| 1805     if (!client()) { | 2024     if (!client()) { | 
| 1806         LOG_ERROR("No NSSpellChecker"); | 2025         LOG_ERROR("No NSSpellChecker"); | 
| 1807         return; | 2026         return; | 
| 1808     } | 2027     } | 
| 1809 | 2028 | 
| 1810 #ifndef BUILDING_ON_TIGER | 2029 #ifndef BUILDING_ON_TIGER | 
| 1811     // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave 
      Tiger behavior alone | 2030     // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave 
      Tiger behavior alone | 
| 1812     // to match rest of OS X. | 2031     // to match rest of OS X. | 
| (...skipping 12 matching lines...) Expand all  Loading... | 
| 1825     if (!client()) | 2044     if (!client()) | 
| 1826         return false; | 2045         return false; | 
| 1827     return client()->spellingUIIsShowing(); | 2046     return client()->spellingUIIsShowing(); | 
| 1828 } | 2047 } | 
| 1829 | 2048 | 
| 1830 void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p) | 2049 void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p) | 
| 1831 { | 2050 { | 
| 1832     if (!isContinuousSpellCheckingEnabled()) | 2051     if (!isContinuousSpellCheckingEnabled()) | 
| 1833         return; | 2052         return; | 
| 1834 | 2053 | 
|  | 2054 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD
      ) | 
|  | 2055     VisibleSelection adjacentWords = VisibleSelection(startOfWord(p, LeftWordIfO
      nBoundary), endOfWord(p, RightWordIfOnBoundary)); | 
|  | 2056     if (isGrammarCheckingEnabled()) { | 
|  | 2057         VisibleSelection selectedSentence = VisibleSelection(startOfSentence(p),
       endOfSentence(p)); | 
|  | 2058         markMisspellingsAndBadGrammar(adjacentWords, true, selectedSentence); | 
|  | 2059     } else { | 
|  | 2060         markMisspellingsAndBadGrammar(adjacentWords, false, adjacentWords); | 
|  | 2061     } | 
|  | 2062 #else | 
| 1835     // Check spelling of one word | 2063     // Check spelling of one word | 
| 1836     markMisspellings(VisibleSelection(startOfWord(p, LeftWordIfOnBoundary), endO
      fWord(p, RightWordIfOnBoundary))); | 2064     markMisspellings(VisibleSelection(startOfWord(p, LeftWordIfOnBoundary), endO
      fWord(p, RightWordIfOnBoundary))); | 
| 1837 | 2065 | 
| 1838     if (!isGrammarCheckingEnabled()) | 2066     if (!isGrammarCheckingEnabled()) | 
| 1839         return; | 2067         return; | 
| 1840 | 2068 | 
| 1841     // Check grammar of entire sentence | 2069     // Check grammar of entire sentence | 
| 1842     markBadGrammar(VisibleSelection(startOfSentence(p), endOfSentence(p))); | 2070     markBadGrammar(VisibleSelection(startOfSentence(p), endOfSentence(p))); | 
|  | 2071 #endif | 
| 1843 } | 2072 } | 
| 1844 | 2073 | 
| 1845 static void markAllMisspellingsInRange(EditorClient* client, Range* searchRange) | 2074 static void markAllMisspellingsInRange(EditorClient* client, Range* searchRange) | 
| 1846 { | 2075 { | 
| 1847     // Use the "markAll" feature of findFirstMisspellingInRange. Ignore the retu
      rn value and the "out parameter"; | 2076     // Use the "markAll" feature of findFirstMisspellingInRange. Ignore the retu
      rn value and the "out parameter"; | 
| 1848     // all we need to do is mark every instance. | 2077     // all we need to do is mark every instance. | 
| 1849     int ignoredOffset; | 2078     int ignoredOffset; | 
| 1850     findFirstMisspellingInRange(client, searchRange, ignoredOffset, true); | 2079     findFirstMisspellingInRange(client, searchRange, ignoredOffset, true); | 
| 1851 } | 2080 } | 
| 1852 | 2081 | 
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1903 | 2132 | 
| 1904 void Editor::markBadGrammar(const VisibleSelection& selection) | 2133 void Editor::markBadGrammar(const VisibleSelection& selection) | 
| 1905 { | 2134 { | 
| 1906 #ifndef BUILDING_ON_TIGER | 2135 #ifndef BUILDING_ON_TIGER | 
| 1907     markMisspellingsOrBadGrammar(this, selection, false); | 2136     markMisspellingsOrBadGrammar(this, selection, false); | 
| 1908 #else | 2137 #else | 
| 1909     UNUSED_PARAM(selection); | 2138     UNUSED_PARAM(selection); | 
| 1910 #endif | 2139 #endif | 
| 1911 } | 2140 } | 
| 1912 | 2141 | 
|  | 2142 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD
      ) | 
|  | 2143 | 
|  | 2144 static void markAllMisspellingsAndBadGrammarInRanges(EditorClient* client, Range
       *spellingRange, bool markGrammar, Range *grammarRange) | 
|  | 2145 { | 
|  | 2146     // This function is called with selections already expanded to word boundari
      es. | 
|  | 2147     ExceptionCode ec; | 
|  | 2148     if (!client || !spellingRange || (markGrammar && !grammarRange)) | 
|  | 2149         return; | 
|  | 2150 | 
|  | 2151     // If we're not in an editable node, bail. | 
|  | 2152     Node* editableNode = spellingRange->startContainer(); | 
|  | 2153     if (!editableNode || !editableNode->isContentEditable()) | 
|  | 2154         return; | 
|  | 2155 | 
|  | 2156     // Expand the range to encompass entire paragraphs, since text checking need
      s that much context. | 
|  | 2157     int spellingRangeStartOffset; | 
|  | 2158     int spellingRangeEndOffset; | 
|  | 2159     int grammarRangeStartOffset; | 
|  | 2160     int grammarRangeEndOffset; | 
|  | 2161     String paragraphString; | 
|  | 2162 | 
|  | 2163     if (markGrammar) { | 
|  | 2164         // The spelling range should be contained in the paragraph-aligned exten
      sion of the grammar range. | 
|  | 2165         RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(grammarRang
      e, grammarRangeStartOffset, paragraphString); | 
|  | 2166         RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContain
      er(ec)->document(), paragraphRange->startPosition(), spellingRange->startPositio
      n()); | 
|  | 2167         spellingRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get()
      ); | 
|  | 2168         grammarRangeEndOffset = grammarRangeStartOffset + TextIterator::rangeLen
      gth(grammarRange); | 
|  | 2169     } else { | 
|  | 2170         RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(spellingRan
      ge, spellingRangeStartOffset, paragraphString); | 
|  | 2171     } | 
|  | 2172     spellingRangeEndOffset = spellingRangeStartOffset + TextIterator::rangeLengt
      h(spellingRange); | 
|  | 2173     if (paragraphString.length() == 0 || (spellingRangeStartOffset >= spellingRa
      ngeEndOffset && (!markGrammar || grammarRangeStartOffset >= grammarRangeEndOffse
      t))) | 
|  | 2174         return; | 
|  | 2175 | 
|  | 2176     Vector<TextCheckingResult> results; | 
|  | 2177     client->checkSpellingAndGrammarOfParagraph(paragraphString.characters(), par
      agraphString.length(), markGrammar, results); | 
|  | 2178 | 
|  | 2179     for (unsigned i = 0; i < results.size(); i++) { | 
|  | 2180         const TextCheckingResult* result = &results[i]; | 
|  | 2181         if (result->resultType == 1 && result->location >= spellingRangeStartOff
      set && result->location + result->length <= spellingRangeEndOffset) { | 
|  | 2182             ASSERT(result->length > 0 && result->location >= 0); | 
|  | 2183             RefPtr<Range> misspellingRange = TextIterator::subrange(spellingRang
      e, result->location - spellingRangeStartOffset, result->length); | 
|  | 2184             misspellingRange->startContainer(ec)->document()->addMarker(misspell
      ingRange.get(), DocumentMarker::Spelling); | 
|  | 2185         } else if (markGrammar && result->resultType == 2 && result->location < 
      grammarRangeEndOffset && result->location + result->length > grammarRangeStartOf
      fset) { | 
|  | 2186             ASSERT(result->length > 0 && result->location >= 0); | 
|  | 2187             for (unsigned j = 0; j < result->details.size(); j++) { | 
|  | 2188                 const GrammarDetail* detail = &result->details[j]; | 
|  | 2189                 ASSERT(detail->length > 0 && detail->location >= 0); | 
|  | 2190                 if (result->location + detail->location >= grammarRangeStartOffs
      et && result->location + detail->location + detail->length <= grammarRangeEndOff
      set) { | 
|  | 2191                     RefPtr<Range> badGrammarRange = TextIterator::subrange(gramm
      arRange, result->location + detail->location - grammarRangeStartOffset, detail->
      length); | 
|  | 2192                     grammarRange->startContainer(ec)->document()->addMarker(badG
      rammarRange.get(), DocumentMarker::Grammar, detail->userDescription); | 
|  | 2193                 } | 
|  | 2194             } | 
|  | 2195         } | 
|  | 2196     } | 
|  | 2197 } | 
|  | 2198 | 
|  | 2199 #endif | 
|  | 2200 | 
|  | 2201 void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelec
      tion, bool markGrammar, const VisibleSelection& grammarSelection) | 
|  | 2202 { | 
|  | 2203 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD
      ) | 
|  | 2204     if (!isContinuousSpellCheckingEnabled()) | 
|  | 2205         return; | 
|  | 2206     markAllMisspellingsAndBadGrammarInRanges(client(), spellingSelection.toNorma
      lizedRange().get(), markGrammar && isGrammarCheckingEnabled(), grammarSelection.
      toNormalizedRange().get()); | 
|  | 2207 #else | 
|  | 2208     markMisspellings(spellingSelection); | 
|  | 2209     if (markGrammar) | 
|  | 2210         markBadGrammar(grammarSelection); | 
|  | 2211 #endif | 
|  | 2212 } | 
|  | 2213 | 
| 1913 PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint) | 2214 PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint) | 
| 1914 { | 2215 { | 
| 1915     Document* document = m_frame->documentAtPoint(windowPoint); | 2216     Document* document = m_frame->documentAtPoint(windowPoint); | 
| 1916     if (!document) | 2217     if (!document) | 
| 1917         return 0; | 2218         return 0; | 
| 1918 | 2219 | 
| 1919     Frame* frame = document->frame(); | 2220     Frame* frame = document->frame(); | 
| 1920     ASSERT(frame); | 2221     ASSERT(frame); | 
| 1921     FrameView* frameView = frame->view(); | 2222     FrameView* frameView = frame->view(); | 
| 1922     if (!frameView) | 2223     if (!frameView) | 
| (...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 2182 | 2483 | 
| 2183     if (!wrapFlag) | 2484     if (!wrapFlag) | 
| 2184         return Range::create(m_frame->document()); | 2485         return Range::create(m_frame->document()); | 
| 2185 | 2486 | 
| 2186     if (forward) | 2487     if (forward) | 
| 2187         return firstVisibleRange(target, caseFlag); | 2488         return firstVisibleRange(target, caseFlag); | 
| 2188 | 2489 | 
| 2189     return lastVisibleRange(target, caseFlag); | 2490     return lastVisibleRange(target, caseFlag); | 
| 2190 } | 2491 } | 
| 2191 | 2492 | 
|  | 2493 void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, b
      ool closeTyping, bool clearTypingStyle, EditCommand* cmd) | 
|  | 2494 { | 
|  | 2495     // If there is no selection change, don't bother sending shouldChangeSelecti
      on, but still call setSelection, | 
|  | 2496     // because there is work that it must do in this situation. | 
|  | 2497     // The old selection can be invalid here and calling shouldChangeSelection c
      an produce some strange calls. | 
|  | 2498     // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ra
      nges for selections that are no longer valid | 
|  | 2499     bool selectionDidNotChangeDOMPosition = newSelection == m_frame->selection()
      ->selection(); | 
|  | 2500     if (selectionDidNotChangeDOMPosition || m_frame->shouldChangeSelection(newSe
      lection)) | 
|  | 2501         m_frame->selection()->setSelection(newSelection, closeTyping, clearTypin
      gStyle); | 
|  | 2502 | 
|  | 2503     // Some kinds of deletes and line break insertions change the selection's po
      sition within the document without | 
|  | 2504     // changing its position within the DOM.  For example when you press return 
      in the following (the caret is marked by ^): | 
|  | 2505     // <div contentEditable="true"><div>^Hello</div></div> | 
|  | 2506     // WebCore inserts <div><br></div> *before* the current block, which correct
      ly moves the paragraph down but which doesn't | 
|  | 2507     // change the caret's DOM position (["hello", 0]).  In these situations the 
      above SelectionController::setSelection call | 
|  | 2508     // does not call EditorClient::respondToChangedSelection(), which, on the Ma
      c, sends selection change notifications and | 
|  | 2509     // starts a new kill ring sequence, but we want to do these things (matches 
      AppKit). | 
|  | 2510     if (selectionDidNotChangeDOMPosition && cmd->isTypingCommand()) | 
|  | 2511         client()->respondToChangedSelection(); | 
|  | 2512 } | 
|  | 2513 | 
| 2192 } // namespace WebCore | 2514 } // namespace WebCore | 
| 2193 |  | 
| 2194 |  | 
| 2195 |  | 
| 2196 |  | 
| OLD | NEW | 
|---|