mazilla mozilla
はじめに
モジラとは
当ページの主旨
モジラの特徴
モジラ概観
モジュール
Webツール
モジラの技術
その他のリソース
モジラのビルド
ビルド環境の構築
ソースのチェックアウト
ビルド手順
ビルド後のイメージ
モジラを実行
エンベッディング
エンベッディングとは
エンベッディングの概念
PPBrowserの構造
PPBrowserの解析
エンベッディングの構造
marbrowの構造
marbrowの解析
フィジラ
フィジラとは
フィジラのビルド
フィジラのエンベッディング
FAQ
やつぎ氏との会話

marbrowを解析

marbrowを解析!


ここでは、marbrowのコードを実際に解析していきます。


◆ メイン処理 ◆

■ main() - メイン関数

プログラムのメインです。初期化処理、イベントループ、終了処理の順にコールします。

void main( void )
{
    DoInitialize();

    DoEventLoop();

    DoTerminate();
}


◆ 初期化/終了処理 ◆

■ DoInitialize() - 初期化処理

プログラム全体の初期化です。ツールボックス初期化、環境チェック、メニューバー初期化、アップルイベント初期化、エンベッディング初期化を行っています。

void DoInitialize( void )
{
    InitToolbox();
    CheckEnviron();

    SetThemeCursor( kThemeWatchCursor );

    InitMenubar();
    InitAppleEvent();
    InitWebShell();

#if DEBUG_WMNG
    DumpWmng();
#endif /* DEBUG_WMNG */

    SetThemeCursor( kThemeArrowCursor );
}

■ InitToolbox() - ツールボックス初期化

nsStdLib.shlbのInitializeMacToolbox()により、ツールボックス初期化を行っています。

static void InitToolbox( void )
{
    InitializeMacToolbox();
    setbuf(stdout,0);

    gScriptCode = IntlScript();

    if( !gMouseRgn ){
        gMouseRgn = NewRgn();
    }
}

■ InitWebShell() - エンベッディング初期化

NS_InitEmbedding()により、エンベッディング初期化を行っています。メニューでキャラセットを変更できるようにするので、英語以外のフォントサイズも指定しています。

static nsresult InitWebShell( void )
{
    // Get the directory which contains the mozilla parts
    // In this case it is the app directory but it could
    // be anywhere (an existing install of mozilla)
    nsresult                    rv;
    ProcessSerialNumber         psn;
    ProcessInfoRec              processInfo;
    FSSpec                      appSpec;
    nsCOMPtr<nsILocalFileMac>   macDir;
    nsCOMPtr<nsILocalFile>      appDir
        // If this ends up being NULL, default is used

    // Get App Directory
    if (!::GetCurrentProcess(&psn)) {
        processInfo.processInfoLength = sizeof(processInfo);
        processInfo.processName = NULL;
        processInfo.processAppSpec = &appSpec;    
        if (!::GetProcessInformation(&psn, &processInfo)) {
            // Turn the FSSpec of the app into an FSSpec of the app's directory
            ::FSMakeFSSpec(appSpec.vRefNum, appSpec.parID, "\p", &appSpec);
            // Make an nsILocalFile out of it
            rv = NS_NewLocalFileWithFSSpec(&appSpec
                        PR_TRUE, getter_AddRefs(macDir));
            if (NS_SUCCEEDED(rv))
                appDir = do_QueryInterface(macDir);
        }
    }

    // Init Embedding
    rv = NS_InitEmbedding(appDir, nsnull);

    // Make Pref Directory
    nsMPFileLocProvider *locationProvider = new nsMPFileLocProvider;
    NS_ENSURE_TRUE(locationProvider, NS_ERROR_FAILURE);
    nsCOMPtr<nsIFile> rootDir;
    rv = NS_GetSpecialDirectory(NS_MAC_PREFS_DIR, getter_AddRefs(rootDir));
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    rv = locationProvider->Initialize(rootDir, "marbrow");   
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   
    // Init Pref
    NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv);
    if( NS_SUCCEEDED(rv) ){
        prefs->ResetPrefs(); // Needed because things read default prefs during startup
        prefs->ReadUserPrefs();

        // HACK ALERT: Since we don't have prefs UI, reduce the font size here by hand
        prefs->SetIntPref("font.size.variable.x-western", 12);
        prefs->SetIntPref("font.size.fixed.x-western", 9);

        prefs->SetIntPref("font.size.variable.x-central-euro", 12);
        prefs->SetIntPref("font.size.fixed.x-central-euro", 9);

        prefs->SetIntPref("font.size.variable.x-mac-roman", 12);
        prefs->SetIntPref("font.size.fixed.x-mac-roman", 9);

        prefs->SetIntPref("font.size.variable.ja", 12);
        prefs->SetIntPref("font.size.fixed.ja", 9);

        prefs->SetIntPref("font.size.variable.ko", 12);
        prefs->SetIntPref("font.size.fixed.ko", 9);

        prefs->SetIntPref("font.size.variable.zh", 12);
        prefs->SetIntPref("font.size.fixed.zh", 9);

        prefs->SetIntPref("font.size.variable.zh-cn", 12);
        prefs->SetIntPref("font.size.fixed.zh-cn", 9);

        prefs->SetIntPref("font.size.variable.zh-tw", 12);
        prefs->SetIntPref("font.size.fixed.zh-tw", 9);

        prefs->SavePrefFile();
    }else{
        NS_ASSERTION(PR_FALSE, "Could not get preferences service");
    }

    /* Update Bookmark Menu */
    MBrow::UpdateBookmarkMenu();

    return NS_OK;
}

■ DoTerminate() - 終了処理

プログラム全体の終了処理です。エンベッディング終了、アップルイベント終了、ツールボックス終了を行っています。

void DoTerminate( void )
{
    TermWebShell();
    TermAppleEvent();
    TermToolbox();
}

■ TermToolbox() - ツールボックス終了

ツールボックス終了処理を行っています。nsStdLib.shlbのInitializeMacToolbox()内で、OpenTSMAwareApplication()しているので、ここでクローズしています。

static void TermToolbox( void )
{
    if( gMouseRgn ){
        DisposeRgn( gMouseRgn );
        gMouseRgn = NULL;
    }

    CloseTSMAwareApplication();
}

■ TermWebShell() - エンベッディング終了

エンベッディングの終了処理です。PPBrowserの処理と同等です。

static void TermWebShell( void )
{
    nsresult rv;

    // ShutDown preferences
    {
        NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv);
        if( NS_SUCCEEDED(rv) ){
            prefs->SavePrefFile();
        }
    }

    UMacUnicode::ReleaseUnit();
   
    // Term Embedding
    NS_TermEmbedding();
}


◆ イベント処理 ◆

■ DoEventLoop() - イベントループ

イベントループです。DoNew() によりメインウインドウを初期表示し、Macintoshの通常のイベントループに入ります。取得した1つのイベントは、ProcessEvent() に振っています。

void DoEventLoop( void )
{
    EventRecord     event;

    /* Init New Window */
    DoNew( 0xffff, NULL );

    gRunning = true;

    while( gRunning ){

        /* get next event */
        SetUpdateCommandStatus( false );
        Boolean gotEvent = WaitNextEvent( everyEvent, &event,  gSleepTime, gMouseRgn );
        if( !gotEvent ){
            event.what = nullEvent;
        }

        /* Process Event */
        ProcessEvent( &event );

        /* Adjust Menus */
        if( !gInBackground && gUpdateCommandStatus ){
            AdjustMenus();
        }
    }
}

■ ProcessEvent() - イベント処理

ここではモジラ特有の処理を行っています。イベントループ毎に nsRepeater::DoRepeaters() を、nullイベント毎に nsRepeater::DoIdlers() をコールします。実際のイベント実行は、DoEvent() に振っています。

void ProcessEvent( EventRecord *event )
{
    if( event->what != nullEvent ){ /* Some Event */

#ifdef DEBUG
        if( event->what != keyDown ){
            if( SIOUXHandleOneEvent( event ) ){
                return;
            }
        }
#endif /* DEBUG */

        /* Modeless Dialog */
        if( IsDialogEvent( event ) ){            DialogPtr       dlog;
            short           itemNo;
            if( DialogSelect( event, &dlog, &itemNo ) ){
                MMlDlog *mmidlog = MMlDlog::FindByDlog( dlog );
                if( mmidlog ){
                    mmidlog->DoEvent( itemNo );
                }
            }
            return;
        }

        /* handle event */
        DoEvent( event );

    }else{          /* Null Event */
        /* Do App Idle */
        DoIdle( event );
        /* Do Moz Idle */
        Repeater::DoIdlers( *event );
        ::PR_Sleep(PR_INTERVAL_NO_WAIT);
    }

    /* Do App Repeat */
    DoRepeat( event );
    /* Do Moz Repeat */
    Repeater::DoRepeaters( *event );

    return;
}

					

■ DoEvent() - イベント実行

イベント実行のケーススイッチです。通常のMacintoshの処理です。各イベント毎の実行処理は、ほとんど下位関数に振っています。

static void DoEvent( EventRecord *event )
{
    OSErr       err;
    WindowPtr   window;
    short       part;
    Point       aPoint;

    switch( event->what ){
    case mouseDown:
        part = FindWindow( event->where, &window );
        switch( part ){
        case inMenuBar:
            DoMenuCommand( MenuSelect( event->where ) );
            break;
        case inSysWindow:
            SystemClick( event, window );
            break;
        case inContent:
            if( window != FrontWindow() ){
                SelectWindow( window );
            }else{
                DoContentClick( window, event );
            }
            break;
        case inDrag:
            if( window != FrontWindow() ){
                SelectWindow( window );
            }else{
                DoDragWindow( window, event );
            }
            break;
        case inGrow:
            DoGrowWindow( window, event );
            break;
        case inZoomIn:
        case inZoomOut:
            if( TrackBox( window, event->where, part ) ){
                DoZoomWindow( window, part );
            }
            break;
        case inCollapseBox:
            break;
        case inGoAway:
            if( TrackGoAway( window, event->where ) ){
                DoCloseWindow( window );
            }
            break;
        }
        break;
    case mouseUp:
        DoMouseUp( event );
        break;
    case keyDown:
    case autoKey:
        DoKeyDown( event );
        break;
    case activateEvt:
        DoActivate( (WindowPtr)event->message, ( event->modifiers & activeFlag ) != 0 );
        break;
    case updateEvt:
        DoUpdate( (WindowPtr)event->message );
        break;
    case diskEvt:
        if( HiWord( event->message ) != noErr ){
            SetPt( &aPoint, kDILeft, kDITop );
            err = DIBadMount( aPoint, event->message );
        }
        break;
    case osEvt:
        switch( ( event->message >> 24 ) & 0x0FF ){
        case mouseMovedMessage:
            DoMouseMove( event );
            break;
        case suspendResumeMessage:
            gInBackground = ( event->message & resumeFlag ) == 0;
            DoActivate( FrontWindow(), !gInBackground );
            DoIdle( event );
            break;
        }
        break;
    case kHighLevelEvent:
        AEProcessAppleEvent( event );
        break;
    }
}

					

■ DoDragWindow() - ドラッグウィンドウ処理

ドラッグウィンドウ時の実際の処理です。各々のイベント処理の一例として挙げます。

static void DoDragWindow( WindowPtr window, EventRecord *event )
{
    if( IsBrowWindow(window) ){         /* Browser Window */
        Point   oldPt, newPt;
        oldPt = *(Point*)&window->portRect.top;        DragWindow( window, event->where, &qd.screenBits.bounds );
        newPt = *(Point*)&window->portRect.top;

        WMNG_WIND *wmngWind = FindWmngWindByWind( window );
        if( wmngWind ){
            wmngWind->mwind->MoveBy( newPt.h - oldPt.h, newPt.v - oldPt.v, true );
        }
    }
}

このように、各イベント処理では、対象となるウィンドウオブジェクト(MWind)を抽出し、これに処理を依頼します。

ここでは、対象となるウィンドウオブジェクトの抽出に FindWmngWindByWind() を使用していますが、これは wmng.cpp (ウィンドウ管理マネージャー) で実装しています。ウィンドウ管理マネージャーは、各ビジュアルパーツのデータ構造をクラス定義し、各ウィンドウ毎のビジュアル階層をツリー構造で保持します。

MWindクラスは、MViewクラスから継承した、各イベントの処理メソッドを持っています。

他のイベント処理も、基本はこれと同様なので、ここでは割愛します。

■ DoMenuCommand() - メニュー処理

メニュー処理を行っています。ウィンドウオブジェクト以下にメニュー処理を依頼した後、アプリケーションレベルのメニューを実際に実行しています。メインウィンドウのオープン、クローズ、アプリケーションの終了、netlibによるファイルのダウンロードなどが、これに当ります。

static Boolean DoMenuCommand( long menuResult )
{
    short       menuID, menuItem;
    WindowPtr   window;
    Boolean     handled = false;

    menuID = HiWord( menuResult );
    menuItem = LoWord( menuResult );

    /* ObeyCommand to MWind */
    window = FrontWindow();
    WMNG_WIND *wmngWind = FindWmngWindByWind( window );
    if( wmngWind ){
        handled = wmngWind->mwind->ObeyCommand( menuID, menuItem );
    }

    /* ObeyCommand to Application */
    if( !handled ){
        switch( menuID ){
        case mApple:
            switch( menuItem ){
            case iAbout:
                DspAbout();
                handled = true;
                break;
            default:
                {
                    Str255      daName;
                    short       daRefNum;
                    GetMenuItemText( GetMenuHandle(mApple), menuItem, daName );
                    daRefNum = OpenDeskAcc( daName );
                }
                handled = true;
                break;
            }
            break;
        case mFile:
            switch( menuItem ){
            case iNew:
                DoNew( 0xffff, NULL );
                handled = true;
                break;
            case iClose:
                window = FrontWindow();
                if( IsBrowWindow(window) ){
                    DoCloseWindow( window );
                }
                handled = true;
                break;
            case iDnload:
                {
                    /* Dialog Select URL */
                    Str255      strURL = "\p";
                    if( DspSelectURL( "\pDownLoad File",
                            "\pEnter URL of DownLooad File", strURL ) ){
                        char url[256];
                        CopyPtoCStr( url, strURL );

                        /* Make FileName */
                        char *ptr = strrchr( url, '/' );
                        if( ptr ){
                            ptr++;
                        }else{
                            ptr = url;
                        }
                        char name[256];
                        strcpy( name, ptr );
                        Str255  strName;
                        CopyCtoPStr( strName, name );

                        /* Put File */
                        StandardFileReply   reply;
                        StandardPutFile( "\pEnter DownLoad FileName & Location"
                          , strName, &reply );
                        if( reply.sfGood ){
                            /* Set Creator, FileType */
                            OSType  creator = '????';
                            OSType  fileType = '????';

                            /* DnLoad File */
                            nsresult rv;
                            rv = DnLoadFile( url, &reply.sfFile, creator, fileType );
                            printf( "DnLoadFile rv=%d\n", rv );
                        }
                    }
                }
                handled = true;
                break;
            case iQuit:
                DoCloseWindowAll();
                gRunning = false;
                handled = true;
                break;
            }
            break;
        }
    }

    if( handled ){
        SetUpdateCommandStatus( true );
    }
    HiliteMenu(0);

    return handled;
}

ファイルのダウンロードでは、ダイアログによりダウンロードファイルのURLを取得し、dnload.cpp の DnLoadFile() により行っています。


◆ ウィンドウクラス (MWind) ◆

■ クラス定義

MWindクラスの定義では、モジラ関連のメンバ変数として、メインウィンドウのモジラ的表現であるウィンドウウィジェットへのポインタを持ちます。

class MWind : public MView
{
private:
    typedef MView Inherited;

/* Constructor, Destructor */
public:
    MWind( WMNG_WIND *wmngWind );
    virtual ~MWind();
    .
    .
/* Attribute */
public:
    WMNG_WIND       *mWmngWind;
    static MView*   mviewClicked;
    MView           *mTarget;
    MBrow           *mTargetBrow;
/* BrowserWindow */
public:
    NS_METHOD GetWidget( nsIWidget** aWidget );
    MBrow* GetBrowserShell( UInt16 id=CNTLID_ANY ) const;
    void GetTitle( Str255 title );
    void SetTitle( Str255 title );
protected:
    nsCOMPtr<nsIWidget> mWindow;
//  MChrome*            mBrowserChrome;
};

PPBrowserではウィンドウクラスで他に、子コントロールとして抱える各インスタンスへのポインタ、クロームインスタンスへのポインタを持っていました。

marbrowでは、子コントロールとして抱える各インスタンスへのポインタは直接持ちません。WMNG_WIND *mWmngWind が、ウィンドウ管理マネージャーのウィンドウデータへのポインタで、これから各コントロールへのリンクがたどれます。

またmarbrowでは、1ウインドウに複数のブラウザを配置できる為、クロームインスタンスは各ブラウザシェル毎に生成します。その為、クロームへのポインタは、ウインドウクラスではなく、ブラウザシェルクラスで持ちます。

◆◆◆ 生成、破棄 ◆◆◆

■ MWind::MWind() - コンストラクタ

MWindクラスのコンストラクタでは、メインウィンドウの作成、ブラウザシェルを含む子コントロールの作成初期化を全てコントロールします。

MWind::MWind( WMNG_WIND *wmngWind )
    : mWmngWind( wmngWind ), mTarget( NULL ), mTargetBrow( NULL )
{
    MBrow::GetCharset( &gCharCodeMenuItem );
    MBrow::GetAutoDet( &gAutoDetMenuItem );

    Create();
}

NS_IMETHODIMP MWind::Create()
{
    OSErr           err;
    Rect            rect;
    WindowPtr       window;
    ControlHandle   rootControl;

Macintoshの通常の方法で、メインウィンドウの作成、ルートコントロールの作成などを行います。

    /* Init Param */
    rect = mWmngWind->bounds;

    /* Create Window */
    err = CreateNewWindow( kDocumentWindowClass,
            kWindowStandardDocumentAttributes, &rect, &window );
    if( err ){
        ExtErr( err, "CreateNewWindow error", "MWind::Create", "none" );
    }
    DrawEnv de( window );

    /* Set WindowKind */
    ((WindowPeek)window)->windowKind = WKIND_BROW;
    /* Root Control */
    err = GetRootControl( window, &rootControl );
    if( err ){
        err = CreateRootControl( window, &rootControl );
        if( err ){
            ExtErr( err, "CreateRootControl error", "MWind::Create", "none" );
        }
    }
    /* Set Title */
    SetWTitle( window, "\pmarbrow" );
    /* Set Window Content Color */
    RGBColor color;
    SetRGBColor( color, 192, 192, 192 );
    SetWindowContentColor( window, &color );

ウィンドウ管理マネージャーのウィンドウデータ(mWmngWind)を設定します。

    /* Set WmngWind */
    mWmngWind->wind = window;
    mWmngWind->rootControl = rootControl;
    mWmngWind->mwind = this;

ベースウィジェットをインスタンス生成します。

PPBrowserでは、ここでクロームの生成を行っていますが、marbrowではクロームはブラウザ毎に生成されるので、その処理はブラウザシェルクラスのコンストラクタに移動しています。

    /* BrowserWindow : Make Base Widget */
    nsresult    rv;
    mWindow = do_CreateInstance(kWindowCID, &rv);
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);

mWmngWindの子コントロール配列をイテレートし、各オブジェクトを生成します。この時点で各コントロールクラスのコンストラクタが呼ばれます。

    /* Create Cntls */
    MView           *initFocus = NULL;
    WMNG_CNTLS      cntls = mWmngWind->cntls;
    WMNG_CNTLIT     cntlit;
    WMNG_CNTL       *cntl;
    for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
        cntl = *cntlit;
        switch( cntl->cntl_type ){
        case CNTLTYPE_LOCA:
            {
                MLoca *mloca = new MLoca( mWmngWind, cntl );
                if( !mloca ){
                    ExtErr( MemError(), "new MLoca error", "MWind::Create", "none" );
                }
                if( !initFocus ) initFocus = mloca;
            }
            break;
        case CNTLTYPE_STAT:
            {
                MStat *mstat = new MStat( mWmngWind, cntl );
                if( !mstat ){
                    ExtErr( MemError(), "new MStat error", "MWind::Create", "none" );
                }
            }
            break;
        case CNTLTYPE_BUTT:
            {
                MButt *mbutt = new MButt( mWmngWind, cntl );
                if( !mbutt ){
                    ExtErr( MemError(), "new MButt error", "MWind::Create", "none" );
                }
            }
            break;
        case CNTLTYPE_BROW:
            {
                MBrow *mbrow = new MBrow( mWmngWind, cntl );
                if( !mbrow ){
                    ExtErr( MemError(), "new MBrow error", "MWind::Create", "none" );
                }
            }
            break;
        }
    }

ベースウィジェットを作成します。

    /* BrowserWindow ( FinishCreate ) : Create Base Widget */
    nsRect r(0, 0
         mWmngWind->bounds.right - mWmngWind->bounds.left
         mWmngWind->bounds.bottom - mWmngWind->bounds.top);
    rv = mWindow->Create((nsNativeWidget)mWmngWind->wind, r,
                    nsnull, nsnull, nsnull, nsnull, nsnull);
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);

もう一度、mWmngWindの子コントロール配列をイテレートし、各オブジェクトにFinishCeateSelf() を送ります。

    /* FinishCreateSelf Cntls */
    cntls = mWmngWind->cntls;
    for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
        cntl = *cntlit;
        cntl->mview->FinishCreateSelf();
    }

最後に、自分自身にFinishCeateSelf() を送ります。

    /* BrowserWindow FinishCreateSelf */
    FinishCreateSelf();

ウィンドウを表示し、初期フォーカスを設定します。

    /* Show Window */
    err = TransitionWindow( window,
        kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL );
    if( err ){
        ExtErr( err, "TransitionWindow error", "MWind::Create", "Main Window" );
    }

    /* Init Focus */
    if( initFocus ) initFocus->SetFocus();

#if DEBUG_CONTROL
    {
        FSSpec spec;
        FSMakeFSSpec( 0, 0, "\pControlHierarchy.txt", &spec );
        DumpControlHierarchy( mWmngWind->wind, &spec );
    }
#endif /* DEBUG_CONTROL */

    return NS_OK;
}

■ MWind::FinishCreateSelf() - 全階層構築後の最終処理

ウィンドウを有効化し、表示します。

void MWind::FinishCreateSelf()
{
    /* Enable & Show */
    EnableSelf( true );
    ShowSelf( true );

    Inherited::FinishCreateSelf();
}

■ MWind::~MWind() - デストラクタ

MWindクラスのデストラクタでは、全ての子コントロールと自分自身を破棄します。

MWind::~MWind()
{
    Destroy();
}

void MWind::Destroy()
{
#if DEBUG_CONTROL
    {
        FSSpec spec;
        FSMakeFSSpec( 0, 0, "\pControlHierarchyEnd.txt", &spec );
        DumpControlHierarchy( mWmngWind->wind, &spec );
    }
#endif /* DEBUG_CONTROL */

    /* Loop Cntls */
    WMNG_CNTLS      cntls = mWmngWind->cntls;
    WMNG_CNTLIT     cntlit;
    WMNG_CNTL       *cntl;
    for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
        cntl = *cntlit;
        delete cntl->mview;
    }

    /* Hide Window */
    OSErr   err;
    err = TransitionWindow( mWmngWind->wind,
        kWindowZoomTransitionEffect, kWindowHideTransitionAction, NULL );
    if( err ){
        ExtErr( err, "TransitionWindow error", "MWind::Destroy", "Main Window" );
    }

    /* Destroy Window */
    DisposeWindow( mWmngWind->wind );
}

◆◆◆ イベント ◆◆◆

MWindオブジェクトは、ブラウザシェル、ボタンなどの子コントロールのオブジェクトを抱えているので、これらのコントロールオブジェクトにイベントを通知する責任を負います。

MWindクラスの各イベント処理の基本は、

  • イベントをウインドウ自体で処理する必要があれば、それを実行する
  • イベントを処理すべきコントロールがあれば、それら全てにイベントを通知する

という形に一般化されます。

■ MWind::DrawSelf() - 描画処理

ウィンドウの描画処理です。visRgnと交差する全てのコントロールオブジェクトに DrawSelf() を送ります。

void MWind::DrawSelf()
{
    DrawEnv de( mWmngWind->wind );

    WMNG_CNTLS      cntls = mWmngWind->cntls;
    WMNG_CNTLIT     cntlit;
    WMNG_CNTL       *cntl;

    for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
        cntl = *cntlit;
        Rect rect = cntl->bounds;
        if( RectInRgn( &rect, mWmngWind->wind->visRgn ) ){
            cntl->mview->DrawSelf();
        }
    }
}

■ MWind::ShowSelf() - 表示/非表示

ウィンドウを表示/非表示します。モジラのベースウィジェットに処理を依頼しています。

void MWind::ShowSelf( Boolean flag )
{
    mWindow->Show( flag );
    Inherited::ShowSelf( flag );
}

■ MWind::EnableSelf() - 有効/無効化

ウィンドウを有効/無効化します。MViewのメソッドでフラグを設定するのみです。

void MWind::EnableSelf( Boolean flag )
{
    Inherited::EnableSelf( flag );
}

■ MWind::ActivateSelf() - 活性/非活性化

ウィンドウを活性/非活性化します。下位コントロールオブジェクトに状態を伝えます。

void MWind::ActivateSelf( Boolean flag )
{
    /* Loop Cntls */
    WMNG_CNTLS      cntls = mWmngWind->cntls;
    WMNG_CNTLIT     cntlit;
    WMNG_CNTL       *cntl;
    for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
        cntl = *cntlit;
        cntl->mview->ActivateSelf( flag );
    }

    Inherited::ActivateSelf( flag );
}

■ MWind::ResizeFrameTo() - 絶対リサイズ

■ MWind::ResizeFrameBy() - 相対リサイズ

■ MWind::MoveTo() - 絶対ムーブ

■ MWind::MoveBy() - 相対ムーブ

■ MWind::AdjustCntlFrame() - コントロールフレーム調整

ムーブ系は、MacintoshのDragWindow()により処理されるので、新座標をmWmngWindに設定するだけです。

void MWind::MoveTo( SInt16 inHoriz, SInt16 inVert, Boolean inRefresh )
{
    MoveBy( inHoriz - mWmngWind->bounds.left, inVert - mWmngWind->bounds.top, inRefresh );
}

void MWind::MoveBy( SInt16 inHorizDelta, SInt16 inVertDelta, Boolean inRefresh )
{
    OffsetRect( &mWmngWind->bounds, inHorizDelta, inVertDelta );
}

リサイズ系は、ベースウィジェットにリサイズ通知後、AdjustCntlFrame() 共通処理により、全子コントロールにリサイズを送ります。

void MWind::ResizeFrameTo( SInt16 inWidth, SInt16 inHeight, Boolean inRefresh )
{
    ResizeFrameBy( inWidth - ( mWmngWind->bounds.right - mWmngWind->bounds.left ),
                   inHeight - ( mWmngWind->bounds.bottom - mWmngWind->bounds.top),
                   inRefresh );
}

void MWind::ResizeFrameBy( SInt16 inWidthDelta, SInt16 inHeightDelta, Boolean inRefresh )
{
    DrawEnv de( mWmngWind->wind );

    /* Resize Frame */
    mWmngWind->bounds.right  += inWidthDelta;
    mWmngWind->bounds.bottom += inHeightDelta;
    if( inRefresh )
        InvalRect( &mWmngWind->wind->portRect );

    /* Resize Widget */
    mWindow->Resize(mWmngWind->bounds.right - mWmngWind->bounds.left,
            mWmngWind->bounds.bottom - mWmngWind->bounds.top, PR_TRUE);

    /* Loop Cntls */
    WMNG_CNTLS      cntls = mWmngWind->cntls;
    WMNG_CNTLIT     cntlit;
    WMNG_CNTL       *cntl;
    for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
        cntl = *cntlit;
        AdjustCntlFrame( cntl, inWidthDelta, inHeightDelta, false );
    }
}

AdjustCntlFrame() では、各コントロールのバインド設定を考慮して、これらに MoveBy()、ResizeFrameBy() により、ムーブ、リサイズを依頼します。

void MWind::AdjustCntlFrame( WMNG_CNTL *wmngCntl,
    SInt16 inSurrWidthDelta, SInt16 inSurrHeightDelta, Boolean inRefresh )
{
    SInt16      widthDelta  = 0;
    SInt16      heightDelta = 0;
    SInt16      horizDelta  = 0;
    SInt16      vertDelta   = 0;

    if( wmngCntl->bind & BIND_RIGHT ){
        if( wmngCntl->bind & BIND_LEFT ){
                                        // Both right and left are bound
                                        // Pane resizes horizontally
            widthDelta = inSurrWidthDelta;
        } else {
                                        // Right bound, left free
                                        // Pane moves horizontally
            horizDelta = inSurrWidthDelta;
        }
    }

    if( wmngCntl->bind & BIND_BOTTOM ){
        if( wmngCntl->bind & BIND_TOP ){
                                        // Both bottom and top are bound
                                        // Pane resizes vertically
            heightDelta = inSurrHeightDelta;
        } else {
                                        // Bottom bound, left free
                                        // Pane moves vertically
            vertDelta = inSurrHeightDelta;
        }
    }

        // Perform the actual move and/or resize. Do the move first
        // because the resize can cause an immediate redraw.

    if ( (horizDelta != 0) || (vertDelta != 0) ) {
        wmngCntl->mview->MoveBy( horizDelta, vertDelta, inRefresh );
    }

    if ( (widthDelta != 0) || (heightDelta != 0) ) {
        wmngCntl->mview->ResizeFrameBy( widthDelta, heightDelta, inRefresh );
    }
}

■ MWind::ClickSelf() - クリック処理

クリックされたコントロールを探し、これに ClickSelf() を送ります。marbrowでは、ブラウザ以外のコントロールはMacintoshのコントロールマネージャーのものなので、FindControlUnderMouse() により取得できます。ブラウザシェルだけは、手動で検査しています。

void MWind::ClickSelf( const EventRecord &inEvent )
{
    if( !mActive || !mEnable ) return;

    Point           where;
    ControlHandle   cntlH;
    SInt16          part;

    DrawEnv de( mWmngWind->wind );

    /* Get Mouse Location */
    where = inEvent.where;
    GlobalToLocal( &where );

    /* Find Control */
    Boolean handled = false;
    cntlH = FindControlUnderMouse( where, mWmngWind->wind, &part );
    if( cntlH ){
        WMNG_CNTLS      cntls = mWmngWind->cntls;
        WMNG_CNTLIT     cntlit;
        WMNG_CNTL       *cntl;
        for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
            cntl = *cntlit;
            if( cntl->cntlH == cntlH && cntl->cntl_type != CNTLTYPE_BROW ){
                mMViewClicked = cntl->mview;
                cntl->mview->ClickSelf( inEvent );
                handled = true;
            }
        }
    }
    /* To Brow */
    if( !handled ){
        WMNG_CNTLS      cntls = mWmngWind->cntls;
        WMNG_CNTLIT     cntlit;
        WMNG_CNTL       *cntl;
        Rect            rect;
        for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
            cntl = *cntlit;
            if( cntl->cntl_type == CNTLTYPE_BROW ){
                rect = cntl->bounds;
                if( PtInRect( where, &rect ) ){
                    mMViewClicked = cntl->mview;
                    cntl->mview->ClickSelf( inEvent );
                }
            }
        }
    }
}

■ MWind::HandleKeyPress() - キープレス処理

フォーカスを持つコントロールに HandleKeyPress() を送ります。

Boolean MWind::HandleKeyPress( const EventRecord &inEvent )
{
    Boolean     handled = false;

    DrawEnv de( mWmngWind->wind );

    /* Send Target */
    if( mWmngWind->mwind->mTarget ){
        handled = mWmngWind->mwind->mTarget->HandleKeyPress( inEvent );
    }

    return handled;
}

■ MWind::DoIdle() - アイドル処理

■ MWind::DoRepeat() - リピート処理

全コントロールに DoIdle()、DoRepeat() を送ります。

void MWind::DoIdle( const EventRecord &inEvent )
{
    /* Loop Cntls */
    WMNG_CNTLS      cntls = mWmngWind->cntls;
    WMNG_CNTLIT     cntlit;
    WMNG_CNTL       *cntl;
    for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
        cntl = *cntlit;
        cntl->mview->DoIdle( inEvent );
    }
}

void MWind::DoRepeat( const EventRecord &inEvent )
{
    /* Loop Cntls */
    WMNG_CNTLS      cntls = mWmngWind->cntls;
    WMNG_CNTLIT     cntlit;
    WMNG_CNTL       *cntl;
    for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
        cntl = *cntlit;
        cntl->mview->DoRepeat( inEvent );
    }
}

■ MWind::HandleMouseMove() - マウスムーブ処理

マウスムーブ処理です。マウスポインタが指すコントロールに HandleMouseMove() を送ります。マウスプレス時は、プレスされたコントロールに送り続けます。

void MWind::HandleMouseMove( const EventRecord &inEvent )
{
    DrawEnv de( mWmngWind->wind );

    /* Get Mouse Location */
    Point           where;
    where = inEvent.where;
    GlobalToLocal( &where );

    WMNG_CNTLS      cntls = mWmngWind->cntls;
    WMNG_CNTLIT     cntlit;
    WMNG_CNTL       *cntl;
    Rect            rect;
    Boolean         found = false;
    if( ::Button() && MView::mMViewClicked ){
        for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
            cntl = *cntlit;
            if( cntl->mview == MView::mMViewClicked ){
                cntl->mview->HandleMouseMove( inEvent );
                found = true;
                break;
            }
        }
    }else{
        for( cntlit=cntls.begin(); cntlit!=cntls.end(); ++cntlit ){
            cntl = *cntlit;
            rect = cntl->bounds;
            if( PtInRect( where, &rect ) ){
                cntl->mview->HandleMouseMove( inEvent );
                found = true;
                break;
            }
        }
    }
    if( !found ){
        SetThemeCursor( kThemeArrowCursor );
    }
}

■ MWind::ObeyCommand() - コマンド処理

フォーカスを持つコントロールにメニュー処理依頼後、ウィンドウ担当のメニューを実行しています。

Boolean MWind::ObeyCommand( const short menuID, const short menuItem )
{
    Boolean handled = false;

    /* ObeyCommand to Target */
    if( mTarget ){
        handled = mTarget->ObeyCommand( menuID, menuItem );
    }

    /* ObeyCommand to MWind */
    switch( menuID ){
    case mBookmarks:
        switch( menuItem ){
        case iAddCurPage:
            {
                MBrow *mbrow = mWmngWind->mwind->mTargetBrow;
                if( mbrow ){
                    MBrow::AddBookmark( mbrow );
                }
            }
            handled = true;
            break;
        case iManamgeBm:
            SysBeep( 1 );
            handled = true;
            break;
        default:
            {
                MBrow *mbrow = mWmngWind->mwind->mTargetBrow;
                if( mbrow ){
                    MBrow::GoToBookmark( mbrow, menuID, menuItem );
                }
            }
            handled = true;
            break;
        }
        break;
    case mCharCode:
        switch( menuItem ){
        case iWE_ISO8859:
        case iCE_ISO8859:
        case iSE_ISO8859:
        case iMACROMAN:
        case iJP_ISO2022:
        case iJP_SHIFTJIS:
        case iJP_EUC:
        case iKR_EUC:
        case iCH_GB2312:
        case iCH_GBK:
        case iCH_HZ:
        case iCH_BIG5:
        case iCH_EUCTW:
            MBrow::DoCharset( menuItem );
            gCharCodeMenuItem = menuItem;
            handled = true;
            break;
        }
        break;
    case mAutoDet:
        switch( menuItem ){
        case iAutoDetOff:
        case iAutoDetJp:
        case iAutoDetKr:
        case iAutoDetCh:
        case iAutoDetChSimp:
        case iAutoDetChTrad:
        case iAutoDetEAsia:
            MBrow::DoAutoDet( menuItem );
            gAutoDetMenuItem = menuItem;
            handled = true;
            break;
        }
        break;
    default:
        if( menuID < 128 ){
            MBrow *mbrow = mWmngWind->mwind->mTargetBrow;
            if( mbrow ){
                MBrow::GoToBookmark( mbrow, menuID, menuItem );
            }
            handled = true;
        }
        break;
    }

    return handled;
}

キャラセット、オートディテクトは、ブラウザシェルに処理を依頼します。

ブックマーク追加、ブックマークへ移動も、同様にブラウザに処理を依頼しています。

◆◆◆ アクセッサー ◆◆◆

ブラウザシェルクラス、クロームクラスが使用するアクセッサーです。

■ MWind::GetWidget() - ベースウィジェット所得

ウィンドウに対応するモジラのベースウィジェットを返します。

NS_METHOD MWind::GetWidget( nsIWidget** aWidget )
{
    NS_ENSURE_ARG_POINTER(aWidget);

    *aWidget = mWindow;
    NS_IF_ADDREF(*aWidget);

    return NS_OK;
}

XPCOMに於いて、ゲッター関数と呼ばれるタイプのもので、インターフェースポインタを内部でAddRef()して、返します。

上位が nsCOMPtr で受け取る場合は、

    nsCOMPtr<nsIWidget>  aWidget;
    mWmngWind->mwind->GetWidget(getter_AddRefs(aWidget));
    NS_ENSURE_TRUE(aWidget, NS_ERROR_FAILURE);

この様に処理します。nsCOMPtrのおかげで、aWidget のスコープの終わりで、自動的に Release() されます。

■ MWind::GetTitle() - タイトル所得

■ MWind::SetTitle() - タイトル設定

Macintosh標準の方法で、ウィンドウのタイトルを設定/取得します。

void MWind::GetTitle( Str255 title )
{
    NS_ASSERTION(mWmngWind, "SetTitle on null mWmngWind");
    NS_ASSERTION(mWmngWind->wind, "SetTitle on null WindowPtr");

    GetWTitle( mWmngWind->wind, title );
}

void MWind::SetTitle( Str255 title )
{
    NS_ASSERTION(mWmngWind, "SetTitle on null mWmngWind");
    NS_ASSERTION(mWmngWind->wind, "SetTitle on null WindowPtr");

    SetWTitle( mWmngWind->wind, title );
}


◆ ブラウザシェルクラス (MBrow) ◆

■ クラス定義

MBrowクラスの定義では、モジラ関連のメンバ変数として、WebBrowser関連インターフェースへのポインタを持ちます。また、モジラへのイベントの渡し口であるメッセージシンクをstaticメンバとして持ちます。

WMNG_WIND *mWmngWind、WMNG_CNTL *mWmngCntl はウィンドウ管理マネージャーにより管理されるデータで、親WMNG_WINDデータへのポインタ、当WMNG_BROWデータへのポインタを保持します。

初期URL文字列は、ウィンドウ管理マネージャーのブラウザシェルデータであるWMNG_BROWクラスが持っています。

PPBrowserでは、クロームオブジェクトへのポインタは、ウィンドウクラスが保持していますが、marbrowでは、このブラウザシェルクラスが保持します。クロームは、ウィンドウ毎の処理とブラウザ毎の処理を含みます。marbrowでは、1ウインドウ複数ブラウザをサポートするので、クロームインスタンスはブラウザインスタンスと1対1で生成され、各ブラウザ、ブラウザシェルに関連付けされます。

class MBrow : public MView
{
private:
    typedef MView Inherited;

// Friends
friend class MChrome;

/* Constructor, Destructor */
public:
    MBrow( WMNG_WIND *wmngWind, WMNG_CNTL *wmngCntl );
    virtual ~MBrow();
    .
    .
/* Attribute */
public:
    WMNG_WIND       *mWmngWind;
    WMNG_CNTL       *mWmngCntl;
/* BrowserShell */
protected:
    static nsMacMessageSink         mMessageSink;
protected:
    nsCOMPtr<nsIWebBrowser>         mWebBrowser;
    nsCOMPtr<nsIBaseWindow>         mWebBrowserAsBaseWin;
    nsCOMPtr<nsIWebNavigation>      mWebBrowserAsWebNav;
    /* BrowserWindow in PP Brow */
    MChrome*                        mBrowserChrome;
    /* Some Browser Status */
protected:
    Boolean                         mLoading;
    nsString                        mTitle;
};

【MBrow.cpp】

/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
/* Static Variable                                               */
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
nsMacMessageSink MBrow::mMessageSink;

モジラのイベント処理は、widgetモジュール内に存在します。エンベッディングの場合、フレームワークで受けた各種イベントを、nsMacMessageSinkを使用して、mMessageSink.DispatchOSEvent()によりモジラに送り込んで行きます。

◆◆◆ 生成、破棄 ◆◆◆

■ MBrow::MBrow() - コンストラクタ

MBrowクラスのコンストラクタでは、クロームの生成と自分への関連付け、ブラウザインスタンスの生成を行います。

MBrow::MBrow( WMNG_WIND *wmngWind, WMNG_CNTL *wmngCntl )
    : mWmngWind( wmngWind ), mWmngCntl( wmngCntl ),
      mBrowserChrome( NULL ),
      mLoading( false )
{
    Create();
}

NS_IMETHODIMP MBrow::Create()
{
    nsresult        rv;

    /* BrowserWindow : Make Chrome */
    mBrowserChrome = new MChrome;
    NS_ENSURE_TRUE(mBrowserChrome, NS_ERROR_OUT_OF_MEMORY);
    NS_ADDREF(mBrowserChrome);
    mBrowserChrome->BrowserWindow() = mWmngWind->mwind;

    /* Create WebBrowser */     /* BrowserShell */
    mWebBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);

    nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(mWebBrowser));
    NS_ENSURE_TRUE(baseWin, NS_ERROR_FAILURE);
    mWebBrowserAsBaseWin = baseWin;

    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mWebBrowser));
    NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
    mWebBrowserAsWebNav = webNav;

    /* Set WmngWind */
    mWmngCntl->cntlH = NULL;
    mWmngCntl->mview = this;

    return NS_OK;
}

marbrowでは、クロームインスタンスは各ブラウザインスタンス毎に生成され、関連付けられます。

ブラウザは、nsWebBrowserクラスのインスタンスです。そのソースは、embeddingモジュール内に存在します。nsWebBrowserは、do_CreateInstance(NS_WEBBROWSER_CONTRACTID, &rv); によりインスタンス生成され、その代表的インターフェースである、nsIWebBrowerを返します。これをmWebBrowserメンバに格納しています。

nsWebBrowserを扱う場合、このnsIWebBrower mWebBrowserだけで十分で、他のインターフェースは必要な時にQueryInterface()でそのつど取得しても良いのですが、ここでは頻繁に使用するインターフェースである、nsIBaseWindow、nsIWebNavigationも、同時に取得してメンバ変数に格納しています。

■ MBrow::FinishCreateSelf() - 全階層構築後の最終処理

ここでのモジラ固有の処理は、nsWebBrowserの形状(位置、サイズなど)を初期化することです。形状関連のインターフェースである nsIBaseWindow mWebBrowserAsBaseWin の InitWindow()、Create() により、とりあえず初期化し、その後、AdjustFrame() 共通メソッドにより、正しく位置決めしています。

また、SetTopLevelWindow() により、クロームを設定、初期化します。

void MBrow::FinishCreateSelf()
{
    DrawEnv de( mWmngWind->wind );

    nsCOMPtr<nsIWidget>  aWidget;
    mWmngWind->mwind->GetWidget(getter_AddRefs(aWidget));
    if( !aWidget ) NS_ERROR( "GetWidget failure" );

    /* Init BaseWindow */
    Rect    rect;
    GetBestCntlRect( mWmngCntl->bounds, rect );
    nsRect r(rect.left, rect.top,
                rect.right - rect.left, rect.bottom - rect.top);
    mWebBrowserAsBaseWin->InitWindow(aWidget->GetNativeData(NS_NATIVE_WIDGET),
                                 nsnull, r.x, r.y, r.width, r.height);
    mWebBrowserAsBaseWin->Create();
    AdjustFrame( rect );

    /* Tell BrowserShell about Chrome */
    SetTopLevelWindow(mBrowserChrome);
    /* Tell Chrome about BrowserShell */
    mBrowserChrome->BrowserShell() = this;

    /* Kill Scrollbars */
    WMNG_BROW *wmng_brow = dynamic_cast<WMNG_BROW*>(mWmngCntl);
    if( wmng_brow->killScrollbars ){
        KillContentScrollbars();
    }

    /* Enable & Show */
    EnableSelf( true );
    ShowSelf( true );

    /* Set Focus */
    SetFocus();

    /* Load URL */
    if( wmng_brow->startURL[0] != 0 ){
        LoadURL( wmng_brow->startURL, MAX_URLSTRING );
    }
}

自分自身を有効化し、表示した後、最後に初期URLをロードします。

■ MBrow::SetTopLevelWindow() - ブラウザクロームの設定、初期化

MBrow::FinishCreateSelf() からコールされます。ブラウザ初期化の最終段階です。nsIWebBrowser::SetContainerWindow() により、nsWebBrowserに MChromeオブジェクトを、クロームとして認識させます。

また、コメントにあるようにnsIDocShell関連の初期化が必要であれば、合わせて行います。

NS_IMETHODIMP MBrow::SetTopLevelWindow(nsIWebBrowserChrome * aTopLevelWindow)
{
    mWebBrowser->SetContainerWindow(aTopLevelWindow);  

    /*
    In case we needed to do something with the underlying docshell...   

    nsCOMPtr<nsIDocShell>    aDocShell;
    mWebBrowser->GetDocShell(getter_AddRefs(aDocShell));
    NS_ENSURE_TRUE(aDocShell, NS_ERROR_FAILURE);
    */

    nsresult    rv;

    /* Set URIContentListener to WebBrowser */
    if( aTopLevelWindow ){
        nsCOMPtr<nsIURIContentListener> browserAsURIContentListene
              r(do_GetInterface(aTopLevelWindow));
        NS_ENSURE_TRUE(browserAsURIContentListener, NS_ERROR_INVALID_ARG);
        rv = mWebBrowser->SetParentURIContentListener( browserAsURIContentListener );
        NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    }else{
        rv = mWebBrowser->SetParentURIContentListener( nsnull );
        NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    }

    return NS_OK;
}

marbrowのクロームは、PPBrowserが装備しているインターフェースに加え、nsIURIContentListenerを装備します。nsIWebBrowser::SetParentURIContentListener() により、nsWebBrowserに nsIURIContentListener を認識させます。

■ MBrow::~MBrow() - デストラクタ

MBrowクラスのデストラクタでは、ネット処理を中止し、ブラウザシェルとクロームの関係を断ち切り、クロームを破棄します。

MBrow::~MBrow()
{
    Destroy();

    SetTopLevelWindow( nsnull );

    /* Destroy Chrome */
    if( mBrowserChrome ){
        mBrowserChrome->BrowserShell() = nsnull;
        mBrowserChrome->BrowserWindow() = nsnull;
        NS_RELEASE(mBrowserChrome);
    }
}

void MBrow::Destroy()
{
    /* Fource Stop */
    Stop();
}

◆◆◆ ウェブシェル ◆◆◆

■ MBrow::GetWebBrowser() - ブラウザゲッター

■ MBrow::SetWebBrowser() - ブラウザセッター

主にクロームからコールされるメソッドです。クロームクラスが使用する、ブラウザ (nsIWebBrowser) の、ゲッター(取得)、セッター(設定) です。

NS_METHOD MBrow::GetWebBrowser(nsIWebBrowser** aBrowser)
{
    NS_ENSURE_ARG_POINTER(aBrowser);

    *aBrowser = mWebBrowser;
    NS_IF_ADDREF(*aBrowser);
    return NS_OK;
}


NS_METHOD MBrow::SetWebBrowser(nsIWebBrowser* aBrowser)
{
    NS_ENSURE_ARG(aBrowser);

    DrawEnv de( mWmngWind->wind );

    nsCOMPtr<nsIWidget>  aWidget;
    mWmngWind->mwind->GetWidget(getter_AddRefs(aWidget));
    NS_ENSURE_TRUE(aWidget, NS_ERROR_FAILURE);

    mWebBrowser = aBrowser;

    nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(mWebBrowser));
    NS_ENSURE_TRUE(baseWin, NS_ERROR_FAILURE);
    mWebBrowserAsBaseWin = baseWin;

    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mWebBrowser));
    NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
    mWebBrowserAsWebNav = webNav;

    /* Init BaseWindow */
    Rect    rect;
    GetBestCntlRect( mWmngCntl->bounds, rect );
    nsRect r(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
    mWebBrowserAsBaseWin->InitWindow(aWidget->GetNativeData(NS_NATIVE_WIDGET),
                nsnull, r.x, r.y, r.width, r.height);
    mWebBrowserAsBaseWin->Create();
    AdjustFrame( rect );

    return NS_OK;
}

■ MBrow::GetTitle() - タイトル取得

■ MBrow::SetTitle() - タイトル設定

marbrowは、1ウインドウ複数ブラウザの為、クロームからのタイトル通知を各ブラウザシェルが保持し、メインウィンドウのタイトルバーには、最後に通知されたものを表示しています。

void MBrow::GetTitle( nsString &outTitle )
{
    outTitle = mTitle;
}

void MBrow::SetTitle( const nsString &inTitle )
{
    mTitle = inTitle;

    Str255 aStr;
    UMacUnicode::StringToStr255( mTitle, aStr );
    mWmngWind->mwind->SetTitle( aStr );
}

◆◆◆ イベント ◆◆◆

MBrowオブジェクトのイベント処理は、基本的には、nsMacMessageSinkを介してモジラにイベントをディスパッチすることにより、処理します。

■ MBrow::DrawSelf() - 描画処理

■ MBrow::ShowSelf() - 表示/非表示

■ MBrow::EnableSelf() - 有効/無効化

■ MBrow::ActivateSelf() - 活性/非活性化

void MBrow::DrawSelf()
{
    DrawEnv de( mWmngWind->wind );

    /* Draw Self */
    EventRecord osEvent;
    osEvent.what = updateEvt;
    mMessageSink.DispatchOSEvent(osEvent, mWmngWind->wind);
}

void MBrow::ShowSelf( Boolean flag )
{
    mWebBrowserAsBaseWin->SetVisibility( flag );
    Inherited::ShowSelf( flag );
}

void MBrow::EnableSelf( Boolean flag )
{
    Inherited::EnableSelf(flag );
}

void MBrow::ActivateSelf( Boolean flag )
{
    Inherited::ActivateSelf(flag );
}

■ MBrow::ResizeFrameTo() - 絶対リサイズ

■ MBrow::ResizeFrameBy() - 相対リサイズ

■ MBrow::MoveBy() - 相対ムーブ

■ MBrow::AdjustFrame() - フレーム調整

ブラウザのムーブ、リサイズを処理します。最終的には、nsIBaseWindow::SetPositionAndSize() により設定します。

void MBrow::ResizeFrameTo( SInt16 inWidth, SInt16 inHeight, Boolean inRefresh )
{
    Rect rect;
    SetRectWH( mWmngCntl->bounds, inWidth, inHeight );
    GetBestCntlRect( mWmngCntl->bounds, rect );
    AdjustFrame( rect );
}

void MBrow::ResizeFrameBy( SInt16 inWidthDelta, SInt16 inHeightDelta, Boolean inRefresh )
{
    Rect rect;
    IncRectWH( mWmngCntl->bounds, inWidthDelta, inHeightDelta );
    GetBestCntlRect( mWmngCntl->bounds, rect );
    AdjustFrame( rect );
}

void MBrow::MoveBy( SInt16 inHorizDelta, SInt16 inVertDelta, Boolean inRefresh )
{
    Rect rect;
    OffsetRect( &mWmngCntl->bounds, inHorizDelta, inVertDelta );
    GetBestCntlRect( mWmngCntl->bounds, rect );
    AdjustFrame( rect );
}

void MBrow::AdjustFrame( const Rect &inRect )
{
    DrawEnv de( mWmngWind->wind );

    nsRect r(inRect.left, inRect.top, inRect.right - inRect.left, inRect.bottom - inRect.top);
    mWebBrowserAsBaseWin->SetPositionAndSize(r.x, r.y, r.width, r.height, PR_TRUE);
}

■ MBrow::ClickSelf() - クリック処理

■ MBrow::EventMouseUp() - マウスアップ処理

■ MBrow::HandleKeyPress() - キープレス処理

■ MBrow::DoIdle() - アイドル処理

■ MBrow::HandleMouseMoved() - マウスムーブ共通処理

nsMacMessageSinkを使用して、DispatchOSEvent() によりイベントを送り込みます。

void MBrow::ClickSelf( const EventRecord &inEvent )
{
    if( !mActive || !mEnable ) return;

    DrawEnv de( mWmngWind->wind );
    if( mWmngWind->mwind->mTarget != this ){
        SetFocus();
    }
    mMessageSink.DispatchOSEvent( (EventRecord&)inEvent, mWmngWind->wind );
}

他は同様にて割愛。

■ MBrow::ObeyCommand() - コマンド処理

コピー、ペースト等のクリップボード処理を行っています。PPBrowserと同等の処理です。

Boolean MBrow::ObeyCommand( const short menuID, const short menuItem )
{
    Boolean handled = false;
    nsresult rv;
    nsCOMPtr<nsIClipboardCommands> clipCmd;

    /* ObeyCommand to Me */
    switch( menuID ){
    case mEdit:
        switch( menuItem ){
        case iCut:
            rv = GetClipboardHandler( getter_AddRefs(clipCmd) );
            if( NS_SUCCEEDED(rv) ){
                clipCmd->CutSelection();
            }
            handled = true;
            break;
        case iCopy:
            rv = GetClipboardHandler( getter_AddRefs(clipCmd) );
            if( NS_SUCCEEDED(rv) ){
                clipCmd->CopySelection();
            }
            handled = true;
            break;
        case iPaste:
            rv = GetClipboardHandler( getter_AddRefs(clipCmd) );
            if( NS_SUCCEEDED(rv) ){
                clipCmd->PasteSelection();
            }
            handled = true;
            break;
        case iClear:
            break;
        case iSelectAll:
            rv = GetClipboardHandler( getter_AddRefs(clipCmd) );
            if( NS_SUCCEEDED(rv) ){
                clipCmd->SelectAll();
            }
            handled = true;
            break;
        }
    }

    return handled;
}

◆◆◆ オペレーション ◆◆◆

■ MBrow::LoadURL() - URLロード

<nsIWebNavigation::LoadURI() により、URLをロードします。

void MBrow::LoadURL( const char *text, SInt32 len )
{
    if( len < 0 ) len = strlen( text );
    nsAutoString string; string.AssignWithConversion(text, len);
    LoadURL(string);
}

void MBrow::LoadURL( const nsString& text )
{
    nsresult rv = mWebBrowserAsWebNav->LoadURI( text.GetUnicode(),
        nsIWebNavigation::LOAD_FLAGS_NONE );
#ifdef DEBUG
    if( NS_FAILED(rv) ){
        char    strText[MAX_URLSTRING];
        PRInt32 len = MAX_URLSTRING;
        UMacUnicode::StringToStr( text, strText, &len );
        printf("mWebBrowserAsWebNav->LoadURI failed : '%s'\n",strText);
    }
#endif /* DEBUG */
}

■ MBrow::ViewSource() - ビューソース

プレーンウインドウを作成し、そのブラウザオブジェクトのドックシェルに対し、SetViewMode() します。

int MBrow::ViewSource( const nsString& url )
{
    /* Create New Browser */
    WMNG_WIND *wmngWind = DoNewPlain( 1000, -1, -1, -1, -1,
        nsIWebBrowserChrome::CHROME_DEFAULT );
    NS_ENSURE_TRUE(wmngWind, NS_ERROR_FAILURE);
    
    WMNG_CNTL *cntl = FindWmngCntlByID( wmngWind, CNTLTYPE_BROW, 1000 );
    NS_ENSURE_TRUE(cntl, NS_ERROR_FAILURE);
    
    MBrow *mcntl = dynamic_cast<MBrow*>(cntl->mview);
    NS_ENSURE_TRUE(mcntl, NS_ERROR_FAILURE);

    /* Get WebBrowser */
    nsCOMPtr<nsIWebBrowser> webBrowser;
    mcntl->GetWebBrowser(getter_AddRefs(webBrowser));    
    NS_ENSURE_TRUE(webBrowser, NS_ERROR_FAILURE);

    /* Get DocShell */
    nsCOMPtr<nsIDocShell> docShell = do_GetInterface(webBrowser);
    NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);

    /* Set ViewMode */
    docShell->SetViewMode( nsIDocShell::viewSource );

    /* Load URL */
    mcntl->LoadURL( url );

    return NS_OK;
}

■ MBrow::DoPrint() - プリント処理

nsIDocShell -> nsIContentViewer -> nsIContentViewerFile とインターフェースを辿り、Print() をコールします。

int MBrow::DoPrint()
{
    /* Get DocShell */
    nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mWebBrowser);
    NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);

    /* Get Viewer */
    nsCOMPtr<nsIContentViewer> viewer;
    docShell->GetContentViewer(getter_AddRefs(viewer));

    if( viewer ){
        /* Get ViewerFile */
        nsCOMPtr<nsIContentViewerFile> viewerFile = do_QueryInterface(viewer);
        if( viewerFile ){
            viewerFile->Print(PR_FALSE,0);
        }
    }

    return 0;
}

■ MBrow::SetLocaCntlText() - ロケーションバー文字列設定

■ MBrow::SetStatCntlText() - ステータスバー文字列設定

自分と関連付けられたロケーションバー、ステータスバーを検索し、これらに文字列設定を依頼します。

void MBrow::SetLocaCntlText( const nsString& locaText )
{
    MLoca* mcntl = FindLocaCntl();
    if( mcntl ){
        mcntl->SetText( locaText );
        mcntl->SetFocus();
    }
}

void MBrow::SetStatCntlText( const char* statText, SInt32 len )
{
    MStat* mcntl = FindStatCntl();
    if( mcntl ){
        mcntl->SetText( statText, len );
    }
}

void MBrow::SetStatCntlText( const PRUnichar* statText )
{
    MStat* mcntl = FindStatCntl();
    if( mcntl ){
        mcntl->SetText( statText );
    }
}

void MBrow::SetStatCntlText( const nsString& statText )
{
    MStat* mcntl = FindStatCntl();
    if( mcntl ){
        mcntl->SetText( statText );
    }
}

◆◆◆ ナビゲーション ◆◆◆

■ MBrow::OnNetStart() - ネット処理開始

■ MBrow::OnNetStop() - ネット処理終了

■ MBrow::OnProgressChange() - プログレスチェンジ

ネット処理状況を各コントロールに反映します。

PPBrowserでは、これらはウィンドウクラスにありますが、本来はブラウザ毎のステータスなので、ブラウザシェルクラスにあるべきでしょう。

marbrowでは、プログレスバーをサポートしていないので、プログレスチェンジは空です。

NS_METHOD MBrow::OnNetStart( nsIWebProgress *progress, nsIRequest *request, 
    PRInt32 progressStateFlags, PRUint32 status )
{
    /* Set Loading */
    SetLoading( true );

    /* Disp StatCntl */
    nsAutoString str;
    str.AssignWithConversion("Transferring ...");
    SetStatCntlText( str );

    /* Enable Stop Button */
    {
        WMNG_CNTL*  cntl;
        MButt*      mcntl;

        for( cntl=nil; ; ){
            cntl = FindSourceCntlWithFuncAll( cntl, mWmngWind, "stop", mWmngCntl->id );
            if( !cntl ) break;
            mcntl = dynamic_cast<MButt*>(cntl->mview);
            if( mcntl ){
                mcntl->EnableSelf( true );
            }
        }
    }

    return NS_OK;
}

NS_METHOD MBrow::OnNetStop( nsIWebProgress *progress, nsIRequest *request, 
    PRInt32 progressStateFlags, PRUint32 status )
{
    /* Reset Loading */
    SetLoading( false );

    /* Disp StatCntl */
    nsAutoString str;
    str.AssignWithConversion("Transfer Done.");
    SetStatCntlText( str );

    WMNG_CNTL*  cntl;
    MButt*      mcntl;

    /* Activate/Deactivate Back Button */
    for( cntl=nil; ; ){
        cntl = FindSourceCntlWithFuncAll( cntl, mWmngWind, "back", mWmngCntl->id );
        if( !cntl ) break;
        mcntl = dynamic_cast<MButt*>(cntl->mview);
        if( mcntl ){
            mcntl->EnableSelf( CanGoBack() );
        }
    }
    /* Activate/Deactivate Forward Button */
    for( cntl=nil; ; ){
        cntl = FindSourceCntlWithFuncAll( cntl, mWmngWind, "forward", mWmngCntl->id );
        if( !cntl ) break;
        mcntl = dynamic_cast<MButt*>(cntl->mview);
        if( mcntl ){
            mcntl->EnableSelf( CanGoForward() );
        }
    }
    /* Dectivate Stop Button */
    for( cntl=nil; ; ){
        cntl = FindSourceCntlWithFuncAll( cntl, mWmngWind, "stop", mWmngCntl->id );
        if( !cntl ) break;
        mcntl = dynamic_cast<MButt*>(cntl->mview);
        if( mcntl ){
            mcntl->EnableSelf( false );
        }
    }

    return NS_OK;
}

NS_METHOD MBrow::OnProgressChange( nsIWebProgress *progress, nsIRequest *request,
                                    PRInt32 curSelfProgress, PRInt32 maxSelfProgress,
                                    PRInt32 curTotalProgress, PRInt32 maxTotalProgress )
{
    return NS_OK;
}

■ MBrow::CanGoBack() - Back可能?

■ MBrow::CanGoForward() - Forward可能?

■ MBrow::Back() - Back処理

■ MBrow::Forward() - Forward処理

■ MBrow::Stop() - Stop処理

■ MBrow::Refresh() - Refresh処理

■ MBrow::GetCurrentURL() - 現URL取得

全て、nsIWebNavigationインターフェースに処理を依頼します。

Boolean MBrow::CanGoBack()
{
    PRBool      canDo;
    nsresult    rv;

    rv = mWebBrowserAsWebNav->GetCanGoBack(&canDo);
    return (NS_SUCCEEDED(rv) && canDo);
}

Boolean MBrow::CanGoForward()
{
    PRBool      canDo;
    nsresult    rv;

    rv = mWebBrowserAsWebNav->GetCanGoForward(&canDo);
    return (NS_SUCCEEDED(rv) && canDo);
}

void MBrow::Back()
{
    if (CanGoBack())
        mWebBrowserAsWebNav->GoBack();
    else
        ::SysBeep(5);
}

void MBrow::Forward()
{
    if (CanGoForward())
        mWebBrowserAsWebNav->GoForward();
    else
        ::SysBeep(5);
}

void MBrow::Stop()
{
    mWebBrowserAsWebNav->Stop();
}
void MBrow::Refresh( PRInt32 reloadFlags )
{
    /* -- Reload Flags --                           */
    /* enum { LOAD_FLAGS_NONE = 0U };               */
    /* enum { LOAD_FLAGS_MASK = 65535U };           */
    /* enum { LOAD_FLAGS_IS_REFRESH = 16U };        */
    /* enum { LOAD_FLAGS_IS_LINK = 32U };           */
    /* enum { LOAD_FLAGS_BYPASS_HISTORY = 64U };    */
    /* enum { LOAD_FLAGS_REPLACE_HISTORY = 128U };  */
    /* enum { LOAD_FLAGS_BYPASS_CACHE = 256U };     */
    /* enum { LOAD_FLAGS_BYPASS_PROXY = 512U };     */
    mWebBrowserAsWebNav->Reload( reloadFlags );
}

void MBrow::GetCurrentURL( char *text, SInt32 len )
{
    text[0] = 0;

    nsresult            rv;
    nsCOMPtr<nsIURI>    uri;
    rv = mWebBrowserAsWebNav->GetCurrentURI( getter_AddRefs(uri) );
    if( NS_SUCCEEDED(rv) ){
        if( uri ){
            char *buf = nsnull;
            uri->GetSpec( &buf );
            if( buf ){
                if( strlen( buf ) < len ){
                    strcpy( text, buf );
                }else{
                    strncpy( text, buf, len-1 );
                    text[len-1] = 0;
                }
                Recycle(buf);
            }
        }else{
            text[0] = 0;
        }
    }
}

■ キャラセット、オートディテクト処理

■ ブックマーク処理

ここでは割愛します。詳しくはソースを御覧くだい。


◆ クロームクラス (MChrome) ◆

marbrowのクロームクラス (MChrome)は、PPBrowserのクロームクラス (CWebBrowserChrome)とほとんど同等ですが、大きく異なる点が2つあります。

◆その1 - クロームインスタンスの生成

PPBrowserのクロームクラスは、ウィンドウクラスのコンストラクタにて、ウィンドウオブジェクト毎に生成されますが、marbrowのクロームクラスは、ブラウザシェルクラスのコンストラクタにて、ブラウザオブジェクト毎に生成されます。

クロームクラスは、ウィンドウオブジェクト毎の処理、ブラウザオブジェクト毎の処理の両者を含みます。marbrowでは、1ウインドウ複数ブラウザをサポートするので、この構成が必須となります。

◆その2 - nsIURIContentListenerインターフェース

marbrowでは、PPBrowserがクロームに装備するインターフェースに加え、nsIURIContentListenerインターフェースを追加装備します。

このnsIURIContentListenerインターフェースを装備すると、URLオープン開始時に、当該インターフェース内のOnStartURIOpen()メソッドがコールされます。このメソッドにより、スキームやターゲット名、URLの内容に応じて、処理をインターセプトすることが可能になります。

marbrowでは、スキームが、mailto:、news: の時、処理をインターセプトして、InternetConfigで設定されたデフォルトメールクライアントに処理を依頼しています。

■ クラス定義

PPBrowserのクロームクラスとの差は、nsIURIContentListenerインターフェースを追加装備していることだけです。それ以外は、全く同等です。

class MChrome : public nsIWebBrowserChrome,
                public nsIWebProgressListener,
                public nsIBaseWindow,
                public nsIPrompt,
                public nsIURIContentListener,
                public nsIInterfaceRequestor
{
// Friends
friend class MBrow;

// Constructor, Destructor
protected:
    MChrome();
    virtual ~MChrome();

// COM Interfaces
public:
    // nsISupports
    NS_DECL_ISUPPORTS
    // nsIWebBrowserChrome
    NS_DECL_NSIWEBBROWSERCHROME
    // nsIWebProgressListener
    NS_DECL_NSIWEBPROGRESSLISTENER
    // nsIBaseWindow
    NS_DECL_NSIBASEWINDOW
    // nsIPrompt
    NS_DECL_NSIPROMPT
    // nsIURIContentListener
    NS_DECL_NSIURICONTENTLISTENER
    // nsIInterfaceRequestor
    NS_DECL_NSIINTERFACEREQUESTOR

// Accessors
protected:
    MWind*& BrowserWindow();
    MBrow*& BrowserShell();

// Attributes
protected:
    MWind* mBrowserWindow;
    MBrow* mBrowserShell;

    static vector<MChrome*> mgBrowserList;
};

【MChrome.cpp】

/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
/* Static Variable                                               */
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
vector<MChrome*> MChrome::mgBrowserList;
							
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
/* nsISupports                                                   */
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
NS_IMPL_ADDREF(MChrome)
NS_IMPL_RELEASE(MChrome)

NS_INTERFACE_MAP_BEGIN(MChrome)
    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome)
    NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
    NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
    NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
    NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
    NS_INTERFACE_MAP_ENTRY(nsIURIContentListener)
    NS_INTERFACE_MAP_ENTRY(nsIPrompt)
NS_INTERFACE_MAP_END

◆◆◆ nsIURIContentListenerインターフェース ◆◆◆

以下に追加装備したnsIURIContentListenerインターフェースのみを示します。

他のインターフェースは、クラス名の違いや、フレームワークの違いによる、文法的な差のみで、PPBrowserの実装と同等ですので、割愛します。

■ MChrome::OnStartURIOpen() - URLオープン開始

スキームが、mailto: または news: の時、InternetConfig にURLロードを依頼することにより、ユーザーが設定したデフォルトのメールクライアントを起動し、モジラの処理はアボートさせています。

NS_IMETHODIMP MChrome::OnStartURIOpen(nsIURI* aURI, 
   const char* aWindowTarget, PRBool* aAbortOpen)
{
    NS_ENSURE_ARG_POINTER(aURI);
    NS_ENSURE_ARG_POINTER(aAbortOpen);

    nsresult rv;
    char *bufSpec = nsnull;
    char *bufScheme = nsnull;
    char *bufTarget;
    nsAutoString strSpec;
    nsAutoString strScheme;
    nsAutoString strTarget;

    rv = aURI->GetSpec(&bufSpec);
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    rv = aURI->GetScheme(&bufScheme);
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    bufTarget = (char*)aWindowTarget;

    strSpec.AssignWithConversion(bufSpec);
    strScheme.AssignWithConversion(bufScheme);
    strTarget.AssignWithConversion(bufTarget);

#ifdef DEBUG
    printf("OnStartURIOpen : scheme='%s' target='%s' spec='%s'\n",
        bufScheme, bufTarget, bufSpec);
#endif /* DEBUG */

    if( strScheme.EqualsIgnoreCase("http") ){       /* http: */
        if( strTarget.EqualsIgnoreCase("_blank") ){     /* New Window */
            /* Leave process to nsIWebBrowserChrome::FindNamedBrowserItem() */
            /* which fails to set nsIDocShellTreeItem *aBrowserItem ,       */
            /* then nsIWebBrowserChrome::GetNewBrowser()                    */
            /* which makes Plain Browser Window ,                           */
            /* so I do nothing here .                                       */
            ;
        }
    }else
    if( strScheme.EqualsIgnoreCase("javascript") ){ /* javascript: */
        /* Through javascript: */
        ;
    }else
    if( strScheme.EqualsIgnoreCase("mailto")        /* mailto: */
     || strScheme.EqualsIgnoreCase("news") ){       /* news: */
        /* Launch Default Browser */
        OSStatus err = uinetc::LaunchURL( bufSpec );
#ifdef DEBUG
        if( err ){
            printf("uinetc::LaunchURL fail %d: '%s'\n",err,bufSpec);
        }
#endif /* DEBUG */
        *aAbortOpen = PR_TRUE;
    }

    if( bufSpec )   Recycle( bufSpec );
    if( bufScheme ) Recycle( bufScheme );

    return NS_OK;
}

ここでは何も行っていませんが、このメソッドの実装により、

  • スキームが javascript: だった場合
  • スキームが http: で、新しいウインドウが開かれる場合

などの条件で、処理の流れを変えることも可能です。

■ nsIURIContentListenerのその他のメソッド

その他のメソッドはデフォルトの処理で構わないので、基本的には、nsWebBrowserが抱えているデフォルトクロームの当該インターフェースのインプリメント (nsWBURIContentListener) に処理を振ります。

NS_IMETHODIMP MChrome::GetProtocolHandler(nsIURI* aURI,
   nsIProtocolHandler** aProtocolHandler)
{
    /* Leave nsWebBrowser (nsWBURIContentListener) */
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP MChrome::DoContent(const char* aContentType, 
   nsURILoadCommand aCommand, const char* aWindowTarget, 
   nsIChannel* aOpenedChannel, nsIStreamListener** aContentHandler,
   PRBool* aAbortProcess)
{     
    /* Leave nsWebBrowser (nsWBURIContentListener) */
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP MChrome::IsPreferred(const char* aContentType,
   nsURILoadCommand aCommand, const char* aWindowTarget, char ** aDesiredContentType,
   PRBool* aCanHandle)
{
    /* Leave nsWebBrowser (nsWBURIContentListener) */
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP MChrome::CanHandleContent(const char* aContentType,
   nsURILoadCommand aCommand, const char* aWindowTarget, char ** aDesiredContentType,
   PRBool* aCanHandleContent)
{
    /* Leave nsWebBrowser (nsWBURIContentListener) */
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP MChrome::GetLoadCookie(nsISupports ** aLoadCookie)
{
    /* Leave nsWebBrowser (nsWBURIContentListener) */
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP MChrome::SetLoadCookie(nsISupports * aLoadCookie)
{
    /* Leave nsWebBrowser (nsWBURIContentListener) */
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP MChrome::GetParentContentListener(nsIURIContentListener**
   aParentListener)
{
    NS_ENSURE_ARG_POINTER(aParentListener);

    NS_ASSERTION(PR_FALSE, "Not Yet Implemented");
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP MChrome::SetParentContentListener(nsIURIContentListener* 
   aParentListener)
{
    NS_ASSERTION(PR_FALSE, "Not Yet Implemented");
    return NS_ERROR_NOT_IMPLEMENTED;
}


◆ netlibによるファイルダウンロード ◆

ここでは、モジラモジュールの低レベルな利用例として、netlibを直接使用し、指定URLのファイルダウンロードを行ってみます。

■ DnLoadFile() - ファイルダウンロード処理

引き数で指定されたURLのファイルを、netlibを使用してダウンロードし、引き数で指定されたFSSpec、クリエータ、ファイルタイプのファイルとして保存します。

nsresult DnLoadFile( const char* url, FSSpec *spec, OSType creator, OSType fileType )
{
    nsresult rv;

引き数で受け取った、URLのC文字列から、モジラに於けるジェネリックなURI表現であるnsIURIオブジェクトを作成します。

    /* New Uri */
    nsCOMPtr<nsIURI> uri;
    printf( "Calling NS_NewURI for %s...\n", url );
    rv = NS_NewURI( getter_AddRefs( uri ), url );

    if ( NS_SUCCEEDED( rv ) ) {
        printf( "...NS_NewURI completed OK\n" );
    } else {
        printf( "%s %d: NS_NewURI failed, rv=0x%08X\n",
            (char*)__FILE__, (int)__LINE__, (int)rv );
        return rv;
    }

このnsIURIオブジェクトを渡して、netlibの実際のダウンロード部へのチャネルを開きます。

    /* New Channel */
    nsCOMPtr<nsIChannel> channel;
    printf( "Calling NS_OpenURI...\n" );
    rv = NS_OpenURI( getter_AddRefs( channel ), uri, 0 );

    if ( NS_SUCCEEDED( rv ) ) {
        printf( "...NS_OpenURI completed OK\n" );
    } else {
        printf( "%s %d: NS_OpenURI failed, rv=0x%08X\n",
            (char*)__FILE__, (int)__LINE__, (int)rv );
        return rv;
    }

netlibのダウンロードに応じて、こちら側での受け取りを執行する、リスナーオブジェクト (DnloadListener) を生成します。

    /* New Listener */
    DnloadListener *listener = new DnloadListener( url,
        spec, creator, fileType );
    NS_ENSURE_TRUE( listener, NS_ERROR_FAILURE);
    NS_IF_ADDREF( listener );
netlibチャネルに対し、リスナーオブジェクトを渡して、非同期リードの開始を依頼します。
    /* Read Channel Async */
    printf( "Doing AsyncRead...\n" );
    rv = channel->AsyncRead( listener, 0 );
    printf( "...AsyncRead completed rv=0x%08X\n", rv );

    /* Cleanup */
    NS_RELEASE( listener );

    return NS_OK;
}

■ DnloadListenerクラス定義

DnloadListenerクラスの定義です。

nsIStreamObserver、nsIStreamListenerインターフェースを継承し、

  • OnStartRequest()
  • OnStopRequest()
  • OnDataAvailable()

メソッドを装備します。

メンバ変数には、ダウンロードするURLの文字列、セーブするファイルのFSSpec、クリエータ、ファイルタイプ、進行状況表示ダイアログへのポインタ、各種進行状況管理数値、ダミーウィジェットへのポインタなどを持ちます。

class DnloadListener : public nsIStreamListener {
public:
    NS_DECL_ISUPPORTS
    NS_DECL_NSISTREAMOBSERVER
    NS_DECL_NSISTREAMLISTENER

/* Constructor, Destructor */
public:
    DnloadListener( const char* aUrl,
        FSSpec *spec, OSType creator, OSType fileType );
    virtual ~DnloadListener();

/* Attribute */
protected:
    nsCString   mURLString;
    PRInt32     mTotalLength;
    PRInt32     mBytesRead;
    nsCOMPtr<nsIWidget> mDummyWidget;

    FSSpec      mSpec;
    OSType      mCreator;
    OSType      mFileType;
    short       mRefNum;

    MProgDlog   *mDlog;
};

■ DnloadListener::DnloadListener - コンストラクタ

ここでの主要な処理は、ダミーウィジェットの作成です。

非同期ダウンロード状況は、モジラのイベントシンクを通して通知されます。イベントシンクはモジラウィジェットにより処理されます。

このダウンロードはブラウザウィンドウが表示されていない場合にも(つまり、モジラウィジェットが存在しない場合)起動しうるので、イベントシンクを稼動する為にダミーウィジェットを生成しています。

DnloadListener::DnloadListener( const char* aUrl, 
    FSSpec *spec, OSType creator, OSType fileType )
 : mTotalLength( 0 ), mBytesRead( 0 ),
   mURLString( aUrl ),
   mSpec( *spec ), mCreator( creator ),
   mFileType( fileType ), mRefNum( 0 ), mDlog( nil )
{
    printf( "DnloadListener::DnloadListener -> in\n");

    NS_INIT_REFCNT();

    /* New Dummy Widget */
    nsresult    rv;
    mDummyWidget = do_CreateInstance(kWindowCID, &rv);
    NS_ASSERTION(NS_SUCCEEDED(rv), "New DummyWidget failed");

    /* Create Dummy Widget */
    nsRect r(0, 0, 0, 0);
    rv = mDummyWidget->Create((nsNativeWidget)nil, r,
        nsnull, nsnull, nsnull, nsnull, nsnull);
    NS_ASSERTION(NS_SUCCEEDED(rv), "Create DummyWidget failed");

    printf( "DnloadListener::DnloadListener -> out\n");
}

■ DnloadListener::~DnloadListener - デストラクタ

デストラクタでは、ダミーウィジェットを破棄します。

DnloadListener::~DnloadListener()
{
    printf( "DnloadListener::~DnloadListener -> in\n");

    /* Destroy Dummy Widget */
    nsresult    rv;
    rv = mDummyWidget->Destroy();
    NS_ASSERTION(NS_SUCCEEDED(rv), "Destroy DummyWidget failed");

    printf( "DnloadListener::~DnloadListener -> out\n");
}

■ DnloadListener::OnStartRequest - スタートリクエスト

進行状況表示の為に、ファイルのトータルサイズを取得します。進行状況表示用ダイアログを作成します。セーブファイルをクリエイト、オープンします。

NS_IMETHODIMP DnloadListener::OnStartRequest(nsIChannel *aChannel, nsISupports* aContext)
{
    printf("DnloadListener::OnStartRequest() -> in\n");

    /* Get Total Length */
    nsresult rv;
    PRInt32 aContentLength;
    rv = aChannel->GetContentLength( &aContentLength );
    if ( NS_SUCCEEDED( rv ) ) {
        printf( "nsIChannel::GetContentLength completed OK\n" );
        mTotalLength = aContentLength;
    } else {
        printf( "%s %d: nsIChannel::GetContentLength failed, rv=0x%08X\n",
            (char*)__FILE__, (int)__LINE__, (int)rv );
    }
    printf( "TotalLength = %ld\n", mTotalLength );

    /* New Progress Dialog */
    mDlog = new MProgDlog( dProgress, mTotalLength );

    /* Create File */
    OSErr err;
    err = FSpCreate( &mSpec, mCreator, mFileType, smSystemScript );
    if( err == dupFNErr ){
        err = FSpDelete( &mSpec );
        if( err ){
            printf( "FSpDelete failed, err=0x%08X\n",
                (char*)__FILE__, (int)__LINE__, (int)err );
            return NS_ERROR_FAILURE;
        }
        err = FSpCreate( &mSpec, mCreator, mFileType, smSystemScript );
        if( err ){
            printf( "FSpCreate failed, err=0x%08X\n",
                (char*)__FILE__, (int)__LINE__, (int)err );
            return NS_ERROR_FAILURE;
        }
    }else
    if( err ){        printf( "FSpCreate failed, err=0x%08X\n",
            (char*)__FILE__, (int)__LINE__, (int)err );
        return NS_ERROR_FAILURE;
    }

    /* Open File */
    err = FSpOpenDF( &mSpec, fsWrPerm, &mRefNum );
    if( err ){
        printf( "FSpOpenDF failed, err=0x%08X\n",
            (char*)__FILE__, (int)__LINE__, (int)err );
        return NS_ERROR_FAILURE;
    }

    printf( "DnloadListener::OnStartRequest() -> out\n");

    return NS_OK;
}

■ DnloadListener::OnStopRequest - ストップリクエスト

ファイルをクローズし、ダイアログを破棄します。

NS_IMETHODIMP DnloadListener::OnStopRequest(nsIChannel *aChannel, 
                       nsISupports *aContext,
                        nsresult aStatus, const PRUnichar* aStatusArg)
{
    printf("DnloadListener::OnStopRequest() -> in\n");

    /* Check Read Length */
    if( mBytesRead != mTotalLength ){
        printf( "mBytesRead != mTotalLength : %ld %ld\n",
            mBytesRead, mTotalLength );
    }

    /* Close File */
    if( mRefNum ){
        OSErr   err;
        err = FSClose( mRefNum );
        if( err ){
            printf( "FSClose failed, err=0x%08X\n",
                (char*)__FILE__, (int)__LINE__, (int)err );
        }
        mRefNum = 0;
    }

    /* Delete Progress Dialog */
    if( mDlog ){
        delete mDlog;
        mDlog = nil;
    }

    printf( "------------------------\n" );
    printf( "mURLString   = '%s'\n",    mURLString.GetBuffer() );
    printf( "mTotalLength = %ld\n",     mTotalLength );
    printf( "mBytesRead   = %ld\n",     mBytesRead );
    printf( "------------------------\n" );

    printf( "DnloadListener::OnStopRequest() -> out\n");
    return NS_OK;
}

■ DnloadListener::OnDataAvailable - データ到着

到着したデータ分をチャネルのストリームからリードし、ファイルにライトします。また、ダイアログをアップデートします。

NS_IMETHODIMP DnloadListener::OnDataAvailable(nsIChannel *aChannel,
                         nsISupports *aContext,
                          nsIInputStream *aIStream,
                          PRUint32 aOffset, PRUint32 aLength)
{
    printf( "DnloadListener::OnDataAvailable() -> in\n");

    nsresult rv = NS_OK;
    char buffer[ SIZE_BUFFER ];
    unsigned long bytesRemaining = aLength;
    while ( bytesRemaining ) {
        unsigned int bytesRead;
        /* Read to Buffer */
        rv = aIStream->Read( buffer,
                             PR_MIN( sizeof( buffer ), bytesRemaining ),
                             &bytesRead );
        if ( NS_SUCCEEDED( rv ) ) {
            /* Write to File */
            if( mRefNum ){
                OSErr   err;
                long    wrCount = bytesRead;
                err = FSWrite( mRefNum, &wrCount, buffer );
                if( err ){
                    printf( "FSWrite failed, err=0x%08X\n",
                        (char*)__FILE__, (int)__LINE__, (int)err );
                    break;
                }
                if( wrCount != bytesRead ){
                    printf( "FSWrite failed, wrCount=0x%08X bytesRead=0x%08X\n"
                      , (char*)__FILE__, (int)__LINE__, wrCount, bytesRead );
                    break;
                }
            }
            /* Update Param */
            printf( "bytesRead = %d\n", bytesRead );
            mBytesRead += bytesRead;
            bytesRemaining -= bytesRead;
            /* Update Progress Dialog */
            if( mDlog ){
                if( mDlog->mCancel ){
                    aChannel->Cancel( NS_ERROR_ABORT );
                    return NS_ERROR_ABORT;
                }
                mDlog->SetValue( mBytesRead );
            }
        } else {
            printf( "%s %d: Read error, rv=0x%08X\n",
                (char*)__FILE__, (int)__LINE__, (int)rv );
            break;
        }
    }

    printf( "DnloadListener::OnDataAvailable() -> out\n");

    return NS_OK;
}


 
 
c_o_n_t_a_c_t
Copyright (C) 2000-2002 Symphony, Inc. All Rights Reserved.
English