DumpRenderTree.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /*
  2. * Copyright (C) 2011 ProFUSION Embedded Systems
  3. * Copyright (C) 2011 Samsung Electronics
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
  15. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  16. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  17. * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  18. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  19. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  20. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  21. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  23. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #include "config.h"
  26. #include "DumpRenderTree.h"
  27. #include "DumpHistoryItem.h"
  28. #include "DumpRenderTreeChrome.h"
  29. #include "DumpRenderTreeView.h"
  30. #include "EventSender.h"
  31. #include "FontManagement.h"
  32. #include "NotImplemented.h"
  33. #include "PixelDumpSupport.h"
  34. #include "TestRunner.h"
  35. #include "WebCoreSupport/DumpRenderTreeSupportEfl.h"
  36. #include "WebCoreTestSupport.h"
  37. #include "WorkQueue.h"
  38. #include "ewk_private.h"
  39. #include <EWebKit.h>
  40. #include <Ecore.h>
  41. #include <Ecore_Evas.h>
  42. #include <Ecore_File.h>
  43. #include <Edje.h>
  44. #include <Evas.h>
  45. #include <fontconfig/fontconfig.h>
  46. #include <getopt.h>
  47. #include <stdlib.h>
  48. #include <unistd.h>
  49. #include <wtf/Assertions.h>
  50. #include <wtf/OwnPtr.h>
  51. #include <wtf/text/CString.h>
  52. #include <wtf/text/StringBuilder.h>
  53. OwnPtr<DumpRenderTreeChrome> browser;
  54. Evas_Object* topLoadingFrame = 0;
  55. bool waitForPolicy = false;
  56. bool policyDelegateEnabled = false;
  57. bool policyDelegatePermissive = false;
  58. Ecore_Timer* waitToDumpWatchdog = 0;
  59. extern Ewk_History_Item* prevTestBFItem;
  60. // From the top-level DumpRenderTree.h
  61. RefPtr<TestRunner> gTestRunner;
  62. volatile bool done = false;
  63. static bool dumpPixelsForCurrentTest;
  64. static int dumpPixelsForAllTests = false;
  65. static int dumpTree = true;
  66. static int printSeparators = true;
  67. static int useTimeoutWatchdog = true;
  68. static String dumpFramesAsText(Evas_Object* frame)
  69. {
  70. if (!frame)
  71. return String();
  72. String result;
  73. const char* frameContents = ewk_frame_plain_text_get(frame);
  74. if (!frameContents)
  75. return String();
  76. if (browser->mainFrame() != frame) {
  77. result.append("\n--------\nFrame: '");
  78. result.append(String::fromUTF8(ewk_frame_name_get(frame)));
  79. result.append("'\n--------\n");
  80. }
  81. result.append(String::fromUTF8(frameContents));
  82. result.append("\n");
  83. eina_stringshare_del(frameContents);
  84. if (gTestRunner->dumpChildFramesAsText()) {
  85. Eina_List* children = DumpRenderTreeSupportEfl::frameChildren(frame);
  86. void* iterator;
  87. EINA_LIST_FREE(children, iterator) {
  88. Evas_Object* currentFrame = static_cast<Evas_Object*>(iterator);
  89. String tempText(dumpFramesAsText(currentFrame));
  90. if (tempText.isEmpty())
  91. continue;
  92. result.append(tempText);
  93. }
  94. }
  95. return result;
  96. }
  97. static void dumpFrameScrollPosition(Evas_Object* frame)
  98. {
  99. int x, y;
  100. ewk_frame_scroll_pos_get(frame, &x, &y);
  101. if (abs(x) > 0 || abs(y) > 0) {
  102. StringBuilder result;
  103. Evas_Object* parent = evas_object_smart_parent_get(frame);
  104. // smart parent of main frame is view object.
  105. if (parent != browser->mainView()) {
  106. result.append("frame '");
  107. result.append(ewk_frame_name_get(frame));
  108. result.append("' ");
  109. }
  110. result.append("scrolled to ");
  111. result.append(WTF::String::number(x));
  112. result.append(",");
  113. result.append(WTF::String::number(y));
  114. result.append("\n");
  115. printf("%s", result.toString().utf8().data());
  116. }
  117. if (gTestRunner->dumpChildFrameScrollPositions()) {
  118. Eina_List* children = DumpRenderTreeSupportEfl::frameChildren(frame);
  119. void* iterator;
  120. EINA_LIST_FREE(children, iterator) {
  121. Evas_Object* currentFrame = static_cast<Evas_Object*>(iterator);
  122. dumpFrameScrollPosition(currentFrame);
  123. }
  124. }
  125. }
  126. static bool shouldLogFrameLoadDelegates(const String& pathOrURL)
  127. {
  128. return pathOrURL.contains("loading/");
  129. }
  130. static bool shouldDumpAsText(const String& pathOrURL)
  131. {
  132. return pathOrURL.contains("dumpAsText/");
  133. }
  134. static bool shouldOpenWebInspector(const String& pathOrURL)
  135. {
  136. return pathOrURL.contains("inspector/");
  137. }
  138. static void sendPixelResultsEOF()
  139. {
  140. puts("#EOF");
  141. fflush(stdout);
  142. fflush(stderr);
  143. }
  144. bool shouldSetWaitToDumpWatchdog()
  145. {
  146. return !waitToDumpWatchdog && useTimeoutWatchdog;
  147. }
  148. static void invalidateAnyPreviousWaitToDumpWatchdog()
  149. {
  150. if (waitToDumpWatchdog) {
  151. ecore_timer_del(waitToDumpWatchdog);
  152. waitToDumpWatchdog = 0;
  153. }
  154. waitForPolicy = false;
  155. }
  156. static void onEcoreEvasResize(Ecore_Evas* ecoreEvas)
  157. {
  158. int width, height;
  159. ecore_evas_geometry_get(ecoreEvas, 0, 0, &width, &height);
  160. evas_object_move(browser->mainView(), 0, 0);
  161. evas_object_resize(browser->mainView(), width, height);
  162. }
  163. static void onCloseWindow(Ecore_Evas*)
  164. {
  165. notImplemented();
  166. }
  167. static Eina_Bool useLongRunningServerMode(int argc, char** argv)
  168. {
  169. return (argc == optind + 1 && !strcmp(argv[optind], "-"));
  170. }
  171. static bool parseCommandLineOptions(int argc, char** argv)
  172. {
  173. static const option options[] = {
  174. {"notree", no_argument, &dumpTree, false},
  175. {"pixel-tests", no_argument, &dumpPixelsForAllTests, true},
  176. {"tree", no_argument, &dumpTree, true},
  177. {"no-timeout", no_argument, &useTimeoutWatchdog, false},
  178. {0, 0, 0, 0}
  179. };
  180. int option;
  181. while ((option = getopt_long(argc, (char* const*)argv, "", options, 0)) != -1) {
  182. switch (option) {
  183. case '?':
  184. case ':':
  185. return false;
  186. }
  187. }
  188. return true;
  189. }
  190. static inline bool isGlobalHistoryTest(const String& cTestPathOrURL)
  191. {
  192. return cTestPathOrURL.contains("/globalhistory/");
  193. }
  194. static void createTestRunner(const String& testURL, const String& expectedPixelHash)
  195. {
  196. gTestRunner =
  197. TestRunner::create(std::string(testURL.utf8().data()),
  198. std::string(expectedPixelHash.utf8().data()));
  199. topLoadingFrame = 0;
  200. done = false;
  201. gTestRunner->setIconDatabaseEnabled(false);
  202. if (shouldLogFrameLoadDelegates(testURL))
  203. gTestRunner->setDumpFrameLoadCallbacks(true);
  204. gTestRunner->setDeveloperExtrasEnabled(true);
  205. if (shouldOpenWebInspector(testURL))
  206. gTestRunner->showWebInspector();
  207. gTestRunner->setDumpHistoryDelegateCallbacks(isGlobalHistoryTest(testURL));
  208. if (shouldDumpAsText(testURL)) {
  209. gTestRunner->setDumpAsText(true);
  210. gTestRunner->setGeneratePixelResults(false);
  211. }
  212. }
  213. static String getFinalTestURL(const String& testURL)
  214. {
  215. if (!testURL.startsWith("http://") && !testURL.startsWith("https://")) {
  216. char* cFilePath = ecore_file_realpath(testURL.utf8().data());
  217. const String filePath = String::fromUTF8(cFilePath);
  218. free(cFilePath);
  219. if (ecore_file_exists(filePath.utf8().data()))
  220. return String("file://") + filePath;
  221. }
  222. return testURL;
  223. }
  224. static void runTest(const char* inputLine)
  225. {
  226. TestCommand command = parseInputLine(inputLine);
  227. const String testPathOrURL(command.pathOrURL.c_str());
  228. ASSERT(!testPathOrURL.isEmpty());
  229. dumpPixelsForCurrentTest = command.shouldDumpPixels || dumpPixelsForAllTests;
  230. const String expectedPixelHash(command.expectedPixelHash.c_str());
  231. // Convert the path into a full file URL if it does not look
  232. // like an HTTP/S URL (doesn't start with http:// or https://).
  233. const String testURL = getFinalTestURL(testPathOrURL);
  234. browser->resetDefaultsToConsistentValues();
  235. createTestRunner(testURL, expectedPixelHash);
  236. WorkQueue::shared()->clear();
  237. WorkQueue::shared()->setFrozen(false);
  238. const bool isSVGW3CTest = testURL.contains("svg/W3C-SVG-1.1");
  239. const int width = isSVGW3CTest ? TestRunner::w3cSVGViewWidth : TestRunner::viewWidth;
  240. const int height = isSVGW3CTest ? TestRunner::w3cSVGViewHeight : TestRunner::viewHeight;
  241. evas_object_resize(browser->mainView(), width, height);
  242. if (prevTestBFItem)
  243. ewk_history_item_free(prevTestBFItem);
  244. const Ewk_History* history = ewk_view_history_get(browser->mainView());
  245. prevTestBFItem = ewk_history_history_item_current_get(history);
  246. evas_object_focus_set(browser->mainView(), EINA_TRUE);
  247. ewk_view_uri_set(browser->mainView(), testURL.utf8().data());
  248. ecore_main_loop_begin();
  249. gTestRunner->closeWebInspector();
  250. gTestRunner->setDeveloperExtrasEnabled(false);
  251. browser->clearExtraViews();
  252. // FIXME: Move to DRTChrome::resetDefaultsToConsistentValues() after bug 85209 lands.
  253. WebCoreTestSupport::resetInternalsObject(DumpRenderTreeSupportEfl::globalContextRefForFrame(browser->mainFrame()));
  254. ewk_view_uri_set(browser->mainView(), "about:blank");
  255. gTestRunner.clear();
  256. sendPixelResultsEOF();
  257. }
  258. static void runTestingServerLoop()
  259. {
  260. char filename[PATH_MAX];
  261. while (fgets(filename, sizeof(filename), stdin)) {
  262. char* newLine = strrchr(filename, '\n');
  263. if (newLine)
  264. *newLine = '\0';
  265. if (filename[0] != '\0')
  266. runTest(filename);
  267. }
  268. }
  269. static void adjustOutputTypeByMimeType(const Evas_Object* frame)
  270. {
  271. const String responseMimeType(DumpRenderTreeSupportEfl::responseMimeType(frame));
  272. if (responseMimeType == "text/plain") {
  273. gTestRunner->setDumpAsText(true);
  274. gTestRunner->setGeneratePixelResults(false);
  275. }
  276. }
  277. static void dumpFrameContentsAsText(Evas_Object* frame)
  278. {
  279. String result;
  280. if (gTestRunner->dumpAsText())
  281. result = dumpFramesAsText(frame);
  282. else
  283. result = DumpRenderTreeSupportEfl::renderTreeDump(frame);
  284. printf("%s", result.utf8().data());
  285. }
  286. static bool shouldDumpFrameScrollPosition()
  287. {
  288. return !gTestRunner->dumpAsText() && !gTestRunner->dumpDOMAsWebArchive() && !gTestRunner->dumpSourceAsWebArchive();
  289. }
  290. static bool shouldDumpPixelsAndCompareWithExpected()
  291. {
  292. return dumpPixelsForCurrentTest && gTestRunner->generatePixelResults() && !gTestRunner->dumpDOMAsWebArchive() && !gTestRunner->dumpSourceAsWebArchive();
  293. }
  294. static bool shouldDumpBackForwardList()
  295. {
  296. return gTestRunner->dumpBackForwardList();
  297. }
  298. static bool initEfl()
  299. {
  300. if (!ecore_evas_init())
  301. return false;
  302. if (!ecore_file_init()) {
  303. ecore_evas_shutdown();
  304. return false;
  305. }
  306. if (!edje_init()) {
  307. ecore_file_shutdown();
  308. ecore_evas_shutdown();
  309. return false;
  310. }
  311. if (!ewk_init()) {
  312. edje_shutdown();
  313. ecore_file_shutdown();
  314. ecore_evas_shutdown();
  315. return false;
  316. }
  317. return true;
  318. }
  319. static void shutdownEfl()
  320. {
  321. ewk_shutdown();
  322. edje_shutdown();
  323. ecore_file_shutdown();
  324. ecore_evas_shutdown();
  325. }
  326. void displayWebView()
  327. {
  328. DumpRenderTreeSupportEfl::forceLayout(browser->mainFrame());
  329. DumpRenderTreeSupportEfl::setTracksRepaints(browser->mainFrame(), true);
  330. DumpRenderTreeSupportEfl::resetTrackedRepaints(browser->mainFrame());
  331. }
  332. void dump()
  333. {
  334. Evas_Object* frame = browser->mainFrame();
  335. invalidateAnyPreviousWaitToDumpWatchdog();
  336. if (dumpTree) {
  337. adjustOutputTypeByMimeType(frame);
  338. dumpFrameContentsAsText(frame);
  339. if (shouldDumpFrameScrollPosition())
  340. dumpFrameScrollPosition(frame);
  341. if (shouldDumpBackForwardList())
  342. dumpBackForwardListForWebViews();
  343. if (printSeparators) {
  344. puts("#EOF");
  345. fputs("#EOF\n", stderr);
  346. fflush(stdout);
  347. fflush(stderr);
  348. }
  349. }
  350. if (shouldDumpPixelsAndCompareWithExpected())
  351. dumpWebViewAsPixelsAndCompareWithExpected(gTestRunner->expectedPixelHash());
  352. done = true;
  353. ecore_main_loop_quit();
  354. }
  355. static Ecore_Evas* initEcoreEvas()
  356. {
  357. const char* engine = 0;
  358. #if defined(WTF_USE_ACCELERATED_COMPOSITING) && defined(HAVE_ECORE_X)
  359. engine = "opengl_x11";
  360. #endif
  361. Ecore_Evas* ecoreEvas = ecore_evas_new(engine, 0, 0, 800, 600, 0);
  362. if (!ecoreEvas) {
  363. shutdownEfl();
  364. exit(EXIT_FAILURE);
  365. }
  366. ecore_evas_title_set(ecoreEvas, "EFL DumpRenderTree");
  367. ecore_evas_callback_resize_set(ecoreEvas, onEcoreEvasResize);
  368. ecore_evas_callback_delete_request_set(ecoreEvas, onCloseWindow);
  369. ecore_evas_show(ecoreEvas);
  370. return ecoreEvas;
  371. }
  372. int main(int argc, char** argv)
  373. {
  374. if (!parseCommandLineOptions(argc, argv))
  375. return EXIT_FAILURE;
  376. if (!initEfl())
  377. return EXIT_FAILURE;
  378. WTFInstallReportBacktraceOnCrashHook();
  379. OwnPtr<Ecore_Evas> ecoreEvas = adoptPtr(initEcoreEvas());
  380. browser = DumpRenderTreeChrome::create(ecore_evas_get(ecoreEvas.get()));
  381. addFontsToEnvironment();
  382. if (useLongRunningServerMode(argc, argv)) {
  383. printSeparators = true;
  384. runTestingServerLoop();
  385. } else {
  386. printSeparators = (optind < argc - 1 || (dumpPixelsForCurrentTest && dumpTree));
  387. for (int i = optind; i != argc; ++i)
  388. runTest(argv[i]);
  389. }
  390. ecoreEvas.clear();
  391. shutdownEfl();
  392. return EXIT_SUCCESS;
  393. }