Skip to content

Commit 063b560

Browse files
Release build 4.61.0 [ci release]
1 parent 528f0c3 commit 063b560

File tree

14 files changed

+453
-9
lines changed

14 files changed

+453
-9
lines changed

README.md

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,16 @@ In the built output you will see these dramatic differences in the bundled code
7070
To handle the difference in scope injection we expose multiple utilities which behave differently per browser in src/utils.js and `ContentFeature` base class. for Firefox the code exposed handles [xrays correctly](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Sharing_objects_with_page_scripts) without needing the features to be authored differently.
7171

7272
- `ContentFeature.defineProperty()`
73-
- defineProperty(object, propertyName, descriptor) behaves the same as Object.defineProperty(object, propertyName, descriptor)
73+
- `defineProperty(object, propertyName, descriptor)` behaves the same as `Object.defineProperty(object, propertyName, descriptor)`
7474
- The difference is for Firefox we export the relevant functions so it can go across the xray
75+
- Use this method if `Object.getOwnPropertyDescriptors(object).propertyName` should to exist in the supporting browser.
7576
- `ContentFeature.wrapProperty(object, propertyName, descriptor)`
76-
- a simple wrapper around defineProperty() that ignores non-existing properties and retains unspecified descriptor keys.
77+
- A simple wrapper around `defineProperty()` that ignores non-existing properties and retains unspecified descriptor keys.
7778
- Example usage: `this.wrapProperty('Navigator.prototype.userAgent', { get: () => 'fakeUA' })`
7879
- `ContentFeature.wrapMethod(object, propertyName, wrapperFn)`
79-
- overrides a native method. wrapperFn() will be called in place of the original method. The original method will be passed as the first argument.
80+
- Overrides a native method. wrapperFn() will be called in place of the original method. The original method will be passed as the first argument.
8081
- Example usage:
81-
```
82+
```JavaScript
8283
this.wrapMethod(Permissions.prototype, 'query', async function (originalFn, queryObject) {
8384
if (queryObject.name === 'blocked-permission') {
8485
return {
@@ -90,12 +91,25 @@ To handle the difference in scope injection we expose multiple utilities which b
9091
return await nativeImpl.call(this, queryObject)
9192
})
9293
```
93-
94-
- DDGProxy
95-
- Behaves a lot like new window.Proxy with a few differences:
96-
- has an overload function to actually apply the function to the native property.
94+
- `DDGProxy`
95+
- Behaves a lot like `new window.Proxy` with a few differences:
96+
- has an `overload` method to actually apply the function to the native property.
9797
- Stores the native original property in _native such that it can be called elsewhere if needed without going through the proxy.
98-
- DDGReflect
98+
- Triggers `addDebugFlag` if get/apply is called.
99+
- Sends debugging messaging if debug is enabled.
100+
- Allows for remotely disabling the override based on script URL via `shouldExemptMethod`.
101+
- Fixes `value.toString()` to appear like it was defined natively.
102+
- Example usage:
103+
```JavaScript
104+
const historyMethodProxy = new DDGProxy(this, History.prototype, 'pushState', {
105+
apply (target, thisArg, args) {
106+
applyRules(activeRules)
107+
return DDGReflect.apply(target, thisArg, args)
108+
}
109+
})
110+
historyMethodProxy.overload()
111+
```
112+
- `DDGReflect`
99113
- Calls into wrappedJSObject.Reflect for Firefox but otherwise exactly the same as [window.Reflect](Sources/BrowserServicesKit/UserScript/ContentScopeUserScript.swift)
100114

101115
### Testing Locally
Lines changed: 15 additions & 0 deletions
Loading
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport"
6+
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
7+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
8+
<style>
9+
body {
10+
font-family:'-apple-system-font';
11+
background: rgb(255, 255, 255);
12+
13+
display: flex;
14+
height: 100vh;
15+
margin: 0 auto;
16+
17+
text-align: center;
18+
align-items: center;
19+
20+
cursor: default;
21+
}
22+
23+
.content-container {
24+
min-width: 320px;
25+
max-width: 580px;
26+
margin: 0 auto;
27+
}
28+
29+
.error-header {
30+
font-size: 14px;
31+
font-weight: 600;
32+
line-height: 18px;
33+
margin: 0 auto;
34+
padding: 0px;
35+
}
36+
37+
.error-description {
38+
font-size: 13px;
39+
line-height: 16px;
40+
margin: 0 auto;
41+
padding: 8px;
42+
}
43+
44+
.header-container .description-container {
45+
color: rgb(44, 44, 44);
46+
position: relative;
47+
width: 100%;
48+
word-wrap: break-word;
49+
}
50+
51+
.watermark-container {
52+
display: inline-block;
53+
position: fixed;
54+
width: 100px;
55+
height: 100px;
56+
bottom: 20px;
57+
right: 20px;
58+
opacity: 0.5;
59+
filter: grayscale(100%);
60+
pointer-events: none;
61+
}
62+
63+
.watermark {
64+
max-width: 100px;
65+
max-height: 100px;
66+
}
67+
68+
@media (prefers-color-scheme: dark) {
69+
body {
70+
background: rgb(51, 51, 51);
71+
}
72+
73+
.error-description, .error-header {
74+
color: rgb(210, 210, 210);
75+
}
76+
}
77+
78+
</style>
79+
</head>
80+
<body>
81+
<div class="content-container">
82+
<div class="error-container">
83+
<div class="header-container">
84+
<h1 class="error-header">$HEADER$</h1>
85+
</div>
86+
<div class="description-container">
87+
<p class="error-description">$ERROR_DESCRIPTION$</p>
88+
</div>
89+
</div>
90+
<div class="watermark-container">
91+
<img alt="DuckDuckGo" class="watermark" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjU2IDUxMkMzOTcuMzg1IDUxMiA1MTIgMzk3LjM4NSA1MTIgMjU2QzUxMiAxMTQuNjE1IDM5Ny4zODUgMCAyNTYgMEMxMTQuNjE1IDAgMCAxMTQuNjE1IDAgMjU2QzAgMzk3LjM4NSAxMTQuNjE1IDUxMiAyNTYgNTEyWiIgZmlsbD0iI0RFNTgzMyIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTI5MiA0NDdDMjkyIDQ0NSAyOTIuNDkxIDQ0NC41NDQgMjg2LjEzMyA0MzEuODcyQzI2OS4yMzcgMzk4LjAzNyAyNTIuMjU2IDM1MC4zMzYgMjU5Ljk3OSAzMTkuNTczQzI2MS4zODcgMzEzLjk4NCAyNDQuMDY0IDExMi41OTcgMjMxLjgxOSAxMDYuMTEyQzIxOC4yMDggOTguODU4NCAyMDEuNDYyIDg3LjM1MjMgMTg2LjE0NCA4NC43OTIzQzE3OC4zNzMgODMuNTQ4OSAxNjguMTgzIDg0LjEzNzggMTYwLjIxNyA4NS4yMTA2QzE1OC44MDEgODUuNDAxMyAxNTguNzQzIDg3Ljk0NiAxNjAuMDk2IDg4LjQwNDRDMTY1LjMyNiA5MC4xNzYyIDE3MS42NzYgOTMuMjUyNSAxNzUuNDE4IDk3LjkwMzhDMTc2LjEyNyA5OC43ODQyIDE3NS4xNzYgMTAwLjE2OSAxNzQuMDQ3IDEwMC4yMTFDMTcwLjUxOSAxMDAuMzQxIDE2NC4xMTkgMTAxLjgyIDE1NS42NzYgMTA4Ljk5MkMxNTQuNjk5IDEwOS44MjEgMTU1LjUxIDExMS4zNjIgMTU2Ljc2NyAxMTEuMTEzQzE3NC45MTMgMTA3LjUyMyAxOTMuNDQ1IDEwOS4yOTIgMjA0LjM2NyAxMTkuMjE4QzIwNS4wNzUgMTE5Ljg2MyAyMDQuNzA1IDEyMS4wMTggMjAzLjc4MSAxMjEuMjdDMTA5LjAwNCAxNDcuMDI2IDEyNy43NjYgMjI5LjQ3IDE1Mi45OTcgMzMwLjY0NUMxNzUuNDcgNDIwLjc2NCAxODMuOTI4IDQ0OS44MTEgMTg2LjU5NCA0NTguNjZDMTg2Ljg1MiA0NTkuNTE3IDE4Ny40NTUgNDYwLjE5NiAxODguMjg2IDQ2MC41MjdDMjIwLjkxMiA0NzMuNTE5IDI5MiA0NzQuMDk2IDI5MiA0NTJWNDQ3WiIgZmlsbD0iI0RERERERCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTQ5MSAyNTZDNDkxIDM4NS43ODcgMzg1Ljc4NyA0OTEgMjU2IDQ5MUMxMjYuMjEzIDQ5MSAyMSAzODUuNzg3IDIxIDI1NkMyMSAxMjYuMjEzIDEyNi4yMTMgMjEgMjU2IDIxQzM4NS43ODcgMjEgNDkxIDEyNi4yMTMgNDkxIDI1NlpNMjAxLjE1NyA0NjMuOTQyQzE5NC42NjMgNDQzLjg4MiAxNzYuNTE0IDM4NS43MzkgMTU4LjU4OSAzMTIuMjZDMTU3Ljk5MiAzMDkuODEyIDE1Ny4zOTYgMzA3LjM3NSAxNTYuODAyIDMwNC45NTFMMTU2Ljc4NyAzMDQuODlDMTM1LjA5IDIxNi4yNjYgMTE3LjM2OCAxNDMuODggMjE0LjQ5NSAxMjEuMTRDMjE1LjM4MyAxMjAuOTMyIDIxNS44MTggMTE5Ljg3IDIxNS4yMyAxMTkuMTczQzIwNC4wODYgMTA1Ljk1MyAxODMuMjA5IDEwMS42MTkgMTU2LjgxMiAxMTAuNzI2QzE1NS43MjkgMTExLjEgMTU0Ljc4OSAxMTAuMDA4IDE1NS40NjEgMTA5LjA4MUMxNjAuNjM4IDEwMS45NDYgMTcwLjc1MyA5Ni40NjAyIDE3NS43NDcgOTQuMDU1M0MxNzYuNzggOTMuNTU4MiAxNzYuNzE3IDkyLjA0NjkgMTc1LjYyMyA5MS43MDQ2QzE3Mi4zNTcgOTAuNjgyMSAxNjYuNzk1IDg5LjExNzMgMTYwLjUzOSA4OC4xMDc1QzE1OS4wNTkgODcuODY4NiAxNTguOTI1IDg1LjMzMiAxNjAuNDExIDg1LjEzMkMxOTcuODM0IDgwLjA5NzEgMjM2LjkxMyA5MS4zMzI1IDI1Ni41MjMgMTE2LjAzNUMyNTYuNzA4IDExNi4yNjggMjU2Ljk3NSAxMTYuNDMgMjU3LjI2NSAxMTYuNDkyQzMyOS4wNzcgMTMxLjkxNCAzMzQuMjE4IDI0NS40MzIgMzI1Ljk0NiAyNTAuNjAzQzMyNC4zMTUgMjUxLjYyMiAzMTkuMDg4IDI1MS4wMzcgMzEyLjE5NCAyNTAuMjY2QzI4NC4yNTIgMjQ3LjEzOSAyMjguOTIyIDI0MC45NDkgMjc0LjU4OCAzMjYuMDU1QzI3NS4wMzkgMzI2Ljg5NSAyNzQuNDQxIDMyOC4wMDggMjczLjQ5OSAzMjguMTU1QzI0Ny44MzYgMzMyLjE0NSAyODAuNTIgNDEyLjI5MiAzMDQuNzQ5IDQ2NS40NUM0MDAuMDA3IDQ0My4zNjcgNDcxIDM1Ny45NzQgNDcxIDI1NkM0NzEgMTM3LjI1OSAzNzQuNzQxIDQxIDI1NiA0MUMxMzcuMjU5IDQxIDQxIDEzNy4yNTkgNDEgMjU2QzQxIDM1NS43ODcgMTA4Ljk4MSA0MzkuNjk3IDIwMS4xNTcgNDYzLjk0MloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0zMzcuMTIxIDM2Mi43OTJDMzMxLjY1NCAzNjAuMjU5IDMxMC42MzQgMzc1LjMzMyAyOTYuNjgzIDM4Ni45MDVDMjkzLjc2NyAzODIuNzggMjg4LjI2OSAzNzkuNzgxIDI3NS44NjggMzgxLjkzOEMyNjUuMDE0IDM4My44MjUgMjU5LjAyMiAzODYuNDQyIDI1Ni4zNDkgMzkwLjk1NEMyMzkuMjE3IDM4NC40NTggMjEwLjM5NiAzNzQuNDM1IDIwMy40MzQgMzg0LjExNUMxOTUuODIzIDM5NC42OTcgMjA1LjMzNiA0NDQuNzU3IDIxNS40NDQgNDUxLjI1N0MyMjAuNzIyIDQ1NC42NSAyNDUuOTY3IDQzOC40MjQgMjU5LjE1IDQyNy4yMzRDMjYxLjI3NyA0MzAuMjMxIDI2NC43MDEgNDMxLjk0NiAyNzEuNzQgNDMxLjc4M0MyODIuMzg3IDQzMS41MzYgMjk5LjY1NCA0MjkuMDU5IDMwMi4zMzQgNDI0LjFDMzAyLjQ5NyA0MjMuNzk5IDMwMi42MzcgNDIzLjQ0MyAzMDIuNzU3IDQyMy4wMzhDMzE2LjMwNyA0MjguMTAyIDM0MC4xNTQgNDMzLjQ2MiAzNDUuNDgyIDQzMi42NjFDMzU5LjM2NSA0MzAuNTc2IDM0My41NDggMzY1Ljc3IDMzNy4xMjEgMzYyLjc5MloiIGZpbGw9IiMzQ0E4MkIiLz4KPHBhdGggZD0iTTI5Ny45NjEgMzg4LjM4NkMyOTguNTM2IDM4OS40MTEgMjk4Ljk5OCAzOTAuNDkyIDI5OS4zOTEgMzkxLjU5MkMzMDEuMzIyIDM5Ni45OTQgMzA0LjQ3IDQxNC4xOCAzMDIuMDkgNDE4LjQyNUMyOTkuNzEgNDIyLjY3IDI4NC4yNTIgNDI0LjcyIDI3NC43MTYgNDI0Ljg4NUMyNjUuMTgxIDQyNS4wNDkgMjYzLjAzNCA0MjEuNTYyIDI2MS4xMDIgNDE2LjE2QzI1OS41NTcgNDExLjgzOCAyNTguNzk2IDQwMS42NzcgMjU4LjgxNSAzOTUuODU5QzI1OC40MjQgMzg3LjIyOSAyNjEuNTc3IDM4NC4xOTUgMjc2LjE1MSAzODEuODM3QzI4Ni45MzYgMzgwLjA5MSAyOTIuNjM4IDM4Mi4xMjEgMjk1LjkyOSAzODUuNTk1QzMxMS4yNDEgMzc0LjE2NyAzMzYuNzg4IDM1OC4wNCAzMzkuMjggMzYwLjk4N0MzNTEuNzA0IDM3NS42ODQgMzUzLjI3NSA0MTAuNjcgMzUwLjU4NCA0MjQuNzQ1QzM0OS43MDUgNDI5LjM0NyAzMDguNTY1IDQyMC4xODYgMzA4LjU2NSA0MTUuMjI0QzMwOC41NjUgMzk0LjYxNyAzMDMuMjE4IDM4OC45NjQgMjk3Ljk2MSAzODguMzg2WiIgZmlsbD0iIzRDQkEzQyIvPgo8cGF0aCBkPSJNMjA3Ljg0MiAzODEuOTUxQzIxMS4yMTEgMzc2LjYxOCAyMzguNTM2IDM4My4yNSAyNTMuNTM1IDM4OS45MjVDMjUzLjUzNSAzODkuOTI1IDI1MC40NTMgNDAzLjg4OSAyNTUuMzYgNDIwLjM0MUMyNTYuNzk1IDQyNS4xNTMgMjIwLjg1MiA0NDYuNTcyIDIxNi4xNiA0NDIuODg3QzIxMC43MzggNDM4LjYyOCAyMDAuNzU5IDM5My4xNiAyMDcuODQyIDM4MS45NTFaIiBmaWxsPSIjNENCQTNDIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjIxLjA3NiAyNzMuNjI2QzIyMy4yODYgMjY0LjAxMyAyMzMuNTg1IDI0NS44OTggMjcwLjM2IDI0Ni4zMzhDMjg4Ljk1MiAyNDYuMjYgMzEyLjA0NiAyNDYuMzMgMzI3LjM1NyAyNDQuNTkxQzM1MC4xMTQgMjQyLjAwNiAzNjcuMDA2IDIzNi41MDEgMzc4LjI2MiAyMzIuMjExQzM5NC4xODEgMjI2LjEzNyAzOTkuODMxIDIyNy40OTIgNDAxLjgxMSAyMzEuMTI2QzQwMy45ODggMjM1LjExOCA0MDEuNDIzIDI0Mi4wMTMgMzk1Ljg2MSAyNDguMzZDMzg1LjIzNiAyNjAuNDg0IDM2Ni4xMzUgMjY5Ljg3OCAzMzIuNDAxIDI3Mi42NjZDMjk4LjY2NyAyNzUuNDU0IDI3Ni4zMTkgMjY2LjQwNSAyNjYuNjk4IDI4MS4xMzhDMjYyLjU0OSAyODcuNDkyIDI2NS43NTcgMzAyLjQ2OCAyOTguMzggMzA3LjE4NEMzNDIuNDY0IDMxMy41NDYgMzc4LjY2OSAyOTkuNTE4IDM4My4xNDIgMzA3Ljk5QzM4Ny42MTYgMzE2LjQ2MiAzNjEuODQ3IDMzMy43IDMxNy42OTIgMzM0LjA2MUMyNzMuNTM3IDMzNC40MjIgMjQ1Ljk1NyAzMTguNjAxIDIzNi4xNzYgMzEwLjczN0MyMjMuNzY3IDMwMC43NiAyMTguMjE1IDI4Ni4yMDkgMjIxLjA3NiAyNzMuNjI2WiIgZmlsbD0iI0ZGQ0MzMyIvPgo8ZyBvcGFjaXR5PSIwLjgiPgo8cGF0aCBkPSJNMjc3LjMwOCAxNjguNTA4QzI3OS43NyAxNjQuNDc1IDI4NS4yMzIgMTYxLjM2MiAyOTQuMTcgMTYxLjM2MkMzMDMuMTA4IDE2MS4zNjIgMzA3LjMxMiAxNjQuOTE5IDMxMC4yMjMgMTY4Ljg4NUMzMTAuODE1IDE2OS42OTIgMzA5LjkxOCAxNzAuNjQxIDMwOSAxNzAuMjQ0QzMwOC43NzkgMTcwLjE0OSAzMDguNTU1IDE3MC4wNTEgMzA4LjMyOCAxNjkuOTUyQzMwNS4wNTkgMTY4LjUyNCAzMDEuMDQ2IDE2Ni43NzEgMjk0LjE3IDE2Ni42NzNDMjg2LjgxNiAxNjYuNTY5IDI4Mi4xNzkgMTY4LjQxIDI3OS4yNTkgMTY5Ljk5OEMyNzguMjc1IDE3MC41MzMgMjc2LjcyNCAxNjkuNDY0IDI3Ny4zMDggMTY4LjUwOFoiIGZpbGw9IiMxNDMwN0UiLz4KPHBhdGggZD0iTTE3Ni42ODMgMTczLjY2OEMxODUuMzYyIDE3MC4wNDEgMTkyLjE4MiAxNzAuNTEgMTk3LjAwNSAxNzEuNjVDMTk4LjAyIDE3MS44OTEgMTk4LjcyNSAxNzAuNzk4IDE5Ny45MTMgMTcwLjE0M0MxOTQuMTcxIDE2Ny4xMjQgMTg1Ljc5NCAxNjMuMzc2IDE3NC44NjkgMTY3LjQ1QzE2NS4xMjQgMTcxLjA4NCAxNjAuNTMxIDE3OC42MzQgMTYwLjUwMyAxODMuNTk4QzE2MC40OTcgMTg0Ljc2OCAxNjIuOTA0IDE4NC44NjcgMTYzLjUyNiAxODMuODc3QzE2NS4yMDggMTgxLjIwMSAxNjguMDA0IDE3Ny4yOTUgMTc2LjY4MyAxNzMuNjY4WiIgZmlsbD0iIzE0MzA3RSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTMwMS43NjIgMjIzLjY3OUMyOTQuMDg5IDIyMy42NzkgMjg3Ljg2NSAyMTcuNDczIDI4Ny44NjUgMjA5LjgzNEMyODcuODY1IDIwMi4xOTUgMjk0LjA4OSAxOTUuOTg5IDMwMS43NjIgMTk1Ljk4OUMzMDkuNDM0IDE5NS45ODkgMzE1LjY1OCAyMDIuMTk1IDMxNS42NTggMjA5LjgzNEMzMTUuNjU4IDIxNy40NzMgMzA5LjQzNCAyMjMuNjc5IDMwMS43NjIgMjIzLjY3OVpNMzExLjU0OSAyMDUuMjQ4QzMxMS41NDkgMjAzLjI3IDMwOS45MjkgMjAxLjY2NyAzMDcuOTUxIDIwMS42NjdDMzA1Ljk3MyAyMDEuNjY3IDMwNC4zNyAyMDMuMjcgMzA0LjM1MyAyMDUuMjQ4QzMwNC4zNTMgMjA3LjIyNSAzMDUuOTczIDIwOC44MjggMzA3Ljk1MSAyMDguODI4QzMwOS45NDYgMjA4LjgyOCAzMTEuNTQ5IDIwNy4yMjUgMzExLjU0OSAyMDUuMjQ4WiIgZmlsbD0iIzE0MzA3RSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIwOS42OTMgMjE5LjQxNkMyMDkuNjkzIDIyOC4zMzMgMjAyLjQ0NyAyMzUuNTYzIDE5My40OTUgMjM1LjU2M0MxODQuNTYxIDIzNS41NjMgMTc3LjI5OCAyMjguMzMzIDE3Ny4yOTggMjE5LjQxNkMxNzcuMjk4IDIxMC40OTggMTg0LjU2MSAyMDMuMjY5IDE5My40OTUgMjAzLjI2OUMyMDIuNDMgMjAzLjI2OSAyMDkuNjkzIDIxMC40OTggMjA5LjY5MyAyMTkuNDE2Wk0yMDQuOTE5IDIxNC4wNjJDMjA0LjkxOSAyMTEuNzYgMjAzLjA0MyAyMDkuODg1IDIwMC43MjUgMjA5Ljg4NUMxOTguNDIzIDIwOS44ODUgMTk2LjU0NyAyMTEuNzQzIDE5Ni41MyAyMTQuMDYyQzE5Ni41MyAyMTYuMzY0IDE5OC40MDYgMjE4LjIzOSAyMDAuNzI1IDIxOC4yMzlDMjAzLjA0MyAyMTguMjM5IDIwNC45MTkgMjE2LjM2NCAyMDQuOTE5IDIxNC4wNjJaIiBmaWxsPSIjMTQzMDdFIi8+CjwvZz4KPC9zdmc+Cg==" />
92+
</div>
93+
</div>
94+
</body>
95+
</html>

0 commit comments

Comments
 (0)