Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 public:
19 nsFrameUtil();
20 virtual ~nsFrameUtil();
22 NS_DECL_ISUPPORTS
24 NS_IMETHOD CompareRegressionData(FILE* aFile1, FILE* aFile2,int32_t aRegressionOutput=0);
25 NS_IMETHOD DumpRegressionData(FILE* aInputFile, FILE* aOutputFile);
27 struct Node;
28 struct Tag;
30 struct NodeList {
31 NodeList();
32 ~NodeList();
34 static void Destroy(NodeList* aLists);
36 NodeList* next; // for lists of lists
37 Node* node;
38 char* name;
39 };
41 struct Node {
42 Node();
43 ~Node();
45 static void Destroy(Node* aNode);
47 static Node* Read(FILE* aFile, Tag* aTag);
49 static Node* ReadTree(FILE* aFile);
51 Node* next;
52 char* type;
53 uint32_t state;
54 nsRect bbox;
55 nsCString styleData;
56 NodeList* lists;
57 };
59 struct Tag {
60 Tag();
61 ~Tag();
63 static Tag* Parse(FILE* aFile);
65 void AddAttr(char* aAttr, char* aValue);
67 const char* GetAttr(const char* aAttr);
69 void ReadAttrs(FILE* aFile);
71 void ToString(nsString& aResult);
73 enum Type {
74 open,
75 close,
76 openClose
77 };
79 char* name;
80 Type type;
81 char** attributes;
82 int32_t num;
83 int32_t size;
84 char** values;
85 };
87 static char* Copy(const char* aString);
89 static void DumpNode(Node* aNode, FILE* aOutputFile, int32_t aIndent);
90 static void DumpTree(Node* aNode, FILE* aOutputFile, int32_t aIndent);
91 static bool CompareTrees(Node* aNode1, Node* aNode2);
92 };
94 char*
95 nsFrameUtil::Copy(const char* aString)
96 {
97 if (aString) {
98 int l = ::strlen(aString);
99 char* c = new char[l+1];
100 if (!c)
101 return nullptr;
102 memcpy(c, aString, l+1);
103 return c;
104 }
105 return nullptr;
106 }
108 //----------------------------------------------------------------------
110 nsFrameUtil::NodeList::NodeList()
111 : next(nullptr), node(nullptr), name(nullptr)
112 {
113 }
115 nsFrameUtil::NodeList::~NodeList()
116 {
117 if (nullptr != name) {
118 delete name;
119 }
120 if (nullptr != node) {
121 Node::Destroy(node);
122 }
123 }
125 void
126 nsFrameUtil::NodeList::Destroy(NodeList* aLists)
127 {
128 while (nullptr != aLists) {
129 NodeList* next = aLists->next;
130 delete aLists;
131 aLists = next;
132 }
133 }
135 //----------------------------------------------------------------------
137 nsFrameUtil::Node::Node()
138 : next(nullptr), type(nullptr), state(0), lists(nullptr)
139 {
140 }
142 nsFrameUtil::Node::~Node()
143 {
144 if (nullptr != type) {
145 delete type;
146 }
147 if (nullptr != lists) {
148 NodeList::Destroy(lists);
149 }
150 }
152 void
153 nsFrameUtil::Node::Destroy(Node* aList)
154 {
155 while (nullptr != aList) {
156 Node* next = aList->next;
157 delete aList;
158 aList = next;
159 }
160 }
162 static int32_t GetInt(nsFrameUtil::Tag* aTag, const char* aAttr)
163 {
164 const char* value = aTag->GetAttr(aAttr);
165 if (nullptr != value) {
166 return int32_t( atoi(value) );
167 }
168 return 0;
169 }
171 nsFrameUtil::Node*
172 nsFrameUtil::Node::ReadTree(FILE* aFile)
173 {
174 Tag* tag = Tag::Parse(aFile);
175 if (nullptr == tag) {
176 return nullptr;
177 }
178 if (PL_strcmp(tag->name, "frame") != 0) {
179 delete tag;
180 return nullptr;
181 }
182 Node* result = Read(aFile, tag);
183 fclose(aFile);
184 return result;
185 }
187 nsFrameUtil::Node*
188 nsFrameUtil::Node::Read(FILE* aFile, Tag* tag)
189 {
190 Node* node = new Node;
191 node->type = Copy(tag->GetAttr("type"));
192 if (!node->type) {
193 /* crash() */
194 }
195 node->state = GetInt(tag, "state");
196 delete tag;
198 for (;;) {
199 tag = Tag::Parse(aFile);
200 if (nullptr == tag) break;
201 if (PL_strcmp(tag->name, "frame") == 0) {
202 delete tag;
203 break;
204 }
205 if (PL_strcmp(tag->name, "bbox") == 0) {
206 nscoord x = nscoord( GetInt(tag, "x") );
207 nscoord y = nscoord( GetInt(tag, "y") );
208 nscoord w = nscoord( GetInt(tag, "w") );
209 nscoord h = nscoord( GetInt(tag, "h") );
210 node->bbox.SetRect(x, y, w, h);
211 }
212 else if (PL_strcmp(tag->name, "child-list") == 0) {
213 NodeList* list = new NodeList();
214 list->name = Copy(tag->GetAttr("name"));
215 if (!list->name) {
216 /* crash() */
217 }
218 list->next = node->lists;
219 node->lists = list;
220 delete tag;
222 Node** tailp = &list->node;
223 for (;;) {
224 tag = Tag::Parse(aFile);
225 if (nullptr == tag) {
226 break;
227 }
228 if (PL_strcmp(tag->name, "child-list") == 0) {
229 break;
230 }
231 if (PL_strcmp(tag->name, "frame") != 0) {
232 break;
233 }
234 Node* child = Node::Read(aFile, tag);
235 if (nullptr == child) {
236 break;
237 }
238 *tailp = child;
239 tailp = &child->next;
240 }
241 }
242 else if((PL_strcmp(tag->name, "font") == 0) ||
243 (PL_strcmp(tag->name, "color") == 0) ||
244 (PL_strcmp(tag->name, "spacing") == 0) ||
245 (PL_strcmp(tag->name, "list") == 0) ||
246 (PL_strcmp(tag->name, "position") == 0) ||
247 (PL_strcmp(tag->name, "text") == 0) ||
248 (PL_strcmp(tag->name, "display") == 0) ||
249 (PL_strcmp(tag->name, "table") == 0) ||
250 (PL_strcmp(tag->name, "content") == 0) ||
251 (PL_strcmp(tag->name, "UI") == 0) ||
252 (PL_strcmp(tag->name, "print") == 0)) {
253 const char* attr = tag->GetAttr("data");
254 node->styleData.Append('|');
255 node->styleData.Append(attr ? attr : "null attr");
256 }
258 delete tag;
259 }
260 return node;
261 }
263 //----------------------------------------------------------------------
265 nsFrameUtil::Tag::Tag()
266 : name(nullptr), type(open), attributes(nullptr), num(0), size(0),
267 values(nullptr)
268 {
269 }
271 nsFrameUtil::Tag::~Tag()
272 {
273 int32_t i, n = num;
274 if (0 != n) {
275 for (i = 0; i < n; i++) {
276 delete attributes[i];
277 delete values[i];
278 }
279 delete attributes;
280 delete values;
281 }
282 }
284 void
285 nsFrameUtil::Tag::AddAttr(char* aAttr, char* aValue)
286 {
287 if (num == size) {
288 int32_t newSize = size * 2 + 4;
289 char** a = new char*[newSize];
290 char** v = new char*[newSize];
291 if (0 != num) {
292 memcpy(a, attributes, num * sizeof(char*));
293 memcpy(v, values, num * sizeof(char*));
294 delete attributes;
295 delete values;
296 }
297 attributes = a;
298 values = v;
299 size = newSize;
300 }
301 attributes[num] = aAttr;
302 values[num] = aValue;
303 num = num + 1;
304 }
306 const char*
307 nsFrameUtil::Tag::GetAttr(const char* aAttr)
308 {
309 int32_t i, n = num;
310 for (i = 0; i < n; i++) {
311 if (PL_strcmp(attributes[i], aAttr) == 0) {
312 return values[i];
313 }
314 }
315 return nullptr;
316 }
318 static inline int IsWhiteSpace(int c) {
319 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
320 }
322 static bool EatWS(FILE* aFile)
323 {
324 for (;;) {
325 int c = getc(aFile);
326 if (c < 0) {
327 return false;
328 }
329 if (!IsWhiteSpace(c)) {
330 ungetc(c, aFile);
331 break;
332 }
333 }
334 return true;
335 }
337 static bool Expect(FILE* aFile, char aChar)
338 {
339 int c = getc(aFile);
340 if (c < 0) return false;
341 if (c != aChar) {
342 ungetc(c, aFile);
343 return false;
344 }
345 return true;
346 }
348 static char* ReadIdent(FILE* aFile)
349 {
350 char id[1000];
351 char* ip = id;
352 char* end = ip + sizeof(id) - 1;
353 while (ip < end) {
354 int c = fgetc(aFile);
355 if (c < 0) return nullptr;
356 if ((c == '=') || (c == '>') || (c == '/') || IsWhiteSpace(c)) {
357 ungetc(c, aFile);
358 break;
359 }
360 *ip++ = char(c);
361 }
362 *ip = '\0';
363 return nsFrameUtil::Copy(id);
364 /* may return a null pointer */
365 }
367 static char* ReadString(FILE* aFile)
368 {
369 if (!Expect(aFile, '\"')) {
370 return nullptr;
371 }
372 char id[1000];
373 char* ip = id;
374 char* end = ip + sizeof(id) - 1;
375 while (ip < end) {
376 int c = fgetc(aFile);
377 if (c < 0) return nullptr;
378 if (c == '\"') {
379 break;
380 }
381 *ip++ = char(c);
382 }
383 *ip = '\0';
384 return nsFrameUtil::Copy(id);
385 /* may return a null pointer */
386 }
388 void
389 nsFrameUtil::Tag::ReadAttrs(FILE* aFile)
390 {
391 for (;;) {
392 if (!EatWS(aFile)) {
393 break;
394 }
395 int c = getc(aFile);
396 if (c < 0) break;
397 if (c == '/') {
398 if (!EatWS(aFile)) {
399 return;
400 }
401 if (Expect(aFile, '>')) {
402 type = openClose;
403 break;
404 }
405 }
406 else if (c == '>') {
407 break;
408 }
409 ungetc(c, aFile);
410 char* attr = ReadIdent(aFile);
411 if ((nullptr == attr) || !EatWS(aFile)) {
412 break;
413 }
414 char* value = nullptr;
415 if (Expect(aFile, '=')) {
416 value = ReadString(aFile);
417 if (nullptr == value) {
418 delete [] attr;
419 break;
420 }
421 }
422 AddAttr(attr, value);
423 }
424 }
426 nsFrameUtil::Tag*
427 nsFrameUtil::Tag::Parse(FILE* aFile)
428 {
429 if (!EatWS(aFile)) {
430 return nullptr;
431 }
432 if (Expect(aFile, '<')) {
433 Tag* tag = new Tag;
434 if (Expect(aFile, '/')) {
435 tag->type = close;
436 }
437 else {
438 tag->type = open;
439 }
440 tag->name = ReadIdent(aFile);
441 tag->ReadAttrs(aFile);
442 return tag;
443 }
444 return nullptr;
445 }
447 void
448 nsFrameUtil::Tag::ToString(nsString& aResult)
449 {
450 aResult.Truncate();
451 aResult.Append(char16_t('<'));
452 if (type == close) {
453 aResult.Append(char16_t('/'));
454 }
455 aResult.AppendASCII(name);
456 if (0 != num) {
457 int32_t i, n = num;
458 for (i = 0; i < n; i++) {
459 aResult.Append(char16_t(' '));
460 aResult.AppendASCII(attributes[i]);
461 if (values[i]) {
462 aResult.AppendLiteral("=\"");
463 aResult.AppendASCII(values[i]);
464 aResult.Append(char16_t('\"'));
465 }
466 }
467 }
468 if (type == openClose) {
469 aResult.Append(char16_t('/'));
470 }
471 aResult.Append(char16_t('>'));
472 }
474 //----------------------------------------------------------------------
476 nsresult
477 NS_NewFrameUtil(nsIFrameUtil** aResult)
478 {
479 NS_PRECONDITION(nullptr != aResult, "null pointer");
480 if (nullptr == aResult) {
481 return NS_ERROR_NULL_POINTER;
482 }
484 nsFrameUtil* it = new nsFrameUtil();
486 NS_ADDREF(*aResult = it);
487 return NS_OK;
488 }
490 nsFrameUtil::nsFrameUtil()
491 {
492 }
494 nsFrameUtil::~nsFrameUtil()
495 {
496 }
498 NS_IMPL_ISUPPORTS(nsFrameUtil, nsIFrameUtil)
500 void
501 nsFrameUtil::DumpNode(Node* aNode, FILE* aOutputFile, int32_t aIndent)
502 {
503 nsFrame::IndentBy(aOutputFile, aIndent);
504 fprintf(aOutputFile, "%s 0x%x %d,%d,%d,%d, %s\n", aNode->type, aNode->state,
505 aNode->bbox.x, aNode->bbox.y,
506 aNode->bbox.width, aNode->bbox.height,
507 aNode->styleData.get());
508 }
510 void
511 nsFrameUtil::DumpTree(Node* aNode, FILE* aOutputFile, int32_t aIndent)
512 {
513 while (nullptr != aNode) {
514 DumpNode(aNode, aOutputFile, aIndent);
515 nsFrameUtil::NodeList* lists = aNode->lists;
516 if (nullptr != lists) {
517 while (nullptr != lists) {
518 nsFrame::IndentBy(aOutputFile, aIndent);
519 fprintf(aOutputFile, " list: %s\n",
520 lists->name ? lists->name : "primary");
521 DumpTree(lists->node, aOutputFile, aIndent + 1);
522 lists = lists->next;
523 }
524 }
525 aNode = aNode->next;
526 }
527 }
529 bool
530 nsFrameUtil::CompareTrees(Node* tree1, Node* tree2)
531 {
532 bool result = true;
533 for (;; tree1 = tree1->next, tree2 = tree2->next) {
534 // Make sure both nodes are non-null, or at least agree with each other
535 if (nullptr == tree1) {
536 if (nullptr == tree2) {
537 break;
538 }
539 printf("first tree prematurely ends\n");
540 return false;
541 }
542 else if (nullptr == tree2) {
543 printf("second tree prematurely ends\n");
544 return false;
545 }
547 // Check the attributes that we care about
548 if (0 != PL_strcmp(tree1->type, tree2->type)) {
549 printf("frame type mismatch: %s vs. %s\n", tree1->type, tree2->type);
550 printf("Node 1:\n");
551 DumpNode(tree1, stdout, 1);
552 printf("Node 2:\n");
553 DumpNode(tree2, stdout, 1);
554 return false;
555 }
557 // Ignore the XUL scrollbar frames
558 static const char kScrollbarFrame[] = "ScrollbarFrame";
559 if (0 == PL_strncmp(tree1->type, kScrollbarFrame, sizeof(kScrollbarFrame) - 1))
560 continue;
562 if (tree1->state != tree2->state) {
563 printf("frame state mismatch: 0x%x vs. 0x%x\n",
564 tree1->state, tree2->state);
565 printf("Node 1:\n");
566 DumpNode(tree1, stdout, 1);
567 printf("Node 2:\n");
568 DumpNode(tree2, stdout, 1);
569 result = false; // we have a non-critical failure, so remember that but continue
570 }
571 if (tree1->bbox.IsEqualInterior(tree2->bbox)) {
572 printf("frame bbox mismatch: %d,%d,%d,%d vs. %d,%d,%d,%d\n",
573 tree1->bbox.x, tree1->bbox.y,
574 tree1->bbox.width, tree1->bbox.height,
575 tree2->bbox.x, tree2->bbox.y,
576 tree2->bbox.width, tree2->bbox.height);
577 printf("Node 1:\n");
578 DumpNode(tree1, stdout, 1);
579 printf("Node 2:\n");
580 DumpNode(tree2, stdout, 1);
581 result = false; // we have a non-critical failure, so remember that but continue
582 }
583 if (tree1->styleData != tree2->styleData) {
584 printf("frame style data mismatch: %s vs. %s\n",
585 tree1->styleData.get(),
586 tree2->styleData.get());
587 }
589 // Check child lists too
590 NodeList* list1 = tree1->lists;
591 NodeList* list2 = tree2->lists;
592 for (;;) {
593 if (nullptr == list1) {
594 if (nullptr != list2) {
595 printf("first tree prematurely ends (no child lists)\n");
596 printf("Node 1:\n");
597 DumpNode(tree1, stdout, 1);
598 printf("Node 2:\n");
599 DumpNode(tree2, stdout, 1);
600 return false;
601 }
602 else {
603 break;
604 }
605 }
606 if (nullptr == list2) {
607 printf("second tree prematurely ends (no child lists)\n");
608 printf("Node 1:\n");
609 DumpNode(tree1, stdout, 1);
610 printf("Node 2:\n");
611 DumpNode(tree2, stdout, 1);
612 return false;
613 }
614 if (0 != PL_strcmp(list1->name, list2->name)) {
615 printf("child-list name mismatch: %s vs. %s\n",
616 list1->name ? list1->name : "(null)",
617 list2->name ? list2->name : "(null)");
618 result = false; // we have a non-critical failure, so remember that but continue
619 }
620 else {
621 bool equiv = CompareTrees(list1->node, list2->node);
622 if (!equiv) {
623 return equiv;
624 }
625 }
626 list1 = list1->next;
627 list2 = list2->next;
628 }
629 }
630 return result;
631 }
633 NS_IMETHODIMP
634 nsFrameUtil::CompareRegressionData(FILE* aFile1, FILE* aFile2,int32_t aRegressionOutput)
635 {
636 Node* tree1 = Node::ReadTree(aFile1);
637 Node* tree2 = Node::ReadTree(aFile2);
639 nsresult rv = NS_OK;
640 if (!CompareTrees(tree1, tree2)) {
641 // only output this if aRegressionOutput is 0
642 if( 0 == aRegressionOutput ){
643 printf("Regression data 1:\n");
644 DumpTree(tree1, stdout, 0);
645 printf("Regression data 2:\n");
646 DumpTree(tree2, stdout, 0);
647 }
648 rv = NS_ERROR_FAILURE;
649 }
651 Node::Destroy(tree1);
652 Node::Destroy(tree2);
654 return rv;
655 }
657 NS_IMETHODIMP
658 nsFrameUtil::DumpRegressionData(FILE* aInputFile, FILE* aOutputFile)
659 {
660 Node* tree1 = Node::ReadTree(aInputFile);
661 if (nullptr != tree1) {
662 DumpTree(tree1, aOutputFile, 0);
663 Node::Destroy(tree1);
664 return NS_OK;
665 }
666 return NS_ERROR_FAILURE;
667 }
668 #endif