//********************************************************************* //* Microsoft Windows ** //* Copyright(c) Microsoft Corp., 1995 ** //********************************************************************* /*Included Files------------------------------------------------------------*/ #include "all.h" #include "safestrm.h" #include "decoder.h" #include "history.h" #include "midi.h" #include "mci.h" #include "wc_html.h" #include "blob.h" #ifdef FEATURE_VRML #include "vrml.h" #endif /*Structures----------------------------------------------------------------*/ /* This is the internal structure that will eventually get passed to the blob callback */ PBGBLOBPARAMS BGBLOBPARAMSConstruct(){ PBGBLOBPARAMS pBGBlobParams; pBGBlobParams = GTR_MALLOC(sizeof(*pBGBlobParams)); if (pBGBlobParams){ memset(pBGBlobParams, 0, sizeof(*pBGBlobParams)); } return pBGBlobParams; } static void BGBLOBPARAMSDestruct(PBGBLOBPARAMS pBGBlobParams){ /*nore any of this*/ if (pBGBlobParams) { if (pBGBlobParams->pszFilePath) { GTR_FREE(pBGBlobParams->pszFilePath); } if (pBGBlobParams->szRequestedURL) { GTR_FREE(pBGBlobParams->szRequestedURL); } GTR_FREE(pBGBlobParams); } } static void BackgroundBlob_Callback(void *param, const char *pszURL, BOOL bAbort, const char *pszFileHREF, BOOL fDCache, DCACHETIME dctExpires, DCACHETIME dctLastModif) { BGBLOBPARAMS * pBGBlobParams = (BGBLOBPARAMS *) param; ELEMENT *pel; int z; BOOL fResetDCacheCurDoc=FALSE; /*Did everything go as planned?*/ if (!bAbort && pBGBlobParams && pBGBlobParams->pszFilePath) { /*Cache this file*/ if (fDCache && gPrefs.bEnableDiskCache) { FUpdateBuiltinDCache(pBGBlobParams->OriginalFormat, pszURL, &pBGBlobParams->pszFilePath, dctExpires, dctLastModif, TRUE, pBGBlobParams->tw); fResetDCacheCurDoc=TRUE; } /*Start everyone that requested a fixup*/ if (pBGBlobParams->tw && pBGBlobParams->tw->w3doc){ pel = pBGBlobParams->tw->w3doc->aElements; for (z=0;z>=0;z=pel[z].next){ if ( (pel[z].pblob) && (pel[z].pblob->dwFlags & BLOB_FLAGS_FIXUP) && (0 == _stricmp(pel[z].pblob->szURL, pel[pBGBlobParams->iIndex].pblob->szURL)) ){ ASSERT(!(pel[z].pblob->dwFlags & BLOB_FLAGS_LOADED)); /*reset state flags*/ pel[z].pblob->dwFlags &= ~(BLOB_FLAGS_LOADING|BLOB_FLAGS_FIXUP); pel[z].pblob->dwFlags |= BLOB_FLAGS_LOADED; /*fill in blob structure with newly resolved local file name*/ if (BlobStoreFileName(pel[z].pblob, (char*) pBGBlobParams->pszFilePath)){ /*callback to start execution*/ (*pBGBlobParams->pCallback)(pBGBlobParams->tw, pel+z); } } } } } if (fResetDCacheCurDoc) ResetCIFEntryCurDoc(pszURL); /*clean up callback data*/ BGBLOBPARAMSDestruct(pBGBlobParams); } /* Called from within Image_LoadAll_Async in imgcache.c, this lightweight thread is responsible for loading the pel/blob combo that was passed in. */ #define STATE_BLOB_LOADED STATE_OTHER int LoadBackgroundBlobs_Async(struct Mwin *tw, int nState, void **ppInfo){ struct Params_LoadBackgroundBlobs *pParams; struct Params_LoadAsync *pLoadParams; PBGBLOBPARAMS pBGBlobParams; ELEMENT *pel; char *pFileName; BOOL fWire; int output_format; int z; int iStatusBarTextID; BOOL bAutoLoadVideos = gPrefs.bAutoLoadVideos; if (ppInfo){ pParams = *ppInfo; switch (nState) { case STATE_INIT: if (!tw || !tw->w3doc) return STATE_ABORT; pel = pParams->twDoc->w3doc->aElements; #ifdef FEATURE_VRML // If we're on a full pane VRML page, pretend gprefs.bAutoLoadVideos is TRUE if ( (tw->w3doc->flags & W3DOC_FLAG_FULL_PANE_VRML) && (pel[pParams->iIndex].lFlags & ELEFLAG_FULL_PANE_VRML_FIRST_LOAD ) && !(pel[pParams->iIndex].lFlags & ELEFLAG_HIDDEN ) ) { // Clear the first load flag, so that reconnects to page will load pel[pParams->iIndex].lFlags &= ~ELEFLAG_FULL_PANE_VRML_FIRST_LOAD; bAutoLoadVideos = TRUE; // we bail out since we're downloading it from another place goto LoadBackgroundBlobs_Async_Exit; } #endif /*Is another instance of us loading?*/ ASSERT(pel[pParams->iIndex].pblob); for (z=0;z>=0;z=pel[z].next){ if ((z!=pParams->iIndex) && (pel[z].pblob) && (0 == _stricmp(pel[z].pblob->szURL, pel[pParams->iIndex].pblob->szURL)) ){ if (pel[z].pblob->dwFlags & BLOB_FLAGS_LOADING){ pel[pParams->iIndex].pblob->dwFlags |= BLOB_FLAGS_FIXUP; goto LoadBackgroundBlobs_Async_Exit; } } } pel = pParams->twDoc->w3doc->aElements + pParams->iIndex; // making the assertion that this loop above is going // to find a pel with a pblob ASSERT(pel); ASSERT(pel->pblob); // clear any error that may be set pel->pblob->dwFlags &= ~BLOB_FLAGS_ERROR; /*do we get this from the wire, or is the local version ok?!?!?*/ if (PROT_FILE == ProtocolIdentify(pel->pblob->szURL)) { fWire = FALSE; pFileName = pel->pblob->szURL+5; } else { fWire = TRUE; pFileName = NULL; } if (pFileName && !BlobStoreFileName(pel->pblob, (char*) pFileName)){ goto LoadBackgroundBlobs_Async_Exit; } if (pParams->bLocalOnly){ // if our autoload images are off, but our play bk sounds // are on, then we need to do a download for the sounds. // otherwise we'll end up not loading sounds just because // we're not showing images, B#391 if ( !gPrefs.bAutoLoadImages && gPrefs.bPlayBackgroundSounds && pel->type == ELE_BGSOUND ) { pParams->bLocalOnly = FALSE; } else if ( !gPrefs.bAutoLoadImages && bAutoLoadVideos && pel->type == ELE_IMAGE ) { pParams->bLocalOnly = FALSE; } else { // otherwise do the normal stuff for bLocalOnly if (!pFileName) goto LoadBackgroundBlobs_Async_Exit; else fWire = FALSE; } } // default iStatusBarTextID = RES_STRING_BLOB_MCI; /*determine what we are getting anyway. restart if local version ok*/ switch(pel->type){ case ELE_BGSOUND: iStatusBarTextID = RES_STRING_BLOB_BGSOUND; if (gPrefs.bPlayBackgroundSounds){ // if we don't have a sound card // or MIDI device, then there is no need // to download this file, B#539 if ( waveOutGetNumDevs() == 0 && midiOutGetNumDevs() == 0 ) goto LoadBackgroundBlobs_Async_Exit; if (!fWire){ pel->pblob->dwFlags |= BLOB_FLAGS_LOADED; BackgroundSoundFile_Callback(pParams->twDoc, pel); goto LoadBackgroundBlobs_Async_Exit; } else{ /* This is a hack to figure out if we have a * valid sound file. We basically just check for * the filename (url in this case) to have the * correct extension. Refer to DwValidSoundFile * for details. (Bug 318) */ if (DwValidSoundFile(pel->pblob->szURL)) output_format = WWW_BGSOUND; else goto LoadBackgroundBlobs_Async_Exit; } } else goto LoadBackgroundBlobs_Async_Exit; break; case ELE_IMAGE: #ifdef FEATURE_VRML ASSERT(pel->pmo || pel->pVrml); // if we're forcing a download then we need // to download this guy even though we're not // supposed to show the images if (pel->pVrml) iStatusBarTextID = RES_STRING_BLOB_VRML; else iStatusBarTextID = RES_STRING_BLOB_MCI; if (bAutoLoadVideos || pParams->bJustOne) { if (!fWire) { pel->pblob->dwFlags |= BLOB_FLAGS_LOADED; if (pel->pmo) BackgroundMciFile_Callback(pParams->twDoc, pel); else if (pel->pVrml) BackgroundVRMLFile_Callback(pParams->twDoc, pel); goto LoadBackgroundBlobs_Async_Exit; } else { if (pel->pmo) output_format = WWW_MCI; else if (pel->pVrml) output_format = WWW_VRML; } } else { // if we're not in an image download mode // then lets stop this download process. if (!gPrefs.bAutoLoadImages && !bAutoLoadVideos && pel->myImage) { pel->myImage->flags = IMG_NOTLOADED; pel->myImage->flags |= IMG_LOADSUP; pel->myImage->height = pel->myImage->width = 0; } goto LoadBackgroundBlobs_Async_Exit; } break; #else ASSERT(pel->pmo); if (bAutoLoadVideos || pParams->bJustOne){ if (!fWire){ pel->pblob->dwFlags |= BLOB_FLAGS_LOADED; BackgroundMciFile_Callback(pParams->twDoc, pel); goto LoadBackgroundBlobs_Async_Exit; } else output_format = WWW_MCI; } break; #endif default: ASSERT(0); break; } /*we have a valid thing we are grabbing from the wire*/ pParams->pDest = Dest_CreateDest(pel->pblob->szURL); if (NULL == pParams->pDest) return STATE_ABORT; pLoadParams = GTR_MALLOC(sizeof(*pLoadParams)); if (pLoadParams == NULL) return STATE_ABORT; memset(pLoadParams,0,sizeof(*pLoadParams)); pParams->status = 0; pLoadParams->pStatus = &(pParams->status); // By default, we say that it's not ok to load from dcache. // This will kick in the If-Modified-since code if this // blob is in dcache. Else it will fetch from wire anyway. pLoadParams->fLoadFromDCacheOK = FALSE; /* Set up the request structure */ pParams->pRequest = pLoadParams->request = HTRequest_new(); if (pLoadParams->request) { /*force the output format so that we get called back*/ HTFormatInit(pLoadParams->request->conversions); pLoadParams->request->output_format = output_format; /*create our user defined data and place it in a spot that will be passsed along*/ pBGBlobParams = BGBLOBPARAMSConstruct(); if (pBGBlobParams){ char szBuf[MAX_URL_STRING+1]; // scratch buffer for rendering Status Bar char *pszMallocBuf; pBGBlobParams->iIndex = pParams->iIndex; pBGBlobParams->tw = pParams->twDoc; pLoadParams->request->context = (void *) pBGBlobParams; pLoadParams->request->destination = pParams->pDest; pLoadParams->request->referer = pParams->twDoc->w3doc->szActualURL; pel->pblob->dwFlags |= (BLOB_FLAGS_LOADING|BLOB_FLAGS_FIXUP); // if we're downloading a bunch of blobs ie we're doing // a complete download of a page, then don't complain on // each and every failure. Just show the error placeholder if ( ! pParams->bJustOne ) pLoadParams->request->iFlags |= HTREQ_STOP_WHINING; pLoadParams->request->fNotFromCache = pParams->bNoImageCache; pLoadParams->request->iFlags &= ~HTREQ_RECORD; pLoadParams->request->iFlags |= HTREQ_IF_IN_CACHE_DONT_READ_DATA; pszMallocBuf = GTR_MALLOC( MAX_URL_STRING ); if ( pszMallocBuf ) { GTR_strncpy(szBuf, pel->pblob->szURL, MAX_URL_STRING ); make_URL_HumanReadable(szBuf, NULL, FALSE ); GTR_formatmsg(iStatusBarTextID,pszMallocBuf, MAX_URL_STRING,szBuf); WAIT_Push(tw, -1, pszMallocBuf); WAIT_Lock( TRUE ) ; GTR_FREE(pszMallocBuf); } else { // worst case is we won't have our status bar text WAIT_Push(tw, -1, NULL); } Async_DoCall(HTLoadDocument_Async, pLoadParams); return STATE_BLOB_LOADED; } } /*fallthrough to abort*/ case STATE_ABORT: case STATE_BLOB_LOADED: if (pParams){ if (pParams->twDoc && pParams->twDoc->w3doc){ WAIT_Lock( FALSE ) ; WAIT_Pop(tw); pel = pParams->twDoc->w3doc->aElements + pParams->iIndex; if (pel && pel->pblob) { pel->pblob->dwFlags &= ~BLOB_FLAGS_LOADING; if ( pParams->pRequest && (pParams->pRequest->iFlags & HTREQ_HAD_ERROR)) { pel->pblob->dwFlags |= BLOB_FLAGS_ERROR; #ifdef FEATURE_VRML // If we encountered an error when downloading a blob for the VRML hidden // element, we need to notify the VRML viewer. // if (pel->pVrml && (pel->pVrml->dwFlags & VRMLF_INLINE)) { BackgroundVRMLFile_Callback(pParams->twDoc, pel); } #endif // if we don't have an Image, then mark the image as // well as errored out if ( pel->myImage && !pel->myImage->actualURL) pel->myImage->flags = IMG_ERROR | (pel->myImage->flags & ~(IMG_ERROR|IMG_MISSING|IMG_NOTLOADED)); } } if (pParams->pDest) Dest_DestroyDest(pParams->pDest); } if (pParams->pRequest) HTRequest_delete(pParams->pRequest); } break; } /* sometimes you feel like a nut, sometimes you don't but really, quick exit so flag won't move as much on a revisit to a page where everything is cached */ LoadBackgroundBlobs_Async_Exit: /*reduce outstanding thread count so flag functions properly*/ if (pParams->pImgThreads){ pParams->pImgThreads = pSafeFree(pParams->pImgThreads); if (pParams->pImgThreads) Async_UnblockThread(pParams->thidParent); } if (pParams->pDecoder) makeAvailable((PDECODER)pParams->pDecoder); } return STATE_DONE; } /******************************************************************* NAME: GTR_DownloadBackgroundBlob SYNOPSIS: Called when we are about to start downloading a background sound file ********************************************************************/ HTStream *GTR_DownloadBackgroundBlob(struct Mwin *tw, HTRequest *request, void *param, HTFormat input_format, HTFormat output_format, HTStream *output_stream){ PBGBLOBPARAMS pBGBlobParams; BOOL bWasSilent; HTStream *me; request = HTRequest_validate(request); if (!request) return NULL; pBGBlobParams = (PBGBLOBPARAMS) request->context; if (request->szLocalFileName){ request->savefile = GTR_strdup(request->szLocalFileName); } else{ if (!request->fImgFromDCache) { // generate a temporary file name. This will be in the DCache // directory but have some random .TMP name. This will get renamed // by the caching code later on so it arrives at its proper name // in the cache. A little weird, huh? if (!(request->savefile = (char *) GTR_MALLOC(_MAX_PATH + 1))) { BGBLOBPARAMSDestruct(pBGBlobParams); return NULL; } // Get a temporary file name if ( GetTempFileName(gPrefs.szCacheLocation, "A", 0, request->savefile) == 0 ) { char path[_MAX_PATH + 1] = ""; PREF_GetTempPath(_MAX_PATH, path); GetTempFileName(path, "A", 0, request->savefile); } } } pBGBlobParams->szRequestedURL = GTR_strdup(request->destination->szRequestedURL); if (pBGBlobParams->szRequestedURL){ pBGBlobParams->OriginalFormat = input_format; pBGBlobParams->pszFilePath = request->savefile; request->nosavedlg = TRUE; /* get state of stuff downloading in background we do this because HTSaveWithCallback puts up that annoying message when we are downloading and want to switch pages. we supress this behavior if a BLOB was the cause */ bWasSilent = tw->bSilent; if (WWW_BGSOUND == request->output_format) pBGBlobParams->pCallback = BackgroundSoundFile_Callback; else if (WWW_MCI == request->output_format) pBGBlobParams->pCallback = BackgroundMciFile_Callback; #ifdef FEATURE_VRML else if (WWW_VRML == request->output_format) pBGBlobParams->pCallback = BackgroundVRMLFile_Callback; #endif else if (WWW_PRESENT == request->output_format) pBGBlobParams->pCallback = BackgroundVRMLFile_Callback; else ASSERT(0); me = HTSaveWithCallback(tw, request,pBGBlobParams, input_format, BackgroundBlob_Callback); tw->bSilent = bWasSilent; } else me = NULL; return me; } /*Helper Functions for external structures----------------------------------*/ PBLOBstuff BlobConstruct(){ PBLOBstuff pblob; pblob = GTR_MALLOC(sizeof(*pblob)); memset(pblob, 0, sizeof(*pblob)); return pblob; } void BlobDestruct(PBLOBstuff pblob){ if (pblob){ if (pblob->szURL) GTR_FREE(pblob->szURL); if (pblob->szFileName) GTR_FREE(pblob->szFileName); GTR_FREE(pblob); } } BOOL BlobStoreUrl(PBLOBstuff pblob, char *pURL){ ASSERT(pblob); if (pblob->szURL) GTR_FREE(pblob->szURL); pblob->szURL = GTR_strdup(pURL); return pblob->szURL?TRUE:FALSE; } BOOL BlobStoreFileName(PBLOBstuff pblob, char *pFileName){ ASSERT(pblob); if (pblob->szFileName) GTR_FREE(pblob->szFileName); pblob->szFileName = GTR_strdup(pFileName); return pblob->szFileName?TRUE:FALSE; } /* Remove all blobs in a document * Sort of replicated from Image_NukeImages && blob part of Image_Nuke */ BOOL FNukeBlobs(struct _www *pW3doc, BOOL bNukeDCache) { int i; BOOL fSomethingNuked = FALSE; for (i = 0; i < pW3doc->elementCount; i++) { if (pW3doc->aElements[i].pblob) { if (bNukeDCache && pW3doc->aElements[i].pblob->szURL ) { pW3doc->aElements[i].pblob->dwFlags &= ~( BLOB_FLAGS_LOADING | BLOB_FLAGS_FIXUP | BLOB_FLAGS_LOADED ); FlushDCacheEntry(pW3doc->aElements[i].pblob->szURL); fSomethingNuked = TRUE; } } } return fSomethingNuked; }