summaryrefslogtreecommitdiffstats
path: root/layout/base/nsQuoteList.cpp
blob: 19e4306ec86624c5ae4132bb66bf13ecec7516a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* implementation of quotes for the CSS 'content' property */

#include "nsQuoteList.h"
#include "nsReadableUtils.h"
#include "nsIContent.h"
#include "nsIFrame.h"
#include "nsIFrameInlines.h"
#include "nsContainerFrame.h"
#include "mozilla/ContainStyleScopeManager.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/Text.h"
#include "mozilla/intl/Quotes.h"

using namespace mozilla;

bool nsQuoteNode::InitTextFrame(nsGenConList* aList, nsIFrame* aPseudoFrame,
                                nsIFrame* aTextFrame) {
  nsGenConNode::InitTextFrame(aList, aPseudoFrame, aTextFrame);

  nsQuoteList* quoteList = static_cast<nsQuoteList*>(aList);
  bool dirty = false;
  quoteList->Insert(this);
  if (quoteList->IsLast(this))
    quoteList->Calc(this);
  else
    dirty = true;

  // Don't set up text for 'no-open-quote' and 'no-close-quote'.
  if (IsRealQuote()) {
    aTextFrame->GetContent()->AsText()->SetText(Text(), false);
  }
  return dirty;
}

nsString nsQuoteNode::Text() {
  NS_ASSERTION(mType == StyleContentType::OpenQuote ||
                   mType == StyleContentType::CloseQuote,
               "should only be called when mText should be non-null");
  nsString result;
  int32_t depth = Depth();
  MOZ_ASSERT(depth >= -1);

  if (depth < 0) {
    return result;
  }

  const auto& quotesProp = mPseudoFrame->StyleList()->mQuotes;

  if (quotesProp.IsAuto()) {
    // Look up CLDR-derived quotation marks for the language of the context.
    const nsIFrame* frame = mPseudoFrame->GetInFlowParent();
    // Parent of the pseudo is the element around which the quotes are applied;
    // we want lang from *its* parent, unless it is the root.
    // XXX Are there other cases where we shouldn't look up to the parent?
    if (!frame->Style()->IsRootElementStyle()) {
      if (const nsIFrame* parent = frame->GetInFlowParent()) {
        frame = parent;
      }
    }
    const intl::Quotes* quotes =
        intl::QuotesForLang(frame->StyleFont()->mLanguage);
    // If we don't have quote-mark data for the language, use built-in
    // defaults.
    if (!quotes) {
      static const intl::Quotes sDefaultQuotes = {
          {0x201c, 0x201d, 0x2018, 0x2019}};
      quotes = &sDefaultQuotes;
    }
    size_t index = (depth == 0 ? 0 : 2);  // select first or second pair
    index += (mType == StyleContentType::OpenQuote ? 0 : 1);  // open or close
    result.Append(quotes->mChars[index]);
    return result;
  }

  MOZ_ASSERT(quotesProp.IsQuoteList());
  const Span<const StyleQuotePair> quotes = quotesProp.AsQuoteList().AsSpan();

  // Reuse the last pair when the depth is greater than the number of
  // pairs of quotes.  (Also make 'quotes: none' and close-quote from
  // a depth of 0 equivalent for the next test.)
  if (depth >= static_cast<int32_t>(quotes.Length())) {
    depth = static_cast<int32_t>(quotes.Length()) - 1;
  }

  if (depth == -1) {
    // close-quote from a depth of 0 or 'quotes: none'
    return result;
  }

  const StyleQuotePair& pair = quotes[depth];
  const StyleOwnedStr& quote =
      mType == StyleContentType::OpenQuote ? pair.opening : pair.closing;
  result.Assign(NS_ConvertUTF8toUTF16(quote.AsString()));
  return result;
}

static int32_t GetDepthBeforeFirstQuoteNode(ContainStyleScope* aScope) {
  for (auto* ancestor = aScope->GetParent(); ancestor;
       ancestor = ancestor->GetParent()) {
    auto& quoteList = ancestor->GetQuoteList();
    if (auto* node = static_cast<nsQuoteNode*>(
            aScope->GetPrecedingElementInGenConList(&quoteList))) {
      return node->DepthAfter();
    }
  }
  return 0;
}

void nsQuoteList::Calc(nsQuoteNode* aNode) {
  if (aNode == FirstNode()) {
    aNode->mDepthBefore = GetDepthBeforeFirstQuoteNode(mScope);
  } else {
    aNode->mDepthBefore = Prev(aNode)->DepthAfter();
  }
}

void nsQuoteList::RecalcAll() {
  for (nsQuoteNode* node = FirstNode(); node; node = Next(node)) {
    int32_t oldDepth = node->mDepthBefore;
    Calc(node);

    if (node->mDepthBefore != oldDepth && node->mText && node->IsRealQuote())
      node->mText->SetData(node->Text(), IgnoreErrors());
  }
}

#ifdef DEBUG
void nsQuoteList::PrintChain() {
  using StyleContentType = nsQuoteNode::StyleContentType;

  printf("Chain: \n");
  for (nsQuoteNode* node = FirstNode(); node; node = Next(node)) {
    printf("  %p %d - ", static_cast<void*>(node), node->mDepthBefore);
    switch (node->mType) {
      case StyleContentType::OpenQuote:
        printf("open");
        break;
      case StyleContentType::NoOpenQuote:
        printf("noOpen");
        break;
      case StyleContentType::CloseQuote:
        printf("close");
        break;
      case StyleContentType::NoCloseQuote:
        printf("noClose");
        break;
      default:
        printf("unknown!!!");
    }
    printf(" %d - %d,", node->Depth(), node->DepthAfter());
    if (node->mText) {
      nsAutoString data;
      node->mText->GetData(data);
      printf(" \"%s\",", NS_ConvertUTF16toUTF8(data).get());
    }
    printf("\n");
  }
}
#endif