nsQuoteList.cpp (5358B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* implementation of quotes for the CSS 'content' property */ 8 9 #include "nsQuoteList.h" 10 11 #include "mozilla/ContainStyleScopeManager.h" 12 #include "mozilla/ErrorResult.h" 13 #include "mozilla/dom/Text.h" 14 #include "mozilla/intl/Quotes.h" 15 #include "nsContainerFrame.h" 16 #include "nsIContent.h" 17 #include "nsIFrame.h" 18 #include "nsIFrameInlines.h" 19 #include "nsReadableUtils.h" 20 21 using namespace mozilla; 22 23 bool nsQuoteNode::InitTextFrame(nsGenConList* aList, nsIFrame* aPseudoFrame, 24 nsIFrame* aTextFrame) { 25 nsGenConNode::InitTextFrame(aList, aPseudoFrame, aTextFrame); 26 27 nsQuoteList* quoteList = static_cast<nsQuoteList*>(aList); 28 bool dirty = false; 29 quoteList->Insert(this); 30 if (quoteList->IsLast(this)) { 31 quoteList->Calc(this); 32 } else { 33 dirty = true; 34 } 35 36 // Don't set up text for 'no-open-quote' and 'no-close-quote'. 37 if (IsRealQuote()) { 38 aTextFrame->GetContent()->AsText()->SetText(Text(), false); 39 } 40 return dirty; 41 } 42 43 nsString nsQuoteNode::Text() { 44 NS_ASSERTION(mType == StyleContentType::OpenQuote || 45 mType == StyleContentType::CloseQuote, 46 "should only be called when mText should be non-null"); 47 nsString result; 48 int32_t depth = Depth(); 49 MOZ_ASSERT(depth >= -1); 50 51 if (depth < 0) { 52 return result; 53 } 54 55 const auto& quotesProp = mPseudoFrame->StyleList()->mQuotes; 56 57 if (quotesProp.IsAuto()) { 58 // Look up CLDR-derived quotation marks for the language of the context. 59 const nsIFrame* frame = mPseudoFrame->GetInFlowParent(); 60 // Parent of the pseudo is the element around which the quotes are applied; 61 // we want lang from *its* parent, unless it is the root. 62 // XXX Are there other cases where we shouldn't look up to the parent? 63 if (!frame->Style()->IsRootElementStyle()) { 64 if (const nsIFrame* parent = frame->GetInFlowParent()) { 65 frame = parent; 66 } 67 } 68 const intl::Quotes* quotes = 69 intl::QuotesForLang(frame->StyleFont()->mLanguage); 70 // If we don't have quote-mark data for the language, use built-in 71 // defaults. 72 if (!quotes) { 73 static const intl::Quotes sDefaultQuotes = { 74 {0x201c, 0x201d, 0x2018, 0x2019}}; 75 quotes = &sDefaultQuotes; 76 } 77 size_t index = (depth == 0 ? 0 : 2); // select first or second pair 78 index += (mType == StyleContentType::OpenQuote ? 0 : 1); // open or close 79 result.Append(quotes->mChars[index]); 80 return result; 81 } 82 83 MOZ_ASSERT(quotesProp.IsQuoteList()); 84 const Span<const StyleQuotePair> quotes = quotesProp.AsQuoteList().AsSpan(); 85 86 // Reuse the last pair when the depth is greater than the number of 87 // pairs of quotes. (Also make 'quotes: none' and close-quote from 88 // a depth of 0 equivalent for the next test.) 89 if (depth >= static_cast<int32_t>(quotes.Length())) { 90 depth = static_cast<int32_t>(quotes.Length()) - 1; 91 } 92 93 if (depth == -1) { 94 // close-quote from a depth of 0 or 'quotes: none' 95 return result; 96 } 97 98 const StyleQuotePair& pair = quotes[depth]; 99 const StyleOwnedStr& quote = 100 mType == StyleContentType::OpenQuote ? pair.opening : pair.closing; 101 result.Assign(NS_ConvertUTF8toUTF16(quote.AsString())); 102 return result; 103 } 104 105 static int32_t GetDepthBeforeFirstQuoteNode(ContainStyleScope* aScope) { 106 for (auto* ancestor = aScope->GetParent(); ancestor; 107 ancestor = ancestor->GetParent()) { 108 auto& quoteList = ancestor->GetQuoteList(); 109 if (auto* node = static_cast<nsQuoteNode*>( 110 aScope->GetPrecedingElementInGenConList("eList))) { 111 return node->DepthAfter(); 112 } 113 } 114 return 0; 115 } 116 117 void nsQuoteList::Calc(nsQuoteNode* aNode) { 118 if (aNode == FirstNode()) { 119 aNode->mDepthBefore = GetDepthBeforeFirstQuoteNode(mScope); 120 } else { 121 aNode->mDepthBefore = Prev(aNode)->DepthAfter(); 122 } 123 } 124 125 void nsQuoteList::RecalcAll() { 126 for (nsQuoteNode* node = FirstNode(); node; node = Next(node)) { 127 int32_t oldDepth = node->mDepthBefore; 128 Calc(node); 129 130 if (node->mDepthBefore != oldDepth && node->mText && node->IsRealQuote()) { 131 node->mText->SetData(node->Text(), IgnoreErrors()); 132 } 133 } 134 } 135 136 #ifdef DEBUG 137 void nsQuoteList::PrintChain() { 138 using StyleContentType = nsQuoteNode::StyleContentType; 139 140 printf("Chain: \n"); 141 for (nsQuoteNode* node = FirstNode(); node; node = Next(node)) { 142 printf(" %p %d - ", static_cast<void*>(node), node->mDepthBefore); 143 switch (node->mType) { 144 case StyleContentType::OpenQuote: 145 printf("open"); 146 break; 147 case StyleContentType::NoOpenQuote: 148 printf("noOpen"); 149 break; 150 case StyleContentType::CloseQuote: 151 printf("close"); 152 break; 153 case StyleContentType::NoCloseQuote: 154 printf("noClose"); 155 break; 156 default: 157 printf("unknown!!!"); 158 } 159 printf(" %d - %d,", node->Depth(), node->DepthAfter()); 160 if (node->mText) { 161 nsAutoString data; 162 node->mText->GetData(data); 163 printf(" \"%s\",", NS_ConvertUTF16toUTF8(data).get()); 164 } 165 printf("\n"); 166 } 167 } 168 #endif