Back to index

lightning-sunbird  0.9+nobinonly
nsFrameUtil.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "nsIFrameUtil.h"
00038 #include "nsFrame.h"
00039 #include "nsString.h"
00040 #include "nsRect.h"
00041 #include <stdlib.h>
00042 #include "plstr.h"
00043 
00044 
00045 #ifdef NS_DEBUG
00046 class nsFrameUtil : public nsIFrameUtil {
00047 public:
00048   nsFrameUtil();
00049   virtual ~nsFrameUtil();
00050 
00051   NS_DECL_ISUPPORTS
00052 
00053   NS_IMETHOD CompareRegressionData(FILE* aFile1, FILE* aFile2,PRInt32 aRegressionOutput=0);
00054   NS_IMETHOD DumpRegressionData(FILE* aInputFile, FILE* aOutputFile);
00055 
00056   struct Node;
00057   struct Tag;
00058 
00059   struct NodeList {
00060     NodeList();
00061     ~NodeList();
00062 
00063     static void Destroy(NodeList* aLists);
00064 
00065     NodeList* next;            // for lists of lists
00066     Node* node;
00067     char* name;
00068   };
00069 
00070   struct Node {
00071     Node();
00072     ~Node();
00073 
00074     static void Destroy(Node* aNode);
00075 
00076     static Node* Read(FILE* aFile, Tag* aTag);
00077 
00078     static Node* ReadTree(FILE* aFile);
00079 
00080     Node* next;
00081     char* type;
00082     PRUint32 state;
00083     nsRect bbox;
00084     nsCString styleData;
00085     NodeList* lists;
00086   };
00087 
00088   struct Tag {
00089     Tag();
00090     ~Tag();
00091 
00092     static Tag* Parse(FILE* aFile);
00093 
00094     void AddAttr(char* aAttr, char* aValue);
00095 
00096     char* GetAttr(char* aAttr);
00097 
00098     void ReadAttrs(FILE* aFile);
00099 
00100     void ToString(nsString& aResult);
00101 
00102     enum Type {
00103       open,
00104       close,
00105       openClose
00106     };
00107 
00108     char* name;
00109     Type type;
00110     char** attributes;
00111     PRInt32 num;
00112     PRInt32 size;
00113     char** values;
00114   };
00115 
00116   static char* Copy(char* aString);
00117 
00118   static void DumpNode(Node* aNode, FILE* aOutputFile, PRInt32 aIndent);
00119   static void DumpTree(Node* aNode, FILE* aOutputFile, PRInt32 aIndent);
00120   static PRBool CompareTrees(Node* aNode1, Node* aNode2);
00121 };
00122 
00123 char*
00124 nsFrameUtil::Copy(char* aString)
00125 {
00126   if (aString) {
00127     int l = ::strlen(aString);
00128     char* c = new char[l+1];
00129     memcpy(c, aString, l+1);
00130     return c;
00131   }
00132   return aString;
00133 }
00134 
00135 //----------------------------------------------------------------------
00136 
00137 nsFrameUtil::NodeList::NodeList()
00138   : next(nsnull), node(nsnull), name(nsnull)
00139 {
00140 }
00141 
00142 nsFrameUtil::NodeList::~NodeList()
00143 {
00144   if (nsnull != name) {
00145     delete name;
00146   }
00147   if (nsnull != node) {
00148     Node::Destroy(node);
00149   }
00150 }
00151 
00152 void
00153 nsFrameUtil::NodeList::Destroy(NodeList* aLists)
00154 {
00155   while (nsnull != aLists) {
00156     NodeList* next = aLists->next;
00157     delete aLists;
00158     aLists = next;
00159   }
00160 }
00161 
00162 //----------------------------------------------------------------------
00163 
00164 nsFrameUtil::Node::Node()
00165   : next(nsnull), type(nsnull), state(0), lists(nsnull)
00166 {
00167 }
00168 
00169 nsFrameUtil::Node::~Node()
00170 {
00171   if (nsnull != type) {
00172     delete type;
00173   }
00174   if (nsnull != lists) {
00175     NodeList::Destroy(lists);
00176   }
00177 }
00178 
00179 void
00180 nsFrameUtil::Node::Destroy(Node* aList)
00181 {
00182   while (nsnull != aList) {
00183     Node* next = aList->next;
00184     delete aList;
00185     aList = next;
00186   }
00187 }
00188 
00189 static PRInt32 GetInt(nsFrameUtil::Tag* aTag, char* aAttr)
00190 {
00191   char* value = aTag->GetAttr(aAttr);
00192   if (nsnull != value) {
00193     return PRInt32( atoi(value) );
00194   }
00195   return 0;
00196 }
00197 
00198 nsFrameUtil::Node*
00199 nsFrameUtil::Node::ReadTree(FILE* aFile)
00200 {
00201   Tag* tag = Tag::Parse(aFile);
00202   if (nsnull == tag) {
00203     return nsnull;
00204   }
00205   if (PL_strcmp(tag->name, "frame") != 0) {
00206     delete tag;
00207     return nsnull;
00208   }
00209   Node* result = Read(aFile, tag);
00210   fclose(aFile);
00211   return result;
00212 }
00213 
00214 nsFrameUtil::Node*
00215 nsFrameUtil::Node::Read(FILE* aFile, Tag* tag)
00216 {
00217   Node* node = new Node;
00218   node->type = Copy(tag->GetAttr("type"));
00219   node->state = GetInt(tag, "state");
00220   delete tag;
00221 
00222   for (;;) {
00223     tag = Tag::Parse(aFile);
00224     if (nsnull == tag) break;
00225     if (PL_strcmp(tag->name, "frame") == 0) {
00226       delete tag;
00227       break;
00228     }
00229     if (PL_strcmp(tag->name, "bbox") == 0) {
00230       nscoord x = nscoord( GetInt(tag, "x") );
00231       nscoord y = nscoord( GetInt(tag, "y") );
00232       nscoord w = nscoord( GetInt(tag, "w") );
00233       nscoord h = nscoord( GetInt(tag, "h") );
00234       node->bbox.SetRect(x, y, w, h);
00235     }
00236     else if (PL_strcmp(tag->name, "child-list") == 0) {
00237       NodeList* list = new NodeList();
00238       list->name = Copy(tag->GetAttr("name"));
00239       list->next = node->lists;
00240       node->lists = list;
00241       delete tag;
00242 
00243       Node** tailp = &list->node;
00244       for (;;) {
00245         tag = Tag::Parse(aFile);
00246         if (nsnull == tag) {
00247           break;
00248         }
00249         if (PL_strcmp(tag->name, "child-list") == 0) {
00250           break;
00251         }
00252         if (PL_strcmp(tag->name, "frame") != 0) {
00253           break;
00254         }
00255         Node* child = Node::Read(aFile, tag);
00256         if (nsnull == child) {
00257           break;
00258         }
00259         *tailp = child;
00260         tailp = &child->next;
00261       }
00262     }
00263     else if((PL_strcmp(tag->name, "font") == 0) ||
00264             (PL_strcmp(tag->name, "color") == 0) ||
00265             (PL_strcmp(tag->name, "spacing") == 0) ||
00266             (PL_strcmp(tag->name, "list") == 0) ||
00267             (PL_strcmp(tag->name, "position") == 0) ||
00268             (PL_strcmp(tag->name, "text") == 0) ||
00269             (PL_strcmp(tag->name, "display") == 0) ||
00270             (PL_strcmp(tag->name, "table") == 0) ||
00271             (PL_strcmp(tag->name, "content") == 0) ||
00272             (PL_strcmp(tag->name, "UI") == 0) ||
00273             (PL_strcmp(tag->name, "print") == 0)) {
00274       char* attr = tag->GetAttr("data");
00275       node->styleData.Append('|');
00276       node->styleData.Append((const char *)(attr ? attr : "null attr"));
00277     }
00278 
00279     delete tag;
00280   }
00281   return node;
00282 }
00283 
00284 //----------------------------------------------------------------------
00285 
00286 nsFrameUtil::Tag::Tag()
00287   : name(nsnull), type(open), attributes(nsnull), num(0), size(0),
00288     values(nsnull)
00289 {
00290 }
00291 
00292 nsFrameUtil::Tag::~Tag()
00293 {
00294   PRInt32 i, n = num;
00295   if (0 != n) {
00296     for (i = 0; i < n; i++) {
00297       delete attributes[i];
00298       delete values[i];
00299     }
00300     delete attributes;
00301     delete values;
00302   }
00303 }
00304 
00305 void
00306 nsFrameUtil::Tag::AddAttr(char* aAttr, char* aValue)
00307 {
00308   if (num == size) {
00309     PRInt32 newSize = size * 2 + 4;
00310     char** a = new char*[newSize];
00311     char** v = new char*[newSize];
00312     if (0 != num) {
00313       memcpy(a, attributes, num * sizeof(char*));
00314       memcpy(v, values, num * sizeof(char*));
00315       delete attributes;
00316       delete values;
00317     }
00318     attributes = a;
00319     values = v;
00320     size = newSize;
00321   }
00322   attributes[num] = aAttr;
00323   values[num] = aValue;
00324   num = num + 1;
00325 }
00326 
00327 char*
00328 nsFrameUtil::Tag::GetAttr(char* aAttr)
00329 {
00330   PRInt32 i, n = num;
00331   for (i = 0; i < n; i++) {
00332     if (PL_strcmp(attributes[i], aAttr) == 0) {
00333       return values[i];
00334     }
00335   }
00336   return nsnull;
00337 }
00338 
00339 static inline int IsWhiteSpace(int c) {
00340   return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
00341 }
00342 
00343 static PRBool EatWS(FILE* aFile)
00344 {
00345   for (;;) {
00346     int c = getc(aFile);
00347     if (c < 0) {
00348       return PR_FALSE;
00349     }
00350     if (!IsWhiteSpace(c)) {
00351       ungetc(c, aFile);
00352       break;
00353     }
00354   }
00355   return PR_TRUE;
00356 }
00357 
00358 static PRBool Expect(FILE* aFile, char aChar)
00359 {
00360   int c = getc(aFile);
00361   if (c < 0) return PR_FALSE;
00362   if (c != aChar) {
00363     ungetc(c, aFile);
00364     return PR_FALSE;
00365   }
00366   return PR_TRUE;
00367 }
00368 
00369 static char* ReadIdent(FILE* aFile)
00370 {
00371   char id[1000];
00372   char* ip = id;
00373   char* end = ip + sizeof(id) - 1;
00374   while (ip < end) {
00375     int c = fgetc(aFile);
00376     if (c < 0) return nsnull;
00377     if ((c == '=') || (c == '>') || (c == '/') || IsWhiteSpace(c)) {
00378       ungetc(c, aFile);
00379       break;
00380     }
00381     *ip++ = char(c);
00382   }
00383   *ip = '\0';
00384   return nsFrameUtil::Copy(id);
00385 }
00386 
00387 static char* ReadString(FILE* aFile)
00388 {
00389   if (!Expect(aFile, '\"')) {
00390     return nsnull;
00391   }
00392   char id[1000];
00393   char* ip = id;
00394   char* end = ip + sizeof(id) - 1;
00395   while (ip < end) {
00396     int c = fgetc(aFile);
00397     if (c < 0) return nsnull;
00398     if (c == '\"') {
00399       break;
00400     }
00401     *ip++ = char(c);
00402   }
00403   *ip = '\0';
00404   return nsFrameUtil::Copy(id);
00405 }
00406 
00407 void
00408 nsFrameUtil::Tag::ReadAttrs(FILE* aFile)
00409 {
00410   for (;;) {
00411     if (!EatWS(aFile)) {
00412       break;
00413     }
00414     int c = getc(aFile);
00415     if (c < 0) break;
00416     if (c == '/') {
00417       if (!EatWS(aFile)) {
00418         return;
00419       }
00420       if (Expect(aFile, '>')) {
00421         type = openClose;
00422         break;
00423       }
00424     }
00425     else if (c == '>') {
00426       break;
00427     }
00428     ungetc(c, aFile);
00429     char* attr = ReadIdent(aFile);
00430     if ((nsnull == attr) || !EatWS(aFile)) {
00431       break;
00432     }
00433     char* value = nsnull;
00434     if (Expect(aFile, '=')) {
00435       value = ReadString(aFile);
00436       if (nsnull == value) {
00437         break;
00438       }
00439     }
00440     AddAttr(attr, value);
00441   }
00442 }
00443 
00444 nsFrameUtil::Tag*
00445 nsFrameUtil::Tag::Parse(FILE* aFile)
00446 {
00447   if (!EatWS(aFile)) {
00448     return nsnull;
00449   }
00450   if (Expect(aFile, '<')) {
00451     Tag* tag = new Tag;
00452     if (Expect(aFile, '/')) {
00453       tag->type = close;
00454     }
00455     else {
00456       tag->type = open;
00457     }
00458     tag->name = ReadIdent(aFile);
00459     tag->ReadAttrs(aFile);
00460     return tag;
00461   }
00462   return nsnull;
00463 }
00464 
00465 void
00466 nsFrameUtil::Tag::ToString(nsString& aResult)
00467 {
00468   aResult.Truncate();
00469   aResult.Append(PRUnichar('<'));
00470   if (type == close) {
00471     aResult.Append(PRUnichar('/'));
00472   }
00473   aResult.AppendASCII(name);
00474   if (0 != num) {
00475     PRInt32 i, n = num;
00476     for (i = 0; i < n; i++) {
00477       aResult.Append(PRUnichar(' '));
00478       aResult.AppendASCII(attributes[i]);
00479       if (values[i]) {
00480         aResult.AppendLiteral("=\"");
00481         aResult.AppendASCII(values[i]);
00482         aResult.Append(PRUnichar('\"'));
00483       }
00484     }
00485   }
00486   if (type == openClose) {
00487     aResult.Append(PRUnichar('/'));
00488   }
00489   aResult.Append(PRUnichar('>'));
00490 }
00491 
00492 //----------------------------------------------------------------------
00493 
00494 nsresult NS_NewFrameUtil(nsIFrameUtil** aResult);
00495 nsresult
00496 NS_NewFrameUtil(nsIFrameUtil** aResult)
00497 {
00498   NS_PRECONDITION(nsnull != aResult, "null pointer");
00499   if (nsnull == aResult) {
00500     return NS_ERROR_NULL_POINTER;
00501   }
00502   *aResult = nsnull;
00503 
00504   nsFrameUtil* it = new nsFrameUtil();
00505   if (nsnull == it) {
00506     return NS_ERROR_OUT_OF_MEMORY;
00507   }
00508   return it->QueryInterface(NS_GET_IID(nsIFrameUtil), (void**) aResult);
00509 }
00510 
00511 nsFrameUtil::nsFrameUtil()
00512 {
00513 }
00514 
00515 nsFrameUtil::~nsFrameUtil()
00516 {
00517 }
00518 
00519 NS_IMPL_ISUPPORTS1(nsFrameUtil, nsIFrameUtil)
00520 
00521 void
00522 nsFrameUtil::DumpNode(Node* aNode, FILE* aOutputFile, PRInt32 aIndent)
00523 {
00524   nsFrame::IndentBy(aOutputFile, aIndent);
00525   fprintf(aOutputFile, "%s 0x%x %d,%d,%d,%d, %s\n", aNode->type, aNode->state,
00526           aNode->bbox.x, aNode->bbox.y,
00527           aNode->bbox.width, aNode->bbox.height,
00528           aNode->styleData.get());
00529 }
00530 
00531 void
00532 nsFrameUtil::DumpTree(Node* aNode, FILE* aOutputFile, PRInt32 aIndent)
00533 {
00534   while (nsnull != aNode) {
00535     DumpNode(aNode, aOutputFile, aIndent);
00536     nsFrameUtil::NodeList* lists = aNode->lists;
00537     if (nsnull != lists) {
00538       while (nsnull != lists) {
00539         nsFrame::IndentBy(aOutputFile, aIndent);
00540         fprintf(aOutputFile, " list: %s\n",
00541                 lists->name ? lists->name : "primary");
00542         DumpTree(lists->node, aOutputFile, aIndent + 1);
00543         lists = lists->next;
00544       }
00545     }
00546     aNode = aNode->next;
00547   }
00548 }
00549 
00550 PRBool
00551 nsFrameUtil::CompareTrees(Node* tree1, Node* tree2)
00552 {
00553   PRBool result = PR_TRUE;
00554   for (;; tree1 = tree1->next, tree2 = tree2->next) {
00555     // Make sure both nodes are non-null, or at least agree with each other
00556     if (nsnull == tree1) {
00557       if (nsnull == tree2) {
00558         break;
00559       }
00560       printf("first tree prematurely ends\n");
00561       return PR_FALSE;
00562     }
00563     else if (nsnull == tree2) {
00564       printf("second tree prematurely ends\n");
00565       return PR_FALSE;
00566     }
00567 
00568     // Check the attributes that we care about
00569     if (0 != PL_strcmp(tree1->type, tree2->type)) {
00570       printf("frame type mismatch: %s vs. %s\n", tree1->type, tree2->type);
00571       printf("Node 1:\n");
00572       DumpNode(tree1, stdout, 1);
00573       printf("Node 2:\n");
00574       DumpNode(tree2, stdout, 1);
00575       return PR_FALSE;
00576     }
00577 
00578     // Ignore the XUL scrollbar frames
00579     static const char kScrollbarFrame[] = "ScrollbarFrame";
00580     if (0 == PL_strncmp(tree1->type, kScrollbarFrame, sizeof(kScrollbarFrame) - 1))
00581       continue;
00582 
00583     if (tree1->state != tree2->state) {
00584       printf("frame state mismatch: 0x%x vs. 0x%x\n",
00585              tree1->state, tree2->state);
00586       printf("Node 1:\n");
00587       DumpNode(tree1, stdout, 1);
00588       printf("Node 2:\n");
00589       DumpNode(tree2, stdout, 1);
00590       result = PR_FALSE; // we have a non-critical failure, so remember that but continue
00591     }
00592     if (tree1->bbox != tree2->bbox) {
00593       printf("frame bbox mismatch: %d,%d,%d,%d vs. %d,%d,%d,%d\n",
00594              tree1->bbox.x, tree1->bbox.y,
00595              tree1->bbox.width, tree1->bbox.height,
00596              tree2->bbox.x, tree2->bbox.y,
00597              tree2->bbox.width, tree2->bbox.height);
00598       printf("Node 1:\n");
00599       DumpNode(tree1, stdout, 1);
00600       printf("Node 2:\n");
00601       DumpNode(tree2, stdout, 1);
00602       result = PR_FALSE; // we have a non-critical failure, so remember that but continue
00603     }
00604     if (tree1->styleData != tree2->styleData) {
00605       printf("frame style data mismatch: %s vs. %s\n",
00606         tree1->styleData.get(),
00607         tree2->styleData.get());
00608     }
00609 
00610     // Check child lists too
00611     NodeList* list1 = tree1->lists;
00612     NodeList* list2 = tree2->lists;
00613     for (;;) {
00614       if (nsnull == list1) {
00615         if (nsnull != list2) {
00616           printf("first tree prematurely ends (no child lists)\n");
00617           printf("Node 1:\n");
00618           DumpNode(tree1, stdout, 1);
00619           printf("Node 2:\n");
00620           DumpNode(tree2, stdout, 1);
00621           return PR_FALSE;
00622         }
00623         else {
00624           break;
00625         }
00626       }
00627       if (nsnull == list2) {
00628         printf("second tree prematurely ends (no child lists)\n");
00629         printf("Node 1:\n");
00630         DumpNode(tree1, stdout, 1);
00631         printf("Node 2:\n");
00632         DumpNode(tree2, stdout, 1);
00633         return PR_FALSE;
00634       }
00635       if (0 != PL_strcmp(list1->name, list2->name)) {
00636         printf("child-list name mismatch: %s vs. %s\n",
00637                list1->name ? list1->name : "(null)",
00638                list2->name ? list2->name : "(null)");
00639         result = PR_FALSE; // we have a non-critical failure, so remember that but continue
00640       }
00641       else {
00642         PRBool equiv = CompareTrees(list1->node, list2->node);
00643         if (!equiv) {
00644           return equiv;
00645         }
00646       }
00647       list1 = list1->next;
00648       list2 = list2->next;
00649     }
00650   }
00651   return result;
00652 }
00653 
00654 NS_IMETHODIMP
00655 nsFrameUtil::CompareRegressionData(FILE* aFile1, FILE* aFile2,PRInt32 aRegressionOutput)
00656 {
00657   Node* tree1 = Node::ReadTree(aFile1);
00658   Node* tree2 = Node::ReadTree(aFile2);
00659 
00660   nsresult rv = NS_OK;
00661   if (!CompareTrees(tree1, tree2)) {
00662     // only output this if aRegressionOutput is 0
00663     if( 0 == aRegressionOutput ){
00664       printf("Regression data 1:\n");
00665       DumpTree(tree1, stdout, 0);
00666       printf("Regression data 2:\n");
00667       DumpTree(tree2, stdout, 0);
00668     }
00669     rv = NS_ERROR_FAILURE;
00670   }
00671 
00672   Node::Destroy(tree1);
00673   Node::Destroy(tree2);
00674 
00675   return rv;
00676 }
00677 
00678 NS_IMETHODIMP
00679 nsFrameUtil::DumpRegressionData(FILE* aInputFile, FILE* aOutputFile)
00680 {
00681   Node* tree1 = Node::ReadTree(aInputFile);
00682   if (nsnull != tree1) {
00683     DumpTree(tree1, aOutputFile, 0);
00684     Node::Destroy(tree1);
00685     return NS_OK;
00686   }
00687   return NS_ERROR_FAILURE;
00688 }
00689 #endif