303a70b726bd07a427f7011021dca6149e981238
[WebKit-https.git] / Source / WebCore / editing / mac / EditorMac.mm
1 /*
2  * Copyright (C) 2006, 2007, 2008 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 COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "Editor.h"
28
29 #import "ColorMac.h"
30 #import "ClipboardMac.h"
31 #import "CachedResourceLoader.h"
32 #import "DocumentFragment.h"
33 #import "DOMRangeInternal.h"
34 #import "EditingText.h"
35 #import "Editor.h"
36 #import "EditorClient.h"
37 #import "Font.h"
38 #import "Frame.h"
39 #import "FrameView.h"
40 #import "HTMLConverter.h"
41 #import "HTMLNames.h"
42 #import "LegacyWebArchive.h"
43 #import "Pasteboard.h"
44 #import "PasteboardStrategy.h"
45 #import "PlatformStrategies.h"
46 #import "Range.h"
47 #import "RenderBlock.h"
48 #import "RuntimeApplicationChecks.h"
49 #import "Sound.h"
50 #import "TypingCommand.h"
51 #import "htmlediting.h"
52 #import "WebNSAttributedStringExtras.h"
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
58 PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy, Frame* frame)
59 {
60     return ClipboardMac::create(Clipboard::CopyAndPaste,
61         policy == ClipboardWritable ? platformStrategies()->pasteboardStrategy()->uniqueName() : String(NSGeneralPboard), policy, ClipboardMac::CopyAndPasteGeneric, frame);
62 }
63
64 void Editor::showFontPanel()
65 {
66     [[NSFontManager sharedFontManager] orderFrontFontPanel:nil];
67 }
68
69 void Editor::showStylesPanel()
70 {
71     [[NSFontManager sharedFontManager] orderFrontStylesPanel:nil];
72 }
73
74 void Editor::showColorPanel()
75 {
76     [[NSApplication sharedApplication] orderFrontColorPanel:nil];
77 }
78
79 void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
80 {
81     RefPtr<Range> range = selectedRange();
82     bool choosePlainText;
83     
84     m_frame->editor()->client()->setInsertionPasteboard(NSGeneralPboard);
85 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
86     RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, choosePlainText);
87     if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
88         pasteAsFragment(fragment, canSmartReplaceWithPasteboard(pasteboard), false);
89 #else
90     // Mail is ignoring the frament passed to the delegate and creates a new one.
91     // We want to avoid creating the fragment twice.
92     if (applicationIsAppleMail()) {
93         if (shouldInsertFragment(NULL, range, EditorInsertActionPasted)) {
94             RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, choosePlainText);
95             if (fragment)
96                 pasteAsFragment(fragment, canSmartReplaceWithPasteboard(pasteboard), false);
97         }        
98     } else {
99         RefPtr<DocumentFragment>fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, choosePlainText);
100         if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
101             pasteAsFragment(fragment, canSmartReplaceWithPasteboard(pasteboard), false);
102     }
103 #endif
104     m_frame->editor()->client()->setInsertionPasteboard(String());
105 }
106
107 bool Editor::insertParagraphSeparatorInQuotedContent()
108 {
109     // FIXME: Why is this missing calls to canEdit, canEditRichly, etc...
110     TypingCommand::insertParagraphSeparatorInQuotedContent(m_frame->document());
111     revealSelectionAfterEditingOperation();
112     return true;
113 }
114
115 static RenderStyle* styleForSelectionStart(Frame* frame, Node *&nodeToRemove)
116 {
117     nodeToRemove = 0;
118
119     if (frame->selection()->isNone())
120         return 0;
121
122     Position position = frame->selection()->selection().visibleStart().deepEquivalent();
123     if (!position.isCandidate() || position.isNull())
124         return 0;
125
126     RefPtr<EditingStyle> typingStyle = frame->selection()->typingStyle();
127     if (!typingStyle || !typingStyle->style())
128         return position.deprecatedNode()->renderer()->style();
129
130     RefPtr<Element> styleElement = frame->document()->createElement(spanTag, false);
131
132     String styleText = typingStyle->style()->asText() + " display: inline";
133     styleElement->setAttribute(styleAttr, styleText.impl());
134
135     styleElement->appendChild(frame->document()->createEditingTextNode(""), ASSERT_NO_EXCEPTION);
136
137     position.deprecatedNode()->parentNode()->appendChild(styleElement, ASSERT_NO_EXCEPTION);
138
139     nodeToRemove = styleElement.get();
140     return styleElement->renderer() ? styleElement->renderer()->style() : 0;
141 }
142
143 const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const
144 {
145     hasMultipleFonts = false;
146
147     if (!m_frame->selection()->isRange()) {
148         Node* nodeToRemove;
149         RenderStyle* style = styleForSelectionStart(m_frame, nodeToRemove); // sets nodeToRemove
150
151         const SimpleFontData* result = 0;
152         if (style)
153             result = style->font().primaryFont();
154
155         if (nodeToRemove) {
156             ExceptionCode ec;
157             nodeToRemove->remove(ec);
158             ASSERT(!ec);
159         }
160
161         return result;
162     }
163
164     const SimpleFontData* font = 0;
165     RefPtr<Range> range = m_frame->selection()->toNormalizedRange();
166     if (Node* startNode = adjustedSelectionStartForStyleComputation(m_frame->selection()->selection()).deprecatedNode()) {
167         Node* pastEnd = range->pastLastNode();
168         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
169         // unreproducible case where this didn't happen, so check for null also.
170         for (Node* node = startNode; node && node != pastEnd; node = node->traverseNextNode()) {
171             RenderObject* renderer = node->renderer();
172             if (!renderer)
173                 continue;
174             // FIXME: Are there any node types that have renderers, but that we should be skipping?
175             const SimpleFontData* primaryFont = renderer->style()->font().primaryFont();
176             if (!font)
177                 font = primaryFont;
178             else if (font != primaryFont) {
179                 hasMultipleFonts = true;
180                 break;
181             }
182         }
183     }
184
185     return font;
186 }
187
188 NSDictionary* Editor::fontAttributesForSelectionStart() const
189 {
190     Node* nodeToRemove;
191     RenderStyle* style = styleForSelectionStart(m_frame, nodeToRemove);
192     if (!style)
193         return nil;
194
195     NSMutableDictionary* result = [NSMutableDictionary dictionary];
196
197     if (style->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && style->visitedDependentColor(CSSPropertyBackgroundColor).alpha() != 0)
198         [result setObject:nsColor(style->visitedDependentColor(CSSPropertyBackgroundColor)) forKey:NSBackgroundColorAttributeName];
199
200     if (style->font().primaryFont()->getNSFont())
201         [result setObject:style->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
202
203     if (style->visitedDependentColor(CSSPropertyColor).isValid() && style->visitedDependentColor(CSSPropertyColor) != Color::black)
204         [result setObject:nsColor(style->visitedDependentColor(CSSPropertyColor)) forKey:NSForegroundColorAttributeName];
205
206     const ShadowData* shadow = style->textShadow();
207     if (shadow) {
208         RetainPtr<NSShadow> s(AdoptNS, [[NSShadow alloc] init]);
209         [s.get() setShadowOffset:NSMakeSize(shadow->x(), shadow->y())];
210         [s.get() setShadowBlurRadius:shadow->blur()];
211         [s.get() setShadowColor:nsColor(shadow->color())];
212         [result setObject:s.get() forKey:NSShadowAttributeName];
213     }
214
215     int decoration = style->textDecorationsInEffect();
216     if (decoration & LINE_THROUGH)
217         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
218
219     int superscriptInt = 0;
220     switch (style->verticalAlign()) {
221         case BASELINE:
222         case BOTTOM:
223         case BASELINE_MIDDLE:
224         case LENGTH:
225         case MIDDLE:
226         case TEXT_BOTTOM:
227         case TEXT_TOP:
228         case TOP:
229             break;
230         case SUB:
231             superscriptInt = -1;
232             break;
233         case SUPER:
234             superscriptInt = 1;
235             break;
236     }
237     if (superscriptInt)
238         [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
239
240     if (decoration & UNDERLINE)
241         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
242
243     if (nodeToRemove) {
244         ExceptionCode ec = 0;
245         nodeToRemove->remove(ec);
246         ASSERT(ec == 0);
247     }
248
249     return result;
250 }
251
252 NSWritingDirection Editor::baseWritingDirectionForSelectionStart() const
253 {
254     NSWritingDirection result = NSWritingDirectionLeftToRight;
255
256     Position pos = m_frame->selection()->selection().visibleStart().deepEquivalent();
257     Node* node = pos.deprecatedNode();
258     if (!node)
259         return result;
260
261     RenderObject* renderer = node->renderer();
262     if (!renderer)
263         return result;
264
265     if (!renderer->isBlockFlow()) {
266         renderer = renderer->containingBlock();
267         if (!renderer)
268             return result;
269     }
270
271     RenderStyle* style = renderer->style();
272     if (!style)
273         return result;
274         
275     switch (style->direction()) {
276         case LTR:
277             result = NSWritingDirectionLeftToRight;
278             break;
279         case RTL:
280             result = NSWritingDirectionRightToLeft;
281             break;
282     }
283
284     return result;
285 }
286
287 bool Editor::canCopyExcludingStandaloneImages()
288 {
289     FrameSelection* selection = m_frame->selection();
290     return selection->isRange() && !selection->isInPasswordField();
291 }
292
293 void Editor::takeFindStringFromSelection()
294 {
295     if (!canCopyExcludingStandaloneImages()) {
296         systemBeep();
297         return;
298     }
299
300     Vector<String> types;
301     types.append(String(NSStringPboardType));
302     platformStrategies()->pasteboardStrategy()->setTypes(types, NSFindPboard);
303     platformStrategies()->pasteboardStrategy()->setStringForType(m_frame->displayStringModifiedByEncoding(selectedText()), NSStringPboardType, NSFindPboard);
304 }
305
306 void Editor::writeSelectionToPasteboard(const String& pasteboardName, const Vector<String>& pasteboardTypes)
307 {
308     Pasteboard pasteboard(pasteboardName);
309     pasteboard.writeSelectionForTypes(pasteboardTypes, true, m_frame);
310 }
311     
312 void Editor::readSelectionFromPasteboard(const String& pasteboardName)
313 {
314     Pasteboard pasteboard(pasteboardName);
315     if (m_frame->selection()->isContentRichlyEditable())
316         pasteWithPasteboard(&pasteboard, true);
317     else
318         pasteAsPlainTextWithPasteboard(&pasteboard);   
319 }
320
321 String Editor::stringSelectionForPasteboard()
322 {
323     return Pasteboard::getStringSelection(m_frame);
324 }
325
326 PassRefPtr<SharedBuffer> Editor::dataSelectionForPasteboard(const String& pasteboardType)
327 {
328     return Pasteboard::getDataSelection(m_frame, pasteboardType);
329 }
330
331 } // namespace WebCore