Skip to content

Commit 44854b3

Browse files
Fix IndexError when editing legacy webapps missing browser metadata
Commit 247f167 added browser restoration during edit but assumed X-WebApp-Browser always exists. Legacy desktop files miss that key, so on_edit_button hits IndexError at line 366, the GTK main loop stalls, and CPU usage spikes. Match by Exec when the name lookup fails, and fall back if nothing fits. Editing now completes and rewrites the desktop file with fresh metadata. Fix #345
1 parent a49ec21 commit 44854b3

File tree

1 file changed

+111
-3
lines changed

1 file changed

+111
-3
lines changed

usr/lib/webapp-manager/webapp-manager.py

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import gettext
55
import locale
66
import os
7+
import shlex
78
import shutil
89
import subprocess
910
import warnings
@@ -23,6 +24,104 @@
2324
# 3. Local application/library specific imports.
2425
from common import _async, idle, WebAppManager, download_favicon, ICONS_DIR, BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_ZEN_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP
2526

27+
28+
def _strip_leading_env(tokens):
29+
"""Remove leading env invocations and KEY=VALUE assignments."""
30+
31+
def _consume_env_opts(index):
32+
while index < len(tokens) and tokens[index].startswith("-"):
33+
option = tokens[index]
34+
index += 1
35+
if option == "--":
36+
return index
37+
if option.startswith("-u") and option not in ("-u", "--unset"):
38+
continue
39+
if option.startswith("-C") and option not in ("-C", "--chdir"):
40+
continue
41+
if option.startswith("-S") and option not in ("-S", "--split-string"):
42+
try:
43+
extra = shlex.split(option[2:])
44+
except ValueError:
45+
extra = []
46+
if extra:
47+
tokens[index:index] = extra
48+
continue
49+
if option in ("-u", "--unset", "-C", "--chdir", "-S", "--split-string") and index < len(tokens):
50+
if option in ("-S", "--split-string"):
51+
try:
52+
extra = shlex.split(tokens[index])
53+
except ValueError:
54+
extra = []
55+
index += 1
56+
if extra:
57+
tokens[index:index] = extra
58+
else:
59+
index += 1
60+
elif option.startswith("--unset="):
61+
continue
62+
elif option.startswith("--chdir="):
63+
continue
64+
elif option.startswith("--split-string="):
65+
try:
66+
extra = shlex.split(option.split("=", 1)[1])
67+
except ValueError:
68+
extra = []
69+
if extra:
70+
tokens[index:index] = extra
71+
continue
72+
while index < len(tokens) and "=" in tokens[index] and not tokens[index].startswith("-"):
73+
index += 1
74+
return index
75+
76+
idx = 0
77+
while idx < len(tokens):
78+
token = tokens[idx]
79+
if token == "env" or os.path.basename(token) == "env":
80+
idx = _consume_env_opts(idx + 1)
81+
continue
82+
if "=" in token and not token.startswith("--") and not token.startswith("-"):
83+
idx += 1
84+
continue
85+
break
86+
return tokens[idx:]
87+
88+
89+
def _extract_exec_binary(exec_line):
90+
"""Return the executable invoked by a desktop Exec line."""
91+
if not exec_line:
92+
return None
93+
try:
94+
tokens = shlex.split(exec_line)
95+
except ValueError:
96+
return None
97+
if not tokens:
98+
return None
99+
tokens = _strip_leading_env(tokens)
100+
if not tokens:
101+
return None
102+
if len(tokens) >= 3 and tokens[1] == "-c" and os.path.basename(tokens[0]) == "sh":
103+
try:
104+
inner_tokens = shlex.split(tokens[2])
105+
except ValueError:
106+
return None
107+
inner_tokens = _strip_leading_env(inner_tokens)
108+
if not inner_tokens:
109+
return None
110+
return inner_tokens[0]
111+
return tokens[0]
112+
113+
114+
def _exec_matches(browser_exec_path, exec_binary):
115+
if not exec_binary:
116+
return False
117+
if browser_exec_path == exec_binary:
118+
return True
119+
if os.path.isabs(browser_exec_path) and os.path.basename(browser_exec_path) == exec_binary:
120+
return True
121+
if os.path.isabs(exec_binary) and os.path.basename(exec_binary) == browser_exec_path:
122+
return True
123+
return False
124+
26125
setproctitle.setproctitle("webapp-manager")
27126

28127
# i18n
@@ -362,9 +461,18 @@ def on_edit_button(self, widget):
362461
self.isolated_switch.set_active(self.selected_webapp.isolate_profile)
363462
self.privatewindow_switch.set_active(self.selected_webapp.privatewindow)
364463

365-
web_browsers = map(lambda i: i[0], self.browser_combo.get_model())
366-
selected_browser_index = [idx for idx, x in enumerate(web_browsers) if x.name == self.selected_webapp.web_browser][0]
367-
self.browser_combo.set_active(selected_browser_index)
464+
web_browsers = list(map(lambda i: i[0], self.browser_combo.get_model()))
465+
matching_indexes = [idx for idx, x in enumerate(web_browsers)
466+
if x.name == self.selected_webapp.web_browser]
467+
if not matching_indexes and self.selected_webapp.exec:
468+
exec_binary = _extract_exec_binary(self.selected_webapp.exec)
469+
if exec_binary:
470+
matching_indexes = [idx for idx, x in enumerate(web_browsers)
471+
if _exec_matches(x.exec_path, exec_binary)]
472+
if matching_indexes:
473+
self.browser_combo.set_active(matching_indexes[0])
474+
elif web_browsers:
475+
self.browser_combo.set_active(0)
368476
self.on_browser_changed(self.selected_webapp)
369477

370478
model = self.category_combo.get_model()

0 commit comments

Comments
 (0)