diff --git a/browser/components/shell/nsGNOMEShellDBusHelper.h b/browser/components/shell/nsGNOMEShellDBusHelper.h --- a/browser/components/shell/nsGNOMEShellDBusHelper.h +++ b/browser/components/shell/nsGNOMEShellDBusHelper.h @@ -19,6 +19,8 @@ #define DBUS_BUS_NAME "org.mozilla.Firefox.SearchProvider" #define DBUS_OBJECT_PATH "/org/mozilla/Firefox/SearchProvider" +class nsGNOMEShellHistorySearchResult; + DBusHandlerResult DBusIntrospect(DBusConnection* aConnection, DBusMessage* aMsg); DBusHandlerResult DBusHandleInitialResultSet( diff --git a/browser/components/shell/nsGNOMEShellDBusHelper.cpp b/browser/components/shell/nsGNOMEShellDBusHelper.cpp --- a/browser/components/shell/nsGNOMEShellDBusHelper.cpp +++ b/browser/components/shell/nsGNOMEShellDBusHelper.cpp @@ -6,7 +6,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsGNOMEShellSearchProvider.h" -#include "nsGNOMEShellDBusHelper.h" #include "nsPrintfCString.h" #include "RemoteUtils.h" @@ -174,6 +173,49 @@ dbus_message_iter_close_container(aIter, &iterDict); } +/* + "icon-data": a tuple of type (iiibiiay) describing a pixbuf with width, + height, rowstride, has-alpha, + bits-per-sample, channels, + image data +*/ +static void DBusAppendIcon(GnomeHistoryIcon* aIcon, DBusMessageIter* aIter) { + DBusMessageIter iterDict, iterVar, iterStruct; + dbus_message_iter_open_container(aIter, DBUS_TYPE_DICT_ENTRY, nullptr, + &iterDict); + const char* key = "icon-data"; + dbus_message_iter_append_basic(&iterDict, DBUS_TYPE_STRING, &key); + dbus_message_iter_open_container(&iterDict, DBUS_TYPE_VARIANT, "(iiibiiay)", + &iterVar); + dbus_message_iter_open_container(&iterVar, DBUS_TYPE_STRUCT, nullptr, + &iterStruct); + + int width = aIcon->GetWidth(); + int height = aIcon->GetHeight(); + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &width); + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &height); + int rowstride = width * 4; + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &rowstride); + int hasAlpha = true; + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_BOOLEAN, &hasAlpha); + int bitsPerSample = 8; + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &bitsPerSample); + int channels = 4; + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &channels); + + DBusMessageIter iterArray; + dbus_message_iter_open_container(&iterStruct, DBUS_TYPE_ARRAY, "y", + &iterArray); + unsigned char* array = aIcon->GetData(); + dbus_message_iter_append_fixed_array(&iterArray, DBUS_TYPE_BYTE, &array, + width * height * 4); + dbus_message_iter_close_container(&iterStruct, &iterArray); + + dbus_message_iter_close_container(&iterVar, &iterStruct); + dbus_message_iter_close_container(&iterDict, &iterVar); + dbus_message_iter_close_container(aIter, &iterDict); +} + /* Appends history search results to the DBUS reply. We can return those fields at GetResultMetas: @@ -188,11 +230,14 @@ "description": an optional short description (1-2 lines) */ static void DBusAppendResultID( - nsCOMPtr aHistResultContainer, + RefPtr aSearchResult, DBusMessageIter* aIter, const char* aID) { + nsCOMPtr container = + aSearchResult->GetSearchResultContainer(); + + int index = DBusGetIndexFromIDKey(aID); nsCOMPtr child; - aHistResultContainer->GetChild(DBusGetIndexFromIDKey(aID), - getter_AddRefs(child)); + container->GetChild(index, getter_AddRefs(child)); nsAutoCString title; if (NS_FAILED(child->GetTitle(title))) { return; @@ -207,7 +252,13 @@ const char* titleStr = title.get(); appendStringDictionary(aIter, "id", aID); appendStringDictionary(aIter, "name", titleStr); - appendStringDictionary(aIter, "gicon", "text-html"); + + GnomeHistoryIcon* icon = aSearchResult->GetHistoryIcon(index); + if (icon) { + DBusAppendIcon(icon, aIter); + } else { + appendStringDictionary(aIter, "gicon", "text-html"); + } } /* Search the web for: "searchTerm" to the DBUS reply. @@ -265,8 +316,7 @@ KEYWORD_SEARCH_STRING_LEN) == 0) { DBusAppendSearchID(&iterArray2, stringArray[i]); } else { - DBusAppendResultID(aSearchResult->GetSearchResultContainer(), - &iterArray2, stringArray[i]); + DBusAppendResultID(aSearchResult, &iterArray2, stringArray[i]); } dbus_message_iter_close_container(&iterArray, &iterArray2); } diff --git a/browser/components/shell/nsGNOMEShellSearchProvider.h b/browser/components/shell/nsGNOMEShellSearchProvider.h --- a/browser/components/shell/nsGNOMEShellSearchProvider.h +++ b/browser/components/shell/nsGNOMEShellSearchProvider.h @@ -12,9 +12,37 @@ #include "nsINavHistoryService.h" #include "nsUnixRemoteServer.h" #include "nsCOMPtr.h" +#include "mozilla/UniquePtr.h" +#include "nsGNOMEShellDBusHelper.h" class nsGNOMEShellSearchProvider; +class GnomeHistoryIcon { + public: + GnomeHistoryIcon() : mTimeStamp(-1), mWidth(0), mHeight(0){}; + + // From which search is this icon + void Set(int aTimeStamp, mozilla::UniquePtr aData, int aWidth, + int aHeight) { + mTimeStamp = aTimeStamp; + mWidth = aWidth; + mHeight = aHeight; + mData = std::move(aData); + } + + bool IsLoaded() { return mData && mWidth > 0 && mHeight > 0; } + int GetTimeStamp() { return mTimeStamp; } + uint8_t* GetData() { return mData.get(); } + int GetWidth() { return mWidth; } + int GetHeight() { return mHeight; } + + private: + int mTimeStamp; + mozilla::UniquePtr mData; + int mWidth; + int mHeight; +}; + // nsGNOMEShellHistorySearchResult is a container with contains search results // which are files asynchronously by nsGNOMEShellHistoryService. // The search results can be opened by Firefox then. @@ -34,11 +62,16 @@ mSearchTerm = nsAutoCString(aSearchTerm); } DBusConnection* GetDBusConnection() { return mConnection; } + void SetTimeStamp(int aTimeStamp) { mTimeStamp = aTimeStamp; } int GetTimeStamp() { return mTimeStamp; } - void SetTimeStamp(int aTimeStamp) { mTimeStamp = aTimeStamp; } nsAutoCString& GetSearchTerm() { return mSearchTerm; } - void SetSearchResultContainer( + + // Receive (asynchronously) history search results from history service. + // This is called asynchronously by nsGNOMEShellHistoryService + // when we have search results available. + void ReceiveSearchResultContainer( nsCOMPtr aHistResultContainer); + nsCOMPtr GetSearchResultContainer() { return mHistResultContainer; } @@ -46,8 +79,12 @@ nsUnixRemoteServer::HandleCommandLine(aBuffer, aTimestamp); } + void SetHistoryIcon(int aTimeStamp, mozilla::UniquePtr aData, + int aWidth, int aHeight, int aIconIndex); + GnomeHistoryIcon* GetHistoryIcon(int aIconIndex); + private: - void SendDBusSearchResultReply(); + void HandleSearchResultReply(); ~nsGNOMEShellHistorySearchResult() = default; @@ -58,6 +95,7 @@ DBusMessage* mReply; DBusConnection* mConnection; int mTimeStamp; + GnomeHistoryIcon mHistoryIcons[MAX_SEARCH_RESULTS_NUM]; }; class nsGNOMEShellHistoryService { diff --git a/browser/components/shell/nsGNOMEShellSearchProvider.cpp b/browser/components/shell/nsGNOMEShellSearchProvider.cpp --- a/browser/components/shell/nsGNOMEShellSearchProvider.cpp +++ b/browser/components/shell/nsGNOMEShellSearchProvider.cpp @@ -6,7 +6,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsGNOMEShellSearchProvider.h" -#include "nsGNOMEShellDBusHelper.h" #include "nsIWidget.h" #include "nsToolkitCompsCID.h" @@ -14,10 +13,113 @@ #include "RemoteUtils.h" #include "base/message_loop.h" // for MessageLoop #include "base/task.h" // for NewRunnableMethod, etc +#include "nsIServiceManager.h" +#include "nsNetCID.h" +#include "nsIIOService.h" #include #include +#include "imgIContainer.h" +#include "imgITools.h" +#include "mozilla/gfx/DataSurfaceHelpers.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +class AsyncFaviconDataReady final : public nsIFaviconDataCallback { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIFAVICONDATACALLBACK + + AsyncFaviconDataReady(RefPtr aSearchResult, + int aIconIndex, int aTimeStamp) + : mSearchResult(aSearchResult), + mIconIndex(aIconIndex), + mTimeStamp(aTimeStamp){}; + + private: + ~AsyncFaviconDataReady() {} + + RefPtr mSearchResult; + int mIconIndex; + int mTimeStamp; +}; + +NS_IMPL_ISUPPORTS(AsyncFaviconDataReady, nsIFaviconDataCallback) + +// Inspired by SurfaceToPackedBGRA +static UniquePtr SurfaceToPackedRGBA(DataSourceSurface* aSurface) { + IntSize size = aSurface->GetSize(); + CheckedInt bufferSize = + CheckedInt(size.width * 4) * CheckedInt(size.height); + if (!bufferSize.isValid()) { + return nullptr; + } + UniquePtr imageBuffer(new (std::nothrow) + uint8_t[bufferSize.value()]); + if (!imageBuffer) { + return nullptr; + } + + DataSourceSurface::MappedSurface map; + if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { + return nullptr; + } + + // Convert BGRA to RGBA + uint32_t* aSrc = (uint32_t*)map.mData; + uint32_t* aDst = (uint32_t*)imageBuffer.get(); + for (int i = 0; i < size.width * size.height; i++, aDst++, aSrc++) { + *aDst = *aSrc & 0xff00ff00; + *aDst |= (*aSrc & 0xff) << 16; + *aDst |= (*aSrc & 0xff0000) >> 16; + } + + aSurface->Unmap(); + + return imageBuffer; +} + +NS_IMETHODIMP +AsyncFaviconDataReady::OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen, + const uint8_t* aData, + const nsACString& aMimeType, + uint16_t aWidth) { + // This is a callback from some previous search so we don't want it + if (mTimeStamp != mSearchResult->GetTimeStamp() || !aData || !aDataLen) { + return NS_ERROR_FAILURE; + } + + // Decode the image from the format it was returned to us in (probably PNG) + nsCOMPtr container; + nsCOMPtr imgtool = do_CreateInstance("@mozilla.org/image/tools;1"); + nsresult rv = imgtool->DecodeImageFromBuffer( + reinterpret_cast(aData), aDataLen, aMimeType, + getter_AddRefs(container)); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr surface = container->GetFrame( + imgIContainer::FRAME_FIRST, + imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY); + + if (!surface || surface->GetFormat() != SurfaceFormat::B8G8R8A8) { + return NS_ERROR_FAILURE; + } + + // Allocate a new buffer that we own. + RefPtr dataSurface = surface->GetDataSurface(); + UniquePtr data = SurfaceToPackedRGBA(dataSurface); + if (!data) { + return NS_ERROR_OUT_OF_MEMORY; + } + + mSearchResult->SetHistoryIcon(mTimeStamp, std::move(data), + surface->GetSize().width, + surface->GetSize().height, mIconIndex); + return NS_OK; +} + DBusHandlerResult nsGNOMEShellSearchProvider::HandleSearchResultSet( DBusMessage* aMsg, bool aInitialSearch) { // Discard any existing search results. @@ -179,7 +281,7 @@ static void DispatchSearchResults( RefPtr aSearchResult, nsCOMPtr aHistResultContainer) { - aSearchResult->SetSearchResultContainer(aHistResultContainer); + aSearchResult->ReceiveSearchResultContainer(aHistResultContainer); } nsresult nsGNOMEShellHistoryService::QueryHistory( @@ -242,7 +344,7 @@ aIDKey = nsPrintfCString("%.2d:%s", aIndex, aUri.get()); } -void nsGNOMEShellHistorySearchResult::SendDBusSearchResultReply() { +void nsGNOMEShellHistorySearchResult::HandleSearchResultReply() { MOZ_ASSERT(mReply); uint32_t childCount = 0; @@ -254,6 +356,11 @@ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &iterArray); if (NS_SUCCEEDED(rv) && childCount > 0) { + // Obtain the favicon service and get the favicon for the specified page + nsCOMPtr favIconSvc( + do_GetService("@mozilla.org/browser/favicon-service;1")); + nsCOMPtr ios(do_GetService(NS_IOSERVICE_CONTRACTID)); + if (childCount > MAX_SEARCH_RESULTS_NUM) { childCount = MAX_SEARCH_RESULTS_NUM; } @@ -271,6 +378,12 @@ nsAutoCString uri; child->GetUri(uri); + nsCOMPtr iconIri; + ios->NewURI(uri, nullptr, nullptr, getter_AddRefs(iconIri)); + nsCOMPtr callback = + new AsyncFaviconDataReady(this, i, mTimeStamp); + favIconSvc->GetFaviconDataForPage(iconIri, callback, 0); + nsAutoCString idKey; DBusGetIDKeyForURI(i, uri, idKey); @@ -292,14 +405,36 @@ mReply = nullptr; } -void nsGNOMEShellHistorySearchResult::SetSearchResultContainer( +void nsGNOMEShellHistorySearchResult::ReceiveSearchResultContainer( nsCOMPtr aHistResultContainer) { + // Propagate search results to nsGNOMEShellSearchProvider. + // SetSearchResult() checks this is up-to-date search (our time stamp matches + // latest requested search timestamp). if (mSearchProvider->SetSearchResult(this)) { mHistResultContainer = aHistResultContainer; - SendDBusSearchResultReply(); + HandleSearchResultReply(); } } +void nsGNOMEShellHistorySearchResult::SetHistoryIcon(int aTimeStamp, + UniquePtr aData, + int aWidth, int aHeight, + int aIconIndex) { + MOZ_ASSERT(mTimeStamp == aTimeStamp); + MOZ_RELEASE_ASSERT(aIconIndex < MAX_SEARCH_RESULTS_NUM); + mHistoryIcons[aIconIndex].Set(mTimeStamp, std::move(aData), aWidth, aHeight); +} + +GnomeHistoryIcon* nsGNOMEShellHistorySearchResult::GetHistoryIcon( + int aIconIndex) { + MOZ_RELEASE_ASSERT(aIconIndex < MAX_SEARCH_RESULTS_NUM); + if (mHistoryIcons[aIconIndex].GetTimeStamp() == mTimeStamp && + mHistoryIcons[aIconIndex].IsLoaded()) { + return mHistoryIcons + aIconIndex; + } + return nullptr; +} + nsGNOMEShellHistoryService* GetGNOMEShellHistoryService() { static nsGNOMEShellHistoryService gGNOMEShellHistoryService; return &gGNOMEShellHistoryService;