WebCore:
[WebKit-https.git] / WebKit / qt / Api / qwebpage.cpp
1 /*
2     Copyright (C) 2007 Trolltech ASA
3     Copyright (C) 2007 Staikos Computing Services Inc.
4     Copyright (C) 2007 Apple Inc.
5
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Library General Public
8     License as published by the Free Software Foundation; either
9     version 2 of the License, or (at your option) any later version.
10
11     This library is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14     Library General Public License for more details.
15
16     You should have received a copy of the GNU Library General Public License
17     along with this library; see the file COPYING.LIB.  If not, write to
18     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19     Boston, MA 02110-1301, USA.
20
21     This class provides all functionality needed for loading images, style sheets and html
22     pages from the web. It has a memory cache for these objects.
23 */
24 #include "config.h"
25 #include "qwebpage.h"
26 #include "qwebframe.h"
27 #include "qwebpage_p.h"
28 #include "qwebframe_p.h"
29 #include "qwebnetworkinterface.h"
30 #include "qwebpagehistory.h"
31 #include "qwebpagehistory_p.h"
32 #include "qwebsettings.h"
33
34 #include "Frame.h"
35 #include "ChromeClientQt.h"
36 #include "ContextMenu.h"
37 #include "ContextMenuClientQt.h"
38 #include "DragClientQt.h"
39 #include "DragController.h"
40 #include "DragData.h"
41 #include "EditorClientQt.h"
42 #include "Settings.h"
43 #include "Page.h"
44 #include "FrameLoader.h"
45 #include "FrameLoadRequest.h"
46 #include "KURL.h"
47 #include "Image.h"
48 #include "IconDatabase.h"
49 #include "InspectorClientQt.h"
50 #include "InspectorController.h"
51 #include "FocusController.h"
52 #include "Editor.h"
53 #include "PlatformScrollBar.h"
54 #include "PlatformKeyboardEvent.h"
55 #include "PlatformWheelEvent.h"
56 #include "ProgressTracker.h"
57 #include "RefPtr.h"
58 #include "HashMap.h"
59 #include "HitTestResult.h"
60 #include "WindowFeatures.h"
61 #include "LocalizedStrings.h"
62
63 #include <QDebug>
64 #include <QDragEnterEvent>
65 #include <QDragLeaveEvent>
66 #include <QDragMoveEvent>
67 #include <QDropEvent>
68 #include <QFileDialog>
69 #include <QHttpRequestHeader>
70 #include <QInputDialog>
71 #include <QMessageBox>
72 #include <QNetworkProxy>
73 #include <QUndoStack>
74 #include <QUrl>
75 #include <QPainter>
76
77 using namespace WebCore;
78
79 QWebPagePrivate::QWebPagePrivate(QWebPage *qq)
80     : q(qq)
81     , modified(false)
82 {
83     q->setMouseTracking(true);
84     q->setFocusPolicy(Qt::ClickFocus);
85     chromeClient = new ChromeClientQt(q);
86     contextMenuClient = new ContextMenuClientQt();
87     editorClient = new EditorClientQt(q);
88     page = new Page(chromeClient, contextMenuClient, editorClient,
89                     new DragClientQt(q), new InspectorClientQt(q));
90
91     // ### should be configurable
92     page->settings()->setDefaultTextEncodingName("iso-8859-1");
93
94     settings = new QWebSettings(page->settings());
95
96     undoStack = 0;
97     mainFrame = 0;
98     networkInterface = 0;
99     insideOpenCall = false;
100
101     history.d = new QWebPageHistoryPrivate(page->backForwardList());
102     memset(actions, 0, sizeof(actions));
103 }
104
105 QWebPagePrivate::~QWebPagePrivate()
106 {
107     delete undoStack;
108     delete settings;
109     delete page;
110 }
111
112 QWebPage::NavigationRequestResponse QWebPagePrivate::navigationRequested(QWebFrame *frame, const QWebNetworkRequest &request, QWebPage::NavigationType type)
113 {
114     if (insideOpenCall
115         && frame == mainFrame)
116         return QWebPage::AcceptNavigationRequest;
117     return q->navigationRequested(frame, request, type);
118 }
119
120 void QWebPagePrivate::createMainFrame()
121 {
122     if (!mainFrame) {
123         QWebFrameData frameData;
124         frameData.ownerElement = 0;
125         frameData.allowsScrolling = true;
126         frameData.marginWidth = 0;
127         frameData.marginHeight = 0;
128         mainFrame = new QWebFrame(q, &frameData);
129         QObject::connect(mainFrame, SIGNAL(titleChanged(const QString&)),
130                 q, SIGNAL(titleChanged(const QString&)));
131         mainFrame->d->frameView->setFrameGeometry(q->geometry());
132
133         emit q->frameCreated(mainFrame);
134     }
135 }
136
137 static QWebPage::WebAction webActionForContextMenuAction(WebCore::ContextMenuAction action)
138 {
139     switch (action) {
140         case WebCore::ContextMenuItemTagOpenLink: return QWebPage::OpenLink;
141         case WebCore::ContextMenuItemTagOpenLinkInNewWindow: return QWebPage::OpenLinkInNewWindow;
142         case WebCore::ContextMenuItemTagDownloadLinkToDisk: return QWebPage::DownloadLinkToDisk;
143         case WebCore::ContextMenuItemTagCopyLinkToClipboard: return QWebPage::CopyLinkToClipboard;
144         case WebCore::ContextMenuItemTagOpenImageInNewWindow: return QWebPage::OpenImageInNewWindow;
145         case WebCore::ContextMenuItemTagDownloadImageToDisk: return QWebPage::DownloadImageToDisk;
146         case WebCore::ContextMenuItemTagCopyImageToClipboard: return QWebPage::CopyImageToClipboard;
147         case WebCore::ContextMenuItemTagOpenFrameInNewWindow: return QWebPage::OpenFrameInNewWindow;
148         case WebCore::ContextMenuItemTagCopy: return QWebPage::Copy;
149         case WebCore::ContextMenuItemTagGoBack: return QWebPage::GoBack;
150         case WebCore::ContextMenuItemTagGoForward: return QWebPage::GoForward;
151         case WebCore::ContextMenuItemTagStop: return QWebPage::Stop;
152         case WebCore::ContextMenuItemTagReload: return QWebPage::Reload;
153         case WebCore::ContextMenuItemTagCut: return QWebPage::Cut;
154         case WebCore::ContextMenuItemTagPaste: return QWebPage::Paste;
155         case WebCore::ContextMenuItemTagDefaultDirection: return QWebPage::SetTextDirectionDefault;
156         case WebCore::ContextMenuItemTagLeftToRight: return QWebPage::SetTextDirectionLeftToRight;
157         case WebCore::ContextMenuItemTagRightToLeft: return QWebPage::SetTextDirectionRightToLeft;
158         case WebCore::ContextMenuItemTagBold: return QWebPage::ToggleBold;
159         case WebCore::ContextMenuItemTagItalic: return QWebPage::ToggleItalic;
160         case WebCore::ContextMenuItemTagUnderline: return QWebPage::ToggleUnderline;
161         case WebCore::ContextMenuItemTagInspectElement: return QWebPage::InspectElement;
162         default: break;
163     }
164     return QWebPage::NoWebAction;
165 }
166
167 QMenu *QWebPagePrivate::createContextMenu(const WebCore::ContextMenu *webcoreMenu, const QList<WebCore::ContextMenuItem> *items)
168 {
169     QMenu *menu = new QMenu;
170     for (int i = 0; i < items->count(); ++i) {
171         const ContextMenuItem &item = items->at(i);
172         switch (item.type()) {
173             case WebCore::ActionType: {
174                 QWebPage::WebAction action = webActionForContextMenuAction(item.action());
175                 QAction *a = q->action(action);
176                 if (a) {
177                     ContextMenuItem it(item);
178                     webcoreMenu->checkOrEnableIfNeeded(it);
179                     PlatformMenuItemDescription desc = it.releasePlatformDescription();
180                     a->setEnabled(desc.enabled);
181                     a->setChecked(desc.checked);
182
183                     menu->addAction(a);
184                 }
185                 break;
186             }
187             case WebCore::SeparatorType:
188                 menu->addSeparator();
189                 break;
190             case WebCore::SubmenuType: {
191                 QMenu *subMenu = createContextMenu(webcoreMenu, item.platformSubMenu());
192                 if (!subMenu->actions().isEmpty()) {
193                     subMenu->setTitle(item.title());
194                     menu->addAction(subMenu->menuAction());
195                 } else {
196                     delete subMenu;
197                 }
198                 break;
199             }
200         }
201     }
202     return menu;
203 }
204
205 QWebFrame *QWebPagePrivate::frameAt(const QPoint &pos) const
206 {
207     QWebFrame *frame = mainFrame;
208
209 redo:
210     QList<QWebFrame*> children = frame->childFrames();
211     for (int i = 0; i < children.size(); ++i) {
212         if (children.at(i)->geometry().contains(pos)) {
213             frame = children.at(i);
214             goto redo;
215         }
216     }
217     if (frame->geometry().contains(pos))
218         return frame;
219     return 0;
220 }
221
222 void QWebPagePrivate::_q_webActionTriggered(bool checked)
223 {
224     QAction *a = qobject_cast<QAction *>(q->sender());
225     if (!a)
226         return;
227     QWebPage::WebAction action = static_cast<QWebPage::WebAction>(a->data().toInt());
228     q->triggerAction(action, checked);
229 }
230
231 void QWebPagePrivate::updateAction(QWebPage::WebAction action)
232 {
233     QAction *a = actions[action];
234     if (!a || !mainFrame)
235         return;
236
237     WebCore::FrameLoader *loader = mainFrame->d->frame->loader();
238     WebCore::Editor *editor = page->focusController()->focusedOrMainFrame()->editor();
239
240     bool enabled = a->isEnabled();
241
242     switch (action) {
243         case QWebPage::GoBack:
244             enabled = loader->canGoBackOrForward(-1);
245             break;
246         case QWebPage::GoForward:
247             enabled = loader->canGoBackOrForward(1);
248             break;
249         case QWebPage::Stop:
250             enabled = loader->isLoading();
251             break;
252         case QWebPage::Reload:
253             enabled = !loader->isLoading();
254             break;
255         case QWebPage::Cut:
256             enabled = editor->canCut();
257             break;
258         case QWebPage::Copy:
259             enabled = editor->canCopy();
260             break;
261         case QWebPage::Paste:
262             enabled = editor->canPaste();
263             break;
264         case QWebPage::Undo:
265         case QWebPage::Redo:
266             // those two are handled by QUndoStack
267             break;
268         default: break;
269     }
270
271     a->setEnabled(enabled);
272 }
273
274 void QWebPagePrivate::updateNavigationActions()
275 {
276     updateAction(QWebPage::GoBack);
277     updateAction(QWebPage::GoForward);
278     updateAction(QWebPage::Stop);
279     updateAction(QWebPage::Reload);
280 }
281
282 void QWebPagePrivate::updateEditorActions()
283 {
284     updateAction(QWebPage::Cut);
285     updateAction(QWebPage::Copy);
286     updateAction(QWebPage::Paste);
287 }
288
289 QWebPage::QWebPage(QWidget *parent)
290     : QWidget(parent)
291     , d(new QWebPagePrivate(this))
292 {
293
294     QPalette pal = palette();
295     pal.setBrush(QPalette::Background, Qt::white);
296
297     setAttribute(Qt::WA_OpaquePaintEvent);
298
299     setPalette(pal);
300     setAcceptDrops(true);
301     connect(this, SIGNAL(loadProgressChanged(int)), this, SLOT(_q_onLoadProgressChanged(int)));
302 }
303
304 QWebPage::~QWebPage()
305 {
306     FrameLoader *loader = d->mainFrame->d->frame->loader();
307     if (loader)
308         loader->detachFromParent();
309     delete d;
310 }
311
312 void QWebPage::open(const QUrl &url)
313 {
314     open(QWebNetworkRequest(url));
315 }
316
317 void QWebPage::open(const QWebNetworkRequest &req)
318 {
319     d->insideOpenCall = true;
320
321     QUrl url = req.url();
322     QHttpRequestHeader httpHeader = req.httpHeader();
323     QByteArray postData = req.postData();
324
325     WebCore::ResourceRequest request(KURL(url.toString()));
326
327     QString method = httpHeader.method();
328     if (!method.isEmpty())
329         request.setHTTPMethod(method);
330
331     QList<QPair<QString, QString> > values = httpHeader.values();
332     for (int i = 0; i < values.size(); ++i) {
333         const QPair<QString, QString> &val = values.at(i);
334         request.addHTTPHeaderField(val.first, val.second);
335     }
336
337     if (!postData.isEmpty()) {
338         WTF::RefPtr<WebCore::FormData> formData = new WebCore::FormData(postData.constData(), postData.size());
339         request.setHTTPBody(formData);
340     }
341
342     mainFrame()->d->frame->loader()->load(request);
343     d->insideOpenCall = false;
344 }
345
346 QUrl QWebPage::url() const
347 {
348     return QUrl((QString)mainFrame()->d->frame->loader()->url().string());
349 }
350
351 QString QWebPage::title() const
352 {
353     return mainFrame()->title();
354 }
355
356 QWebFrame *QWebPage::mainFrame() const
357 {
358     d->createMainFrame();
359     return d->mainFrame;
360 }
361
362 QSize QWebPage::sizeHint() const
363 {
364     return QSize(800, 600);
365 }
366
367 void QWebPage::stop()
368 {
369     triggerAction(Stop);
370 }
371
372 QWebPageHistory *QWebPage::history() const
373 {
374     return &d->history;
375 }
376
377 void QWebPage::javaScriptConsoleMessage(const QString& message, unsigned int lineNumber, const QString& sourceID)
378 {
379 }
380
381 void QWebPage::javaScriptAlert(QWebFrame *frame, const QString& msg)
382 {
383     //FIXME frame pos...
384     QMessageBox::information(this, title(), msg, QMessageBox::Ok);
385 }
386
387 bool QWebPage::javaScriptConfirm(QWebFrame *frame, const QString& msg)
388 {
389     //FIXME frame pos...
390     return 0 == QMessageBox::information(this, title(), msg, QMessageBox::Yes, QMessageBox::No);
391 }
392
393 bool QWebPage::javaScriptPrompt(QWebFrame *frame, const QString& msg, const QString& defaultValue, QString* result)
394 {
395     //FIXME frame pos...
396     bool ok = false;
397 #ifndef QT_NO_INPUTDIALOG
398     QString x = QInputDialog::getText(this, title(), msg, QLineEdit::Normal, defaultValue, &ok);
399     if (ok && result) {
400         *result = x;
401     }
402 #endif
403     return ok;
404 }
405
406 QWebPage *QWebPage::createWindow()
407 {
408     return 0;
409 }
410
411 QWebPage *QWebPage::createModalDialog()
412 {
413     return 0;
414 }
415
416 QObject *QWebPage::createPlugin(const QString &classid, const QUrl &url, const QStringList &paramNames, const QStringList &paramValues)
417 {
418     Q_UNUSED(classid)
419     Q_UNUSED(url)
420     Q_UNUSED(paramNames)
421     Q_UNUSED(paramValues)
422     return 0;
423 }
424
425 static WebCore::FrameLoadRequest frameLoadRequest(const QUrl &url, WebCore::Frame *frame)
426 {
427     WebCore::ResourceRequest rr(WebCore::KURL(url.toString()),
428                                 frame->loader()->outgoingReferrer());
429     return WebCore::FrameLoadRequest(rr);
430 }
431
432 static void openNewWindow(const QUrl& url, WebCore::Frame* frame)
433 {
434     if (Page* oldPage = frame->page()) {
435         WindowFeatures features;
436         if (Page* newPage = oldPage->chrome()->createWindow(frame,
437                 frameLoadRequest(url, frame), features))
438             newPage->chrome()->show();
439     }
440 }
441
442 void QWebPage::triggerAction(WebAction action, bool checked)
443 {
444     WebCore::Frame *frame = d->page->focusController()->focusedOrMainFrame();
445     WebCore::Editor *editor = frame->editor();
446     const char *command = 0;
447
448     switch (action) {
449         case OpenLink:
450             if (QWebFrame *targetFrame = d->currentContext.targetFrame()) {
451                 WTF::RefPtr<WebCore::Frame> wcFrame = targetFrame->d->frame;
452                 targetFrame->d->frame->loader()->load(frameLoadRequest(d->currentContext.linkUrl(), wcFrame.get()),
453                                                       /*lockHistory*/ false,
454                                                       /*userGesture*/ true,
455                                                       /*event*/ 0,
456                                                       /*HTMLFormElement*/ 0,
457                                                       /*formValues*/
458                                                       WTF::HashMap<String, String>());
459                 break;
460             } else {
461             }
462             // fall through
463         case OpenLinkInNewWindow:
464             openNewWindow(d->currentContext.linkUrl(), frame);
465             break;
466         case OpenFrameInNewWindow:
467             break;
468         case DownloadLinkToDisk:
469         case CopyLinkToClipboard:
470             editor->copyURL(WebCore::KURL(d->currentContext.linkUrl().toString()), d->currentContext.text());
471             break;
472         case OpenImageInNewWindow:
473             openNewWindow(d->currentContext.imageUrl(), frame);
474             break;
475         case DownloadImageToDisk:
476         case CopyImageToClipboard:
477             break;
478         case GoBack:
479             d->page->goBack();
480             break;
481         case GoForward:
482             d->page->goForward();
483             break;
484         case Stop:
485             mainFrame()->d->frame->loader()->stopForUserCancel();
486             break;
487         case Reload:
488             mainFrame()->d->frame->loader()->reload();
489             break;
490         case Cut:
491             command = "Cut";
492             break;
493         case Copy:
494             command = "Copy";
495             break;
496         case Paste:
497             command = "Paste";
498             break;
499
500         case Undo:
501             command = "Undo";
502             break;
503         case Redo:
504             command = "Redo";
505             break;
506
507         case MoveToNextChar:
508             command = "MoveForward";
509             break;
510         case MoveToPreviousChar:
511             command = "MoveBackward";
512             break;
513         case MoveToNextWord:
514             command = "MoveWordForward";
515             break;
516         case MoveToPreviousWord:
517             command = "MoveWordBackward";
518             break;
519         case MoveToNextLine:
520             command = "MoveDown";
521             break;
522         case MoveToPreviousLine:
523             command = "MoveUp";
524             break;
525         case MoveToStartOfLine:
526             command = "MoveToBeginningOfLine";
527             break;
528         case MoveToEndOfLine:
529             command = "MoveToEndOfLine";
530             break;
531         case MoveToStartOfBlock:
532             command = "MoveToBeginningOfParagraph";
533             break;
534         case MoveToEndOfBlock:
535             command = "MoveToEndOfParagraph";
536             break;
537         case MoveToStartOfDocument:
538             command = "MoveToBeginningOfDocument";
539             break;
540         case MoveToEndOfDocument:
541             command = "MoveToEndOfDocument";
542             break;
543         case SelectNextChar:
544             command = "MoveForwardAndModifySelection";
545             break;
546         case SelectPreviousChar:
547             command = "MoveBackwardAndModifySelection";
548             break;
549         case SelectNextWord:
550             command = "MoveWordForwardAndModifySelection";
551             break;
552         case SelectPreviousWord:
553             command = "MoveWordBackwardAndModifySelection";
554             break;
555         case SelectNextLine:
556             command = "MoveDownAndModifySelection";
557             break;
558         case SelectPreviousLine:
559             command = "MoveUpAndModifySelection";
560             break;
561         case SelectStartOfLine:
562             command = "MoveToBeginningOfLineAndModifySelection";
563             break;
564         case SelectEndOfLine:
565             command = "MoveToEndOfLineAndModifySelection";
566             break;
567         case SelectStartOfBlock:
568             command = "MoveToBeginningOfParagraphAndModifySelection";
569             break;
570         case SelectEndOfBlock:
571             command = "MoveToEndOfParagraphAndModifySelection";
572             break;
573         case SelectStartOfDocument:
574             command = "MoveToBeginningOfDocumentAndModifySelection";
575             break;
576         case SelectEndOfDocument:
577             command = "MoveToEndOfDocumentAndModifySelection";
578             break;
579         case DeleteStartOfWord:
580             command = "DeleteWordBackward";
581             break;
582         case DeleteEndOfWord:
583             command = "DeleteWordForward";
584             break;
585
586         case SetTextDirectionDefault:
587             editor->setBaseWritingDirection("inherit");
588             break;
589         case SetTextDirectionLeftToRight:
590             editor->setBaseWritingDirection("ltr");
591             break;
592         case SetTextDirectionRightToLeft:
593             editor->setBaseWritingDirection("rtl");
594             break;
595
596         case ToggleBold:
597             command = "ToggleBold";
598             break;
599         case ToggleItalic:
600             command = "ToggleItalic";
601             break;
602         case ToggleUnderline:
603             editor->toggleUnderline();
604
605         case InspectElement:
606             d->page->inspectorController()->inspect(d->currentContext.d->innerNonSharedNode.get());
607             break;
608
609         default: break;
610     }
611
612     if (command)
613         editor->command(command).execute();
614 }
615
616 QWebPage::NavigationRequestResponse QWebPage::navigationRequested(QWebFrame *frame, const QWebNetworkRequest &request, QWebPage::NavigationType type)
617 {
618     Q_UNUSED(request)
619     return AcceptNavigationRequest;
620 }
621
622 QString QWebPage::selectedText() const
623 {
624     return d->page->focusController()->focusedOrMainFrame()->selectedText();
625 }
626
627 QAction *QWebPage::action(WebAction action) const
628 {
629     if (action == QWebPage::NoWebAction) return 0;
630     if (d->actions[action])
631         return d->actions[action];
632
633     QString text;
634     bool checkable = false;
635
636     switch (action) {
637         case OpenLink:
638             text = contextMenuItemTagOpenLink();
639             break;
640         case OpenLinkInNewWindow:
641             text = contextMenuItemTagOpenLinkInNewWindow();
642             break;
643         case OpenFrameInNewWindow:
644             text = contextMenuItemTagOpenFrameInNewWindow();
645             break;
646
647         case DownloadLinkToDisk:
648             text = contextMenuItemTagDownloadLinkToDisk();
649             break;
650         case CopyLinkToClipboard:
651             text = contextMenuItemTagCopyLinkToClipboard();
652             break;
653
654         case OpenImageInNewWindow:
655             text = contextMenuItemTagOpenImageInNewWindow();
656             break;
657         case DownloadImageToDisk:
658             text = contextMenuItemTagDownloadImageToDisk();
659             break;
660         case CopyImageToClipboard:
661             text = contextMenuItemTagCopyImageToClipboard();
662             break;
663
664         case GoBack:
665             text = contextMenuItemTagGoBack();
666             break;
667         case GoForward:
668             text = contextMenuItemTagGoForward();
669             break;
670         case Stop:
671             text = contextMenuItemTagStop();
672             break;
673         case Reload:
674             text = contextMenuItemTagReload();
675             break;
676
677         case Cut:
678             text = contextMenuItemTagCut();
679             break;
680         case Copy:
681             text = contextMenuItemTagCopy();
682             break;
683         case Paste:
684             text = contextMenuItemTagPaste();
685             break;
686
687         case Undo: {
688             QAction *a = undoStack()->createUndoAction(d->q);
689             d->actions[action] = a;
690             return a;
691         }
692         case Redo: {
693             QAction *a = undoStack()->createRedoAction(d->q);
694             d->actions[action] = a;
695             return a;
696         }
697         case MoveToNextChar:
698         case MoveToPreviousChar:
699         case MoveToNextWord:
700         case MoveToPreviousWord:
701         case MoveToNextLine:
702         case MoveToPreviousLine:
703         case MoveToStartOfLine:
704         case MoveToEndOfLine:
705         case MoveToStartOfBlock:
706         case MoveToEndOfBlock:
707         case MoveToStartOfDocument:
708         case MoveToEndOfDocument:
709         case SelectNextChar:
710         case SelectPreviousChar:
711         case SelectNextWord:
712         case SelectPreviousWord:
713         case SelectNextLine:
714         case SelectPreviousLine:
715         case SelectStartOfLine:
716         case SelectEndOfLine:
717         case SelectStartOfBlock:
718         case SelectEndOfBlock:
719         case SelectStartOfDocument:
720         case SelectEndOfDocument:
721         case DeleteStartOfWord:
722         case DeleteEndOfWord:
723             break; // ####
724
725         case SetTextDirectionDefault:
726             text = contextMenuItemTagDefaultDirection();
727             break;
728         case SetTextDirectionLeftToRight:
729             text = contextMenuItemTagLeftToRight();
730             checkable = true;
731             break;
732         case SetTextDirectionRightToLeft:
733             text = contextMenuItemTagRightToLeft();
734             checkable = true;
735             break;
736
737         case ToggleBold:
738             text = contextMenuItemTagBold();
739             checkable = true;
740             break;
741         case ToggleItalic:
742             text = contextMenuItemTagItalic();
743             checkable = true;
744             break;
745         case ToggleUnderline:
746             text = contextMenuItemTagUnderline();
747             checkable = true;
748             break;
749
750         case InspectElement:
751             text = contextMenuItemTagInspectElement();
752             break;
753
754         case NoWebAction:
755             return 0;
756     }
757
758     if (text.isEmpty())
759         return 0;
760
761     QAction *a = new QAction(d->q);
762     a->setText(text);
763     a->setData(action);
764     a->setCheckable(checkable);
765
766     connect(a, SIGNAL(triggered(bool)),
767             this, SLOT(_q_webActionTriggered(bool)));
768
769     d->actions[action] = a;
770     d->updateAction(action);
771     return a;
772 }
773
774 /*!
775   Returns true if the page contains unsubmitted form data.
776 */
777 bool QWebPage::isModified() const
778 {
779     return d->modified;
780 }
781
782
783 QUndoStack *QWebPage::undoStack() const
784 {
785     if (!d->undoStack)
786         d->undoStack = new QUndoStack(const_cast<QWebPage *>(this));
787
788     return d->undoStack;
789 }
790
791 static inline DragOperation dropActionToDragOp(Qt::DropActions actions)
792 {
793     unsigned result = 0;
794     if (actions & Qt::CopyAction)
795         result |= DragOperationCopy;
796     if (actions & Qt::MoveAction)
797         result |= DragOperationMove;
798     if (actions & Qt::LinkAction)
799         result |= DragOperationLink;
800     return (DragOperation)result;    
801 }
802
803 static inline Qt::DropAction dragOpToDropAction(unsigned actions)
804 {
805     Qt::DropAction result = Qt::IgnoreAction;
806     if (actions & DragOperationCopy)
807         result = Qt::CopyAction;
808     else if (actions & DragOperationMove)
809         result = Qt::MoveAction;
810     else if (actions & DragOperationLink)
811         result = Qt::LinkAction;
812     return result;    
813 }
814
815 void QWebPage::resizeEvent(QResizeEvent *e)
816 {
817     QWidget::resizeEvent(e);
818     if (mainFrame()->d->frame && mainFrame()->d->frameView) {
819         mainFrame()->d->frameView->setFrameGeometry(rect());
820         mainFrame()->d->frame->forceLayout();
821         mainFrame()->d->frame->view()->adjustViewSize();
822     }
823 }
824
825 void QWebPage::paintEvent(QPaintEvent *ev)
826 {
827 #ifdef QWEBKIT_TIME_RENDERING
828     QTime time;
829     time.start();
830 #endif
831
832     mainFrame()->layout();
833     QPainter p(this);
834
835     QVector<QRect> vector = ev->region().rects();
836     if (!vector.isEmpty()) {
837         for (int i = 0; i < vector.size(); ++i) {
838             mainFrame()->render(&p, vector.at(i));
839         }
840     } else {
841         mainFrame()->render(&p, ev->rect());
842     }
843
844 #ifdef    QWEBKIT_TIME_RENDERING
845     int elapsed = time.elapsed();
846     qDebug()<<"paint event on "<<ev->region()<<", took to render =  "<<elapsed;
847 #endif
848 }
849
850 void QWebPage::mouseMoveEvent(QMouseEvent *ev)
851 {
852     QWebFrame *f = d->currentFrame(ev->pos());
853     if (!f)
854         return;
855
856     QWebFramePrivate *frame = f->d;
857     if (!frame->frameView)
858         return;
859
860     frame->eventHandler->handleMouseMoveEvent(PlatformMouseEvent(ev, 0));
861     const int xOffset =
862         frame->horizontalScrollBar() ? frame->horizontalScrollBar()->value() : 0;
863     const int yOffset =
864         frame->verticalScrollBar() ? frame->verticalScrollBar()->value() : 0;
865     IntPoint pt(ev->x() + xOffset, ev->y() + yOffset);
866     WebCore::HitTestResult result = frame->eventHandler->hitTestResultAtPoint(pt, false);
867     WebCore::Element *link = result.URLElement();
868     if (link != frame->lastHoverElement) {
869         frame->lastHoverElement = link;
870         emit hoveringOverLink(result.absoluteLinkURL().prettyURL(), result.title(), result.textContent());
871     }
872 }
873
874 void QWebPage::mousePressEvent(QMouseEvent *ev)
875 {
876     d->frameUnderMouse = d->frameAt(ev->pos());
877     if (!d->frameUnderMouse)
878         return;
879
880     QWebFramePrivate *frame = d->frameUnderMouse->d;
881     if (!frame->eventHandler)
882         return;
883
884     frame->eventHandler->handleMousePressEvent(PlatformMouseEvent(ev, 1));
885
886     //FIXME need to keep track of subframe focus for key events!
887     frame->page->setFocus();
888 }
889
890 void QWebPage::mouseDoubleClickEvent(QMouseEvent *ev)
891 {
892     QWebFrame *f = d->currentFrame(ev->pos());
893     if (!f)
894         return;
895
896     QWebFramePrivate *frame = f->d;
897     if (!frame->eventHandler)
898         return;
899
900     frame->eventHandler->handleMousePressEvent(PlatformMouseEvent(ev, 2));
901
902     //FIXME need to keep track of subframe focus for key events!
903     frame->page->setFocus();
904 }
905
906 void QWebPage::mouseReleaseEvent(QMouseEvent *ev)
907 {
908     QWebFrame *f = d->currentFrame(ev->pos());
909     if (!f)
910         return;
911
912     QWebFramePrivate *frame = f->d;
913     if (!frame->frameView)
914         return;
915
916     frame->eventHandler->handleMouseReleaseEvent(PlatformMouseEvent(ev, 0));
917
918     //FIXME need to keep track of subframe focus for key events!
919     frame->page->setFocus();
920     d->frameUnderMouse = 0;
921 }
922
923 void QWebPage::contextMenuEvent(QContextMenuEvent *ev)
924 {
925     QWebFrame *f = d->currentFrame(ev->pos());
926     if (!f)
927         return;
928
929     QWebFramePrivate *frame = f->d;
930     if (!frame->eventHandler)
931         return;
932
933     d->page->contextMenuController()->clearContextMenu();
934     frame->eventHandler->sendContextMenuEvent(PlatformMouseEvent(ev, 1));
935     ContextMenu *menu = d->page->contextMenuController()->contextMenu();
936
937     QWebPageContext oldContext = d->currentContext;
938     d->currentContext = QWebPageContext(menu->hitTestResult());
939
940     const QList<ContextMenuItem> *items = menu->platformDescription();
941     QMenu *qmenu = d->createContextMenu(menu, items);
942     if (qmenu) {
943         qmenu->exec(ev->globalPos());
944         delete qmenu;
945     }
946     d->currentContext = oldContext;
947 }
948
949 void QWebPage::wheelEvent(QWheelEvent *ev)
950 {
951     QWebFramePrivate *frame = d->currentFrame(ev->pos())->d;
952
953     bool accepted = false;
954     if (frame->eventHandler) {
955         WebCore::PlatformWheelEvent pev(ev);
956         accepted = frame->eventHandler->handleWheelEvent(pev);
957     }
958
959     ev->setAccepted(accepted);
960
961     //FIXME need to keep track of subframe focus for key events!
962     frame->page->setFocus();
963
964     if (!ev->isAccepted())
965         QWidget::wheelEvent(ev);
966 }
967
968 void QWebPage::keyPressEvent(QKeyEvent *ev)
969 {
970     if (!mainFrame()->d->eventHandler)
971         return;
972
973     bool handled = false;
974     QWebFrame *frame = mainFrame();
975     WebCore::Editor *editor = frame->d->frame->editor();
976     if (editor->canEdit()) {
977         if (ev == QKeySequence::Cut) {
978             triggerAction(Cut);
979             handled = true;
980         } else if (ev == QKeySequence::Copy) {
981             triggerAction(Copy);
982             handled = true;
983         } else if (ev == QKeySequence::Paste) {
984             triggerAction(Paste);
985             handled = true;
986         } else if (ev == QKeySequence::Undo) {
987             triggerAction(Undo);
988             handled = true;
989         } else if (ev == QKeySequence::Redo) {
990             triggerAction(Redo);
991             handled = true;
992         } else if(ev == QKeySequence::MoveToNextChar) {
993             triggerAction(MoveToNextChar);
994             handled = true;
995         } else if(ev == QKeySequence::MoveToPreviousChar) {
996             triggerAction(MoveToPreviousChar);
997             handled = true;
998         } else if(ev == QKeySequence::MoveToNextWord) {
999             triggerAction(MoveToNextWord);
1000             handled = true;
1001         } else if(ev == QKeySequence::MoveToPreviousWord) {
1002             triggerAction(MoveToPreviousWord);
1003             handled = true;
1004         } else if(ev == QKeySequence::MoveToNextLine) {
1005             triggerAction(MoveToNextLine);
1006             handled = true;
1007         } else if(ev == QKeySequence::MoveToPreviousLine) {
1008             triggerAction(MoveToPreviousLine);
1009             handled = true;
1010 //             } else if(ev == QKeySequence::MoveToNextPage) {
1011 //             } else if(ev == QKeySequence::MoveToPreviousPage) {
1012         } else if(ev == QKeySequence::MoveToStartOfLine) {
1013             triggerAction(MoveToStartOfLine);
1014             handled = true;
1015         } else if(ev == QKeySequence::MoveToEndOfLine) {
1016             triggerAction(MoveToEndOfLine);
1017             handled = true;
1018         } else if(ev == QKeySequence::MoveToStartOfBlock) {
1019             triggerAction(MoveToStartOfBlock);
1020             handled = true;
1021         } else if(ev == QKeySequence::MoveToEndOfBlock) {
1022             triggerAction(MoveToEndOfBlock);
1023             handled = true;
1024         } else if(ev == QKeySequence::MoveToStartOfDocument) {
1025             triggerAction(MoveToStartOfDocument);
1026             handled = true;
1027         } else if(ev == QKeySequence::MoveToEndOfDocument) {
1028             triggerAction(MoveToEndOfDocument);
1029             handled = true;
1030         } else if(ev == QKeySequence::SelectNextChar) {
1031             triggerAction(SelectNextChar);
1032             handled = true;
1033         } else if(ev == QKeySequence::SelectPreviousChar) {
1034             triggerAction(SelectPreviousChar);
1035             handled = true;
1036         } else if(ev == QKeySequence::SelectNextWord) {
1037             triggerAction(SelectNextWord);
1038             handled = true;
1039         } else if(ev == QKeySequence::SelectPreviousWord) {
1040             triggerAction(SelectPreviousWord);
1041             handled = true;
1042         } else if(ev == QKeySequence::SelectNextLine) {
1043             triggerAction(SelectNextLine);
1044             handled = true;
1045         } else if(ev == QKeySequence::SelectPreviousLine) {
1046             triggerAction(SelectPreviousLine);
1047             handled = true;
1048 //             } else if(ev == QKeySequence::SelectNextPage) {
1049 //             } else if(ev == QKeySequence::SelectPreviousPage) {
1050         } else if(ev == QKeySequence::SelectStartOfLine) {
1051             triggerAction(SelectStartOfLine);
1052             handled = true;
1053         } else if(ev == QKeySequence::SelectEndOfLine) {
1054             triggerAction(SelectEndOfLine);
1055             handled = true;
1056         } else if(ev == QKeySequence::SelectStartOfBlock) {
1057             triggerAction(SelectStartOfBlock);
1058             handled = true;
1059         } else if(ev == QKeySequence::SelectEndOfBlock) {
1060             triggerAction(SelectEndOfBlock);
1061             handled = true;
1062         } else if(ev == QKeySequence::SelectStartOfDocument) {
1063             triggerAction(SelectStartOfDocument);
1064             handled = true;
1065         } else if(ev == QKeySequence::SelectEndOfDocument) {
1066             triggerAction(SelectEndOfDocument);
1067             handled = true;
1068         } else if(ev == QKeySequence::DeleteStartOfWord) {
1069             triggerAction(DeleteStartOfWord);
1070             handled = true;
1071         } else if(ev == QKeySequence::DeleteEndOfWord) {
1072             triggerAction(DeleteEndOfWord);
1073             handled = true;
1074 //             } else if(ev == QKeySequence::DeleteEndOfLine) {
1075         }
1076     }
1077     if (!handled) 
1078         handled = frame->d->eventHandler->keyEvent(ev);
1079     if (!handled) {
1080         handled = true;
1081         PlatformScrollbar *h, *v;
1082         h = mainFrame()->d->horizontalScrollBar();
1083         v = mainFrame()->d->verticalScrollBar();
1084
1085         if (ev == QKeySequence::MoveToNextPage) {
1086             if (v)
1087                 v->setValue(v->value() + height());
1088         } else if (ev == QKeySequence::MoveToPreviousPage) {
1089             if (v)
1090                 v->setValue(v->value() - height());
1091         } else {
1092             switch (ev->key()) {
1093             case Qt::Key_Up:
1094                 if (v)
1095                     v->setValue(v->value() - 10);
1096                 break;
1097             case Qt::Key_Down:
1098                 if (v)
1099                     v->setValue(v->value() + 10);
1100                 break;
1101             case Qt::Key_Left:
1102                 if (h)
1103                     h->setValue(h->value() - 10);
1104                 break;
1105             case Qt::Key_Right:
1106                 if (h)
1107                     h->setValue(h->value() + 10);
1108                 break;
1109             default:
1110                 handled = false;
1111                 break;
1112             }
1113         }
1114     }
1115
1116     ev->setAccepted(handled);
1117 }
1118
1119 void QWebPage::keyReleaseEvent(QKeyEvent *ev)
1120 {
1121     if (ev->isAutoRepeat()) {
1122         ev->setAccepted(true);
1123         return;
1124     }
1125
1126     if (!mainFrame()->d->eventHandler)
1127         return;
1128
1129     bool handled = mainFrame()->d->eventHandler->keyEvent(ev);
1130     ev->setAccepted(handled);
1131 }
1132
1133 void QWebPage::focusInEvent(QFocusEvent *ev)
1134 {
1135     if (ev->reason() != Qt::PopupFocusReason) 
1136         mainFrame()->d->frame->page()->focusController()->setFocusedFrame(mainFrame()->d->frame);
1137     QWidget::focusInEvent(ev);
1138 }
1139
1140 void QWebPage::focusOutEvent(QFocusEvent *ev)
1141 {
1142     QWidget::focusOutEvent(ev);
1143     if (ev->reason() != Qt::PopupFocusReason) {
1144         mainFrame()->d->frame->selectionController()->clear();
1145         mainFrame()->d->frame->setIsActive(false);
1146     }
1147 }
1148
1149 bool QWebPage::focusNextPrevChild(bool next)
1150 {
1151     Q_UNUSED(next)
1152     return false;
1153 }
1154
1155 void QWebPage::dragEnterEvent(QDragEnterEvent *ev)
1156 {
1157 #ifndef QT_NO_DRAGANDDROP
1158     DragData dragData(ev->mimeData(), ev->pos(), QCursor::pos(), 
1159                       dropActionToDragOp(ev->possibleActions()));
1160     Qt::DropAction action = dragOpToDropAction(d->page->dragController()->dragEntered(&dragData));
1161     ev->setDropAction(action);
1162     ev->accept();
1163 #endif
1164 }
1165
1166 void QWebPage::dragLeaveEvent(QDragLeaveEvent *ev)
1167 {
1168 #ifndef QT_NO_DRAGANDDROP
1169     DragData dragData(0, IntPoint(), QCursor::pos(), DragOperationNone);
1170     d->page->dragController()->dragExited(&dragData);
1171     ev->accept();
1172 #endif
1173 }
1174
1175 void QWebPage::dragMoveEvent(QDragMoveEvent *ev)
1176 {
1177 #ifndef QT_NO_DRAGANDDROP
1178     DragData dragData(ev->mimeData(), ev->pos(), QCursor::pos(), 
1179                       dropActionToDragOp(ev->possibleActions()));
1180     Qt::DropAction action = dragOpToDropAction(d->page->dragController()->dragUpdated(&dragData));
1181     ev->setDropAction(action);
1182     ev->accept();
1183 #endif
1184 }
1185
1186 void QWebPage::dropEvent(QDropEvent *ev)
1187 {
1188 #ifndef QT_NO_DRAGANDDROP
1189     DragData dragData(ev->mimeData(), ev->pos(), QCursor::pos(), 
1190                       dropActionToDragOp(ev->possibleActions()));
1191     Qt::DropAction action = dragOpToDropAction(d->page->dragController()->performDrag(&dragData));
1192     ev->accept();
1193 #endif
1194 }
1195
1196 void QWebPage::setNetworkInterface(QWebNetworkInterface *interface)
1197 {
1198     d->networkInterface = interface;
1199 }
1200
1201 QWebNetworkInterface *QWebPage::networkInterface() const
1202 {
1203     if (d->networkInterface)
1204         return d->networkInterface;
1205     else
1206         return QWebNetworkInterface::defaultInterface();
1207 }
1208
1209 QPixmap QWebPage::icon() const
1210 {
1211     Image* image = iconDatabase()->iconForPageURL(url().toString(), IntSize(16, 16));
1212     if (!image || image->isNull()) {
1213         image = iconDatabase()->defaultIcon(IntSize(16, 16));
1214     }
1215
1216     if (!image) {
1217         return QPixmap();
1218     }
1219
1220     QPixmap *icon = image->getPixmap();
1221     if (!icon) {
1222         return QPixmap();
1223     }
1224     return *icon;
1225 }
1226
1227 QWebSettings *QWebPage::settings()
1228 {
1229     return d->settings;
1230 }
1231
1232 QString QWebPage::chooseFile(QWebFrame *parentFrame, const QString& oldFile)
1233 {
1234     //FIXME frame pos...
1235 #ifndef QT_NO_FILEDIALOG
1236     return QFileDialog::getOpenFileName(this, QString::null, oldFile);
1237 #else
1238     return QString::null;
1239 #endif
1240 }
1241
1242 #ifndef QT_NO_NETWORKPROXY
1243 void QWebPage::setNetworkProxy(const QNetworkProxy& proxy)
1244 {
1245     d->networkProxy = proxy;
1246 }
1247
1248 QNetworkProxy QWebPage::networkProxy() const
1249 {
1250     return d->networkProxy;
1251 }
1252 #endif
1253
1254 QString QWebPage::userAgentFor(const QUrl& url) const {
1255     Q_UNUSED(url)
1256     return QLatin1String("Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3 Qt");
1257 }
1258
1259
1260 void QWebPagePrivate::_q_onLoadProgressChanged(int) {
1261     m_totalBytes = page->progress()->totalPageAndResourceBytesToLoad();
1262     m_bytesReceived = page->progress()->totalBytesReceived();
1263 }
1264
1265
1266 quint64 QWebPage::totalBytes() const {
1267     return d->m_bytesReceived;
1268 }
1269
1270
1271 quint64 QWebPage::bytesReceived() const {
1272     return d->m_totalBytes;
1273 }
1274
1275 QWebPageContext::QWebPageContext(const WebCore::HitTestResult &hitTest)
1276     : d(new QWebPageContextPrivate)
1277 {
1278     d->pos = hitTest.point();
1279     d->text = hitTest.textContent();
1280     d->linkUrl = hitTest.absoluteLinkURL().string();
1281     d->imageUrl = hitTest.absoluteImageURL().string();
1282     d->innerNonSharedNode = hitTest.innerNonSharedNode();
1283     WebCore::Image *img = hitTest.image();
1284     if (img) {
1285         QPixmap *pix = img->getPixmap();
1286         if (pix)
1287             d->image = *pix;
1288     }
1289     WebCore::Frame *frame = hitTest.targetFrame();
1290     if (frame)
1291         d->targetFrame = frame->view()->qwebframe();
1292 }
1293
1294 QWebPageContext::QWebPageContext()
1295     : d(0)
1296 {
1297 }
1298
1299 QWebPageContext::QWebPageContext(const QWebPageContext &other)
1300     : d(0)
1301 {
1302     if (other.d)
1303         d = new QWebPageContextPrivate(*other.d);
1304 }
1305
1306 QWebPageContext &QWebPageContext::operator=(const QWebPageContext &other)
1307 {
1308     if (this != &other) {
1309         if (other.d) {
1310             if (!d)
1311                 d = new QWebPageContextPrivate;
1312             *d = *other.d;
1313         } else {
1314             delete d;
1315             d = 0;
1316         }
1317     }
1318     return *this;
1319 }
1320
1321 QWebPageContext::~QWebPageContext()
1322 {
1323     delete d;
1324 }
1325
1326 QPoint QWebPageContext::pos() const
1327 {
1328     if (!d)
1329         return QPoint();
1330     return d->pos;
1331 }
1332
1333 QString QWebPageContext::text() const
1334 {
1335     if (!d)
1336         return QString();
1337     return d->text;
1338 }
1339
1340 QUrl QWebPageContext::linkUrl() const
1341 {
1342     if (!d)
1343         return QUrl();
1344     return d->linkUrl;
1345 }
1346
1347 QUrl QWebPageContext::imageUrl() const
1348 {
1349     if (!d)
1350         return QUrl();
1351     return d->linkUrl;
1352 }
1353
1354 QPixmap QWebPageContext::image() const
1355 {
1356     if (!d)
1357         return QPixmap();
1358     return d->image;
1359 }
1360
1361 QWebFrame *QWebPageContext::targetFrame() const
1362 {
1363     if (!d)
1364         return 0;
1365     return d->targetFrame;
1366 }
1367
1368 #include "moc_qwebpage.cpp"