Skip to content

Commit f51219f

Browse files
Adds @grant to model for userscripts
* Implements GM.getValue / GM.setValue and that's it so far. * Needs some work, but I think from some basic testing seems to be working alright. * Access is scoped to each script.
1 parent 6ed3107 commit f51219f

File tree

6 files changed

+206
-63
lines changed

6 files changed

+206
-63
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ firebase.json
8585
**/ios/Flutter/flutter_assets/
8686
**/ios/ServiceDefinitions.json
8787
**/ios/Runner/GeneratedPluginRegistrant.*
88+
**/ios/Flutter/ephemeral/
8889

8990
# Windows
9091
**/windows/flutter/ephemeral/

lib/models/userscript_model.dart

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
import 'dart:convert';
77
import "package:http/http.dart" as http;
88

9-
UserScriptModel userScriptModelFromJson(String str) => UserScriptModel.fromJson(json.decode(str));
9+
UserScriptModel userScriptModelFromJson(String str) =>
10+
UserScriptModel.fromJson(json.decode(str));
1011

11-
String userScriptModelToJson(UserScriptModel data) => json.encode(data.toJson());
12+
String userScriptModelToJson(UserScriptModel data) =>
13+
json.encode(data.toJson());
1214

1315
enum UserScriptTime { start, end }
1416

@@ -32,7 +34,8 @@ class UserScriptModel {
3234
this.time = UserScriptTime.end,
3335
this.url,
3436
this.updateStatus = UserScriptUpdateStatus.noRemote,
35-
required this.isExample});
37+
required this.isExample,
38+
this.grants = const []});
3639

3740
bool enabled;
3841
List<String> matches;
@@ -44,23 +47,31 @@ class UserScriptModel {
4447
String? url;
4548
UserScriptUpdateStatus updateStatus;
4649
bool isExample;
50+
List<String> grants;
4751

4852
factory UserScriptModel.fromJson(Map<String, dynamic> json) {
4953
// First check if is old model
5054
if (json["exampleCode"] is int) {
5155
final bool enabled = json["enabled"] is bool ? json["enabled"] : true;
5256
final String source = json["source"] is String ? json["source"] : "";
53-
final List<String> matches = json["urls"] is List<dynamic> ? json["urls"].cast<String>() : tryGetMatches(source);
57+
final List<String> matches = json["urls"] is List<dynamic>
58+
? json["urls"].cast<String>()
59+
: tryGetMatches(source);
5460
final String name = json["name"] is String ? json["name"] : "Unknown";
55-
final String version = json["version"] is String ? json["version"] : tryGetVersion(source) ?? "0.0.0";
61+
final String version = json["version"] is String
62+
? json["version"]
63+
: tryGetVersion(source) ?? "0.0.0";
5664
final bool edited = json["edited"] is bool ? json["edited"] : false;
57-
final UserScriptTime time = json["time"] == "start" ? UserScriptTime.start : UserScriptTime.end;
58-
final bool isExample = json["isExample"] ?? (json["exampleCode"] ?? 0) > 0;
65+
final UserScriptTime time =
66+
json["time"] == "start" ? UserScriptTime.start : UserScriptTime.end;
67+
final bool isExample =
68+
json["isExample"] ?? (json["exampleCode"] ?? 0) > 0;
5969
final url = json["url"] is String
6070
? json["url"]
61-
: tryGetUrl(json["source"]) ?? (isExample ? exampleScriptURLs[json["exampleCode"] - 1] : null);
62-
final updateStatus =
63-
UserScriptUpdateStatus.values.byName(json["updateStatus"] ?? (url is String ? "upToDate" : "noRemote"));
71+
: tryGetUrl(json["source"]) ??
72+
(isExample ? exampleScriptURLs[json["exampleCode"] - 1] : null);
73+
final updateStatus = UserScriptUpdateStatus.values.byName(
74+
json["updateStatus"] ?? (url is String ? "upToDate" : "noRemote"));
6475
return UserScriptModel(
6576
enabled: enabled,
6677
matches: matches,
@@ -72,19 +83,27 @@ class UserScriptModel {
7283
url: url,
7384
updateStatus: updateStatus,
7485
isExample: isExample,
86+
grants: <String>[] // Old model does not have grants
7587
);
7688
} else {
7789
return UserScriptModel(
7890
enabled: json["enabled"],
79-
matches: json["matches"] is List<dynamic> ? json["matches"].cast<String>() : const ["*"],
91+
matches: json["matches"] is List<dynamic>
92+
? json["matches"].cast<String>()
93+
: const ["*"],
8094
name: json["name"],
8195
version: json["version"],
8296
edited: json["edited"],
8397
source: json["source"],
84-
time: json["time"] == "start" ? UserScriptTime.start : UserScriptTime.end,
98+
time:
99+
json["time"] == "start" ? UserScriptTime.start : UserScriptTime.end,
85100
url: json["url"],
86-
updateStatus: UserScriptUpdateStatus.values.byName(json["updateStatus"] ?? "noRemote"),
101+
updateStatus: UserScriptUpdateStatus.values
102+
.byName(json["updateStatus"] ?? "noRemote"),
87103
isExample: json["isExample"] ?? (json["exampleCode"] ?? 0) > 0,
104+
grants: json["grants"] is List<dynamic>
105+
? json["grants"].cast<String>()
106+
: const [],
88107
);
89108
}
90109
}
@@ -110,12 +129,17 @@ class UserScriptModel {
110129
matches: metaMap["matches"] ?? ["*"],
111130
url: url ?? metaMap["downloadURL"],
112131
updateStatus: updateStatus,
113-
time: time ?? (metaMap["injectionTime"] == "document-start" ? UserScriptTime.start : UserScriptTime.end),
132+
time: time ??
133+
(metaMap["injectionTime"] == "document-start"
134+
? UserScriptTime.start
135+
: UserScriptTime.end),
114136
isExample: isExample ?? false,
137+
grants: metaMap["grant"] ?? <String>[],
115138
);
116139
}
117140

118-
static Future<({bool success, String message, UserScriptModel? model})> fromURL(String url, {bool? isExample}) async {
141+
static Future<({bool success, String message, UserScriptModel? model})>
142+
fromURL(String url, {bool? isExample}) async {
119143
try {
120144
final response = await http.get(Uri.parse(url));
121145
if (response.statusCode == 200) {
@@ -124,7 +148,9 @@ class UserScriptModel {
124148
success: true,
125149
message: "Success",
126150
model: UserScriptModel.fromMetaMap(metaMap,
127-
url: url, updateStatus: UserScriptUpdateStatus.upToDate, isExample: isExample ?? false),
151+
url: url,
152+
updateStatus: UserScriptUpdateStatus.upToDate,
153+
isExample: isExample ?? false),
128154
);
129155
} else {
130156
return (
@@ -151,7 +177,8 @@ class UserScriptModel {
151177
final List<String> version1List = version1.split(".");
152178
final List<String> version2List = version2.split(".");
153179
for (int i = 0; i < version1List.length; i++) {
154-
if (version2List.length <= i || int.parse(version1List[i]) > int.parse(version2List[i])) {
180+
if (version2List.length <= i ||
181+
int.parse(version1List[i]) > int.parse(version2List[i])) {
155182
return true;
156183
}
157184
}
@@ -168,18 +195,22 @@ class UserScriptModel {
168195
"url": url,
169196
"updateStatus": updateStatus.name,
170197
"isExample": isExample,
198+
"grants": grants,
171199
"time": time == UserScriptTime.start ? "start" : "end",
172200
};
173201

174202
static Map<String, dynamic> parseHeader(String source) {
175203
// Thanks to [ViolentMonkey](https://github.com/violentmonkey/violentmonkey) for the following two regexes
176-
String? meta =
177-
RegExp(r"((?:^|\n)\s*\/\/\x20==UserScript==)([\s\S]*?\n)\s*\/\/\x20==\/UserScript==|$").stringMatch(source);
204+
String? meta = RegExp(
205+
r"((?:^|\n)\s*\/\/\x20==UserScript==)([\s\S]*?\n)\s*\/\/\x20==\/UserScript==|$")
206+
.stringMatch(source);
178207
if (meta == null || meta.isEmpty) {
179208
throw Exception("No header found in userscript.");
180209
}
181-
Iterable<RegExpMatch> metaMatches = RegExp(r"^(?:^|\n)\s*\/\/\x20(@\S+)(.*)$", multiLine: true).allMatches(meta);
182-
Map<String, dynamic> metaMap = {"@match": <String>[]};
210+
Iterable<RegExpMatch> metaMatches =
211+
RegExp(r"^(?:^|\n)\s*\/\/\x20(@\S+)(.*)$", multiLine: true)
212+
.allMatches(meta);
213+
Map<String, dynamic> metaMap = {"@match": <String>[], "@grant": <String>[]};
183214
for (final match in metaMatches) {
184215
if (match.groupCount < 2) {
185216
continue;
@@ -189,6 +220,11 @@ class UserScriptModel {
189220
}
190221
if (match.group(1)?.toLowerCase() == "@match") {
191222
metaMap["@match"].add(match.group(2)!.trim());
223+
} else if (match.group(1)?.toLowerCase() == "@grant") {
224+
if (match.group(2)?.trim() == "none") {
225+
continue;
226+
}
227+
metaMap["@grant"].add(match.group(2)!.trim());
192228
} else {
193229
metaMap[match.group(1)!.trim().toLowerCase()] = match.group(2)!.trim();
194230
}
@@ -201,14 +237,16 @@ class UserScriptModel {
201237
"injectionTime": metaMap["@run-at"] ?? "document-end",
202238
"downloadURL": metaMap["@downloadurl"],
203239
"updateURL": metaMap["@updateurl"],
240+
"grant": metaMap["@grant"],
204241
"source": source,
205242
};
206243
}
207244

208245
shouldInject(String url, [UserScriptTime? time]) =>
209246
enabled &&
210247
(this.time == time || time == null) &&
211-
matches.any((match) => (match == "*" || url.contains(match.replaceAll("*", ""))));
248+
matches.any(
249+
(match) => (match == "*" || url.contains(match.replaceAll("*", ""))));
212250

213251
void update({
214252
bool? enabled,
@@ -219,6 +257,7 @@ class UserScriptModel {
219257
String? source,
220258
UserScriptTime? time,
221259
String? url,
260+
List<String>? grants,
222261
required UserScriptUpdateStatus updateStatus,
223262
}) {
224263
if (source != null) {
@@ -231,11 +270,16 @@ class UserScriptModel {
231270
if (metaMap["matches"] != null) {
232271
this.matches = metaMap["matches"];
233272
}
273+
if (metaMap["grant"] != null) {
274+
this.grants = metaMap["grant"];
275+
}
234276
if (metaMap["name"] != null) {
235277
this.name = metaMap["name"];
236278
}
237279
if (metaMap["injectionTime"] != null) {
238-
this.time = metaMap["injectionTime"] == "document-start" ? UserScriptTime.start : UserScriptTime.end;
280+
this.time = metaMap["injectionTime"] == "document-start"
281+
? UserScriptTime.start
282+
: UserScriptTime.end;
239283
}
240284
if (metaMap["downloadURL"] != null) {
241285
this.url = metaMap["downloadURL"];
@@ -302,6 +346,15 @@ class UserScriptModel {
302346
}
303347
}
304348

349+
static List<String> tryGetGrants(String source) {
350+
try {
351+
final metaMap = UserScriptModel.parseHeader(source);
352+
return metaMap["grant"] ?? <String>[];
353+
} catch (e) {
354+
return const [];
355+
}
356+
}
357+
305358
static String? tryGetUrl(String source) {
306359
try {
307360
final metaMap = UserScriptModel.parseHeader(source);

0 commit comments

Comments
 (0)