原文地址:http://blog.csdn.net/dlmu2001/article/details/6208241javascript
摘要: 瀏覽器的GUI接口分紅兩種,一種是控件的繪製,另外一種則是同應用息息相關的窗口交互操做。本文主要介紹的是後一種,在WebKit裏面,稱之爲宿主窗口。java
Chrome類爲WebKit內核定義了一系列的宿主窗口相關的操做接口,並最終在不一樣的實現中由ChromeClient類的派生類來實現,好比,在Qt裏面的ChromeClientQt類。web
本文將介紹Chrome類在WebKit中的做用,以及在移植WebKit的時候,如何來實現 ChromeClient類。chrome
瀏覽器的定義,特地查了百度百科,我綜合一下,比較好的解釋多是這樣,」瀏覽器是Web/Wap服務的客戶端瀏覽程序,可向Web/Wap服務器發送各類請求,並對服務器發回的超文本信息和各類多媒體數據格式進行解釋、顯示和播放,並讓用戶與此些文件互動「。瀏覽器
從上面這個定義裏面,我簡單提煉出了瀏覽器須要的幾個功能件:發送請求(http),解釋超文本信息和各類多媒體數據(解析),顯示和播放這些信息(排版,渲染,以及可能存在的插件),互動(交互)。這幾個模塊裏面,同平臺GUI相關的是排版、渲染和互動。而Chrome類就是WebCore內核渲染網頁以及互動所需的並定義出來會由移植實現的同平臺相關的接口,這個接口不包括控件的渲染。ChromeClient的具體實現(好比ChromeClientQt),則是移植對這些接口的實現。若是以MVC的角度來看,Chrome就是V,固然WebKit並不是MVC的架構。 安全
Chrome是對應於Page的,每一個Page都會在構造函數中建立一個Chrome對象,並將對象指針賦值給Page類的 m_chrome成員服務器
Chrome類繼承自HostWindow類,HostWindow類定義了宿主窗口必須實現的一系列接口,包括刷窗口(及內容),滾動,窗口相對座標和屏幕座標之間的相互轉換等接口。架構
HostWindow是個抽象類,沒有構造函數,沒法實例化,Chrome經過繼承對這個類進行了實現app
在類Chrome的實現中,經過了組合的方式,將具體的實現委託給了ChromeClient。異步
例:
1 void Chrome::invalidateWindow(const IntRect& updateRect, bool immediate) 2 { 3 m_client->invalidateWindow(updateRect, immediate); 4 } 5 6 void Chrome::invalidateContentsAndWindow(const IntRect& updateRect, bool immediate) 7 { 8 m_client->invalidateContentsAndWindow(updateRect, immediate); 9 } 10 11 void Chrome::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate) 12 { 13 m_client->invalidateContentsForSlowScroll(updateRect, immediate); 14 }
ChromeClient也是抽象接口類,沒有構造函數,必須在porting的時候,進行繼承,由不一樣的移植依託本身的平臺進行實現。
以Qt移植爲例,由ChromeClientQt類來最終實現(google的Chrome分支主要由 ChromeClientImp類實現)
而在ChromeClientQt的具體實現中,不少又是經過Page類的內部QwebPageClient類數據成員(client)提供的接口來實現的。
一樣的,QWebPageClient是抽象接口類,沒法實例化,經過繼承類PageClientQWidget來實現。
而PageClientQWidget的實現又最終經過qwebview來實現,這個過程有點繞彎彎。。。
PageClientQWidget的實例化在QwebPage::setView接口中完成。
在代碼結構上,ChromeClientQt.cpp 和 PageClientQt.cpp都位於WebKit/qt/WebCoreSupport目錄下,代表他們是爲了實現WebCore須要實現的移植層代碼。
例:
1 void QWebPage::setView(QWidget* view) 2 { 3 if (this->view() == view) 4 return; 5 6 d->view = view; 7 setViewportSize(view ? view->size() : QSize(0, 0)); 8 9 // If we have no client, we install a special client delegating 10 // the responsibility to the QWidget. This is the code path 11 // handling a.o. the "legacy" QWebView. 12 // 13 // If such a special delegate already exist, we substitute the view. 14 15 if (d->client) { 16 if (d->client->isQWidgetClient()) 17 static_cast<PageClientQWidget*>(d->client.get())->view = view; 18 return; 19 } 20 21 if (view) 22 d->client = new PageClientQWidget(view, this); 23 }
1 IntPoint ChromeClientQt::screenToWindow(const IntPoint& point) const 2 { 3 QWebPageClient* pageClient = platformPageClient(); 4 if (!pageClient) 5 return point; 6 7 QWidget* ownerWidget = pageClient->ownerWidget(); 8 if (!ownerWidget) 9 return point; 10 11 return ownerWidget->mapFromGlobal(point); 12 } 13 14 PlatformPageClient ChromeClientQt::platformPageClient() const 15 { 16 return m_webPage->d->client.get(); 17 }
1 ChromeClientQt::ChromeClientQt(QWebPage* webPage)
描述:
構造函數,以QWebPage爲依賴對象建立。通常在建立Page對象前調用這個構造函數實例化ChromeClientQt,並以之爲參數建立Page對象
代碼:
1 QWebPagePrivate::QWebPagePrivate(QWebPage *qq) 2 : q(qq) 3 , page(0) 4 { 5 WebCore::InitializeLoggingChannelsIfNecessary(); //初始化環境變量 QT_WEBKIT_LOG 指定的log channel,xitongji 6 ScriptController::initializeThreading(); //初始化線程, 注意:必須在主線程裏面調用。能夠安全屢次調用,可重入//僅僅初始化一次 7 WTF::initializeMainThread(); 8 WebCore::SecurityOrigin::setLocalLoadPolicy(WebCore::SecurityOrigin::AllowLocalLoadsForLocalAndSubstituteData); 9 WebPlatformStrategies::initialize(); 10 11 Page::PageClients pageClients; 12 pageClients.chromeClient = new ChromeClientQt(q); 13 pageClients.contextMenuClient = new ContextMenuClientQt(); 14 pageClients.editorClient = new EditorClientQt(q); 15 pageClients.dragClient = new DragClientQt(q); 16 pageClients.inspectorClient = new InspectorClientQt(q); 17 #if ENABLE(DEVICE_ORIENTATION) 18 pageClients.deviceOrientationClient = new DeviceOrientationClientQt(q); 19 pageClients.deviceMotionClient = new DeviceMotionClientQt(q); 20 #endif 21 #if ENABLE(CLIENT_BASED_GEOLOCATION) 22 if (QWebPagePrivate::drtRun) 23 pageClients.geolocationClient = new GeolocationClientMock(); 24 else 25 pageClients.geolocationClient = new GeolocationClientQt(q); 26 #endif 27 page = new Page(pageClients); 28 page->setGroupName("Default Group"); 29 ...... 30 }
1 virtual FloatRect windowRect();
描述:
得到當前瀏覽器窗口區域的大小,這個區域不止包括顯示區域,還包括狀態條,菜單欄,工具欄等等
代碼:
1 FloatRect ChromeClientQt::windowRect() 2 { 3 if (!platformPageClient()) 4 return FloatRect(); 5 return platformPageClient()->windowRect(); 6 }
1 virtual FloatRect pageRect();
描述:
獲取顯示區域的大小,在排版的時候會常常調用
代碼:
1 FloatRect ChromeClientQt::pageRect() 2 { 3 if (!m_webPage) 4 return FloatRect(); 5 return FloatRect(QRectF(QPointF(0, 0), m_webPage->viewportSize())); 6 }
1 virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&);
描述:
建立一個窗口。通常是在新建一個新窗口或者tab頁的時候調用。
一個新窗口的建立意味着會有一個qwebview的建立和qwebpage的建立。窗口建立成功之後,還要在qwebframe主幀中發起網頁請求。
這個接口在諸多移植中很是重要。
代碼:
1 Page* ChromeClientQt::createWindow(Frame*, const FrameLoadRequest& request, const WindowFeatures& features, const NavigationAction&) 2 { 3 QWebPage* newPage = m_webPage->createWindow(features.dialog ? QWebPage::WebModalDialog : QWebPage::WebBrowserWindow); 4 if (!newPage) 5 return 0; 6 7 // A call to QWebPage::mainFrame() implicitly creates the main frame. 8 // Make sure it exists, as WebCore expects it when returning from this call. 9 newPage->mainFrame(); 10 return newPage->d->page; 11 }
1 setToolbarsVisible(bool visible); 2 bool toolbarsVisible(); 3 void setStatusbarVisible(bool); 4 statusbarVisible(); 5 setScrollbarsVisible(bool); 6 scrollbarsVisible(); 7 setMenubarVisible(bool); 8 menubarVisible();
描述:
這些接口用來設置這些區域的顯示與否
1 virtual void addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message, unsigned int lineNumber, const String& sourceID);
描述:
不少瀏覽器都在提供了javascript控制檯工具,方便開發人員進行調試,這個接口 就是要把信息在控制檯中打印出來
代碼:
1 void ChromeClientQt::addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message, 2 unsigned int lineNumber, const String& sourceID) 3 { 4 QString x = message; 5 QString y = sourceID; 6 m_webPage->javaScriptConsoleMessage(x, lineNumber, y); 7 }
1 virtual void runJavaScriptAlert(Frame*, const String&); 2 virtual bool runJavaScriptConfirm(Frame*, const String&); 3 virtual bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result);
描述:
用來實現javascript中的alert框,確認框,提示框。完成同用戶的交互。
代碼:
1 void ChromeClientQt::runJavaScriptAlert(Frame* f, const String& msg) 2 { 3 QString x = msg; 4 QWebFrame* webFrame = qobject_cast<QWebFrame*>(f->loader()->networkingContext()->originatingObject()); 5 m_webPage->javaScriptAlert(webFrame, x); 6 } 7 8 bool ChromeClientQt::runJavaScriptConfirm(Frame* f, const String& msg) 9 { 10 QString x = msg; 11 QWebFrame* webFrame = qobject_cast<QWebFrame*>(f->loader()->networkingContext()->originatingObject()); 12 return m_webPage->javaScriptConfirm(webFrame, x); 13 } 14 15 bool ChromeClientQt::runJavaScriptPrompt(Frame* f, const String& message, const String& defaultValue, String& result) 16 { 17 QString x = result; 18 QWebFrame* webFrame = qobject_cast<QWebFrame*>(f->loader()->networkingContext()->originatingObject()); 19 bool rc = m_webPage->javaScriptPrompt(webFrame, (QString)message, (QString)defaultValue, &x); 20 21 // Fix up a quirk in the QInputDialog class. If no input happened the string should be empty 22 // but it is null. See https://bugs.webkit.org/show_bug.cgi?id=30914. 23 if (rc && x.isNull()) 24 result = String(""); 25 else 26 result = x; 27 28 return rc; 29 }
1 virtual void setStatusbarText(const String&);
描述:
設置狀態條顯示信心
代碼:
1 void ChromeClientQt::setStatusbarText(const String& msg) 2 { 3 QString x = msg; 4 emit m_webPage->statusBarMessage(x); 5 }
1 virtual void invalidateContentsAndWindow(const IntRect&, bool);
描述:
很是重要的一個移植接口,用來刷屏(包含內容和窗體)。
通常狀況下,平臺趨向於一個異步的調用(並不立刻調用),也就是說,可能屢次的invalidateContentsAndWindow調用的結果才致使一次屏幕的刷新。
第二個bool類型的參數用來表示是否當即進行屏幕刷新,不過不少移植都不對這個參數進行支持
代碼:
1 void ChromeClientQt::invalidateContentsAndWindow(const IntRect& windowRect, bool immediate) 2 { 3 // No double buffer, so only update the QWidget if content changed. 4 if (platformPageClient()) { 5 QRect rect(windowRect); 6 rect = rect.intersected(QRect(QPoint(0, 0), m_webPage->viewportSize())); 7 if (!rect.isEmpty()) 8 platformPageClient()->update(rect); 9 } 10 QMetaObject::invokeMethod(m_webPage, "repaintRequested", Qt::QueuedConnection, Q_ARG(QRect, windowRect)); 11 12 // FIXME: There is no "immediate" support for window painting. This should be done always whenever the flag 13 // is set. 14 }
1 virtual void scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect);
描述:
滾動支持。移植通常調用native widget缺省的scroll功能來實現這個接口
代碼:
1 void ChromeClientQt::scroll(const IntSize& delta, const IntRect& scrollViewRect, const IntRect&) 2 { 3 if (platformPageClient()) 4 platformPageClient()->scroll(delta.width(), delta.height(), scrollViewRect); 5 emit m_webPage->scrollRequested(delta.width(), delta.height(), scrollViewRect); 6 }
1 virtual IntPoint screenToWindow(const IntPoint&) const; 2 virtual IntRect windowToScreen(const IntRect&) const;
描述:
很是重要的移植接口,用來實現基於控件或者小窗口的相對座標和屏幕座標之間的轉換。在排版的時候,會常常用到這兩個轉換
代碼:
1 IntRect ChromeClientQt::windowToScreen(const IntRect& rect) const 2 { 3 QWebPageClient* pageClient = platformPageClient(); 4 if (!pageClient) 5 return rect; 6 7 QWidget* ownerWidget = pageClient->ownerWidget(); 8 if (!ownerWidget) 9 return rect; 10 11 QRect screenRect(rect); 12 screenRect.translate(ownerWidget->mapToGlobal(QPoint(0, 0))); 13 14 return screenRect; 15 } 16 17 IntPoint ChromeClientQt::screenToWindow(const IntPoint& point) const 18 { 19 QWebPageClient* pageClient = platformPageClient(); 20 if (!pageClient) 21 return point; 22 23 QWidget* ownerWidget = pageClient->ownerWidget(); 24 if (!ownerWidget) 25 return point; 26 27 return ownerWidget->mapFromGlobal(point); 28 }
1 virtual void requestGeolocationPermissionForFrame(Frame*, Geolocation*) { } 2 virtual void cancelGeolocationPermissionRequestForFrame(Frame*, Geolocation*) { }
描述:
在Geolocation(基於瀏覽器的地理定位技術)的時候,瀏覽器在調用Geolocation API獲取你的地理位置以前,會有一個用戶確認,這兩個接口就是用來實現這個確認以及確認的取消
1 virtual PassRefPtr<PopupMenu> createPopupMenu(PopupMenuClient*) const;
描述:
在webkit中,網頁中的下拉框(select+option)並非做爲一個控件來實現的,而是結合畫 input,畫下來三角 和彈出選項來實現。
這兩個接口就是用來彈出選項的。畫框和input則在 RenderThemeQt中實現
代碼:
1 PassRefPtr<PopupMenu> ChromeClientQt::createPopupMenu(PopupMenuClient* client) const 2 { 3 return adoptRef(new PopupMenuQt(client, this)); 4 }
1 virtual void show();
描述:
用來顯示窗體和內容,當即執行刷屏
代碼:
1 void ChromeClientQt::show() 2 { 3 if (!m_webPage) 4 return; 5 QWidget* view = m_webPage->view(); 6 if (!view) 7 return; 8 view->window()->show(); 9 }