• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
30using namespace WebCore;
31
32static 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
46CorrectionPanel::CorrectionPanel()
47    : m_wasDismissedExternally(false)
48    , m_reasonForDismissing(ReasonForDismissingCorrectionPanelIgnored)
49    , m_resultCondition(AdoptNS, [[NSCondition alloc] init])
50{
51}
52
53CorrectionPanel::~CorrectionPanel()
54{
55    dismissInternal(ReasonForDismissingCorrectionPanelIgnored, false);
56}
57
58void 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
83void CorrectionPanel::dismiss(ReasonForDismissingCorrectionPanel reason)
84{
85    dismissInternal(reason, true);
86}
87
88String 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
101void 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
113void 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
118void 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