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