~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/layout/base/src/nsLayoutUtils.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
2
/* ***** BEGIN LICENSE BLOCK *****
 
3
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 
4
 *
 
5
 * The contents of this file are subject to the Netscape Public License
 
6
 * Version 1.1 (the "License"); you may not use this file except in
 
7
 * compliance with the License. You may obtain a copy of the License at
 
8
 * http://www.mozilla.org/NPL/
 
9
 *
 
10
 * Software distributed under the License is distributed on an "AS IS" basis,
 
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
12
 * for the specific language governing rights and limitations under the
 
13
 * License.
 
14
 *
 
15
 * The Original Code is mozilla.org code.
 
16
 *
 
17
 * The Initial Developer of the Original Code is Netscape
 
18
 * Communications Corporation.  Portions created by Netscape are
 
19
 * Copyright (C) 1998 Netscape Communications Corporation. All
 
20
 * Rights Reserved.
 
21
 *
 
22
 * Contributor(s): 
 
23
 *
 
24
 * Alternatively, the contents of this file may be used under the terms of
 
25
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 
26
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
27
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
28
 * of those above. If you wish to allow use of your version of this file only
 
29
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
30
 * use your version of this file under the terms of the NPL, indicate your
 
31
 * decision by deleting the provisions above and replace them with the notice
 
32
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
33
 * the provisions above, a recipient may use your version of this file under
 
34
 * the terms of any one of the NPL, the GPL or the LGPL.
 
35
 *
 
36
 * ***** END LICENSE BLOCK ***** */
 
37
 
 
38
#include "nsLayoutUtils.h"
 
39
#include "nsIFrame.h"
 
40
#include "nsIPresContext.h"
 
41
#include "nsIContent.h"
 
42
#include "nsFrameList.h"
 
43
#include "nsLayoutAtoms.h"
 
44
#include "nsIAtom.h"
 
45
#include "nsCSSPseudoElements.h"
 
46
#include "nsIView.h"
 
47
 
 
48
/**
 
49
 * A namespace class for static layout utilities.
 
50
 */
 
51
 
 
52
/**
 
53
 * GetFirstChildFrame returns the first "real" child frame of a
 
54
 * given frame.  It will descend down into pseudo-frames (unless the
 
55
 * pseudo-frame is the :before generated frame).   
 
56
 * @param aPresContext the prescontext
 
57
 * @param aFrame the frame
 
58
 * @param aFrame the frame's content node
 
59
 */
 
60
static nsIFrame*
 
61
GetFirstChildFrame(nsIPresContext* aPresContext,
 
62
                   nsIFrame*       aFrame,
 
63
                   nsIContent*     aContent)
 
64
{
 
65
  NS_PRECONDITION(aFrame, "NULL frame pointer");
 
66
 
 
67
  // Get the first child frame
 
68
  nsIFrame* childFrame = aFrame->GetFirstChild(nsnull);
 
69
 
 
70
  // If the child frame is a pseudo-frame, then return its first child.
 
71
  // Note that the frame we create for the generated content is also a
 
72
  // pseudo-frame and so don't drill down in that case
 
73
  if (childFrame &&
 
74
      childFrame->IsPseudoFrame(aContent) &&
 
75
      !childFrame->IsGeneratedContentFrame()) {
 
76
    return GetFirstChildFrame(aPresContext, childFrame, aContent);
 
77
  }
 
78
 
 
79
  return childFrame;
 
80
}
 
81
 
 
82
/**
 
83
 * GetLastChildFrame returns the last "real" child frame of a
 
84
 * given frame.  It will descend down into pseudo-frames (unless the
 
85
 * pseudo-frame is the :after generated frame).   
 
86
 * @param aPresContext the prescontext
 
87
 * @param aFrame the frame
 
88
 * @param aFrame the frame's content node
 
89
 */
 
90
static nsIFrame*
 
91
GetLastChildFrame(nsIPresContext* aPresContext,
 
92
                  nsIFrame*       aFrame,
 
93
                  nsIContent*     aContent)
 
94
{
 
95
  NS_PRECONDITION(aFrame, "NULL frame pointer");
 
96
 
 
97
  // Get the last in flow frame
 
98
  nsIFrame* lastInFlow = aFrame->GetLastInFlow();
 
99
 
 
100
  // Get the last child frame
 
101
  nsIFrame* firstChildFrame = lastInFlow->GetFirstChild(nsnull);
 
102
  if (firstChildFrame) {
 
103
    nsFrameList frameList(firstChildFrame);
 
104
    nsIFrame*   lastChildFrame = frameList.LastChild();
 
105
 
 
106
    NS_ASSERTION(lastChildFrame, "unexpected error");
 
107
 
 
108
    // Get the frame's first-in-flow. This matters in case the frame has
 
109
    // been continuted across multiple lines
 
110
    lastChildFrame = lastChildFrame->GetFirstInFlow();
 
111
    
 
112
    // If the last child frame is a pseudo-frame, then return its last child.
 
113
    // Note that the frame we create for the generated content is also a
 
114
    // pseudo-frame and so don't drill down in that case
 
115
    if (lastChildFrame &&
 
116
        lastChildFrame->IsPseudoFrame(aContent) &&
 
117
        !lastChildFrame->IsGeneratedContentFrame()) {
 
118
      return GetLastChildFrame(aPresContext, lastChildFrame, aContent);
 
119
    }
 
120
 
 
121
    return lastChildFrame;
 
122
  }
 
123
 
 
124
  return nsnull;
 
125
}
 
126
 
 
127
// static
 
128
nsIFrame*
 
129
nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame, nsIPresContext* aPresContext)
 
130
{
 
131
  NS_PRECONDITION(aFrame, "NULL frame pointer");
 
132
#ifdef DEBUG
 
133
  nsIFrame* prevInFlow = nsnull;
 
134
  aFrame->GetPrevInFlow(&prevInFlow);
 
135
  NS_ASSERTION(!prevInFlow, "aFrame must be first-in-flow");
 
136
#endif
 
137
  
 
138
  nsIFrame* firstFrame = GetFirstChildFrame(aPresContext, aFrame, aFrame->GetContent());
 
139
 
 
140
  if (firstFrame && IsGeneratedContentFor(nsnull, firstFrame,
 
141
                                          nsCSSPseudoElements::before)) {
 
142
    return firstFrame;
 
143
  }
 
144
 
 
145
  return nsnull;
 
146
}
 
147
 
 
148
// static
 
149
nsIFrame*
 
150
nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame, nsIPresContext* aPresContext)
 
151
{
 
152
  NS_PRECONDITION(aFrame, "NULL frame pointer");
 
153
 
 
154
  nsIFrame* lastFrame = GetLastChildFrame(aPresContext, aFrame, aFrame->GetContent());
 
155
 
 
156
  if (lastFrame && IsGeneratedContentFor(nsnull, lastFrame,
 
157
                                         nsCSSPseudoElements::after)) {
 
158
    return lastFrame;
 
159
  }
 
160
 
 
161
  return nsnull;
 
162
}
 
163
 
 
164
// static
 
165
nsIFrame*
 
166
nsLayoutUtils::GetPageFrame(nsIFrame* aFrame)
 
167
{
 
168
  for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
 
169
    if (frame->GetType() == nsLayoutAtoms::pageFrame) {
 
170
      return frame;
 
171
    }
 
172
  }
 
173
  return nsnull;
 
174
}
 
175
 
 
176
// static
 
177
PRBool
 
178
nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent,
 
179
                                     nsIFrame* aFrame,
 
180
                                     nsIAtom* aPseudoElement)
 
181
{
 
182
  NS_PRECONDITION(aFrame, "Must have a frame");
 
183
  NS_PRECONDITION(aPseudoElement, "Must have a pseudo name");
 
184
 
 
185
  if (!aFrame->IsGeneratedContentFrame()) {
 
186
    return PR_FALSE;
 
187
  }
 
188
  
 
189
  if (aContent && aFrame->GetContent() != aContent) {
 
190
    return PR_FALSE;
 
191
  }
 
192
 
 
193
  return aFrame->GetStyleContext()->GetPseudoType() == aPseudoElement;
 
194
}
 
195
 
 
196
// static
 
197
PRBool
 
198
nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
 
199
                                     nsIFrame* aCommonAncestor)
 
200
{
 
201
  if (aFrame == aCommonAncestor) {
 
202
    return PR_FALSE;
 
203
  }
 
204
  
 
205
  nsIFrame* parentFrame = aFrame->GetParent();
 
206
 
 
207
  while (parentFrame != aCommonAncestor) {
 
208
    if (parentFrame == aAncestorFrame) {
 
209
      return PR_TRUE;
 
210
    }
 
211
 
 
212
    parentFrame = parentFrame->GetParent();
 
213
  }
 
214
 
 
215
  return PR_FALSE;
 
216
}
 
217
 
 
218
// static
 
219
PRInt32
 
220
nsLayoutUtils::CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2,
 
221
                                   nsIContent* aCommonAncestor) {
 
222
  NS_PRECONDITION(aContent1, "aContent1 must not be null");
 
223
  NS_PRECONDITION(aContent2, "aContent2 must not be null");
 
224
 
 
225
  nsAutoVoidArray content1Ancestors;
 
226
  nsIContent* c1;
 
227
  for (c1 = aContent1; c1 && c1 != aCommonAncestor; c1 = c1->GetParent()) {
 
228
    content1Ancestors.AppendElement(c1);
 
229
  }
 
230
  if (!c1 && aCommonAncestor) {
 
231
    // So, it turns out aCommonAncestor was not an ancestor of c1. Oops.
 
232
    // Never mind. We can continue as if aCommonAncestor was null.
 
233
    aCommonAncestor = nsnull;
 
234
  }
 
235
 
 
236
  nsAutoVoidArray content2Ancestors;
 
237
  nsIContent* c2;
 
238
  for (c2 = aContent2; c2 && c2 != aCommonAncestor; c2 = c2->GetParent()) {
 
239
    content2Ancestors.AppendElement(c2);
 
240
  }
 
241
  if (!c2 && aCommonAncestor) {
 
242
    // So, it turns out aCommonAncestor was not an ancestor of c2.
 
243
    // We need to retry with no common ancestor hint.
 
244
    return CompareTreePosition(aContent1, aContent2, nsnull);
 
245
  }
 
246
  
 
247
  int last1 = content1Ancestors.Count() - 1;
 
248
  int last2 = content2Ancestors.Count() - 1;
 
249
  nsIContent* content1Ancestor = nsnull;
 
250
  nsIContent* content2Ancestor = nsnull;
 
251
  while (last1 >= 0 && last2 >= 0
 
252
         && ((content1Ancestor = NS_STATIC_CAST(nsIContent*, content1Ancestors.ElementAt(last1)))
 
253
             == (content2Ancestor = NS_STATIC_CAST(nsIContent*, content2Ancestors.ElementAt(last2))))) {
 
254
    last1--;
 
255
    last2--;
 
256
  }
 
257
 
 
258
  if (last1 < 0) {
 
259
    if (last2 < 0) {
 
260
      NS_ASSERTION(aContent1 == aContent2, "internal error?");
 
261
      return 0;
 
262
    } else {
 
263
      // aContent1 is an ancestor of aContent2
 
264
      return -1;
 
265
    }
 
266
  } else {
 
267
    if (last2 < 0) {
 
268
      // aContent2 is an ancestor of aContent1
 
269
      return 1;
 
270
    } else {
 
271
      // content1Ancestor != content2Ancestor, so they must be siblings with the same parent
 
272
      nsIContent* parent = content1Ancestor->GetParent();
 
273
      NS_ASSERTION(parent, "no common ancestor at all???");
 
274
      if (!parent) { // different documents??
 
275
        return 0;
 
276
      }
 
277
 
 
278
      PRInt32 index1 = parent->IndexOf(content1Ancestor);
 
279
      PRInt32 index2 = parent->IndexOf(content2Ancestor);
 
280
      if (index1 < 0 || index2 < 0) {
 
281
        // one of them must be anonymous; we can't determine the order
 
282
        return 0;
 
283
      }
 
284
 
 
285
      return index1 - index2;
 
286
    }
 
287
  }
 
288
}
 
289
 
 
290
// static
 
291
nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) {
 
292
  if (!aFrame) {
 
293
    return nsnull;
 
294
  }
 
295
 
 
296
  nsIFrame* next;
 
297
  while ((next = aFrame->GetNextSibling()) != nsnull) {
 
298
    aFrame = next;
 
299
  }
 
300
  return aFrame;
 
301
}
 
302
 
 
303
// static
 
304
nsIView*
 
305
nsLayoutUtils::FindSiblingViewFor(nsIView* aParentView, nsIFrame* aFrame) {
 
306
  nsIFrame* parentViewFrame = NS_STATIC_CAST(nsIFrame*, aParentView->GetClientData());
 
307
  nsIContent* parentViewContent = parentViewFrame ? parentViewFrame->GetContent() : nsnull;
 
308
  for (nsIView* insertBefore = aParentView->GetFirstChild(); insertBefore;
 
309
       insertBefore = insertBefore->GetNextSibling()) {
 
310
    nsIFrame* f = NS_STATIC_CAST(nsIFrame*, insertBefore->GetClientData());
 
311
    if (!f) {
 
312
      // this view could be some anonymous view attached to a meaningful parent
 
313
      for (nsIView* searchView = insertBefore->GetParent(); searchView;
 
314
           searchView = searchView->GetParent()) {
 
315
        f = NS_STATIC_CAST(nsIFrame*, searchView->GetClientData());
 
316
        if (f) {
 
317
          break;
 
318
        }
 
319
      }
 
320
      NS_ASSERTION(f, "Can't find a frame anywhere!");
 
321
    }
 
322
    if (f && CompareTreePosition(aFrame->GetContent(),
 
323
                                 f->GetContent(), parentViewContent) > 0) {
 
324
      // aFrame's content is after f's content, so put our view before f's view
 
325
      return insertBefore;
 
326
    }
 
327
  }
 
328
  return nsnull;
 
329
}