/* * Copyright 2024, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ #include "PackageScreenshotRepository.h" #include #include #include #include "FileIO.h" #include "Logger.h" #include "StorageUtils.h" #include "WebAppInterface.h" static const uint32 kMaxRetainedCachedScreenshots = 25; PackageScreenshotRepository::PackageScreenshotRepository( PackageScreenshotRepositoryListenerRef listener, WebAppInterface* webAppInterface) : fListener(listener), fWebAppInterface(webAppInterface) { _Init(); } PackageScreenshotRepository::~PackageScreenshotRepository() { fListener.Unset(); _CleanCache(); } /*! This method will load the specified screenshot from remote, but will by-pass the cache. It will load the data into a file before loading it in order to avoid the data needing to be resident in memory twice; thus saving memory use. */ status_t PackageScreenshotRepository::LoadScreenshot(const ScreenshotCoordinate& coord, BitmapHolderRef& bitmapHolderRef) { if (!coord.IsValid()) return B_BAD_VALUE; BPath temporaryFilePath(tmpnam(NULL), NULL, true); status_t result = _DownloadToLocalFile(coord, temporaryFilePath); const char* temporaryFilePathStr = temporaryFilePath.Path(); if (result == B_OK) { FILE* file = fopen(temporaryFilePathStr, "rb"); if (file == NULL) { HDERROR("unable to open the screenshot file for read at [%s]", temporaryFilePathStr); result = B_IO_ERROR; } if (result == B_OK) { BFileIO fileIo(file, true); // takes ownership BBitmap* bitmap = BTranslationUtils::GetBitmap(&fileIo); if (bitmap == NULL) return B_IO_ERROR; bitmapHolderRef.SetTo(new(std::nothrow) BitmapHolder(bitmap), true); } } // even if the temporary file cannot be deleted, still return that it was OK. if (remove(temporaryFilePathStr) != 0) HDERROR("unable to delete the temporary file [%s]", temporaryFilePathStr); return result; } status_t PackageScreenshotRepository::CacheAndLoadScreenshot(const ScreenshotCoordinate& coord, BitmapHolderRef& bitmapHolderRef) { if (!coord.IsValid()) return B_BAD_VALUE; CacheScreenshot(coord); BPositionIO* data = NULL; status_t result = _CreateCachedData(coord, &data); if (result == B_OK) { ObjectDeleter dataDeleter(data); BBitmap* bitmap = BTranslationUtils::GetBitmap(data); if (bitmap == NULL) return B_IO_ERROR; bitmapHolderRef.SetTo(new(std::nothrow) BitmapHolder(bitmap), true); } return result; } status_t PackageScreenshotRepository::HasCachedScreenshot(const ScreenshotCoordinate& coord, bool* value) { if (value == NULL) debugger("expected the value to be supplied"); *value = false; if (!coord.IsValid()) return B_BAD_VALUE; BPath path = _DeriveCachePath(coord); const char* pathStr = path.Path(); BEntry entry(pathStr); struct stat s = {}; status_t result = entry.GetStat(&s); switch (result) { case B_ENTRY_NOT_FOUND: *value = false; return B_OK; case B_OK: *value = (s.st_size > 0); return B_OK; default: return result; } } status_t PackageScreenshotRepository::CacheScreenshot(const ScreenshotCoordinate& coord) { if (!coord.IsValid()) return B_BAD_VALUE; bool present = false; status_t result = HasCachedScreenshot(coord, &present); if (result == B_OK && present) return result; if (result == B_OK) result = _DownloadToLocalFile(coord, _DeriveCachePath(coord)); return result; } status_t PackageScreenshotRepository::_Init() { fBaseDirectory.Unset(); status_t result = StorageUtils::LocalWorkingDirectoryPath( "screenshot_cache", fBaseDirectory); if (B_OK != result) HDERROR("unable to setup the cache directory"); _CleanCache(); return result; } /*! Gets all of the cached files and looks to see the last access on them. The most recent files are retained and the rest are deleted from the disk. */ status_t PackageScreenshotRepository::_CleanCache() { HDINFO("will clean the screenshot cache"); // get all of the files and then order them by last accessed and then // delete the older ones. return StorageUtils::RemoveDirectoryContentsRetainingLatestFiles(fBaseDirectory, kMaxRetainedCachedScreenshots); } status_t PackageScreenshotRepository::_DownloadToLocalFile(const ScreenshotCoordinate& coord, const BPath& path) { const char* pathStr = path.Path(); FILE* file = fopen(pathStr, "wb"); if (file == NULL) { HDERROR("unable to open the screenshot file for writing at [%s]", pathStr); return B_IO_ERROR; } BFileIO outputDataStream(file, true); // takes ownership status_t result = fWebAppInterface->RetrieveScreenshot( coord.Code(), coord.Width(), coord.Height(), &outputDataStream); if (result == B_OK) result = outputDataStream.Flush(); if (result == B_OK) fListener->ScreenshotCached(coord); return result; } BPath PackageScreenshotRepository::_DeriveCachePath(const ScreenshotCoordinate& coord) const { BPath path(fBaseDirectory); path.Append(coord.CacheFilename()); return path; } status_t PackageScreenshotRepository::_CreateCachedData(const ScreenshotCoordinate& coord, BPositionIO** data) { status_t result = B_OK; BPath path = _DeriveCachePath(coord); const char* pathStr = path.Path(); FILE* file = fopen(pathStr, "rb"); if (file == NULL) { HDERROR("unable to open the screenshot file for read at [%s]", pathStr); result = B_IO_ERROR; } if (result == B_OK) *data = new BFileIO(file, true); // takes ownership return result; }