1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
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
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or 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 MPL, 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 MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 /* utilities for regression tests based on frame tree comparison */
40 #include "nsIFrameUtil.h"
49 class nsFrameUtil
: public nsIFrameUtil
{
52 virtual ~nsFrameUtil();
56 NS_IMETHOD
CompareRegressionData(FILE* aFile1
, FILE* aFile2
,PRInt32 aRegressionOutput
=0);
57 NS_IMETHOD
DumpRegressionData(FILE* aInputFile
, FILE* aOutputFile
);
66 static void Destroy(NodeList
* aLists
);
68 NodeList
* next
; // for lists of lists
77 static void Destroy(Node
* aNode
);
79 static Node
* Read(FILE* aFile
, Tag
* aTag
);
81 static Node
* ReadTree(FILE* aFile
);
95 static Tag
* Parse(FILE* aFile
);
97 void AddAttr(char* aAttr
, char* aValue
);
99 const char* GetAttr(const char* aAttr
);
101 void ReadAttrs(FILE* aFile
);
103 void ToString(nsString
& aResult
);
119 static char* Copy(const char* aString
);
121 static void DumpNode(Node
* aNode
, FILE* aOutputFile
, PRInt32 aIndent
);
122 static void DumpTree(Node
* aNode
, FILE* aOutputFile
, PRInt32 aIndent
);
123 static PRBool
CompareTrees(Node
* aNode1
, Node
* aNode2
);
127 nsFrameUtil::Copy(const char* aString
)
130 int l
= ::strlen(aString
);
131 char* c
= new char[l
+1];
134 memcpy(c
, aString
, l
+1);
140 //----------------------------------------------------------------------
142 nsFrameUtil::NodeList::NodeList()
143 : next(nsnull
), node(nsnull
), name(nsnull
)
147 nsFrameUtil::NodeList::~NodeList()
149 if (nsnull
!= name
) {
152 if (nsnull
!= node
) {
158 nsFrameUtil::NodeList::Destroy(NodeList
* aLists
)
160 while (nsnull
!= aLists
) {
161 NodeList
* next
= aLists
->next
;
167 //----------------------------------------------------------------------
169 nsFrameUtil::Node::Node()
170 : next(nsnull
), type(nsnull
), state(0), lists(nsnull
)
174 nsFrameUtil::Node::~Node()
176 if (nsnull
!= type
) {
179 if (nsnull
!= lists
) {
180 NodeList::Destroy(lists
);
185 nsFrameUtil::Node::Destroy(Node
* aList
)
187 while (nsnull
!= aList
) {
188 Node
* next
= aList
->next
;
194 static PRInt32
GetInt(nsFrameUtil::Tag
* aTag
, const char* aAttr
)
196 const char* value
= aTag
->GetAttr(aAttr
);
197 if (nsnull
!= value
) {
198 return PRInt32( atoi(value
) );
204 nsFrameUtil::Node::ReadTree(FILE* aFile
)
206 Tag
* tag
= Tag::Parse(aFile
);
210 if (PL_strcmp(tag
->name
, "frame") != 0) {
214 Node
* result
= Read(aFile
, tag
);
220 nsFrameUtil::Node::Read(FILE* aFile
, Tag
* tag
)
222 Node
* node
= new Node
;
226 node
->type
= Copy(tag
->GetAttr("type"));
230 node
->state
= GetInt(tag
, "state");
234 tag
= Tag::Parse(aFile
);
235 if (nsnull
== tag
) break;
236 if (PL_strcmp(tag
->name
, "frame") == 0) {
240 if (PL_strcmp(tag
->name
, "bbox") == 0) {
241 nscoord x
= nscoord( GetInt(tag
, "x") );
242 nscoord y
= nscoord( GetInt(tag
, "y") );
243 nscoord w
= nscoord( GetInt(tag
, "w") );
244 nscoord h
= nscoord( GetInt(tag
, "h") );
245 node
->bbox
.SetRect(x
, y
, w
, h
);
247 else if (PL_strcmp(tag
->name
, "child-list") == 0) {
248 NodeList
* list
= new NodeList();
249 list
->name
= Copy(tag
->GetAttr("name"));
253 list
->next
= node
->lists
;
257 Node
** tailp
= &list
->node
;
259 tag
= Tag::Parse(aFile
);
263 if (PL_strcmp(tag
->name
, "child-list") == 0) {
266 if (PL_strcmp(tag
->name
, "frame") != 0) {
269 Node
* child
= Node::Read(aFile
, tag
);
270 if (nsnull
== child
) {
274 tailp
= &child
->next
;
277 else if((PL_strcmp(tag
->name
, "font") == 0) ||
278 (PL_strcmp(tag
->name
, "color") == 0) ||
279 (PL_strcmp(tag
->name
, "spacing") == 0) ||
280 (PL_strcmp(tag
->name
, "list") == 0) ||
281 (PL_strcmp(tag
->name
, "position") == 0) ||
282 (PL_strcmp(tag
->name
, "text") == 0) ||
283 (PL_strcmp(tag
->name
, "display") == 0) ||
284 (PL_strcmp(tag
->name
, "table") == 0) ||
285 (PL_strcmp(tag
->name
, "content") == 0) ||
286 (PL_strcmp(tag
->name
, "UI") == 0) ||
287 (PL_strcmp(tag
->name
, "print") == 0)) {
288 const char* attr
= tag
->GetAttr("data");
289 node
->styleData
.Append('|');
290 node
->styleData
.Append(attr
? attr
: "null attr");
298 //----------------------------------------------------------------------
300 nsFrameUtil::Tag::Tag()
301 : name(nsnull
), type(open
), attributes(nsnull
), num(0), size(0),
306 nsFrameUtil::Tag::~Tag()
310 for (i
= 0; i
< n
; i
++) {
311 delete attributes
[i
];
320 nsFrameUtil::Tag::AddAttr(char* aAttr
, char* aValue
)
323 PRInt32 newSize
= size
* 2 + 4;
324 char** a
= new char*[newSize
];
325 char** v
= new char*[newSize
];
327 memcpy(a
, attributes
, num
* sizeof(char*));
328 memcpy(v
, values
, num
* sizeof(char*));
336 attributes
[num
] = aAttr
;
337 values
[num
] = aValue
;
342 nsFrameUtil::Tag::GetAttr(const char* aAttr
)
345 for (i
= 0; i
< n
; i
++) {
346 if (PL_strcmp(attributes
[i
], aAttr
) == 0) {
353 static inline int IsWhiteSpace(int c
) {
354 return (c
== ' ') || (c
== '\t') || (c
== '\n') || (c
== '\r');
357 static PRBool
EatWS(FILE* aFile
)
364 if (!IsWhiteSpace(c
)) {
372 static PRBool
Expect(FILE* aFile
, char aChar
)
375 if (c
< 0) return PR_FALSE
;
383 static char* ReadIdent(FILE* aFile
)
387 char* end
= ip
+ sizeof(id
) - 1;
389 int c
= fgetc(aFile
);
390 if (c
< 0) return nsnull
;
391 if ((c
== '=') || (c
== '>') || (c
== '/') || IsWhiteSpace(c
)) {
398 return nsFrameUtil::Copy(id
);
399 /* may return a null pointer */
402 static char* ReadString(FILE* aFile
)
404 if (!Expect(aFile
, '\"')) {
409 char* end
= ip
+ sizeof(id
) - 1;
411 int c
= fgetc(aFile
);
412 if (c
< 0) return nsnull
;
419 return nsFrameUtil::Copy(id
);
420 /* may return a null pointer */
424 nsFrameUtil::Tag::ReadAttrs(FILE* aFile
)
436 if (Expect(aFile
, '>')) {
445 char* attr
= ReadIdent(aFile
);
446 if ((nsnull
== attr
) || !EatWS(aFile
)) {
449 char* value
= nsnull
;
450 if (Expect(aFile
, '=')) {
451 value
= ReadString(aFile
);
452 if (nsnull
== value
) {
457 AddAttr(attr
, value
);
462 nsFrameUtil::Tag::Parse(FILE* aFile
)
467 if (Expect(aFile
, '<')) {
469 if (Expect(aFile
, '/')) {
475 tag
->name
= ReadIdent(aFile
);
476 tag
->ReadAttrs(aFile
);
483 nsFrameUtil::Tag::ToString(nsString
& aResult
)
486 aResult
.Append(PRUnichar('<'));
488 aResult
.Append(PRUnichar('/'));
490 aResult
.AppendASCII(name
);
493 for (i
= 0; i
< n
; i
++) {
494 aResult
.Append(PRUnichar(' '));
495 aResult
.AppendASCII(attributes
[i
]);
497 aResult
.AppendLiteral("=\"");
498 aResult
.AppendASCII(values
[i
]);
499 aResult
.Append(PRUnichar('\"'));
503 if (type
== openClose
) {
504 aResult
.Append(PRUnichar('/'));
506 aResult
.Append(PRUnichar('>'));
509 //----------------------------------------------------------------------
511 nsresult
NS_NewFrameUtil(nsIFrameUtil
** aResult
);
513 NS_NewFrameUtil(nsIFrameUtil
** aResult
)
515 NS_PRECONDITION(nsnull
!= aResult
, "null pointer");
516 if (nsnull
== aResult
) {
517 return NS_ERROR_NULL_POINTER
;
521 nsFrameUtil
* it
= new nsFrameUtil();
523 return NS_ERROR_OUT_OF_MEMORY
;
525 return it
->QueryInterface(NS_GET_IID(nsIFrameUtil
), (void**) aResult
);
528 nsFrameUtil::nsFrameUtil()
532 nsFrameUtil::~nsFrameUtil()
536 NS_IMPL_ISUPPORTS1(nsFrameUtil
, nsIFrameUtil
)
539 nsFrameUtil::DumpNode(Node
* aNode
, FILE* aOutputFile
, PRInt32 aIndent
)
541 nsFrame::IndentBy(aOutputFile
, aIndent
);
542 fprintf(aOutputFile
, "%s 0x%x %d,%d,%d,%d, %s\n", aNode
->type
, aNode
->state
,
543 aNode
->bbox
.x
, aNode
->bbox
.y
,
544 aNode
->bbox
.width
, aNode
->bbox
.height
,
545 aNode
->styleData
.get());
549 nsFrameUtil::DumpTree(Node
* aNode
, FILE* aOutputFile
, PRInt32 aIndent
)
551 while (nsnull
!= aNode
) {
552 DumpNode(aNode
, aOutputFile
, aIndent
);
553 nsFrameUtil::NodeList
* lists
= aNode
->lists
;
554 if (nsnull
!= lists
) {
555 while (nsnull
!= lists
) {
556 nsFrame::IndentBy(aOutputFile
, aIndent
);
557 fprintf(aOutputFile
, " list: %s\n",
558 lists
->name
? lists
->name
: "primary");
559 DumpTree(lists
->node
, aOutputFile
, aIndent
+ 1);
568 nsFrameUtil::CompareTrees(Node
* tree1
, Node
* tree2
)
570 PRBool result
= PR_TRUE
;
571 for (;; tree1
= tree1
->next
, tree2
= tree2
->next
) {
572 // Make sure both nodes are non-null, or at least agree with each other
573 if (nsnull
== tree1
) {
574 if (nsnull
== tree2
) {
577 printf("first tree prematurely ends\n");
580 else if (nsnull
== tree2
) {
581 printf("second tree prematurely ends\n");
585 // Check the attributes that we care about
586 if (0 != PL_strcmp(tree1
->type
, tree2
->type
)) {
587 printf("frame type mismatch: %s vs. %s\n", tree1
->type
, tree2
->type
);
589 DumpNode(tree1
, stdout
, 1);
591 DumpNode(tree2
, stdout
, 1);
595 // Ignore the XUL scrollbar frames
596 static const char kScrollbarFrame
[] = "ScrollbarFrame";
597 if (0 == PL_strncmp(tree1
->type
, kScrollbarFrame
, sizeof(kScrollbarFrame
) - 1))
600 if (tree1
->state
!= tree2
->state
) {
601 printf("frame state mismatch: 0x%x vs. 0x%x\n",
602 tree1
->state
, tree2
->state
);
604 DumpNode(tree1
, stdout
, 1);
606 DumpNode(tree2
, stdout
, 1);
607 result
= PR_FALSE
; // we have a non-critical failure, so remember that but continue
609 if (tree1
->bbox
!= tree2
->bbox
) {
610 printf("frame bbox mismatch: %d,%d,%d,%d vs. %d,%d,%d,%d\n",
611 tree1
->bbox
.x
, tree1
->bbox
.y
,
612 tree1
->bbox
.width
, tree1
->bbox
.height
,
613 tree2
->bbox
.x
, tree2
->bbox
.y
,
614 tree2
->bbox
.width
, tree2
->bbox
.height
);
616 DumpNode(tree1
, stdout
, 1);
618 DumpNode(tree2
, stdout
, 1);
619 result
= PR_FALSE
; // we have a non-critical failure, so remember that but continue
621 if (tree1
->styleData
!= tree2
->styleData
) {
622 printf("frame style data mismatch: %s vs. %s\n",
623 tree1
->styleData
.get(),
624 tree2
->styleData
.get());
627 // Check child lists too
628 NodeList
* list1
= tree1
->lists
;
629 NodeList
* list2
= tree2
->lists
;
631 if (nsnull
== list1
) {
632 if (nsnull
!= list2
) {
633 printf("first tree prematurely ends (no child lists)\n");
635 DumpNode(tree1
, stdout
, 1);
637 DumpNode(tree2
, stdout
, 1);
644 if (nsnull
== list2
) {
645 printf("second tree prematurely ends (no child lists)\n");
647 DumpNode(tree1
, stdout
, 1);
649 DumpNode(tree2
, stdout
, 1);
652 if (0 != PL_strcmp(list1
->name
, list2
->name
)) {
653 printf("child-list name mismatch: %s vs. %s\n",
654 list1
->name
? list1
->name
: "(null)",
655 list2
->name
? list2
->name
: "(null)");
656 result
= PR_FALSE
; // we have a non-critical failure, so remember that but continue
659 PRBool equiv
= CompareTrees(list1
->node
, list2
->node
);
672 nsFrameUtil::CompareRegressionData(FILE* aFile1
, FILE* aFile2
,PRInt32 aRegressionOutput
)
674 Node
* tree1
= Node::ReadTree(aFile1
);
675 Node
* tree2
= Node::ReadTree(aFile2
);
678 if (!CompareTrees(tree1
, tree2
)) {
679 // only output this if aRegressionOutput is 0
680 if( 0 == aRegressionOutput
){
681 printf("Regression data 1:\n");
682 DumpTree(tree1
, stdout
, 0);
683 printf("Regression data 2:\n");
684 DumpTree(tree2
, stdout
, 0);
686 rv
= NS_ERROR_FAILURE
;
689 Node::Destroy(tree1
);
690 Node::Destroy(tree2
);
696 nsFrameUtil::DumpRegressionData(FILE* aInputFile
, FILE* aOutputFile
)
698 Node
* tree1
= Node::ReadTree(aInputFile
);
699 if (nsnull
!= tree1
) {
700 DumpTree(tree1
, aOutputFile
, 0);
701 Node::Destroy(tree1
);
704 return NS_ERROR_FAILURE
;