changeset: 504680:441b26f2d4f4 parent: 504674:5a55ac856fc4 user: Martin Stransky date: Mon Dec 02 12:21:08 2019 +0100 files: browser/components/shell/nsGNOMEShellSearchProvider.cpp browser/components/shell/nsGNOMEShellSearchProvider.h description: Bug 1239694 Use history icons with Gnome shell search provider, r?jhorak Differential Revision: https://phabricator.services.mozilla.com/D55434 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 @@ -19,21 +19,27 @@ #include "nsPrintfCString.h" #include "nsCOMPtr.h" #include "nsGTKToolkit.h" #include "nsINavHistoryService.h" #include "nsToolkitCompsCID.h" #include "nsIFaviconService.h" #include "RemoteUtils.h" #include "nsIStringBundle.h" +#include "imgIContainer.h" +#include "imgITools.h" + +#include "mozilla/gfx/DataSurfaceHelpers.h" #include #include -#define MAX_SEARCH_RESULTS_NUM 9 +using namespace mozilla; +using namespace mozilla::gfx; + #define KEYWORD_SEARCH_STRING "special:search" #define KEYWORD_SEARCH_STRING_LEN 14 #define DBUS_BUS_NAME "org.mozilla.Firefox.SearchProvider" #define DBUS_OBJECT_PATH "/org/mozilla/Firefox/SearchProvider" static const char* introspect_template = " aData, + int aWidth, int aHeight, + int aIconIndex) { + MOZ_ASSERT(mSearchSerial == aSearchSerial); + MOZ_ASSERT(aIconIndex < MAX_SEARCH_RESULTS_NUM); + mHistoryIcons[aIconIndex].Set(aSearchSerial, std::move(aData), aWidth, + aHeight); +} + +nsresult nsGNOMEShellSearchProvider::NewHistorySearch(const char* aSearchTerm) { + // Initialize new search which invalidates all preview ones + mSearchSerial++; + nsresult rv; nsCOMPtr histQuery; rv = mHistoryService->GetNewQuery(getter_AddRefs(histQuery)); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString searchTerm(aSearchTerm); rv = histQuery->SetSearchTerms(NS_ConvertUTF8toUTF16(searchTerm)); NS_ENSURE_SUCCESS(rv, rv); @@ -165,24 +189,123 @@ void nsGNOMEShellSearchProvider::GetIDKe int nsGNOMEShellSearchProvider::GetIndexFromIDKey(const char* aIDKey) { // ID is NN:URL where NN is index to our current history // result container. char tmp[] = {aIDKey[0], aIDKey[1], '\0'}; return atoi(tmp); } +class AsyncFaviconDataReady final : public nsIFaviconDataCallback { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIFAVICONDATACALLBACK + + AsyncFaviconDataReady(nsGNOMEShellSearchProvider* aSearchProvider, + int aIconIndex, int aSearchSerial) + : mSearchProvider(aSearchProvider), + mIconIndex(aIconIndex), + mSearchSerial(aSearchSerial){}; + + private: + ~AsyncFaviconDataReady() {} + + nsGNOMEShellSearchProvider* mSearchProvider; + int mIconIndex; + int mSearchSerial; +}; + +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 (mSearchSerial != mSearchProvider->GetSearchSerial() || !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; + } + + mSearchProvider->SetHistoryIcon(mSearchSerial, std::move(data), + surface->GetSize().width, + surface->GetSize().height, mIconIndex); + return NS_OK; +} + void nsGNOMEShellSearchProvider::ComposeSearchResultReply( DBusMessage* reply, const char* aSearchTerm) { uint32_t childCount = 0; nsresult rv = mHistResultContainer->GetChildCount(&childCount); if (NS_FAILED(rv) || childCount == 0) { return; } + // 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; } DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); DBusMessageIter iterArray; dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &iterArray); @@ -195,16 +318,22 @@ void nsGNOMEShellSearchProvider::Compose } if (!IsHistoryResultNodeURI(child)) { continue; } nsAutoCString uri; child->GetUri(uri); + nsCOMPtr iconIri; + ios->NewURI(uri, nullptr, nullptr, getter_AddRefs(iconIri)); + nsCOMPtr callback = + new AsyncFaviconDataReady(this, i, mSearchSerial); + favIconSvc->GetFaviconDataForPage(iconIri, callback, 0); + nsAutoCString idKey; GetIDKeyForURI(i, uri, idKey); const char* id = idKey.get(); dbus_message_iter_append_basic(&iterArray, DBUS_TYPE_STRING, &id); } nsPrintfCString searchString("%s:%s", KEYWORD_SEARCH_STRING, aSearchTerm); @@ -221,17 +350,17 @@ DBusHandlerResult nsGNOMEShellSearchProv int elements; if (!dbus_message_get_args(aMsg, nullptr, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &stringArray, &elements, DBUS_TYPE_INVALID) || elements == 0) { reply = dbus_message_new_error(aMsg, DBUS_BUS_NAME, "Wrong argument"); } else { reply = dbus_message_new_method_return(aMsg); - nsresult rv = QueryHistory(stringArray[0]); + nsresult rv = NewHistorySearch(stringArray[0]); if (NS_SUCCEEDED(rv)) { ComposeSearchResultReply(reply, stringArray[0]); } dbus_free_string_array(stringArray); } dbus_connection_send(mConnection, reply, nullptr); dbus_message_unref(reply); @@ -249,17 +378,17 @@ DBusHandlerResult nsGNOMEShellSearchProv if (!dbus_message_get_args(aMsg, nullptr, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &unusedArray, &unusedNum, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &stringArray, &elements, DBUS_TYPE_INVALID) || elements == 0) { reply = dbus_message_new_error(aMsg, DBUS_BUS_NAME, "Wrong argument"); } else { reply = dbus_message_new_method_return(aMsg); - nsresult rv = QueryHistory(stringArray[0]); + nsresult rv = NewHistorySearch(stringArray[0]); if (NS_SUCCEEDED(rv)) { ComposeSearchResultReply(reply, stringArray[0]); } } if (unusedArray) { dbus_free_string_array(unusedArray); } @@ -280,45 +409,88 @@ static void appendStringDictionary(DBusM &iterDict); dbus_message_iter_append_basic(&iterDict, DBUS_TYPE_STRING, &aKey); dbus_message_iter_open_container(&iterDict, DBUS_TYPE_VARIANT, "s", &iterVar); dbus_message_iter_append_basic(&iterVar, DBUS_TYPE_STRING, &aValue); dbus_message_iter_close_container(&iterDict, &iterVar); 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 +*/ +void GnomeHistoryIcon::AppendIcon(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); + + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &mWidth); + dbus_message_iter_append_basic(&iterStruct, DBUS_TYPE_INT32, &mHeight); + int rowstride = mWidth * 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 = mData.get(); + dbus_message_iter_append_fixed_array(&iterArray, DBUS_TYPE_BYTE, &array, + mWidth * mHeight * 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); +} + /* We can return those fields at GetResultMetas: "id": the result ID "name": the display name for the result "icon": a serialized GIcon (see g_icon_serialize()), or alternatively, "gicon": a textual representation of a GIcon (see g_icon_to_string()), or alternativly, "icon-data": a tuple of type (iiibiiay) describing a pixbuf with width, height, rowstride, has-alpha, bits-per-sample, and image data "description": an optional short description (1-2 lines) */ void nsGNOMEShellSearchProvider::AppendResultID(DBusMessageIter* aIter, const char* aID) { + int index = GetIndexFromIDKey(aID); nsCOMPtr child; - mHistResultContainer->GetChild(GetIndexFromIDKey(aID), getter_AddRefs(child)); + mHistResultContainer->GetChild(index, getter_AddRefs(child)); nsAutoCString title; - if (NS_FAILED(child->GetTitle(title))) { - return; - } + nsAutoCString uri; + child->GetTitle(title); + child->GetUri(uri); - if (title.IsEmpty()) { - if (NS_FAILED(child->GetUri(title)) || title.IsEmpty()) { - return; - } - } + const char* titleStr = !(title.IsEmpty()) ? title.get() : uri.get(); + const char* descStr = uri.get(); - const char* titleStr = title.get(); appendStringDictionary(aIter, "id", aID); appendStringDictionary(aIter, "name", titleStr); - appendStringDictionary(aIter, "gicon", "text-html"); + appendStringDictionary(aIter, "description", descStr); + if (mHistoryIcons[index].GetSearchSerial() == mSearchSerial) { + mHistoryIcons[index].AppendIcon(aIter); + } else { + appendStringDictionary(aIter, "gicon", "text-html"); + } } void nsGNOMEShellSearchProvider::AppendSearchID(DBusMessageIter* aIter, const char* aID) { if (strlen(aID) < KEYWORD_SEARCH_STRING_LEN + 2) { return; } appendStringDictionary(aIter, "id", KEYWORD_SEARCH_STRING); 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 @@ -7,49 +7,81 @@ #ifndef __nsGNOMEShellSearchProvider_h__ #define __nsGNOMEShellSearchProvider_h__ #include "mozilla/DBusHelpers.h" #include "nsINavHistoryService.h" #include "nsUnixRemoteServer.h" #include "nsCOMPtr.h" +#include "mozilla/UniquePtr.h" + +#define MAX_SEARCH_RESULTS_NUM 9 + +class GnomeHistoryIcon { + public: + GnomeHistoryIcon() : mSearchSerial(-1), mWidth(0), mHeight(0){}; + + // From which search is this icon + void Set(int aSearchSerial, mozilla::UniquePtr aData, int aWidth, + int aHeight) { + mSearchSerial = aSearchSerial; + mWidth = aWidth; + mHeight = aHeight; + mData = std::move(aData); + } + + int GetSearchSerial() { return mSearchSerial; } + void AppendIcon(DBusMessageIter* aIter); + + private: + int mSearchSerial; + mozilla::UniquePtr mData; + int mWidth; + int mHeight; +}; class nsGNOMEShellSearchProvider : public nsUnixRemoteServer { public: - nsGNOMEShellSearchProvider() : mConnection(nullptr) {} + nsGNOMEShellSearchProvider(); ~nsGNOMEShellSearchProvider() { Shutdown(); } nsresult Startup(); void Shutdown(); DBusHandlerResult HandleDBusMessage(DBusConnection* aConnection, DBusMessage* msg); void UnregisterDBusInterface(DBusConnection* aConnection); + int GetSearchSerial() { return mSearchSerial; } + void SetHistoryIcon(int aSearchSerial, mozilla::UniquePtr aData, + int aWidth, int aHeight, int aIconIndex); + private: DBusHandlerResult Introspect(DBusMessage* msg); DBusHandlerResult GetInitialResultSet(DBusMessage* msg); DBusHandlerResult GetSubsearchResultSet(DBusMessage* msg); DBusHandlerResult GetResultMetas(DBusMessage* msg); DBusHandlerResult ActivateResult(DBusMessage* msg); DBusHandlerResult LaunchSearch(DBusMessage* msg); - nsresult QueryHistory(const char* aSearchTerm); + nsresult NewHistorySearch(const char* aSearchTerm); void GetIDKeyForURI(int aIndex, nsAutoCString& aUri, nsAutoCString& aIDKey); int GetIndexFromIDKey(const char* aIDKey); bool IsHistoryResultNodeURI(nsINavHistoryResultNode* aHistoryNode); void AppendResultID(DBusMessageIter* aIter, const char* aID); void AppendSearchID(DBusMessageIter* aIter, const char* aID); void ComposeSearchResultReply(DBusMessage* aReply, const char* aSearchTerm); void LaunchWithID(const char* aID, uint32_t aTimeStamp); void LaunchWithAllResults(uint32_t aTimeStamp); // The connection is owned by DBus library RefPtr mConnection; nsCOMPtr mHistResultContainer; nsCOMPtr mHistoryService; nsAutoCStringN<32> mSearchTerm; nsAutoCString mGnomeSearchTitle; + int mSearchSerial; + GnomeHistoryIcon mHistoryIcons[MAX_SEARCH_RESULTS_NUM]; }; #endif // __nsGNOMEShellSearchProvider_h__