1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsFrameUtil.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,668 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* utilities for regression tests based on frame tree comparison */ 1.10 + 1.11 +#include "nsIFrameUtil.h" 1.12 +#include "nsFrame.h" 1.13 +#include "nsString.h" 1.14 +#include "nsRect.h" 1.15 +#include <stdlib.h> 1.16 +#include "plstr.h" 1.17 + 1.18 + 1.19 +#ifdef DEBUG 1.20 +class nsFrameUtil : public nsIFrameUtil { 1.21 +public: 1.22 + nsFrameUtil(); 1.23 + virtual ~nsFrameUtil(); 1.24 + 1.25 + NS_DECL_ISUPPORTS 1.26 + 1.27 + NS_IMETHOD CompareRegressionData(FILE* aFile1, FILE* aFile2,int32_t aRegressionOutput=0); 1.28 + NS_IMETHOD DumpRegressionData(FILE* aInputFile, FILE* aOutputFile); 1.29 + 1.30 + struct Node; 1.31 + struct Tag; 1.32 + 1.33 + struct NodeList { 1.34 + NodeList(); 1.35 + ~NodeList(); 1.36 + 1.37 + static void Destroy(NodeList* aLists); 1.38 + 1.39 + NodeList* next; // for lists of lists 1.40 + Node* node; 1.41 + char* name; 1.42 + }; 1.43 + 1.44 + struct Node { 1.45 + Node(); 1.46 + ~Node(); 1.47 + 1.48 + static void Destroy(Node* aNode); 1.49 + 1.50 + static Node* Read(FILE* aFile, Tag* aTag); 1.51 + 1.52 + static Node* ReadTree(FILE* aFile); 1.53 + 1.54 + Node* next; 1.55 + char* type; 1.56 + uint32_t state; 1.57 + nsRect bbox; 1.58 + nsCString styleData; 1.59 + NodeList* lists; 1.60 + }; 1.61 + 1.62 + struct Tag { 1.63 + Tag(); 1.64 + ~Tag(); 1.65 + 1.66 + static Tag* Parse(FILE* aFile); 1.67 + 1.68 + void AddAttr(char* aAttr, char* aValue); 1.69 + 1.70 + const char* GetAttr(const char* aAttr); 1.71 + 1.72 + void ReadAttrs(FILE* aFile); 1.73 + 1.74 + void ToString(nsString& aResult); 1.75 + 1.76 + enum Type { 1.77 + open, 1.78 + close, 1.79 + openClose 1.80 + }; 1.81 + 1.82 + char* name; 1.83 + Type type; 1.84 + char** attributes; 1.85 + int32_t num; 1.86 + int32_t size; 1.87 + char** values; 1.88 + }; 1.89 + 1.90 + static char* Copy(const char* aString); 1.91 + 1.92 + static void DumpNode(Node* aNode, FILE* aOutputFile, int32_t aIndent); 1.93 + static void DumpTree(Node* aNode, FILE* aOutputFile, int32_t aIndent); 1.94 + static bool CompareTrees(Node* aNode1, Node* aNode2); 1.95 +}; 1.96 + 1.97 +char* 1.98 +nsFrameUtil::Copy(const char* aString) 1.99 +{ 1.100 + if (aString) { 1.101 + int l = ::strlen(aString); 1.102 + char* c = new char[l+1]; 1.103 + if (!c) 1.104 + return nullptr; 1.105 + memcpy(c, aString, l+1); 1.106 + return c; 1.107 + } 1.108 + return nullptr; 1.109 +} 1.110 + 1.111 +//---------------------------------------------------------------------- 1.112 + 1.113 +nsFrameUtil::NodeList::NodeList() 1.114 + : next(nullptr), node(nullptr), name(nullptr) 1.115 +{ 1.116 +} 1.117 + 1.118 +nsFrameUtil::NodeList::~NodeList() 1.119 +{ 1.120 + if (nullptr != name) { 1.121 + delete name; 1.122 + } 1.123 + if (nullptr != node) { 1.124 + Node::Destroy(node); 1.125 + } 1.126 +} 1.127 + 1.128 +void 1.129 +nsFrameUtil::NodeList::Destroy(NodeList* aLists) 1.130 +{ 1.131 + while (nullptr != aLists) { 1.132 + NodeList* next = aLists->next; 1.133 + delete aLists; 1.134 + aLists = next; 1.135 + } 1.136 +} 1.137 + 1.138 +//---------------------------------------------------------------------- 1.139 + 1.140 +nsFrameUtil::Node::Node() 1.141 + : next(nullptr), type(nullptr), state(0), lists(nullptr) 1.142 +{ 1.143 +} 1.144 + 1.145 +nsFrameUtil::Node::~Node() 1.146 +{ 1.147 + if (nullptr != type) { 1.148 + delete type; 1.149 + } 1.150 + if (nullptr != lists) { 1.151 + NodeList::Destroy(lists); 1.152 + } 1.153 +} 1.154 + 1.155 +void 1.156 +nsFrameUtil::Node::Destroy(Node* aList) 1.157 +{ 1.158 + while (nullptr != aList) { 1.159 + Node* next = aList->next; 1.160 + delete aList; 1.161 + aList = next; 1.162 + } 1.163 +} 1.164 + 1.165 +static int32_t GetInt(nsFrameUtil::Tag* aTag, const char* aAttr) 1.166 +{ 1.167 + const char* value = aTag->GetAttr(aAttr); 1.168 + if (nullptr != value) { 1.169 + return int32_t( atoi(value) ); 1.170 + } 1.171 + return 0; 1.172 +} 1.173 + 1.174 +nsFrameUtil::Node* 1.175 +nsFrameUtil::Node::ReadTree(FILE* aFile) 1.176 +{ 1.177 + Tag* tag = Tag::Parse(aFile); 1.178 + if (nullptr == tag) { 1.179 + return nullptr; 1.180 + } 1.181 + if (PL_strcmp(tag->name, "frame") != 0) { 1.182 + delete tag; 1.183 + return nullptr; 1.184 + } 1.185 + Node* result = Read(aFile, tag); 1.186 + fclose(aFile); 1.187 + return result; 1.188 +} 1.189 + 1.190 +nsFrameUtil::Node* 1.191 +nsFrameUtil::Node::Read(FILE* aFile, Tag* tag) 1.192 +{ 1.193 + Node* node = new Node; 1.194 + node->type = Copy(tag->GetAttr("type")); 1.195 + if (!node->type) { 1.196 + /* crash() */ 1.197 + } 1.198 + node->state = GetInt(tag, "state"); 1.199 + delete tag; 1.200 + 1.201 + for (;;) { 1.202 + tag = Tag::Parse(aFile); 1.203 + if (nullptr == tag) break; 1.204 + if (PL_strcmp(tag->name, "frame") == 0) { 1.205 + delete tag; 1.206 + break; 1.207 + } 1.208 + if (PL_strcmp(tag->name, "bbox") == 0) { 1.209 + nscoord x = nscoord( GetInt(tag, "x") ); 1.210 + nscoord y = nscoord( GetInt(tag, "y") ); 1.211 + nscoord w = nscoord( GetInt(tag, "w") ); 1.212 + nscoord h = nscoord( GetInt(tag, "h") ); 1.213 + node->bbox.SetRect(x, y, w, h); 1.214 + } 1.215 + else if (PL_strcmp(tag->name, "child-list") == 0) { 1.216 + NodeList* list = new NodeList(); 1.217 + list->name = Copy(tag->GetAttr("name")); 1.218 + if (!list->name) { 1.219 + /* crash() */ 1.220 + } 1.221 + list->next = node->lists; 1.222 + node->lists = list; 1.223 + delete tag; 1.224 + 1.225 + Node** tailp = &list->node; 1.226 + for (;;) { 1.227 + tag = Tag::Parse(aFile); 1.228 + if (nullptr == tag) { 1.229 + break; 1.230 + } 1.231 + if (PL_strcmp(tag->name, "child-list") == 0) { 1.232 + break; 1.233 + } 1.234 + if (PL_strcmp(tag->name, "frame") != 0) { 1.235 + break; 1.236 + } 1.237 + Node* child = Node::Read(aFile, tag); 1.238 + if (nullptr == child) { 1.239 + break; 1.240 + } 1.241 + *tailp = child; 1.242 + tailp = &child->next; 1.243 + } 1.244 + } 1.245 + else if((PL_strcmp(tag->name, "font") == 0) || 1.246 + (PL_strcmp(tag->name, "color") == 0) || 1.247 + (PL_strcmp(tag->name, "spacing") == 0) || 1.248 + (PL_strcmp(tag->name, "list") == 0) || 1.249 + (PL_strcmp(tag->name, "position") == 0) || 1.250 + (PL_strcmp(tag->name, "text") == 0) || 1.251 + (PL_strcmp(tag->name, "display") == 0) || 1.252 + (PL_strcmp(tag->name, "table") == 0) || 1.253 + (PL_strcmp(tag->name, "content") == 0) || 1.254 + (PL_strcmp(tag->name, "UI") == 0) || 1.255 + (PL_strcmp(tag->name, "print") == 0)) { 1.256 + const char* attr = tag->GetAttr("data"); 1.257 + node->styleData.Append('|'); 1.258 + node->styleData.Append(attr ? attr : "null attr"); 1.259 + } 1.260 + 1.261 + delete tag; 1.262 + } 1.263 + return node; 1.264 +} 1.265 + 1.266 +//---------------------------------------------------------------------- 1.267 + 1.268 +nsFrameUtil::Tag::Tag() 1.269 + : name(nullptr), type(open), attributes(nullptr), num(0), size(0), 1.270 + values(nullptr) 1.271 +{ 1.272 +} 1.273 + 1.274 +nsFrameUtil::Tag::~Tag() 1.275 +{ 1.276 + int32_t i, n = num; 1.277 + if (0 != n) { 1.278 + for (i = 0; i < n; i++) { 1.279 + delete attributes[i]; 1.280 + delete values[i]; 1.281 + } 1.282 + delete attributes; 1.283 + delete values; 1.284 + } 1.285 +} 1.286 + 1.287 +void 1.288 +nsFrameUtil::Tag::AddAttr(char* aAttr, char* aValue) 1.289 +{ 1.290 + if (num == size) { 1.291 + int32_t newSize = size * 2 + 4; 1.292 + char** a = new char*[newSize]; 1.293 + char** v = new char*[newSize]; 1.294 + if (0 != num) { 1.295 + memcpy(a, attributes, num * sizeof(char*)); 1.296 + memcpy(v, values, num * sizeof(char*)); 1.297 + delete attributes; 1.298 + delete values; 1.299 + } 1.300 + attributes = a; 1.301 + values = v; 1.302 + size = newSize; 1.303 + } 1.304 + attributes[num] = aAttr; 1.305 + values[num] = aValue; 1.306 + num = num + 1; 1.307 +} 1.308 + 1.309 +const char* 1.310 +nsFrameUtil::Tag::GetAttr(const char* aAttr) 1.311 +{ 1.312 + int32_t i, n = num; 1.313 + for (i = 0; i < n; i++) { 1.314 + if (PL_strcmp(attributes[i], aAttr) == 0) { 1.315 + return values[i]; 1.316 + } 1.317 + } 1.318 + return nullptr; 1.319 +} 1.320 + 1.321 +static inline int IsWhiteSpace(int c) { 1.322 + return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'); 1.323 +} 1.324 + 1.325 +static bool EatWS(FILE* aFile) 1.326 +{ 1.327 + for (;;) { 1.328 + int c = getc(aFile); 1.329 + if (c < 0) { 1.330 + return false; 1.331 + } 1.332 + if (!IsWhiteSpace(c)) { 1.333 + ungetc(c, aFile); 1.334 + break; 1.335 + } 1.336 + } 1.337 + return true; 1.338 +} 1.339 + 1.340 +static bool Expect(FILE* aFile, char aChar) 1.341 +{ 1.342 + int c = getc(aFile); 1.343 + if (c < 0) return false; 1.344 + if (c != aChar) { 1.345 + ungetc(c, aFile); 1.346 + return false; 1.347 + } 1.348 + return true; 1.349 +} 1.350 + 1.351 +static char* ReadIdent(FILE* aFile) 1.352 +{ 1.353 + char id[1000]; 1.354 + char* ip = id; 1.355 + char* end = ip + sizeof(id) - 1; 1.356 + while (ip < end) { 1.357 + int c = fgetc(aFile); 1.358 + if (c < 0) return nullptr; 1.359 + if ((c == '=') || (c == '>') || (c == '/') || IsWhiteSpace(c)) { 1.360 + ungetc(c, aFile); 1.361 + break; 1.362 + } 1.363 + *ip++ = char(c); 1.364 + } 1.365 + *ip = '\0'; 1.366 + return nsFrameUtil::Copy(id); 1.367 + /* may return a null pointer */ 1.368 +} 1.369 + 1.370 +static char* ReadString(FILE* aFile) 1.371 +{ 1.372 + if (!Expect(aFile, '\"')) { 1.373 + return nullptr; 1.374 + } 1.375 + char id[1000]; 1.376 + char* ip = id; 1.377 + char* end = ip + sizeof(id) - 1; 1.378 + while (ip < end) { 1.379 + int c = fgetc(aFile); 1.380 + if (c < 0) return nullptr; 1.381 + if (c == '\"') { 1.382 + break; 1.383 + } 1.384 + *ip++ = char(c); 1.385 + } 1.386 + *ip = '\0'; 1.387 + return nsFrameUtil::Copy(id); 1.388 + /* may return a null pointer */ 1.389 +} 1.390 + 1.391 +void 1.392 +nsFrameUtil::Tag::ReadAttrs(FILE* aFile) 1.393 +{ 1.394 + for (;;) { 1.395 + if (!EatWS(aFile)) { 1.396 + break; 1.397 + } 1.398 + int c = getc(aFile); 1.399 + if (c < 0) break; 1.400 + if (c == '/') { 1.401 + if (!EatWS(aFile)) { 1.402 + return; 1.403 + } 1.404 + if (Expect(aFile, '>')) { 1.405 + type = openClose; 1.406 + break; 1.407 + } 1.408 + } 1.409 + else if (c == '>') { 1.410 + break; 1.411 + } 1.412 + ungetc(c, aFile); 1.413 + char* attr = ReadIdent(aFile); 1.414 + if ((nullptr == attr) || !EatWS(aFile)) { 1.415 + break; 1.416 + } 1.417 + char* value = nullptr; 1.418 + if (Expect(aFile, '=')) { 1.419 + value = ReadString(aFile); 1.420 + if (nullptr == value) { 1.421 + delete [] attr; 1.422 + break; 1.423 + } 1.424 + } 1.425 + AddAttr(attr, value); 1.426 + } 1.427 +} 1.428 + 1.429 +nsFrameUtil::Tag* 1.430 +nsFrameUtil::Tag::Parse(FILE* aFile) 1.431 +{ 1.432 + if (!EatWS(aFile)) { 1.433 + return nullptr; 1.434 + } 1.435 + if (Expect(aFile, '<')) { 1.436 + Tag* tag = new Tag; 1.437 + if (Expect(aFile, '/')) { 1.438 + tag->type = close; 1.439 + } 1.440 + else { 1.441 + tag->type = open; 1.442 + } 1.443 + tag->name = ReadIdent(aFile); 1.444 + tag->ReadAttrs(aFile); 1.445 + return tag; 1.446 + } 1.447 + return nullptr; 1.448 +} 1.449 + 1.450 +void 1.451 +nsFrameUtil::Tag::ToString(nsString& aResult) 1.452 +{ 1.453 + aResult.Truncate(); 1.454 + aResult.Append(char16_t('<')); 1.455 + if (type == close) { 1.456 + aResult.Append(char16_t('/')); 1.457 + } 1.458 + aResult.AppendASCII(name); 1.459 + if (0 != num) { 1.460 + int32_t i, n = num; 1.461 + for (i = 0; i < n; i++) { 1.462 + aResult.Append(char16_t(' ')); 1.463 + aResult.AppendASCII(attributes[i]); 1.464 + if (values[i]) { 1.465 + aResult.AppendLiteral("=\""); 1.466 + aResult.AppendASCII(values[i]); 1.467 + aResult.Append(char16_t('\"')); 1.468 + } 1.469 + } 1.470 + } 1.471 + if (type == openClose) { 1.472 + aResult.Append(char16_t('/')); 1.473 + } 1.474 + aResult.Append(char16_t('>')); 1.475 +} 1.476 + 1.477 +//---------------------------------------------------------------------- 1.478 + 1.479 +nsresult 1.480 +NS_NewFrameUtil(nsIFrameUtil** aResult) 1.481 +{ 1.482 + NS_PRECONDITION(nullptr != aResult, "null pointer"); 1.483 + if (nullptr == aResult) { 1.484 + return NS_ERROR_NULL_POINTER; 1.485 + } 1.486 + 1.487 + nsFrameUtil* it = new nsFrameUtil(); 1.488 + 1.489 + NS_ADDREF(*aResult = it); 1.490 + return NS_OK; 1.491 +} 1.492 + 1.493 +nsFrameUtil::nsFrameUtil() 1.494 +{ 1.495 +} 1.496 + 1.497 +nsFrameUtil::~nsFrameUtil() 1.498 +{ 1.499 +} 1.500 + 1.501 +NS_IMPL_ISUPPORTS(nsFrameUtil, nsIFrameUtil) 1.502 + 1.503 +void 1.504 +nsFrameUtil::DumpNode(Node* aNode, FILE* aOutputFile, int32_t aIndent) 1.505 +{ 1.506 + nsFrame::IndentBy(aOutputFile, aIndent); 1.507 + fprintf(aOutputFile, "%s 0x%x %d,%d,%d,%d, %s\n", aNode->type, aNode->state, 1.508 + aNode->bbox.x, aNode->bbox.y, 1.509 + aNode->bbox.width, aNode->bbox.height, 1.510 + aNode->styleData.get()); 1.511 +} 1.512 + 1.513 +void 1.514 +nsFrameUtil::DumpTree(Node* aNode, FILE* aOutputFile, int32_t aIndent) 1.515 +{ 1.516 + while (nullptr != aNode) { 1.517 + DumpNode(aNode, aOutputFile, aIndent); 1.518 + nsFrameUtil::NodeList* lists = aNode->lists; 1.519 + if (nullptr != lists) { 1.520 + while (nullptr != lists) { 1.521 + nsFrame::IndentBy(aOutputFile, aIndent); 1.522 + fprintf(aOutputFile, " list: %s\n", 1.523 + lists->name ? lists->name : "primary"); 1.524 + DumpTree(lists->node, aOutputFile, aIndent + 1); 1.525 + lists = lists->next; 1.526 + } 1.527 + } 1.528 + aNode = aNode->next; 1.529 + } 1.530 +} 1.531 + 1.532 +bool 1.533 +nsFrameUtil::CompareTrees(Node* tree1, Node* tree2) 1.534 +{ 1.535 + bool result = true; 1.536 + for (;; tree1 = tree1->next, tree2 = tree2->next) { 1.537 + // Make sure both nodes are non-null, or at least agree with each other 1.538 + if (nullptr == tree1) { 1.539 + if (nullptr == tree2) { 1.540 + break; 1.541 + } 1.542 + printf("first tree prematurely ends\n"); 1.543 + return false; 1.544 + } 1.545 + else if (nullptr == tree2) { 1.546 + printf("second tree prematurely ends\n"); 1.547 + return false; 1.548 + } 1.549 + 1.550 + // Check the attributes that we care about 1.551 + if (0 != PL_strcmp(tree1->type, tree2->type)) { 1.552 + printf("frame type mismatch: %s vs. %s\n", tree1->type, tree2->type); 1.553 + printf("Node 1:\n"); 1.554 + DumpNode(tree1, stdout, 1); 1.555 + printf("Node 2:\n"); 1.556 + DumpNode(tree2, stdout, 1); 1.557 + return false; 1.558 + } 1.559 + 1.560 + // Ignore the XUL scrollbar frames 1.561 + static const char kScrollbarFrame[] = "ScrollbarFrame"; 1.562 + if (0 == PL_strncmp(tree1->type, kScrollbarFrame, sizeof(kScrollbarFrame) - 1)) 1.563 + continue; 1.564 + 1.565 + if (tree1->state != tree2->state) { 1.566 + printf("frame state mismatch: 0x%x vs. 0x%x\n", 1.567 + tree1->state, tree2->state); 1.568 + printf("Node 1:\n"); 1.569 + DumpNode(tree1, stdout, 1); 1.570 + printf("Node 2:\n"); 1.571 + DumpNode(tree2, stdout, 1); 1.572 + result = false; // we have a non-critical failure, so remember that but continue 1.573 + } 1.574 + if (tree1->bbox.IsEqualInterior(tree2->bbox)) { 1.575 + printf("frame bbox mismatch: %d,%d,%d,%d vs. %d,%d,%d,%d\n", 1.576 + tree1->bbox.x, tree1->bbox.y, 1.577 + tree1->bbox.width, tree1->bbox.height, 1.578 + tree2->bbox.x, tree2->bbox.y, 1.579 + tree2->bbox.width, tree2->bbox.height); 1.580 + printf("Node 1:\n"); 1.581 + DumpNode(tree1, stdout, 1); 1.582 + printf("Node 2:\n"); 1.583 + DumpNode(tree2, stdout, 1); 1.584 + result = false; // we have a non-critical failure, so remember that but continue 1.585 + } 1.586 + if (tree1->styleData != tree2->styleData) { 1.587 + printf("frame style data mismatch: %s vs. %s\n", 1.588 + tree1->styleData.get(), 1.589 + tree2->styleData.get()); 1.590 + } 1.591 + 1.592 + // Check child lists too 1.593 + NodeList* list1 = tree1->lists; 1.594 + NodeList* list2 = tree2->lists; 1.595 + for (;;) { 1.596 + if (nullptr == list1) { 1.597 + if (nullptr != list2) { 1.598 + printf("first tree prematurely ends (no child lists)\n"); 1.599 + printf("Node 1:\n"); 1.600 + DumpNode(tree1, stdout, 1); 1.601 + printf("Node 2:\n"); 1.602 + DumpNode(tree2, stdout, 1); 1.603 + return false; 1.604 + } 1.605 + else { 1.606 + break; 1.607 + } 1.608 + } 1.609 + if (nullptr == list2) { 1.610 + printf("second tree prematurely ends (no child lists)\n"); 1.611 + printf("Node 1:\n"); 1.612 + DumpNode(tree1, stdout, 1); 1.613 + printf("Node 2:\n"); 1.614 + DumpNode(tree2, stdout, 1); 1.615 + return false; 1.616 + } 1.617 + if (0 != PL_strcmp(list1->name, list2->name)) { 1.618 + printf("child-list name mismatch: %s vs. %s\n", 1.619 + list1->name ? list1->name : "(null)", 1.620 + list2->name ? list2->name : "(null)"); 1.621 + result = false; // we have a non-critical failure, so remember that but continue 1.622 + } 1.623 + else { 1.624 + bool equiv = CompareTrees(list1->node, list2->node); 1.625 + if (!equiv) { 1.626 + return equiv; 1.627 + } 1.628 + } 1.629 + list1 = list1->next; 1.630 + list2 = list2->next; 1.631 + } 1.632 + } 1.633 + return result; 1.634 +} 1.635 + 1.636 +NS_IMETHODIMP 1.637 +nsFrameUtil::CompareRegressionData(FILE* aFile1, FILE* aFile2,int32_t aRegressionOutput) 1.638 +{ 1.639 + Node* tree1 = Node::ReadTree(aFile1); 1.640 + Node* tree2 = Node::ReadTree(aFile2); 1.641 + 1.642 + nsresult rv = NS_OK; 1.643 + if (!CompareTrees(tree1, tree2)) { 1.644 + // only output this if aRegressionOutput is 0 1.645 + if( 0 == aRegressionOutput ){ 1.646 + printf("Regression data 1:\n"); 1.647 + DumpTree(tree1, stdout, 0); 1.648 + printf("Regression data 2:\n"); 1.649 + DumpTree(tree2, stdout, 0); 1.650 + } 1.651 + rv = NS_ERROR_FAILURE; 1.652 + } 1.653 + 1.654 + Node::Destroy(tree1); 1.655 + Node::Destroy(tree2); 1.656 + 1.657 + return rv; 1.658 +} 1.659 + 1.660 +NS_IMETHODIMP 1.661 +nsFrameUtil::DumpRegressionData(FILE* aInputFile, FILE* aOutputFile) 1.662 +{ 1.663 + Node* tree1 = Node::ReadTree(aInputFile); 1.664 + if (nullptr != tree1) { 1.665 + DumpTree(tree1, aOutputFile, 0); 1.666 + Node::Destroy(tree1); 1.667 + return NS_OK; 1.668 + } 1.669 + return NS_ERROR_FAILURE; 1.670 +} 1.671 +#endif