Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / nsFrameUtil.cpp
blob0e21caf0d38b6e32af8b6d9f478008a3d0c220db
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* utilities for regression tests based on frame tree comparison */
8 #include "nsIFrameUtil.h"
9 #include "nsFrame.h"
10 #include "nsString.h"
11 #include "nsRect.h"
12 #include <stdlib.h>
13 #include "plstr.h"
16 #ifdef DEBUG
17 class nsFrameUtil : public nsIFrameUtil {
18 protected:
19 virtual ~nsFrameUtil();
21 public:
22 nsFrameUtil();
24 NS_DECL_ISUPPORTS
26 NS_IMETHOD CompareRegressionData(FILE* aFile1, FILE* aFile2,int32_t aRegressionOutput=0) MOZ_OVERRIDE;
27 NS_IMETHOD DumpRegressionData(FILE* aInputFile, FILE* aOutputFile) MOZ_OVERRIDE;
29 struct Node;
30 struct Tag;
32 struct NodeList {
33 NodeList();
34 ~NodeList();
36 static void Destroy(NodeList* aLists);
38 NodeList* next; // for lists of lists
39 Node* node;
40 char* name;
43 struct Node {
44 Node();
45 ~Node();
47 static void Destroy(Node* aNode);
49 static Node* Read(FILE* aFile, Tag* aTag);
51 static Node* ReadTree(FILE* aFile);
53 Node* next;
54 char* type;
55 uint32_t state;
56 nsRect bbox;
57 nsCString styleData;
58 NodeList* lists;
61 struct Tag {
62 Tag();
63 ~Tag();
65 static Tag* Parse(FILE* aFile);
67 void AddAttr(char* aAttr, char* aValue);
69 const char* GetAttr(const char* aAttr);
71 void ReadAttrs(FILE* aFile);
73 void ToString(nsString& aResult);
75 enum Type {
76 open,
77 close,
78 openClose
81 char* name;
82 Type type;
83 char** attributes;
84 int32_t num;
85 int32_t size;
86 char** values;
89 static char* Copy(const char* aString);
91 static void DumpNode(Node* aNode, FILE* aOutputFile, int32_t aIndent);
92 static void DumpTree(Node* aNode, FILE* aOutputFile, int32_t aIndent);
93 static bool CompareTrees(Node* aNode1, Node* aNode2);
96 char*
97 nsFrameUtil::Copy(const char* aString)
99 if (aString) {
100 int l = ::strlen(aString);
101 char* c = new char[l+1];
102 if (!c)
103 return nullptr;
104 memcpy(c, aString, l+1);
105 return c;
107 return nullptr;
110 //----------------------------------------------------------------------
112 nsFrameUtil::NodeList::NodeList()
113 : next(nullptr), node(nullptr), name(nullptr)
117 nsFrameUtil::NodeList::~NodeList()
119 if (nullptr != name) {
120 delete name;
122 if (nullptr != node) {
123 Node::Destroy(node);
127 void
128 nsFrameUtil::NodeList::Destroy(NodeList* aLists)
130 while (nullptr != aLists) {
131 NodeList* next = aLists->next;
132 delete aLists;
133 aLists = next;
137 //----------------------------------------------------------------------
139 nsFrameUtil::Node::Node()
140 : next(nullptr), type(nullptr), state(0), lists(nullptr)
144 nsFrameUtil::Node::~Node()
146 if (nullptr != type) {
147 delete type;
149 if (nullptr != lists) {
150 NodeList::Destroy(lists);
154 void
155 nsFrameUtil::Node::Destroy(Node* aList)
157 while (nullptr != aList) {
158 Node* next = aList->next;
159 delete aList;
160 aList = next;
164 static int32_t GetInt(nsFrameUtil::Tag* aTag, const char* aAttr)
166 const char* value = aTag->GetAttr(aAttr);
167 if (nullptr != value) {
168 return int32_t( atoi(value) );
170 return 0;
173 nsFrameUtil::Node*
174 nsFrameUtil::Node::ReadTree(FILE* aFile)
176 Tag* tag = Tag::Parse(aFile);
177 if (nullptr == tag) {
178 return nullptr;
180 if (PL_strcmp(tag->name, "frame") != 0) {
181 delete tag;
182 return nullptr;
184 Node* result = Read(aFile, tag);
185 fclose(aFile);
186 return result;
189 nsFrameUtil::Node*
190 nsFrameUtil::Node::Read(FILE* aFile, Tag* tag)
192 Node* node = new Node;
193 node->type = Copy(tag->GetAttr("type"));
194 if (!node->type) {
195 /* crash() */
197 node->state = GetInt(tag, "state");
198 delete tag;
200 for (;;) {
201 tag = Tag::Parse(aFile);
202 if (nullptr == tag) break;
203 if (PL_strcmp(tag->name, "frame") == 0) {
204 delete tag;
205 break;
207 if (PL_strcmp(tag->name, "bbox") == 0) {
208 nscoord x = nscoord( GetInt(tag, "x") );
209 nscoord y = nscoord( GetInt(tag, "y") );
210 nscoord w = nscoord( GetInt(tag, "w") );
211 nscoord h = nscoord( GetInt(tag, "h") );
212 node->bbox.SetRect(x, y, w, h);
214 else if (PL_strcmp(tag->name, "child-list") == 0) {
215 NodeList* list = new NodeList();
216 list->name = Copy(tag->GetAttr("name"));
217 if (!list->name) {
218 /* crash() */
220 list->next = node->lists;
221 node->lists = list;
222 delete tag;
224 Node** tailp = &list->node;
225 for (;;) {
226 tag = Tag::Parse(aFile);
227 if (nullptr == tag) {
228 break;
230 if (PL_strcmp(tag->name, "child-list") == 0) {
231 break;
233 if (PL_strcmp(tag->name, "frame") != 0) {
234 break;
236 Node* child = Node::Read(aFile, tag);
237 if (nullptr == child) {
238 break;
240 *tailp = child;
241 tailp = &child->next;
244 else if((PL_strcmp(tag->name, "font") == 0) ||
245 (PL_strcmp(tag->name, "color") == 0) ||
246 (PL_strcmp(tag->name, "spacing") == 0) ||
247 (PL_strcmp(tag->name, "list") == 0) ||
248 (PL_strcmp(tag->name, "position") == 0) ||
249 (PL_strcmp(tag->name, "text") == 0) ||
250 (PL_strcmp(tag->name, "display") == 0) ||
251 (PL_strcmp(tag->name, "table") == 0) ||
252 (PL_strcmp(tag->name, "content") == 0) ||
253 (PL_strcmp(tag->name, "UI") == 0) ||
254 (PL_strcmp(tag->name, "print") == 0)) {
255 const char* attr = tag->GetAttr("data");
256 node->styleData.Append('|');
257 node->styleData.Append(attr ? attr : "null attr");
260 delete tag;
262 return node;
265 //----------------------------------------------------------------------
267 nsFrameUtil::Tag::Tag()
268 : name(nullptr), type(open), attributes(nullptr), num(0), size(0),
269 values(nullptr)
273 nsFrameUtil::Tag::~Tag()
275 int32_t i, n = num;
276 if (0 != n) {
277 for (i = 0; i < n; i++) {
278 delete attributes[i];
279 delete values[i];
281 delete attributes;
282 delete values;
286 void
287 nsFrameUtil::Tag::AddAttr(char* aAttr, char* aValue)
289 if (num == size) {
290 int32_t newSize = size * 2 + 4;
291 char** a = new char*[newSize];
292 char** v = new char*[newSize];
293 if (0 != num) {
294 memcpy(a, attributes, num * sizeof(char*));
295 memcpy(v, values, num * sizeof(char*));
296 delete attributes;
297 delete values;
299 attributes = a;
300 values = v;
301 size = newSize;
303 attributes[num] = aAttr;
304 values[num] = aValue;
305 num = num + 1;
308 const char*
309 nsFrameUtil::Tag::GetAttr(const char* aAttr)
311 int32_t i, n = num;
312 for (i = 0; i < n; i++) {
313 if (PL_strcmp(attributes[i], aAttr) == 0) {
314 return values[i];
317 return nullptr;
320 static inline int IsWhiteSpace(int c) {
321 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
324 static bool EatWS(FILE* aFile)
326 for (;;) {
327 int c = getc(aFile);
328 if (c < 0) {
329 return false;
331 if (!IsWhiteSpace(c)) {
332 ungetc(c, aFile);
333 break;
336 return true;
339 static bool Expect(FILE* aFile, char aChar)
341 int c = getc(aFile);
342 if (c < 0) return false;
343 if (c != aChar) {
344 ungetc(c, aFile);
345 return false;
347 return true;
350 static char* ReadIdent(FILE* aFile)
352 char id[1000];
353 char* ip = id;
354 char* end = ip + sizeof(id) - 1;
355 while (ip < end) {
356 int c = fgetc(aFile);
357 if (c < 0) return nullptr;
358 if ((c == '=') || (c == '>') || (c == '/') || IsWhiteSpace(c)) {
359 ungetc(c, aFile);
360 break;
362 *ip++ = char(c);
364 *ip = '\0';
365 return nsFrameUtil::Copy(id);
366 /* may return a null pointer */
369 static char* ReadString(FILE* aFile)
371 if (!Expect(aFile, '\"')) {
372 return nullptr;
374 char id[1000];
375 char* ip = id;
376 char* end = ip + sizeof(id) - 1;
377 while (ip < end) {
378 int c = fgetc(aFile);
379 if (c < 0) return nullptr;
380 if (c == '\"') {
381 break;
383 *ip++ = char(c);
385 *ip = '\0';
386 return nsFrameUtil::Copy(id);
387 /* may return a null pointer */
390 void
391 nsFrameUtil::Tag::ReadAttrs(FILE* aFile)
393 for (;;) {
394 if (!EatWS(aFile)) {
395 break;
397 int c = getc(aFile);
398 if (c < 0) break;
399 if (c == '/') {
400 if (!EatWS(aFile)) {
401 return;
403 if (Expect(aFile, '>')) {
404 type = openClose;
405 break;
408 else if (c == '>') {
409 break;
411 ungetc(c, aFile);
412 char* attr = ReadIdent(aFile);
413 if ((nullptr == attr) || !EatWS(aFile)) {
414 break;
416 char* value = nullptr;
417 if (Expect(aFile, '=')) {
418 value = ReadString(aFile);
419 if (nullptr == value) {
420 delete [] attr;
421 break;
424 AddAttr(attr, value);
428 nsFrameUtil::Tag*
429 nsFrameUtil::Tag::Parse(FILE* aFile)
431 if (!EatWS(aFile)) {
432 return nullptr;
434 if (Expect(aFile, '<')) {
435 Tag* tag = new Tag;
436 if (Expect(aFile, '/')) {
437 tag->type = close;
439 else {
440 tag->type = open;
442 tag->name = ReadIdent(aFile);
443 tag->ReadAttrs(aFile);
444 return tag;
446 return nullptr;
449 void
450 nsFrameUtil::Tag::ToString(nsString& aResult)
452 aResult.Truncate();
453 aResult.Append(char16_t('<'));
454 if (type == close) {
455 aResult.Append(char16_t('/'));
457 aResult.AppendASCII(name);
458 if (0 != num) {
459 int32_t i, n = num;
460 for (i = 0; i < n; i++) {
461 aResult.Append(char16_t(' '));
462 aResult.AppendASCII(attributes[i]);
463 if (values[i]) {
464 aResult.AppendLiteral("=\"");
465 aResult.AppendASCII(values[i]);
466 aResult.Append(char16_t('\"'));
470 if (type == openClose) {
471 aResult.Append(char16_t('/'));
473 aResult.Append(char16_t('>'));
476 //----------------------------------------------------------------------
478 nsresult
479 NS_NewFrameUtil(nsIFrameUtil** aResult)
481 NS_PRECONDITION(nullptr != aResult, "null pointer");
482 if (nullptr == aResult) {
483 return NS_ERROR_NULL_POINTER;
486 nsFrameUtil* it = new nsFrameUtil();
488 NS_ADDREF(*aResult = it);
489 return NS_OK;
492 nsFrameUtil::nsFrameUtil()
496 nsFrameUtil::~nsFrameUtil()
500 NS_IMPL_ISUPPORTS(nsFrameUtil, nsIFrameUtil)
502 void
503 nsFrameUtil::DumpNode(Node* aNode, FILE* aOutputFile, int32_t aIndent)
505 nsFrame::IndentBy(aOutputFile, aIndent);
506 fprintf(aOutputFile, "%s 0x%x %d,%d,%d,%d, %s\n", aNode->type, aNode->state,
507 aNode->bbox.x, aNode->bbox.y,
508 aNode->bbox.width, aNode->bbox.height,
509 aNode->styleData.get());
512 void
513 nsFrameUtil::DumpTree(Node* aNode, FILE* aOutputFile, int32_t aIndent)
515 while (nullptr != aNode) {
516 DumpNode(aNode, aOutputFile, aIndent);
517 nsFrameUtil::NodeList* lists = aNode->lists;
518 if (nullptr != lists) {
519 while (nullptr != lists) {
520 nsFrame::IndentBy(aOutputFile, aIndent);
521 fprintf(aOutputFile, " list: %s\n",
522 lists->name ? lists->name : "primary");
523 DumpTree(lists->node, aOutputFile, aIndent + 1);
524 lists = lists->next;
527 aNode = aNode->next;
531 bool
532 nsFrameUtil::CompareTrees(Node* tree1, Node* tree2)
534 bool result = true;
535 for (;; tree1 = tree1->next, tree2 = tree2->next) {
536 // Make sure both nodes are non-null, or at least agree with each other
537 if (nullptr == tree1) {
538 if (nullptr == tree2) {
539 break;
541 printf("first tree prematurely ends\n");
542 return false;
544 else if (nullptr == tree2) {
545 printf("second tree prematurely ends\n");
546 return false;
549 // Check the attributes that we care about
550 if (0 != PL_strcmp(tree1->type, tree2->type)) {
551 printf("frame type mismatch: %s vs. %s\n", tree1->type, tree2->type);
552 printf("Node 1:\n");
553 DumpNode(tree1, stdout, 1);
554 printf("Node 2:\n");
555 DumpNode(tree2, stdout, 1);
556 return false;
559 // Ignore the XUL scrollbar frames
560 static const char kScrollbarFrame[] = "ScrollbarFrame";
561 if (0 == PL_strncmp(tree1->type, kScrollbarFrame, sizeof(kScrollbarFrame) - 1))
562 continue;
564 if (tree1->state != tree2->state) {
565 printf("frame state mismatch: 0x%x vs. 0x%x\n",
566 tree1->state, tree2->state);
567 printf("Node 1:\n");
568 DumpNode(tree1, stdout, 1);
569 printf("Node 2:\n");
570 DumpNode(tree2, stdout, 1);
571 result = false; // we have a non-critical failure, so remember that but continue
573 if (tree1->bbox.IsEqualInterior(tree2->bbox)) {
574 printf("frame bbox mismatch: %d,%d,%d,%d vs. %d,%d,%d,%d\n",
575 tree1->bbox.x, tree1->bbox.y,
576 tree1->bbox.width, tree1->bbox.height,
577 tree2->bbox.x, tree2->bbox.y,
578 tree2->bbox.width, tree2->bbox.height);
579 printf("Node 1:\n");
580 DumpNode(tree1, stdout, 1);
581 printf("Node 2:\n");
582 DumpNode(tree2, stdout, 1);
583 result = false; // we have a non-critical failure, so remember that but continue
585 if (tree1->styleData != tree2->styleData) {
586 printf("frame style data mismatch: %s vs. %s\n",
587 tree1->styleData.get(),
588 tree2->styleData.get());
591 // Check child lists too
592 NodeList* list1 = tree1->lists;
593 NodeList* list2 = tree2->lists;
594 for (;;) {
595 if (nullptr == list1) {
596 if (nullptr != list2) {
597 printf("first tree prematurely ends (no child lists)\n");
598 printf("Node 1:\n");
599 DumpNode(tree1, stdout, 1);
600 printf("Node 2:\n");
601 DumpNode(tree2, stdout, 1);
602 return false;
604 else {
605 break;
608 if (nullptr == list2) {
609 printf("second tree prematurely ends (no child lists)\n");
610 printf("Node 1:\n");
611 DumpNode(tree1, stdout, 1);
612 printf("Node 2:\n");
613 DumpNode(tree2, stdout, 1);
614 return false;
616 if (0 != PL_strcmp(list1->name, list2->name)) {
617 printf("child-list name mismatch: %s vs. %s\n",
618 list1->name ? list1->name : "(null)",
619 list2->name ? list2->name : "(null)");
620 result = false; // we have a non-critical failure, so remember that but continue
622 else {
623 bool equiv = CompareTrees(list1->node, list2->node);
624 if (!equiv) {
625 return equiv;
628 list1 = list1->next;
629 list2 = list2->next;
632 return result;
635 NS_IMETHODIMP
636 nsFrameUtil::CompareRegressionData(FILE* aFile1, FILE* aFile2,int32_t aRegressionOutput)
638 Node* tree1 = Node::ReadTree(aFile1);
639 Node* tree2 = Node::ReadTree(aFile2);
641 nsresult rv = NS_OK;
642 if (!CompareTrees(tree1, tree2)) {
643 // only output this if aRegressionOutput is 0
644 if( 0 == aRegressionOutput ){
645 printf("Regression data 1:\n");
646 DumpTree(tree1, stdout, 0);
647 printf("Regression data 2:\n");
648 DumpTree(tree2, stdout, 0);
650 rv = NS_ERROR_FAILURE;
653 Node::Destroy(tree1);
654 Node::Destroy(tree2);
656 return rv;
659 NS_IMETHODIMP
660 nsFrameUtil::DumpRegressionData(FILE* aInputFile, FILE* aOutputFile)
662 Node* tree1 = Node::ReadTree(aInputFile);
663 if (nullptr != tree1) {
664 DumpTree(tree1, aOutputFile, 0);
665 Node::Destroy(tree1);
666 return NS_OK;
668 return NS_ERROR_FAILURE;
670 #endif