S60提供全局函数 CreateBrowserControlL 用以创建浏览器控件,此API已经包含在三版及五版SDK中,函数原型如下:
CBrCtlInterface* CreateBrowserControlL(
CCoeControl* aParent,
TRect aRect,
TUint aBrCtlCapabilities,
TUint aCommandIdBase,
MBrCtlSoftkeysObserver* aBrCtlSoftkeysObserver,
MBrCtlLinkResolver* aBrCtlLinkResolver,
MBrCtlSpecialLoadObserver* aBrCtlSpecialLoadObserver,
MBrCtlLayoutObserver* aBrCtlLayoutObserver,
MBrCtlDialogsProvider* aBrCtlDialogsProvider,
MBrCtlWindowObserver* aBrCtlWindowObserver,
MBrCtlDownloadObserver* aBrCtlDownloadObserver);
函数实现于Webkit/BrowserControl下的BrCtl.cpp中:
EXPORT_C CBrCtlInterface* CreateBrowserControlL(CCoeControl* aParent, TRect aRect, TUint aBrCtlCapabilities,
TUint aCommandIdBase, MBrCtlSoftkeysObserver* aBrCtlSoftkeysObserver,
MBrCtlLinkResolver* aBrCtlLinkResolver, MBrCtlSpecialLoadObserver* aBrCtlSpecialLoadObserver,
MBrCtlLayoutObserver* aBrCtlLayoutObserver, MBrCtlDialogsProvider* aBrCtlDialogsProvider,
MBrCtlWindowObserver* aBrCtlWindowObserver, MBrCtlDownloadObserver* aBrCtlDownloadObserver)
{
return CBrCtl::NewL(aParent, aRect, aBrCtlCapabilities,
aCommandIdBase, aBrCtlSoftkeysObserver,
aBrCtlLinkResolver, aBrCtlSpecialLoadObserver,
aBrCtlLayoutObserver, aBrCtlDialogsProvider,
aBrCtlWindowObserver, aBrCtlDownloadObserver);
}
可以看到,该函数仅仅只是调用了CBrCtl::NewL静态成员函数并返回结果。我们来看看CBrCtl类:
class CBrCtl : public CBrCtlInterface, public MBrCtlLoadEventObserver
该类派生自CBrCtlInterface,关于CBrCtlInterface,可以在Symbian SDK文档中查到这样的信息:
The CBrCtlInterface class is the base class of the Browser Control API.
All clients of browser control must use this class. The functions defined in the CBrCtlInterface class implement basic Browser Control functionality. You can customize the Browser Control to extend its functionality by implementing additional interface classes in the host application. Examples of such classes are MBrCtlDataLoadSupplier, MBrCtlDialogsProvider, MBrCtlDialogsProvider etc.
继续查看CBrCtlInterface的静态成员函数NewL:
CBrCtlInterface* CBrCtl::NewL(
CCoeControl* aParent,
TRect aRect,
TUint aBrCtlCapabilities,
TUint aCommandIdBase,
MBrCtlSoftkeysObserver* aBrCtlSoftkeysObserver,
MBrCtlLinkResolver* aBrCtlLinkResolver,
MBrCtlSpecialLoadObserver* aBrCtlSpecialLoadObserver,
MBrCtlLayoutObserver* aBrCtlLayoutObserver,
MBrCtlDialogsProvider* aBrCtlDialogsProvider,
MBrCtlWindowObserver* aBrCtlWindowObserver,
MBrCtlDownloadObserver* aBrCtlDownloadObserver )
{
CBrCtl* self = new (ELeave) CBrCtl(aParent, aBrCtlCapabilities, aCommandIdBase, aBrCtlSoftkeysObserver,
aBrCtlLinkResolver, aBrCtlSpecialLoadObserver, aBrCtlLayoutObserver, aBrCtlDialogsProvider,
aBrCtlWindowObserver, aBrCtlDownloadObserver );
CleanupStack::PushL(self);
self->ConstructL(aParent, aRect);
CleanupStack::Pop(); //self
return self;
}
通过Symbian典型的两阶构造方式创建了一个CBrCtl实例并返回,下面看看其二阶构造干了什么:
void CBrCtl::ConstructL(CCoeControl* aParent, TRect& aRect)
{
SetContainerWindowL(*aParent );
iRect = aRect;
// Setup default support, if it wasn't passed into the constructor...
// Create and initialize the Softkey Observer
if (iBrCtlSoftkeysObserver == NULL) {
iBrCtlSoftkeysObserver = new (ELeave) CBrCtlSoftkeysObserver;
iOwnSoftkeysObserver = ETrue;
}
// Don't create default Link Resolver, its supplied by calling app or skip
// Create and initialize the Special Load Observer
if (iBrCtlSpecialLoadObserver == NULL) {
iBrCtlSpecialLoadObserver = new (ELeave) CBrCtlSpecialLoadObserver;
iOwnSpecialLoadObserver = ETrue;
}
// Create and initialize the Layout Observer
if (iBrCtlLayoutObserver == NULL) {
iBrCtlLayoutObserver = new (ELeave) CBrCtlLayoutObserver;
iOwnLayoutObserver = ETrue;
}
// Create and initialize the Dialog Provider
if (iBrCtlDialogsProvider == NULL) {
iBrCtlDialogsProvider = CBrowserDialogsProvider::NewL(NULL);
iOwnDialogsProvider = ETrue;
}
// window observer
if (!iBrCtlWindowObserver ) {
iBrCtlWindowObserver = new(ELeave) CBrCtlWindowObserver;
iOwnWindowObserver = ETrue;
}
if (!iBrCtlDownloadObserver ) {
iOwnDownloadObserver = ETrue;
}
// Set up our capabilites, capabilities mask passed into our constructor...
// Do we support our internal scrolling solution. Scrolling can also be
// handled by the parent, i.e. BrowserUI, Email, SMS, or other application.
if (Capabilities() & TBrCtlDefs::ECapabilityDisplayScrollBar) {
iBrCtlScrollingProvider = CBrCtlScrollingProvider::NewL(*this, *this);
iOwnScrollingProvider = ETrue;
}
// Create instance of CHttpLoaderEventToUiListener object
// this has to be initialized before the browser view
// as resource loader needs this
iHttpLoaderEventToUiListener = CHttpLoaderEventToUiListener::NewL( *this );
// Setup our internal support...
// Create and initialize WebKitView
iWebKitControl = CWebKitControl::NewL( *this );
iBrHistoryInterface = CBrHistoryInterface::NewL(this);
// Create history controller
iHistoryController = CHistoryController::NewL( *this, iBrHistoryInterface );
iWebKitControl->SetHistoryController( *iHistoryController );
// Set the rect for BrowserControl (a CCoeControl).
SetRect(aRect);
ActivateL();
}
代码比较多,挑一处重点 iWebKitControl = CWebKitControl::NewL(*this ); 这里创建一个CWebKitControl的实例并赋值给了其成员变量iWebKitControl。
class CWebKitControl : public CBase
CWebKitControl类没什么特别的,简单派生自CBase。来看看其静态成员函数NewL:
CWebKitControl* CWebKitControl::NewL(CBrCtl& aBrCtl)
{
CWebKitControl* self = new( ELeave ) CWebKitControl( aBrCtl );
CleanupStack::PushL( self );
self->ConstructL();
CleanupStack::Pop();
return self;
}
void CWebKitControl::ConstructL()
{
// refer count static object for cleanup
CStaticObjectContainer::Instance().Ref();
InitializeWebCoreL();
// settings
InitializeSettingsL();
// init webkit view
// pass parent CCoeControl
iWebKitView = CWebKitView::NewL( *iBrCtl, *this );
// scrolling provider can be null
iWebKitView->SetScrollingProvider( *iBrCtl->BrCtlScrollingProvider() );
iWebKitView->SetLayoutObserver( iBrCtl->BrCtlLayoutObserver() );
// init resloader preferences
MWebCoreLoaderContainer* loaderContainer = TWebCoreLoaderContainer::LoaderContainer();
loaderContainer->SetCookiesEnabled( GetBrowserSettingL( TBrCtlDefs::ESettingsCookiesEnabled ) );
loaderContainer->SetDownloadsOpenEnabled( GetBrowserSettingL( TBrCtlDefs::ESettingsAutoOpenDownloads ) );
loaderContainer->SetBrowserEmbedded( GetBrowserSettingL( TBrCtlDefs::ESettingsEmbedded ) );
loaderContainer->SetLoadObservers(*(iBrCtl->BrCtlSpecialLoadObserver()),
*(iBrCtl->BrCtlDownloadObserver()), *(iBrCtl->HttpLoaderEventToUi()) );
LoadResourceFileL();
iProgressTimeout = CPeriodic::NewL( CActive::EPriorityHigh );
// generic callback timer
iCancelTimer = CPeriodic::NewL( CActive::EPriorityHigh + 1 );
if (!iDefaultCharset) {
CRepository* rep = CRepository::NewL( KCRUidBrowser );
rep->Get( KBrowserDefaultCharset, iDefaultCharset );
delete rep;
}
}
我们还是挑一处重点代码 iWebKitView = CWebKitView::NewL(*iBrCtl, *this); 构造了一个CWebkitView,这个类的关系就比较复杂了:
class CWebKitView : public CEikBorderedControl,
private MPageScalerCallback,
public MAknPictographAnimatorCallBack,
private MImageDecodeListener
其两阶构造过程:
CWebKitView* CWebKitView::NewL(CCoeControl& aParent, CWebKitControl& aWebKitControl)
{
CWebKitView* self = new (ELeave) CWebKitView( aWebKitControl );
CleanupStack::PushL( self );
self->ConstructL( aParent );
CleanupStack::Pop(); // self
return self;
}
void CWebKitView::ConstructL(CCoeControl& aParent)
{
SetContainerWindowL(aParent);
for( TInt i = 0;i < KZoomLevelCount;i++) {
iZoomLevelArray.AppendL(KZoomLevels[i]);
}
// Create the KeyEventHandler
iKeyEventHandler = CKeyEventHandler::NewL( *this );
// Create the PointerEventHandler
iPointerEventHandler = CPointerEventHandler::NewL( *this );
// construct the main frame
iMainFrame = CWebKitFrame::NewL( *this, 2, 2 );
// render target
if( iWebKitControl->BrCtl().Capabilities() & TBrCtlDefs::ECapabilityWebKitLite ) {
iRenderTarget = &CStaticObjectContainer::Instance().SurfaceL(EColor64K);
iIsPageScalerEnabled = EFalse;
MemoryManager::SetRescueBufferSize( KRescueBufferSizeForLite );
}
else {
iRenderTarget = &CStaticObjectContainer::Instance().SurfaceL(EColor16MU);
// initialize page scaler in full version
InitializePageScalerL();
iIsPageScalerEnabled = ETrue;
MemoryManager::SetRescueBufferSize( KRescueBufferSizeForFull );
}
// Create and offscreen device and context
iBitmapDevice = CFbsBitmapDevice::NewL( iRenderTarget->OffscreenBitmap() );
iWebCoreContext = CWebCoreGraphicsContext::NewL( iBitmapDevice, iRenderTarget->OffscreenBitmap(), iMainFrame );
iRepaintTimer = CPeriodic::NewL( CActive::EPriorityHigh );
#if USE_DIRECT_SCREEN_ACCESS
iDirectAccessScreenDevice = CFbsScreenDevice::NewL(_L("scdv"),EColor64K);
User::LeaveIfError(iDirectAccessScreenDevice->CreateContext(iDirectAccessGc));
#endif //USE_DIRECT_SCREEN_ACCESS
iInfoTextRenderer = CTextRendererFactory::InstanceL()->RendererWithFamilies(0,0,25,0);
iInfoTextRenderer->Ref();
// virtual cursor
if ( iWebKitControl->BrCtl().Capabilities() & TBrCtlDefs::ECapabilityCursorNavigation) {
iCursor = &CStaticObjectContainer::Instance().CursorL();
}
else {
iTabbedNavigation = ETrue;
}
CImageRendererFactory::InstanceL()->AppendImageDecodeListener(*this);
if (FeatureManager::FeatureSupported(KFeatureIdJapanesePicto)) {
iPictographInterface = CAknPictographInterface::NewL( *this, *this );
}
else {
iPictographInterface = NULL;
}
if (AknLayoutUtils::PenEnabled()) {
DrawableWindow()->SetPointerGrab(ETrue);
EnableDragEvents();
}
SetViewVisible(ETrue);
}
我们可以看到代码依据不同的能力创建了不同的surface(iRenderTarget),来看看创建surface的代码:
CWebKitSurface& CStaticObjectContainer::SurfaceL(TDisplayMode aMode)
{
if ( !iWebKitSurface ) {
// init offscreen surface
iWebKitSurface = CWebKitSurface::NewL( aMode );
}
return *iWebKitSurface;
}
CWebKitSurface* CWebKitSurface::NewL(TDisplayMode aMode)
{
CWebKitSurface* self = new (ELeave) CWebKitSurface(aMode);
CleanupStack::PushL( self );
self->ConstructL();
CleanupStack::Pop( self );
return self;
}
void CWebKitSurface::ConstructL()
{
// Create a bitmap to be used offscreen
iOffscreenBitmap = new (ELeave) CFbsBitmap();
User::LeaveIfError( iOffscreenBitmap->Create( TSize(0,0), iDisplayMode) );
}
嗯,到这里,一个被CWebKitSurface所封装的CFbsBitmap的实例被创建了出来。可以想象得到,该Bitmap作为一个buack buffer,WebKit将所有需要绘制的东西全部渲染到该buffer上,然后通过Symbian的GC刷到屏幕上。
让我们回到CWebKitView,上面已经看到,这个Surface是被引用到了成员变量iRenderTarget。既然已经做了猜测,那我们来应征一下是不是这样做的:
void CWebKitView::Draw( const TRect& aRect ) const
{
CWindowGc& gc = SystemGc();
CEikBorderedControl::Draw( aRect );
#if USE_DIRECT_SCREEN_ACCESS
// put offscreen bitmap onto the screen
iDirectAccessGc->BitBlt( Rect().iTl, iRenderTarget->OffscreenBitmap() );
TRegionFix<1> reg;
reg.AddRect(Rect());
iDirectAccessScreenDevice->Update(reg);
#else
// put offscreen bitmap onto the screen
iRenderTarget->Flip( Rect().iTl, gc );
CWidgetExtension* wdgtExt = iWebKitControl->WidgetExtension();
if (wdgtExt) {
TWidgetRenderer* widgetRenderer = iWebKitControl->WidgetExtension()->WidgetRenderer();
if (widgetRenderer && widgetRenderer->TransitionInProgress()) {
widgetRenderer->DrawTransition(gc, OffscreenBitmap());
}
}
if (iIsPageScalerEnabled) {
iPageScaler->Draw( gc,aRect );
}
if (iToolBar) {
iToolBar->Draw(gc);
}
if (iPopupDrawer) {
iPopupDrawer->DrawPopup();
}
# if DRAW_DEBUG
…
# endif // DRAW_DEBUG
#endif // USE_DIRECT_SCREEN_ACCESS
}
好的,这里用宏 USE_DIRECT_SCREEN_ACCESS 来区分使用DSA和不使用DSA两种刷屏方式。那么这个Draw是何时被调用的呢?下面看看:
//-------------------------------------------------------------------------------
// ScheduleRepaint
// Schedule an asynchronous repaint
//-------------------------------------------------------------------------------
void CWebKitView::ScheduleRepaint( const TRect& aRect )
{
iRepaintRegion.AddRect( aRect );
// if we are active, we'll repaint when timer fires
if ( !iRepaintTimer->IsActive() ) {
// no timer yet, repaint immediatly (but asynchronously)
iRepaintTimer->Start( 0, 0, TCallBack( &DoRepaintCb, this ) );
}
}
//-------------------------------------------------------------------------------
// DoRepaintCb
// C-style TCallback function
//-------------------------------------------------------------------------------
TInt DoRepaintCb( TAny* aPtr )
{
static_cast(aPtr)->DoRepaint();
return EFalse;
}
//-------------------------------------------------------------------------------
// DoRepaint
// Perform a scheduled repaint
//-------------------------------------------------------------------------------
void CWebKitView::DoRepaint()
{
// the timer must always be canceled here!
iRepaintTimer->Cancel();
// only repaint when this view owns the surface
if ( iRenderTarget->CurrentView() != this ) return;
// anything to do?
if ( !iRepaintRegion.IsEmpty() ) {
SyncRepaint( );
}
}
//-------------------------------------------------------------------------------
// SyncRepaint
// Repaint the current repaint region synchronously and clear it
//-------------------------------------------------------------------------------
void CWebKitView::SyncRepaint()
{
// only repaint when view owns the surface
if( iRenderTarget->CurrentView() != this ) return;
CFbsBitGc&gc = iWebCoreContext->Gc();
gc.Reset();
TRect rect(iRepaintRegion.BoundingRect());
TBool needsDraw( EFalse );
TRect viewRect( iMainFrame->VisibleRect() );
// check if the are is in view
if ( rect.Intersects( viewRect ) ) {
// ensure all documents have valid layout
iMainFrame->LayoutRecursive();
// maybe it got bigger in layout?
rect = iRepaintRegion.BoundingRect();
TPoint p( iMainFrame->ContentPosition() );
rect.Move(-p);
gc.Clear( rect ); // TSW Id: SJUN-73XBVC : text appears overlapped when the document
// changes in onload since the OffScreenBitmap is not cleared.
// draw to back buffer
iMainFrame->Draw( *iWebCoreContext, rect );
needsDraw = ETrue;
}
if (needsDraw) {
// blit the whole backbuffer to the screen (in Draw())
// This could be optimized further to blit only the affected area.
// Not a big win, scrolling is the most critical case and there you
// need to blit the whole buffer anyway.
DrawNow();
if (iWebKitControl->PageView()) {
iWebKitControl->BrCtl().DrawNow();