WebKit内核分析之Page
参考地址:http://blog.csdn.net/dlmu2001/article/details/6213377
注:本系列博客是在原博主博客基础上增加了自己的理解和片段,可以看源博文获得清晰的结构
摘要:浏览器的请求一般是以页面请求为单位,当用户通过网址输入一个URL,浏览器就开始一个页面请求。而一个页面请求可能包含一到多个页面子帧,以及图片、CSS和插件等派生子资源。Page类就是用来对应这样的页面请求。Page类是WebKit中非常重要的类,它就像内核对外的一个聚合器。
1. 概述
浏览器的请求一般是以页面请求为单位,当用户通过网址输入一个URL,浏览器就开始一个页面请求。而一个页面请求可能包含有一到多个子帧,以及图片、CSS和插件等派生子资源。Page类就是用来对应这样的页面请求。前进后退,导航,编辑,右键菜单,设置,Inspector等这些用户参与的动作,大部分是同Page相关的。而标记语言解析、排版、加载则更多的同Frame相关
我们通过几个图来看Qt移植中Page类同应用之间的关系。

QWebPage通过QWebPagePrivate维护Page类的指针,并在QWebPagePrivate的构造函数中实例化Page对象。QWebPage类通过之后的createMainFrame调用实例化QWebFrame,而在QWebFrameData的构造函数中,以Page指针为参数调用了 Frame::create创建出 Frame对象
1,QWebPagePrivate::QWebPagePrivate(QWebPage *qq)
QWebPagePrivate::QWebPagePrivate(QWebPage *qq)
: q(qq)
, page()
, mainFrame()
#ifndef QT_NO_UNDOSTACK
, undoStack()
#endif
, insideOpenCall(false)
, m_totalBytes()
, m_bytesReceived()
, clickCausedFocus(false)
, networkManager()
, forwardUnsupportedContent(false)
, smartInsertDeleteEnabled(true)
, selectTrailingWhitespaceEnabled(false)
, linkPolicy(QWebPage::DontDelegateLinks)
, viewportSize(QSize(, ))
, pixelRatio()
#ifndef QT_NO_CONTEXTMENU
, currentContextMenu()
#endif
, settings()
, useFixedLayout(false)
, pluginFactory()
, inspectorFrontend()
, inspector()
, inspectorIsInternalOnly(false)
, m_lastDropAction(Qt::IgnoreAction)
{
WebCore::InitializeLoggingChannelsIfNecessary(); //初始化环境变量 QT_WEBKIT_LOG 指定的log channel,xitongji
ScriptController::initializeThreading(); //初始化线程, 注意:必须在主线程里面调用。可以安全多次调用,可重入//仅仅初始化一次
WTF::initializeMainThread();
WebCore::SecurityOrigin::setLocalLoadPolicy(WebCore::SecurityOrigin::AllowLocalLoadsForLocalAndSubstituteData); WebPlatformStrategies::initialize(); #if USE(QTKIT)
InitWebCoreSystemInterface();
#endif Page::PageClients pageClients;
pageClients.chromeClient = new ChromeClientQt(q);
pageClients.contextMenuClient = new ContextMenuClientQt();
pageClients.editorClient = new EditorClientQt(q);
pageClients.dragClient = new DragClientQt(q);
pageClients.inspectorClient = new InspectorClientQt(q);
#if ENABLE(DEVICE_ORIENTATION)
pageClients.deviceOrientationClient = new DeviceOrientationClientQt(q);
pageClients.deviceMotionClient = new DeviceMotionClientQt(q);
#endif
#if ENABLE(CLIENT_BASED_GEOLOCATION)
if (QWebPagePrivate::drtRun)
pageClients.geolocationClient = new GeolocationClientMock();
else
pageClients.geolocationClient = new GeolocationClientQt(q);
#endif
page = new Page(pageClients); // By default each page is put into their own unique page group, which affects popup windows
// and visited links. Page groups (per process only) is a feature making it possible to use
// separate settings for each group, so that for instance an integrated browser/email reader
// can use different settings for displaying HTML pages and HTML email. To make QtWebKit work
// as expected out of the box, we use a default group similar to what other ports are doing.
page->setGroupName("Default Group"); #if ENABLE(CLIENT_BASED_GEOLOCATION)
// In case running in DumpRenderTree mode set the controller to mock provider.
if (QWebPagePrivate::drtRun)
static_cast<GeolocationClientMock*>(pageClients.geolocationClient)->setController(page->geolocationController());
#endif
settings = new QWebSettings(page->settings()); history.d = new QWebHistoryPrivate(static_cast<WebCore::BackForwardListImpl*>(page->backForwardList()));
memset(actions, , sizeof(actions)); PageGroup::setShouldTrackVisitedLinks(true); #if ENABLE(NOTIFICATIONS)
NotificationPresenterClientQt::notificationPresenter()->addClient();
#endif
}
2, QWebPagePrivate::createMainFrame()
void QWebPagePrivate::createMainFrame()
{
if (!mainFrame) {
QWebFrameData frameData(page);
5 mainFrame = new QWebFrame(q, &frameData); emit q->frameCreated(mainFrame);
}
}
3,QWebFrameData::QWebFrameData
QWebFrameData::QWebFrameData(WebCore::Page* parentPage, WebCore::Frame* parentFrame,
WebCore::HTMLFrameOwnerElement* ownerFrameElement,
const WTF::String& frameName)
: name(frameName)
, ownerElement(ownerFrameElement)
, page(parentPage)
, allowsScrolling(true)
, marginWidth()
, marginHeight()
{
frameLoaderClient = new FrameLoaderClientQt();
frame = Frame::create(page, ownerElement, frameLoaderClient); // FIXME: All of the below should probably be moved over into WebCore
frame->tree()->setName(name);
if (parentFrame)
parentFrame->tree()->appendChild(frame);
}

Page类通过组合其他类的方式,实现了很多功能,Page类本身并没有多少代码。
类成员结构:
class Page {
WTF_MAKE_NONCOPYABLE(Page);
friend class Settings;
public:
static void scheduleForcedStyleRecalcForAllPages();
// It is up to the platform to ensure that non-null clients are provided where required.
struct PageClients {
WTF_MAKE_NONCOPYABLE(PageClients); WTF_MAKE_FAST_ALLOCATED;
public:
PageClients();
~PageClients();
ChromeClient* chromeClient;
ContextMenuClient* contextMenuClient;
EditorClient* editorClient;
DragClient* dragClient;
InspectorClient* inspectorClient;
OwnPtr<PluginHalterClient> pluginHalterClient;
GeolocationClient* geolocationClient;
DeviceMotionClient* deviceMotionClient;
DeviceOrientationClient* deviceOrientationClient;
RefPtr<BackForwardList> backForwardClient;
SpeechInputClient* speechInputClient;
MediaStreamClient* mediaStreamClient;
};
Page(PageClients&);
~Page();
enum ViewMode {
ViewModeInvalid,
ViewModeWindowed,
ViewModeFloating,
ViewModeFullscreen,
ViewModeMaximized,
ViewModeMinimized
};
private:
OwnPtr<Chrome> m_chrome;
OwnPtr<SelectionController> m_dragCaretController;
#if ENABLE(ACCELERATED_2D_CANVAS)
RefPtr<SharedGraphicsContext3D> m_sharedGraphicsContext3D;
#endif
#if ENABLE(DRAG_SUPPORT)
OwnPtr<DragController> m_dragController;
#endif
OwnPtr<FocusController> m_focusController;
#if ENABLE(CONTEXT_MENUS)
OwnPtr<ContextMenuController> m_contextMenuController;
#endif
#if ENABLE(INSPECTOR)
OwnPtr<InspectorController> m_inspectorController;
#endif
#if ENABLE(CLIENT_BASED_GEOLOCATION)
OwnPtr<GeolocationController> m_geolocationController;
#endif
#if ENABLE(DEVICE_ORIENTATION)
OwnPtr<DeviceMotionController> m_deviceMotionController;
OwnPtr<DeviceOrientationController> m_deviceOrientationController;
#endif
#if ENABLE(MEDIA_STREAM)
OwnPtr<MediaStreamController> m_mediaStreamController;
#endif
#if ENABLE(INPUT_SPEECH)
SpeechInputClient* m_speechInputClient;
OwnPtr<SpeechInput> m_speechInput;
#endif
OwnPtr<Settings> m_settings;
OwnPtr<ProgressTracker> m_progress;
OwnPtr<BackForwardController> m_backForwardController;
RefPtr<Frame> m_mainFrame;
mutable RefPtr<PluginData> m_pluginData;
RefPtr<RenderTheme> m_theme;
EditorClient* m_editorClient;
int m_frameCount;
String m_groupName;
bool m_openedByDOM;
bool m_tabKeyCyclesThroughElements;
bool m_defersLoading;
bool m_inLowQualityInterpolationMode;
bool m_cookieEnabled;
bool m_areMemoryCacheClientCallsEnabled;
float m_mediaVolume;
bool m_javaScriptURLsAreAllowed;
String m_userStyleSheetPath;
mutable String m_userStyleSheet;
mutable bool m_didLoadUserStyleSheet;
mutable time_t m_userStyleSheetModificationTime;
OwnPtr<PageGroup> m_singlePageGroup;
PageGroup* m_group;
JSC::Debugger* m_debugger;
double m_customHTMLTokenizerTimeDelay;
int m_customHTMLTokenizerChunkSize;
bool m_canStartMedia;
OwnPtr<PluginHalter> m_pluginHalter;
#if ENABLE(DOM_STORAGE)
RefPtr<StorageNamespace> m_sessionStorage;
#endif
#if ENABLE(NOTIFICATIONS)
NotificationPresenter* m_notificationPresenter;
#endif
ViewMode m_viewMode;
ViewportArguments m_viewportArguments;
double m_minimumTimerInterval;
OwnPtr<ScrollableAreaSet> m_scrollableAreaSet;
bool m_isEditable;
}
2. 类关系

2.1 PageGroup
PageGroup并不是用来对Page进行管理的,而是设计用来将一些具有共同的属性或者设置的Page编成组的,以方便对这些属性进行管理。
目前这些属性包括 localStorage的属性, indexDB,User Script,User StyleSheet等。最常见的同PageGroup相关的操作是维护已访问链接(如addVisitedLink等接口)。根据理解,假设webkit内核之上假设多个应用(浏览器是一个应用),比较可能得是,一个应用独立一个PageGroup。这里同多tab页没有关系,多tab页属于同一个PageGroup。原博主曾在maining group上就这个问题咨询过,一位RIM的同学给我举了一个例子,比如基于webkit的邮件程序,一方面他可能调用基于webkit的setting哟很大可能不一样,他们就使用不同的PageGroup
PageGroup中有这个Group已经安装并且使用的User Script和User StyleSheet的集合,一般在网页解析完毕后,这些User Script和User StyleSheet会插入到Document中
PageGroup中还维护了Local Storage和IndexDB相关的设置,比如他们的Path,上限等,通过GroupSettings类实现
PageGroup创建以后,每次创建一个新的Page对象,会通过addPage接口加入到这个PageGroup的m_pages中。
每次有导航行为发生的时候,会调用 addVisitedLink来将URL加入到已访问链接中。如果浏览器要跟踪已访问的接口,则在初始化的时候必须调用PageGroup::setShouldTrackVisitedLinks,且参数为true。此处shouldTrackVisitedLinks是一个静态的全局变量,也就是说,所有应用维护一样的行为(一个应用将其设置为false,会影响到其他同样基于此核的应用)?
Page类中维护了PageGroup指针,并提供了group接口,这是个lazy接口,如果m_group不存在,会调用InitGroup来创建一个。对于Page类来说,如果没有设置GroupName,则在初始化的时候会生成一个空的GroupName的PageGroup,由m_singlePageGroup维护,并把指针赋给m_group,如果以非空的名字调用了setGroupName,则会重新创建PageGroup,此时这个PageGroup由m_group来维护。
2.2 Setting
WebCore中的设置相关的类,浏览器应用的不少配置、选项同该类相关,Qt移植中,应用在创建Page对象后,会根据Page::settings来实例化QWebSetting
2.3 Chrome
原生窗口接口类,参考原博主文章"WebKit中的Chrome和ChromeClient"
2.4 其它
SelectionController 负责管理Page中的选取操作,绝大部分选取操作是基于Frame的,只有在Frame额Selection为空的时候,对焦点游标的绘制工作才会使用到Page类的SelectionController
SharedGraphicsContext3D: 共享3D图形上下文,为了优化2D显示而加入。在加速2D canvas中,引入的DrawingBuffer的概念,SharedGraphicsContext3D提供了createDrawingBuffer来创建DrawingBuffer
DragController: 拖拽控制器。 Chrome的超级拖拽功能同这个相关?此后博主会求证
FocusController: 焦点控制器。 考虑到焦点会在各个frame之间切换,所以由Page类维护焦点控制器最合适不过
ContextMenuController:右键下来菜单控制器
InspectorController: Inspector控制器,浏览器中的很多开发工具都同这个类相关
GeolocationController: 定位服务控制器
DeviceMotionController:设备移动控制器
DeviceOrientationController: 设备方向控制器
SpeechInputClient: 语音输入client
ProgressTracker: 进度跟踪
BackForwardController: 前进后退操作控制
Frame:一个Page由至少一个主帧和若干个其他子帧构成
HistoryItem:历史记录
PluginData:插件相关,未来可能同PluginDatabase类合并。主要是初始化Plugin的信息
PluginHalter: 用来控制Plugin的停止和重新开始
RenderTheme:这个类提供了控件的渲染和绘制接口。Qt移植中,RenderThemeQt是RenderTheme接口的具体实现
EditorClient: 同编辑功能相关,比如拷贝、剪切、删除等操作。
WebKit内核分析之Page的更多相关文章
- webkit内核分析之 Frame
参考地址:http://blog.csdn.net/dlmu2001/article/details/6164873 1. 描述 Frame类是WebCore内核同应用之间联系的一个重要的类.它 ...
- WebKit内核分析之FrameLoader
参考地址:http://blog.csdn.net/dlmu2001/article/details/6168545 FrameLoader类负责一个Frame的加载,在Frame的流程中起到非常重要 ...
- [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析
[WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285 ...
- css3中-moz、-ms、-webkit,-o分别代表的意思,以及微信浏览器内核分析
这种方式在业界上统称:识别码.前缀 //-ms代表[ie]内核识别码 //-moz代表火狐[firefox]内核识别码 //-webkit代表谷歌[chrome]/苹果[safari]内核识别码 // ...
- Linux内核分析(五)----字符设备驱动实现
原文:Linux内核分析(五)----字符设备驱动实现 Linux内核分析(五) 昨天我们对linux内核的子系统进行简单的认识,今天我们正式进入驱动的开发,我们今后的学习为了避免大家没有硬件的缺陷, ...
- PostgreSQL内核分析——BTree索引
文中附图参考至<PostgreSQL数据库内核分析> (一)概念描述 B+树是一种索引数据结构,其一个特征在于非叶子节点用于描述索引,而叶子节点指向具体的数据存储位置.在PostgreSQ ...
- 【WebKit内核 CEF3 】 第一篇:下载分支代码并本地编译
关于CEF Chromium Embedded Framework 简单说就是 WebKit内核的 对外绑定. 当前主流浏览器内核 一.Trident内核代表产品Internet Explorer ...
- linux内核分析作业8:理解进程调度时机跟踪分析进程调度与进程切换的过程
1. 实验目的 选择一个系统调用(13号系统调用time除外),系统调用列表,使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 分析汇编代码调用系统调用的工作过程,特别是参数的传递的方 ...
- Linux内核分析作业7:Linux内核如何装载和启动一个可执行程序
1.可执行文件的格式 在 Linux 平台下主要有以下三种可执行文件格式: 1.a.out(assembler and link editor output 汇编器和链接编辑器的输出) ...
随机推荐
- [翻译]-马丁·福勒-page对象
译者注:这篇文章翻译自马丁·福勒(Martin Flower,对,没错,就是软件教父)官网的一篇文章,原文出处在文底.如果你正在做WEB自动化测试,那么我强烈推荐你看这篇文章.另外透露Martin F ...
- 【Java】Lucene检索引擎详解
基于Java的全文索引/检索引擎——Lucene Lucene不是一个完整的全文索引应用,而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能. L ...
- mysql高级排序&高级匹配查询示例
在大多数应用场景下,我们使用mysql进行查询时只会用到'=', '>' , '<' , in, like 等常用的方法,看起来,大多数情况下,已经足以应付我们的小型应用了.不过,在一些特 ...
- MultiTouch————多点触控,伸缩图片,变换图片位置
前言:当今的手机都支持多点触控功能(可以进行图片伸缩,变换位置),但是我们程序员要怎样结合硬件去实现这个功能呢? 跟随我一起,来学习这个功能 国际惯例:先上DEMO免费下载地址:http://down ...
- JavaScript中的CSS属性对照表
盒子标签和属性对照 CSS语法(不区分大小写) JavaScript语法(区分大小写) border border border-bottom borderBottom border-bottom-c ...
- python ide: pycharm
1, 设置python路径 2,运行py文件 https://www.jetbrains.com/help/pycharm/2016.1/creating-and-running-your-first ...
- 2014 Hangjs 见闻流水账第一天
前言 6月21日~6月22日, 第一次跑远门去参加一个大会(广州 -> 杭州),本来打算,在火车的回来的路上,把这两天的东西记录一下,不过,火车上的环境实在恶劣,同时也高估了自己的专注力,所以, ...
- java目录
1. 在jsp文件或Servlet中,可以通过getServletContext().getRealPath("/")来获取项目根目录的绝对路径. 2. Java桌面程序中,可以通 ...
- 【原创】开源BI领袖-SpagoBI5.X最详细的中文版介绍
SpagoBI是唯一100%的开源商业智能套件由 Engineering Group的SpagoBI实验室(www.eng.it)开发和管理.它提供了强大的分析能力,从传统的报表和图表功能到自助分析. ...
- Turn off swi-prolog protocol output of ANSI terminal control sequences
To save a record of program execution in prolog, we use the special predicates: protocol and noproto ...