Added proper Browser Extension support

- Proper Browser Extension support with configuration page
- Map Browser Extension by name: id, this will then be used to add extensions as "addons" in stremio
- Reworked add_ContextMenuRequested to allow for "back" ctx on extension page.
- Added "navigate" to allow for navigation to chrome-extension://
This commit is contained in:
Zarg 2025-02-01 04:09:04 +01:00
parent 26ca7c6368
commit 6a950eec56
5 changed files with 100 additions and 39 deletions

View file

@ -0,0 +1,28 @@
{
"ublock": {
"name": "uBlock Origin",
"logo": "https://lh3.googleusercontent.com/rrgyVBVte7CfjjeTU-rCHDKba7vtq-yn3o8-10p5b6QOj_2VCDAO3VdggV5fUnugbG2eDGPPjoJ9rsiU_tUZBExgLGc=s60",
"description": "uBlock Origin is not just an “ad blocker“, it's a wide-spectrum content blocker with CPU and memory efficiency as a primary feature.",
"types": ["Browser Extension", "other"],
"behaviorHints": {
"configurable": true,
"configurationRequired": false,
"adult": false,
"p2p": false
},
"page": "dashboard.html"
},
"premid": {
"name": "PreMiD",
"logo": "https://premid.app/_ipx/loading_lazy,f_webp,s_450x150/images/logo-wordmark-blue.png",
"description": "PreMiD is a simple, configurable utility that allows you to show what you're doing on the web in your Discord now playing status / Discord Rich Presence ",
"types": ["Browser Extension", "other"],
"behaviorHints": {
"configurable": true,
"configurationRequired": false,
"adult": false,
"p2p": false
},
"page": "popup.html"
}
}

View file

@ -83,6 +83,9 @@ std::wstring g_launchProtocol;
std::atomic<bool> g_isAppReady = false;
std::atomic<bool> g_waitStarted(false);
// Extensions
std::map<std::wstring, std::wstring> g_extensionMap;
// Updater
std::atomic_bool g_updaterRunning = false;
std::filesystem::path g_installerPath;

View file

@ -129,6 +129,9 @@ extern std::wstring g_launchProtocol;
extern std::atomic<bool> g_isAppReady;
extern std::atomic<bool> g_waitStarted;
// Extensions
extern std::map<std::wstring, std::wstring> g_extensionMap;
// Updater
extern std::atomic_bool g_updaterRunning;
extern std::filesystem::path g_installerPath;

View file

@ -219,6 +219,9 @@ void HandleEvent(const std::string &ev, std::vector<std::string> &args)
} else if (ev=="open-external") {
std::wstring uri(args[0].begin(), args[0].end());
ShellExecuteW(nullptr, L"open", uri.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
} else if (ev=="navigate") {
std::wstring uri(args[0].begin(), args[0].end());
g_webview->Navigate(uri.c_str());
} else {
std::cout<<"Unknown event="<<ev<<"\n";
}
@ -243,9 +246,18 @@ void HandleInboundJSON(const std::string &msg)
nlohmann::json root;
root["id"] = 0;
nlohmann::json transportObj;
json extData = {};
if (!g_extensionMap.empty()) {
for (auto& [name, id] : g_extensionMap) {
extData[WStringToUtf8(name)] = WStringToUtf8(id);
}
}
transportObj["properties"] = {
1,
nlohmann::json::array({0, "shellVersion", 0, APP_VERSION}),
nlohmann::json::array({0, "BrowserExtensions", 0, extData}),
};
transportObj["signals"] = {
nlohmann::json::array({0, "handleInboundJSONSignal"}),

View file

@ -291,36 +291,52 @@ static void SetupWebMessageHandler()
g_webview->add_ContextMenuRequested(
Microsoft::WRL::Callback<ICoreWebView2ContextMenuRequestedEventHandler>(
[](ICoreWebView2* sender, ICoreWebView2ContextMenuRequestedEventArgs* args) -> HRESULT {
// Existing variable declarations
wil::com_ptr<ICoreWebView2ContextMenuItemCollection> items;
HRESULT hr = args->get_MenuItems(&items);
if (FAILED(hr) || !items) {
return hr;
}
if (FAILED(hr) || !items) return hr;
#ifdef DEBUG_BUILD
return S_OK; //DEV TOOLS DEBUG ONLY
#endif
// Get current URL
wil::unique_cotaskmem_string currentUri;
sender->get_Source(&currentUri);
std::wstring uri(currentUri.get());
bool isExtensionUrl = uri.starts_with(L"chrome-extension://");
// Get context menu target
wil::com_ptr<ICoreWebView2ContextMenuTarget> target;
hr = args->get_ContextMenuTarget(&target);
BOOL isEditable = FALSE;
if (SUCCEEDED(hr) && target) {
hr = target->get_IsEditable(&isEditable);
}
if (FAILED(hr)) {
return hr;
target->get_IsEditable(&isEditable);
}
UINT count = 0;
items->get_Count(&count);
if (!isEditable) {
while(count > 0) {
// Allow only Back command (ID 33000) for extension URLs
std::set<INT32> allowedCommands = isExtensionUrl ?
std::set<INT32>{33000} :
std::set<INT32>{};
for (UINT i = 0; i < count;) {
wil::com_ptr<ICoreWebView2ContextMenuItem> item;
items->GetValueAtIndex(0, &item);
if(item) {
items->RemoveValueAtIndex(0);
hr = items->GetValueAtIndex(i, &item);
if (FAILED(hr)) {
i++;
continue;
}
INT32 commandId;
item->get_CommandId(&commandId);
if (allowedCommands.find(commandId) == allowedCommands.end()) {
items->RemoveValueAtIndex(i);
items->get_Count(&count);
} else {
i++;
}
items->get_Count(&count);
}
return S_OK;
}
@ -334,38 +350,26 @@ static void SetupWebMessageHandler()
50156 // Select all
};
for (UINT i = 0; i < count; )
{
for (UINT i = 0; i < count;) {
wil::com_ptr<ICoreWebView2ContextMenuItem> item;
hr = items->GetValueAtIndex(i, &item);
if (FAILED(hr) || !item) {
++i;
continue;
}
INT32 commandId = 0;
hr = item->get_CommandId(&commandId);
if (FAILED(hr)) {
++i;
i++;
continue;
}
// If the commandId is not in the allowed list, remove the item
INT32 commandId;
item->get_CommandId(&commandId);
if (allowedCommandIds.find(commandId) == allowedCommandIds.end()) {
hr = items->RemoveValueAtIndex(i);
if (FAILED(hr)) {
std::wcerr << L"Failed to remove item at index " << i << std::endl;
return hr;
}
// After removal, the collection size reduces, so update count and don't increment i
items->RemoveValueAtIndex(i);
items->get_Count(&count);
continue;
} else {
i++;
}
++i;
}
return S_OK;
}
).Get(),
}).Get(),
&contextMenuToken
);
@ -459,13 +463,24 @@ static void SetupExtensions()
try {
for(const auto& entry : std::filesystem::directory_iterator(extensionsRoot)) {
if(entry.is_directory()) {
std::wstring folderName = entry.path().filename().wstring();
HRESULT hr = g_webviewProfile->AddBrowserExtension(
entry.path().wstring().c_str(),
Microsoft::WRL::Callback<ICoreWebView2ProfileAddBrowserExtensionCompletedHandler>(
[extPath=entry.path().wstring()](HRESULT result, ICoreWebView2BrowserExtension* extension)->HRESULT
[folderName](HRESULT result, ICoreWebView2BrowserExtension* extension)->HRESULT
{
if(SUCCEEDED(result)) {
std::wcout<<L"[EXTENSIONS]: Added extension "<<extPath<<std::endl;
if (SUCCEEDED(result) && extension)
{
wil::unique_cotaskmem_string extId;
HRESULT hrId = extension->get_Id(&extId);
if (SUCCEEDED(hrId) && extId)
{
// Store extension ID in the global map
g_extensionMap[folderName] = extId.get();
std::wcout << L"[EXTENSIONS]: " << folderName
<< L" => " << extId.get() << std::endl;
}
std::wcout << L"[EXTENSIONS]: Added extension " << folderName << std::endl;
} else {
std::wstring err = L"[EXTENSIONS]: Failed to add extension => " + std::to_wstring(result);
AppendToCrashLog(err);