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)

 1 QWebPagePrivate::QWebPagePrivate(QWebPage *qq)
 2     : q(qq)
 3     , page(0)
 4     , mainFrame(0)
 5 #ifndef QT_NO_UNDOSTACK
 6     , undoStack(0)
 7 #endif
 8     , insideOpenCall(false)
 9     , m_totalBytes(0)
10     , m_bytesReceived()
11     , clickCausedFocus(false)
12     , networkManager(0)
13     , forwardUnsupportedContent(false)
14     , smartInsertDeleteEnabled(true)
15     , selectTrailingWhitespaceEnabled(false)
16     , linkPolicy(QWebPage::DontDelegateLinks)
17     , viewportSize(QSize(0, 0))
18     , pixelRatio(1)
19 #ifndef QT_NO_CONTEXTMENU
20     , currentContextMenu(0)
21 #endif
22     , settings(0)
23     , useFixedLayout(false)
24     , pluginFactory(0)
25     , inspectorFrontend(0)
26     , inspector(0)
27     , inspectorIsInternalOnly(false)
28     , m_lastDropAction(Qt::IgnoreAction)
29 {
30     WebCore::InitializeLoggingChannelsIfNecessary();  //初始化环境变量 QT_WEBKIT_LOG 指定的log channel,xitongji
31     ScriptController::initializeThreading();   //初始化线程, 注意:必须在主线程里面调用。可以安全多次调用,可重入//仅仅初始化一次
32     WTF::initializeMainThread();
33     WebCore::SecurityOrigin::setLocalLoadPolicy(WebCore::SecurityOrigin::AllowLocalLoadsForLocalAndSubstituteData);
34 
35     WebPlatformStrategies::initialize();
36 
37 #if USE(QTKIT)
38     InitWebCoreSystemInterface();
39 #endif
40 
41     Page::PageClients pageClients;
42     pageClients.chromeClient = new ChromeClientQt(q);
43     pageClients.contextMenuClient = new ContextMenuClientQt();
44     pageClients.editorClient = new EditorClientQt(q);
45     pageClients.dragClient = new DragClientQt(q);
46     pageClients.inspectorClient = new InspectorClientQt(q);
47 #if ENABLE(DEVICE_ORIENTATION)
48     pageClients.deviceOrientationClient = new DeviceOrientationClientQt(q);
49     pageClients.deviceMotionClient = new DeviceMotionClientQt(q);
50 #endif
51 #if ENABLE(CLIENT_BASED_GEOLOCATION)
52     if (QWebPagePrivate::drtRun)
53         pageClients.geolocationClient = new GeolocationClientMock();
54     else
55         pageClients.geolocationClient = new GeolocationClientQt(q);
56 #endif
57     page = new Page(pageClients);
58 
59     // By default each page is put into their own unique page group, which affects popup windows
60     // and visited links. Page groups (per process only) is a feature making it possible to use
61     // separate settings for each group, so that for instance an integrated browser/email reader
62     // can use different settings for displaying HTML pages and HTML email. To make QtWebKit work
63     // as expected out of the box, we use a default group similar to what other ports are doing.
64     page->setGroupName("Default Group");
65 
66 #if ENABLE(CLIENT_BASED_GEOLOCATION)
67     // In case running in DumpRenderTree mode set the controller to mock provider.
68     if (QWebPagePrivate::drtRun)
69         static_cast<GeolocationClientMock*>(pageClients.geolocationClient)->setController(page->geolocationController());
70 #endif
71     settings = new QWebSettings(page->settings());
72 
73     history.d = new QWebHistoryPrivate(static_cast<WebCore::BackForwardListImpl*>(page->backForwardList()));
74     memset(actions, 0, sizeof(actions));
75 
76     PageGroup::setShouldTrackVisitedLinks(true);
77     
78 #if ENABLE(NOTIFICATIONS)    
79     NotificationPresenterClientQt::notificationPresenter()->addClient();
80 #endif
81 }

2, QWebPagePrivate::createMainFrame()

1 void QWebPagePrivate::createMainFrame()
2 {
3     if (!mainFrame) {
4         QWebFrameData frameData(page);
5         mainFrame = new QWebFrame(q, &frameData);
6 
7         emit q->frameCreated(mainFrame);
8     }
9 }

3,QWebFrameData::QWebFrameData

 1 QWebFrameData::QWebFrameData(WebCore::Page* parentPage, WebCore::Frame* parentFrame,
 2                              WebCore::HTMLFrameOwnerElement* ownerFrameElement,
 3                              const WTF::String& frameName)
 4     : name(frameName)
 5     , ownerElement(ownerFrameElement)
 6     , page(parentPage)
 7     , allowsScrolling(true)
 8     , marginWidth(0)
 9     , marginHeight(0)
10 {
11     frameLoaderClient = new FrameLoaderClientQt();
12     frame = Frame::create(page, ownerElement, frameLoaderClient);
13 
14     // FIXME: All of the below should probably be moved over into WebCore
15     frame->tree()->setName(name);
16     if (parentFrame)
17         parentFrame->tree()->appendChild(frame);
18 }

Page类通过组合其他类的方式,实现了很多功能,Page类本身并没有多少代码。

类成员结构:

  1     class Page {
  2         WTF_MAKE_NONCOPYABLE(Page);
  3         friend class Settings;
  4     public:
  5         static void scheduleForcedStyleRecalcForAllPages();
  6 
  7         // It is up to the platform to ensure that non-null clients are provided where required.
  8         struct PageClients {
  9             WTF_MAKE_NONCOPYABLE(PageClients); WTF_MAKE_FAST_ALLOCATED;
 10         public:
 11             PageClients();
 12             ~PageClients();
 13 
 14             ChromeClient* chromeClient;
 15             ContextMenuClient* contextMenuClient;
 16             EditorClient* editorClient;
 17             DragClient* dragClient;
 18             InspectorClient* inspectorClient;
 19             OwnPtr<PluginHalterClient> pluginHalterClient;
 20             GeolocationClient* geolocationClient;
 21             DeviceMotionClient* deviceMotionClient;
 22             DeviceOrientationClient* deviceOrientationClient;
 23             RefPtr<BackForwardList> backForwardClient;
 24             SpeechInputClient* speechInputClient;
 25             MediaStreamClient* mediaStreamClient;
 26         };
 27 
 28         Page(PageClients&);
 29         ~Page();
 30 
 31         enum ViewMode {
 32             ViewModeInvalid,
 33             ViewModeWindowed,
 34             ViewModeFloating,
 35             ViewModeFullscreen,
 36             ViewModeMaximized,
 37             ViewModeMinimized
 38         };
 39         
 40 private:
 41         OwnPtr<Chrome> m_chrome;
 42         OwnPtr<SelectionController> m_dragCaretController;
 43 
 44 #if ENABLE(ACCELERATED_2D_CANVAS)
 45         RefPtr<SharedGraphicsContext3D> m_sharedGraphicsContext3D;
 46 #endif
 47         
 48 #if ENABLE(DRAG_SUPPORT)
 49         OwnPtr<DragController> m_dragController;
 50 #endif
 51         OwnPtr<FocusController> m_focusController;
 52 #if ENABLE(CONTEXT_MENUS)
 53         OwnPtr<ContextMenuController> m_contextMenuController;
 54 #endif
 55 #if ENABLE(INSPECTOR)
 56         OwnPtr<InspectorController> m_inspectorController;
 57 #endif
 58 #if ENABLE(CLIENT_BASED_GEOLOCATION)
 59         OwnPtr<GeolocationController> m_geolocationController;
 60 #endif
 61 #if ENABLE(DEVICE_ORIENTATION)
 62         OwnPtr<DeviceMotionController> m_deviceMotionController;
 63         OwnPtr<DeviceOrientationController> m_deviceOrientationController;
 64 #endif
 65 #if ENABLE(MEDIA_STREAM)
 66         OwnPtr<MediaStreamController> m_mediaStreamController;
 67 #endif
 68 #if ENABLE(INPUT_SPEECH)
 69         SpeechInputClient* m_speechInputClient;
 70         OwnPtr<SpeechInput> m_speechInput;
 71 #endif
 72         OwnPtr<Settings> m_settings;
 73         OwnPtr<ProgressTracker> m_progress;
 74         
 75         OwnPtr<BackForwardController> m_backForwardController;
 76         RefPtr<Frame> m_mainFrame;
 77 
 78         mutable RefPtr<PluginData> m_pluginData;
 79 
 80         RefPtr<RenderTheme> m_theme;
 81 
 82         EditorClient* m_editorClient;
 83 
 84         int m_frameCount;
 85         String m_groupName;
 86         bool m_openedByDOM;
 87 
 88         bool m_tabKeyCyclesThroughElements;
 89         bool m_defersLoading;
 90 
 91         bool m_inLowQualityInterpolationMode;
 92         bool m_cookieEnabled;
 93         bool m_areMemoryCacheClientCallsEnabled;
 94         float m_mediaVolume;
 95 
 96         bool m_javaScriptURLsAreAllowed;
 97 
 98         String m_userStyleSheetPath;
 99         mutable String m_userStyleSheet;
100         mutable bool m_didLoadUserStyleSheet;
101         mutable time_t m_userStyleSheetModificationTime;
102 
103         OwnPtr<PageGroup> m_singlePageGroup;
104         PageGroup* m_group;
105 
106         JSC::Debugger* m_debugger;
107 
108         double m_customHTMLTokenizerTimeDelay;
109         int m_customHTMLTokenizerChunkSize;
110 
111         bool m_canStartMedia;
112 
113         OwnPtr<PluginHalter> m_pluginHalter;
114 
115 #if ENABLE(DOM_STORAGE)
116         RefPtr<StorageNamespace> m_sessionStorage;
117 #endif
118 
119 #if ENABLE(NOTIFICATIONS)
120         NotificationPresenter* m_notificationPresenter;
121 #endif
122 
123         ViewMode m_viewMode;
124 
125         ViewportArguments m_viewportArguments;
126 
127         double m_minimumTimerInterval;
128 
129         OwnPtr<ScrollableAreaSet> m_scrollableAreaSet;
130 
131         bool m_isEditable;
132     }

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: 同编辑功能相关,比如拷贝、剪切、删除等操作。

原文地址:https://www.cnblogs.com/lfsblack/p/5417266.html