Backed out 2 changesets (bug 1830022) for causing multiple failures. CLOSED TREE
[gecko.git] / browser / components / shell / nsGNOMEShellDBusHelper.cpp
blobaf0fd3aea369ccca9ca6a08030d468a61a0d908c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "nsGNOMEShellSearchProvider.h"
10 #include "RemoteUtils.h"
11 #include "nsIStringBundle.h"
12 #include "nsServiceManagerUtils.h"
13 #include "nsPrintfCString.h"
14 #include "mozilla/XREAppData.h"
15 #include "nsAppRunner.h"
16 #include "nsImportModule.h"
17 #include "nsIOpenTabsProvider.h"
19 #define DBUS_BUS_NAME_TEMPLATE "org.mozilla.%s.SearchProvider"
20 #define DBUS_OBJECT_PATH_TEMPLATE "/org/mozilla/%s/SearchProvider"
22 const char* GetDBusBusName() {
23 static const char* name = []() {
24 nsAutoCString appName;
25 gAppData->GetDBusAppName(appName);
26 return ToNewCString(nsPrintfCString(DBUS_BUS_NAME_TEMPLATE,
27 appName.get())); // Intentionally leak
28 }();
29 return name;
32 const char* GetDBusObjectPath() {
33 static const char* path = []() {
34 nsAutoCString appName;
35 gAppData->GetDBusAppName(appName);
36 return ToNewCString(nsPrintfCString(DBUS_OBJECT_PATH_TEMPLATE,
37 appName.get())); // Intentionally leak
38 }();
39 return path;
42 static bool GetGnomeSearchTitle(const char* aSearchedTerm,
43 nsAutoCString& aGnomeSearchTitle) {
44 static nsCOMPtr<nsIStringBundle> bundle;
45 if (!bundle) {
46 nsCOMPtr<nsIStringBundleService> sbs =
47 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
48 if (NS_WARN_IF(!sbs)) {
49 return false;
52 sbs->CreateBundle("chrome://browser/locale/browser.properties",
53 getter_AddRefs(bundle));
54 if (NS_WARN_IF(!bundle)) {
55 return false;
59 AutoTArray<nsString, 1> formatStrings;
60 CopyUTF8toUTF16(nsCString(aSearchedTerm), *formatStrings.AppendElement());
62 nsAutoString gnomeSearchTitle;
63 bundle->FormatStringFromName("gnomeSearchProviderSearchWeb", formatStrings,
64 gnomeSearchTitle);
65 AppendUTF16toUTF8(gnomeSearchTitle, aGnomeSearchTitle);
66 return true;
69 int DBusGetIndexFromIDKey(const char* aIDKey) {
70 // ID is NN:S:URL where NN is index to our current history
71 // result container.
72 char tmp[] = {aIDKey[0], aIDKey[1], '\0'};
73 return atoi(tmp);
76 char DBusGetStateFromIDKey(const char* aIDKey) {
77 // ID is NN:S:URL where NN is index to our current history
78 // result container, and S is the state, which can be 'o'pen or 'h'istory
79 if (std::strlen(aIDKey) > 3) {
80 return aIDKey[3];
82 // Should never happen, but just to avoid any possible segfault, we
83 // default to state 'history'.
84 return 'h';
87 static void ConcatArray(nsACString& aOutputStr, const char** aStringArray) {
88 for (const char** term = aStringArray; *term; term++) {
89 aOutputStr.Append(*term);
90 if (*(term + 1)) {
91 aOutputStr.Append(" ");
96 // GetInitialResultSet :: (as) → (as)
97 // GetSubsearchResultSet :: (as,as) → (as)
98 void DBusHandleResultSet(RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
99 GVariant* aParameters, bool aInitialSearch,
100 GDBusMethodInvocation* aReply) {
101 // Inital search has params (as), any following one has (as,as) and we want
102 // the second string array.
103 fprintf(stderr, "%s\n", g_variant_get_type_string(aParameters));
104 RefPtr<GVariant> variant = dont_AddRef(
105 g_variant_get_child_value(aParameters, aInitialSearch ? 0 : 1));
106 const char** stringArray = g_variant_get_strv(variant, nullptr);
107 if (!stringArray) {
108 g_dbus_method_invocation_return_error(
109 aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
110 return;
113 aSearchResult->SetReply(aReply);
114 nsAutoCString searchTerm;
115 ConcatArray(searchTerm, stringArray);
116 aSearchResult->SetSearchTerm(searchTerm.get());
117 GetGNOMEShellHistoryService()->QueryHistory(aSearchResult);
118 // DBus reply will be send asynchronously by
119 // nsGNOMEShellHistorySearchResult::SendDBusSearchResultReply()
120 // when GetGNOMEShellHistoryService() has the results.
122 g_free(stringArray);
126 "icon-data": a tuple of type (iiibiiay) describing a pixbuf with width,
127 height, rowstride, has-alpha,
128 bits-per-sample, channels,
129 image data
131 static void DBusAppendIcon(GVariantBuilder* aBuilder, GnomeHistoryIcon* aIcon) {
132 GVariantBuilder b;
133 g_variant_builder_init(&b, G_VARIANT_TYPE("(iiibiiay)"));
134 g_variant_builder_add_value(&b, g_variant_new_int32(aIcon->GetWidth()));
135 g_variant_builder_add_value(&b, g_variant_new_int32(aIcon->GetHeight()));
136 g_variant_builder_add_value(&b, g_variant_new_int32(aIcon->GetWidth() * 4));
137 g_variant_builder_add_value(&b, g_variant_new_boolean(true));
138 g_variant_builder_add_value(&b, g_variant_new_int32(8));
139 g_variant_builder_add_value(&b, g_variant_new_int32(4));
140 g_variant_builder_add_value(
141 &b, g_variant_new_fixed_array(G_VARIANT_TYPE("y"), aIcon->GetData(),
142 aIcon->GetWidth() * aIcon->GetHeight() * 4,
143 sizeof(char)));
144 g_variant_builder_add(aBuilder, "{sv}", "icon-data",
145 g_variant_builder_end(&b));
148 /* Appends history search results to the DBUS reply.
150 We can return those fields at GetResultMetas:
152 "id": the result ID
153 "name": the display name for the result
154 "icon": a serialized GIcon (see g_icon_serialize()), or alternatively,
155 "gicon": a textual representation of a GIcon (see g_icon_to_string()),
156 or alternativly,
157 "icon-data": a tuple of type (iiibiiay) describing a pixbuf with width,
158 height, rowstride, has-alpha, bits-per-sample, and image data
159 "description": an optional short description (1-2 lines)
161 static already_AddRefed<GVariant> DBusAppendResultID(
162 nsGNOMEShellHistorySearchResult* aSearchResult, const char* aID) {
163 nsCOMPtr<nsINavHistoryContainerResultNode> container =
164 aSearchResult->GetSearchResultContainer();
166 int index = DBusGetIndexFromIDKey(aID);
167 char state = DBusGetStateFromIDKey(aID);
168 nsCOMPtr<nsINavHistoryResultNode> child;
169 container->GetChild(index, getter_AddRefs(child));
170 nsAutoCString title;
171 if (!child || NS_FAILED(child->GetTitle(title))) {
172 return nullptr;
175 if (title.IsEmpty()) {
176 if (NS_FAILED(child->GetUri(title)) || title.IsEmpty()) {
177 return nullptr;
181 // Check if the URI state is "open tab". If so, mark it with an asterisk to
182 // indicate this to the user.
183 if (state == 'o') {
184 title = "(*) "_ns + title;
187 GVariantBuilder b;
188 g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));
190 const char* titleStr = title.get();
191 g_variant_builder_add(&b, "{sv}", "id", g_variant_new_string(aID));
192 g_variant_builder_add(&b, "{sv}", "name", g_variant_new_string(titleStr));
194 GnomeHistoryIcon* icon = aSearchResult->GetHistoryIcon(index);
195 if (icon) {
196 DBusAppendIcon(&b, icon);
197 } else {
198 g_variant_builder_add(&b, "{sv}", "gicon",
199 g_variant_new_string("text-html"));
201 return dont_AddRef(g_variant_ref_sink(g_variant_builder_end(&b)));
204 // Search the web for: "searchTerm" to the DBUS reply.
205 static already_AddRefed<GVariant> DBusAppendSearchID(const char* aID) {
206 /* aID contains:
208 KEYWORD_SEARCH_STRING:ssssss
210 KEYWORD_SEARCH_STRING is a 'special:search' keyword
211 ssssss is a searched term, must be at least one character long
214 // aID contains only 'KEYWORD_SEARCH_STRING:' so we're missing searched
215 // string.
216 if (strlen(aID) <= KEYWORD_SEARCH_STRING_LEN + 1) {
217 return nullptr;
220 GVariantBuilder b;
221 g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));
222 g_variant_builder_add(&b, "{sv}", "id",
223 g_variant_new_string(KEYWORD_SEARCH_STRING));
225 // Extract ssssss part from aID
226 nsAutoCString searchTerm(aID + KEYWORD_SEARCH_STRING_LEN + 1);
227 nsAutoCString gnomeSearchTitle;
228 if (GetGnomeSearchTitle(searchTerm.get(), gnomeSearchTitle)) {
229 g_variant_builder_add(&b, "{sv}", "name",
230 g_variant_new_string(gnomeSearchTitle.get()));
231 // TODO: When running on flatpak/snap we may need to use
232 // icon like org.mozilla.Firefox or so.
233 g_variant_builder_add(&b, "{sv}", "gicon", g_variant_new_string("firefox"));
236 return dont_AddRef(g_variant_ref_sink(g_variant_builder_end(&b)));
239 // GetResultMetas :: (as) → (aa{sv})
240 void DBusHandleResultMetas(
241 RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
242 GVariant* aParameters, GDBusMethodInvocation* aReply) {
243 RefPtr<GVariant> variant =
244 dont_AddRef(g_variant_get_child_value(aParameters, 0));
245 gsize elements;
246 const char** stringArray = g_variant_get_strv(variant, &elements);
247 if (!stringArray) {
248 g_dbus_method_invocation_return_error(
249 aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
250 return;
253 GVariantBuilder b;
254 g_variant_builder_init(&b, G_VARIANT_TYPE("aa{sv}"));
255 for (gsize i = 0; i < elements; i++) {
256 RefPtr<GVariant> value;
257 if (strncmp(stringArray[i], KEYWORD_SEARCH_STRING,
258 KEYWORD_SEARCH_STRING_LEN) == 0) {
259 value = DBusAppendSearchID(stringArray[i]);
260 } else {
261 value = DBusAppendResultID(aSearchResult, stringArray[i]);
263 if (value) {
264 g_variant_builder_add_value(&b, value);
268 GVariant* v = g_variant_builder_end(&b);
269 g_dbus_method_invocation_return_value(aReply, g_variant_new_tuple(&v, 1));
271 g_free(stringArray);
272 } // namespace mozilla
274 static void ActivateResultID(
275 RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
276 const char* aResultID, uint32_t aTimeStamp) {
277 char* commandLine = nullptr;
278 int len;
280 if (strncmp(aResultID, KEYWORD_SEARCH_STRING, KEYWORD_SEARCH_STRING_LEN) ==
281 0) {
282 const char* urlList[3] = {"unused", "--search",
283 aSearchResult->GetSearchTerm().get()};
284 commandLine =
285 ConstructCommandLine(std::size(urlList), urlList, nullptr, &len);
286 } else {
287 int keyIndex = atoi(aResultID);
288 char state = DBusGetStateFromIDKey(aResultID);
289 nsCOMPtr<nsINavHistoryResultNode> child;
290 aSearchResult->GetSearchResultContainer()->GetChild(keyIndex,
291 getter_AddRefs(child));
292 if (!child) {
293 return;
296 nsAutoCString uri;
297 nsresult rv = child->GetUri(uri);
298 if (NS_FAILED(rv)) {
299 return;
302 // If the state of the URI is 'o'pen, we send it along to JS and let
303 // it switch the tab accordingly
304 if (state == 'o') {
305 // If we can't successfully switch to an open tab, use the existing
306 // 'open in a new tab'-mechanism as a fallback.
307 nsresult rv;
308 nsCOMPtr<nsIOpenTabsProvider> provider = do_ImportESModule(
309 "resource:///modules/OpenTabsProvider.sys.mjs", &rv);
310 if (NS_SUCCEEDED(rv)) {
311 rv = provider->SwitchToOpenTab(uri);
312 if (NS_SUCCEEDED(rv)) {
313 return;
318 const char* urlList[2] = {"unused", uri.get()};
319 commandLine =
320 ConstructCommandLine(std::size(urlList), urlList, nullptr, &len);
323 if (commandLine) {
324 aSearchResult->HandleCommandLine(mozilla::Span(commandLine, len),
325 aTimeStamp);
326 free(commandLine);
330 static void DBusLaunchWithAllResults(
331 RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
332 uint32_t aTimeStamp) {
333 uint32_t childCount = 0;
334 nsresult rv =
335 aSearchResult->GetSearchResultContainer()->GetChildCount(&childCount);
336 if (NS_FAILED(rv) || childCount == 0) {
337 return;
340 if (childCount > MAX_SEARCH_RESULTS_NUM) {
341 childCount = MAX_SEARCH_RESULTS_NUM;
344 // Allocate space for all found results, "unused", "--search" and
345 // potential search request.
346 const char** urlList =
347 (const char**)moz_xmalloc(sizeof(char*) * (childCount + 3));
348 int urlListElements = 0;
350 urlList[urlListElements++] = strdup("unused");
352 for (uint32_t i = 0; i < childCount; i++) {
353 nsCOMPtr<nsINavHistoryResultNode> child;
354 aSearchResult->GetSearchResultContainer()->GetChild(i,
355 getter_AddRefs(child));
357 if (!IsHistoryResultNodeURI(child)) {
358 continue;
361 nsAutoCString uri;
362 nsresult rv = child->GetUri(uri);
363 if (NS_FAILED(rv)) {
364 continue;
366 urlList[urlListElements++] = strdup(uri.get());
369 // When there isn't any uri to open pass search at least.
370 if (!childCount) {
371 urlList[urlListElements++] = strdup("--search");
372 urlList[urlListElements++] = strdup(aSearchResult->GetSearchTerm().get());
375 int len;
376 char* commandLine =
377 ConstructCommandLine(urlListElements, urlList, nullptr, &len);
378 if (commandLine) {
379 aSearchResult->HandleCommandLine(mozilla::Span(commandLine, len),
380 aTimeStamp);
381 free(commandLine);
384 for (int i = 0; i < urlListElements; i++) {
385 free((void*)urlList[i]);
387 free(urlList);
390 // ActivateResult :: (s,as,u) → ()
391 void DBusActivateResult(RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
392 GVariant* aParameters, GDBusMethodInvocation* aReply) {
393 const char* resultID;
395 // aParameters is "(s,as,u)" type
396 RefPtr<GVariant> r = dont_AddRef(g_variant_get_child_value(aParameters, 0));
397 if (!(resultID = g_variant_get_string(r, nullptr))) {
398 g_dbus_method_invocation_return_error(
399 aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
400 return;
402 RefPtr<GVariant> t = dont_AddRef(g_variant_get_child_value(aParameters, 2));
403 uint32_t timestamp = g_variant_get_uint32(t);
405 ActivateResultID(aSearchResult, resultID, timestamp);
406 g_dbus_method_invocation_return_value(aReply, nullptr);
409 // LaunchSearch :: (as,u) → ()
410 void DBusLaunchSearch(RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
411 GVariant* aParameters, GDBusMethodInvocation* aReply) {
412 RefPtr<GVariant> variant =
413 dont_AddRef(g_variant_get_child_value(aParameters, 1));
414 if (!variant) {
415 g_dbus_method_invocation_return_error(
416 aReply, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Wrong params!");
417 return;
419 DBusLaunchWithAllResults(aSearchResult, g_variant_get_uint32(variant));
420 g_dbus_method_invocation_return_value(aReply, nullptr);
423 bool IsHistoryResultNodeURI(nsINavHistoryResultNode* aHistoryNode) {
424 uint32_t type;
425 nsresult rv = aHistoryNode->GetType(&type);
426 if (NS_FAILED(rv) || type != nsINavHistoryResultNode::RESULT_TYPE_URI)
427 return false;
429 nsAutoCString title;
430 rv = aHistoryNode->GetTitle(title);
431 if (NS_SUCCEEDED(rv) && !title.IsEmpty()) {
432 return true;
435 rv = aHistoryNode->GetUri(title);
436 return NS_SUCCEEDED(rv) && !title.IsEmpty();