/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "stdafx.h" #include "cxabstra.h" #include "xp_thrmo.h" #include "timer.h" // for nettimer since can't call base class #include "libi18n.h" // International function #include "template.h" #include "dialog.h" #include "mainfrm.h" #ifdef MOZ_MAIL_NEWS #include "fldrfrm.h" #include "thrdfrm.h" #include "msgfrm.h" #include "msgcom.h" #include "wfemsg.h" #endif /* MOZ_MAIL_NEWS */ #include "winclose.h" #include "libevent.h" #include "np.h" #include "intl_csi.h" #include "mkhelp.h" // for HelpInfoStruct #include "feimage.h" #include "cxicon.h" #ifdef EDITOR #include "edt.h" #endif /* EDITOR */ #include "mozilla.h" CAbstractCX::CAbstractCX() { // Purpose: Constructor for the abstract base class. // Arguments: void // Returns: none // Comments: Responsible for creating the XP context. m_pLastStatus = NULL; // First set our context type. // This is really irrelevant, just a reminder to those which derive from us. m_cxType = Abstract; // Create the XP context. m_pXPCX = XP_NewContext(); m_pImageGroupContext = 0; m_bImagesLoading = FALSE; m_bImagesLooping = FALSE; m_bImagesDelayed = FALSE; m_bMochaImagesLoading = FALSE; m_bMochaImagesLooping = FALSE; m_bMochaImagesDelayed = FALSE; m_bNetHelpWnd = FALSE; // Set its type to an invalid default. // Up to others to correctly set. m_pXPCX->type = MWContextAny; m_pXPCX->fontScalingPercentage = 1.0; // Assign in the common front end functions. m_pXPCX->funcs = new ContextFuncs; #define MAKE_FE_FUNCS_PREFIX(funkage) CFE_##funkage #define MAKE_FE_FUNCS_ASSIGN m_pXPCX->funcs-> #include "mk_cx_fn.h" // Set the FE data to simply point to this class. m_pXPCX->fe.cx = this; // Initialize the global history of the object. SHIST_InitSession(m_pXPCX); // We haven't been told to attempt to interrupt the load. // Nor have we decided to self destruct. m_bIdleInterrupt = FALSE; m_bIdleDestroy = FALSE; // We have no client pull information. m_pClientPullData = FALSE; m_pClientPullTimeout = FALSE; // Reset our stopwatch, in case it never gets initialized. ResetStopwatch(); // We haven't been destroyed yet. m_bDestroyed = FALSE; // We have ncapi data yet. m_pNcapiUrlData = NULL; // We are initially allowing clicks. GetContext()->waitingMode = FALSE; // Add the context to the XP list. XP_AddContextToList(GetContext()); // No levels of interruptedness. m_iInterruptNest = 0; } int16 CAbstractCX::GetWinCsid() { return INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(m_pXPCX)); } /* * Mocha has removed all of its info from this context, it's * safe to trash it now */ void CAbstractCX::MochaDestructionComplete() { // Now, attempt to interrupt. This allows us to clean up a context, even // if the network library is blocking. // Eventually, this will cause this context to delete itself. Interrupt(); } /* * Mocha has removed all of its info from this context */ void mocha_done_callback(void * data) { ((CAbstractCX *)data)->MochaDestructionComplete(); } void CAbstractCX::DestroyContext() { // Purpose: Before deleting a context, you must destroy it for proper cleanup. // Arguments: void // Returns: void // Comments: Because the XP libraries perform calls on a context when it is going // away, and those calls are virtual, we must first "destroy" a context, // and then we are able to delete it properly. if(m_bDestroyed == FALSE) { // Only call in here once, please. // We have to lock the application from exiting until we receive // a series of callbacks and eventually remove the context // from memory. // This lock is removed in the destructor. AfxOleLockApp(); // Have the document interrupt. // We call again when we get the mocha complete callback, but that // causes us to get deleted physically after the destroyed flag // is set. Interrupt(); // Layout needs to cleanup. LO_DiscardDocument(m_pXPCX); // End the session history of the context. SHIST_EndSession(m_pXPCX); #ifdef MOZ_MAIL_NEWS // Free any memory used for MIME objects MimeDestroyContextData(m_pXPCX); #endif /* MOZ_MAIL_NEWS */ // We are now destroyed, set a member to let us know. m_bDestroyed = TRUE; if(m_pImageGroupContext) { IL_RemoveGroupObserver(m_pImageGroupContext, ImageGroupObserver, (void*)GetContext()); IL_DestroyGroupContext(m_pImageGroupContext); m_pXPCX->img_cx = NULL; } if (m_pXPCX->color_space) { IL_ReleaseColorSpace(m_pXPCX->color_space); m_pXPCX->color_space = NULL; } // Remove ourselves from the XP context list. // Must be done before call to mocha, or lists in // unstable state. XP_RemoveContextFromList(m_pXPCX); // Have mocha perform cleanup and continue destruction in the // callback ET_RemoveWindowContext(m_pXPCX, mocha_done_callback, this); } } /*-----------------------------------------------** ** Let a load complete before self destructing. ** **-----------------------------------------------*/ void CAbstractCX::NiceDestroyContext() { m_bIdleDestroy = FALSE; if(m_bDestroyed == FALSE && GetContext()) { if(XP_IsContextBusy(GetContext())) { // Try again later. m_bIdleDestroy = TRUE; FEU_RequestIdleProcessing(GetContext()); } else { // Time's up. DestroyContext(); return; } } } // Routine to detect when we can exit. void fe_finish_exit(void *pClosure) { BOOL bCanExit = TRUE; static void *pExitTimer = NULL; if(pClosure != NULL) { // Called via timeout. // Clear the timeout backpointer. pExitTimer = NULL; } else { // Called via destructor (anal). // Make sure we haven't already set the timer. if(pExitTimer != NULL) { return; } } #ifdef MOZ_LOC_INDEP /* li_stuff - time to do last synch */ if (theApp.LIStuffEnd() == FALSE) { // synching files. bCanExit = FALSE; } #endif // MOZ_LOC_INDEP if(AfxOleCanExitApp() == FALSE) { // Outstanding com lock. bCanExit = FALSE; } if(XP_ListCount(XP_GetGlobalContextList()) != 0) { // Context is out there. bCanExit = FALSE; } if(theApp.m_pMainWnd != NULL) { if(bCanExit) { TRACE("Posting WM_CLOSE to %p hidden frame to exit app.\n", theApp.m_pMainWnd); theApp.m_pMainWnd->PostMessage(WM_CLOSE); } else { // Have to retry in a little while. pExitTimer = FE_SetTimeout(fe_finish_exit, (void *)-1, 1000); ASSERT(pExitTimer != NULL); } } } CAbstractCX::~CAbstractCX() { // Purpose: Destroy the context. // Arguments: void // Returns: none // Comments: Perform any necessary cleanup also. // Can remove lock set in DestroyContext. // Window of instability gone. AfxOleUnlockApp(); // Let those watching this context that it is no more (NCAPI). CWindowChangeItem::Closing(GetContextID()); // If you get this ASSERTION, then you should call DestroyContext and // NEVER delete the context. ASSERT(m_bDestroyed == TRUE); // Get rid of the last and default status. if (m_pLastStatus) { XP_FREE(m_pLastStatus); m_pLastStatus = NULL; } if(m_pXPCX->defaultStatus != NULL) { XP_FREE(m_pXPCX->defaultStatus); m_pXPCX->defaultStatus = NULL; } // If we allocated a title for the context, get rid of it. if(m_pXPCX->title != NULL) { XP_FREE(m_pXPCX->title); m_pXPCX->title = NULL; } // Get rid of its name. if(m_pXPCX->name != NULL) { XP_FREE(m_pXPCX->name); m_pXPCX->name = NULL; } // Free the transparent pixel used by the Image Library. if(m_pXPCX->transparent_pixel) { XP_FREE(m_pXPCX->transparent_pixel); m_pXPCX->transparent_pixel = NULL; } /* EA: Remove any help information associated with this context */ if (m_pXPCX && (m_pXPCX->pHelpInfo != NULL)) { if (((HelpInfoStruct *) m_pXPCX->pHelpInfo)->topicURL != NULL) { XP_FREE(((HelpInfoStruct *) m_pXPCX->pHelpInfo)->topicURL); ((HelpInfoStruct *) m_pXPCX->pHelpInfo)->topicURL = NULL; } XP_FREE(m_pXPCX->pHelpInfo); m_pXPCX->pHelpInfo = NULL; } // Delete our function table in the XP context. memset(m_pXPCX->funcs, 0, sizeof(*(m_pXPCX->funcs))); delete m_pXPCX->funcs; m_pXPCX->funcs = NULL; // Delete our XP context. XP_DeleteContext(m_pXPCX); m_pXPCX = NULL; // NOTE: Frames and Views should be taken care of by sub-classes // There are three "hiding" contexts #ifdef MOZ_MAIL_NEWS #define HIDDENCXS 4 #else #define HIDDENCXS 2 #endif /* MOZ_MAIL_NEWS */ // bookmarks window // address book window // biff / check mail context // Misc. URL loader context which switch context, ie: NetHelp, etc. // Check the number of items in the context list to see if we should exit. if(XP_ListCount(XP_GetGlobalContextList()) == HIDDENCXS) { // These are the stragglers that we need to see if visible, // and if not, remove them at this time. // All or none. // Do in seperate for loops or list state is undefined. CAbstractCX *pCXs[HIDDENCXS]; int iTraverse; for(iTraverse = 0; iTraverse < HIDDENCXS; iTraverse++) { pCXs[iTraverse] = ABSTRACTCX((MWContext *)XP_ListGetObjectNum(XP_GetGlobalContextList(), iTraverse + 1)); } for(iTraverse = 0; iTraverse < HIDDENCXS; iTraverse++) { if(pCXs[iTraverse]) { pCXs[iTraverse]->DestroyContext(); } } // Because we've killed off the hidden contexts, clear pointers // to them. theApp.m_pSlaveCX = NULL; theApp.m_pRDFCX = NULL; // Start to detect when we can exit the application. fe_finish_exit(NULL); } } void CAbstractCX::SetContextName(const char *pName) { // Set the context name. if(GetContext()->name != NULL) { XP_FREE(GetContext()->name); } if(pName != NULL) { GetContext()->name = XP_STRDUP(pName); } else { GetContext()->name = NULL; } } void CAbstractCX::SetParentContext(MWContext *pParentContext) { // Set the context's parent context. GetContext()->grid_parent = pParentContext; // Also set that this is a grid cell. // Can't have a grid parent if not a true grid. GetContext()->is_grid_cell = TRUE; // Finally, update what is called the parent's list of children. // Removal is taken care of automagically. XP_UpdateParentContext(GetContext()); } BOOL CAbstractCX::IsGridCell() const { return(GetContext()->is_grid_cell); } BOOL CAbstractCX::IsGridParent() const { BOOL bRetval = FALSE; MWContext *pContext = GetContext(); if(pContext) { bRetval = !XP_ListIsEmpty(pContext->grid_children); } return bRetval; } CWnd *CAbstractCX::GetDialogOwner() const { // Purpose: Return the parent/owner of any dialogs for the context. // Arguments: void // Returns: CWnd * The parent as needed. // Comments: Generic use function to more easily define the owner // of any dialogs. // As a default, we'll be returning the desktop window. return(CWnd::GetDesktopWindow()); } // This routine changes the context of the specified URL structure to be // a browser context. Returns the new context if successful and NULL otherwise MWContext *SwitchToBrowserContext(URL_Struct* pUrlStruct) { ASSERT(NET_IsSafeForNewContext(pUrlStruct)); // Use an existing browser window if we have one CFrameWnd* pFrameWnd = FEU_GetLastActiveFrame(MWContextBrowser, FEU_FINDBROWSERONLY); MWContext* pNewContext = NULL; if (pFrameWnd) { CFrameGlue *pGlue = CFrameGlue::GetFrameGlue(pFrameWnd); CWinCX* pWinCX; if (pGlue && ((pWinCX = pGlue->GetMainWinContext()) != NULL)) { pNewContext = pWinCX->GetContext(); // Don't use this context if it's busy or doesn't want to be used. if (ABSTRACTCX(pNewContext)->IsContextStoppable() || pNewContext->restricted_target) pNewContext = NULL; // create a new context instead else { pWinCX->GetUrl( pUrlStruct, FO_CACHE_AND_PRESENT, TRUE ); // Make sure the window is visible if(pFrameWnd->IsIconic()) pFrameWnd->ShowWindow(SW_RESTORE); else { #ifdef XP_WIN16 pFrameWnd->BringWindowToTop(); #else pFrameWnd->SetForegroundWindow(); #endif } } } } // If we couldn't find an existing browser window, then create a new one if (!pNewContext) { // Create a new browser window pNewContext = CFE_CreateNewDocWindow(NULL, pUrlStruct); } return pNewContext; } int CAbstractCX::NormalGetUrl(const char *pUrl, const char *pReferrer, const char *pTarget, BOOL bForceNew) { // Generic brain dead interface to GetUrl. URL_Struct *pUrlStruct = NET_CreateURLStruct(pUrl, NET_DONT_RELOAD); // Add the referrer field. if(pReferrer != NULL) { pUrlStruct->referer = XP_STRDUP(pReferrer); } // Add the target name. if(pTarget != NULL) { if (pTarget[0] == '_') { pUrlStruct->window_target = XP_STRDUP(""); } else { pUrlStruct->window_target = XP_STRDUP(pTarget); } } // Load it. #ifdef EDITOR // Use Edit present type to filter mime types we can't edit return( GetUrl(pUrlStruct, EDT_IS_EDITOR(GetContext()) ? FO_CACHE_AND_EDIT : FO_CACHE_AND_PRESENT, TRUE, bForceNew)); #else return(GetUrl(pUrlStruct, FO_CACHE_AND_PRESENT, TRUE, bForceNew)); #endif } int CAbstractCX::GetUrl(URL_Struct *pUrl, FO_Present_Types iFormatOut, BOOL bReallyLoad, BOOL bForceNew) { // Purpose: Load a url into the context class. // Arguments: pUrl The URL to load // iFormatOut The format out of the load (helps determine the content type converter // along with the mime type). // bReallyLoading Is a switch to tell us not to actually request the URL. // This helps and context changing URLs to bootstrap the loading UI. // Returns: int As NET_GetURL // Comments: Use this instead of NET_GetURL // Determine URL type. -1 is out of range. int iUrlType = -1; if(pUrl && pUrl->address) { iUrlType = NET_URL_Type(pUrl->address); } // Switch on URL type to see how we should handle. int iRetval = MK_NO_ACTION; switch(iUrlType) { case NETHELP_TYPE_URL: { MWContext *pNetHelpCX = FE_GetNetHelpContext(); if(pNetHelpCX) { iRetval = NET_GetURL(pUrl, FO_PRESENT, pNetHelpCX, CFE_SimpleGetUrlExitRoutine); } break; } default: { // See if this is the current page (named anchor), if so don't load. // But don't do this if we're not really initiating the load ourselves. // Also make sure that this is a present type, if not, don't check // for this named anchor stuff. // If this isn't acceptable behaviour, we really need to split what // you're doing into a new context (view source dialog could be this // way as an example). int32 lX, lY; BOOL bNamedAnchor = FALSE; if(((iFormatOut & 0x1f ) == FO_PRESENT) && bReallyLoad == TRUE && XP_FindNamedAnchor(GetContext(), pUrl, &lX, &lY) == TRUE) { if(IsWindowContext()) { // Make the location visible on the screen.... PANECX(GetContext())->MakeElementVisible(lX, lY); } if(pUrl->history_num == 0) { // Create URL from prev history entry to preserve security attributes, etc. History_entry *pHist = SHIST_GetCurrent(&GetContext()->hist); URL_Struct *pCopyURL = SHIST_CreateURLStructFromHistoryEntry(GetContext(), pHist); // Swap addresses. char *pSaveAddr = pUrl->address; pUrl->address = pCopyURL->address; pCopyURL->address = pSaveAddr; // Free old URL, and reassign. NET_FreeURLStruct(pUrl); pUrl = pCopyURL; SHIST_AddDocument(GetContext(), SHIST_CreateHistoryEntry(pUrl, pHist->title)); } else { SHIST_SetCurrent(&GetContext()->hist, pUrl->history_num); } // Didn't have to perform a load. // However, we should call the normal routines to stop all the UI.... bReallyLoad = FALSE; bNamedAnchor = TRUE; } // The URLs anchor and referrer may be a local file path. // Change them to be network worthy. CString csUrl; if(pUrl->address != NULL) { WFE_ConvertFile2Url(csUrl, pUrl->address); XP_FREE(pUrl->address); pUrl->address = XP_STRDUP(csUrl); } if(pUrl->referer != NULL) { WFE_ConvertFile2Url(csUrl, pUrl->referer); XP_FREE(pUrl->referer); pUrl->referer = XP_STRDUP(csUrl); } #ifdef MOZ_MAIL_NEWS if ((((iFormatOut & 0x1f ) == FO_PRESENT) && (GetContext()->type != MWContextMetaFile) && MSG_NewWindowRequired(GetContext(), pUrl->address)) || bForceNew) { MWContextType type = MWContextBrowser; if ( MSG_RequiresMailWindow ( pUrl->address ) ) type = MWContextMail; else if ( MSG_RequiresNewsWindow ( pUrl->address ) ) type = MWContextNews; if (( ( type == MWContextNews ) || ( type == MWContextMail ) ) && !bForceNew ) { CMailNewsFrame *pMailNewsFrame = NULL; MSG_PaneType paneType = MSG_PaneTypeForURL(pUrl->address); switch ( paneType ) { case MSG_THREADPANE: { MSG_FolderInfo *folder = MSG_GetFolderInfoFromURL(WFE_MSGGetMaster(), pUrl->address); BOOL bContinue = FALSE; if (folder){ C3PaneMailFrame::Open(folder, MSG_MESSAGEKEYNONE, &bContinue); } if (!bContinue) return MK_CHANGING_CONTEXT; else break; } case MSG_MESSAGEPANE: { C3PaneMailFrame *pThreadFrame = CMailNewsFrame::GetLastThreadFrame(); if (pThreadFrame && pThreadFrame == CWnd::GetActiveWindow() && pThreadFrame->GetMainContext() == this) break; pMailNewsFrame = CMessageFrame::Open(); if ( pMailNewsFrame ) { pUrl->msg_pane = pMailNewsFrame->GetPane(); pMailNewsFrame->GetContext()->GetUrl( pUrl, iFormatOut, bReallyLoad ); } return MK_CHANGING_CONTEXT; } case MSG_FOLDERPANE: default: pMailNewsFrame = CFolderFrame::Open(); if ( pMailNewsFrame ) pMailNewsFrame->GetContext()->GetUrl( pUrl, iFormatOut, bReallyLoad ); return MK_CHANGING_CONTEXT; } } else if (GetContextType() != IconCX && GetContextType() != Pane) { MWContext* pNewContext = SwitchToBrowserContext(pUrl); return MK_CHANGING_CONTEXT; } } #endif // MOZ_MAIL_NEWS // Our first (well, almost) task is to interrupt the load. // Only one load per window should be instantiated by the front end. // Do this only if this is the context which is going to handle it. // If this is a view source request, don't interrupt the context // because we will be creating a new window. if(bNamedAnchor == FALSE && bReallyLoad == TRUE && iFormatOut != FO_CACHE_AND_VIEW_SOURCE) { Interrupt(); } // Disable clicking. GetContext()->waitingMode = TRUE; // The fun part about all this is, is that we don't want to call reentrantly // into the netlib, or else our idle interrupt routine will interrupt // both requests. Check to see if we're waiting for an idle interrupt. // Use the client pull mechanism to get around this. // Also, if we're not supposed to really call to load this URL, then don't. if(bReallyLoad == TRUE) { if(m_bIdleInterrupt == FALSE) { // Simply call the netlib routine, filling in some default data. // Pass along the common exit routine, which will call the appropriate // contexts exit routine. int iOldInProcessNet = winfeInProcessNet; winfeInProcessNet = TRUE; StartAnimation(); if ( m_cxType == IconCX) iRetval = NET_GetURL(pUrl, iFormatOut, GetContext(), Icon_GetUrlExitRoutine); else iRetval = NET_GetURL(pUrl, iFormatOut, GetContext(), CFE_GetUrlExitRoutine); FE_UpdateStopState(GetContext()); winfeInProcessNet = iOldInProcessNet; } else { // Set client pull on this URL. FEU_ClientPull(GetContext(), 250, pUrl, iFormatOut, TRUE); // Return that no action was taken, yet. iRetval = MK_NO_ACTION; } } else { // Indicate that we skipped loading since we're allowing someone to change // context. iRetval = MK_CHANGING_CONTEXT; } // If we were a named anchor, clean everything we did up. if(bNamedAnchor == TRUE) { iRetval = MK_DATA_LOADED; // We need to update the location bar's text, since LayoutNewDocument was never // called (routine responsible for this). if(IsFrameContext() && GetContext()->type == MWContextBrowser && !EDT_IS_EDITOR(GetContext())){ if(IsGridCell() == FALSE) { TRACE("Updating location bar for named anchor\n"); CWinCX *pWinCX = WINCX(GetContext()); CFrameGlue *pFrame = pWinCX->GetFrame(); if(pFrame){ CURLBar *pUrlBar = (CURLBar *)pFrame->GetChrome()->GetToolbar(ID_LOCATION_TOOLBAR); if(pUrlBar) pUrlBar->UpdateFields(pUrl->address); } } } // Call the exit routine on the URL. // Just use the common one that would have been used anyhow. CFE_GetUrlExitRoutine(pUrl, iRetval, GetContext()); // If there aren't any connections for the window, also call all connections // complete. // Context by context version, don't use XP_IsContextBusy. if(NET_AreThereActiveConnectionsForWindow(GetContext()) == FALSE) { FE_AllConnectionsComplete(GetContext()); } } break; } } // All Done. return(iRetval); } void CAbstractCX::Reload(NET_ReloadMethod iReloadType) { // Purpose: Reloads the current document in the context's history. // Arguments: void // Returns: void // Comments: Standard stuff. if(CanCreateUrlFromHist()) { URL_Struct *pUrl = CreateUrlFromHist(FALSE, NULL, iReloadType == NET_RESIZE_RELOAD); // Force the load (normally). if(pUrl) { pUrl->force_reload = iReloadType; GetUrl(pUrl, FO_CACHE_AND_PRESENT); } } } extern "C" Bool LO_LayingOut(MWContext * context); void CAbstractCX::NiceReload(int usePassInType, NET_ReloadMethod iReloadType ) { // Purpose: Reloads the current document in the context's history, only // if we're currently not loading. // Arguments: void // Returns: void // Comments: Standard stuff. MWContext * context = GetContext(); if(XP_IsContextBusy(context) == TRUE || LO_LayingOut(context)) { FEU_RequestIdleProcessing(context); context->reSize = TRUE; } else { context->reSize = FALSE; NPL_SamePage(context); if( usePassInType ) { Reload( iReloadType ); return; } if (MAIL_NEWS_TYPE(context->type)) { Reload(NET_NORMAL_RELOAD); } else { Reload(NET_RESIZE_RELOAD); } } } void CAbstractCX::Back() { // Purpose: Goes back one in the history. // Arguments: void // Returns: void // Comments: Standard stuff. History_entry *pHist = SHIST_GetPrevious(GetContext()); if (GetContext()->grid_children) { if (LO_BackInGrid(GetDocumentContext())) { return; } } if(pHist) { GetUrl(SHIST_CreateURLStructFromHistoryEntry(GetContext(), pHist), FO_CACHE_AND_PRESENT); } } void CAbstractCX::Forward() { // Purpose: Goes forward one in the history. // Arguments: void // Returns: void // Comments: Standard stuff. History_entry *pHist = SHIST_GetNext(GetContext()); if (GetContext()->grid_children) { if (LO_ForwardInGrid(GetDocumentContext())) { return; } } if(pHist) { GetUrl(SHIST_CreateURLStructFromHistoryEntry(GetContext(), pHist), FO_CACHE_AND_PRESENT); } } XL_TextTranslation CAbstractCX::TranslateText(URL_Struct *pUrl, const char *pFileName, const char *pPrefix, int iWidth) { // Purpose: Translate a url into text. // Arguments: pUrl The URL to translate. // pFileName The file in which to save the translation, CANNOT BE NULL. // pPrefix A prefix for each line, CANNOT BE NULL. // iWidth The width in characters of each line. // Returns: XL_TextTranslation as XL_TranslateText // Comments: Wraps all the work of manually calling the text translation routines. // There's no need to track the PrintSetup struct we create in this function, // as it will come back in the translation exit routine and can be // freed there. // Create a new print setup. PrintSetup *pTextFE = XP_NEW(PrintSetup); // Initialize. XL_InitializeTextSetup(pTextFE); // Assign in the needed members. pTextFE->width = iWidth; if(pPrefix) pTextFE->prefix = XP_STRDUP(pPrefix); else pTextFE->prefix = XP_STRDUP(""); pTextFE->eol = "\r\n"; pTextFE->filename = XP_STRDUP(pFileName); pTextFE->out = fopen(pFileName, "wb"); pTextFE->completion = CFE_TextTranslationExitRoutine; // Abstracted exit routine. pTextFE->url = pUrl; pTextFE->carg = CX2VOID(this, CAbstractCX); // Do it. return(XL_TranslateText(GetContext(), pUrl, pTextFE)); } void CAbstractCX::Interrupt() { // Purpose: Interrupt any loads in a context. // Arguments: void // Returns: void // Comments: If able, this will interrupt any loads in a context. // If unable, then sets a flag for idle time interrupting. // Increment our level of nested ness in the Interrupt function. // Problem is, that this is the only function which actually deletes // a context. // If we're nested several levels, then each tries to free the context. m_iInterruptNest++; // Provide a way to interrupt even if we're winfeInProcessNet but there // is currently no activity. // If we're not busy, then there's no need to interrupt. if(IsContextStoppable() == FALSE) { // Clear the idle interrupt if already set. m_bIdleInterrupt = FALSE; } // Reentrant safe version. else if(winfeInProcessNet == FALSE) { winfeInProcessNet = TRUE; XP_InterruptContext(GetContext()); winfeInProcessNet = FALSE; // Set the flag that we need no idle interrupt. // This is to avoid cases where we have set to idle interrupt, and get // called again where we actually do interrupt. m_bIdleInterrupt = FALSE; } else { // Request some idle processing. FEU_RequestIdleProcessing(GetContext()); m_bIdleInterrupt = TRUE; } // Decrement the level of nestedness. m_iInterruptNest--; // Check to see if we're supposed to delete this thing. // Idle interrupt will be true if unable to do anything, and will need // idle time so we don't destroy it then. // m_bDestroyed will be true, requesting the deletion. // m_iInterruptNest will be 0, indicating that we are at the bottom // most level of interrupt call. if(m_bDestroyed == TRUE && m_bIdleInterrupt == FALSE && m_iInterruptNest == 0) { // Delete. delete this; } return; } BOOL CAbstractCX::IsContextStoppableRecurse() { MWContext *pChild; if (!GetContext()) return FALSE; if ((m_bImagesLoading || m_bImagesLooping) && !m_bImagesDelayed) return TRUE; if ((m_bMochaImagesLoading || m_bMochaImagesLooping) && !m_bMochaImagesDelayed) return TRUE; XP_List *pTraverse = GetContext()->grid_children; while (pChild = (MWContext *)XP_ListNextObject(pTraverse)) { if (ABSTRACTCX(pChild)->IsContextStoppableRecurse()) return TRUE; } return FALSE; } // A wrapper around XP_IsContextStoppable. This is necessary // because a context is stoppable if its image context is // stoppable, and the image context is owned by the Front End. BOOL CAbstractCX::IsContextStoppable() { return (IsContextStoppableRecurse() || XP_IsContextStoppable(GetContext())); } void CAbstractCX::MailDocument() { #ifdef MOZ_MAIL_NEWS if(CanMailDocument()) { #ifdef EDITOR if (EDT_IS_EDITOR(GetContext())) { // Don't need to force saving document, EDT_MailDocument // will do the right thing. EDT_MailDocument(GetContext()); } else { MSG_MailDocumentURL(GetContext(),NULL); } #endif } #endif /* MOZ_MAIL_NEWS */ } BOOL CAbstractCX::CanMailDocument() { BOOL bRetval = TRUE; #ifdef MOZ_MAIL_NEWS if (!theApp.m_hPostalLib) return(FALSE); #endif // MOZ_MAIL_NEWS if(IsDestroyed()) { bRetval = FALSE; } else if(CanCreateUrlFromHist() == FALSE) { bRetval = FALSE; } else if(IsGridParent()) { bRetval = FALSE; } return(bRetval); } void CAbstractCX::OpenUrl() { // If we've a parent context, let them deal with this. if( GetParentContext() ) { ABSTRACTCX(GetParentContext())->OpenUrl(); } else if( !IsDestroyed() ) { ASSERT( GetDialogOwner() ); // Have the frame bring up the open url dialog. if( GetDialogOwner() ) { CDialogURL urlDlg(GetDialogOwner(), GetContext()); urlDlg.DoModal(); } } } BOOL CAbstractCX::CanOpenUrl() { // If we've a parent context, let them deal with this. if(GetParentContext()) { return(ABSTRACTCX(GetParentContext())->CanOpenUrl()); } else if(IsDestroyed()) { return(FALSE); } else { // Perhaps not if we're in some kiosk mode. return( GetDialogOwner() ? TRUE : FALSE); } } void CAbstractCX::NewWindow() { // Create a new window. if(CanNewWindow()) { CFE_CreateNewDocWindow(GetContext(), NULL); } } BOOL CAbstractCX::CanNewWindow() { // Can always get a new window (well....) BOOL bRetval = TRUE; if(IsDestroyed() == TRUE) { bRetval = FALSE; } else { // Limit windows only for win16 #ifdef XP_WIN16 // Limit to 8 top level browser contexts. // We may have Edit windows open and we run into // the limit of four real quick! if(XP_ContextCount(MWContextBrowser, TRUE) >= 8) { bRetval = FALSE; } #endif } return(bRetval); } static void UpdateUI(CAbstractCX *pCX) { if(pCX->IsFrameContext() == TRUE) { CWinCX *pWinCX = WINCX(pCX->GetContext()); CFrameGlue *pFrame = pWinCX->GetFrame(); // We need to make sure the toolbar buttons are correctly updated. // Most importantly, the stop button needs to be updated if(pFrame){ CFrameWnd* pFrameWnd = pFrame->GetFrameWnd(); if (pFrameWnd) pFrameWnd->SendMessageToDescendants(WM_IDLEUPDATECMDUI, (WPARAM)TRUE, (LPARAM)0); } } } void CAbstractCX::AllBack() { // Pass up to the parent for the ALL effect. if(GetParentContext()) { ABSTRACTCX(GetParentContext())->AllBack(); } else if(IsDestroyed() == FALSE) { Back(); UpdateUI(this); } } BOOL CAbstractCX::CanAllBack() { // Ask the parent for the ALL effect. if(GetParentContext()) { return(ABSTRACTCX(GetParentContext())->CanAllBack()); } BOOL bRetval = TRUE; if(IsDestroyed()) { bRetval = FALSE; } else { // Ask the session history. bRetval = SHIST_CanGoBack((GetContext())); } return(bRetval); } void CAbstractCX::AllForward() { // Pass up to the parent for the ALL effect. if(GetParentContext()) { ABSTRACTCX(GetParentContext())->AllForward(); } else if(IsDestroyed() == FALSE) { Forward(); UpdateUI(this); } } BOOL CAbstractCX::CanAllForward() { // Ask the parent for the ALL effect. if(GetParentContext()) { return(ABSTRACTCX(GetParentContext())->CanAllForward()); } BOOL bRetval = TRUE; if(IsDestroyed()) { bRetval = FALSE; } else { // Ask the session history. bRetval = SHIST_CanGoForward((GetContext())); } return(bRetval); } void CAbstractCX::AllInterrupt() { // Pass up to the parent for the ALL effect. if(GetParentContext()) { ABSTRACTCX(GetParentContext())->AllInterrupt(); } else if(IsDestroyed() == FALSE) { Interrupt(); } } BOOL CAbstractCX::CanAllInterrupt() { // Ask the parent for the ALL effect. if(GetParentContext()) { return(ABSTRACTCX(GetParentContext())->CanAllInterrupt()); } BOOL bRetval = TRUE; if(IsDestroyed()) { bRetval = FALSE; } else { // See if the context is busy. bRetval = IsContextStoppable(); } return(bRetval); } void CAbstractCX::AllReload(NET_ReloadMethod iReloadType) { // Pass up to the parent for the ALL effect. if(GetParentContext()) { ABSTRACTCX(GetParentContext())->AllReload(iReloadType); } else if(IsDestroyed() == FALSE) { Reload(iReloadType); } } BOOL CAbstractCX::CanAllReload() { // Ask the parent for the ALL effect. if(GetParentContext()) { return(ABSTRACTCX(GetParentContext())->CanAllReload()); } BOOL bRetval = TRUE; if(IsDestroyed()) { bRetval = FALSE; } else { // See if the context can be reloaded (has history). History_entry *pHist = SHIST_GetCurrent(&(GetContext()->hist)); if(pHist == NULL) { bRetval = FALSE; } } return(bRetval); } BOOL CAbstractCX::CanAddToBookmarks() { if( GetParentContext() ) { return(ABSTRACTCX(GetParentContext())->CanAddToBookmarks()); } if( IsDestroyed() ) { return(FALSE); } // Can only do this if we have something currently loaded. if( !CanCreateUrlFromHist() ) { return(FALSE); } return(TRUE); } void CAbstractCX::AddToBookmarks() { if( GetParentContext() ) { ABSTRACTCX(GetParentContext())->AddToBookmarks(); return; } if( !CanAddToBookmarks() ) { return; } History_entry *pHistEnt = SHIST_GetCurrent( &(GetContext()->hist) ); ASSERT(pHistEnt); HT_AddBookmark( pHistEnt->address, GetContext()->title ); } void CAbstractCX::CopySelection() { if(CanCopySelection() == FALSE) { return; } HANDLE h; HANDLE hData; char * string; char * text; text = (char *)LO_GetSelectionText(GetDocumentContext()); if(!text) { // Layout is reporting a selection, but we can't get it. ASSERT(0); return; } if(!::OpenClipboard(NULL)) { FE_Alert(GetContext(), szLoadString(IDS_OPEN_CLIPBOARD)); return; } if(!::EmptyClipboard()) { FE_Alert(GetContext(), szLoadString(IDS_EMPTY_CLIPBOARD)); return; } int len = XP_STRLEN(text) + 1; hData = GlobalAlloc(GMEM_MOVEABLE, (DWORD) len); string = (char *) GlobalLock(hData); strncpy(string, text, len); string[len - 1] = '\0'; GlobalUnlock(hData); h = ::SetClipboardData(CF_TEXT, hData); #ifdef XP_WIN32 int datacsid = GetWinCsid() & ~CS_AUTO; if((CS_USER_DEFINED_ENCODING != datacsid) && (0 != datacsid)) { len = CASTINT((INTL_StrToUnicodeLen(datacsid, (unsigned char*)text)+1) * 2); hData = GlobalAlloc(GMEM_MOVEABLE, (DWORD) len); string = (char *) GlobalLock(hData); INTL_StrToUnicode(datacsid, (unsigned char*)text, (INTL_Unicode*)string, len); GlobalUnlock(hData); } h = ::SetClipboardData(CF_UNICODETEXT, hData); #endif ::CloseClipboard(); XP_FREE(text); } BOOL CAbstractCX::CanCopySelection() { BOOL bRetval; History_entry *pHist = SHIST_GetCurrent(&(GetContext()->hist)); if(IsDestroyed()) { bRetval = FALSE; } else if(pHist == NULL) { bRetval = FALSE; } else { // Is there anything selected to be copied? bRetval = LO_HaveSelection(GetDocumentContext()); } return(bRetval); } // Look up a context via ID. // Used mainly by DDE and external application context lookup (out of process). // Returns NULL on failure. // To get the context ID, call FE_GetContextID or CAbstractCX::GetContextID CAbstractCX *CAbstractCX::FindContextByID(DWORD dwID) { MWContext *pTraverseContext = NULL; CAbstractCX *pTraverseCX = NULL; XP_List * thelist = XP_GetGlobalContextList(); while (pTraverseContext = (MWContext *)XP_ListNextObject(thelist)) { if(pTraverseContext != NULL && ABSTRACTCX(pTraverseContext) != NULL) { pTraverseCX = ABSTRACTCX(pTraverseContext); if(pTraverseCX->GetContextID() == dwID) { break; } pTraverseCX = NULL; } } return(pTraverseCX); } // Create a url struct from the history, with appropriate checking // for NULL and such, so that we don't have this code scattered // throughout the client. // bClearStateData is a flag set to erase any data that can't be // tossed around without upsetting the client (form data). // It's off by default, so be careful out there. URL_Struct *CAbstractCX::CreateUrlFromHist(BOOL bClearStateData, SHIST_SavedData *pSavedData, BOOL bWysiwyg) { TRACE("Creating URL from the current history entry.\n"); // Make sure that we're not destroyed. if(IsDestroyed()) { return(NULL); } // Do we have a history entry from which to create // the url? History_entry *pHistoryEntry = SHIST_GetCurrent(&(GetContext()->hist)); if(pHistoryEntry == NULL || pHistoryEntry->address == NULL) { return(NULL); } URL_Struct *pUrl = NULL; if(!bWysiwyg) { pUrl = SHIST_CreateURLStructFromHistoryEntry(GetContext(), pHistoryEntry); } else { pUrl = SHIST_CreateWysiwygURLStruct(GetContext(), pHistoryEntry); } if(pUrl == NULL) { return(NULL); } if(bClearStateData != FALSE) { TRACE("Clearing URL state data.\n"); // We may be wanting to copy the form data beyond the scope of this function. // Make sure layout saves the current state. LO_SaveFormData(GetDocumentContext()); // Are we to copy the session history to the parameter passed in (used in // subsequent call to copy the form data)? if(pSavedData) { memcpy(pSavedData, (void *)&(pUrl->savedData), sizeof(SHIST_SavedData)); } // If this structure changes in the future to hold data which can be carried // across contexts, then we lose. memset((void *)&(pUrl->savedData), 0, sizeof(SHIST_SavedData)); } return(pUrl); } // Used mainly in cmdui enablers to determine if we could load from // the current history entry. BOOL CAbstractCX::CanCreateUrlFromHist() { // Make sure that we're not destroyed. if(IsDestroyed()) { return(FALSE); } // Do we have a history entry from which to create // the url? History_entry *pHistoryEntry = SHIST_GetCurrent(&(GetContext()->hist)); if(pHistoryEntry == NULL || pHistoryEntry->address == NULL) { return(FALSE); } // Looks safe. return(TRUE); } void CAbstractCX::ResetStopwatch() { m_ttStopwatch = theApp.GetTime(); m_ttOldwatch = m_ttStopwatch - 1; }