Skip to content

Commit

Permalink
Merge pull request #866 from openkraken/fix/window_open
Browse files Browse the repository at this point in the history
Feat/Kraken Bundle
  • Loading branch information
answershuto committed Dec 7, 2021
2 parents 129f347 + 5aa6671 commit f95b2e7
Show file tree
Hide file tree
Showing 14 changed files with 196 additions and 173 deletions.
3 changes: 0 additions & 3 deletions bridge/bindings/qjs/bom/window.cc
Expand Up @@ -35,7 +35,6 @@ JSClassID Window::classId() {
}

JSValue Window::open(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
JSValue url = argv[0];
auto window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, Window::classId()));
NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0])};
return window->callNativeMethods("open", 1, arguments);
Expand All @@ -46,8 +45,6 @@ JSValue Window::scrollTo(QjsContext* ctx, JSValue this_val, int argc, JSValue* a
return window->callNativeMethods("scroll", 2, arguments);
}
JSValue Window::scrollBy(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
JSValue x = argv[0];
JSValue y = argv[1];
auto window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, Window::classId()));
NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0]), jsValueToNativeValue(ctx, argv[1])};
return window->callNativeMethods("scrollBy", 2, arguments);
Expand Down
3 changes: 2 additions & 1 deletion integration_tests/lib/main.dart
Expand Up @@ -6,6 +6,7 @@ import 'package:kraken/css.dart';
import 'package:kraken/bridge.dart';
import 'package:kraken/dom.dart';
import 'package:kraken/foundation.dart';
import 'package:kraken/kraken.dart';
import 'package:kraken/module.dart';
import 'package:kraken/widget.dart';
import 'package:ansicolor/ansicolor.dart';
Expand Down Expand Up @@ -82,7 +83,7 @@ void main() async {
var kraken = krakenMap[i] = Kraken(
viewportWidth: 360,
viewportHeight: 640,
bundleContent: 'console.log("Starting integration tests...")',
bundle: KrakenBundle.fromContent('console.log("Starting integration tests...")'),
disableViewportWidthAssertion: true,
disableViewportHeightAssertion: true,
javaScriptChannel: javaScriptChannel,
Expand Down
3 changes: 2 additions & 1 deletion integration_tests/lib/plugin.dart
Expand Up @@ -7,6 +7,7 @@ import 'package:kraken/dom.dart';
import 'package:kraken/foundation.dart';
import 'package:kraken/module.dart';
import 'package:kraken/widget.dart';
import 'package:kraken/launcher.dart';
import 'package:ansicolor/ansicolor.dart';
import 'package:path/path.dart' as path;
import 'bridge/from_native.dart';
Expand Down Expand Up @@ -68,7 +69,7 @@ void main() async {
var kraken = krakenMap[i] = Kraken(
viewportWidth: 360,
viewportHeight: 640,
bundleContent: 'console.log("Starting Plugin tests...")',
bundle: KrakenBundle.fromContent('console.log("Starting Plugin tests...")'),
disableViewportWidthAssertion: true,
disableViewportHeightAssertion: true,
uriParser: IntegrationTestUriParser(),
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/specs/dom/elements/script.ts
Expand Up @@ -4,7 +4,7 @@ describe('script element', () => {
const p = <p>Should see hello below:</p>;
document.body.appendChild(p);
var x = document.createElement('script');
x.src = 'assets/hello.js';
x.src = 'assets://assets/hello.js';
document.head.appendChild(x);
x.onload = async () => {
await snapshot();
Expand Down
4 changes: 2 additions & 2 deletions kraken/example/lib/main.dart
Expand Up @@ -63,7 +63,7 @@ class _MyHomePageState extends State<MyBrowser> {
controller: textEditingController,
onSubmitted: (value) {
textEditingController.text = value;
_kraken?.loadURL(value);
_kraken?.loadBundle(KrakenBundle.fromUrl(value));
},
decoration: InputDecoration(
hintText: 'Enter a app url',
Expand Down Expand Up @@ -92,7 +92,7 @@ class _MyHomePageState extends State<MyBrowser> {
devToolsService: ChromeDevToolsService(),
viewportWidth: viewportSize.width - queryData.padding.horizontal,
viewportHeight: viewportSize.height - appBar.preferredSize.height - queryData.padding.vertical,
bundleURL: 'assets/bundle.js',
bundle: KrakenBundle.fromUrl('assets://assets/bundle.js'),
),
));
}
Expand Down
7 changes: 5 additions & 2 deletions kraken/lib/src/dom/elements/head.dart
Expand Up @@ -9,6 +9,7 @@ import 'package:flutter/scheduler.dart';
import 'package:kraken/bridge.dart';
import 'package:kraken/css.dart';
import 'package:kraken/dom.dart';
import 'package:kraken/kraken.dart';
import 'package:kraken/launcher.dart';

// Children of the <head> element all have display:none
Expand Down Expand Up @@ -73,7 +74,8 @@ class ScriptElement extends Element {
// Must
if (src.isNotEmpty && isConnected && (type == _JAVASCRIPT_MIME || type == _JAVASCRIPT_MODULE)) {
try {
KrakenBundle bundle = await KrakenBundle.getBundle(src, contextId: elementManager.contextId);
KrakenBundle bundle = KrakenBundle.fromUrl(src);
await bundle.resolve(elementManager.contextId);
await bundle.eval(elementManager.contextId);
// Successful load.
SchedulerBinding.instance!.addPostFrameCallback((_) {
Expand Down Expand Up @@ -108,7 +110,8 @@ class ScriptElement extends Element {
int contextId = elementManager.contextId;
KrakenController? controller = KrakenController.getControllerOfJSContextId(contextId);
if (controller != null) {
KrakenBundle bundle = await KrakenBundle.getBundle(controller.href, contentOverride: script, contextId: contextId);
KrakenBundle bundle = KrakenBundle.fromContent(script, url: controller.href);
bundle.resolve(contextId);
await bundle.eval(elementManager.contextId);
}
}
Expand Down
2 changes: 1 addition & 1 deletion kraken/lib/src/dom/window.dart
Expand Up @@ -25,7 +25,7 @@ class Window extends EventTarget {

static void _open(ElementManager elementManager, String url) {
KrakenController rootController = elementManager.controller.view.rootController;
String? sourceUrl = rootController.bundleURL ?? rootController.bundlePath;
String? sourceUrl = rootController.href;

elementManager.controller.view.handleNavigationAction(sourceUrl, url, KrakenNavigationType.navigate);
}
Expand Down
106 changes: 64 additions & 42 deletions kraken/lib/src/launcher/bundle.dart
Expand Up @@ -22,6 +22,8 @@ const String BUNDLE_PATH = 'KRAKEN_BUNDLE_PATH';
const String ENABLE_DEBUG = 'KRAKEN_ENABLE_DEBUG';
const String ENABLE_PERFORMANCE_OVERLAY = 'KRAKEN_ENABLE_PERFORMANCE_OVERLAY';

const String ASSETS_PROROCOL = 'assets://';

String? getBundleURLFromEnv() {
return Platform.environment[BUNDLE_URL];
}
Expand All @@ -46,17 +48,21 @@ String getAcceptHeader() {
}

bool isAssetAbsolutePath(String path) {
return path.indexOf('assets/') == 0;
return path.startsWith(ASSETS_PROROCOL);
}

abstract class KrakenBundle {
KrakenBundle(this.url);
KrakenBundle(this.src);

// Unique resource locator.
final Uri url;
final String src;

// Customize the parsed uri by uriParser.
Uri? uri;

late ByteData rawBundle;
// JS Content in UTF-8 bytes.
Uint8List? byteCode;
Uint8List? bytecode;
// JS Content is String
String? content;
// JS line offset, default to 0.
Expand All @@ -69,60 +75,64 @@ abstract class KrakenBundle {
// Bundle contentType.
ContentType contentType = ContentType.binary;

Future<void> resolve();

static Future<KrakenBundle> getBundle(String path, { String? contentOverride, required int contextId }) async {
KrakenBundle bundle;

@mustCallSuper
Future<void> resolve(int contextId) async {
if (kDebugMode) {
print('Kraken getting bundle for contextId: $contextId, path: $path');
print('Kraken getting bundle for contextId: $contextId, src: $src');
}

Uri uri = Uri.parse(path);
uri = Uri.parse(src);
KrakenController? controller = KrakenController.getControllerOfJSContextId(contextId);
if (controller != null && !isAssetAbsolutePath(path)) {
uri = controller.uriParser!.resolve(Uri.parse(controller.href), uri);
if (controller != null && !isAssetAbsolutePath(src)) {
uri = controller.uriParser!.resolve(Uri.parse(controller.href), uri!);
}

if (contentOverride != null && contentOverride.isNotEmpty) {
bundle = RawBundle.fromString(contentOverride, uri);
} else if (uri.isScheme('HTTP') || uri.isScheme('HTTPS')) {
bundle = NetworkBundle(uri, contextId: contextId);
isResolved = true;
}

static KrakenBundle fromUrl(String url, { Map<String, String>? additionalHttpHeaders }) {
if (isAssetAbsolutePath(url)) {
return AssetsBundle(url);
} else {
bundle = AssetsBundle(uri);
return NetworkBundle(url, additionalHttpHeaders: additionalHttpHeaders);
}
}

await bundle.resolve();
static KrakenBundle fromContent(String content, { String url = '' }) {
return RawBundle.fromString(content, url);
}

return bundle;
static KrakenBundle fromBytecode(Uint8List bytecode, { String url = '' }) {
return RawBundle.fromBytecode(bytecode, url);
}


Future<void> eval(int contextId) async {
if (!isResolved) await resolve();
if (!isResolved) await resolve(contextId);

if (kProfileMode) {
PerformanceTiming.instance().mark(PERF_JS_BUNDLE_EVAL_START);
}

// For raw javascript code or bytecode from API directly.
if (content != null) {
evaluateScripts(contextId, content!, url.toString(), lineOffset);
} else if (byteCode != null) {
evaluateQuickjsByteCode(contextId, byteCode!);
evaluateScripts(contextId, content!, src, lineOffset);
} else if (bytecode != null) {
evaluateQuickjsByteCode(contextId, bytecode!);
}

// For javascript code, HTML or bytecode from networks and hardware disk.
else if (contentType.mimeType == ContentType.html.mimeType || url.toString().contains('.html')) {
else if (contentType.mimeType == ContentType.html.mimeType || src.contains('.html')) {
String code = _resolveStringFromData(rawBundle);
// parse html.
parseHTML(contextId, code);
} else if (isByteCodeSupported(contentType.mimeType, url.toString())) {
} else if (isByteCodeSupported(contentType.mimeType, src)) {
Uint8List buffer = rawBundle.buffer.asUint8List();
evaluateQuickjsByteCode(contextId, buffer);
} else {
String code = _resolveStringFromData(rawBundle);
// eval JavaScript.
evaluateScripts(contextId, code, url.toString(), lineOffset);
evaluateScripts(contextId, code, src, lineOffset);
}

if (kProfileMode) {
Expand All @@ -132,33 +142,37 @@ abstract class KrakenBundle {
}

class RawBundle extends KrakenBundle {
RawBundle.fromString(String content, Uri url)
RawBundle.fromString(String content, String url)
: super(url) {
this.content = content;
}

RawBundle.fromByteCode(Uint8List byteCode, Uri url) : super(url) {
this.byteCode = byteCode;
RawBundle.fromBytecode(Uint8List bytecode, String url)
: super(url) {
this.bytecode = bytecode;
}

@override
Future<void> resolve() async {
Future<void> resolve(int contextId) async {
super.resolve(contextId);
isResolved = true;
}
}

class NetworkBundle extends KrakenBundle {
int contextId;
NetworkBundle(Uri url, { required this.contextId })
NetworkBundle(String url, { this.additionalHttpHeaders })
: super(url);

Map<String, String>? additionalHttpHeaders = {};

@override
Future<void> resolve() async {
Future<void> resolve(int contextId) async {
super.resolve(contextId);
KrakenController controller = KrakenController.getControllerOfJSContextId(contextId)!;
Uri baseUrl = Uri.parse(controller.href);
NetworkAssetBundle bundle = NetworkAssetBundle(controller.uriParser!.resolve(baseUrl, url), contextId: contextId);
NetworkAssetBundle bundle = NetworkAssetBundle(controller.uriParser!.resolve(baseUrl, Uri.parse(src)), contextId: contextId, additionalHttpHeaders: additionalHttpHeaders);
bundle.httpClient.userAgent = getKrakenInfo().userAgent;
String absoluteURL = url.toString();
String absoluteURL = src;
rawBundle = await bundle.load(absoluteURL);
contentType = bundle.contentType;
isResolved = true;
Expand All @@ -177,13 +191,15 @@ String _resolveStringFromData(ByteData data) {
class NetworkAssetBundle extends AssetBundle {
/// Creates an network asset bundle that resolves asset keys as URLs relative
/// to the given base URL.
NetworkAssetBundle(Uri baseUrl, { required this.contextId })
NetworkAssetBundle(Uri baseUrl, { required this.contextId, additionalHttpHeaders })
: _baseUrl = baseUrl,
_additionalHttpHeaders = additionalHttpHeaders,
httpClient = HttpClient();

final Uri _baseUrl;
final int contextId;
final HttpClient httpClient;
final Map<String, String>? _additionalHttpHeaders;
ContentType contentType = ContentType.binary;

Uri _urlFromKey(String key) => _baseUrl.resolve(key);
Expand All @@ -192,6 +208,9 @@ class NetworkAssetBundle extends AssetBundle {
Future<ByteData> load(String key) async {
final HttpClientRequest request = await httpClient.getUrl(_urlFromKey(key));
request.headers.set('Accept', getAcceptHeader());
if (_additionalHttpHeaders != null) {
_additionalHttpHeaders?.forEach(request.headers.set);
}
KrakenHttpOverrides.setContextHeader(request.headers, contextId);

final HttpClientResponse response = await request.close();
Expand Down Expand Up @@ -223,15 +242,18 @@ class NetworkAssetBundle extends AssetBundle {
}

class AssetsBundle extends KrakenBundle {
AssetsBundle(Uri url)
AssetsBundle(String url)
: super(url);

@override
Future<void> resolve() async {
Future<KrakenBundle> resolve(int contextId) async {
super.resolve(contextId);
// JSBundle get default bundle manifest.
manifest = AppManifest();
String localPath = url.toString();
rawBundle = await rootBundle.load(localPath);
isResolved = true;
if (isAssetAbsolutePath(src)) {
String localPath = src.substring(ASSETS_PROROCOL.length);
rawBundle = await rootBundle.load(localPath);
}
return this;
}
}

0 comments on commit f95b2e7

Please sign in to comment.