Home | History | Annotate | Download | only in WebCoreSupport
      1 /*
      2  * Copyright (C) 2011 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 #import "CorrectionPanel.h"
     26 
     27 #import "WebViewPrivate.h"
     28 
     29 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
     30 using namespace WebCore;
     31 
     32 static inline NSCorrectionIndicatorType correctionIndicatorType(CorrectionPanelInfo::PanelType panelType)
     33 {
     34     switch (panelType) {
     35     case CorrectionPanelInfo::PanelTypeCorrection:
     36         return NSCorrectionIndicatorTypeDefault;
     37     case CorrectionPanelInfo::PanelTypeReversion:
     38         return NSCorrectionIndicatorTypeReversion;
     39     case CorrectionPanelInfo::PanelTypeSpellingSuggestions:
     40         return NSCorrectionIndicatorTypeGuesses;
     41     }
     42     ASSERT_NOT_REACHED();
     43     return NSCorrectionIndicatorTypeDefault;
     44 }
     45 
     46 CorrectionPanel::CorrectionPanel()
     47     : m_wasDismissedExternally(false)
     48     , m_reasonForDismissing(ReasonForDismissingCorrectionPanelIgnored)
     49     , m_resultCondition(AdoptNS, [[NSCondition alloc] init])
     50 {
     51 }
     52 
     53 CorrectionPanel::~CorrectionPanel()
     54 {
     55     dismissInternal(ReasonForDismissingCorrectionPanelIgnored, false);
     56 }
     57 
     58 void CorrectionPanel::show(WebView* view, CorrectionPanelInfo::PanelType type, const FloatRect& boundingBoxOfReplacedString, const String& replacedString, const String& replacementString, const Vector<String>& alternativeReplacementStrings)
     59 {
     60     dismissInternal(ReasonForDismissingCorrectionPanelIgnored, false);
     61 
     62     if (!view)
     63         return;
     64 
     65     NSString* replacedStringAsNSString = replacedString;
     66     NSString* replacementStringAsNSString = replacementString;
     67     m_view = view;
     68     NSCorrectionIndicatorType indicatorType = correctionIndicatorType(type);
     69 
     70     NSMutableArray* alternativeStrings = 0;
     71     if (!alternativeReplacementStrings.isEmpty()) {
     72         size_t size = alternativeReplacementStrings.size();
     73         alternativeStrings = [NSMutableArray arrayWithCapacity:size];
     74         for (size_t i = 0; i < size; ++i)
     75             [alternativeStrings addObject:(NSString*)alternativeReplacementStrings[i]];
     76     }
     77 
     78     [[NSSpellChecker sharedSpellChecker] showCorrectionIndicatorOfType:indicatorType primaryString:replacementStringAsNSString alternativeStrings:alternativeStrings forStringInRect:[view convertRect:boundingBoxOfReplacedString fromView:nil] view:m_view.get() completionHandler:^(NSString* acceptedString) {
     79         handleAcceptedReplacement(acceptedString, replacedStringAsNSString, replacementStringAsNSString, indicatorType);
     80     }];
     81 }
     82 
     83 void CorrectionPanel::dismiss(ReasonForDismissingCorrectionPanel reason)
     84 {
     85     dismissInternal(reason, true);
     86 }
     87 
     88 String CorrectionPanel::dismissSoon(ReasonForDismissingCorrectionPanel reason)
     89 {
     90     if (!isShowing())
     91         return String();
     92 
     93     dismissInternal(reason, true);
     94     [m_resultCondition.get() lock];
     95     while (!m_resultForSynchronousDismissal)
     96         [m_resultCondition.get() wait];
     97     [m_resultCondition.get() unlock];
     98     return m_resultForSynchronousDismissal.get();
     99 }
    100 
    101 void CorrectionPanel::dismissInternal(ReasonForDismissingCorrectionPanel reason, bool dismissingExternally)
    102 {
    103     m_wasDismissedExternally = dismissingExternally;
    104     if (!isShowing())
    105         return;
    106 
    107     m_reasonForDismissing = reason;
    108     m_resultForSynchronousDismissal.clear();
    109     [[NSSpellChecker sharedSpellChecker] dismissCorrectionIndicatorForView:m_view.get()];
    110     m_view.clear();
    111 }
    112 
    113 void CorrectionPanel::recordAutocorrectionResponse(WebView* view, NSCorrectionResponse response, const String& replacedString, const String& replacementString)
    114 {
    115     [[NSSpellChecker sharedSpellChecker] recordResponse:response toCorrection:replacementString forWord:replacedString language:nil inSpellDocumentWithTag:[view spellCheckerDocumentTag]];
    116 }
    117 
    118 void CorrectionPanel::handleAcceptedReplacement(NSString* acceptedReplacement, NSString* replaced, NSString* proposedReplacement,  NSCorrectionIndicatorType correctionIndicatorType)
    119 {
    120     NSSpellChecker* spellChecker = [NSSpellChecker sharedSpellChecker];
    121     NSInteger documentTag = [m_view.get() spellCheckerDocumentTag];
    122 
    123     switch (correctionIndicatorType) {
    124     case NSCorrectionIndicatorTypeDefault:
    125         if (acceptedReplacement)
    126             [spellChecker recordResponse:NSCorrectionResponseAccepted toCorrection:acceptedReplacement forWord:replaced language:nil inSpellDocumentWithTag:documentTag];
    127         else {
    128             if (!m_wasDismissedExternally || m_reasonForDismissing == ReasonForDismissingCorrectionPanelCancelled)
    129                 [spellChecker recordResponse:NSCorrectionResponseRejected toCorrection:proposedReplacement forWord:replaced language:nil inSpellDocumentWithTag:documentTag];
    130             else
    131                 [spellChecker recordResponse:NSCorrectionResponseIgnored toCorrection:proposedReplacement forWord:replaced language:nil inSpellDocumentWithTag:documentTag];
    132         }
    133         break;
    134     case NSCorrectionIndicatorTypeReversion:
    135         if (acceptedReplacement)
    136             [spellChecker recordResponse:NSCorrectionResponseReverted toCorrection:replaced forWord:acceptedReplacement language:nil inSpellDocumentWithTag:documentTag];
    137         break;
    138     case NSCorrectionIndicatorTypeGuesses:
    139         if (acceptedReplacement)
    140             [spellChecker recordResponse:NSCorrectionResponseAccepted toCorrection:acceptedReplacement forWord:replaced language:nil inSpellDocumentWithTag:documentTag];
    141         break;
    142     }
    143 
    144     if (!m_wasDismissedExternally) {
    145         [m_view.get() handleCorrectionPanelResult:acceptedReplacement];
    146         return;
    147     }
    148 
    149     [m_resultCondition.get() lock];
    150     if (acceptedReplacement)
    151         m_resultForSynchronousDismissal.adoptNS([acceptedReplacement copy]);
    152     [m_resultCondition.get() signal];
    153     [m_resultCondition.get() unlock];
    154 }
    155 
    156 #endif // !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
    157 
    158