Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions WebView2/Extension.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
** Copyright (C) 2025 nstechbytes. All rights reserved.
*/

#include "Extension.h"
#include "Plugin.h"
#include "../API/RainmeterAPI.h"
#include <sstream>

bool g_extensions_checked = false;

std::vector<std::wstring> GetExtensionsID(const std::wstring& input)
{
std::vector<std::wstring> result;
std::wstringstream ss(input);
std::wstring token;

while (std::getline(ss, token, L',')) {
token.erase(0, token.find_first_not_of(L" \t"));
token.erase(token.find_last_not_of(L" \t") + 1);

if (!token.empty()) {
result.push_back(token);
}
}
return result;
}

void EnableExtension(ICoreWebView2BrowserExtension* extension, BOOL enable)
{
extension->Enable(
enable,
Callback<ICoreWebView2BrowserExtensionEnableCompletedHandler>(
[](HRESULT hr) -> HRESULT
{
if (FAILED(hr))
ShowFailure(hr, L"Enable extension failed");
return S_OK;
}).Get());
}

void RemoveExtension(void* rm, ICoreWebView2BrowserExtension* extension, const std::wstring& name)
{
extension->Remove(
Callback<ICoreWebView2BrowserExtensionRemoveCompletedHandler>(
[](HRESULT hr) -> HRESULT
{
if (FAILED(hr))
ShowFailure(hr, L"Uninstall extension failed");
return S_OK;
}).Get());

RmLogF(rm, LOG_DEBUG, L"WebView2: \"%s\" extension removed.", name.c_str());
}
18 changes: 18 additions & 0 deletions WebView2/Extension.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
** Copyright (C) 2025 nstechbytes. All rights reserved.
*/

#pragma once

#include <Windows.h>
#include <WebView2.h>
#include <string>
#include <vector>

// Global flag for extension checking
extern bool g_extensions_checked;

// Extension utility functions
std::vector<std::wstring> GetExtensionsID(const std::wstring& input);
void EnableExtension(ICoreWebView2BrowserExtension* extension, BOOL enable);
void RemoveExtension(void* rm, ICoreWebView2BrowserExtension* extension, const std::wstring& name);
4 changes: 3 additions & 1 deletion WebView2/HostObject.idl
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// Copyright (C) 2025 nstechbytes. All rights reserved.
/*
** Copyright (C) 2025 nstechbytes. All rights reserved.
*/

import "oaidl.idl";
import "ocidl.idl";
Expand Down
4 changes: 2 additions & 2 deletions WebView2/HostObjectRmAPI.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2025 nstechbytes. All rights reserved.
/*
** Copyright (C) 2025 nstechbytes. All rights reserved.
*/

#include "HostObjectRmAPI.h"
Expand Down
6 changes: 3 additions & 3 deletions WebView2/HostObjectRmAPI.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Copyright (C) 2025 nstechbytes. All rights reserved.
*/
/*
** Copyright (C) 2025 nstechbytes. All rights reserved.
*/

#pragma once
#include "HostObject_h.h"
Expand Down
172 changes: 172 additions & 0 deletions WebView2/PathUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
** Copyright (C) 2025 nstechbytes. All rights reserved.
*/

#include "PathUtils.h"
#include "Utils.h"
#include "../API/RainmeterAPI.h"
#include <vector>

// Path and URI utilities
std::wstring NormalizeUri(const std::wstring& uri)
{
const std::wstring scheme_sep = L"://";
auto scheme_pos = uri.find(scheme_sep);
if (scheme_pos == std::wstring::npos)
return uri;

const std::wstring scheme = uri.substr(0, scheme_pos);
const size_t after_scheme = scheme_pos + scheme_sep.length();

if (scheme == L"file")
{
size_t last_slash = uri.find_last_of(L'/');
if (last_slash != std::wstring::npos)
{
return uri.substr(0, last_slash + 1);
}
return uri;
}

size_t path_start = uri.find(L'/', after_scheme);
if (path_start == std::wstring::npos)
{
return uri + L"/";
}

return uri.substr(0, path_start + 1);
}

std::wstring NormalizePath(void* rm, LPCWSTR path)
{
if (!path || !*path)
return {};

std::wstring value = path;

// Relative path - absolute
if (value[0] != L'/' && (value.length() < 2 || value[1] != L':'))
{
if (LPCWSTR absolutePath = RmPathToAbsolute(rm, value.c_str()))
{
value = absolutePath;
}
}

// Normalize slashes
for (wchar_t& ch : value)
{
if (ch == L'\\') ch = L'/';
}

// Enforce extension if required
auto dotPos = value.find_last_of(L'.');
if (dotPos == std::wstring::npos)
{
RmLog(rm, LOG_ERROR, L"Execute: File extension is missing, use '.js'.");
return {};
}

std::wstring extension = value.substr(dotPos);
for (wchar_t& ch : extension)
ch = towlower(ch);

if (_wcsicmp(extension.c_str(), L".js") != 0)
{
std::wstring msg = L"Execute: The file extension '";
msg.append(extension);
msg += L"' is not supported. Use '.js' extension.";
RmLog(rm, LOG_ERROR, msg.c_str());
return {};
}
return value;
}

bool IsFilePathSyntax(LPCWSTR input)
{
if (!input || input[0] == L'\0') return false;

bool hasExtension = false;
bool hasSeparator = false;

const wchar_t* lastDot = nullptr;
const wchar_t* p = input;

while (*p) {
if (wcschr(L"<>:\"|?*();'", *p)) {
if (!(*p == L':' && p == input + 1)) {
return false;
}
}

if (*p == L'\\' || *p == L'/') {
hasSeparator = true;
lastDot = nullptr;
}
else if (*p == L'.') {
lastDot = p;
}
p++;
}

if (lastDot != nullptr && lastDot != input) {
if (!iswspace(*(lastDot + 1)) && *(lastDot + 1) != L'\0') {
hasExtension = true;
}
}

bool hasSpace = (wcschr(input, L' ') != nullptr);

if (hasSpace && !hasSeparator) {
return false;
}

return hasSeparator || hasExtension;
}

// File reading utilities
std::wstring ReadScriptFile(const std::wstring& path)
{
std::ifstream is(path, std::ios::binary);
if (!is) {
throw std::runtime_error("Failed to open file");
}

// Read all bytes
std::vector<char> bytes((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
size_t n = bytes.size();
if (n == 0) return std::wstring();

const unsigned char* ub = reinterpret_cast<const unsigned char*>(bytes.data());

// Detect BOMs
if (n >= 3 && ub[0] == 0xEFu && ub[1] == 0xBBu && ub[2] == 0xBFu) {
// UTF-8 with BOM -> skip BOM then convert
return Utf8ToWstring(reinterpret_cast<const char*>(ub + 3), static_cast<int>(n - 3));
}

if (n >= 2 && ub[0] == 0xFFu && ub[1] == 0xFEu) {
// UTF-16 LE with BOM
std::wstring out;
out.reserve((n - 2) / 2);
for (size_t i = 2; i + 1 < n; i += 2) {
wchar_t ch = static_cast<wchar_t>(ub[i] | (ub[i + 1] << 8));
out.push_back(ch);
}
return out;
}

if (n >= 2 && ub[0] == 0xFEu && ub[1] == 0xFFu) {
// UTF-16 BE with BOM -> swap bytes
std::wstring out;
out.reserve((n - 2) / 2);
for (size_t i = 2; i + 1 < n; i += 2) {
wchar_t ch = static_cast<wchar_t>((ub[i] << 8) | ub[i + 1]);
out.push_back(ch);
}
return out;
}

// No BOM: assume UTF-8 and convert
return Utf8ToWstring(reinterpret_cast<const char*>(ub), static_cast<int>(n));
}
17 changes: 17 additions & 0 deletions WebView2/PathUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
** Copyright (C) 2025 nstechbytes. All rights reserved.
*/

#pragma once

#include <Windows.h>
#include <string>
#include <fstream>

// Path and URI utilities
std::wstring NormalizeUri(const std::wstring& uri);
std::wstring NormalizePath(void* rm, LPCWSTR path);
bool IsFilePathSyntax(LPCWSTR input);

// File reading utilities
std::wstring ReadScriptFile(const std::wstring& path);
Loading
Loading