1 /*
2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3 * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
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 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 #include <jsobjects.h>
30 #include <qwebpage.h>
31 #include <qwebhistory.h>
32 #include <qwebframe.h>
33 #include <qwebsecurityorigin.h>
34 #include <qwebdatabase.h>
35 #include <qevent.h>
36 #include <qapplication.h>
37 #include <qevent.h>
38 #include <qtimer.h>
39
40 #include "DumpRenderTree.h"
41 #include "WorkQueueItem.h"
42 #include "WorkQueue.h"
43 extern void qt_dump_editing_callbacks(bool b);
44 extern void qt_dump_resource_load_callbacks(bool b);
45 extern void qt_drt_setJavaScriptProfilingEnabled(QWebFrame*, bool enabled);
46 extern bool qt_drt_pauseAnimation(QWebFrame*, const QString &name, double time, const QString &elementId);
47 extern bool qt_drt_pauseTransitionOfProperty(QWebFrame*, const QString &name, double time, const QString &elementId);
48 extern int qt_drt_numberOfActiveAnimations(QWebFrame*);
49
findFrameNamed(const QString & frameName,QWebFrame * frame)50 QWebFrame *findFrameNamed(const QString &frameName, QWebFrame *frame)
51 {
52 if (frame->frameName() == frameName)
53 return frame;
54
55 foreach (QWebFrame *childFrame, frame->childFrames())
56 if (QWebFrame *f = findFrameNamed(frameName, childFrame))
57 return f;
58
59 return 0;
60 }
61
invoke() const62 bool LoadItem::invoke() const
63 {
64 //qDebug() << ">>>LoadItem::invoke";
65 Q_ASSERT(m_webPage);
66
67 QWebFrame *frame = 0;
68 const QString t = target();
69 if (t.isEmpty())
70 frame = m_webPage->mainFrame();
71 else
72 frame = findFrameNamed(t, m_webPage->mainFrame());
73
74 if (!frame)
75 return false;
76
77 frame->load(url());
78 return true;
79 }
80
invoke() const81 bool ReloadItem::invoke() const
82 {
83 //qDebug() << ">>>ReloadItem::invoke";
84 Q_ASSERT(m_webPage);
85 m_webPage->triggerAction(QWebPage::Reload);
86 return true;
87 }
88
invoke() const89 bool ScriptItem::invoke() const
90 {
91 //qDebug() << ">>>ScriptItem::invoke";
92 Q_ASSERT(m_webPage);
93 m_webPage->mainFrame()->evaluateJavaScript(script());
94 return true;
95 }
96
invoke() const97 bool BackForwardItem::invoke() const
98 {
99 //qDebug() << ">>>BackForwardItem::invoke";
100 Q_ASSERT(m_webPage);
101 if (!m_howFar)
102 return false;
103
104 if (m_howFar > 0) {
105 for (int i = 0; i != m_howFar; ++i)
106 m_webPage->triggerAction(QWebPage::Forward);
107 } else {
108 for (int i = 0; i != m_howFar; --i)
109 m_webPage->triggerAction(QWebPage::Back);
110 }
111 return true;
112 }
113
LayoutTestController(WebCore::DumpRenderTree * drt)114 LayoutTestController::LayoutTestController(WebCore::DumpRenderTree *drt)
115 : QObject()
116 , m_drt(drt)
117 {
118 reset();
119 }
120
reset()121 void LayoutTestController::reset()
122 {
123 m_isLoading = true;
124 m_textDump = false;
125 m_dumpBackForwardList = false;
126 m_dumpChildrenAsText = false;
127 m_canOpenWindows = false;
128 m_waitForDone = false;
129 m_dumpTitleChanges = false;
130 m_dumpDatabaseCallbacks = false;
131 m_timeoutTimer.stop();
132 m_topLoadingFrame = 0;
133 qt_dump_editing_callbacks(false);
134 qt_dump_resource_load_callbacks(false);
135 QWebSettings::globalSettings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, false);
136 }
137
processWork()138 void LayoutTestController::processWork()
139 {
140 qDebug() << ">>>processWork";
141
142 // if we didn't start a new load, then we finished all the commands, so we're ready to dump state
143 if (!WorkQueue::shared()->processWork() && !shouldWaitUntilDone()) {
144 emit done();
145 m_isLoading = false;
146 }
147 }
148
149 // Called on loadFinished on mainFrame.
maybeDump(bool success)150 void LayoutTestController::maybeDump(bool success)
151 {
152 Q_ASSERT(sender() == m_topLoadingFrame);
153
154 m_topLoadingFrame = 0;
155 WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
156
157 if (!shouldWaitUntilDone()) {
158 if (WorkQueue::shared()->count())
159 QTimer::singleShot(0, this, SLOT(processWork()));
160 else {
161 if (success)
162 emit done();
163 m_isLoading = false;
164 }
165 }
166 }
167
waitUntilDone()168 void LayoutTestController::waitUntilDone()
169 {
170 //qDebug() << ">>>>waitForDone";
171 m_waitForDone = true;
172 m_timeoutTimer.start(11000, this);
173 }
174
notifyDone()175 void LayoutTestController::notifyDone()
176 {
177 //qDebug() << ">>>>notifyDone";
178 if (!m_timeoutTimer.isActive())
179 return;
180 m_timeoutTimer.stop();
181 emit done();
182 m_isLoading = false;
183 }
184
windowCount()185 int LayoutTestController::windowCount()
186 {
187 return m_drt->windowCount();
188 }
189
clearBackForwardList()190 void LayoutTestController::clearBackForwardList()
191 {
192 m_drt->webPage()->history()->clear();
193 }
194
dumpEditingCallbacks()195 void LayoutTestController::dumpEditingCallbacks()
196 {
197 qDebug() << ">>>dumpEditingCallbacks";
198 qt_dump_editing_callbacks(true);
199 }
200
dumpResourceLoadCallbacks()201 void LayoutTestController::dumpResourceLoadCallbacks()
202 {
203 qt_dump_resource_load_callbacks(true);
204 }
205
queueBackNavigation(int howFarBackward)206 void LayoutTestController::queueBackNavigation(int howFarBackward)
207 {
208 //qDebug() << ">>>queueBackNavigation" << howFarBackward;
209 WorkQueue::shared()->queue(new BackItem(howFarBackward, m_drt->webPage()));
210 }
211
queueForwardNavigation(int howFarForward)212 void LayoutTestController::queueForwardNavigation(int howFarForward)
213 {
214 //qDebug() << ">>>queueForwardNavigation" << howFarForward;
215 WorkQueue::shared()->queue(new ForwardItem(howFarForward, m_drt->webPage()));
216 }
217
queueLoad(const QString & url,const QString & target)218 void LayoutTestController::queueLoad(const QString &url, const QString &target)
219 {
220 //qDebug() << ">>>queueLoad" << url << target;
221 QUrl mainResourceUrl = m_drt->webPage()->mainFrame()->url();
222 QString absoluteUrl = mainResourceUrl.resolved(QUrl(url)).toEncoded();
223 WorkQueue::shared()->queue(new LoadItem(absoluteUrl, target, m_drt->webPage()));
224 }
225
queueReload()226 void LayoutTestController::queueReload()
227 {
228 //qDebug() << ">>>queueReload";
229 WorkQueue::shared()->queue(new ReloadItem(m_drt->webPage()));
230 }
231
queueScript(const QString & url)232 void LayoutTestController::queueScript(const QString &url)
233 {
234 //qDebug() << ">>>queueScript" << url;
235 WorkQueue::shared()->queue(new ScriptItem(url, m_drt->webPage()));
236 }
237
provisionalLoad()238 void LayoutTestController::provisionalLoad()
239 {
240 QWebFrame *frame = qobject_cast<QWebFrame*>(sender());
241 if (!m_topLoadingFrame && m_isLoading)
242 m_topLoadingFrame = frame;
243 }
244
timerEvent(QTimerEvent * ev)245 void LayoutTestController::timerEvent(QTimerEvent *ev)
246 {
247 if (ev->timerId() == m_timeoutTimer.timerId()) {
248 qDebug() << ">>>>>>>>>>>>> timeout";
249 notifyDone();
250 } else {
251 QObject::timerEvent(ev);
252 }
253 }
254
encodeHostName(const QString & host)255 QString LayoutTestController::encodeHostName(const QString &host)
256 {
257 QString encoded = QString::fromLatin1(QUrl::toAce(host + QLatin1String(".no")));
258 encoded.truncate(encoded.length() - 3); // strip .no
259 return encoded;
260 }
261
decodeHostName(const QString & host)262 QString LayoutTestController::decodeHostName(const QString &host)
263 {
264 QString decoded = QUrl::fromAce(host.toLatin1() + QByteArray(".no"));
265 decoded.truncate(decoded.length() - 3);
266 return decoded;
267 }
268
setJavaScriptProfilingEnabled(bool enable)269 void LayoutTestController::setJavaScriptProfilingEnabled(bool enable)
270 {
271 m_topLoadingFrame->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
272 qt_drt_setJavaScriptProfilingEnabled(m_topLoadingFrame, enable);
273 }
274
setFixedContentsSize(int width,int height)275 void LayoutTestController::setFixedContentsSize(int width, int height)
276 {
277 m_topLoadingFrame->page()->setFixedContentsSize(QSize(width, height));
278 }
279
setPrivateBrowsingEnabled(bool enable)280 void LayoutTestController::setPrivateBrowsingEnabled(bool enable)
281 {
282 QWebSettings::globalSettings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, enable);
283 }
284
pauseAnimationAtTimeOnElementWithId(const QString & animationName,double time,const QString & elementId)285 bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const QString &animationName,
286 double time,
287 const QString &elementId)
288 {
289 QWebFrame *frame = m_drt->webPage()->mainFrame();
290 Q_ASSERT(frame);
291 return qt_drt_pauseAnimation(frame, animationName, time, elementId);
292 }
293
pauseTransitionAtTimeOnElementWithId(const QString & propertyName,double time,const QString & elementId)294 bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const QString &propertyName,
295 double time,
296 const QString &elementId)
297 {
298 QWebFrame *frame = m_drt->webPage()->mainFrame();
299 Q_ASSERT(frame);
300 return qt_drt_pauseTransitionOfProperty(frame, propertyName, time, elementId);
301 }
302
numberOfActiveAnimations() const303 unsigned LayoutTestController::numberOfActiveAnimations() const
304 {
305 QWebFrame *frame = m_drt->webPage()->mainFrame();
306 Q_ASSERT(frame);
307 return qt_drt_numberOfActiveAnimations(frame);
308 }
309
disableImageLoading()310 void LayoutTestController::disableImageLoading()
311 {
312 // FIXME: Implement for testing fix for https://bugs.webkit.org/show_bug.cgi?id=27896
313 // Also need to make sure image loading is re-enabled for each new test.
314 }
315
dispatchPendingLoadRequests()316 void LayoutTestController::dispatchPendingLoadRequests()
317 {
318 // FIXME: Implement for testing fix for 6727495
319 }
320
setDatabaseQuota(int size)321 void LayoutTestController::setDatabaseQuota(int size)
322 {
323 if (!m_topLoadingFrame)
324 return;
325 m_topLoadingFrame->securityOrigin().setDatabaseQuota(size);
326 }
327
clearAllDatabases()328 void LayoutTestController::clearAllDatabases()
329 {
330 QWebDatabase::removeAllDatabases();
331 }
332
EventSender(QWebPage * parent)333 EventSender::EventSender(QWebPage *parent)
334 : QObject(parent)
335 {
336 m_page = parent;
337 }
338
mouseDown()339 void EventSender::mouseDown()
340 {
341 // qDebug() << "EventSender::mouseDown" << frame;
342 QMouseEvent event(QEvent::MouseButtonPress, m_mousePos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
343 QApplication::sendEvent(m_page, &event);
344 }
345
mouseUp()346 void EventSender::mouseUp()
347 {
348 // qDebug() << "EventSender::mouseUp" << frame;
349 QMouseEvent event(QEvent::MouseButtonRelease, m_mousePos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
350 QApplication::sendEvent(m_page, &event);
351 }
352
mouseMoveTo(int x,int y)353 void EventSender::mouseMoveTo(int x, int y)
354 {
355 // qDebug() << "EventSender::mouseMoveTo" << x << y;
356 m_mousePos = QPoint(x, y);
357 QMouseEvent event(QEvent::MouseMove, m_mousePos, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
358 QApplication::sendEvent(m_page, &event);
359 }
360
leapForward(int ms)361 void EventSender::leapForward(int ms)
362 {
363 m_timeLeap += ms;
364 qDebug() << "EventSender::leapForward" << ms;
365 }
366
keyDown(const QString & string,const QStringList & modifiers)367 void EventSender::keyDown(const QString &string, const QStringList &modifiers)
368 {
369 QString s = string;
370 Qt::KeyboardModifiers modifs = 0;
371 for (int i = 0; i < modifiers.size(); ++i) {
372 const QString &m = modifiers.at(i);
373 if (m == "ctrlKey")
374 modifs |= Qt::ControlModifier;
375 else if (m == "shiftKey")
376 modifs |= Qt::ShiftModifier;
377 else if (m == "altKey")
378 modifs |= Qt::AltModifier;
379 else if (m == "metaKey")
380 modifs |= Qt::MetaModifier;
381 }
382 int code = 0;
383 if (string.length() == 1) {
384 code = string.unicode()->unicode();
385 qDebug() << ">>>>>>>>> keyDown" << code << (char)code;
386 // map special keycodes used by the tests to something that works for Qt/X11
387 if (code == '\t') {
388 code = Qt::Key_Tab;
389 if (modifs == Qt::ShiftModifier)
390 code = Qt::Key_Backtab;
391 s = QString();
392 } else if (code == 127) {
393 code = Qt::Key_Backspace;
394 if (modifs == Qt::AltModifier)
395 modifs = Qt::ControlModifier;
396 s = QString();
397 } else if (code == 'o' && modifs == Qt::ControlModifier) {
398 s = QLatin1String("\n");
399 code = '\n';
400 modifs = 0;
401 } else if (code == 'y' && modifs == Qt::ControlModifier) {
402 s = QLatin1String("c");
403 code = 'c';
404 } else if (code == 'k' && modifs == Qt::ControlModifier) {
405 s = QLatin1String("x");
406 code = 'x';
407 } else if (code == 'a' && modifs == Qt::ControlModifier) {
408 s = QString();
409 code = Qt::Key_Home;
410 modifs = 0;
411 } else if (code == 0xf702) {
412 s = QString();
413 code = Qt::Key_Left;
414 if (modifs & Qt::MetaModifier) {
415 code = Qt::Key_Home;
416 modifs &= ~Qt::MetaModifier;
417 }
418 } else if (code == 0xf703) {
419 s = QString();
420 code = Qt::Key_Right;
421 if (modifs & Qt::MetaModifier) {
422 code = Qt::Key_End;
423 modifs &= ~Qt::MetaModifier;
424 }
425 } else if (code == 0xf700) {
426 s = QString();
427 code = Qt::Key_Up;
428 if (modifs & Qt::MetaModifier) {
429 code = Qt::Key_PageUp;
430 modifs &= ~Qt::MetaModifier;
431 }
432 } else if (code == 0xf701) {
433 s = QString();
434 code = Qt::Key_Down;
435 if (modifs & Qt::MetaModifier) {
436 code = Qt::Key_PageDown;
437 modifs &= ~Qt::MetaModifier;
438 }
439 } else if (code == 'a' && modifs == Qt::ControlModifier) {
440 s = QString();
441 code = Qt::Key_Home;
442 modifs = 0;
443 } else {
444 code = string.unicode()->toUpper().unicode();
445 }
446 }
447 QKeyEvent event(QEvent::KeyPress, code, modifs, s);
448 QApplication::sendEvent(m_page, &event);
449 }
450
frameUnderMouse() const451 QWebFrame *EventSender::frameUnderMouse() const
452 {
453 QWebFrame *frame = m_page->mainFrame();
454
455 redo:
456 QList<QWebFrame*> children = frame->childFrames();
457 for (int i = 0; i < children.size(); ++i) {
458 if (children.at(i)->geometry().contains(m_mousePos)) {
459 frame = children.at(i);
460 goto redo;
461 }
462 }
463 if (frame->geometry().contains(m_mousePos))
464 return frame;
465 return 0;
466 }
467
468
TextInputController(QWebPage * parent)469 TextInputController::TextInputController(QWebPage *parent)
470 : QObject(parent)
471 {
472 }
473
doCommand(const QString & command)474 void TextInputController::doCommand(const QString &command)
475 {
476 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
477 int keycode = 0;
478 if (command == "moveBackwardAndModifySelection:") {
479 modifiers |= Qt::ShiftModifier;
480 keycode = Qt::Key_Left;
481 } else if(command =="moveDown:") {
482 keycode = Qt::Key_Down;
483 } else if(command =="moveDownAndModifySelection:") {
484 modifiers |= Qt::ShiftModifier;
485 keycode = Qt::Key_Down;
486 } else if(command =="moveForward:") {
487 keycode = Qt::Key_Right;
488 } else if(command =="moveForwardAndModifySelection:") {
489 modifiers |= Qt::ShiftModifier;
490 keycode = Qt::Key_Right;
491 } else if(command =="moveLeft:") {
492 keycode = Qt::Key_Left;
493 } else if(command =="moveLeftAndModifySelection:") {
494 modifiers |= Qt::ShiftModifier;
495 keycode = Qt::Key_Left;
496 } else if(command =="moveRight:") {
497 keycode = Qt::Key_Right;
498 } else if(command =="moveRightAndModifySelection:") {
499 modifiers |= Qt::ShiftModifier;
500 keycode = Qt::Key_Right;
501 } else if(command =="moveToBeginningOfDocument:") {
502 modifiers |= Qt::ControlModifier;
503 keycode = Qt::Key_Home;
504 } else if(command =="moveToBeginningOfLine:") {
505 keycode = Qt::Key_Home;
506 // } else if(command =="moveToBeginningOfParagraph:") {
507 } else if(command =="moveToEndOfDocument:") {
508 modifiers |= Qt::ControlModifier;
509 keycode = Qt::Key_End;
510 } else if(command =="moveToEndOfLine:") {
511 keycode = Qt::Key_End;
512 // } else if(command =="moveToEndOfParagraph:") {
513 } else if(command =="moveUp:") {
514 keycode = Qt::Key_Up;
515 } else if(command =="moveUpAndModifySelection:") {
516 modifiers |= Qt::ShiftModifier;
517 keycode = Qt::Key_Up;
518 } else if(command =="moveWordBackward:") {
519 modifiers |= Qt::ControlModifier;
520 keycode = Qt::Key_Up;
521 } else if(command =="moveWordBackwardAndModifySelection:") {
522 modifiers |= Qt::ShiftModifier;
523 modifiers |= Qt::ControlModifier;
524 keycode = Qt::Key_Left;
525 } else if(command =="moveWordForward:") {
526 modifiers |= Qt::ControlModifier;
527 keycode = Qt::Key_Right;
528 } else if(command =="moveWordForwardAndModifySelection:") {
529 modifiers |= Qt::ControlModifier;
530 modifiers |= Qt::ShiftModifier;
531 keycode = Qt::Key_Right;
532 } else if(command =="moveWordLeft:") {
533 modifiers |= Qt::ControlModifier;
534 keycode = Qt::Key_Left;
535 } else if(command =="moveWordRight:") {
536 modifiers |= Qt::ControlModifier;
537 keycode = Qt::Key_Left;
538 } else if(command =="moveWordRightAndModifySelection:") {
539 modifiers |= Qt::ShiftModifier;
540 modifiers |= Qt::ControlModifier;
541 keycode = Qt::Key_Right;
542 } else if(command =="moveWordLeftAndModifySelection:") {
543 modifiers |= Qt::ShiftModifier;
544 modifiers |= Qt::ControlModifier;
545 keycode = Qt::Key_Left;
546 } else if(command =="pageDown:") {
547 keycode = Qt::Key_PageDown;
548 } else if(command =="pageUp:") {
549 keycode = Qt::Key_PageUp;
550 } else if(command == "deleteWordBackward:") {
551 modifiers |= Qt::ControlModifier;
552 keycode = Qt::Key_Backspace;
553 } else if(command == "deleteBackward:") {
554 keycode = Qt::Key_Backspace;
555 } else if(command == "deleteForward:") {
556 keycode = Qt::Key_Delete;
557 }
558 QKeyEvent event(QEvent::KeyPress, keycode, modifiers);
559 QApplication::sendEvent(parent(), &event);
560 QKeyEvent event2(QEvent::KeyRelease, keycode, modifiers);
561 QApplication::sendEvent(parent(), &event2);
562 }
563
GCController(QWebPage * parent)564 GCController::GCController(QWebPage* parent)
565 : QObject(parent)
566 {
567 }
568
569 extern int qt_drt_javaScriptObjectsCount();
570 extern void qt_drt_garbageCollector_collect();
571
572 extern void qt_drt_garbageCollector_collectOnAlternateThread(bool waitUntilDone);
573
collect() const574 void GCController::collect() const
575 {
576 qt_drt_garbageCollector_collect();
577 }
578
collectOnAlternateThread(bool waitUntilDone) const579 void GCController::collectOnAlternateThread(bool waitUntilDone) const
580 {
581 qt_drt_garbageCollector_collectOnAlternateThread(waitUntilDone);
582 }
583
getJSObjectCount() const584 size_t GCController::getJSObjectCount() const
585 {
586 return qt_drt_javaScriptObjectsCount();
587 }
588