mazilla mozilla
Introduction
What's mozilla
Purpose of this page
Characteristics of mozilla
Survey of mozilla
Module
Web tool
Technology of mozilla
Other resource
Building mozilla
Construction of environment
for building
Checkout of source
Procedure for building
Image of after-building
Execute mozilla
Embedding
What's embedding
Concept of embedding
Structure of PPBrowser
Analysis of PPBrowser
Structure of embedding
Structure of marbrow
Analysis of marbrow
Fizzilla
What's fizzilla
Build fizzilla
Embedding of fizzilla
FAQ
Talk with Mr. Yatsugi

Analysis of marbrow

Let's analyze marbrow !!


On this sectiion, I analyze codes of marbrow in practice.


Main process

main() - Main function

This is the main procedure of this program. This calls the initializing process, the event loop and the terminating process in this order.

void main( void )
{
    DoInitialize();

    DoEventLoop();

    DoTerminate();
}


Initializing/Terminating process

DoInitialize() - Initializing process

This initializes the whole of the program. This initializes Toolbox, menu bar, apple event and embedding and checks the environment.

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

    SetThemeCursor( kThemeWatchCursor );

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

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

    SetThemeCursor( kThemeArrowCursor );
}

InitToolbox() - Initializing Toolbox

By InitializeMacToolbox() of nsStdLib.shlb, Toolbox is initialized.

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

    gScriptCode = IntlScript();

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

InitWebShell() - Initializing embedding

By NS_InitEmbedding(), embedding is initialized. Because character code setting can be changed by menu, the font sizes except English are also set.

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() -Terminating process

This teminates the whole of the program. This terminates embedding, apple event and Toolbox.

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

TermToolbox() - Terminating Toolbox

This terminates Toolbox. Because OpenTSMAwareApplication() has been done in InitializeMacToolbox() of nsStdLib.shlb, TSM should be closed here.

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

    CloseTSMAwareApplication();
}

TermWebShell() - Terminating embedding

This terminates embedding. This process is equal to that of 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();
}


Event process

DoEventLoop() - Event loop

This is the event loop. The main window is shown intially by DoNew(), and the normal event loop of Macintosh is started. Each gotten event is dispatched to 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() - Event process

This does the process specific to mozilla. This calls nsRepeater::DoRepeaters() in each event loop and nsRepeater::DoIdlers() in each null event. Actually, DoEvent() executes the event.

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() - Event dispatcher

This is the event dispatcher and the normal process of Macintosh. Most of actual processes are left to sub-functions.

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() - Drag window process

This is an actual process at the time of dragging window. I show this as an example of the actual event processes.

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 );
        }
    }
}

Thus, in each event process, a proper window object (MWind) is extracted and requested to do the process.

Here, FindWmngWindByWind() is used to extract the proper window object, which is implemented in wmng.cpp (window manager). Window manager defines data structures for each visual parts as classes, and holds visual hierarchy of each window in tree structure.

MWind class has the method for each event processing, that is inherited from MView class.

All other event processings are something like this, therefore omitted.

DoMenuCommand() - Menu process

This is the menu process. After requesting window related menu process to the window object, this actually executes the menu of application level. That is, to open or close main window, to quit the application, and to download the file by netlib and so on.

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;
}

For file download, URL of downloaded file is gotten by dialog, then executed by DnLoadFile() in dnload.cpp .


Window class (MWind)

Class definition

In class definition of MWind, there is a pointer to the window widget as member variable related to mozilla. Window widget is the mozilla way representation for main window.

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;
};

In addition in PPBrowser, window class has pointers to child control instances that he holds, and to chrome instance.

marbrow doesn't have pointers to each child control instances directly. WMNG_WIND *mWmngWind is the pointer to the window manager's window data. And link to each controls can be traced from it.

And in marbrow, because multiple browsers can be placed on one window, chrome instances are created for each browser. Therefore, pointer to chrome is not held by window class but by browser shell class.

Creation, Disposal

MWind::MWind() - Constructor

Constructor of MWind class controls all of the creation of main window and creation and initialization of child controls including browser.

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;

In ordinary Macintosh way, creates main window, creates root control and so on.

    /* 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 );

Window data of window manager(mWmngWind) is set up.

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

Instance of base widget is created.

In PPBrowser, chrome is created here. But in marbrow, because chrome is created for each browser, the process is shift to browser class constructor.

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

Child control array of mWmngWind is iterated and each objects are created. At this point, constructors of each control classes are called.

    /* 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;
        }
    }

Base widget is created.

    /* 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);

Again child control array of mWmngWind is iterated and FinishCeateSelf() is sent to each object.

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

Finally, FinishCeateSelf() is sent to itself.

    /* BrowserWindow FinishCreateSelf */
    FinishCreateSelf();

Window is shown and initial focus is set up.

    /* 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() - Final process after construction of all hierarchy

Window is enabled and shown.

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

    Inherited::FinishCreateSelf();
}

MWind::~MWind() - Destructor

Destructor of MWind class disposes of all child controls and itself.

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 );
}

Event

Because MWind object holds objects of child controls such as browser, button etc, it has the responsibility of informing these control objects of events.

Basis of each event processes in MWind class is generalized like below.

  • If window itself needs to process the event, it is executed actually.
  • If there are contols that must process the event, event is informed to all of those controls.

MWind::DrawSelf() - Drawing process

Drawing process of window. DrawSelf() is sent to all control objects intersecting with visRgn.

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() - Shown/Not shown

Window is shown/hide. Base widget of mozilla is requested the process.

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

MWind::EnableSelf() - Enabled/Disabled

Window is enabled/disabled. Only a flag is set by MView's method.

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

MWind::ActivateSelf() - Activated/Inactivated

Window is activated/inactivated. Lower control objects are informed of the status.

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() - Absolute resize

MWind::ResizeFrameBy() - Relative resize

MWind::MoveTo() - Absolute move

MWind::MoveBy() - Relative move

MWind::AdjustCntlFrame() - Control frame adjustment

Actually window moving is achieved by DragWindow() of Macintosh. Therefore, only new coordinates are assigned in 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 );
}

After informing base widget of resize, both kind of resize procedure send resize to all child controls by AdjustCntlFrame() common procedure.

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() requests move or resize to each controls, taking account of their bind settings.

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() - Click process

This searches for clicked control and sends ClickSelf() to it. In marbrow, controls except browser are made of Macintosh Control manager, so they can be searched by FindControlUnderMouse(). Only browser is searched in manual coding.

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() - Key press process

This sends HandleKeyPress() to controls owning focus.

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() - Idle process

MWind::DoRepeat() - Repeat process

These sends DoIdle(), DoRepeat() to all controls.

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() - Mouse move process

Mouse move process. This sends HandleMouseMove() to control pointed by mouse pointer. When mouse is being pressed, this keeps sending to pressed control.

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() - Command process

After requesting menu process to control owning focus, this executes menu process of which window is in charge.

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;
}

Browser is requested to process Character set and Auto detect.

Also Browser is requested to process Bookmark addition and Jump to Bookmark.

Accessor

Accessor used by browser class and chrome class.

MWind::GetWidget() - Getting base widget

This returns base widget of mozilla that corresponds to window.

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

    *aWidget = mWindow;
    NS_IF_ADDREF(*aWidget);

    return NS_OK;
}

This is a kind of so called "getter function" in XPCOM, and AddRef()s interface pointer in it and returns it.

If the caller wants to get interface pointer in nsCOMPtr,

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

Thus processed. Thanks to nsCOMPtr, the interface pointer is automatically Release()d at the end of the scope of aWidget.

MWind::GetTitle() - Getting title

MWind::SetTitle() - Setting up title

In ordinary Macintosh way, this sets up/gets title of window.

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 );
}


Browser class (MBrow)

Class definition

In class definition of MBrow, as mozilla related member variables, there are a couple of interface pointers to the WebBrowser. And, there is massage sink as a static member, by way of which events are sent to mozilla.

WMNG_WIND *mWmngWind and WMNG_CNTL *mWmngCntl are pointers to the management data of parent WMNG_WIND and of this WMNG_BROW respectively, which are managed by window manager.

Initial URL character string is kept in window manager's WMNG_BROW class, that describes the data of browser.

In PPBrowser, a pointer to chrome object is owned by window class, but in marbrow, this brwser class owns it. The chrome class includes processes for each window and for each browser. Because marbrow supports multiple browser per one window, chrome instance is created in one-to-one correspondednce to browser instance and associated with each browser.

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;
};

yMBrow.cppz

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

Event process of mozilla exists in widget module. In case of embedding, every kind of event received by framework is sent to mozilla by way of nsMacMessageSink's mMessageSink.DispatchOSEvent().

Creation, Disposal

MBrow::MBrow() - Constructor

Constructor of MBrow class creates the chrome and associates it with himself, creates browser instance.

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;
}

In marbrow, a chrome instance is created and associated with, in one-to-one basis to each browser instances.

Browser is a instance of nsWebBrowser class. Its source code exists in embedding module. nsWebBrowser is instanciated by do_CreateInstance(NS_WEBBROWSER_CONTRACTID, &rv); and returns its typical interface, nsIWebBrower. And this is housed in mWebBrowser member.

In case that you manipulate nsWebBrowser, all you have to keep is this nsIWebBrower mWebBrowser and that's enough, and you can get whatever interfaces you want by QI()ing via nsIWebBrowser whenever other interfaces are necessary. But in this procedure, nsIBaseWindow, nsIWebNavigation are gotten and kept in member variables in this timing, 'cause afterwards they are referenced very often.

MBrow::FinishCreateSelf() - Final process after construction of all hierarchy

Mozilla specific process in this procedure is to initialize geometry of nsWebBrowser(position, size, etc). By InitWindow() and Create() of nsIBaseWindow mWebBrowserAsBaseWin, that is the interface associated with the geometry, initialization is done anyway. Then, by AdjustFrame() common method, the position and size are arranged precisely.

And, by SetTopLevelWindow(), this initialize and set up chrome.

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 );
    }
}

After enabling and showing itself, this load initial URL finally.

MBrow::SetTopLevelWindow() - Initializing and setup of browser chrome

Called by MBrow::FinishCreateSelf(). Last stage of initializing browser. By calling nsIWebBrowser::SetContainerWindow(), nsWebBrowser recognizes MChrome object as a chrome.

And, as the code comment says, if you want to initialize nsIDocShell related matters, it can be done here also.

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;
}

Chrome of marbrow implements nsIURIContentListener interface, in addition to the interfaces PPBrowser implements. By nsIWebBrowser::SetParentURIContentListener(), nsWebBrowser recognizes our chrome's nsIURIContentListener.

MBrow::~MBrow() - Destructor

Destructor of MBrow class stops net process, cut off association between browser shell and chrome and dispose of chrome.

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

    SetTopLevelWindow( nsnull );

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

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

Webshell

MBrow::GetWebBrowser() - Browser getter

MBrow::SetWebBrowser() - Browser setter

Method mainly called by chrome. Getter(to get) and setter(to set up) for browser(nsIWebBrowser) used by chrome class.

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() - Getting title

MBrow::SetTitle() - Setting up title

In marbrow, because of multiple browser on one window, title information from chrome is kept by each browser and title bar of the main window shows last informed one.

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 );
}

Event

Event process of MBrow object processes basically by dispatching event to mozilla through nsMacMessageSink.

MBrow::DrawSelf() - Drawing process

MBrow::ShowSelf() - Shown/Not shown

MBrow::EnableSelf() - Enabled/Disabled

MBrow::ActivateSelf() - Activated/Inactivated

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() - Absolute resize

MBrow::ResizeFrameBy() - Relative resize

MBrow::MoveBy() - Relative move

MBrow::AdjustFrame() - Frame adjustment

This processes move and resize of browser. After all, this sets up it by 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() - Click process

MBrow::EventMouseUp() - Mouse up process

MBrow::HandleKeyPress() - Key press process

MBrow::DoIdle() - Idle process

MBrow::HandleMouseMoved() - Mouse move common process

This uses nsMacMessageSink and sends event by 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 );
}

Others go the same way, and omitted.

MBrow::ObeyCommand() - Command process

This does clipboard process such as copy, paste etc. Process is equal to what is of 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;
}

Operation

MBrow::LoadURL() - URL load

By <nsIWebNavigation::LoadURI(), this loads 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() - View source

This creates plain window and does SetViewMode() to DocShell of its browser object.

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() - Print process

This traces interface as nsIDocShell -> nsIContentViewer -> nsIContentViewerFile and calls 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() - Setting up character string of location bar

MBrow::SetStatCntlText() - Setting up character string of status bar

This seaches for location bar and status bar associated with itself and requests these to set up character string.

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 );
    }
}

Navigation

MBrow::OnNetStart() - Starting net process

MBrow::OnNetStop() - Terminating net process

MBrow::OnProgressChange() - Progress change

This reflects condition of net process in each control.

In PPBrowser, these are in window class, but because these are status of each browser essentially, should be in browser class.

In marbrow, progress bar is not supported and progress change is empty.

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() - Able to go back ?

MBrow::CanGoForward() - Able to go forward ?

MBrow::Back() - Back process

MBrow::Forward() - Forward process

MBrow::Stop() - Stop process

MBrow::Refresh() - Refresh process

MBrow::GetCurrentURL() - Getting current URL

This requests all the actual processes to nsIWebNavigation interface.

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;
        }
    }
}

Character set, Auto detect process

Bookmark process

These are omitted here. Please see the source code for detail.


Chrome class (MChrome)

Chrome class of marbrow (MChrome) is nearly equal to the one of PPBrowser(CWebBrowserChrome), but there are 2 large differences.

First - Creation of chrome instance

Chrome class of PPBrowser is created for each window object by the constructor of window class. But chrome class of marbrow is created for each browser object by the constructor of browser class.

Chrome class includes processes for both each window object and each browser object. Because marbrow supports multiple browser on one window, this construction is essential.

Second - nsIURIContentListener interface

In addition to interfaces that PPBrowser has in chrome, marbrow has addtionally nsIURIContentListener interface.

This nsIURIContentListener interface makes OnStartURIOpen() method in the said interface called on starting URL open. This method makes it possible to intercept process according to scheme, target name and URL.

When scheme is mailto: or news:, marbrow intercepts process and requests default mail client set up in InternetConfig to process.

Class definition

Difference between chrome class of PPBrowser and of marbrow is only that marbrow has nsIURIContentListener interface additionally. Except this, the two are quite equal.

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;
};

yMChrome.cppz

/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
/* 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 interface

Below I show only nsIURIContentListener interface that marbrow has additionally.

Other interfaces are omitted, for they are equal to those of PPBrowser except syntactic difference, results from differences of class name or of the framework.

MChrome::OnStartURIOpen() - On starting URL open

When scheme is mailto: or news:, this starts default mail client user sets up in InternetConfig, requests him to load URL, and makes process of mozilla aborted.

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;
}

Here I do no special, but if this method is implemented, according to the conditions, for example ...

  • the case that scheme is javascript:
  • the case that scheme is http: and new window will be opened

you are able to do something special, whatever you want.

Other methods of nsIURIContentListener

For other methods, default processes are enough. So, basically, processes are thrown to the implementation of the default chrome's corresponding interfaces, that is held by 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;
}


File download by netlib

Here I download file of specified URL by using netlib directly. This is an example of low level usage of mozilla module.

DnLoadFile() - File download process

I download the file on URL specified by argument, using netlib, and save it as a file which owes FSSpec, creator, file type, specified by argument.

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

From C character string of URL received by argument, nsIURI object is created, that is the generic mozilla representation of the URL.

    /* 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;
    }

By passing this nsIURI object, channel to actual downloader is opened.

    /* 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;
    }

Listener object (DnloadListener) is created. This object does execute the reception of data, according to the progress of netlib.

    /* New Listener */
    DnloadListener *listener = new DnloadListener( url,
        spec, creator, fileType );
    NS_ENSURE_TRUE( listener, NS_ERROR_FAILURE);
    NS_IF_ADDREF( listener );

To netlib channel, this passes listener object and requests to start asynchronous reading.

    /* 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;
}

Class definition of DnloadListener

Class definition of DnloadListener.

This inherits from nsIStreamObserver and nsIStreamListener interfaces and has methods that is

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

In member variables, this has character string of URL to download, FSSpec, creator and file type of saved file, pointer to progress condition dialog, various kinds of progress condition management values, pointer to dummy widget and so on.

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 - Constructor

Main process of this part is creation of dummy widget.

Asynchronous download condition is informed through event sink of mozilla. Event sink is processed by mozilla widget.

This download can start even if browser window is not shown (that is, mozilla widget doesn't exist). So, to keep event sink's wheel running, dummy widget is necessary and created.

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 - Destructor

Destructor disposes of dummy widget.

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 - On starting request

For the display of pogress condition, total size of the file is gotten. Progress condition dialog is created. Save file is created and opened.

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 - On stopping request

File is closed and dialog is dispoed of.

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 - On arrival of data

This reads data already arrived from the channel's stream and writes them on file. And, dialog is updated.

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.
Japanese