layout/generic/nsFrameUtil.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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

mercurial