2 * Copyright (C) 2006, 2007, 2008, 2013, 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
13 * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
29 #import "BlockExceptions.h"
30 #import "CSSPrimitiveValueMappings.h"
31 #import "CSSValuePool.h"
32 #import "CachedResourceLoader.h"
34 #import "DOMRangeInternal.h"
35 #import "DataTransfer.h"
36 #import "DocumentFragment.h"
37 #import "DocumentLoader.h"
39 #import "EditorClient.h"
41 #import "FontCascade.h"
43 #import "FrameLoaderClient.h"
45 #import "HTMLAttachmentElement.h"
46 #import "HTMLConverter.h"
47 #import "HTMLElement.h"
49 #import "LegacyWebArchive.h"
50 #import "MIMETypeRegistry.h"
51 #import "NodeTraversal.h"
53 #import "Pasteboard.h"
54 #import "PasteboardStrategy.h"
55 #import "PlatformStrategies.h"
57 #import "RenderBlock.h"
58 #import "RenderImage.h"
59 #import "RuntimeApplicationChecks.h"
61 #import "StyleProperties.h"
63 #import "TypingCommand.h"
65 #import "WebNSAttributedStringExtras.h"
66 #import "htmlediting.h"
71 using namespace HTMLNames;
73 void Editor::showFontPanel()
75 [[NSFontManager sharedFontManager] orderFrontFontPanel:nil];
78 void Editor::showStylesPanel()
80 [[NSFontManager sharedFontManager] orderFrontStylesPanel:nil];
83 void Editor::showColorPanel()
85 [[NSApplication sharedApplication] orderFrontColorPanel:nil];
88 void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText, MailBlockquoteHandling mailBlockquoteHandling)
90 RefPtr<Range> range = selectedRange();
92 // FIXME: How can this hard-coded pasteboard name be right, given that the passed-in pasteboard has a name?
93 client()->setInsertionPasteboard(NSGeneralPboard);
96 RefPtr<DocumentFragment> fragment = webContentFromPasteboard(*pasteboard, *range, allowPlainText, chosePlainText);
98 if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
99 pasteAsFragment(fragment, canSmartReplaceWithPasteboard(*pasteboard), false, mailBlockquoteHandling);
101 client()->setInsertionPasteboard(String());
104 bool Editor::insertParagraphSeparatorInQuotedContent()
106 // FIXME: Why is this missing calls to canEdit, canEditRichly, etc.?
107 TypingCommand::insertParagraphSeparatorInQuotedContent(document());
108 revealSelectionAfterEditingOperation();
112 const Font* Editor::fontForSelection(bool& hasMultipleFonts) const
114 hasMultipleFonts = false;
116 if (!m_frame.selection().isRange()) {
118 RenderStyle* style = styleForSelectionStart(&m_frame, nodeToRemove); // sets nodeToRemove
120 const Font* result = nullptr;
122 result = &style->fontCascade().primaryFont();
124 nodeToRemove->remove(ASSERT_NO_EXCEPTION);
129 const Font* font = nullptr;
130 RefPtr<Range> range = m_frame.selection().toNormalizedRange();
131 Node* startNode = adjustedSelectionStartForStyleComputation(m_frame.selection().selection()).deprecatedNode();
132 if (range && startNode) {
133 Node* pastEnd = range->pastLastNode();
134 // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
135 // unreproducible case where this didn't happen, so check for null also.
136 for (Node* node = startNode; node && node != pastEnd; node = NodeTraversal::next(*node)) {
137 auto renderer = node->renderer();
140 // FIXME: Are there any node types that have renderers, but that we should be skipping?
141 const Font& primaryFont = renderer->style().fontCascade().primaryFont();
144 else if (font != &primaryFont) {
145 hasMultipleFonts = true;
154 NSDictionary* Editor::fontAttributesForSelectionStart() const
157 RenderStyle* style = styleForSelectionStart(&m_frame, nodeToRemove);
161 NSMutableDictionary* result = [NSMutableDictionary dictionary];
163 if (style->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && style->visitedDependentColor(CSSPropertyBackgroundColor).alpha() != 0)
164 [result setObject:nsColor(style->visitedDependentColor(CSSPropertyBackgroundColor)) forKey:NSBackgroundColorAttributeName];
166 if (style->fontCascade().primaryFont().getNSFont())
167 [result setObject:style->fontCascade().primaryFont().getNSFont() forKey:NSFontAttributeName];
169 if (style->visitedDependentColor(CSSPropertyColor).isValid() && style->visitedDependentColor(CSSPropertyColor) != Color::black)
170 [result setObject:nsColor(style->visitedDependentColor(CSSPropertyColor)) forKey:NSForegroundColorAttributeName];
172 const ShadowData* shadow = style->textShadow();
174 RetainPtr<NSShadow> s = adoptNS([[NSShadow alloc] init]);
175 [s.get() setShadowOffset:NSMakeSize(shadow->x(), shadow->y())];
176 [s.get() setShadowBlurRadius:shadow->radius()];
177 [s.get() setShadowColor:nsColor(shadow->color())];
178 [result setObject:s.get() forKey:NSShadowAttributeName];
181 int superscriptInt = 0;
182 switch (style->verticalAlign()) {
185 case BASELINE_MIDDLE:
200 [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
202 getTextDecorationAttributesRespectingTypingStyle(*style, result);
205 nodeToRemove->remove(ASSERT_NO_EXCEPTION);
210 bool Editor::canCopyExcludingStandaloneImages()
212 const VisibleSelection& selection = m_frame.selection().selection();
213 return selection.isRange() && !selection.isInPasswordField();
216 void Editor::takeFindStringFromSelection()
218 if (!canCopyExcludingStandaloneImages()) {
223 Vector<String> types;
224 types.append(String(NSStringPboardType));
225 platformStrategies()->pasteboardStrategy()->setTypes(types, NSFindPboard);
226 platformStrategies()->pasteboardStrategy()->setStringForType(m_frame.displayStringModifiedByEncoding(selectedTextForDataTransfer()), NSStringPboardType, NSFindPboard);
229 void Editor::readSelectionFromPasteboard(const String& pasteboardName, MailBlockquoteHandling mailBlockquoteHandling)
231 Pasteboard pasteboard(pasteboardName);
232 if (m_frame.selection().selection().isContentRichlyEditable())
233 pasteWithPasteboard(&pasteboard, true, mailBlockquoteHandling);
235 pasteAsPlainTextWithPasteboard(pasteboard);
238 static void maybeCopyNodeAttributesToFragment(const Node& node, DocumentFragment& fragment)
240 // This is only supported for single-Node fragments.
241 Node* firstChild = fragment.firstChild();
242 if (!firstChild || firstChild != fragment.lastChild())
245 // And only supported for HTML elements.
246 if (!node.isHTMLElement() || !firstChild->isHTMLElement())
249 // And only if the source Element and destination Element have the same HTML tag name.
250 const HTMLElement& oldElement = downcast<HTMLElement>(node);
251 HTMLElement& newElement = downcast<HTMLElement>(*firstChild);
252 if (oldElement.localName() != newElement.localName())
255 for (const Attribute& attribute : oldElement.attributesIterator()) {
256 if (newElement.hasAttribute(attribute.name()))
258 newElement.setAttribute(attribute.name(), attribute.value());
262 void Editor::replaceNodeFromPasteboard(Node* node, const String& pasteboardName)
266 if (&node->document() != m_frame.document())
269 RefPtr<Range> range = Range::create(node->document(), Position(node, Position::PositionIsBeforeAnchor), Position(node, Position::PositionIsAfterAnchor));
270 m_frame.selection().setSelection(VisibleSelection(*range), FrameSelection::DoNotSetFocus);
272 Pasteboard pasteboard(pasteboardName);
274 if (!m_frame.selection().selection().isContentRichlyEditable()) {
275 pasteAsPlainTextWithPasteboard(pasteboard);
279 // FIXME: How can this hard-coded pasteboard name be right, given that the passed-in pasteboard has a name?
280 client()->setInsertionPasteboard(NSGeneralPboard);
283 if (RefPtr<DocumentFragment> fragment = webContentFromPasteboard(pasteboard, *range, true, chosePlainText)) {
284 maybeCopyNodeAttributesToFragment(*node, *fragment);
285 if (shouldInsertFragment(fragment, range, EditorInsertActionPasted))
286 pasteAsFragment(fragment.release(), canSmartReplaceWithPasteboard(pasteboard), false, MailBlockquoteHandling::IgnoreBlockquote);
289 client()->setInsertionPasteboard(String());
292 // FIXME: Makes no sense that selectedTextForDataTransfer always includes alt text, but stringSelectionForPasteboard does not.
293 // This was left in a bad state when selectedTextForDataTransfer was added. Need to look over clients and fix this.
294 String Editor::stringSelectionForPasteboard()
298 String text = selectedText();
299 text.replace(noBreakSpace, ' ');
303 String Editor::stringSelectionForPasteboardWithImageAltText()
307 String text = selectedTextForDataTransfer();
308 text.replace(noBreakSpace, ' ');
312 PassRefPtr<SharedBuffer> Editor::selectionInWebArchiveFormat()
314 RefPtr<LegacyWebArchive> archive = LegacyWebArchive::createFromSelection(&m_frame);
315 return archive ? SharedBuffer::wrapCFData(archive->rawDataRepresentation().get()) : 0;
318 PassRefPtr<Range> Editor::adjustedSelectionRange()
320 // FIXME: Why do we need to adjust the selection to include the anchor tag it's in?
321 // Whoever wrote this code originally forgot to leave us a comment explaining the rationale.
322 RefPtr<Range> range = selectedRange();
323 Node* commonAncestor = range->commonAncestorContainer();
324 ASSERT(commonAncestor);
325 auto* enclosingAnchor = enclosingElementWithTag(firstPositionInNode(commonAncestor), HTMLNames::aTag);
326 if (enclosingAnchor && comparePositions(firstPositionInOrBeforeNode(range->startPosition().anchorNode()), range->startPosition()) >= 0)
327 range->setStart(enclosingAnchor, 0, IGNORE_EXCEPTION);
331 static PassRefPtr<SharedBuffer> dataInRTFDFormat(NSAttributedString *string)
333 NSUInteger length = string.length;
337 BEGIN_BLOCK_OBJC_EXCEPTIONS;
338 return SharedBuffer::wrapNSData([string RTFDFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
339 END_BLOCK_OBJC_EXCEPTIONS;
344 static PassRefPtr<SharedBuffer> dataInRTFFormat(NSAttributedString *string)
346 NSUInteger length = string.length;
350 BEGIN_BLOCK_OBJC_EXCEPTIONS;
351 return SharedBuffer::wrapNSData([string RTFFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
352 END_BLOCK_OBJC_EXCEPTIONS;
357 PassRefPtr<SharedBuffer> Editor::dataSelectionForPasteboard(const String& pasteboardType)
359 // FIXME: The interface to this function is awkward. We'd probably be better off with three separate functions.
360 // As of this writing, this is only used in WebKit2 to implement the method -[WKView writeSelectionToPasteboard:types:],
361 // which is only used to support OS X services.
363 // FIXME: Does this function really need to use adjustedSelectionRange()? Because writeSelectionToPasteboard() just uses selectedRange().
367 if (pasteboardType == WebArchivePboardType)
368 return selectionInWebArchiveFormat();
370 if (pasteboardType == String(NSRTFDPboardType))
371 return dataInRTFDFormat(attributedStringFromRange(*adjustedSelectionRange()));
373 if (pasteboardType == String(NSRTFPboardType)) {
374 NSAttributedString* attributedString = attributedStringFromRange(*adjustedSelectionRange());
375 // FIXME: Why is this attachment character stripping needed here, but not needed in writeSelectionToPasteboard?
376 if ([attributedString containsAttachments])
377 attributedString = attributedStringByStrippingAttachmentCharacters(attributedString);
378 return dataInRTFFormat(attributedString);
384 void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
386 NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
388 PasteboardWebContent content;
389 content.canSmartCopyOrDelete = canSmartCopyOrDelete();
390 content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
391 content.dataInRTFDFormat = [attributedString containsAttachments] ? dataInRTFDFormat(attributedString) : 0;
392 content.dataInRTFFormat = dataInRTFFormat(attributedString);
393 content.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
394 client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
396 pasteboard.write(content);
399 static void getImage(Element& imageElement, RefPtr<Image>& image, CachedImage*& cachedImage)
401 auto* renderer = imageElement.renderer();
402 if (!is<RenderImage>(renderer))
405 CachedImage* tentativeCachedImage = downcast<RenderImage>(*renderer).cachedImage();
406 if (!tentativeCachedImage || tentativeCachedImage->errorOccurred())
409 image = tentativeCachedImage->imageForRenderer(renderer);
413 cachedImage = tentativeCachedImage;
416 void Editor::fillInUserVisibleForm(PasteboardURL& pasteboardURL)
418 pasteboardURL.userVisibleForm = client()->userVisibleString(pasteboardURL.url);
421 String Editor::plainTextFromPasteboard(const PasteboardPlainText& text)
423 String string = text.text;
425 // FIXME: It's not clear this is 100% correct since we know -[NSURL URLWithString:] does not handle
426 // all the same cases we handle well in the URL code for creating an NSURL.
428 string = client()->userVisibleString([NSURL URLWithString:string]);
430 // FIXME: WTF should offer a non-Mac-specific way to convert string to precomposed form so we can do it for all platforms.
431 return [(NSString *)string precomposedStringWithCanonicalMapping];
434 void Editor::writeImageToPasteboard(Pasteboard& pasteboard, Element& imageElement, const URL& url, const String& title)
436 PasteboardImage pasteboardImage;
438 CachedImage* cachedImage;
439 getImage(imageElement, pasteboardImage.image, cachedImage);
440 if (!pasteboardImage.image)
444 pasteboardImage.url.url = url;
445 pasteboardImage.url.title = title;
446 pasteboardImage.url.userVisibleForm = client()->userVisibleString(pasteboardImage.url.url);
447 pasteboardImage.resourceData = cachedImage->resourceBuffer();
448 pasteboardImage.resourceMIMEType = cachedImage->response().mimeType();
450 pasteboard.write(pasteboardImage);
453 class Editor::WebContentReader final : public PasteboardWebContentReader {
457 const bool allowPlainText;
459 RefPtr<DocumentFragment> fragment;
460 bool madeFragmentFromPlainText;
462 WebContentReader(Frame& frame, Range& context, bool allowPlainText)
465 , allowPlainText(allowPlainText)
466 , madeFragmentFromPlainText(false)
471 virtual bool readWebArchive(PassRefPtr<SharedBuffer>) override;
472 virtual bool readFilenames(const Vector<String>&) override;
473 virtual bool readHTML(const String&) override;
474 virtual bool readRTFD(PassRefPtr<SharedBuffer>) override;
475 virtual bool readRTF(PassRefPtr<SharedBuffer>) override;
476 virtual bool readImage(PassRefPtr<SharedBuffer>, const String& type) override;
477 virtual bool readURL(const URL&, const String& title) override;
478 virtual bool readPlainText(const String&) override;
481 bool Editor::WebContentReader::readWebArchive(PassRefPtr<SharedBuffer> buffer)
483 if (!frame.document())
486 RefPtr<LegacyWebArchive> archive = LegacyWebArchive::create(URL(), buffer.get());
490 RefPtr<ArchiveResource> mainResource = archive->mainResource();
494 const String& type = mainResource->mimeType();
496 if (frame.loader().client().canShowMIMETypeAsHTML(type)) {
497 // FIXME: The code in createFragmentAndAddResources calls setDefersLoading(true). Don't we need that here?
498 if (DocumentLoader* loader = frame.loader().documentLoader())
499 loader->addAllArchiveResources(archive.get());
501 String markupString = String::fromUTF8(mainResource->data()->data(), mainResource->data()->size());
502 fragment = createFragmentFromMarkup(*frame.document(), markupString, mainResource->url(), DisallowScriptingAndPluginContent);
506 if (MIMETypeRegistry::isSupportedImageMIMEType(type)) {
507 fragment = frame.editor().createFragmentForImageResourceAndAddResource(mainResource.release());
514 bool Editor::WebContentReader::readFilenames(const Vector<String>& paths)
519 if (!frame.document())
521 Document& document = *frame.document();
523 fragment = document.createDocumentFragment();
525 for (auto& text : paths) {
526 #if ENABLE(ATTACHMENT_ELEMENT)
527 Ref<HTMLAttachmentElement> attachment = HTMLAttachmentElement::create(attachmentTag, document);
528 attachment->setFile(File::create([[NSURL fileURLWithPath:text] path]).ptr());
529 fragment->appendChild(WTFMove(attachment));
531 Ref<HTMLElement> paragraph = createDefaultParagraphElement(document);
532 paragraph->appendChild(document.createTextNode(frame.editor().client()->userVisibleString([NSURL fileURLWithPath:text])));
533 fragment->appendChild(WTFMove(paragraph));
540 bool Editor::WebContentReader::readHTML(const String& string)
542 String stringOmittingMicrosoftPrefix = string;
544 // This code was added to make HTML paste from Microsoft Word on Mac work, back in 2004.
545 // It's a simple-minded way to ignore the CF_HTML clipboard format, just skipping over the
546 // description part and parsing the entire context plus fragment.
547 if (string.startsWith("Version:")) {
548 size_t location = string.findIgnoringCase("<html");
549 if (location != notFound)
550 stringOmittingMicrosoftPrefix = string.substring(location);
553 if (stringOmittingMicrosoftPrefix.isEmpty())
556 if (!frame.document())
558 Document& document = *frame.document();
560 fragment = createFragmentFromMarkup(document, stringOmittingMicrosoftPrefix, emptyString(), DisallowScriptingAndPluginContent);
564 bool Editor::WebContentReader::readRTFD(PassRefPtr<SharedBuffer> buffer)
566 fragment = frame.editor().createFragmentAndAddResources(adoptNS([[NSAttributedString alloc] initWithRTFD:buffer->createNSData().get() documentAttributes:nullptr]).get());
570 bool Editor::WebContentReader::readRTF(PassRefPtr<SharedBuffer> buffer)
572 fragment = frame.editor().createFragmentAndAddResources(adoptNS([[NSAttributedString alloc] initWithRTF:buffer->createNSData().get() documentAttributes:nullptr]).get());
576 bool Editor::WebContentReader::readImage(PassRefPtr<SharedBuffer> buffer, const String& type)
578 ASSERT(type.contains('/'));
579 String typeAsFilenameWithExtension = type;
580 typeAsFilenameWithExtension.replace('/', '.');
581 URL imageURL = URL::fakeURLWithRelativePart(typeAsFilenameWithExtension);
583 fragment = frame.editor().createFragmentForImageResourceAndAddResource(ArchiveResource::create(buffer, imageURL, type, emptyString(), emptyString()));
587 bool Editor::WebContentReader::readURL(const URL& url, const String& title)
589 if (url.string().isEmpty())
592 Ref<Element> anchor = frame.document()->createElement(HTMLNames::aTag, false);
593 anchor->setAttribute(HTMLNames::hrefAttr, url.string());
594 anchor->appendChild(frame.document()->createTextNode([title precomposedStringWithCanonicalMapping]));
596 fragment = frame.document()->createDocumentFragment();
597 fragment->appendChild(WTFMove(anchor));
601 bool Editor::WebContentReader::readPlainText(const String& text)
606 fragment = createFragmentFromText(context, [text precomposedStringWithCanonicalMapping]);
610 madeFragmentFromPlainText = true;
614 // FIXME: Should give this function a name that makes it clear it adds resources to the document loader as a side effect.
615 // Or refactor so it does not do that.
616 PassRefPtr<DocumentFragment> Editor::webContentFromPasteboard(Pasteboard& pasteboard, Range& context, bool allowPlainText, bool& chosePlainText)
618 WebContentReader reader(m_frame, context, allowPlainText);
619 pasteboard.read(reader);
620 chosePlainText = reader.madeFragmentFromPlainText;
621 return reader.fragment.release();
624 PassRefPtr<DocumentFragment> Editor::createFragmentForImageResourceAndAddResource(PassRefPtr<ArchiveResource> resource)
629 // FIXME: The code in createFragmentAndAddResources calls setDefersLoading(true). Don't we need that here?
630 if (DocumentLoader* loader = m_frame.loader().documentLoader())
631 loader->addArchiveResource(resource.get());
633 Ref<Element> imageElement = document().createElement(HTMLNames::imgTag, false);
634 imageElement->setAttribute(HTMLNames::srcAttr, resource->url().string());
636 RefPtr<DocumentFragment> fragment = document().createDocumentFragment();
637 fragment->appendChild(WTFMove(imageElement));
639 return fragment.release();
642 PassRefPtr<DocumentFragment> Editor::createFragmentAndAddResources(NSAttributedString *string)
644 if (!m_frame.page() || !document().isHTMLDocument())
650 bool wasDeferringCallbacks = m_frame.page()->defersLoading();
651 if (!wasDeferringCallbacks)
652 m_frame.page()->setDefersLoading(true);
654 Vector<RefPtr<ArchiveResource>> resources;
655 RefPtr<DocumentFragment> fragment = client()->documentFragmentFromAttributedString(string, resources);
657 if (DocumentLoader* loader = m_frame.loader().documentLoader()) {
658 for (auto& resource : resources)
659 loader->addArchiveResource(resource);
662 if (!wasDeferringCallbacks)
663 m_frame.page()->setDefersLoading(false);
665 return fragment.release();
668 void Editor::replaceSelectionWithAttributedString(NSAttributedString *attributedString, MailBlockquoteHandling mailBlockquoteHandling)
670 if (m_frame.selection().isNone())
673 if (m_frame.selection().selection().isContentRichlyEditable()) {
674 RefPtr<DocumentFragment> fragment = createFragmentAndAddResources(attributedString);
675 if (fragment && shouldInsertFragment(fragment, selectedRange(), EditorInsertActionPasted))
676 pasteAsFragment(fragment, false, false, mailBlockquoteHandling);
678 String text = [attributedString string];
679 if (shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
680 pasteAsPlainText(text, false);
684 void Editor::applyFontStyles(const String& fontFamily, double fontSize, unsigned fontTraits)
686 auto& cssValuePool = CSSValuePool::singleton();
687 Ref<MutableStyleProperties> style = MutableStyleProperties::create();
688 style->setProperty(CSSPropertyFontFamily, cssValuePool.createFontFamilyValue(fontFamily));
689 style->setProperty(CSSPropertyFontStyle, (fontTraits & NSFontItalicTrait) ? CSSValueItalic : CSSValueNormal);
690 style->setProperty(CSSPropertyFontWeight, cssValuePool.createValue(fontTraits & NSFontBoldTrait ? FontWeightBold : FontWeightNormal));
691 style->setProperty(CSSPropertyFontSize, cssValuePool.createValue(fontSize, CSSPrimitiveValue::CSS_PX));
692 applyStyleToSelection(style.ptr(), EditActionSetFont);
695 } // namespace WebCore