emailuis/emailui/src/FreestyleEmailUiHtmlViewerContainer.cpp
changeset 2 5253a20d2a1e
parent 1 12c456ceeff2
child 3 a4d6f1ea0416
--- a/emailuis/emailui/src/FreestyleEmailUiHtmlViewerContainer.cpp	Thu Jan 07 12:38:38 2010 +0200
+++ b/emailuis/emailui/src/FreestyleEmailUiHtmlViewerContainer.cpp	Mon Jan 18 20:08:26 2010 +0200
@@ -21,13 +21,13 @@
 #include <coemain.h>
 #include <commdbconnpref.h>
 #include <bautils.h>
-
+#include <biditext.h>
 //<cmail>
 #include "CFSMailMessage.h"
 //</cmail>
 #include <brctlinterface.h>
 #include <sysutil.h>
-
+#include <SchemeHandler.h>
 
 #include "FreestyleEmailUiAppui.h"
 #include "FreestyleEmailUiUtilities.h"
@@ -47,10 +47,10 @@
 _LIT( KHeaderHtmlFile, "header.html" );
 _LIT( KBodyHtmlFile, "body.html" );
 _LIT( KMessageHtmlFile, "email.html" );
+_LIT( KMessageHtmlRTLFile, "email_rtl.html" );
 _LIT( KZDrive, "z:" );
 _LIT( KHtmlFlagFile, "html.flag" );
-
-// <cmail>
+_LIT( KURLSlash, "/");
 // Constants used in html content modification
 const TInt KMaxCharsToSearch( 200 );
 _LIT8( KStartTag, "<html" );
@@ -64,7 +64,32 @@
 _LIT8( KHTMLEmptyContent, "<HTML><BODY></BODY></HTML>");
 _LIT( KHTMLDataScheme, "data:0");
 
-// </cmail>
+
+_LIT8( KHtmlLineBreak, "<br>" );
+_LIT8( KHtmlLineBreakCRLF, "<br>\xD\xA" );
+_LIT8( KHtmlLessThan, "&lt;" );
+_LIT8( KHtmlGreaterThan, "&gt;" );
+_LIT8( KHtmlAmpersand, "&amp;" );
+_LIT8( KHtmlQuotation, "&quot;" );
+_LIT8( KHtmlLinkTag, "<a href=\"%S\">" );
+_LIT8( KHtmlLinkEndTag, "</a>" );
+_LIT( KURLTypeBody, "body");
+
+const TText8 KGreaterThan = 0x3e;
+const TText8 KLessThan = 0x3c;
+const TText8 KAmpersand = 0x26;
+const TText8 KQuotation = 0x22;
+const TText8 KCharacterSpace = 0x20;
+const TText8 KSOH = 0x01; // Start Of Heading
+const TText8 KCR = 0x0d; // Carriage Return
+const TText8 KLF = 0x0a; // Line Feed
+const TText8 KHT = 0x09; // Horizontal Tab
+const TReal KOverlayButtonMarginX = 0.01; // 1%
+const TReal KOverlayButtonMarginY = 0.01; // 1%
+const TReal KOverlayButtonSizeP = 0.15; // 15%
+const TReal KOverlayButtonSizeLs = 0.20; // 25%
+const TReal KOverlayButtonPosP = 0.88;
+const TReal KOverlayButtonPosLs = 0.85;
 
 // ---------------------------------------------------------------------------
 // Two-phased constructor.
@@ -115,6 +140,9 @@
     delete iBrCtlInterface;
     iConnection.Close();
     iSocketServer.Close();
+    
+    delete iOverlayControlNext;
+    delete iOverlayControlPrev;
     }
 
 // <cmail> Because of browser changes, followings must be performed before iAppUi.exit()
@@ -145,8 +173,7 @@
     EnsureHTMLResourceL();
     
     CreateWindowL();
-    SetRect( iAppUi.ClientRect() );
-
+    SetRect( iView.ContainerRect() );
 
     TUint brCtlCapabilities = TBrCtlDefs::ECapabilityClientResolveEmbeddedURL |
                               TBrCtlDefs::ECapabilityDisplayScrollBar |
@@ -167,9 +194,102 @@
     iBrCtlInterface->SetBrowserSettingL( TBrCtlDefs::ESettingsFontSize, TBrCtlDefs::EFontSizeLevelLarger );
 
     iEventHandler = CFreestyleMessageHeaderURLEventHandler::NewL( iAppUi, iView );
+    
+    TRect nextButtonRect = OverlayButtonRect( EFalse );
+    iOverlayControlNext = COverlayControl::NewL( this, this, nextButtonRect, 
+            EMbmFreestyleemailuiQgn_indi_cmail_arrow_next, 
+            EMbmFreestyleemailuiQgn_indi_cmail_arrow_next_mask );
+
+    TRect prevButtonRect = OverlayButtonRect( ETrue );
+    iOverlayControlPrev = COverlayControl::NewL( this, this, prevButtonRect,
+            EMbmFreestyleemailuiQgn_indi_cmail_arrow_previous,
+            EMbmFreestyleemailuiQgn_indi_cmail_arrow_previous_mask );
+    iScrollPosition = 0;
     ActivateL();
     }
 
+void CFsEmailUiHtmlViewerContainer::MakeVisible( TBool aVisible )
+    {
+    UpdateOverlayButtons( aVisible );
+    CCoeControl::MakeVisible( aVisible );
+    }
+
+void CFsEmailUiHtmlViewerContainer::HandleOverlayPointerEventL( COverlayControl* aControl, 
+        const TPointerEvent& aEvent )
+    {
+    if( aEvent.iType == TPointerEvent::EButton1Up )
+        {
+        if( aControl == iOverlayControlNext )
+            {
+            iView.HandleCommandL( EFsEmailUiCmdNextMessage );
+            }
+        else if( aControl == iOverlayControlPrev )
+            {
+            iView.HandleCommandL( EFsEmailUiCmdPreviousMessage );
+            }
+        }
+    }
+
+void CFsEmailUiHtmlViewerContainer::UpdateOverlayButtons( TBool aVisible )
+    {
+    TBool nextAvailable = EFalse;
+    TBool prevAvailable = EFalse;
+    if( iMessage )
+        {
+        TFSMailMsgId currentMsgId = iMessage->GetMessageId();
+        TFSMailMsgId tmpMsgId;
+        TFSMailMsgId tmpMsgFolderId;
+        nextAvailable = iAppUi.IsNextMsgAvailable( currentMsgId, tmpMsgId, tmpMsgFolderId );
+        prevAvailable = iAppUi.IsPreviousMsgAvailable( currentMsgId, tmpMsgId, tmpMsgFolderId );
+        }
+    
+    if( iOverlayControlPrev )
+        {
+        iOverlayControlPrev->SetRect( OverlayButtonRect( ETrue ) );
+        iOverlayControlPrev->MakeVisible( aVisible && prevAvailable );
+        }
+    if( iOverlayControlNext )
+        {
+        iOverlayControlNext->SetRect( OverlayButtonRect( EFalse ) );
+        iOverlayControlNext->MakeVisible( aVisible && nextAvailable );
+        }
+    }
+
+// Get rect for button
+TRect CFsEmailUiHtmlViewerContainer::OverlayButtonRect( TBool aLeft )
+    {
+    TRect rect = Rect();
+    TSize size = rect.Size();
+    
+    TBool landscape = size.iWidth > size.iHeight;
+    TInt buttonSize;
+
+    if( landscape )
+        {
+        buttonSize = size.iHeight * KOverlayButtonSizeLs;
+        }
+    else
+        {
+        buttonSize = size.iWidth * KOverlayButtonSizeP;
+        }
+
+    rect.iBr.iY = size.iHeight * (1-KOverlayButtonMarginY);
+    
+    if( aLeft )
+        {
+        rect.iTl.iX = size.iWidth * KOverlayButtonMarginX;
+        rect.iBr.iX = rect.iTl.iX + buttonSize;
+        }
+    else
+        {
+        rect.iBr.iX = size.iWidth * (1 - KOverlayButtonMarginX);
+        rect.iTl.iX = rect.iBr.iX - buttonSize;
+        }
+    
+    rect.iTl.iY = rect.iBr.iY - buttonSize;
+    return rect;
+    }
+
 // Getter for br contro, interface
 CBrCtlInterface* CFsEmailUiHtmlViewerContainer::BrowserControlIf()
     {
@@ -200,11 +320,14 @@
 // ---------------------------------------------------------------------------
 //
 void CFsEmailUiHtmlViewerContainer::LoadContentFromMailMessageL(
-    CFSMailMessage* aMailMessage )
+    CFSMailMessage* aMailMessage, TBool aResetScrollPos )
     {
     FUNC_LOG;
     ASSERT( aMailMessage );
     iMessage = aMailMessage;
+    // Cancel any browser fetch operation, just in case the browser is still
+    // loading a previous message (since we are about to overwrite it).
+    CancelFetch();
     
     TPath headerHtmlFile;
     headerHtmlFile.Copy( iHtmlFolderPath );
@@ -213,7 +336,15 @@
     // insert email header into email.html file
     // CFreestyleMessageHeaderHTML will replace contents of email.html
     // So, no need to clear the contents
-    CFreestyleMessageHeaderHTML::ExportL( *iMessage, iFs, headerHtmlFile, iAppUi.ClientRect().Width() );
+    TLanguage language = User::Language();
+    TBidiText::TDirectionality direction = TBidiText::ScriptDirectionality( language );    
+    if(aResetScrollPos)
+        {
+        iScrollPosition = 0; 
+        }
+    CFreestyleMessageHeaderHTML::ExportL( *iMessage, iFs, headerHtmlFile, iAppUi.ClientRect().Width(), 
+                                            iScrollPosition,
+                                            direction);
     
     // Remove all previously created files from temporary HTML folder
     EmptyTempHtmlFolderL();
@@ -262,16 +393,24 @@
             CleanupStack::PopAndDestroy( contentBuffer );
             CleanupStack::PopAndDestroy( textBodyPart ); 
             }
-
         }
     // pass the emailHtmlFile to the browser for it to load
     if ( bodyPartAvailable )
         {
         TPath emailHtmlFile;
         emailHtmlFile.Copy( iHtmlFolderPath );
-        emailHtmlFile.Append( KMessageHtmlFile );
+        if ( direction == TBidiText::ELeftToRight )
+            {
+            emailHtmlFile.Append( KMessageHtmlFile );
+            }
+        else
+            {
+            emailHtmlFile.Append( KMessageHtmlRTLFile );
+            }
         LoadContentFromFileL( emailHtmlFile );
         }
+    
+    UpdateOverlayButtons( ETrue );
     }
 
 // ---------------------------------------------------------------------------
@@ -347,11 +486,30 @@
 void CFsEmailUiHtmlViewerContainer::SizeChanged()
     {
     FUNC_LOG;
+
+    TRect rect = Rect();
     if ( iBrCtlInterface )
         {
-        TRect rect = Rect();
         iBrCtlInterface->SetRect( rect );
         }
+    
+        if ( iMessage )
+            {
+            // update the width in header part and reload
+            TPath headerHtmlFile;
+            headerHtmlFile.Copy( iHtmlFolderPath );
+            headerHtmlFile.Append( KHeaderHtmlFile );
+                
+            TRAP_IGNORE( CFreestyleMessageHeaderHTML::ExportL( *iMessage, iFs, headerHtmlFile, 
+                  rect.Width(), iScrollPosition ) )
+                
+            TPath emailHtmlFile;
+            emailHtmlFile.Copy( iHtmlFolderPath );
+            emailHtmlFile.Append( KMessageHtmlFile );
+            TRAP_IGNORE( LoadContentFromFileL( emailHtmlFile ) )
+            }
+    
+    UpdateOverlayButtons( IsVisible() );
     }
 
 // ---------------------------------------------------------------------------
@@ -490,6 +648,8 @@
         if ( iMessage )
             {
             LoadContentFromMailMessageL( iMessage );
+            
+            UpdateOverlayButtons( ETrue );
             }
         }
     else if ( aEvent.iProgressStatus == TFSProgress::EFSStatus_RequestCancelled ||
@@ -554,7 +714,23 @@
     const TDesC& /*aCurrentUrl*/, MBrCtlLinkContent& /*aBrCtlLinkContent*/ )
     {
     FUNC_LOG;
-    return iEventHandler->HandleEventL( aUrl );
+    if (IsMessageBodyURL(aUrl))
+        {
+        iView.StartFetchingMessageL();
+        return ETrue;
+        }
+    else
+        {
+        if ( NeedToLaunchBrowserL( aUrl ) )
+            {
+            LaunchBrowserL( aUrl );
+            return ETrue;
+            }
+        else
+            {
+            return iEventHandler->HandleEventL( aUrl );
+            }     
+        }
     }
 
 // ---------------------------------------------------------------------------
@@ -987,9 +1163,11 @@
 //
 // <cmail>
 void CFsEmailUiHtmlViewerContainer::ConvertToHTML( const TDesC8& aContent,
-    const TDesC& aFileName, CFSMailMessagePart& aHtmlBodyPart )
+    const TDesC& aFileName, CFSMailMessagePart& aTextBodyPart )
     {
     FUNC_LOG;
+    const TInt KBodyTextChunkSizeBytes = 1024;
+    
     if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, aContent.Size(), EDriveC ) )
          {
          // Can not write the data, there's not enough free space on disk.
@@ -997,49 +1175,334 @@
         }
     else
         {
+        CBufBase* bodyBuf = CBufSeg::NewL( KBodyTextChunkSizeBytes );
+        CleanupStack::PushL( bodyBuf );
+        
+        bodyBuf->InsertL( 0, aContent );
+        
+        TInt position( 0 );
+        TText8 previous = 0;
+        TBool EndOfString( EFalse );
+        
+        while ( !EndOfString )
+            {
+            TInt startPosition = position;
+            TPtr8 segment( bodyBuf->Ptr( startPosition ) );
+            int i = 0;
+            
+            while(i < segment.Length())
+                {
+                TInt currentPos = position + i;
+                TText8 ch = segment[i];
+                
+                switch( ch )
+                    {
+                    case KSOH:  // end of line for IMAP and POP
+                        bodyBuf->Delete( currentPos, 1 );
+                        bodyBuf->InsertL( currentPos, KHtmlLineBreakCRLF );
+                        i += KHtmlLineBreakCRLF().Length();
+                        segment.Set( bodyBuf->Ptr( startPosition ) );
+                        break;
+                    case KLF: // line feed
+                        if ( previous == KCR )
+                            {
+                            bodyBuf->InsertL( currentPos, KHtmlLineBreak );
+                            i += KHtmlLineBreak().Length();
+                            segment.Set( bodyBuf->Ptr( startPosition ) );
+                            }
+                        else
+                            {
+                            i++;
+                            }
+                        break;  
+                    case KQuotation:
+                        bodyBuf->Delete( currentPos, 1 );
+                        bodyBuf->InsertL( currentPos, KHtmlQuotation );
+                        i += KHtmlQuotation().Length();
+                        segment.Set( bodyBuf->Ptr( startPosition ) );
+                        break;
+                    case KAmpersand:
+                        bodyBuf->Delete( currentPos, 1 );
+                        bodyBuf->InsertL( currentPos, KHtmlAmpersand );
+                        i += KHtmlAmpersand().Length();
+                        segment.Set( bodyBuf->Ptr( startPosition ) );
+                        break;
+                    case KGreaterThan:
+                        bodyBuf->Delete( currentPos, 1 );
+                        bodyBuf->InsertL( currentPos, KHtmlGreaterThan );
+                        i += KHtmlGreaterThan().Length();
+                        segment.Set( bodyBuf->Ptr( startPosition ) );
+                        break;
+                    case KLessThan:
+                        bodyBuf->Delete( currentPos, 1 );
+                        bodyBuf->InsertL( currentPos, KHtmlLessThan );
+                        i += KHtmlLessThan().Length();
+                        segment.Set( bodyBuf->Ptr( startPosition ) );
+                        break;
+                    default:
+                        i++;
+                        break;
+                    }
+                previous = ch;
+                }
+            position += segment.Length();
+            if ( ( bodyBuf->Size() - position ) <= 0 )
+                {
+                EndOfString = ETrue;
+                }
+            }
+        
+        CreateHyperlinksFromUrlsL( *bodyBuf );
+        
         RFile targetFile;
         CleanupClosePushL( targetFile );
-
         User::LeaveIfError( targetFile.Replace( iFs, aFileName, EFileWrite ) );
         
-        HBufC8* charSet = GetCharacterSetL( aHtmlBodyPart );
+        HBufC8* charSet = GetCharacterSetL( aTextBodyPart );
         CleanupStack::PushL( charSet );
         
-        User::LeaveIfError( targetFile.Write( KHtmlHeader1 ) );
-        User::LeaveIfError( targetFile.Write( *charSet ) );
-        User::LeaveIfError( targetFile.Write( KHtmlHeader2 ) );
+        RBuf8 messageHeader;
+        TInt bufSize = KHtmlHeader1().Length() + charSet->Length() + KHtmlHeader2().Length();
+        messageHeader.CreateL( bufSize );
+        messageHeader.CleanupClosePushL();
+        
+        messageHeader.Append( KHtmlHeader1 );
+        messageHeader.Append( *charSet );
+        messageHeader.Append( KHtmlHeader2 );
+        
+        RFileWriteStream fileStream( targetFile );
+        fileStream.PushL();
+        fileStream.WriteL( messageHeader.Ptr(), messageHeader.Length() );
+        
+        TInt bufPos( 0 );
+        TInt bufTotalSize = bodyBuf->Size();
+        
+        while ( bufPos < bufTotalSize )
+            {
+            TInt segmentLength = bodyBuf->Ptr( bufPos ).Length();
+            fileStream.WriteL( bodyBuf->Ptr( bufPos ).Ptr(), segmentLength );
+            bufPos += segmentLength;
+            }
+
+        fileStream.CommitL();
+        
+        CleanupStack::PopAndDestroy( &fileStream );
+        CleanupStack::PopAndDestroy( &messageHeader );
         CleanupStack::PopAndDestroy( charSet );
-
+        CleanupStack::PopAndDestroy( &targetFile );
+        CleanupStack::PopAndDestroy( bodyBuf );
 
-        // Write the original content
-        for(int i=0;i<aContent.Length();i++)
-            {
-          if( i==aContent.Length()-1 )
-            {
-            aContent.Mid( i,1 ).CompareC( _L8("\x01" ) )==0 ? 
-            User::LeaveIfError( targetFile.Write( _L8("<br>") ) ):
-            User::LeaveIfError( targetFile.Write( aContent.Mid(i,1) ) );
+// </cmail>
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Finds and html formats hyperlinks in a document
+// ---------------------------------------------------------------------------
+//
+// <cmail>
+void CFsEmailUiHtmlViewerContainer::CreateHyperlinksFromUrlsL( CBufBase& aSource )
+    {
+    FUNC_LOG;
+    const TInt urlMaxLength = 2048;
+    _LIT8( KHttp, "http://" );
+    _LIT8( KHttps, "https://");
+    _LIT8( KWww, "www."); 
+    
+    TBool eos( EFalse );
+    TInt position( 0 );
+    TInt carryOverInc( 0 );
+    
+    while ( !eos )
+        {
+        while ( carryOverInc >= aSource.Ptr( position ).Length() )
+            { // Skip segments of overlapping url string
+            carryOverInc -= aSource.Ptr( position ).Length();
+            position += aSource.Ptr( position ).Length();
             }
-          else
+        
+        TPtr8 segment( aSource.Ptr( position ) );
+        TLex8 lexSegment( segment );
+        lexSegment.Inc( carryOverInc );
+        carryOverInc = 0;
+        
+        while (!lexSegment.Eos())
             {
-            if(aContent.Mid( i,1 ).CompareC( _L8("\x01" ) )==0 ||
-            aContent.Mid( i,2 ).CompareC( _L8("\x0D\x0A" ) )==0 ) 
-              {
-              User::LeaveIfError( targetFile.Write( _L8("<br>") ));
-              }
-            else
-              {
-          User::LeaveIfError( targetFile.Write( aContent.Mid(i,1) ) );                        
+            TPtrC8 nextToken( lexSegment.NextToken() );
+            TInt foundAt( KErrNotFound );
+            
+            // Find HTTP, HTTPS, or WWW link in CBufSeg segment of size 1024 bytes.
+            if ( ( ( foundAt = nextToken.FindC( KHttp ) ) != KErrNotFound ) ||
+                    (  ( foundAt = nextToken.FindC( KHttps ) ) != KErrNotFound ) ||
+                    ( ( foundAt = nextToken.FindC( KWww ) ) != KErrNotFound ) )
+                {
+                if ( !lexSegment.Eos() )
+                    { 
+                    if ( !foundAt )
+                        { // Token starts with http/https/www.x
+                        TPtrC8 url;
+                        TInt lineBreakPos( KErrNotFound );
+                        if ( ( lineBreakPos = nextToken.FindC( KHtmlLineBreak ) ) != KErrNotFound )
+                            { // Token contains html line break -> remove
+                            url.Set( nextToken.Left( lineBreakPos ) );
+                            }
+                        else
+                            {
+                            url.Set( nextToken );
+                            }
+                        
+                        if ( url.CompareC( KWww ) != KErrNone ) // if token=www., validate format 
+                            {                                   // www.x
+                            RBuf8 urlBuf;
+                            urlBuf.CreateL( KHtmlLinkTag().Length() + url.Length() * 2
+                                    + KHtmlLinkEndTag().Length() + KHtmlLineBreak().Length() );
+                            urlBuf.CleanupClosePushL();
+                            // Format html link
+                            urlBuf.AppendFormat( KHtmlLinkTag, &url );
+                            urlBuf.Append( url );
+                            urlBuf.Append( KHtmlLinkEndTag );
+                            if ( lineBreakPos != KErrNotFound )
+                                { // Add line break if removed earlier
+                                urlBuf.Append( KHtmlLineBreak );
+                                }
+                            //Test
+                            TInt nextTokenLength = nextToken.Length();
+                            TInt segOffset = lexSegment.Offset();
+                            TInt urlLength = urlBuf.Length();
+                            
+                            //Test
+                            TInt offset = lexSegment.Offset() - nextToken.Length();
+                            TLexMark8 tokenMark;
+                            // Move next character last token back
+                            lexSegment.Inc( - nextToken.Length() );
+                            lexSegment.Mark( tokenMark );
+                            aSource.Delete( offset + position, nextToken.Length() );
+                            aSource.InsertL( offset + position, urlBuf );
+                            segment.Set( aSource.Ptr( position ) );
+                            lexSegment.Assign( segment );
+                            // Set next character to the position of inserted hyperlink
+                            lexSegment.UnGetToMark( tokenMark );
+                            
+                            // If Max segment length is reached, set carry over value to 
+                            // properly set next character in following CBufSeg segment
+                            if ( ( offset + urlBuf.Length() ) >= segment.Length() )
+                                {
+                                carryOverInc = offset + urlBuf.Length() - segment.Length();
+                                while ( !lexSegment.Eos() )
+                                    { // Set to segment's end
+                                    lexSegment.NextToken(); 
+                                    }
+                                }
+                            else
+                                {
+                                lexSegment.Inc( urlBuf.Length() );
+                                }
+                            
+                            CleanupStack::PopAndDestroy( &urlBuf );
+                            }
+                        }
+                    }
+                else
+                    // Next token is end of string, here we handle the last token of a segment
+                    {
+                    _LIT8( KUrlEnd, "<" );
+                    
+                    TInt endOfUrlPos( KErrNotFound );
+                    TText8 ch = segment[ segment.Length() - 1];
+                    RBuf8 url;
+                    url.CreateL( urlMaxLength );
+                    url.CleanupClosePushL();
+                    
+                    // Find if hyperlink ends within this segment boundaries
+                    if ( ch == KSOH || ch == KCR || ch == KLF || ch == KHT || ch == KCharacterSpace )
+                        {
+                        endOfUrlPos = nextToken.Length() - 1;
+                        }
+                    else if ( ( endOfUrlPos = nextToken.Right( KHtmlLineBreak().Length() ).Find( KUrlEnd ) ) != KErrNotFound )
+                        {
+                        endOfUrlPos = nextToken.Length() - KHtmlLineBreak().Length() + endOfUrlPos;
+                        }
+                    else
+                        { // Handle hyperlink spread in multiple segments
+                        TInt nextPos = position;
+                        TPtrC8 nextSegment( aSource.Ptr( nextPos ) );
+                        TLex8 lexNextSegment( nextSegment );
+                        TPtrC8 nextNextToken( nextToken );
+                        TBool firstPass( ETrue );
+                        
+                        while ( endOfUrlPos == KErrNotFound || nextPos >= aSource.Size() )
+                            {
+                            if ( ( url.Length() + nextNextToken.Length() )  > urlMaxLength )
+                                { // URL exceeds limit of 2K, do nothing
+                                break;
+                                }
+                            
+                            url.Append( nextNextToken );
+                            if ( ( nextSegment.Length() == nextNextToken.Length() ) || firstPass )
+                                { // Token takes up the whole segment, or first pass( first segment 
+                                  // with last token where hyperlink does not end within segment's
+                                  // boundaries, move to next segment
+                                nextPos += nextSegment.Length();
+                                nextSegment.Set( aSource.Ptr( nextPos ) );
+                                lexNextSegment.Assign( nextSegment );
+                                nextNextToken.Set( lexNextSegment.NextToken() );
+                                if ( firstPass )
+                                    { 
+                                    firstPass =  EFalse;
+                                    }
+                                }
+                            else
+                                { // Last segment's token with hyperlink's end
+                                if ( ( endOfUrlPos = url.Find( KHtmlLineBreak ) ) != KErrNotFound )
+                                    { // Remove line break
+                                    url.Delete( endOfUrlPos, KHtmlLineBreak().Length() );
+                                    endOfUrlPos = nextNextToken.Length() - KHtmlLineBreak().Length();
+                                    }
+                                else
+                                    {
+                                    endOfUrlPos = nextNextToken.Length();
+                                    }  
+                                }
+                            }
+ 
+                        if ( endOfUrlPos != KErrNotFound )
+                            { // Handle hyperlink that is within 2K limit
+                            RBuf8 urlBuf;
+                            urlBuf.CreateL( KHtmlLinkTag().Length() + url.Length() * 2
+                                    + KHtmlLinkEndTag().Length() + KHtmlLineBreak().Length() );
+                            urlBuf.CleanupClosePushL();
+                            // Format html link
+                            urlBuf.AppendFormat( KHtmlLinkTag, &url );
+                            urlBuf.Append( url );
+                            urlBuf.Append( KHtmlLinkEndTag );
+                            urlBuf.Append( KHtmlLineBreak );
+                            
+                            TInt offset = lexSegment.Offset() - nextToken.Length();
+                            // Remove hyperlink from the original message body
+                            aSource.Delete( offset + position, url.Length() );
+                            // Insert html formated hyperlink
+                            aSource.InsertL( offset + position, urlBuf );
+                            segment.Set( aSource.Ptr( position ) );
+                            
+                            // Set carry on value to mark where new token should start in following segment
+                            carryOverInc = endOfUrlPos;
+                            position = nextPos;
+                            
+                            CleanupStack::PopAndDestroy( &urlBuf );
+                            }     
+                        }
+                    CleanupStack::PopAndDestroy( &url );
+                    }
                 }
-              }
             }
-        INFO("Add end tags");
-        User::LeaveIfError( targetFile.Write( KHtmlEndTags ) );
-            
+        position += segment.Length();
+        if ( ( aSource.Size() - position ) <= 0 )
+            {
+            eos = ETrue;
+            }
+        }
 
-        CleanupStack::PopAndDestroy( &targetFile );
-        }
-// </cmail>
+    
     }
 
 
@@ -1076,6 +1539,7 @@
     CleanupStack::Pop( charSet );
     return charSet;
     }
+
 void CFsEmailUiHtmlViewerContainer::StopObserving()
     {
     if( iObservingDownload )
@@ -1116,22 +1580,189 @@
 	if ( aType == CFsEmailUiViewBase::EScreenLayoutChanged )
 		{
 	    // only update header if we get a layout change from email ui
-		if ( iMessage )
+		RefreshCurrentMailHeader();
+		}
+	}
+
+void CFsEmailUiHtmlViewerContainer::RefreshCurrentMailHeader()
+	{
+	if ( iMessage )
+		{
+		// Update the width in header part and reload
+		TPath headerHtmlFile;
+		headerHtmlFile.Copy( iHtmlFolderPath );
+		headerHtmlFile.Append( KHeaderHtmlFile );
+		
+		TLanguage language = User::Language();
+		TBidiText::TDirectionality direction = TBidiText::ScriptDirectionality( language );    
+		TRAP_IGNORE( CFreestyleMessageHeaderHTML::ExportL( *iMessage, iFs, headerHtmlFile, iAppUi.ClientRect().Width(), direction ) )
+		
+        TPath emailHtmlFile;
+        emailHtmlFile.Copy( iHtmlFolderPath );
+        if ( direction == TBidiText::ELeftToRight )
             {
-            // update the width in header part and reload
-            TPath headerHtmlFile;
-            headerHtmlFile.Copy( iHtmlFolderPath );
-            headerHtmlFile.Append( KHeaderHtmlFile );
-            
-            TRAP_IGNORE( CFreestyleMessageHeaderHTML::ExportL( *iMessage, iFs, headerHtmlFile, iAppUi.ClientRect().Width() ) )
-            
-            TPath emailHtmlFile;
-            emailHtmlFile.Copy( iHtmlFolderPath );
             emailHtmlFile.Append( KMessageHtmlFile );
-            
-            TRAP_IGNORE( LoadContentFromFileL( emailHtmlFile ) )
-            
+            }
+        else
+            {
+            emailHtmlFile.Append( KMessageHtmlRTLFile );
+            }
+		
+        //Load page synchronously if menu invisible
+        if(!iEventHandler->IsMenuVisible())
+            {
+            TRAP_IGNORE( LoadContentFromFileL( emailHtmlFile ) );
             SetRect( iAppUi.ClientRect() );
             }
+        //Load page asynchronously after dismissing menu    
+        else
+            {
+            iEventHandler->DismissMenuAndReload();
+            }		
 		}
 	}
+
+void CFsEmailUiHtmlViewerContainer::ReloadPageL()
+    {
+    TLanguage language = User::Language();
+    TBidiText::TDirectionality direction = TBidiText::ScriptDirectionality( language );    
+    TPath emailHtmlFile;
+    emailHtmlFile.Copy( iHtmlFolderPath );
+    if ( direction == TBidiText::ELeftToRight )
+            {
+        emailHtmlFile.Append( KMessageHtmlFile );
+        }
+    else
+        {
+        emailHtmlFile.Append( KMessageHtmlRTLFile );
+        }
+    TRAP_IGNORE( LoadContentFromFileL( emailHtmlFile ) );
+    SetRect( iAppUi.ClientRect() );
+    }
+
+/**
+ * The body fetch link is cmail://body/fetch. Look for the URL separator
+ * and the presence of cmail and body on the url.
+ * @param aUrl 
+ * return ETrue for  a valid body URL
+ */
+TBool CFsEmailUiHtmlViewerContainer::IsMessageBodyURL(const TDesC& aUrl)
+    {
+    TInt index = aUrl.Find(KURLSchemeSeparator);
+    if (index == KErrNotFound)
+        {
+        return EFalse;
+        }
+    else
+        {
+        if (aUrl.Left(index).CompareF(KURLSchemeCmail) == 0)
+            {
+            TInt bodyIndex = aUrl.Find(KURLTypeBody);                      
+            if (bodyIndex == KErrNotFound)
+                {
+                return EFalse;
+                }
+            else
+                {
+                TPtrC16 data= aUrl.Mid(bodyIndex);
+                TInt separator = data.Find(KURLSlash);
+                if(separator == KErrNotFound)
+                    {
+                    return EFalse;
+                    }
+                else
+                    {
+                    TPtrC16 temp = data.Mid(separator+1);
+                    TLex lex(temp);
+                    lex.Val(iScrollPosition);                  
+                    }
+                return ETrue;
+                }
+            
+            }
+        else
+            {
+            return EFalse;
+            }
+        }
+    } 
+// ---------------------------------------------------------------------------
+// From MBrCtlWindowObserver
+// ---------------------------------------------------------------------------
+//
+CBrCtlInterface* CFsEmailUiHtmlViewerContainer::OpenWindowL( TDesC& /*aUrl*/, TDesC* /*aTargetName*/, 
+                                                             TBool /*aUserInitiated*/, TAny* /*aReserved*/ )
+    {
+    return iBrCtlInterface;
+    }
+
+// ---------------------------------------------------------------------------
+// From MBrCtlWindowObserver
+// ---------------------------------------------------------------------------
+//
+CBrCtlInterface* CFsEmailUiHtmlViewerContainer::FindWindowL( const TDesC& /*aTargetName*/ ) const
+    {
+    return NULL;
+    }
+
+// ---------------------------------------------------------------------------
+// From MBrCtlWindowObserver
+// ---------------------------------------------------------------------------
+//
+void CFsEmailUiHtmlViewerContainer::HandleWindowCommandL( const TDesC& /*aTargetName*/, 
+                                                          TBrCtlWindowCommand /*aCommand*/ )
+    {
+    
+    }
+
+// ---------------------------------------------------------------------------
+// Check if a tap on the URL requires browser(standalone) to be launched
+// ---------------------------------------------------------------------------
+//
+TBool CFsEmailUiHtmlViewerContainer::NeedToLaunchBrowserL( const TDesC& aUrl )
+    {
+    TBool launchBrowser( ETrue );
+    // look for file:///
+    _LIT( KFileLink, "file:///");
+    
+    // This might be linking to header.html or body.html frames
+    // Ignore them.
+    if ( aUrl.Left( KFileLink().Length() ).CompareF( KFileLink ) == 0 )
+        {
+        //Now there is a chance that this could be from HTML folder
+        // Replace all slash character with backslash characters
+        HBufC* embeddedUrl = aUrl.AllocLC();
+        TPtr ptr = embeddedUrl->Des();
+        
+        _LIT( KBackslash, "\\" );
+        for ( TInt pos = ptr.Locate('/'); pos >= 0; pos = ptr.Locate('/') )
+            {
+            ptr.Replace( pos, 1, KBackslash );
+            }
+
+        // Check whether given url refers to file in the html folder
+        TInt pos = embeddedUrl->FindF( iHtmlFolderPath );
+        CleanupStack::PopAndDestroy( embeddedUrl );
+        pos >= 0 ? launchBrowser = EFalse : ETrue;        
+        }
+    // Ignore links starting with cmail://
+    else if ( aUrl.Left( KURLSchemeCmail().Length() ).CompareF( KURLSchemeCmail ) == 0 )
+        {
+        launchBrowser = EFalse;
+        }
+
+    return launchBrowser;
+    }
+
+// ---------------------------------------------------------------------------
+// Launch the browser as a standalone app
+// ---------------------------------------------------------------------------
+//
+void CFsEmailUiHtmlViewerContainer::LaunchBrowserL( const TDesC& aUrl )
+    {
+    CSchemeHandler* handler = CSchemeHandler::NewL( aUrl );
+    CleanupStack::PushL( handler );
+    handler->HandleUrlStandaloneL();
+    CleanupStack::PopAndDestroy( handler );
+    }
+