From 11d12d23d08a7bf34fe5e2669528fe0c688dbf05 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 5 Nov 2021 16:54:13 +0800 Subject: [PATCH 001/167] :sparkles: feat: add parseTestHTML to bridge. --- bridge/bridge_test_qjs.cc | 8 +++++++- bridge/bridge_test_qjs.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/bridge/bridge_test_qjs.cc b/bridge/bridge_test_qjs.cc index 35ddc8d226..4adcba6d39 100644 --- a/bridge/bridge_test_qjs.cc +++ b/bridge/bridge_test_qjs.cc @@ -12,10 +12,16 @@ namespace kraken { bool JSBridgeTest::evaluateTestScripts(const uint16_t* code, size_t codeLength, const char* sourceURL, int startLine) { if (!context->isValid()) return false; - // binding::jsc::updateLocation(sourceURL); return context->evaluateJavaScript(code, codeLength, sourceURL, startLine); } +bool JSBridgeTest::parseTestHTML(const uint16_t* code, size_t codeLength) { + if (!context->isValid()) + return false; + std::string utf8Code = toUTF8(std::u16string(reinterpret_cast(code), codeLength)); + return bridge_->parseHTML(utf8Code.c_str(), utf8Code.length()); +} + static JSValue executeTest(QjsContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { JSValue& callback = argv[0]; auto context = static_cast(JS_GetContextOpaque(ctx)); diff --git a/bridge/bridge_test_qjs.h b/bridge/bridge_test_qjs.h index b8e20b4a2b..cc87dc732c 100644 --- a/bridge/bridge_test_qjs.h +++ b/bridge/bridge_test_qjs.h @@ -41,6 +41,7 @@ class JSBridgeTest final { /// evaluete JavaScript source code with build-in test frameworks, use in test only. bool evaluateTestScripts(const uint16_t* code, size_t codeLength, const char* sourceURL, int startLine); + bool parseTestHTML(const uint16_t* code, size_t codeLength); void invokeExecuteTest(ExecuteCallback executeCallback); JSValue executeTestCallback{JS_NULL}; From eb59ab61a9fee3f172e1f20692f68e7080cc6999 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 5 Nov 2021 17:37:12 +0800 Subject: [PATCH 002/167] :sparkles: feat: add parseTestHTML to bridge. --- bridge/bridge_qjs.cc | 5 +++-- bridge/bridge_qjs.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bridge/bridge_qjs.cc b/bridge/bridge_qjs.cc index 841ba5cfee..e635c66a6a 100644 --- a/bridge/bridge_qjs.cc +++ b/bridge/bridge_qjs.cc @@ -116,15 +116,16 @@ JSBridge::JSBridge(int32_t contextId, const JSExceptionHandler& handler) : conte #endif } -void JSBridge::parseHTML(const char* code, size_t length) { +bool JSBridge::parseHTML(const char* code, size_t length) { if (!m_context->isValid()) - return; + return false; Document* Document = Document::instance(m_context.get()); auto document = DocumentInstance::instance(Document); JSValue bodyValue = JS_GetPropertyStr(m_context->ctx(), document->instanceObject, "body"); auto* body = static_cast(JS_GetOpaque(bodyValue, Element::classId())); HTMLParser::parseHTML(code, length, body); JS_FreeValue(m_context->ctx(), bodyValue); + return true; } void JSBridge::invokeModuleEvent(NativeString* moduleName, const char* eventType, void* rawEvent, NativeString* extra) { diff --git a/bridge/bridge_qjs.h b/bridge/bridge_qjs.h index 56fa0b2f0a..c26ce9bbb0 100644 --- a/bridge/bridge_qjs.h +++ b/bridge/bridge_qjs.h @@ -36,7 +36,7 @@ class JSBridge final { // evaluate JavaScript source codes in standard mode. void evaluateScript(const NativeString* script, const char* url, int startLine); void evaluateScript(const uint16_t* script, size_t length, const char* url, int startLine); - void parseHTML(const char* code, size_t length); + bool parseHTML(const char* code, size_t length); void evaluateScript(const char* script, size_t length, const char* url, int startLine); uint8_t* dumpByteCode(const char* script, size_t length, const char* url, size_t* byteLength); void evaluateByteCode(uint8_t* bytes, size_t byteLength); From ce937a4e574c0a7cee095ea4b36952a2feefed2b Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 5 Nov 2021 17:38:39 +0800 Subject: [PATCH 003/167] :sparkles: feat: add export of cc. --- bridge/include/kraken_bridge_test.h | 2 ++ bridge/kraken_bridge_test.cc | 5 +++++ integration_tests/lib/bridge/to_native.dart | 13 ++++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/bridge/include/kraken_bridge_test.h b/bridge/include/kraken_bridge_test.h index 7aa60103c5..3675c69073 100644 --- a/bridge/include/kraken_bridge_test.h +++ b/bridge/include/kraken_bridge_test.h @@ -13,6 +13,8 @@ KRAKEN_EXPORT_C void initTestFramework(int32_t contextId); KRAKEN_EXPORT_C int8_t evaluateTestScripts(int32_t contextId, NativeString* code, const char* bundleFilename, int startLine); +KRAKEN_EXPORT_C +int8_t parseTestHTML(int32_t contextId, NativeString* code); using ExecuteCallback = void* (*)(int32_t contextId, NativeString* status); diff --git a/bridge/kraken_bridge_test.cc b/bridge/kraken_bridge_test.cc index 7dd23ce727..5b5ade5432 100644 --- a/bridge/kraken_bridge_test.cc +++ b/bridge/kraken_bridge_test.cc @@ -30,6 +30,11 @@ int8_t evaluateTestScripts(int32_t contextId, NativeString* code, const char* bu return bridgeTest->evaluateTestScripts(code->string, code->length, bundleFilename, startLine); } +int8_t parseTestHTML(int32_t contextId, NativeString* code) { + auto bridgeTest = bridgeTestPool[contextId]; + return bridgeTest->parseTestHTML(code->string, code->length); +} + void executeTest(int32_t contextId, ExecuteCallback executeCallback) { auto bridgeTest = bridgeTestPool[contextId]; bridgeTest->invokeExecuteTest(executeCallback); diff --git a/integration_tests/lib/bridge/to_native.dart b/integration_tests/lib/bridge/to_native.dart index 61d00f4d50..a092fb52d8 100644 --- a/integration_tests/lib/bridge/to_native.dart +++ b/integration_tests/lib/bridge/to_native.dart @@ -35,13 +35,24 @@ typedef Native_EvaluateTestScripts = Int8 Function(Int32 contextId, Pointer, Pointer, int); final Dart_EvaluateTestScripts _evaluateTestScripts = - nativeDynamicLibrary.lookup>('evaluateTestScripts').asFunction(); +nativeDynamicLibrary.lookup>('evaluateTestScripts').asFunction(); + +// Register parseTestHTML +typedef Native_ParseTestHTML = Int8 Function(Int32 contextId, Pointer); +typedef Dart_ParseTestHTML = int Function(int contextId, Pointer); + +final Dart_ParseTestHTML _parseTestHTML = +nativeDynamicLibrary.lookup>('parseTestHTML').asFunction(); void evaluateTestScripts(int contextId, String code, {String url = 'test://', int line = 0}) { Pointer _url = (url).toNativeUtf8(); _evaluateTestScripts(contextId, stringToNativeString(code), _url, line); } +void parseTestHTML(int contextId, String code) { + _parseTestHTML(contextId, stringToNativeString(code)); +} + typedef NativeExecuteCallback = Void Function(Int32 contextId, Pointer status); typedef DartExecuteCallback = void Function(int); typedef Native_ExecuteTest = Void Function(Int32 contextId, Pointer>); From 1760b4b0422fb092321e49b2923c1d1fe2696d94 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 8 Nov 2021 14:26:35 +0800 Subject: [PATCH 004/167] :sparkles: feat: add html loader to config of webpack. --- integration_tests/scripts/html_loader.js | 8 ++++++++ integration_tests/webpack.config.js | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 integration_tests/scripts/html_loader.js diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js new file mode 100644 index 0000000000..fefb61d212 --- /dev/null +++ b/integration_tests/scripts/html_loader.js @@ -0,0 +1,8 @@ +module.exports = function(source) { + return ` + it('htmltest', async () => { + parseTestHTML('${source.replace(/\n/g, '').replace(/'/g, '"')}'); + await snapshot(); + }); + `; +} diff --git a/integration_tests/webpack.config.js b/integration_tests/webpack.config.js index 002742f34e..3ec564aaa6 100644 --- a/integration_tests/webpack.config.js +++ b/integration_tests/webpack.config.js @@ -1,7 +1,6 @@ const path = require('path'); const glob = require('glob'); const bableTransformSnapshotPlugin = require('./scripts/babel_transform_snapshot'); -const quickjsSyntaxFixLoader = require('./scripts/quickjs_syntax_fix_loader'); const context = path.join(__dirname); const runtimePath = path.join(context, 'runtime'); @@ -10,7 +9,7 @@ const resetRuntimePath = path.join(context, 'runtime/reset'); const buildPath = path.join(context, '.specs'); const testPath = path.join(context, 'specs'); const snapshotPath = path.join(context, 'snapshots'); -const coreSpecFiles = glob.sync('specs/**/*.{js,jsx,ts,tsx}', { +const coreSpecFiles = glob.sync('specs/**/*.{js,jsx,ts,tsx,html}', { cwd: context, ignore: ['node_modules/**'], }).map((file) => './' + file).filter(name => name.indexOf('plugins') < 0); @@ -96,6 +95,10 @@ module.exports = { }, { loader: path.resolve('./scripts/quickjs_syntax_fix_loader'), }] + }, { + test: /\.(html?)$/i, + exclude: /node_modules/, + use: path.resolve('./scripts/html_loader') } ], }, From 0c7ec40735cd8f3db90f2b2b51ae952bc728ac84 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 8 Nov 2021 16:53:56 +0800 Subject: [PATCH 005/167] :sparkles: feat: add __kraken_parse_html__ to runtime. --- bridge/bindings/qjs/html_parser.cc | 8 ++++++-- bridge/bindings/qjs/html_parser.h | 1 + bridge/bridge_test_qjs.cc | 22 +++++++++++++++++++++ bridge/bridge_test_qjs.h | 2 ++ bridge/include/kraken_bridge_test.h | 2 -- bridge/kraken_bridge_test.cc | 5 ----- integration_tests/lib/bridge/to_native.dart | 11 ----------- 7 files changed, 31 insertions(+), 20 deletions(-) diff --git a/bridge/bindings/qjs/html_parser.cc b/bridge/bindings/qjs/html_parser.cc index e685dc3763..32e89e5821 100644 --- a/bridge/bindings/qjs/html_parser.cc +++ b/bridge/bindings/qjs/html_parser.cc @@ -69,9 +69,8 @@ void HTMLParser::traverseHTML(NodeInstance* root, GumboNode* node) { } } } -bool HTMLParser::parseHTML(const char* code, size_t codeLength, NodeInstance* rootNode) { - std::string html = std::string(code, codeLength); +bool HTMLParser::parseHTML(std::string html, NodeInstance* rootNode) { if (rootNode != nullptr) { rootNode->internalClearChild(); @@ -87,6 +86,11 @@ bool HTMLParser::parseHTML(const char* code, size_t codeLength, NodeInstance* ro return true; } + +bool HTMLParser::parseHTML(const char* code, size_t codeLength, NodeInstance* rootNode) { + std::string html = std::string(code, codeLength); + return parseHTML(html, rootNode); +} void HTMLParser::parseProperty(ElementInstance* element, GumboElement* gumboElement) { JSContext* context = element->context(); QjsContext* ctx = context->ctx(); diff --git a/bridge/bindings/qjs/html_parser.h b/bridge/bindings/qjs/html_parser.h index 7cd4c75df6..0b78975915 100644 --- a/bridge/bindings/qjs/html_parser.h +++ b/bridge/bindings/qjs/html_parser.h @@ -16,6 +16,7 @@ namespace kraken::binding::qjs { class HTMLParser { public: static bool parseHTML(const char* code, size_t codeLength, NodeInstance* rootNode); + static bool parseHTML(std::string html, NodeInstance* rootNode); private: JSContext* m_context; diff --git a/bridge/bridge_test_qjs.cc b/bridge/bridge_test_qjs.cc index 4adcba6d39..3ecebb14eb 100644 --- a/bridge/bridge_test_qjs.cc +++ b/bridge/bridge_test_qjs.cc @@ -196,6 +196,27 @@ static JSValue runGC(QjsContext* ctx, JSValueConst this_val, int argc, JSValueCo return JS_NULL; } +static JSValue parseHTML(QjsContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + KRAKEN_LOG(VERBOSE) << "parseHTML111" << "argc=" << argc; + auto* context = static_cast(JS_GetContextOpaque(ctx)); + + if (argc == 1) { + JSValueConst html = argv[0]; + + std::string strHTML = binding::qjs::jsValueToStdString(ctx, html); + + binding::qjs::Document* Document = binding::qjs::Document::instance(context); + auto document = binding::qjs::DocumentInstance::instance(Document); + JSValue bodyValue = JS_GetPropertyStr(context->ctx(), document->instanceObject, "body"); + auto* body = static_cast(JS_GetOpaque(bodyValue, binding::qjs::Element::classId())); + binding::qjs::HTMLParser::parseHTML(strHTML, body); + + JS_FreeValue(ctx, html); + } + + return JS_NULL; +} + static JSValue triggerGlobalError(QjsContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { auto* context = static_cast(JS_GetContextOpaque(ctx)); @@ -220,6 +241,7 @@ JSBridgeTest::JSBridgeTest(JSBridge* bridge) : bridge_(bridge), context(bridge-> QJS_GLOBAL_BINDING_FUNCTION(context, simulateInputText, "__kraken_simulate_inputtext__", 1); QJS_GLOBAL_BINDING_FUNCTION(context, triggerGlobalError, "__kraken_trigger_global_error__", 0); QJS_GLOBAL_BINDING_FUNCTION(context, runGC, "__kraken_run_gc__", 0); + QJS_GLOBAL_BINDING_FUNCTION(context, parseHTML, "__kraken_parse_html__", 1); initKrakenTestFramework(bridge); init_list_head(&image_link); diff --git a/bridge/bridge_test_qjs.h b/bridge/bridge_test_qjs.h index cc87dc732c..f704fb0659 100644 --- a/bridge/bridge_test_qjs.h +++ b/bridge/bridge_test_qjs.h @@ -8,6 +8,8 @@ #include "bridge_qjs.h" #include "kraken_bridge_test.h" +#include "bindings/qjs/html_parser.h" +#include "bindings/qjs/dom/document.h" namespace kraken { diff --git a/bridge/include/kraken_bridge_test.h b/bridge/include/kraken_bridge_test.h index 3675c69073..7aa60103c5 100644 --- a/bridge/include/kraken_bridge_test.h +++ b/bridge/include/kraken_bridge_test.h @@ -13,8 +13,6 @@ KRAKEN_EXPORT_C void initTestFramework(int32_t contextId); KRAKEN_EXPORT_C int8_t evaluateTestScripts(int32_t contextId, NativeString* code, const char* bundleFilename, int startLine); -KRAKEN_EXPORT_C -int8_t parseTestHTML(int32_t contextId, NativeString* code); using ExecuteCallback = void* (*)(int32_t contextId, NativeString* status); diff --git a/bridge/kraken_bridge_test.cc b/bridge/kraken_bridge_test.cc index 5b5ade5432..7dd23ce727 100644 --- a/bridge/kraken_bridge_test.cc +++ b/bridge/kraken_bridge_test.cc @@ -30,11 +30,6 @@ int8_t evaluateTestScripts(int32_t contextId, NativeString* code, const char* bu return bridgeTest->evaluateTestScripts(code->string, code->length, bundleFilename, startLine); } -int8_t parseTestHTML(int32_t contextId, NativeString* code) { - auto bridgeTest = bridgeTestPool[contextId]; - return bridgeTest->parseTestHTML(code->string, code->length); -} - void executeTest(int32_t contextId, ExecuteCallback executeCallback) { auto bridgeTest = bridgeTestPool[contextId]; bridgeTest->invokeExecuteTest(executeCallback); diff --git a/integration_tests/lib/bridge/to_native.dart b/integration_tests/lib/bridge/to_native.dart index a092fb52d8..550cf34c20 100644 --- a/integration_tests/lib/bridge/to_native.dart +++ b/integration_tests/lib/bridge/to_native.dart @@ -37,22 +37,11 @@ typedef Dart_EvaluateTestScripts = int Function(int contextId, Pointer>('evaluateTestScripts').asFunction(); -// Register parseTestHTML -typedef Native_ParseTestHTML = Int8 Function(Int32 contextId, Pointer); -typedef Dart_ParseTestHTML = int Function(int contextId, Pointer); - -final Dart_ParseTestHTML _parseTestHTML = -nativeDynamicLibrary.lookup>('parseTestHTML').asFunction(); - void evaluateTestScripts(int contextId, String code, {String url = 'test://', int line = 0}) { Pointer _url = (url).toNativeUtf8(); _evaluateTestScripts(contextId, stringToNativeString(code), _url, line); } -void parseTestHTML(int contextId, String code) { - _parseTestHTML(contextId, stringToNativeString(code)); -} - typedef NativeExecuteCallback = Void Function(Int32 contextId, Pointer status); typedef DartExecuteCallback = void Function(int); typedef Native_ExecuteTest = Void Function(Int32 contextId, Pointer>); From ff14b81d822e0317e73cb2c07de54ede0b230be4 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 8 Nov 2021 16:54:33 +0800 Subject: [PATCH 006/167] :sparkles: feat: modify parseHTML to __kraken_parse_html__. --- integration_tests/scripts/html_loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index fefb61d212..d895ad1531 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -1,7 +1,7 @@ module.exports = function(source) { return ` it('htmltest', async () => { - parseTestHTML('${source.replace(/\n/g, '').replace(/'/g, '"')}'); + __kraken_parse_html__('${source.replace(/\n/g, '').replace(/'/g, '"')}'); await snapshot(); }); `; From 421b10bab1e6492eda7d9eb48945b7ba3dd41699 Mon Sep 17 00:00:00 2001 From: openkraken-bot Date: Mon, 8 Nov 2021 08:54:58 +0000 Subject: [PATCH 007/167] Committing clang-format changes --- bridge/bridge_test_qjs.cc | 3 ++- bridge/bridge_test_qjs.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bridge/bridge_test_qjs.cc b/bridge/bridge_test_qjs.cc index 3ecebb14eb..5109673225 100644 --- a/bridge/bridge_test_qjs.cc +++ b/bridge/bridge_test_qjs.cc @@ -197,7 +197,8 @@ static JSValue runGC(QjsContext* ctx, JSValueConst this_val, int argc, JSValueCo } static JSValue parseHTML(QjsContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - KRAKEN_LOG(VERBOSE) << "parseHTML111" << "argc=" << argc; + KRAKEN_LOG(VERBOSE) << "parseHTML111" + << "argc=" << argc; auto* context = static_cast(JS_GetContextOpaque(ctx)); if (argc == 1) { diff --git a/bridge/bridge_test_qjs.h b/bridge/bridge_test_qjs.h index f704fb0659..20e8826bc2 100644 --- a/bridge/bridge_test_qjs.h +++ b/bridge/bridge_test_qjs.h @@ -6,10 +6,10 @@ #ifndef KRAKENBRIDGE_BRIDGE_TEST_QJS_H #define KRAKENBRIDGE_BRIDGE_TEST_QJS_H +#include "bindings/qjs/dom/document.h" +#include "bindings/qjs/html_parser.h" #include "bridge_qjs.h" #include "kraken_bridge_test.h" -#include "bindings/qjs/html_parser.h" -#include "bindings/qjs/dom/document.h" namespace kraken { From 7d3098ec511091299862d180403273e8c1648621 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 8 Nov 2021 20:22:27 +0800 Subject: [PATCH 008/167] :sparkles: feat: add potions to html loader. --- integration_tests/scripts/html_loader.js | 32 +++++++++++++++++++++--- integration_tests/webpack.config.js | 18 ++++++++++--- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index d895ad1531..833369ebbb 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -1,8 +1,34 @@ -module.exports = function(source) { +const filepath = require('path'); + +let filename = ''; + +const loader = function(source) { + const opts = this.query || {}; + const snapshotFilepath = filepath.relative( + opts.workspacePath, + filepath.join( + opts.snapshotPath, + filepath.relative(opts.testPath, filename), + ) + ); + return ` - it('htmltest', async () => { + describe(${snapshotFilepath}, async (done) => { + window.html_snapshot = async (...argv) => { + console.log('window.html_snapshot') + if (argv.length === 0) { + await snapshot(null, '${snapshotFilepath}'); + } else if (argv.length === 1) { + await snapshot(argv[0], '${snapshotFilepath}'); + } + }; __kraken_parse_html__('${source.replace(/\n/g, '').replace(/'/g, '"')}'); - await snapshot(); }); `; } + +loader.pitch = (f) => { + filename = f; +} + +module.exports = loader; \ No newline at end of file diff --git a/integration_tests/webpack.config.js b/integration_tests/webpack.config.js index 3ec564aaa6..61813e5dd8 100644 --- a/integration_tests/webpack.config.js +++ b/integration_tests/webpack.config.js @@ -49,6 +49,20 @@ module.exports = { test: /\.css$/i, use: require.resolve('stylesheet-loader'), }, + { + test: /\.(html?)$/i, + exclude: /node_modules/, + use: [ + { + loader: path.resolve('./scripts/html_loader'), + options: { + workspacePath: context, + testPath, + snapshotPath, + } + } + ] + }, { test: /\.(jsx?|tsx?)$/i, exclude: /node_modules/, @@ -95,10 +109,6 @@ module.exports = { }, { loader: path.resolve('./scripts/quickjs_syntax_fix_loader'), }] - }, { - test: /\.(html?)$/i, - exclude: /node_modules/, - use: path.resolve('./scripts/html_loader') } ], }, From 1a13a632adb149df30fb612c0b4ccbeae4ed8c94 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 8 Nov 2021 20:24:05 +0800 Subject: [PATCH 009/167] :recycle: chore: delete comments. --- integration_tests/scripts/html_loader.js | 1 - 1 file changed, 1 deletion(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index 833369ebbb..a1ebf1cc97 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -15,7 +15,6 @@ const loader = function(source) { return ` describe(${snapshotFilepath}, async (done) => { window.html_snapshot = async (...argv) => { - console.log('window.html_snapshot') if (argv.length === 0) { await snapshot(null, '${snapshotFilepath}'); } else if (argv.length === 1) { From 5d7bf720ed064f96be26d18cd091ead866939528 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 8 Nov 2021 20:33:04 +0800 Subject: [PATCH 010/167] :recycle: chore: add newline. --- integration_tests/scripts/html_loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index a1ebf1cc97..7532396165 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -30,4 +30,4 @@ loader.pitch = (f) => { filename = f; } -module.exports = loader; \ No newline at end of file +module.exports = loader; From 7968f0966ae8588c216fa6188bb3ad5eaa11fbce Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 8 Nov 2021 20:58:05 +0800 Subject: [PATCH 011/167] :sparkles: feat: parse script. --- integration_tests/package.json | 3 ++- integration_tests/scripts/html_loader.js | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/integration_tests/package.json b/integration_tests/package.json index 35c40d7eb2..7a9594d869 100644 --- a/integration_tests/package.json +++ b/integration_tests/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "core-js": "^3.8.2", - "lodash.flattendeep": "^4.4.0" + "lodash.flattendeep": "^4.4.0", + "node-html-parser": "^5.1.0" } } diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index 7532396165..953c9a22e4 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -1,6 +1,23 @@ const filepath = require('path'); +var HTMLParser = require('node-html-parser'); + +const SCRIPT = 'script'; let filename = ''; +const scripts = []; + +const traverseParseHTML = (ele) => { + ele.childNodes && ele.childNodes.forEach(e => { + if (e.rawTagName === SCRIPT) { + e.childNodes.forEach(item => { + if (item.nodeType === 3) { + scripts.push(item._rawText); + } + }) + } + traverseParseHTML(e); + }); +} const loader = function(source) { const opts = this.query || {}; @@ -12,6 +29,8 @@ const loader = function(source) { ) ); + traverseParseHTML(HTMLParser.parse(source)); + return ` describe(${snapshotFilepath}, async (done) => { window.html_snapshot = async (...argv) => { @@ -22,6 +41,7 @@ const loader = function(source) { } }; __kraken_parse_html__('${source.replace(/\n/g, '').replace(/'/g, '"')}'); + ${scripts.map(script => script)} }); `; } From b9bbffefc7914a31a91a5b7ea0e162c84f13bcff Mon Sep 17 00:00:00 2001 From: answershuto Date: Tue, 9 Nov 2021 11:09:06 +0800 Subject: [PATCH 012/167] :sparkles: feat: Delete content of script element for avoid to script repetition. --- integration_tests/scripts/html_loader.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index 953c9a22e4..3c4aa4dda1 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -10,9 +10,12 @@ const traverseParseHTML = (ele) => { ele.childNodes && ele.childNodes.forEach(e => { if (e.rawTagName === SCRIPT) { e.childNodes.forEach(item => { + // TextNode of script element. if (item.nodeType === 3) { scripts.push(item._rawText); } + // Delete content of script element for avoid to script repetition. + item._rawText = ''; }) } traverseParseHTML(e); @@ -29,10 +32,11 @@ const loader = function(source) { ) ); - traverseParseHTML(HTMLParser.parse(source)); + let root = HTMLParser.parse(source); + traverseParseHTML(root); return ` - describe(${snapshotFilepath}, async (done) => { + describe(${snapshotFilepath}, async () => { window.html_snapshot = async (...argv) => { if (argv.length === 0) { await snapshot(null, '${snapshotFilepath}'); @@ -40,7 +44,7 @@ const loader = function(source) { await snapshot(argv[0], '${snapshotFilepath}'); } }; - __kraken_parse_html__('${source.replace(/\n/g, '').replace(/'/g, '"')}'); + __kraken_parse_html__('${root.toString().replace(/\n/g, '')}'); ${scripts.map(script => script)} }); `; From 8e3449ec17f28c2b79aa579150d8da23ab10b3f8 Mon Sep 17 00:00:00 2001 From: answershuto Date: Tue, 9 Nov 2021 16:26:19 +0800 Subject: [PATCH 013/167] :sparkles: feat: script should join to string. --- integration_tests/scripts/html_loader.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index 3c4aa4dda1..9a8f2a8ed3 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -36,8 +36,8 @@ const loader = function(source) { traverseParseHTML(root); return ` - describe(${snapshotFilepath}, async () => { - window.html_snapshot = async (...argv) => { + describe('html-${filepath.basename(filename)}', () => { + html_snapshot = async (...argv) => { if (argv.length === 0) { await snapshot(null, '${snapshotFilepath}'); } else if (argv.length === 1) { @@ -45,7 +45,7 @@ const loader = function(source) { } }; __kraken_parse_html__('${root.toString().replace(/\n/g, '')}'); - ${scripts.map(script => script)} + ${scripts.join('\n')} }); `; } From 0ecb7cb83c9fb1786f1662067435c7bd40781e4a Mon Sep 17 00:00:00 2001 From: answershuto Date: Tue, 9 Nov 2021 16:34:32 +0800 Subject: [PATCH 014/167] :sparkles: feat: should auto generate default script to snapshot. --- integration_tests/scripts/html_loader.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index 9a8f2a8ed3..688fbd5610 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -45,7 +45,10 @@ const loader = function(source) { } }; __kraken_parse_html__('${root.toString().replace(/\n/g, '')}'); - ${scripts.join('\n')} + ${ + scripts.length === 0 ? + 'it("should work", async () => { await html_snapshot(); })' : scripts.join('\n') + } }); `; } From 27f1ad946819225fd25c6c6beb5e5902daa3014b Mon Sep 17 00:00:00 2001 From: answershuto Date: Wed, 10 Nov 2021 14:34:59 +0800 Subject: [PATCH 015/167] :recycle: chore: delete invalid code. --- bridge/bindings/qjs/bom/window.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/bridge/bindings/qjs/bom/window.cc b/bridge/bindings/qjs/bom/window.cc index 3f305116d7..087b0bc1e2 100644 --- a/bridge/bindings/qjs/bom/window.cc +++ b/bridge/bindings/qjs/bom/window.cc @@ -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(JS_GetOpaque(this_val, Window::classId())); NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0])}; return window->callNativeMethods("open", 1, arguments); @@ -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(JS_GetOpaque(this_val, Window::classId())); NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0]), jsValueToNativeValue(ctx, argv[1])}; return window->callNativeMethods("scrollBy", 2, arguments); From 3f6008620c4a6f1b3aa4642e6118b22da8302206 Mon Sep 17 00:00:00 2001 From: answershuto Date: Wed, 10 Nov 2021 14:39:05 +0800 Subject: [PATCH 016/167] :sparkles: feat: add navigate type to handleNavigationAction. --- kraken/lib/src/launcher/controller.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index c55802226d..41cef97038 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -369,9 +369,12 @@ class KrakenViewController { if (policy == KrakenNavigationActionPolicy.cancel) return; switch (action.navigationType) { - case KrakenNavigationType.reload: + case KrakenNavigationType.navigate: await rootController.reloadUrl(action.target); break; + case KrakenNavigationType.reload: + await rootController.reloadUrl(action.source!); + break; default: // Navigate and other type, do nothing. } From a67065a467613b14b267590acfc1a27df4cbbad2 Mon Sep 17 00:00:00 2001 From: answershuto Date: Wed, 10 Nov 2021 15:03:23 +0800 Subject: [PATCH 017/167] :fire: feat: _bundlePath and _bundleURL should be deprecated. --- kraken/lib/src/launcher/controller.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index 41cef97038..482278c52f 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -668,23 +668,30 @@ class KrakenController { _bundleByteCode = value; } + @deprecated String? _bundlePath; + @deprecated String? get bundlePath => _bundlePath; + + @deprecated set bundlePath(String? value) { _bundlePath = value; } + @deprecated String? _bundleURL; + @deprecated String? get bundleURL => _bundleURL; + @deprecated set bundleURL(String? value) { if (value == null) return; _bundleURL = value; } - String get origin => _bundleURL ?? _bundlePath ?? 'vm://' + name!; + String get origin => Uri.parse(href).origin; // preload javascript source and cache it. Future loadBundle({ From 6b9498687effaabae32ea0565ac69ffbf41758cb Mon Sep 17 00:00:00 2001 From: answershuto Date: Wed, 10 Nov 2021 15:13:04 +0800 Subject: [PATCH 018/167] :bug: fix: set bundle or url should be save to history modeule. --- kraken/lib/src/launcher/controller.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index 482278c52f..f36ae9a05c 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -637,7 +637,7 @@ class KrakenController { Future reloadUrl(String url) async { assert(!_view._disposed, 'Kraken have already disposed'); - _bundleURL = url; + bundleURL = url; await reload(); } @@ -676,7 +676,10 @@ class KrakenController { @deprecated set bundlePath(String? value) { + if (value == null) return; _bundlePath = value; + // Set bundlePath should set the path to history module. + href = value; } @deprecated @@ -689,6 +692,8 @@ class KrakenController { set bundleURL(String? value) { if (value == null) return; _bundleURL = value; + // Set bundleURL should set the url to history module. + href = value; } String get origin => Uri.parse(href).origin; From 55dee63af033b68d5e5b829a284356b1beb6a6ae Mon Sep 17 00:00:00 2001 From: answershuto Date: Wed, 10 Nov 2021 19:38:04 +0800 Subject: [PATCH 019/167] :sparkles: feat: modify loadURL of widget. --- kraken/lib/widget.dart | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index 773b70ac35..8d73b14c25 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -226,6 +226,7 @@ class Kraken extends StatefulWidget { defineElement(tagName.toUpperCase(), creator); } + @deprecated loadContent(String bundleContent) async { await controller!.unload(); await controller!.loadBundle( @@ -234,26 +235,32 @@ class Kraken extends StatefulWidget { _evalBundle(controller!, animationController); } + @deprecated loadByteCode(Uint8List bytecode) async { await controller!.unload(); await controller!.loadBundle( - bundleByteCode: bytecode + bundleByteCode: bytecode, ); _evalBundle(controller!, animationController); } - loadURL(String bundleURL) async { + + loadURL(String bundleURL, { String? bundleContent, Uint8List? bundleByteCode }) async { await controller!.unload(); await controller!.loadBundle( - bundleURL: bundleURL + bundleURL: bundleURL, + bundleContent: bundleContent, + bundleByteCode: bundleByteCode, ); _evalBundle(controller!, animationController); } - loadPath(String bundlePath) async { + loadPath(String bundlePath, { String? bundleContent, Uint8List? bundleByteCode }) async { await controller!.unload(); await controller!.loadBundle( - bundlePath: bundlePath + bundlePath: bundlePath, + bundleContent: bundleContent, + bundleByteCode: bundleByteCode, ); _evalBundle(controller!, animationController); } @@ -906,7 +913,7 @@ class _KrakenRenderObjectElement extends SingleChildRenderObjectElement { KrakenController controller = (renderObject as RenderObjectWithControllerMixin).controller!; - if (controller.bundleContent == null && controller.bundlePath == null && controller.bundleURL == null) { + if (controller.bundleContent == '' && controller.bundleByteCode == Uint8List(0) && controller.href == '') { return; } From 59ac7e3c1bdce11e934fd86db4127db5e1bf2b02 Mon Sep 17 00:00:00 2001 From: answershuto Date: Wed, 10 Nov 2021 19:38:53 +0800 Subject: [PATCH 020/167] :sparkles: feat: modify history api for add bundleContent. --- kraken/lib/src/launcher/controller.dart | 76 +++++++++++++------------ kraken/lib/src/module/history.dart | 27 ++++++++- 2 files changed, 65 insertions(+), 38 deletions(-) diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index f36ae9a05c..6f13a2b1d6 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -489,15 +489,22 @@ class KrakenController { this.devToolsService, this.uriParser }) : _name = name, - _bundleURL = bundleURL, - _bundlePath = bundlePath, - _bundleContent = bundleContent, _gestureListener = gestureListener { if (kProfileMode) { PerformanceTiming.instance().mark(PERF_CONTROLLER_PROPERTY_INIT); PerformanceTiming.instance().mark(PERF_VIEW_CONTROLLER_INIT_START); } + if (bundleURL != null) { + href = bundleURL; + } else if (bundlePath != null) { + href = bundlePath; + } + + if (bundleContent != null) { + this.bundleContent = bundleContent; + } + _methodChannel = methodChannel; KrakenMethodChannel.setJSMethodCallCallback(this); @@ -620,6 +627,26 @@ class KrakenController { historyModule.href = value; } + set bundleContent(String value) { + HistoryModule historyModule = module.moduleManager.getModule('History')!; + historyModule.bundleContent = value; + } + + String get bundleContent { + HistoryModule historyModule = module.moduleManager.getModule('History')!; + return historyModule.bundleContent; + } + + set bundleByteCode(Uint8List value) { + HistoryModule historyModule = module.moduleManager.getModule('History')!; + historyModule.bundleByteCode = value; + } + + Uint8List get bundleByteCode { + HistoryModule historyModule = module.moduleManager.getModule('History')!; + return historyModule.bundleByteCode; + } + // reload current kraken view. Future reload() async { if (devToolsService != null) { @@ -653,45 +680,22 @@ class KrakenController { } } - String? _bundleContent; - - String? get bundleContent => _bundleContent; - set bundleContent(String? value) { - if (value == null) return; - _bundleContent = value; - } - - Uint8List? _bundleByteCode; - Uint8List? get bundleByteCode => _bundleByteCode; - set bundleByteCode(Uint8List? value) { - if (value == null) return; - _bundleByteCode = value; - } - @deprecated - String? _bundlePath; - - @deprecated - String? get bundlePath => _bundlePath; + String? get bundlePath => href; @deprecated set bundlePath(String? value) { if (value == null) return; - _bundlePath = value; // Set bundlePath should set the path to history module. href = value; } @deprecated - String? _bundleURL; - - @deprecated - String? get bundleURL => _bundleURL; + String? get bundleURL => href; @deprecated set bundleURL(String? value) { if (value == null) return; - _bundleURL = value; // Set bundleURL should set the url to history module. href = value; } @@ -711,12 +715,12 @@ class KrakenController { PerformanceTiming.instance().mark(PERF_JS_BUNDLE_LOAD_START); } - _bundleContent = bundleContent ?? _bundleContent; - _bundlePath = bundlePath ?? _bundlePath; - _bundleURL = bundleURL ?? _bundleURL; - _bundleByteCode = bundleByteCode ?? _bundleByteCode; + this.bundlePath = bundlePath; + this.bundleURL = bundleURL; + this.bundleContent = bundleContent!; + this.bundleByteCode = bundleByteCode!; - String? url = _bundleURL ?? _bundlePath ?? getBundleURLFromEnv() ?? getBundlePathFromEnv(); + String? url = href ?? getBundleURLFromEnv() ?? getBundlePathFromEnv(); if (url == null && methodChannel is KrakenNativeChannel) { url = await (methodChannel as KrakenNativeChannel).getUrl(); @@ -725,15 +729,13 @@ class KrakenController { url = url ?? ''; if (onLoadError != null) { try { - _bundle = await KrakenBundle.getBundle(url, contentOverride: _bundleContent, contextId: view.contextId); + _bundle = await KrakenBundle.getBundle(url, contentOverride: this.bundleContent, contextId: view.contextId); } catch (e, stack) { onLoadError!(FlutterError(e.toString()), stack); } } else { - _bundle = await KrakenBundle.getBundle(url, contentOverride: _bundleContent, contextId: view.contextId); + _bundle = await KrakenBundle.getBundle(url, contentOverride: this.bundleContent, contextId: view.contextId); } - KrakenController controller = KrakenController.getControllerOfJSContextId(view.contextId)!; - controller.href = url; if (kProfileMode) { PerformanceTiming.instance().mark(PERF_JS_BUNDLE_LOAD_END); diff --git a/kraken/lib/src/module/history.dart b/kraken/lib/src/module/history.dart index af464b3dac..5c5b06384a 100644 --- a/kraken/lib/src/module/history.dart +++ b/kraken/lib/src/module/history.dart @@ -6,16 +6,20 @@ import 'dart:collection'; import 'dart:convert'; +import 'dart:async'; +import 'dart:typed_data'; import 'package:kraken/dom.dart'; import 'package:kraken/kraken.dart'; import 'package:kraken/module.dart'; class HistoryItem { - HistoryItem(this.href, this.state, this.needJump); + HistoryItem(this.href, this.state, this.needJump, { this.bundleContent, this.bundleByteCode }); final String href; final dynamic state; final bool needJump; + String? bundleContent; + Uint8List? bundleByteCode; } class HistoryModule extends BaseModule { @@ -31,11 +35,32 @@ class HistoryModule extends BaseModule { if (_previousStack.isEmpty) return ''; return _previousStack.first.href; } + set href(String value) { HistoryItem history = HistoryItem(value, null, true); _addItem(history); } + set bundleContent(String value) { + if (_previousStack.isEmpty) return; + _previousStack.first.bundleContent = value; + } + + String get bundleContent { + if (_previousStack.isEmpty) return ''; + return _previousStack.first.bundleContent!; + } + + set bundleByteCode(Uint8List value) { + if (_previousStack.isEmpty) return; + _previousStack.first.bundleByteCode = value; + } + + Uint8List get bundleByteCode { + if (_previousStack.isEmpty) return Uint8List(0); + return _previousStack.first.bundleByteCode!; + } + void _addItem(HistoryItem historyItem) { if (_previousStack.isNotEmpty && historyItem.href == _previousStack.first.href) return; From b9344aeb7ac0996db378f09e2053ac2320bb6961 Mon Sep 17 00:00:00 2001 From: answershuto Date: Wed, 10 Nov 2021 19:56:02 +0800 Subject: [PATCH 021/167] fix: dealwith href. --- kraken/lib/src/launcher/controller.dart | 9 ++++++--- kraken/lib/widget.dart | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index 6f13a2b1d6..c2c5befc47 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -715,12 +715,15 @@ class KrakenController { PerformanceTiming.instance().mark(PERF_JS_BUNDLE_LOAD_START); } - this.bundlePath = bundlePath; - this.bundleURL = bundleURL; + if (bundlePath != null) { + this.bundlePath = bundlePath; + } else if (bundleURL != null) { + this.bundleURL = bundleURL; + } this.bundleContent = bundleContent!; this.bundleByteCode = bundleByteCode!; - String? url = href ?? getBundleURLFromEnv() ?? getBundlePathFromEnv(); + String? url = href.isEmpty ? (getBundleURLFromEnv() ?? getBundlePathFromEnv()) : href; if (url == null && methodChannel is KrakenNativeChannel) { url = await (methodChannel as KrakenNativeChannel).getUrl(); diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index 8d73b14c25..e6fcc05d25 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -236,10 +236,10 @@ class Kraken extends StatefulWidget { } @deprecated - loadByteCode(Uint8List bytecode) async { + loadByteCode(Uint8List bundleByteCode) async { await controller!.unload(); await controller!.loadBundle( - bundleByteCode: bytecode, + bundleByteCode: bundleByteCode, ); _evalBundle(controller!, animationController); } From 9e7aaf13c11d556cb7a63347c2040af5ac4cc9a3 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 11 Nov 2021 15:36:34 +0800 Subject: [PATCH 022/167] :recycle: refactor: refactor api of Kraken Widget to load bundle. --- kraken/lib/src/dom/elements/head.dart | 3 +- kraken/lib/src/launcher/bundle.dart | 58 +++++++++++++------------ kraken/lib/src/launcher/controller.dart | 55 ++++++++++++----------- kraken/lib/src/module/history.dart | 18 ++++---- kraken/lib/widget.dart | 24 +++------- 5 files changed, 76 insertions(+), 82 deletions(-) diff --git a/kraken/lib/src/dom/elements/head.dart b/kraken/lib/src/dom/elements/head.dart index aead271b1b..d5325f790a 100644 --- a/kraken/lib/src/dom/elements/head.dart +++ b/kraken/lib/src/dom/elements/head.dart @@ -65,7 +65,8 @@ class ScriptElement extends Element { void _fetchBundle(String src) async { if (src.isNotEmpty && isConnected) { 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((_) { diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index a567d7ddee..82734f72a5 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -53,7 +53,7 @@ abstract class KrakenBundle { KrakenBundle(this.url); // Unique resource locator. - final Uri url; + final String url; late ByteData rawBundle; // JS Content in UTF-8 bytes. Uint8List? byteCode; @@ -69,36 +69,37 @@ abstract class KrakenBundle { // Bundle contentType. ContentType contentType = ContentType.binary; - Future resolve(); - - static Future getBundle(String path, { String? contentOverride, required int contextId }) async { - KrakenBundle bundle; - + Future resolve(int contextId) async { if (kDebugMode) { - print('Kraken getting bundle for contextId: $contextId, path: $path'); + print('Kraken getting bundle for contextId: $contextId, url: $url'); } - Uri uri = Uri.parse(path); + Uri uri = Uri.parse(url); KrakenController? controller = KrakenController.getControllerOfJSContextId(contextId); - if (controller != null && !isAssetAbsolutePath(path)) { + if (controller != null && !isAssetAbsolutePath(url)) { 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); - } else { - bundle = AssetsBundle(uri); - } + static KrakenBundle fromUrl(String url) { + return NetworkBundle(url); + } - await bundle.resolve(); + static KrakenBundle fromPath(String path) { + return AssetsBundle(path); + } + + static KrakenBundle fromUrlWithContent(String url, String content) { + return RawBundle.fromString(content, url); + } - return bundle; + static KrakenBundle fromUrlWithByteCode(String url, Uint8List bytecode) { + return RawBundle.fromByteCode(bytecode, url); } + Future eval(int contextId) async { - if (!isResolved) await resolve(); + if (!isResolved) await resolve(contextId); if (kProfileMode) { PerformanceTiming.instance().mark(PERF_JS_BUNDLE_EVAL_START); @@ -132,31 +133,31 @@ 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) { + RawBundle.fromByteCode(Uint8List byteCode, String url) + : super(url) { this.byteCode = byteCode; } @override - Future resolve() async { + Future resolve(int contextId) async { isResolved = true; } } class NetworkBundle extends KrakenBundle { - int contextId; - NetworkBundle(Uri url, { required this.contextId }) + NetworkBundle(String url) : super(url); @override - Future resolve() async { + Future resolve(int contextId) async { 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(url)), contextId: contextId); bundle.httpClient.userAgent = getKrakenInfo().userAgent; String absoluteURL = url.toString(); rawBundle = await bundle.load(absoluteURL); @@ -223,15 +224,16 @@ class NetworkAssetBundle extends AssetBundle { } class AssetsBundle extends KrakenBundle { - AssetsBundle(Uri url) + AssetsBundle(String url) : super(url); @override - Future resolve() async { + Future resolve(int contextId) async { // JSBundle get default bundle manifest. manifest = AppManifest(); String localPath = url.toString(); rawBundle = await rootBundle.load(localPath); isResolved = true; + return this; } } diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index c2c5befc47..e37dd784d4 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -439,6 +439,8 @@ class KrakenController { WidgetDelegate? widgetDelegate; + KrakenBundle? bundle; + LoadHandler? onLoad; // Error handler when load bundle failed. @@ -482,6 +484,7 @@ class KrakenController { KrakenNavigationDelegate? navigationDelegate, KrakenMethodChannel? methodChannel, this.widgetDelegate, + this.bundle, this.onLoad, this.onLoadError, this.onJSError, @@ -495,16 +498,6 @@ class KrakenController { PerformanceTiming.instance().mark(PERF_VIEW_CONTROLLER_INIT_START); } - if (bundleURL != null) { - href = bundleURL; - } else if (bundlePath != null) { - href = bundlePath; - } - - if (bundleContent != null) { - this.bundleContent = bundleContent; - } - _methodChannel = methodChannel; KrakenMethodChannel.setJSMethodCallCallback(this); @@ -526,6 +519,16 @@ class KrakenController { _module = KrakenModuleController(this, contextId); + if (bundleURL != null) { + href = bundleURL; + } else if (bundlePath != null) { + href = bundlePath; + } + + if (bundleContent != null) { + this.bundleContent = bundleContent; + } + assert(!_controllerMap.containsKey(contextId), 'found exist contextId of KrakenController, contextId: $contextId'); _controllerMap[contextId] = this; @@ -555,10 +558,6 @@ class KrakenController { return _module; } - // the bundle manager which used to download javascript source and run. - KrakenBundle? _bundle; - KrakenBundle? get bundle => _bundle; - Uri get referrer { if (bundleURL != null) { return Uri.parse(bundleURL!); @@ -627,22 +626,24 @@ class KrakenController { historyModule.href = value; } - set bundleContent(String value) { + set bundleContent(String? value) { + if (value == null) return; HistoryModule historyModule = module.moduleManager.getModule('History')!; historyModule.bundleContent = value; } - String get bundleContent { + String? get bundleContent { HistoryModule historyModule = module.moduleManager.getModule('History')!; return historyModule.bundleContent; } - set bundleByteCode(Uint8List value) { + set bundleByteCode(Uint8List? value) { + if (value == null) return; HistoryModule historyModule = module.moduleManager.getModule('History')!; historyModule.bundleByteCode = value; } - Uint8List get bundleByteCode { + Uint8List? get bundleByteCode { HistoryModule historyModule = module.moduleManager.getModule('History')!; return historyModule.bundleByteCode; } @@ -720,10 +721,14 @@ class KrakenController { } else if (bundleURL != null) { this.bundleURL = bundleURL; } - this.bundleContent = bundleContent!; - this.bundleByteCode = bundleByteCode!; - String? url = href.isEmpty ? (getBundleURLFromEnv() ?? getBundlePathFromEnv()) : href; + if (bundleContent != null) { + this.bundleContent = bundleContent; + } else if (bundleByteCode != null) { + this.bundleByteCode = bundleByteCode; + } + + String? url = href.isEmpty ? (getBundleURLFromEnv() ?? getBundlePathFromEnv()) : href; if (url == null && methodChannel is KrakenNativeChannel) { url = await (methodChannel as KrakenNativeChannel).getUrl(); @@ -732,12 +737,12 @@ class KrakenController { url = url ?? ''; if (onLoadError != null) { try { - _bundle = await KrakenBundle.getBundle(url, contentOverride: this.bundleContent, contextId: view.contextId); + await bundle?.resolve(view.contextId); } catch (e, stack) { onLoadError!(FlutterError(e.toString()), stack); } } else { - _bundle = await KrakenBundle.getBundle(url, contentOverride: this.bundleContent, contextId: view.contextId); + await bundle?.resolve(view.contextId); } if (kProfileMode) { @@ -748,8 +753,8 @@ class KrakenController { // execute preloaded javascript source Future evalBundle() async { assert(!_view._disposed, 'Kraken have already disposed'); - if (_bundle != null) { - await _bundle!.eval(_view.contextId); + if (bundle != null) { + await bundle!.eval(_view.contextId); // trigger DOMContentLoaded event module.requestAnimationFrame((_) { Event event = Event(EVENT_DOM_CONTENT_LOADED); diff --git a/kraken/lib/src/module/history.dart b/kraken/lib/src/module/history.dart index 5c5b06384a..b83708c3aa 100644 --- a/kraken/lib/src/module/history.dart +++ b/kraken/lib/src/module/history.dart @@ -41,24 +41,24 @@ class HistoryModule extends BaseModule { _addItem(history); } - set bundleContent(String value) { - if (_previousStack.isEmpty) return; + set bundleContent(String? value) { + if (_previousStack.isEmpty || value == null) return; _previousStack.first.bundleContent = value; } - String get bundleContent { - if (_previousStack.isEmpty) return ''; - return _previousStack.first.bundleContent!; + String? get bundleContent { + if (_previousStack.isEmpty) return null; + return _previousStack.first.bundleContent; } - set bundleByteCode(Uint8List value) { - if (_previousStack.isEmpty) return; + set bundleByteCode(Uint8List? value) { + if (_previousStack.isEmpty || value == null) return; _previousStack.first.bundleByteCode = value; } - Uint8List get bundleByteCode { + Uint8List? get bundleByteCode { if (_previousStack.isEmpty) return Uint8List(0); - return _previousStack.first.bundleByteCode!; + return _previousStack.first.bundleByteCode; } void _addItem(HistoryItem historyItem) { diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index e6fcc05d25..7aec312bdb 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -164,17 +164,8 @@ class Kraken extends StatefulWidget { // the height of krakenWidget final double? viewportHeight; - // The initial URL to load. - final String? bundleURL; - - // The initial assets path to load. - final String? bundlePath; - - // The initial raw javascript content to load. - final String? bundleContent; - - // The initial raw bytecode to load. - final Uint8List? bundleByteCode; + // The initial bundle to load. + final KrakenBundle? bundle; // The animationController of Flutter Route object. // Pass this object to KrakenWidget to make sure Kraken execute JavaScripts scripts after route transition animation completed. @@ -273,10 +264,7 @@ class Kraken extends StatefulWidget { Key? key, this.viewportWidth, this.viewportHeight, - this.bundleURL, - this.bundlePath, - this.bundleContent, - this.bundleByteCode, + this.bundle, this.onLoad, this.navigationDelegate, this.javaScriptChannel, @@ -836,9 +824,7 @@ This situation often happened when you trying creating kraken when FlutterView n viewportHeight, background: _krakenWidget.background, showPerformanceOverlay: Platform.environment[ENABLE_PERFORMANCE_OVERLAY] != null, - bundleContent: _krakenWidget.bundleContent, - bundleURL: _krakenWidget.bundleURL, - bundlePath: _krakenWidget.bundlePath, + bundle: _krakenWidget.bundle, onLoad: _krakenWidget.onLoad, onLoadError: _krakenWidget.onLoadError, onJSError: _krakenWidget.onJSError, @@ -913,7 +899,7 @@ class _KrakenRenderObjectElement extends SingleChildRenderObjectElement { KrakenController controller = (renderObject as RenderObjectWithControllerMixin).controller!; - if (controller.bundleContent == '' && controller.bundleByteCode == Uint8List(0) && controller.href == '') { + if (controller.bundleContent == null && controller.bundleByteCode == null && controller.href.isEmpty) { return; } From 4a4710bc78a00f1971476c1a14beb097c30981a5 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 11 Nov 2021 16:00:58 +0800 Subject: [PATCH 023/167] :sparkles: resolve should be mustCallSuper. --- kraken/lib/src/launcher/bundle.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 82734f72a5..683a51b4da 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -54,6 +54,9 @@ abstract class KrakenBundle { // Unique resource locator. final String url; + + Uri? uri; + late ByteData rawBundle; // JS Content in UTF-8 bytes. Uint8List? byteCode; @@ -69,15 +72,16 @@ abstract class KrakenBundle { // Bundle contentType. ContentType contentType = ContentType.binary; + @mustCallSuper Future resolve(int contextId) async { if (kDebugMode) { print('Kraken getting bundle for contextId: $contextId, url: $url'); } - Uri uri = Uri.parse(url); + uri = Uri.parse(url); KrakenController? controller = KrakenController.getControllerOfJSContextId(contextId); if (controller != null && !isAssetAbsolutePath(url)) { - uri = controller.uriParser!.resolve(Uri.parse(controller.href), uri); + uri = controller.uriParser!.resolve(Uri.parse(controller.href), uri!); } } @@ -145,6 +149,7 @@ class RawBundle extends KrakenBundle { @override Future resolve(int contextId) async { + super.resolve(contextId); isResolved = true; } } @@ -155,6 +160,7 @@ class NetworkBundle extends KrakenBundle { @override Future 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, Uri.parse(url)), contextId: contextId); @@ -229,6 +235,7 @@ class AssetsBundle extends KrakenBundle { @override Future resolve(int contextId) async { + super.resolve(contextId); // JSBundle get default bundle manifest. manifest = AppManifest(); String localPath = url.toString(); From 80ee76932b50a83156298d0f4dd2ce627c4fb397 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 11 Nov 2021 16:04:15 +0800 Subject: [PATCH 024/167] :recycle: chore: modify url to src. --- kraken/lib/src/launcher/bundle.dart | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 683a51b4da..777cbcc17f 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -50,11 +50,12 @@ bool isAssetAbsolutePath(String path) { } abstract class KrakenBundle { - KrakenBundle(this.url); + KrakenBundle(this.src); // Unique resource locator. - final String url; + final String src; + // Customize the parsed uri by uriParser. Uri? uri; late ByteData rawBundle; @@ -75,12 +76,12 @@ abstract class KrakenBundle { @mustCallSuper Future resolve(int contextId) async { if (kDebugMode) { - print('Kraken getting bundle for contextId: $contextId, url: $url'); + print('Kraken getting bundle for contextId: $contextId, src: $src'); } - uri = Uri.parse(url); + uri = Uri.parse(src); KrakenController? controller = KrakenController.getControllerOfJSContextId(contextId); - if (controller != null && !isAssetAbsolutePath(url)) { + if (controller != null && !isAssetAbsolutePath(src)) { uri = controller.uriParser!.resolve(Uri.parse(controller.href), uri!); } } @@ -111,23 +112,23 @@ abstract class KrakenBundle { // For raw javascript code or bytecode from API directly. if (content != null) { - evaluateScripts(contextId, content!, url.toString(), lineOffset); + 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) { @@ -163,9 +164,9 @@ class NetworkBundle extends KrakenBundle { super.resolve(contextId); KrakenController controller = KrakenController.getControllerOfJSContextId(contextId)!; Uri baseUrl = Uri.parse(controller.href); - NetworkAssetBundle bundle = NetworkAssetBundle(controller.uriParser!.resolve(baseUrl, Uri.parse(url)), contextId: contextId); + NetworkAssetBundle bundle = NetworkAssetBundle(controller.uriParser!.resolve(baseUrl, Uri.parse(src)), contextId: contextId); bundle.httpClient.userAgent = getKrakenInfo().userAgent; - String absoluteURL = url.toString(); + String absoluteURL = src; rawBundle = await bundle.load(absoluteURL); contentType = bundle.contentType; isResolved = true; @@ -238,7 +239,7 @@ class AssetsBundle extends KrakenBundle { super.resolve(contextId); // JSBundle get default bundle manifest. manifest = AppManifest(); - String localPath = url.toString(); + String localPath = src; rawBundle = await rootBundle.load(localPath); isResolved = true; return this; From e64bd77fd791056ef17198ebbe828901125c997b Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 11 Nov 2021 16:09:03 +0800 Subject: [PATCH 025/167] :recycle: chore: modify kraken widget of main.dart. --- kraken/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/example/lib/main.dart b/kraken/example/lib/main.dart index f651bb7195..c56ebccf33 100644 --- a/kraken/example/lib/main.dart +++ b/kraken/example/lib/main.dart @@ -92,7 +92,7 @@ class _MyHomePageState extends State { devToolsService: ChromeDevToolsService(), viewportWidth: viewportSize.width - queryData.padding.horizontal, viewportHeight: viewportSize.height - appBar.preferredSize.height - queryData.padding.vertical, - bundleURL: 'assets/bundle.js', + bundle: KrakenBundle.fromPath('assets/bundle.js'), ), )); } From 2910bf39ba3a06702ab8f00b7f54ff9f822f8649 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 11 Nov 2021 16:19:56 +0800 Subject: [PATCH 026/167] :white_check_mark: test: modify main of test. --- integration_tests/lib/main.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration_tests/lib/main.dart b/integration_tests/lib/main.dart index d7e91f415a..6edb56d559 100644 --- a/integration_tests/lib/main.dart +++ b/integration_tests/lib/main.dart @@ -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'; @@ -82,7 +83,7 @@ void main() async { var kraken = krakenMap[i] = Kraken( viewportWidth: 360, viewportHeight: 640, - bundleContent: 'console.log("Starting integration tests...")', + bundle: KrakenBundle.fromUrlWithContent('', 'console.log("Starting integration tests...")'), disableViewportWidthAssertion: true, disableViewportHeightAssertion: true, javaScriptChannel: javaScriptChannel, From a7c84c52857938520eecae99ecd96591e393dd03 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 11 Nov 2021 17:00:08 +0800 Subject: [PATCH 027/167] :recycle: chore: dealwith lint. --- kraken/lib/src/dom/window.dart | 2 +- kraken/lib/src/launcher/controller.dart | 14 +++++++------- kraken/lib/src/module/navigation.dart | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/kraken/lib/src/dom/window.dart b/kraken/lib/src/dom/window.dart index a6b21330ed..f6ab9e4d18 100644 --- a/kraken/lib/src/dom/window.dart +++ b/kraken/lib/src/dom/window.dart @@ -37,7 +37,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); } diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index e37dd784d4..6e3660492e 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -559,10 +559,10 @@ class KrakenController { } Uri get referrer { - if (bundleURL != null) { - return Uri.parse(bundleURL!); - } else if (bundlePath != null) { - return Directory(bundlePath!).uri; + if (bundle is NetworkBundle) { + return Uri.parse(href); + } else if (bundle is AssetsBundle) { + return Directory(href).uri; } else { return fallbackBundleUri(_view.contextId); } @@ -665,7 +665,7 @@ class KrakenController { Future reloadUrl(String url) async { assert(!_view._disposed, 'Kraken have already disposed'); - bundleURL = url; + href = url; await reload(); } @@ -717,9 +717,9 @@ class KrakenController { } if (bundlePath != null) { - this.bundlePath = bundlePath; + href = bundlePath; } else if (bundleURL != null) { - this.bundleURL = bundleURL; + href = bundleURL; } if (bundleContent != null) { diff --git a/kraken/lib/src/module/navigation.dart b/kraken/lib/src/module/navigation.dart index efe00e4231..9e80144c03 100644 --- a/kraken/lib/src/module/navigation.dart +++ b/kraken/lib/src/module/navigation.dart @@ -38,12 +38,12 @@ class NavigationModule extends BaseModule { // Navigate kraken page to target Url. Future goTo(String targetUrl) async { - String? sourceUrl = moduleManager!.controller.bundlePath ?? moduleManager!.controller.bundleURL; + String? sourceUrl = moduleManager!.controller.href; Uri targetUri = Uri.parse(targetUrl); - Uri? sourceUri = sourceUrl != null ? Uri.parse(sourceUrl) : null; + Uri sourceUri = Uri.parse(sourceUrl); - if (sourceUri == null || targetUri != sourceUri) { + if (targetUri != sourceUri) { await moduleManager!.controller.view.handleNavigationAction(sourceUrl, targetUrl, KrakenNavigationType.reload); } } From cc5ecfe6ca416ae911cbefc0e7f4bad52e9917c0 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 11 Nov 2021 17:33:32 +0800 Subject: [PATCH 028/167] :bug: fix: set href should new bundle. --- kraken/lib/src/launcher/controller.dart | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index 6e3660492e..a07d51e51f 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -370,10 +370,10 @@ class KrakenViewController { switch (action.navigationType) { case KrakenNavigationType.navigate: - await rootController.reloadUrl(action.target); + await rootController.reload(url: action.target); break; case KrakenNavigationType.reload: - await rootController.reloadUrl(action.source!); + await rootController.reload(url: action.source!); break; default: // Navigate and other type, do nothing. @@ -624,6 +624,8 @@ class KrakenController { set href(String value) { HistoryModule historyModule = module.moduleManager.getModule('History')!; historyModule.href = value; + // set href should new bundle. + bundle = KrakenBundle.fromUrl(value); } set bundleContent(String? value) { @@ -649,13 +651,15 @@ class KrakenController { } // reload current kraken view. - Future reload() async { + Future reload({ String? url }) async { + assert(!_view._disposed, 'Kraken have already disposed'); + if (devToolsService != null) { devToolsService!.willReload(); } await unload(); - await loadBundle(bundleURL: href); + await loadBundle(bundleURL: url ?? href); await evalBundle(); if (devToolsService != null) { @@ -663,12 +667,6 @@ class KrakenController { } } - Future reloadUrl(String url) async { - assert(!_view._disposed, 'Kraken have already disposed'); - href = url; - await reload(); - } - void dispose() { _view.dispose(); _module.dispose(); @@ -716,6 +714,7 @@ class KrakenController { PerformanceTiming.instance().mark(PERF_JS_BUNDLE_LOAD_START); } + // Load bundle need push curret href to history. if (bundlePath != null) { href = bundlePath; } else if (bundleURL != null) { From d30bcdb280c6ca07dcad8b234ac497d3ebe7fc1f Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 15 Nov 2021 11:22:13 +0800 Subject: [PATCH 029/167] :sparkles: feat: modify api of kraken for plugin test. --- integration_tests/lib/plugin.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/lib/plugin.dart b/integration_tests/lib/plugin.dart index 7b440a2ebc..777c8d3a39 100644 --- a/integration_tests/lib/plugin.dart +++ b/integration_tests/lib/plugin.dart @@ -70,7 +70,7 @@ void main() async { var kraken = krakenMap[i] = Kraken( viewportWidth: 360, viewportHeight: 640, - bundleContent: 'console.log("Starting Plugin tests...")', + bundle: KrakenBundle.fromUrlWithContent('', 'console.log("Starting Plugin tests...")'), disableViewportWidthAssertion: true, disableViewportHeightAssertion: true, uriParser: IntegrationTestUriParser(), From 3b482cd875c2df188bb98a0961c77d85650d1570 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 15 Nov 2021 15:17:16 +0800 Subject: [PATCH 030/167] :bug: fix: href of script should work. --- kraken/lib/src/dom/elements/head.dart | 2 +- kraken/lib/src/launcher/bundle.dart | 16 +++++++++------- kraken/lib/src/launcher/controller.dart | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/kraken/lib/src/dom/elements/head.dart b/kraken/lib/src/dom/elements/head.dart index d5325f790a..aca84fe5d9 100644 --- a/kraken/lib/src/dom/elements/head.dart +++ b/kraken/lib/src/dom/elements/head.dart @@ -65,7 +65,7 @@ class ScriptElement extends Element { void _fetchBundle(String src) async { if (src.isNotEmpty && isConnected) { try { - KrakenBundle bundle = KrakenBundle.fromUrl(src); + KrakenBundle bundle = KrakenBundle.fromHref(src); await bundle.resolve(elementManager.contextId); await bundle.eval(elementManager.contextId); // Successful load. diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 777cbcc17f..ab1db3b523 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -84,21 +84,23 @@ abstract class KrakenBundle { if (controller != null && !isAssetAbsolutePath(src)) { uri = controller.uriParser!.resolve(Uri.parse(controller.href), uri!); } - } - static KrakenBundle fromUrl(String url) { - return NetworkBundle(url); + isResolved = true; } - static KrakenBundle fromPath(String path) { - return AssetsBundle(path); + static KrakenBundle fromHref(String href) { + if (isAssetAbsolutePath(href)) { + return AssetsBundle(href); + } else { + return NetworkBundle(href); + } } - static KrakenBundle fromUrlWithContent(String url, String content) { + static KrakenBundle fromHrefWithContent(String url, String content) { return RawBundle.fromString(content, url); } - static KrakenBundle fromUrlWithByteCode(String url, Uint8List bytecode) { + static KrakenBundle fromHrefWithByteCode(String url, Uint8List bytecode) { return RawBundle.fromByteCode(bytecode, url); } diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index a07d51e51f..cd30502c9f 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -625,7 +625,7 @@ class KrakenController { HistoryModule historyModule = module.moduleManager.getModule('History')!; historyModule.href = value; // set href should new bundle. - bundle = KrakenBundle.fromUrl(value); + bundle = KrakenBundle.fromHref(value); } set bundleContent(String? value) { From 27055710fa4bb504c76d44b15a03db73d10e3d20 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 15 Nov 2021 15:18:48 +0800 Subject: [PATCH 031/167] :sparkles: chore: modify main of kraken example. --- kraken/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/example/lib/main.dart b/kraken/example/lib/main.dart index c56ebccf33..f0051e6128 100644 --- a/kraken/example/lib/main.dart +++ b/kraken/example/lib/main.dart @@ -92,7 +92,7 @@ class _MyHomePageState extends State { devToolsService: ChromeDevToolsService(), viewportWidth: viewportSize.width - queryData.padding.horizontal, viewportHeight: viewportSize.height - appBar.preferredSize.height - queryData.padding.vertical, - bundle: KrakenBundle.fromPath('assets/bundle.js'), + bundle: KrakenBundle.fromHref('assets/bundle.js'), ), )); } From dccc7b655e1c01afc1159e81b40fa3a779e9045d Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 15 Nov 2021 15:20:05 +0800 Subject: [PATCH 032/167] :recycle: test: modify bundle of test. --- integration_tests/lib/main.dart | 2 +- integration_tests/lib/plugin.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration_tests/lib/main.dart b/integration_tests/lib/main.dart index 6edb56d559..58dc0b9312 100644 --- a/integration_tests/lib/main.dart +++ b/integration_tests/lib/main.dart @@ -83,7 +83,7 @@ void main() async { var kraken = krakenMap[i] = Kraken( viewportWidth: 360, viewportHeight: 640, - bundle: KrakenBundle.fromUrlWithContent('', 'console.log("Starting integration tests...")'), + bundle: KrakenBundle.fromHrefWithContent('', 'console.log("Starting integration tests...")'), disableViewportWidthAssertion: true, disableViewportHeightAssertion: true, javaScriptChannel: javaScriptChannel, diff --git a/integration_tests/lib/plugin.dart b/integration_tests/lib/plugin.dart index 777c8d3a39..c608705586 100644 --- a/integration_tests/lib/plugin.dart +++ b/integration_tests/lib/plugin.dart @@ -70,7 +70,7 @@ void main() async { var kraken = krakenMap[i] = Kraken( viewportWidth: 360, viewportHeight: 640, - bundle: KrakenBundle.fromUrlWithContent('', 'console.log("Starting Plugin tests...")'), + bundle: KrakenBundle.fromHrefWithContent('', 'console.log("Starting Plugin tests...")'), disableViewportWidthAssertion: true, disableViewportHeightAssertion: true, uriParser: IntegrationTestUriParser(), From 817fdb9bd87e5bc4aeea4ccf98083fd9039b4c78 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 15 Nov 2021 15:48:52 +0800 Subject: [PATCH 033/167] :recycle: chore: loadURL and loadPath should be deprecated. --- kraken/lib/widget.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index 7aec312bdb..fce81fd041 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -235,7 +235,7 @@ class Kraken extends StatefulWidget { _evalBundle(controller!, animationController); } - + @deprecated loadURL(String bundleURL, { String? bundleContent, Uint8List? bundleByteCode }) async { await controller!.unload(); await controller!.loadBundle( @@ -246,6 +246,7 @@ class Kraken extends StatefulWidget { _evalBundle(controller!, animationController); } + @deprecated loadPath(String bundlePath, { String? bundleContent, Uint8List? bundleByteCode }) async { await controller!.unload(); await controller!.loadBundle( From a8723463106b3b3ca727f9552d236d5ed11ee5d7 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 15 Nov 2021 16:22:26 +0800 Subject: [PATCH 034/167] :recycle: chore: modify api of widget. --- kraken/lib/src/launcher/controller.dart | 36 +++++++++------------- kraken/lib/src/launcher/launcher.dart | 22 ++++++++++---- kraken/lib/widget.dart | 40 ++++++++++++++++++++----- 3 files changed, 63 insertions(+), 35 deletions(-) diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index cd30502c9f..08caf4b10a 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -624,8 +624,6 @@ class KrakenController { set href(String value) { HistoryModule historyModule = module.moduleManager.getModule('History')!; historyModule.href = value; - // set href should new bundle. - bundle = KrakenBundle.fromHref(value); } set bundleContent(String? value) { @@ -659,7 +657,7 @@ class KrakenController { } await unload(); - await loadBundle(bundleURL: url ?? href); + await loadBundle(bundle: KrakenBundle.fromHref(url ?? href)); await evalBundle(); if (devToolsService != null) { @@ -703,10 +701,7 @@ class KrakenController { // preload javascript source and cache it. Future loadBundle({ - String? bundleContent, - String? bundlePath, - String? bundleURL, - Uint8List? bundleByteCode + KrakenBundle? bundle }) async { assert(!_view._disposed, 'Kraken have already disposed'); @@ -715,25 +710,22 @@ class KrakenController { } // Load bundle need push curret href to history. - if (bundlePath != null) { - href = bundlePath; - } else if (bundleURL != null) { - href = bundleURL; - } - - if (bundleContent != null) { - this.bundleContent = bundleContent; - } else if (bundleByteCode != null) { - this.bundleByteCode = bundleByteCode; - } + if (bundle != null) { + String? url = bundle.uri.toString().isEmpty ? (getBundleURLFromEnv() ?? getBundlePathFromEnv()) : href; + if (url == null && methodChannel is KrakenNativeChannel) { + url = await (methodChannel as KrakenNativeChannel).getUrl(); + } + href = url ?? ''; - String? url = href.isEmpty ? (getBundleURLFromEnv() ?? getBundlePathFromEnv()) : href; + if (bundle.byteCode != null) { + bundleByteCode = bundle.byteCode; + } - if (url == null && methodChannel is KrakenNativeChannel) { - url = await (methodChannel as KrakenNativeChannel).getUrl(); + if (bundle.content != null) { + bundleContent = bundle.content; + } } - url = url ?? ''; if (onLoadError != null) { try { await bundle?.resolve(view.contextId); diff --git a/kraken/lib/src/launcher/launcher.dart b/kraken/lib/src/launcher/launcher.dart index 425f654644..81280ee8ab 100644 --- a/kraken/lib/src/launcher/launcher.dart +++ b/kraken/lib/src/launcher/launcher.dart @@ -5,7 +5,8 @@ import 'dart:io'; import 'dart:ui'; - +import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/rendering.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/kraken.dart'; @@ -22,6 +23,7 @@ void launch({ String? bundleURL, String? bundlePath, String? bundleContent, + Uint8List? bundleByteCode, bool? debugEnableInspector, Color background = _white, DevToolsService? devToolsService, @@ -44,10 +46,20 @@ void launch({ controller.view.attachView(RendererBinding.instance!.renderView); - await controller.loadBundle( - bundleURL: bundleURL, - bundlePath: bundlePath, - bundleContent: bundleContent); + if (bundleURL == null) { + await controller.loadBundle(); + } else { + KrakenBundle bundle; + if (bundleByteCode != null) { + bundle = KrakenBundle.fromHrefWithByteCode(bundleURL, bundleByteCode); + } else if (bundleContent != null) { + bundle = KrakenBundle.fromHrefWithContent(bundleURL, bundleContent); + } else { + bundle = KrakenBundle.fromHref(bundleURL); + } + + await controller.loadBundle(bundle: bundle); + } await controller.evalBundle(); } diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index fce81fd041..55ff6e2ecf 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -217,11 +217,19 @@ class Kraken extends StatefulWidget { defineElement(tagName.toUpperCase(), creator); } + loadHref(KrakenBundle bundle) async { + await controller!.unload(); + await controller!.loadBundle( + bundle: bundle + ); + _evalBundle(controller!, animationController); + } + @deprecated loadContent(String bundleContent) async { await controller!.unload(); await controller!.loadBundle( - bundleContent: bundleContent + bundle: KrakenBundle.fromHrefWithContent('', bundleContent) ); _evalBundle(controller!, animationController); } @@ -230,7 +238,7 @@ class Kraken extends StatefulWidget { loadByteCode(Uint8List bundleByteCode) async { await controller!.unload(); await controller!.loadBundle( - bundleByteCode: bundleByteCode, + bundle: KrakenBundle.fromHrefWithByteCode('', bundleByteCode) ); _evalBundle(controller!, animationController); } @@ -238,10 +246,18 @@ class Kraken extends StatefulWidget { @deprecated loadURL(String bundleURL, { String? bundleContent, Uint8List? bundleByteCode }) async { await controller!.unload(); + + KrakenBundle bundle; + if (bundleByteCode != null) { + bundle = KrakenBundle.fromHrefWithByteCode(bundleURL, bundleByteCode); + } else if (bundleContent != null) { + bundle = KrakenBundle.fromHrefWithContent(bundleURL, bundleContent); + } else { + bundle = KrakenBundle.fromHref(bundleURL); + } + await controller!.loadBundle( - bundleURL: bundleURL, - bundleContent: bundleContent, - bundleByteCode: bundleByteCode, + bundle: bundle ); _evalBundle(controller!, animationController); } @@ -249,10 +265,18 @@ class Kraken extends StatefulWidget { @deprecated loadPath(String bundlePath, { String? bundleContent, Uint8List? bundleByteCode }) async { await controller!.unload(); + + KrakenBundle bundle; + if (bundleByteCode != null) { + bundle = KrakenBundle.fromHrefWithByteCode(bundlePath, bundleByteCode); + } else if (bundleContent != null) { + bundle = KrakenBundle.fromHrefWithContent(bundlePath, bundleContent); + } else { + bundle = KrakenBundle.fromHref(bundlePath); + } + await controller!.loadBundle( - bundlePath: bundlePath, - bundleContent: bundleContent, - bundleByteCode: bundleByteCode, + bundle: KrakenBundle.fromHref(bundlePath) ); _evalBundle(controller!, animationController); } From 7ac7ea3bc07ea8da459a82cc62321d0d13e8651a Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 15 Nov 2021 16:30:38 +0800 Subject: [PATCH 035/167] :recycle: chore: dealwith lint. --- kraken/example/lib/main.dart | 2 +- kraken/lib/widget.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kraken/example/lib/main.dart b/kraken/example/lib/main.dart index f0051e6128..c6c5a5c203 100644 --- a/kraken/example/lib/main.dart +++ b/kraken/example/lib/main.dart @@ -63,7 +63,7 @@ class _MyHomePageState extends State { controller: textEditingController, onSubmitted: (value) { textEditingController.text = value; - _kraken?.loadURL(value); + _kraken?.loadHref(KrakenBundle.fromHref(value)); }, decoration: InputDecoration( hintText: 'Enter a app url', diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index 55ff6e2ecf..2a50f3dfe6 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -276,7 +276,7 @@ class Kraken extends StatefulWidget { } await controller!.loadBundle( - bundle: KrakenBundle.fromHref(bundlePath) + bundle: bundle ); _evalBundle(controller!, animationController); } From b74af608a4c8d2daccf4f2727c3eea6b5cfcba11 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 15 Nov 2021 16:53:16 +0800 Subject: [PATCH 036/167] :recycle: chore: dealwith lint. --- integration_tests/lib/plugin.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/integration_tests/lib/plugin.dart b/integration_tests/lib/plugin.dart index c608705586..4f1b64e485 100644 --- a/integration_tests/lib/plugin.dart +++ b/integration_tests/lib/plugin.dart @@ -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'; From 8bc69843d0d73b4777c912010a42780295271c54 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 15 Nov 2021 17:00:01 +0800 Subject: [PATCH 037/167] :recycle: chore: modify kraken widget of performance. --- performance_tests/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/performance_tests/lib/main.dart b/performance_tests/lib/main.dart index abd14bcb09..d586fdb0a9 100644 --- a/performance_tests/lib/main.dart +++ b/performance_tests/lib/main.dart @@ -90,7 +90,7 @@ class _MyHomePageState extends State { child: _kraken = Kraken( viewportWidth: viewportSize.width - queryData.padding.horizontal, viewportHeight: viewportSize.height - appBar.preferredSize.height - queryData.padding.vertical, - bundleURL: 'https://kraken.oss-cn-hangzhou.aliyuncs.com/data/cvd3r6f068.js', + bundle: KrakenBundle.fromHref('https://kraken.oss-cn-hangzhou.aliyuncs.com/data/cvd3r6f068.js'), onLoad: (KrakenController controller) { Timer(Duration(seconds: 4), () { exit(0); From cca12b350c1c44d3f4b2931924ffc73d6dc2e603 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 16 Nov 2021 20:48:16 +0800 Subject: [PATCH 038/167] refactor: refactor image provider. --- kraken/lib/src/css/background.dart | 3 +- kraken/lib/src/css/values/url.dart | 33 ++----- kraken/lib/src/dom/elements/img.dart | 7 +- .../src/painting/image_provider_factory.dart | 88 ++++++++++++------- 4 files changed, 73 insertions(+), 58 deletions(-) diff --git a/kraken/lib/src/css/background.dart b/kraken/lib/src/css/background.dart index e1fd43e19d..b48ec2f26f 100644 --- a/kraken/lib/src/css/background.dart +++ b/kraken/lib/src/css/background.dart @@ -8,6 +8,7 @@ import 'dart:ui'; import 'package:flutter/painting.dart'; import 'package:flutter/rendering.dart'; +import 'package:kraken/painting.dart'; import 'package:kraken/css.dart'; import 'package:kraken/launcher.dart'; import 'package:kraken/rendering.dart'; @@ -191,7 +192,7 @@ class CSSBackgroundImage { Uri uri = Uri.parse(url); if (url.isNotEmpty) { uri = controller.uriParser!.resolve(Uri.parse(controller.href), uri); - return CSSUrl.parseUrl(uri, contextId: controller.view.contextId); + return getImageProvider(uri, contextId: controller.view.contextId); } } } diff --git a/kraken/lib/src/css/values/url.dart b/kraken/lib/src/css/values/url.dart index 8c475c0fbe..f29adf987a 100644 --- a/kraken/lib/src/css/values/url.dart +++ b/kraken/lib/src/css/values/url.dart @@ -3,40 +3,23 @@ * Author: Kraken Team. */ -import 'dart:io'; - -import 'package:flutter/rendering.dart'; import 'package:kraken/painting.dart'; // CSS Values and Units: https://drafts.csswg.org/css-values-3/#urls class CSSUrl { - static ImageProvider? parseUrl(Uri resolvedUri, { cache = 'auto', int? contextId }) { - - ImageProvider? imageProvider; + static ImageType parseImageUrl(Uri resolvedUri, { cache = 'auto' }) { if (resolvedUri.isScheme('HTTP') || resolvedUri.isScheme('HTTPS')) { - // @TODO: Caching also works after image downloaded. - ImageType cacheType = (cache == 'store' || cache == 'auto') - ? ImageType.cached - : ImageType.network; - imageProvider = getImageProviderFactory(cacheType)(resolvedUri, [contextId]); + return (cache == 'store' || cache == 'auto') + ? ImageType.cached + : ImageType.network; } else if (resolvedUri.isScheme('FILE')) { - File file = File.fromUri(resolvedUri); - imageProvider = getImageProviderFactory(ImageType.file)(resolvedUri, file); + return ImageType.file; } else if (resolvedUri.isScheme('DATA')) { - // Data URL: https://tools.ietf.org/html/rfc2397 - // dataurl := "data:" [ mediatype ] [ ";base64" ] "," data - UriData data = UriData.fromUri(resolvedUri); - if (data.isBase64) { - imageProvider = getImageProviderFactory(ImageType.dataUrl)(resolvedUri, data.contentAsBytes()); - } + return ImageType.dataUrl; } else if (resolvedUri.isScheme('BLOB')) { - // @TODO: support blob file url - imageProvider = getImageProviderFactory(ImageType.blob)(resolvedUri); + return ImageType.blob; } else { - // Fallback to asset image - imageProvider = getImageProviderFactory(ImageType.assets)(resolvedUri); + return ImageType.assets; } - - return imageProvider; } } diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 76ab7dde57..47197f0f1b 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -12,6 +12,7 @@ import 'package:flutter/scheduler.dart'; import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; +import 'package:kraken/painting.dart'; import 'package:kraken/rendering.dart'; const String IMAGE = 'IMG'; @@ -321,8 +322,10 @@ class ImageElement extends Element { Uri base = Uri.parse(elementManager.controller.href); Uri resolvedUri = elementManager.controller.uriParser!.resolve(base, Uri.parse(_source!)); - ImageProvider? imageProvider = _imageProvider ?? CSSUrl.parseUrl(resolvedUri, - cache: properties['caching'], contextId: elementManager.contextId); + ImageProvider? imageProvider = getImageProvider(resolvedUri, + cache: properties['caching'], + contextId: elementManager.contextId + ); if (imageProvider != null) { _imageProvider = imageProvider; diff --git a/kraken/lib/src/painting/image_provider_factory.dart b/kraken/lib/src/painting/image_provider_factory.dart index 10e2c383ea..67395d2370 100644 --- a/kraken/lib/src/painting/image_provider_factory.dart +++ b/kraken/lib/src/painting/image_provider_factory.dart @@ -10,13 +10,31 @@ import 'dart:typed_data'; import 'package:flutter/cupertino.dart'; import 'package:flutter/painting.dart'; import 'package:kraken/bridge.dart'; +import 'package:kraken/css.dart'; import 'package:kraken/foundation.dart'; import 'package:kraken/painting.dart'; /// This class allows user to customize Kraken's image loading. +class ImageProviderParams {} + +class CachedNetworkImageProviderParams extends ImageProviderParams { + int? contextId; + CachedNetworkImageProviderParams(this.contextId); +} + +class FileImageProviderParams extends ImageProviderParams { + File file; + FileImageProviderParams(this.file); +} + +class DataUrlImageProviderParams extends ImageProviderParams { + Uint8List bytes; + DataUrlImageProviderParams(this.bytes); +} + /// A factory function allow user to build an customized ImageProvider class. -typedef ImageProviderFactory = ImageProvider? Function(Uri uri, [dynamic param]); +typedef ImageProviderFactory = ImageProvider? Function(Uri uri, ImageProviderParams params); /// defines the types of supported image source. enum ImageType { @@ -75,7 +93,35 @@ ImageProviderFactory _dataUrlProviderFactory = defaultDataUrlProviderFactory; ImageProviderFactory _blobProviderFactory = defaultBlobProviderFactory; ImageProviderFactory _assetsProviderFactory = defaultAssetsProvider; -ImageProviderFactory getImageProviderFactory(ImageType imageType) { +ImageProvider? getImageProvider(Uri resolvedUri, { int? contextId, cache = 'auto' }) { + ImageType imageType = CSSUrl.parseImageUrl(resolvedUri, cache: cache); + ImageProviderFactory factory = _getImageProviderFactory(imageType); + + switch(imageType) { + case ImageType.cached: + return factory(resolvedUri, CachedNetworkImageProviderParams(contextId)); + case ImageType.network: + return factory(resolvedUri, CachedNetworkImageProviderParams(contextId)); + case ImageType.file: + File file = File.fromUri(resolvedUri); + return factory(resolvedUri, FileImageProviderParams(file)); + case ImageType.dataUrl: + // Data URL: https://tools.ietf.org/html/rfc2397 + // dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + UriData data = UriData.fromUri(resolvedUri); + if (data.isBase64) { + return factory(resolvedUri, DataUrlImageProviderParams(data.contentAsBytes())); + } + return null; + case ImageType.blob: + // TODO: support blob data type + return null; + case ImageType.assets: + return factory(resolvedUri, ImageProviderParams()); + } +} + +ImageProviderFactory _getImageProviderFactory(ImageType imageType) { switch (imageType) { case ImageType.cached: return _cachedProviderFactory; @@ -117,55 +163,37 @@ void setCustomImageProviderFactory(ImageType imageType, ImageProviderFactory cus } } -int? _getContextId(param) { - int? contextId; - if (param is List && param.isNotEmpty) { - contextId = param[0]; - } - return contextId; -} - /// default ImageProviderFactory implementation of [ImageType.cached] -ImageProvider defaultCachedProviderFactory(Uri uri, [param]) { - int? contextId = _getContextId(param); - return CachedNetworkImage(uri.toString(), contextId: contextId); +ImageProvider defaultCachedProviderFactory(Uri uri, ImageProviderParams params) { + return CachedNetworkImage(uri.toString(), contextId: (params as CachedNetworkImageProviderParams).contextId); } /// default ImageProviderFactory implementation of [ImageType.network] -ImageProvider defaultNetworkProviderFactory(Uri uri, [param]) { - int? contextId = _getContextId(param); +ImageProvider defaultNetworkProviderFactory(Uri uri, ImageProviderParams params) { NetworkImage networkImage = NetworkImage(uri.toString(), headers: { HttpHeaders.userAgentHeader: getKrakenInfo().userAgent, - HttpHeaderContext: contextId.toString(), + HttpHeaderContext: (params as CachedNetworkImageProviderParams).contextId.toString(), }); return networkImage; } /// default ImageProviderFactory implementation of [ImageType.file] -ImageProvider? defaultFileProviderFactory(Uri uri, [param]) { - ImageProvider? _imageProvider; - if (param is File) { - _imageProvider = FileImage(param); - } - return _imageProvider; +ImageProvider? defaultFileProviderFactory(Uri uri, ImageProviderParams params) { + return FileImage((params as FileImageProviderParams).file); } /// default ImageProviderFactory implementation of [ImageType.dataUrl]. -ImageProvider? defaultDataUrlProviderFactory(Uri uri, [param]) { - ImageProvider? _imageProvider; - if (param is Uint8List) { - _imageProvider = MemoryImage(param); - } - return _imageProvider; +ImageProvider? defaultDataUrlProviderFactory(Uri uri, ImageProviderParams params) { + return MemoryImage((params as DataUrlImageProviderParams).bytes); } /// default ImageProviderFactory implementation of [ImageType.blob]. -ImageProvider? defaultBlobProviderFactory(Uri uri, [param]) { +ImageProvider? defaultBlobProviderFactory(Uri uri, ImageProviderParams params) { // @TODO: support blob file url return null; } /// default ImageProviderFactory implementation of [ImageType.assets]. -ImageProvider defaultAssetsProvider(Uri uri, [param]) { +ImageProvider defaultAssetsProvider(Uri uri, ImageProviderParams params) { return AssetImage(uri.toString()); } From 1e02faeb09ca3fa10642b5705d961130211dc773 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 18 Nov 2021 14:48:40 +0800 Subject: [PATCH 039/167] :sparkles: feat: add html_parse to loader. --- integration_tests/scripts/html_loader.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index 688fbd5610..f2cec860c9 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -34,20 +34,28 @@ const loader = function(source) { let root = HTMLParser.parse(source); traverseParseHTML(root); + const htmlString = root.toString().replace(/\n/g, ''); return ` describe('html-${filepath.basename(filename)}', () => { - html_snapshot = async (...argv) => { + // Use html_snapshot to snapshot in html file. + const html_snapshot = async (...argv) => { if (argv.length === 0) { await snapshot(null, '${snapshotFilepath}'); } else if (argv.length === 1) { await snapshot(argv[0], '${snapshotFilepath}'); } }; - __kraken_parse_html__('${root.toString().replace(/\n/g, '')}'); + + // Use html_parse to parser html in html file. + const html_parse = __kraken_parse_html__('${htmlString}'); + ${ + // Eval script of html. scripts.length === 0 ? - 'it("should work", async () => { await html_snapshot(); })' : scripts.join('\n') + 'it("should work", async () => {\ + await html_snapshot();\ + })' : scripts.join('\n') } }); `; From ef48d2f0e2efac0e6dc11aed49e24bb5c3493211 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 18 Nov 2021 14:49:25 +0800 Subject: [PATCH 040/167] :sparkles: feat: add html_parse to default test of html. --- integration_tests/scripts/html_loader.js | 1 + 1 file changed, 1 insertion(+) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index f2cec860c9..67831ff2f9 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -54,6 +54,7 @@ const loader = function(source) { // Eval script of html. scripts.length === 0 ? 'it("should work", async () => {\ + html_parse();\ await html_snapshot();\ })' : scripts.join('\n') } From 8949ca0b1d0315a236086af631bb779d96439f23 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 18 Nov 2021 14:55:03 +0800 Subject: [PATCH 041/167] :art: chore: delete log. --- bridge/bridge_test_qjs.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/bridge/bridge_test_qjs.cc b/bridge/bridge_test_qjs.cc index 5109673225..6fa4a9415e 100644 --- a/bridge/bridge_test_qjs.cc +++ b/bridge/bridge_test_qjs.cc @@ -197,8 +197,6 @@ static JSValue runGC(QjsContext* ctx, JSValueConst this_val, int argc, JSValueCo } static JSValue parseHTML(QjsContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - KRAKEN_LOG(VERBOSE) << "parseHTML111" - << "argc=" << argc; auto* context = static_cast(JS_GetContextOpaque(ctx)); if (argc == 1) { From c787b149cc5f6128ada21bc771c4f11d91f7c066 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 18 Nov 2021 15:08:07 +0800 Subject: [PATCH 042/167] :bug: fix: JSValue should be free. --- bridge/bridge_test_qjs.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/bridge/bridge_test_qjs.cc b/bridge/bridge_test_qjs.cc index 6fa4a9415e..c12f0181dd 100644 --- a/bridge/bridge_test_qjs.cc +++ b/bridge/bridge_test_qjs.cc @@ -210,6 +210,7 @@ static JSValue parseHTML(QjsContext* ctx, JSValueConst this_val, int argc, JSVal auto* body = static_cast(JS_GetOpaque(bodyValue, binding::qjs::Element::classId())); binding::qjs::HTMLParser::parseHTML(strHTML, body); + JS_FreeValue(ctx, bodyValue); JS_FreeValue(ctx, html); } From c9e8769a82ab1623f7e1933c7479a85ce33e8f75 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 18 Nov 2021 15:12:25 +0800 Subject: [PATCH 043/167] :sparkles: feat: modify html loader to modify it. --- integration_tests/scripts/html_loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index 67831ff2f9..d09dacd693 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -48,7 +48,7 @@ const loader = function(source) { }; // Use html_parse to parser html in html file. - const html_parse = __kraken_parse_html__('${htmlString}'); + const html_parse = () => __kraken_parse_html__('${htmlString}'); ${ // Eval script of html. From 66b7ac8f407a916316535faad9b15dc4a0787305 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 18 Nov 2021 15:25:11 +0800 Subject: [PATCH 044/167] :white_check_mark: test: add test of html. --- .../dom/elements/div.html.7e6f39401.png | Bin 0 -> 15786 bytes integration_tests/specs/dom/elements/div.html | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 integration_tests/snapshots/dom/elements/div.html.7e6f39401.png create mode 100644 integration_tests/specs/dom/elements/div.html diff --git a/integration_tests/snapshots/dom/elements/div.html.7e6f39401.png b/integration_tests/snapshots/dom/elements/div.html.7e6f39401.png new file mode 100644 index 0000000000000000000000000000000000000000..8f2cc29778826b13d944aa4222a196a8745f16b8 GIT binary patch literal 15786 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 + +
Welcome to Your Kraken AppMore information about KrakenVisit http://openkraken.com/
+ \ No newline at end of file From b6e1a013e92c83d2ac1bc1ec7a1e5df4c25455a3 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 18 Nov 2021 15:39:57 +0800 Subject: [PATCH 045/167] :art: chore: add newline. --- integration_tests/specs/dom/elements/div.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/specs/dom/elements/div.html b/integration_tests/specs/dom/elements/div.html index cdc2fd7813..0ddf03d04b 100644 --- a/integration_tests/specs/dom/elements/div.html +++ b/integration_tests/specs/dom/elements/div.html @@ -1,4 +1,4 @@
Welcome to Your Kraken AppMore information about KrakenVisit http://openkraken.com/
- \ No newline at end of file + From 11e078039dc8841287a063dfb89943478fa62cc7 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 18 Nov 2021 16:28:34 +0800 Subject: [PATCH 046/167] :bug: html JSValue should not be free. --- bridge/bridge_test_qjs.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bridge/bridge_test_qjs.cc b/bridge/bridge_test_qjs.cc index c12f0181dd..8b98bbd79e 100644 --- a/bridge/bridge_test_qjs.cc +++ b/bridge/bridge_test_qjs.cc @@ -200,7 +200,7 @@ static JSValue parseHTML(QjsContext* ctx, JSValueConst this_val, int argc, JSVal auto* context = static_cast(JS_GetContextOpaque(ctx)); if (argc == 1) { - JSValueConst html = argv[0]; + JSValue& html = argv[0]; std::string strHTML = binding::qjs::jsValueToStdString(ctx, html); @@ -211,7 +211,6 @@ static JSValue parseHTML(QjsContext* ctx, JSValueConst this_val, int argc, JSVal binding::qjs::HTMLParser::parseHTML(strHTML, body); JS_FreeValue(ctx, bodyValue); - JS_FreeValue(ctx, html); } return JS_NULL; From b9ab70545485c6d1445e0fe7d06d43ed4e8a13ee Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Fri, 19 Nov 2021 15:56:22 +0800 Subject: [PATCH 047/167] feat: cache logical content width/height to avoid recursive computation in nested layout --- kraken/lib/src/css/render_style.dart | 132 ++++++++++++++++++++++++ kraken/lib/src/rendering/box_model.dart | 12 ++- 2 files changed, 140 insertions(+), 4 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 05f146f28c..736efdc0e2 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -239,6 +239,79 @@ class RenderStyle } } + double? computeLogicalContentWidth() { + RenderBoxModel current = renderBoxModel!; + RenderStyle renderStyle = this; + double? logicalWidth; + + CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay; + switch (effectiveDisplay) { + case CSSDisplay.block: + case CSSDisplay.flex: + case CSSDisplay.sliver: + // Use width directly if defined. + if (renderStyle.width.isNotAuto) { + logicalWidth = renderStyle.width.computedValue; + + // Use tight constraints if constraints is tight and width not exist. + } else if (current.hasSize && current.constraints.hasTightWidth) { + logicalWidth = current.constraints.maxWidth; + + // Block element (except replaced element) will stretch to the content width of its parent. + } else if (current is! RenderIntrinsic && + current.parent != null && + current.parent is RenderBoxModel + ) { + RenderBoxModel parent = current.parent as RenderBoxModel; + logicalWidth = parent.logicalContentWidth; + } + break; + case CSSDisplay.inlineBlock: + case CSSDisplay.inlineFlex: + if (renderStyle.width.isNotAuto) { + logicalWidth = renderStyle.width.computedValue; + } else if (current.hasSize && current.constraints.hasTightWidth) { + logicalWidth = current.constraints.maxWidth; + } + break; + case CSSDisplay.inline: + break; + default: + break; + } + + double? intrinsicRatio = current.intrinsicRatio; + + // Get width by intrinsic ratio for replaced element if width is auto. + if (logicalWidth == null && intrinsicRatio != null) { + logicalWidth = renderStyle.getWidthByIntrinsicRatio(); + } + + // Constrain width by min-width and max-width. + if (renderStyle.minWidth.isNotAuto) { + double minWidth = renderStyle.minWidth.computedValue; + if (logicalWidth != null && logicalWidth < minWidth) { + logicalWidth = minWidth; + } + } + if (renderStyle.maxWidth.isNotNone) { + double maxWidth = renderStyle.maxWidth.computedValue; + if (logicalWidth != null && logicalWidth > maxWidth) { + logicalWidth = maxWidth; + } + } + + double? logicalContentWidth; + // Subtract padding and border width to get content width. + if (logicalWidth != null) { + logicalContentWidth = logicalWidth - + renderStyle.border.horizontal - + renderStyle.padding.horizontal; + } + + return logicalContentWidth; + } + // Content width of render box model calculated from style. double? getLogicalContentWidth() { RenderStyle renderStyle = this; @@ -340,6 +413,65 @@ class RenderStyle } } + double? computeLogicalContentHeight() { + RenderBoxModel current = renderBoxModel!; + RenderStyle renderStyle = this; + double? logicalHeight; + + CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay; + + // Inline element has no height. + if (effectiveDisplay != CSSDisplay.inline) { + if (renderStyle.height.isNotAuto) { + logicalHeight = renderStyle.height.computedValue; + + // Use tight constraints if constraints is tight and height not exist. + } else if (current.hasSize && current.constraints.hasTightHeight) { + logicalHeight = current.constraints.maxHeight; + + } else { + if (current.parent != null && current.parent is RenderBoxModel) { + RenderBoxModel parent = current.parent as RenderBoxModel; + RenderStyle parentRenderStyle = parent.renderStyle; + if (CSSSizingMixin.isStretchChildHeight(parentRenderStyle, renderStyle)) { + logicalHeight = parent.logicalContentHeight; + } + } + } + } + + double? intrinsicRatio = current.intrinsicRatio; + + // Get height by intrinsic ratio for replaced element if height is auto. + if (logicalHeight == null && intrinsicRatio != null) { + logicalHeight = renderStyle.getHeightByIntrinsicRatio(); + } + + // Constrain height by min-height and max-height. + if (renderStyle.minHeight.isNotAuto) { + double minHeight = renderStyle.minHeight.computedValue; + if (logicalHeight != null && logicalHeight < minHeight) { + logicalHeight = minHeight; + } + } + if (renderStyle.maxHeight.isNotNone) { + double maxHeight = renderStyle.maxHeight.computedValue; + if (logicalHeight != null && logicalHeight > maxHeight) { + logicalHeight = maxHeight; + } + } + + double? logicalContentHeight; + // Subtract padding and border width to get content width. + if (logicalHeight != null) { + logicalContentHeight = logicalHeight - + renderStyle.border.horizontal - + renderStyle.padding.horizontal; + } + + return logicalContentHeight; + } + // Content height of render box model calculated from style. double? getLogicalContentHeight() { RenderStyle renderStyle = this; diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index c332f43021..88619c27a6 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -873,8 +873,10 @@ class RenderBoxModel extends RenderBox double? maxHeight = renderStyle.maxHeight.isNone ? null : renderStyle.maxHeight.computedValue; // Content size calculated from style - logicalContentWidth = renderStyle.getLogicalContentWidth(); - logicalContentHeight = renderStyle.getLogicalContentHeight(); + logicalContentWidth = renderStyle.computeLogicalContentWidth(); +// logicalContentWidth = renderStyle.getLogicalContentWidth(); + logicalContentHeight = renderStyle.computeLogicalContentHeight(); +// logicalContentHeight = renderStyle.getLogicalContentHeight(); // Box size calculated from style double? logicalWidth = logicalContentWidth != null @@ -1027,8 +1029,10 @@ class RenderBoxModel extends RenderBox // Deflate padding constraints. boxConstraints = renderStyle.deflatePaddingConstraints(boxConstraints); - logicalContentWidth = renderStyle.getLogicalContentWidth(); - logicalContentHeight = renderStyle.getLogicalContentHeight(); +// logicalContentWidth = renderStyle.getLogicalContentWidth(); + logicalContentWidth = renderStyle.computeLogicalContentWidth(); +// logicalContentHeight = renderStyle.getLogicalContentHeight(); + logicalContentHeight = renderStyle.computeLogicalContentHeight(); if (!isScrollingContentBox && (logicalContentWidth != null || logicalContentHeight != null)) { double minWidth; From 67d27e50702c482488b6c41980108c2e008c6e22 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Fri, 19 Nov 2021 20:04:47 +0800 Subject: [PATCH 048/167] fix: content width/height should not be negative --- kraken/lib/src/css/render_style.dart | 10 ++++++++-- kraken/lib/src/rendering/box_model.dart | 8 ++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 736efdc0e2..10bc02ebff 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -307,6 +307,9 @@ class RenderStyle logicalContentWidth = logicalWidth - renderStyle.border.horizontal - renderStyle.padding.horizontal; + // Logical width may be smaller than its border and padding width, + // in this case, content width will be negative which is illegal. + logicalContentWidth = math.max(0, logicalContentWidth); } return logicalContentWidth; @@ -465,8 +468,11 @@ class RenderStyle // Subtract padding and border width to get content width. if (logicalHeight != null) { logicalContentHeight = logicalHeight - - renderStyle.border.horizontal - - renderStyle.padding.horizontal; + renderStyle.border.vertical - + renderStyle.padding.vertical; + // Logical height may be smaller than its border and padding width, + // in this case, content height will be negative which is illegal. + logicalContentHeight = math.max(0, logicalContentHeight); } return logicalContentHeight; diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index f13f461f8a..ea387aa740 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -870,10 +870,10 @@ class RenderBoxModel extends RenderBox double? maxHeight = renderStyle.maxHeight.isNone ? null : renderStyle.maxHeight.computedValue; // Content size calculated from style - logicalContentWidth = renderStyle.computeLogicalContentWidth(); // logicalContentWidth = renderStyle.getLogicalContentWidth(); - logicalContentHeight = renderStyle.computeLogicalContentHeight(); // logicalContentHeight = renderStyle.getLogicalContentHeight(); + logicalContentWidth = renderStyle.computeLogicalContentWidth(); + logicalContentHeight = renderStyle.computeLogicalContentHeight(); // Box size calculated from style double? logicalWidth = logicalContentWidth != null @@ -1027,9 +1027,9 @@ class RenderBoxModel extends RenderBox boxConstraints = renderStyle.deflatePaddingConstraints(boxConstraints); // logicalContentWidth = renderStyle.getLogicalContentWidth(); - logicalContentWidth = renderStyle.computeLogicalContentWidth(); // logicalContentHeight = renderStyle.getLogicalContentHeight(); - logicalContentHeight = renderStyle.computeLogicalContentHeight(); +// logicalContentWidth = renderStyle.computeLogicalContentWidth(); +// logicalContentHeight = renderStyle.computeLogicalContentHeight(); if (!isScrollingContentBox && (logicalContentWidth != null || logicalContentHeight != null)) { double minWidth; From a328f72dd8590833430270f8c5c51b04587f0c5b Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Tue, 23 Nov 2021 11:08:56 +0800 Subject: [PATCH 049/167] fix: margin should be subtracted when calculating logical width/height --- kraken/lib/src/css/render_style.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 10bc02ebff..dc691409f7 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -264,6 +264,10 @@ class RenderStyle ) { RenderBoxModel parent = current.parent as RenderBoxModel; logicalWidth = parent.logicalContentWidth; + // Should subtract horizontal margin of own from its parent content width. + if (logicalWidth != null) { + logicalWidth -= renderStyle.margin.horizontal; + } } break; case CSSDisplay.inlineBlock: @@ -438,6 +442,10 @@ class RenderStyle RenderStyle parentRenderStyle = parent.renderStyle; if (CSSSizingMixin.isStretchChildHeight(parentRenderStyle, renderStyle)) { logicalHeight = parent.logicalContentHeight; + // Should subtract vertical margin of own from its parent content height. + if (logicalHeight != null) { + logicalHeight -= renderStyle.margin.vertical; + } } } } From d068c2d2ce74e999fd9fe798fe98560f67c54ba8 Mon Sep 17 00:00:00 2001 From: answershuto Date: Tue, 23 Nov 2021 14:26:30 +0800 Subject: [PATCH 050/167] :art: chore: modify filepath to path. --- integration_tests/scripts/html_loader.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index d09dacd693..b8e890b766 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -1,4 +1,4 @@ -const filepath = require('path'); +const path = require('path'); var HTMLParser = require('node-html-parser'); const SCRIPT = 'script'; @@ -24,11 +24,11 @@ const traverseParseHTML = (ele) => { const loader = function(source) { const opts = this.query || {}; - const snapshotFilepath = filepath.relative( + const snapshotFilepath = path.relative( opts.workspacePath, - filepath.join( + path.join( opts.snapshotPath, - filepath.relative(opts.testPath, filename), + path.relative(opts.testPath, filename), ) ); @@ -37,7 +37,7 @@ const loader = function(source) { const htmlString = root.toString().replace(/\n/g, ''); return ` - describe('html-${filepath.basename(filename)}', () => { + describe('html-${path.basename(filename)}', () => { // Use html_snapshot to snapshot in html file. const html_snapshot = async (...argv) => { if (argv.length === 0) { From a9b4d790852fbd99d46ea49297e0850bb99de462 Mon Sep 17 00:00:00 2001 From: answershuto Date: Tue, 23 Nov 2021 14:27:02 +0800 Subject: [PATCH 051/167] :art: chore: modify var to const. --- integration_tests/scripts/html_loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index b8e890b766..4abe98f035 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -1,5 +1,5 @@ const path = require('path'); -var HTMLParser = require('node-html-parser'); +const HTMLParser = require('node-html-parser'); const SCRIPT = 'script'; From 0a938001a7af8b6449f99e8eb42252e22398468d Mon Sep 17 00:00:00 2001 From: answershuto Date: Tue, 23 Nov 2021 14:27:40 +0800 Subject: [PATCH 052/167] :art: chore: add ;. --- integration_tests/scripts/html_loader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index 4abe98f035..ceceb65ae5 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -60,10 +60,10 @@ const loader = function(source) { } }); `; -} +}; loader.pitch = (f) => { filename = f; -} +}; module.exports = loader; From 3f52aee66e395c28892e6e385f3f8af023d90daa Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Tue, 23 Nov 2021 19:10:44 +0800 Subject: [PATCH 053/167] feat: move logicalContentWidth/height from renderBoxModel to renderStyle --- kraken/lib/src/css/render_style.dart | 17 +++++++---- kraken/lib/src/rendering/box_model.dart | 39 +++++++++++-------------- kraken/lib/src/rendering/flex.dart | 20 +++++++------ kraken/lib/src/rendering/flow.dart | 4 --- 4 files changed, 39 insertions(+), 41 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index dc691409f7..0385f66dc1 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -57,6 +57,12 @@ class RenderStyle required this.target, }); + // Content width which is calculated from render style. + double? logicalContentWidth; + + // Content height which is calculated from render style. + double? logicalContentHeight; + dynamic getProperty(String name) { RenderStyle renderStyle = this; switch (name) { @@ -259,11 +265,10 @@ class RenderStyle // Block element (except replaced element) will stretch to the content width of its parent. } else if (current is! RenderIntrinsic && - current.parent != null && - current.parent is RenderBoxModel + renderStyle.parent != null ) { - RenderBoxModel parent = current.parent as RenderBoxModel; - logicalWidth = parent.logicalContentWidth; + RenderStyle parentRenderStyle = renderStyle.parent!; + logicalWidth = parentRenderStyle.logicalContentWidth; // Should subtract horizontal margin of own from its parent content width. if (logicalWidth != null) { logicalWidth -= renderStyle.margin.horizontal; @@ -437,11 +442,11 @@ class RenderStyle logicalHeight = current.constraints.maxHeight; } else { - if (current.parent != null && current.parent is RenderBoxModel) { + if (current.parent != null && renderStyle.parent != null) { RenderBoxModel parent = current.parent as RenderBoxModel; RenderStyle parentRenderStyle = parent.renderStyle; if (CSSSizingMixin.isStretchChildHeight(parentRenderStyle, renderStyle)) { - logicalHeight = parent.logicalContentHeight; + logicalHeight = parentRenderStyle.logicalContentHeight; // Should subtract vertical margin of own from its parent content height. if (logicalHeight != null) { logicalHeight -= renderStyle.margin.vertical; diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index ea387aa740..2970ea4fd4 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -380,8 +380,6 @@ class RenderLayoutBox extends RenderBoxModel /// Common layout size (including flow and flexbox layout) calculation logic Size getLayoutSize({ - double? logicalContentWidth, - double? logicalContentHeight, double? contentWidth, double? contentHeight, }) { @@ -389,8 +387,8 @@ class RenderLayoutBox extends RenderBoxModel double? layoutHeight = contentHeight; // Size which is specified by sizing styles - double? specifiedWidth = logicalContentWidth; - double? specifiedHeight = logicalContentHeight; + double? specifiedWidth = renderStyle.logicalContentWidth; + double? specifiedHeight = renderStyle.logicalContentHeight; // Flex basis takes priority over main size in flex item. if (parent is RenderFlexLayout) { RenderBoxModel? parentRenderBoxModel = parent as RenderBoxModel?; @@ -872,15 +870,15 @@ class RenderBoxModel extends RenderBox // Content size calculated from style // logicalContentWidth = renderStyle.getLogicalContentWidth(); // logicalContentHeight = renderStyle.getLogicalContentHeight(); - logicalContentWidth = renderStyle.computeLogicalContentWidth(); - logicalContentHeight = renderStyle.computeLogicalContentHeight(); + renderStyle.logicalContentWidth = renderStyle.computeLogicalContentWidth(); + renderStyle.logicalContentHeight = renderStyle.computeLogicalContentHeight(); // Box size calculated from style - double? logicalWidth = logicalContentWidth != null - ? logicalContentWidth! + horizontalPaddingLength + horizontalBorderLength + double? logicalWidth = renderStyle.logicalContentWidth != null + ? renderStyle.logicalContentWidth! + horizontalPaddingLength + horizontalBorderLength : null; - double? logicalHeight = logicalContentHeight != null - ? logicalContentHeight! + verticalPaddingLength + verticalBorderLength + double? logicalHeight = renderStyle.logicalContentHeight != null + ? renderStyle.logicalContentHeight! + verticalPaddingLength + verticalBorderLength : null; // Constraints @@ -997,12 +995,6 @@ class RenderBoxModel extends RenderBox Size? _contentSize; Size get contentSize => _contentSize ?? Size.zero; - /// Logical content width calculated from style - double? logicalContentWidth; - - /// Logical content height calculated from style - double? logicalContentHeight; - double get clientWidth { double width = contentSize.width; width += renderStyle.padding.horizontal; @@ -1031,11 +1023,14 @@ class RenderBoxModel extends RenderBox // logicalContentWidth = renderStyle.computeLogicalContentWidth(); // logicalContentHeight = renderStyle.computeLogicalContentHeight(); + double? logicalContentWidth = renderStyle.logicalContentWidth; + double? logicalContentHeight = renderStyle.logicalContentHeight; + if (!isScrollingContentBox && (logicalContentWidth != null || logicalContentHeight != null)) { double minWidth; - double? maxWidth; + double maxWidth; double minHeight; - double? maxHeight; + double maxHeight; if (boxConstraints.hasTightWidth) { minWidth = maxWidth = boxConstraints.maxWidth; @@ -1063,21 +1058,21 @@ class RenderBoxModel extends RenderBox minHeight = minWidth * intrinsicRatio!; } if (!renderStyle.maxWidth.isNone && renderStyle.maxHeight.isNone) { - maxHeight = maxWidth! * intrinsicRatio!; + maxHeight = maxWidth * intrinsicRatio!; } if (renderStyle.minWidth.isAuto && !renderStyle.minHeight.isAuto) { minWidth = minHeight / intrinsicRatio!; } if (renderStyle.maxWidth.isNone && !renderStyle.maxHeight.isNone) { - maxWidth = maxHeight! / intrinsicRatio!; + maxWidth = maxHeight / intrinsicRatio!; } } _contentConstraints = BoxConstraints( minWidth: minWidth, - maxWidth: maxWidth!, + maxWidth: maxWidth, minHeight: minHeight, - maxHeight: maxHeight! + maxHeight: maxHeight ); } else { _contentConstraints = boxConstraints; diff --git a/kraken/lib/src/rendering/flex.dart b/kraken/lib/src/rendering/flex.dart index 1ede6f5b4b..82730202f9 100644 --- a/kraken/lib/src/rendering/flex.dart +++ b/kraken/lib/src/rendering/flex.dart @@ -495,8 +495,9 @@ class RenderFlexLayout extends RenderLayoutBox { : child.intrinsicHeight!; minMainSize = math.min(contentSize, transferredSize); } else if (child is RenderBoxModel) { - double? specifiedMainSize = - _isHorizontalFlexDirection ? child.logicalContentWidth : child.logicalContentHeight; + double? specifiedMainSize = _isHorizontalFlexDirection + ? child.renderStyle.logicalContentWidth + : child.renderStyle.logicalContentHeight; minMainSize = specifiedMainSize != null ? math.min(contentSize, specifiedMainSize) : contentSize; @@ -724,8 +725,6 @@ class RenderFlexLayout extends RenderLayoutBox { /// If no child exists, stop layout. if (childCount == 0) { Size layoutSize = getLayoutSize( - logicalContentWidth: logicalContentWidth, - logicalContentHeight: logicalContentHeight, contentWidth: 0, contentHeight: 0, ); @@ -761,6 +760,9 @@ class RenderFlexLayout extends RenderLayoutBox { containerSizeMap, ); + double? logicalContentWidth = renderStyle.logicalContentWidth; + double? logicalContentHeight = renderStyle.logicalContentHeight; + /// If no non positioned child exists, stop layout if (runMetrics.isEmpty) { Size preferredSize = Size( @@ -1238,6 +1240,8 @@ class RenderFlexLayout extends RenderLayoutBox { Map containerSizeMap, ) { RenderBox? child = placeholderChild ?? firstChild; + double? logicalContentWidth = renderStyle.logicalContentWidth; + double? logicalContentHeight = renderStyle.logicalContentHeight; // Container's width specified by style or inherited from parent double? containerWidth = 0; @@ -1302,7 +1306,7 @@ class RenderFlexLayout extends RenderLayoutBox { // Flexbox with no size on main axis should adapt the main axis size with children. double initialFreeSpace = mainSizeType != BoxSizeType.automatic ? - maxMainSize! - totalSpace : 0; + maxMainSize - totalSpace : 0; bool isFlexGrow = initialFreeSpace > 0 && totalFlexGrow > 0; bool isFlexShrink = initialFreeSpace < 0 && totalFlexShrink > 0; @@ -1344,8 +1348,8 @@ class RenderFlexLayout extends RenderLayoutBox { if (child is RenderBoxModel && child.hasSize) { Size? childSize = _getChildSize(child); - double? childContentWidth = child.logicalContentWidth; - double? childContentHeight = child.logicalContentHeight; + double? childContentWidth = child.renderStyle.logicalContentWidth; + double? childContentHeight = child.renderStyle.logicalContentHeight; double paddingLeft = child.renderStyle.paddingLeft.computedValue; double paddingRight = child.renderStyle.paddingRight.computedValue; double paddingTop = child.renderStyle.paddingTop.computedValue; @@ -1596,8 +1600,6 @@ class RenderFlexLayout extends RenderLayoutBox { ? containerSizeMap['cross'] : maxAllocatedMainSize; Size layoutSize = getLayoutSize( - logicalContentWidth: logicalContentWidth, - logicalContentHeight: logicalContentHeight, contentWidth: contentWidth, contentHeight: contentHeight, ); diff --git a/kraken/lib/src/rendering/flow.dart b/kraken/lib/src/rendering/flow.dart index de277fd1f7..b98cb24b8e 100644 --- a/kraken/lib/src/rendering/flow.dart +++ b/kraken/lib/src/rendering/flow.dart @@ -475,8 +475,6 @@ class RenderFlowLayout extends RenderLayoutBox { // If no child exists, stop layout. if (childCount == 0) { Size layoutSize = getLayoutSize( - logicalContentWidth: logicalContentWidth, - logicalContentHeight: logicalContentHeight, contentWidth: 0, contentHeight: 0, ); @@ -699,8 +697,6 @@ class RenderFlowLayout extends RenderLayoutBox { final int runCount = runMetrics.length; Size layoutSize = getLayoutSize( - logicalContentWidth: logicalContentWidth, - logicalContentHeight: logicalContentHeight, contentWidth: mainAxisExtent, contentHeight: crossAxisExtent, ); From 5aee6686abcac586776827474dccdcb27514f3d4 Mon Sep 17 00:00:00 2001 From: answershuto Date: Tue, 23 Nov 2021 19:55:37 +0800 Subject: [PATCH 054/167] :recycle: chore: dealwith err. --- kraken/lib/src/dom/elements/head.dart | 4 ++-- kraken/lib/src/launcher/controller.dart | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/kraken/lib/src/dom/elements/head.dart b/kraken/lib/src/dom/elements/head.dart index 8564acb172..b710f5f6ff 100644 --- a/kraken/lib/src/dom/elements/head.dart +++ b/kraken/lib/src/dom/elements/head.dart @@ -110,8 +110,8 @@ class ScriptElement extends Element { int contextId = elementManager.contextId; KrakenController? controller = KrakenController.getControllerOfJSContextId(contextId); if (controller != null) { - Kraken bundle = KrakenBundle.fromHref(controller.href), - KrakenBundle bundle = await KrakenBundle.getBundle(controller.href, contentOverride: script, contextId: contextId); + KrakenBundle bundle = KrakenBundle.fromHrefWithContent(controller.href, script); + bundle.resolve(contextId); await bundle.eval(elementManager.contextId); } } diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index a25f2977ac..200809ddf6 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -558,6 +558,9 @@ class KrakenController { return _module; } + final Queue previousHistoryStack = Queue(); + final Queue nextHistoryStack = Queue(); + Uri get referrer { if (bundle is NetworkBundle) { return Uri.parse(href); From cb2e2a58429fed49425d1cbfe82175ddecb8b537 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Tue, 23 Nov 2021 20:44:11 +0800 Subject: [PATCH 055/167] fix: get parent constraints if width not exist and display is block --- kraken/lib/src/css/render_style.dart | 44 +++++++++++++------------ kraken/lib/src/rendering/box_model.dart | 12 +++---- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 0385f66dc1..2deefcd121 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -258,20 +258,22 @@ class RenderStyle // Use width directly if defined. if (renderStyle.width.isNotAuto) { logicalWidth = renderStyle.width.computedValue; - - // Use tight constraints if constraints is tight and width not exist. - } else if (current.hasSize && current.constraints.hasTightWidth) { - logicalWidth = current.constraints.maxWidth; - - // Block element (except replaced element) will stretch to the content width of its parent. - } else if (current is! RenderIntrinsic && - renderStyle.parent != null - ) { + } else if (renderStyle.parent != null) { RenderStyle parentRenderStyle = renderStyle.parent!; - logicalWidth = parentRenderStyle.logicalContentWidth; - // Should subtract horizontal margin of own from its parent content width. - if (logicalWidth != null) { - logicalWidth -= renderStyle.margin.horizontal; + RenderBoxModel parent = parentRenderStyle.renderBoxModel!; + + // Use parent's tight constraints if constraints is tight and width not exist. + if (parent.hasSize && parent.constraints.hasTightWidth) { + logicalWidth = parent.constraints.maxWidth; + + // Block element (except replaced element) will stretch to the content width of its parent in flow layout. + // Replaced element also stretch in flex layout if align-items is stretch. + } else if (current is! RenderIntrinsic || parent is RenderFlexLayout) { + logicalWidth = parentRenderStyle.logicalContentWidth; + // Should subtract horizontal margin of own from its parent content width. + if (logicalWidth != null) { + logicalWidth -= renderStyle.margin.horizontal; + } } } break; @@ -437,15 +439,15 @@ class RenderStyle if (renderStyle.height.isNotAuto) { logicalHeight = renderStyle.height.computedValue; - // Use tight constraints if constraints is tight and height not exist. - } else if (current.hasSize && current.constraints.hasTightHeight) { - logicalHeight = current.constraints.maxHeight; - } else { - if (current.parent != null && renderStyle.parent != null) { - RenderBoxModel parent = current.parent as RenderBoxModel; - RenderStyle parentRenderStyle = parent.renderStyle; - if (CSSSizingMixin.isStretchChildHeight(parentRenderStyle, renderStyle)) { + if (renderStyle.parent != null) { + RenderStyle parentRenderStyle = renderStyle.parent!; + RenderBoxModel parent = parentRenderStyle.renderBoxModel!; + + // Use parent's tight constraints if constraints is tight and height not exist. + if (parent.hasSize && parent.constraints.hasTightHeight) { + logicalHeight = parent.constraints.maxHeight; + } else if (CSSSizingMixin.isStretchChildHeight(parentRenderStyle, renderStyle)) { logicalHeight = parentRenderStyle.logicalContentHeight; // Should subtract vertical margin of own from its parent content height. if (logicalHeight != null) { diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index 2970ea4fd4..6fdfc7e05f 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -868,8 +868,8 @@ class RenderBoxModel extends RenderBox double? maxHeight = renderStyle.maxHeight.isNone ? null : renderStyle.maxHeight.computedValue; // Content size calculated from style -// logicalContentWidth = renderStyle.getLogicalContentWidth(); -// logicalContentHeight = renderStyle.getLogicalContentHeight(); +// renderStyle.logicalContentWidth = renderStyle.getLogicalContentWidth(); +// renderStyle.logicalContentHeight = renderStyle.getLogicalContentHeight(); renderStyle.logicalContentWidth = renderStyle.computeLogicalContentWidth(); renderStyle.logicalContentHeight = renderStyle.computeLogicalContentHeight(); @@ -1018,10 +1018,10 @@ class RenderBoxModel extends RenderBox // Deflate padding constraints. boxConstraints = renderStyle.deflatePaddingConstraints(boxConstraints); -// logicalContentWidth = renderStyle.getLogicalContentWidth(); -// logicalContentHeight = renderStyle.getLogicalContentHeight(); -// logicalContentWidth = renderStyle.computeLogicalContentWidth(); -// logicalContentHeight = renderStyle.computeLogicalContentHeight(); +// renderStyle.logicalContentWidth = renderStyle.getLogicalContentWidth(); +// renderStyle.logicalContentHeight = renderStyle.getLogicalContentHeight(); +// renderStyle.logicalContentWidth = renderStyle.computeLogicalContentWidth(); +// renderStyle.logicalContentHeight = renderStyle.computeLogicalContentHeight(); double? logicalContentWidth = renderStyle.logicalContentWidth; double? logicalContentHeight = renderStyle.logicalContentHeight; From 13af3807f4daf0305f0b9fcc38864afeaec80072 Mon Sep 17 00:00:00 2001 From: answershuto Date: Tue, 23 Nov 2021 21:22:13 +0800 Subject: [PATCH 056/167] :bug: fix: should use bundle. --- kraken/lib/widget.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index 25aa0ae786..1012257703 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -987,7 +987,8 @@ class _KrakenRenderObjectElement extends SingleChildRenderObjectElement { KrakenController controller = (renderObject as RenderObjectWithControllerMixin).controller!; - if (controller.bundleContent == null && controller.bundleByteCode == null && controller.href.isEmpty) { + + if (controller.bundle == null || (controller.bundle?.content == null && controller.bundle?.byteCode == null && controller.bundle?.src == null)) { return; } From 40b8e4a806311c2ecc31064e823f66a5b382f0e0 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 00:16:12 +0800 Subject: [PATCH 057/167] fix: height stretch logic --- kraken/lib/src/css/render_style.dart | 55 +++++++++++++++++++++++----- kraken/lib/src/css/sizing.dart | 30 --------------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 2deefcd121..53c212099f 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -444,14 +444,16 @@ class RenderStyle RenderStyle parentRenderStyle = renderStyle.parent!; RenderBoxModel parent = parentRenderStyle.renderBoxModel!; - // Use parent's tight constraints if constraints is tight and height not exist. - if (parent.hasSize && parent.constraints.hasTightHeight) { - logicalHeight = parent.constraints.maxHeight; - } else if (CSSSizingMixin.isStretchChildHeight(parentRenderStyle, renderStyle)) { - logicalHeight = parentRenderStyle.logicalContentHeight; - // Should subtract vertical margin of own from its parent content height. - if (logicalHeight != null) { - logicalHeight -= renderStyle.margin.vertical; + if (renderStyle.isHeightStretch) { + // Use parent's tight constraints if constraints is tight and height not exist. + if (parent.hasSize && parent.constraints.hasTightHeight) { + logicalHeight = parent.constraints.maxHeight; + } else { + logicalHeight = parentRenderStyle.logicalContentHeight; + // Should subtract vertical margin of own from its parent content height. + if (logicalHeight != null) { + logicalHeight -= renderStyle.margin.vertical; + } } } } @@ -523,7 +525,7 @@ class RenderStyle } RenderBoxModel parentRenderBoxModel = parentRenderStyle!.renderBoxModel!; - if (CSSSizingMixin.isStretchChildHeight(parentRenderStyle, currentRenderStyle)) { + if (currentRenderStyle.isHeightStretch) { if (parentRenderStyle.height.isNotAuto) { height = parentRenderStyle.height.computedValue; cropHeight = _getCropHeightByPaddingBorder(parentRenderStyle, cropHeight); @@ -565,6 +567,41 @@ class RenderStyle } } + // Whether height is stretched to fill its parent's content height. + bool get isHeightStretch { + RenderStyle renderStyle = this; + if (renderStyle.parent == null) { + return false; + } + bool isStretch = false; + RenderStyle parentRenderStyle = renderStyle.parent!; + + bool isParentFlex = parentRenderStyle.display == CSSDisplay.flex || + parentRenderStyle.display == CSSDisplay.inlineFlex; + bool isHorizontalDirection = false; + bool isFlexNoWrap = false; + bool isChildStretchSelf = false; + if (isParentFlex) { + isHorizontalDirection = CSSFlex.isHorizontalFlexDirection(parentRenderStyle.flexDirection); + isFlexNoWrap = parentRenderStyle.flexWrap != FlexWrap.wrap && + parentRenderStyle.flexWrap != FlexWrap.wrapReverse; + isChildStretchSelf = renderStyle.alignSelf != AlignSelf.auto + ? renderStyle.alignSelf == AlignSelf.stretch + : parentRenderStyle.effectiveAlignItems == AlignItems.stretch; + } + + CSSLengthValue marginTop = renderStyle.marginTop; + CSSLengthValue marginBottom = renderStyle.marginBottom; + + // Display as block if flex vertical layout children and stretch children + if (marginTop.isNotAuto && marginBottom.isNotAuto && + isParentFlex && isHorizontalDirection && isFlexNoWrap && isChildStretchSelf) { + isStretch = true; + } + + return isStretch; + } + // Max constraints width of content, used in calculating the remaining space for line wrapping // in the stage of layout. double get contentMaxConstraintsWidth { diff --git a/kraken/lib/src/css/sizing.dart b/kraken/lib/src/css/sizing.dart index 42b8de38ab..0ecd8f2b08 100644 --- a/kraken/lib/src/css/sizing.dart +++ b/kraken/lib/src/css/sizing.dart @@ -136,34 +136,4 @@ mixin CSSSizingMixin on RenderStyleBase { } } - // Whether current node should stretch children's height - static bool isStretchChildHeight(RenderStyle renderStyle, RenderStyle childRenderStyle) { - bool isStretch = false; - bool isFlex = renderStyle.renderBoxModel is RenderFlexLayout; - bool isHorizontalDirection = false; - bool isAlignItemsStretch = false; - bool isFlexNoWrap = false; - bool isChildAlignSelfStretch = false; - bool isChildStretchSelf = false; - if (isFlex) { - isHorizontalDirection = CSSFlex.isHorizontalFlexDirection(renderStyle.flexDirection); - isAlignItemsStretch = renderStyle.effectiveAlignItems == AlignItems.stretch; - isFlexNoWrap = renderStyle.flexWrap != FlexWrap.wrap && - childRenderStyle.flexWrap != FlexWrap.wrapReverse; - isChildAlignSelfStretch = childRenderStyle.alignSelf == AlignSelf.stretch; - isChildStretchSelf = childRenderStyle.alignSelf != AlignSelf.auto ? - isChildAlignSelfStretch : isAlignItemsStretch; - } - - CSSLengthValue marginTop = childRenderStyle.marginTop; - CSSLengthValue marginBottom = childRenderStyle.marginBottom; - - // Display as block if flex vertical layout children and stretch children - if (!marginTop.isAuto && !marginBottom.isAuto && - isFlex && isHorizontalDirection && isFlexNoWrap && isChildStretchSelf) { - isStretch = true; - } - - return isStretch; - } } From 341b3b70217f2e63806e92a49970ba3c3e7c98c1 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 00:59:16 +0800 Subject: [PATCH 058/167] fix: isNotAuto --- kraken/lib/src/css/values/length.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/src/css/values/length.dart b/kraken/lib/src/css/values/length.dart index 14bcacc90d..91279d191b 100644 --- a/kraken/lib/src/css/values/length.dart +++ b/kraken/lib/src/css/values/length.dart @@ -335,7 +335,7 @@ class CSSLengthValue { } bool get isNotAuto { - return type != CSSLengthType.AUTO; + return !isAuto; } bool get isNone { From ba9113ef42fa1ae423a9610ce221a58b49603f3f Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 01:30:10 +0800 Subject: [PATCH 059/167] fix: should ignore renderStyle of display inline when searching for ancestors to stretch width. --- kraken/lib/src/css/render_style.dart | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 53c212099f..8cddc47fe3 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -245,6 +245,19 @@ class RenderStyle } } + // Find ancestor render style with display of not inline. + RenderStyle? _findAncestoreWithNoDisplayInline() { + RenderStyle renderStyle = this; + RenderStyle? parentRenderStyle = renderStyle.parent; + while(parentRenderStyle != null) { + if (parentRenderStyle.effectiveDisplay != CSSDisplay.inline) { + break; + } + parentRenderStyle = parentRenderStyle.parent; + } + return parentRenderStyle; + } + double? computeLogicalContentWidth() { RenderBoxModel current = renderBoxModel!; RenderStyle renderStyle = this; @@ -269,10 +282,14 @@ class RenderStyle // Block element (except replaced element) will stretch to the content width of its parent in flow layout. // Replaced element also stretch in flex layout if align-items is stretch. } else if (current is! RenderIntrinsic || parent is RenderFlexLayout) { - logicalWidth = parentRenderStyle.logicalContentWidth; - // Should subtract horizontal margin of own from its parent content width. - if (logicalWidth != null) { - logicalWidth -= renderStyle.margin.horizontal; + RenderStyle? targetParentRenderStyle = _findAncestoreWithNoDisplayInline(); + // Should ignore renderStyle of display inline when searching for ancestors to stretch width. + if (targetParentRenderStyle != null) { + logicalWidth = targetParentRenderStyle.logicalContentWidth; + // Should subtract horizontal margin of own from its parent content width. + if (logicalWidth != null) { + logicalWidth -= renderStyle.margin.horizontal; + } } } } From ab7103877b48f070327ea5f20fa4c73ce7d33af6 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 15:16:07 +0800 Subject: [PATCH 060/167] refactor: unify logical content size to contentBoxLogicalWidth & contentBoxLogicalHeight --- .../specs/css/css-sizing/min-height.ts | 2 +- .../specs/css/css-sizing/min-width.ts | 2 +- kraken/lib/src/css/render_style.dart | 61 +++++----- kraken/lib/src/rendering/box_model.dart | 115 ++++-------------- kraken/lib/src/rendering/flex.dart | 32 ++--- 5 files changed, 69 insertions(+), 143 deletions(-) diff --git a/integration_tests/specs/css/css-sizing/min-height.ts b/integration_tests/specs/css/css-sizing/min-height.ts index 6f92059aeb..2c7d5a3462 100644 --- a/integration_tests/specs/css/css-sizing/min-height.ts +++ b/integration_tests/specs/css/css-sizing/min-height.ts @@ -28,7 +28,7 @@ describe('min-height', () => { await snapshot(); }); - fit("should work with padding exist and height not exist", async () => { + it("should work with padding exist and height not exist", async () => { let containingBlock = createElement('div', { style: { border: '2px solid #000', diff --git a/integration_tests/specs/css/css-sizing/min-width.ts b/integration_tests/specs/css/css-sizing/min-width.ts index 47855af044..c5fd9ac2a4 100644 --- a/integration_tests/specs/css/css-sizing/min-width.ts +++ b/integration_tests/specs/css/css-sizing/min-width.ts @@ -28,7 +28,7 @@ describe('min-width', () => { await snapshot(); }); - fit("should work with padding exist and width not exist", async () => { + it("should work with padding exist and width not exist", async () => { let containingBlock = createElement('div', { style: { border: '2px solid #000', diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 8cddc47fe3..b7e3170f42 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -57,12 +57,6 @@ class RenderStyle required this.target, }); - // Content width which is calculated from render style. - double? logicalContentWidth; - - // Content height which is calculated from render style. - double? logicalContentHeight; - dynamic getProperty(String name) { RenderStyle renderStyle = this; switch (name) { @@ -258,7 +252,7 @@ class RenderStyle return parentRenderStyle; } - double? computeLogicalContentWidth() { + void computeContentBoxLogicalWidth() { RenderBoxModel current = renderBoxModel!; RenderStyle renderStyle = this; double? logicalWidth; @@ -285,7 +279,7 @@ class RenderStyle RenderStyle? targetParentRenderStyle = _findAncestoreWithNoDisplayInline(); // Should ignore renderStyle of display inline when searching for ancestors to stretch width. if (targetParentRenderStyle != null) { - logicalWidth = targetParentRenderStyle.logicalContentWidth; + logicalWidth = targetParentRenderStyle.contentBoxLogicalWidth; // Should subtract horizontal margin of own from its parent content width. if (logicalWidth != null) { logicalWidth -= renderStyle.margin.horizontal; @@ -340,7 +334,7 @@ class RenderStyle logicalContentWidth = math.max(0, logicalContentWidth); } - return logicalContentWidth; + _contentBoxLogicalWidth = logicalContentWidth; } // Content width of render box model calculated from style. @@ -444,7 +438,7 @@ class RenderStyle } } - double? computeLogicalContentHeight() { + void computeContentBoxLogicalHeight() { RenderBoxModel current = renderBoxModel!; RenderStyle renderStyle = this; double? logicalHeight; @@ -466,7 +460,7 @@ class RenderStyle if (parent.hasSize && parent.constraints.hasTightHeight) { logicalHeight = parent.constraints.maxHeight; } else { - logicalHeight = parentRenderStyle.logicalContentHeight; + logicalHeight = parentRenderStyle.contentBoxLogicalHeight; // Should subtract vertical margin of own from its parent content height. if (logicalHeight != null) { logicalHeight -= renderStyle.margin.vertical; @@ -476,7 +470,6 @@ class RenderStyle } } } - double? intrinsicRatio = current.intrinsicRatio; // Get height by intrinsic ratio for replaced element if height is auto. @@ -509,7 +502,7 @@ class RenderStyle logicalContentHeight = math.max(0, logicalContentHeight); } - return logicalContentHeight; + _contentBoxLogicalHeight = logicalContentHeight; } // Content height of render box model calculated from style. @@ -692,34 +685,36 @@ class RenderStyle // Content width calculated from renderStyle tree. // https://www.w3.org/TR/css-box-3/#valdef-box-content-box - // @TODO: add cache to avoid recalculate every time. + double? _contentBoxLogicalWidth; double? get contentBoxLogicalWidth { // If renderBox has tight width, its logical size equals max size. - if (renderBoxModel != null && - renderBoxModel!.hasSize && - renderBoxModel!.constraints.hasTightWidth - ) { - return renderBoxModel!.constraints.maxWidth - - effectiveBorderLeftWidth.computedValue - effectiveBorderRightWidth.computedValue - - paddingLeft.computedValue - paddingRight.computedValue; - } - return getLogicalContentWidth(); +// if (renderBoxModel != null && +// renderBoxModel!.hasSize && +// renderBoxModel!.constraints.hasTightWidth +// ) { +// return renderBoxModel!.constraints.maxWidth - +// effectiveBorderLeftWidth.computedValue - effectiveBorderRightWidth.computedValue - +// paddingLeft.computedValue - paddingRight.computedValue; +// } + return _contentBoxLogicalWidth; +// return getLogicalContentWidth(); } // Content height calculated from renderStyle tree. // https://www.w3.org/TR/css-box-3/#valdef-box-content-box - // @TODO: add cache to avoid recalculate every time. + double? _contentBoxLogicalHeight; double? get contentBoxLogicalHeight { // If renderBox has tight height, its logical size equals max size. - if (renderBoxModel != null && - renderBoxModel!.hasSize && - renderBoxModel!.constraints.hasTightHeight - ) { - return renderBoxModel!.constraints.maxHeight - - effectiveBorderTopWidth.computedValue - effectiveBorderBottomWidth.computedValue - - paddingTop.computedValue - paddingBottom.computedValue; - } - return getLogicalContentHeight(); +// if (renderBoxModel != null && +// renderBoxModel!.hasSize && +// renderBoxModel!.constraints.hasTightHeight +// ) { +// return renderBoxModel!.constraints.maxHeight - +// effectiveBorderTopWidth.computedValue - effectiveBorderBottomWidth.computedValue - +// paddingTop.computedValue - paddingBottom.computedValue; +// } + return _contentBoxLogicalHeight; +// return getLogicalContentHeight(); } // Padding box width calculated from renderStyle tree. diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index c750875a99..79c4966916 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -388,8 +388,8 @@ class RenderLayoutBox extends RenderBoxModel double finalContentHeight = contentHeight; // Size which is specified by sizing styles - double? specifiedContentWidth = renderStyle.logicalContentWidth; - double? specifiedContentHeight = renderStyle.logicalContentHeight; + double? specifiedContentWidth = renderStyle.contentBoxLogicalWidth; + double? specifiedContentHeight = renderStyle.contentBoxLogicalHeight; // Flex basis takes priority over main size in flex item. if (parent is RenderFlexLayout) { RenderBoxModel? parentRenderBoxModel = parent as RenderBoxModel?; @@ -845,7 +845,7 @@ class RenderBoxModel extends RenderBox super.layout(newConstraints, parentUsesSize: parentUsesSize); } - /// Calculate renderBoxModel constraints + // Calculate constraints of renderBoxModel on layout stage. BoxConstraints getConstraints() { // Inner scrolling content box of overflow element inherits constraints from parent // but has indefinite max constraints to allow children overflow @@ -873,44 +873,25 @@ class RenderBoxModel extends RenderBox CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay; bool isDisplayInline = effectiveDisplay == CSSDisplay.inline; - EdgeInsets borderEdge = renderStyle.border; - EdgeInsetsGeometry? padding = renderStyle.padding; - - double horizontalBorderLength = borderEdge.horizontal; - double verticalBorderLength = borderEdge.vertical; - double horizontalPaddingLength = padding.horizontal; - double verticalPaddingLength = padding.vertical; - double? minWidth = renderStyle.minWidth.isAuto ? null : renderStyle.minWidth.computedValue; double? maxWidth = renderStyle.maxWidth.isNone ? null : renderStyle.maxWidth.computedValue; double? minHeight = renderStyle.minHeight.isAuto ? null : renderStyle.minHeight.computedValue; double? maxHeight = renderStyle.maxHeight.isNone ? null : renderStyle.maxHeight.computedValue; - // Content size calculated from style -// renderStyle.logicalContentWidth = renderStyle.getLogicalContentWidth(); -// renderStyle.logicalContentHeight = renderStyle.getLogicalContentHeight(); - renderStyle.logicalContentWidth = renderStyle.computeLogicalContentWidth(); - renderStyle.logicalContentHeight = renderStyle.computeLogicalContentHeight(); - - // Box size calculated from style - double? logicalWidth = renderStyle.logicalContentWidth != null - ? renderStyle.logicalContentWidth! + horizontalPaddingLength + horizontalBorderLength - : null; - double? logicalHeight = renderStyle.logicalContentHeight != null - ? renderStyle.logicalContentHeight! + verticalPaddingLength + verticalBorderLength - : null; - - // Constraints + // Need to calculated logic content size on every layout. + renderStyle.computeContentBoxLogicalWidth(); + renderStyle.computeContentBoxLogicalHeight(); + // Width should be not smaller than border and padding in horizontal direction // when box-sizing is border-box which is only supported. double minConstraintWidth = renderStyle.effectiveBorderLeftWidth.computedValue + renderStyle.effectiveBorderRightWidth.computedValue + renderStyle.paddingLeft.computedValue + renderStyle.paddingRight.computedValue; - double maxConstraintWidth = logicalWidth ?? double.infinity; + double maxConstraintWidth = renderStyle.borderBoxLogicalWidth ?? double.infinity; // Height should be not smaller than border and padding in vertical direction // when box-sizing is border-box which is only supported. double minConstraintHeight = renderStyle.effectiveBorderTopWidth.computedValue + renderStyle.effectiveBorderBottomWidth.computedValue + renderStyle.paddingTop.computedValue + renderStyle.paddingBottom.computedValue; - double maxConstraintHeight = logicalHeight ?? double.infinity; + double maxConstraintHeight = renderStyle.borderBoxLogicalHeight ?? double.infinity; if (parent is RenderFlexLayout) { double? flexBasis = renderStyle.flexBasis == CSSLengthValue.auto ? null : renderStyle.flexBasis?.computedValue; @@ -1022,72 +1003,22 @@ class RenderBoxModel extends RenderBox // Base layout methods to compute content constraints before content box layout. // Call this method before content box layout. void beforeLayout() { - BoxConstraints boxConstraints = constraints; - // Deflate border constraints. - boxConstraints = renderStyle.deflateBorderConstraints(boxConstraints); - - // Deflate padding constraints. - boxConstraints = renderStyle.deflatePaddingConstraints(boxConstraints); - -// renderStyle.logicalContentWidth = renderStyle.getLogicalContentWidth(); -// renderStyle.logicalContentHeight = renderStyle.getLogicalContentHeight(); -// renderStyle.logicalContentWidth = renderStyle.computeLogicalContentWidth(); -// renderStyle.logicalContentHeight = renderStyle.computeLogicalContentHeight(); - - double? logicalContentWidth = renderStyle.logicalContentWidth; - double? logicalContentHeight = renderStyle.logicalContentHeight; - - if (!isScrollingContentBox && (logicalContentWidth != null || logicalContentHeight != null)) { - double minWidth; - double maxWidth; - double minHeight; - double maxHeight; - - if (boxConstraints.hasTightWidth) { - minWidth = maxWidth = boxConstraints.maxWidth; - } else if (logicalContentWidth != null) { - minWidth = 0.0; - maxWidth = logicalContentWidth; - } else { - minWidth = boxConstraints.minWidth; - maxWidth = boxConstraints.maxWidth; - } - - if (boxConstraints.hasTightHeight) { - minHeight = maxHeight = boxConstraints.maxHeight; - } else if (logicalContentHeight != null) { - minHeight = 0.0; - maxHeight = logicalContentHeight; - } else { - minHeight = boxConstraints.minHeight; - maxHeight = boxConstraints.maxHeight; - } - - // max and min size of intrinsc element should respect intrinsc ratio of each other - if (intrinsicRatio != null) { - if (!renderStyle.minWidth.isAuto && renderStyle.minHeight.isAuto) { - minHeight = minWidth * intrinsicRatio!; - } - if (!renderStyle.maxWidth.isNone && renderStyle.maxHeight.isNone) { - maxHeight = maxWidth * intrinsicRatio!; - } - if (renderStyle.minWidth.isAuto && !renderStyle.minHeight.isAuto) { - minWidth = minHeight / intrinsicRatio!; - } - if (renderStyle.maxWidth.isNone && !renderStyle.maxHeight.isNone) { - maxWidth = maxHeight / intrinsicRatio!; - } - } - - _contentConstraints = BoxConstraints( - minWidth: minWidth, - maxWidth: maxWidth, - minHeight: minHeight, - maxHeight: maxHeight - ); + BoxConstraints contentConstraints; + // @FIXME: Normally constraints is calculated in getConstraints by parent RenderLayoutBox in Kraken, + // except in sliver layout, constraints is calculated by [RenderSliverList] which kraken can not control, + // so it needs to invoke getConstraints here for sliver container's direct child. + if (parent is RenderSliverList) { + contentConstraints = getConstraints(); } else { - _contentConstraints = boxConstraints; + // Constraints is already calculated in parent layout. + contentConstraints = constraints; } + + // Deflate border constraints. + contentConstraints = renderStyle.deflateBorderConstraints(contentConstraints); + // Deflate padding constraints. + contentConstraints = renderStyle.deflatePaddingConstraints(contentConstraints); + _contentConstraints = contentConstraints; } /// Find scroll container diff --git a/kraken/lib/src/rendering/flex.dart b/kraken/lib/src/rendering/flex.dart index 43eb35b577..d120eacd11 100644 --- a/kraken/lib/src/rendering/flex.dart +++ b/kraken/lib/src/rendering/flex.dart @@ -496,8 +496,8 @@ class RenderFlexLayout extends RenderLayoutBox { minMainSize = math.min(contentSize, transferredSize); } else if (child is RenderBoxModel) { double? specifiedMainSize = _isHorizontalFlexDirection - ? child.renderStyle.logicalContentWidth - : child.renderStyle.logicalContentHeight; + ? child.renderStyle.contentBoxLogicalWidth + : child.renderStyle.contentBoxLogicalHeight; minMainSize = specifiedMainSize != null ? math.min(contentSize, specifiedMainSize) : contentSize; @@ -753,14 +753,14 @@ class RenderFlexLayout extends RenderLayoutBox { containerSizeMap, ); - double? logicalContentWidth = renderStyle.logicalContentWidth; - double? logicalContentHeight = renderStyle.logicalContentHeight; + double? contentBoxLogicalWidth = renderStyle.contentBoxLogicalWidth; + double? contentBoxLogicalHeight = renderStyle.contentBoxLogicalHeight; /// If no non positioned child exists, stop layout if (runMetrics.isEmpty) { Size contentSize = Size( - logicalContentWidth ?? 0, - logicalContentHeight ?? 0, + contentBoxLogicalWidth ?? 0, + contentBoxLogicalHeight ?? 0, ); setMaxScrollableSize(contentSize); size = getBoxSize(contentSize); @@ -770,9 +770,9 @@ class RenderFlexLayout extends RenderLayoutBox { double containerCrossAxisExtent = 0.0; if (!_isHorizontalFlexDirection) { - containerCrossAxisExtent = logicalContentWidth ?? 0; + containerCrossAxisExtent = contentBoxLogicalWidth ?? 0; } else { - containerCrossAxisExtent = logicalContentHeight ?? 0; + containerCrossAxisExtent = contentBoxLogicalHeight ?? 0; } /// Calculate leading and between space between flex lines @@ -1233,21 +1233,21 @@ class RenderFlexLayout extends RenderLayoutBox { Map containerSizeMap, ) { RenderBox? child = placeholderChild ?? firstChild; - double? logicalContentWidth = renderStyle.logicalContentWidth; - double? logicalContentHeight = renderStyle.logicalContentHeight; + double? contentBoxLogicalWidth = renderStyle.contentBoxLogicalWidth; + double? contentBoxLogicalHeight = renderStyle.contentBoxLogicalHeight; // Container's width specified by style or inherited from parent double? containerWidth = 0; - if (logicalContentWidth != null) { - containerWidth = logicalContentWidth; + if (contentBoxLogicalWidth != null) { + containerWidth = contentBoxLogicalWidth; } else if (contentConstraints!.hasTightWidth) { containerWidth = contentConstraints!.maxWidth; } // Container's height specified by style or inherited from parent double? containerHeight = 0; - if (logicalContentHeight != null) { - containerHeight = logicalContentHeight; + if (contentBoxLogicalHeight != null) { + containerHeight = contentBoxLogicalHeight; } else if (contentConstraints!.hasTightHeight) { containerHeight = contentConstraints!.maxHeight; } @@ -1341,8 +1341,8 @@ class RenderFlexLayout extends RenderLayoutBox { if (child is RenderBoxModel && child.hasSize) { Size? childSize = _getChildSize(child); - double? childContentWidth = child.renderStyle.logicalContentWidth; - double? childContentHeight = child.renderStyle.logicalContentHeight; + double? childContentWidth = child.renderStyle.contentBoxLogicalWidth; + double? childContentHeight = child.renderStyle.contentBoxLogicalHeight; double paddingLeft = child.renderStyle.paddingLeft.computedValue; double paddingRight = child.renderStyle.paddingRight.computedValue; double paddingTop = child.renderStyle.paddingTop.computedValue; From 2ee3b2f2164889b5ff99d3b370d0718e5b21c65c Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 15:53:57 +0800 Subject: [PATCH 061/167] fix: logical content size before layout --- kraken/lib/src/css/render_style.dart | 16 ++++++++++++++-- kraken/lib/src/rendering/box_model.dart | 3 ++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index b7e3170f42..adc0be8ca9 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -685,7 +685,8 @@ class RenderStyle // Content width calculated from renderStyle tree. // https://www.w3.org/TR/css-box-3/#valdef-box-content-box - double? _contentBoxLogicalWidth; + // Use double.infinity refers to the value is not computed yet. + double? _contentBoxLogicalWidth = double.infinity; double? get contentBoxLogicalWidth { // If renderBox has tight width, its logical size equals max size. // if (renderBoxModel != null && @@ -696,13 +697,19 @@ class RenderStyle // effectiveBorderLeftWidth.computedValue - effectiveBorderRightWidth.computedValue - // paddingLeft.computedValue - paddingRight.computedValue; // } + // Compute logical width directly if renderBoxModel is not layouted yet, + // eg compute percentage length before layout. + if (_contentBoxLogicalWidth == double.infinity) { + computeContentBoxLogicalWidth(); + } return _contentBoxLogicalWidth; // return getLogicalContentWidth(); } // Content height calculated from renderStyle tree. // https://www.w3.org/TR/css-box-3/#valdef-box-content-box - double? _contentBoxLogicalHeight; + // Use double.infinity refers to the value is not computed yet. + double? _contentBoxLogicalHeight = double.infinity; double? get contentBoxLogicalHeight { // If renderBox has tight height, its logical size equals max size. // if (renderBoxModel != null && @@ -713,6 +720,11 @@ class RenderStyle // effectiveBorderTopWidth.computedValue - effectiveBorderBottomWidth.computedValue - // paddingTop.computedValue - paddingBottom.computedValue; // } + // Compute logical height directly if renderBoxModel is not layouted yet, + // eg compute percentage length before layout. + if (_contentBoxLogicalHeight == double.infinity) { + computeContentBoxLogicalHeight(); + } return _contentBoxLogicalHeight; // return getLogicalContentHeight(); } diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index 79c4966916..cbf16090af 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -845,7 +845,8 @@ class RenderBoxModel extends RenderBox super.layout(newConstraints, parentUsesSize: parentUsesSize); } - // Calculate constraints of renderBoxModel on layout stage. + // Calculate constraints of renderBoxModel on layout stage and + // only needed to be excuted once on every layout. BoxConstraints getConstraints() { // Inner scrolling content box of overflow element inherits constraints from parent // but has indefinite max constraints to allow children overflow From adcbf2c3d0c1a3cc9e7ad0532c75681ac6405121 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 17:53:43 +0800 Subject: [PATCH 062/167] fix: percentage width/height resolve --- kraken/lib/src/css/render_style.dart | 262 +++++++------------------- kraken/lib/src/css/values/length.dart | 43 +++-- 2 files changed, 94 insertions(+), 211 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index adc0be8ca9..801bf48ca1 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -252,6 +252,7 @@ class RenderStyle return parentRenderStyle; } + // Compute the content box width from render style. void computeContentBoxLogicalWidth() { RenderBoxModel current = renderBoxModel!; RenderStyle renderStyle = this; @@ -337,107 +338,7 @@ class RenderStyle _contentBoxLogicalWidth = logicalContentWidth; } - // Content width of render box model calculated from style. - double? getLogicalContentWidth() { - RenderStyle renderStyle = this; - double? intrinsicRatio = renderBoxModel!.intrinsicRatio; - CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay; - double? width = renderStyle.width.isAuto ? null : renderStyle.width.computedValue; - double? minWidth = renderStyle.minWidth.isAuto ? null : renderStyle.minWidth.computedValue; - double? maxWidth = renderStyle.maxWidth.isNone ? null : renderStyle.maxWidth.computedValue; - double cropWidth = 0; - - switch (effectiveDisplay) { - case CSSDisplay.block: - case CSSDisplay.flex: - case CSSDisplay.sliver: - // Get own width if exists else get the width of nearest ancestor width width - if (!renderStyle.width.isAuto) { - cropWidth = _getCropWidthByPaddingBorder(renderStyle, cropWidth); - } else { - // @TODO: flexbox stretch alignment will stretch replaced element in the cross axis - // Block level element will spread to its parent's width except for replaced element - if (renderBoxModel is! RenderIntrinsic) { - RenderStyle currentRenderStyle = renderStyle; - - while (true) { - RenderStyle? parentRenderStyle = renderStyle.parent; - - if (parentRenderStyle != null) { - cropWidth += currentRenderStyle.margin.horizontal; - cropWidth = _getCropWidthByPaddingBorder(currentRenderStyle, cropWidth); - parentRenderStyle = currentRenderStyle.parent; - } else { - break; - } - - CSSDisplay? parentEffectiveDisplay = parentRenderStyle!.effectiveDisplay; - RenderBoxModel parentRenderBoxModel = parentRenderStyle.renderBoxModel!; - // Set width of element according to parent display - if (parentEffectiveDisplay != CSSDisplay.inline) { - // Skip to find upper parent - if (parentRenderStyle.width.isNotAuto) { - // Use style width - width = parentRenderStyle.width.computedValue; - cropWidth = _getCropWidthByPaddingBorder(parentRenderStyle, cropWidth); - break; - } else if (parentRenderBoxModel.hasSize && parentRenderBoxModel.constraints.hasTightWidth) { - // Cases like flex item with flex-grow and no width in flex row direction. - width = parentRenderBoxModel.constraints.maxWidth; - cropWidth = _getCropWidthByPaddingBorder(parentRenderStyle, cropWidth); - break; - } else if (parentEffectiveDisplay == CSSDisplay.inlineBlock || - parentEffectiveDisplay == CSSDisplay.inlineFlex || - parentEffectiveDisplay == CSSDisplay.sliver) { - // Collapse width to children - width = null; - break; - } - } - - currentRenderStyle = parentRenderStyle; - } - } - } - break; - case CSSDisplay.inlineBlock: - case CSSDisplay.inlineFlex: - if (renderStyle.width.isNotAuto) { - width = renderStyle.width.computedValue; - cropWidth = _getCropWidthByPaddingBorder(renderStyle, cropWidth); - } else { - width = null; - } - break; - case CSSDisplay.inline: - width = null; - break; - default: - break; - } - // Get height by intrinsic ratio for replaced element if height is not defined - if (width == null && intrinsicRatio != null) { - width = renderStyle.getWidthByIntrinsicRatio() + cropWidth; - } - - if (minWidth != null) { - if (width != null && width < minWidth) { - width = minWidth; - } - } - if (maxWidth != null) { - if (width != null && width > maxWidth) { - width = maxWidth; - } - } - - if (width != null) { - return math.max(0, width - cropWidth); - } else { - return null; - } - } - + // Compute the content box height from render style. void computeContentBoxLogicalHeight() { RenderBoxModel current = renderBoxModel!; RenderStyle renderStyle = this; @@ -505,78 +406,6 @@ class RenderStyle _contentBoxLogicalHeight = logicalContentHeight; } - // Content height of render box model calculated from style. - double? getLogicalContentHeight() { - RenderStyle renderStyle = this; - CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay; - double? height = renderStyle.height.isAuto ? null : renderStyle.height.computedValue; - double cropHeight = 0; - double? maxHeight = renderStyle.maxHeight.isNone ? null : renderStyle.maxHeight.computedValue; - double? minHeight = renderStyle.minHeight.isAuto ? null : renderStyle.minHeight.computedValue; - double? intrinsicRatio = renderBoxModel!.intrinsicRatio; - - // Inline element has no height. - if (effectiveDisplay == CSSDisplay.inline) { - return null; - } else if (height != null) { - cropHeight = _getCropHeightByPaddingBorder(renderStyle, cropHeight); - } else { - RenderStyle currentRenderStyle = renderStyle; - - while (true) { - RenderStyle? parentRenderStyle = currentRenderStyle.parent; - - if (parentRenderStyle != null) { - cropHeight += currentRenderStyle.margin.vertical; - cropHeight = _getCropHeightByPaddingBorder(currentRenderStyle, cropHeight); - parentRenderStyle = currentRenderStyle.parent; - } else { - break; - } - - RenderBoxModel parentRenderBoxModel = parentRenderStyle!.renderBoxModel!; - if (currentRenderStyle.isHeightStretch) { - if (parentRenderStyle.height.isNotAuto) { - height = parentRenderStyle.height.computedValue; - cropHeight = _getCropHeightByPaddingBorder(parentRenderStyle, cropHeight); - break; - } else if (parentRenderBoxModel.hasSize && parentRenderBoxModel.constraints.hasTightHeight) { - // Cases like flex item with flex-grow and no height in flex column direction. - height = parentRenderBoxModel.constraints.maxHeight; - cropHeight = _getCropHeightByPaddingBorder(parentRenderStyle, cropHeight); - break; - } - } else { - break; - } - - currentRenderStyle = parentRenderStyle; - } - } - - // Get height by intrinsic ratio for replaced element if height is not defined. - if (height == null && intrinsicRatio != null) { - height = renderStyle.getHeightByIntrinsicRatio() + cropHeight; - } - - if (minHeight != null) { - if (height != null && height < minHeight) { - height = minHeight; - } - } - if (maxHeight != null) { - if (height != null && height > maxHeight) { - height = maxHeight; - } - } - - if (height != null) { - return math.max(0, height - cropHeight); - } else { - return null; - } - } - // Whether height is stretched to fill its parent's content height. bool get isHeightStretch { RenderStyle renderStyle = this; @@ -689,21 +518,12 @@ class RenderStyle double? _contentBoxLogicalWidth = double.infinity; double? get contentBoxLogicalWidth { // If renderBox has tight width, its logical size equals max size. -// if (renderBoxModel != null && -// renderBoxModel!.hasSize && -// renderBoxModel!.constraints.hasTightWidth -// ) { -// return renderBoxModel!.constraints.maxWidth - -// effectiveBorderLeftWidth.computedValue - effectiveBorderRightWidth.computedValue - -// paddingLeft.computedValue - paddingRight.computedValue; -// } - // Compute logical width directly if renderBoxModel is not layouted yet, - // eg compute percentage length before layout. + // Compute logical width directly in case as renderBoxModel is not layouted yet, + // eg. compute percentage length before layout. if (_contentBoxLogicalWidth == double.infinity) { computeContentBoxLogicalWidth(); } return _contentBoxLogicalWidth; -// return getLogicalContentWidth(); } // Content height calculated from renderStyle tree. @@ -711,22 +531,12 @@ class RenderStyle // Use double.infinity refers to the value is not computed yet. double? _contentBoxLogicalHeight = double.infinity; double? get contentBoxLogicalHeight { - // If renderBox has tight height, its logical size equals max size. -// if (renderBoxModel != null && -// renderBoxModel!.hasSize && -// renderBoxModel!.constraints.hasTightHeight -// ) { -// return renderBoxModel!.constraints.maxHeight - -// effectiveBorderTopWidth.computedValue - effectiveBorderBottomWidth.computedValue - -// paddingTop.computedValue - paddingBottom.computedValue; -// } - // Compute logical height directly if renderBoxModel is not layouted yet, - // eg compute percentage length before layout. + // Compute logical height directly in case as renderBoxModel is not layouted yet, + // eg. compute percentage length before layout. if (_contentBoxLogicalHeight == double.infinity) { computeContentBoxLogicalHeight(); } return _contentBoxLogicalHeight; -// return getLogicalContentHeight(); } // Padding box width calculated from renderStyle tree. @@ -819,6 +629,66 @@ class RenderStyle return null; } + // Content box width of renderBoxModel calculated from tight width constraints. + double? get contentBoxConstraintsWidth { + if (paddingBoxConstraintsWidth == null) { + return null; + } + return paddingBoxConstraintsWidth! + - paddingLeft.computedValue + - paddingRight.computedValue; + } + + // Content box height of renderBoxModel calculated from tight height constraints. + double? get contentBoxConstraintsHeight { + if (paddingBoxConstraintsHeight == null) { + return null; + } + return paddingBoxConstraintsHeight! + - paddingTop.computedValue + - paddingBottom.computedValue; + } + + // Padding box width of renderBoxModel calculated from tight width constraints. + double? get paddingBoxConstraintsWidth { + if (borderBoxConstraintsWidth == null) { + return null; + } + return borderBoxConstraintsWidth! + - effectiveBorderLeftWidth.computedValue + - effectiveBorderRightWidth.computedValue; + } + + // Padding box height of renderBoxModel calculated from tight height constraints. + double? get paddingBoxConstraintsHeight { + if (borderBoxConstraintsHeight == null) { + return null; + } + return borderBoxConstraintsHeight! + - effectiveBorderTopWidth.computedValue + - effectiveBorderBottomWidth.computedValue; + } + + // Border box width of renderBoxModel calculated from tight width constraints. + double? get borderBoxConstraintsWidth { + if (renderBoxModel!.hasSize && + renderBoxModel!.constraints.hasTightWidth + ) { + return renderBoxModel!.constraints.maxWidth; + } + return null; + } + + // Border box height of renderBoxModel calculated from tight height constraints. + double? get borderBoxConstraintsHeight { + if (renderBoxModel!.hasSize && + renderBoxModel!.constraints.hasTightHeight + ) { + return renderBoxModel!.constraints.maxHeight; + } + return null; + } + /// Get height of replaced element by intrinsic ratio if height is not defined double getHeightByIntrinsicRatio() { // @TODO: move intrinsic width/height to renderStyle diff --git a/kraken/lib/src/css/values/length.dart b/kraken/lib/src/css/values/length.dart index 91279d191b..c28cb4c846 100644 --- a/kraken/lib/src/css/values/length.dart +++ b/kraken/lib/src/css/values/length.dart @@ -122,14 +122,36 @@ class CSSLengthValue { positionType == CSSPositionType.fixed; RenderStyle? parentRenderStyle = renderStyle!.parent; - - // Percentage relative width priority: logical width > renderer width - double? relativeParentWidth = isPositioned ? - parentRenderStyle?.paddingBoxLogicalWidth ?? parentRenderStyle?.paddingBoxWidth : - parentRenderStyle?.contentBoxLogicalWidth ?? parentRenderStyle?.contentBoxWidth; - RenderBoxModel? renderBoxModel = renderStyle!.renderBoxModel; + // Constraints is calculated before layout, the layouted size is identical to the tight constraints + // if constraints is tight, so it's safe to use the tight constraints as the parent size to resolve + // the child percentage length to save one extra layout to wait for parent layout complete. + + // Percentage relative width priority: tight constraints width > renderer width > logical width + double? parentPaddingBoxWidth = parentRenderStyle?.paddingBoxConstraintsWidth + ?? parentRenderStyle?.paddingBoxWidth + ?? parentRenderStyle?.paddingBoxLogicalWidth; + double? parentContentBoxWidth = parentRenderStyle?.contentBoxConstraintsWidth + ?? parentRenderStyle?.contentBoxWidth + ?? parentRenderStyle?.contentBoxLogicalWidth; + // Percentage relative height priority: tight constraints height > renderer height > logical height + double? parentPaddingBoxHeight = parentRenderStyle?.paddingBoxConstraintsHeight + ?? parentRenderStyle?.paddingBoxHeight + ?? parentRenderStyle?.paddingBoxLogicalHeight; + double? parentContentBoxHeight = parentRenderStyle?.contentBoxConstraintsHeight + ?? parentRenderStyle?.contentBoxHeight + ?? parentRenderStyle?.contentBoxLogicalHeight; + + // Positioned element is positioned relative to the padding box of its containing block + // while the others relative to the content box. + double? relativeParentWidth = isPositioned + ? parentPaddingBoxWidth + : parentContentBoxWidth; + double? relativeParentHeight = isPositioned + ? parentPaddingBoxHeight + : parentContentBoxHeight; + switch (propertyName) { case FONT_SIZE: // Relative to the parent font size. @@ -173,11 +195,6 @@ class CSSLengthValue { // The percentage height of positioned element and flex item resolves against the rendered height // of parent, mark parent as needs relayout if rendered height is not ready yet. if (isPositioned || isGrandParentFlexLayout) { - // Percentage relative height priority: logical height > renderer height - double? relativeParentHeight = isPositioned ? - parentRenderStyle?.paddingBoxLogicalHeight ?? parentRenderStyle?.paddingBoxHeight : - parentRenderStyle?.contentBoxLogicalHeight ?? parentRenderStyle?.contentBoxHeight; - if (relativeParentHeight != null) { _computedValue = value! * relativeParentHeight; } else { @@ -240,8 +257,6 @@ class CSSLengthValue { case TOP: case BOTTOM: // Offset of positioned element starts from the edge of padding box of containing block. - double? parentPaddingBoxHeight = parentRenderStyle?.paddingBoxHeight ?? - parentRenderStyle?.paddingBoxLogicalHeight; if (parentPaddingBoxHeight != null) { _computedValue = value! * parentPaddingBoxHeight; } else { @@ -255,8 +270,6 @@ class CSSLengthValue { case LEFT: case RIGHT: // Offset of positioned element starts from the edge of padding box of containing block. - double? parentPaddingBoxWidth = parentRenderStyle?.paddingBoxWidth ?? - parentRenderStyle?.paddingBoxLogicalWidth; if (parentPaddingBoxWidth != null) { _computedValue = value! * parentPaddingBoxWidth; } else { From e937361d031b136fa9257ac38598b988f67d8bae Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 17:57:04 +0800 Subject: [PATCH 063/167] fix: lint --- kraken/lib/src/css/render_style.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 801bf48ca1..aa5ca7e580 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -735,8 +735,3 @@ double _getCropWidthByPaddingBorder(RenderStyle renderStyle, double cropWidth) { return cropWidth; } -double _getCropHeightByPaddingBorder(RenderStyle renderStyle, double cropHeight) { - cropHeight += renderStyle.border.vertical; - cropHeight += renderStyle.padding.vertical; - return cropHeight; -} From b567bb7163aec8ad0ccbe8796903f88919728019 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 19:04:36 +0800 Subject: [PATCH 064/167] test: add tests for sliver constraints calculation --- .../css/css-display/sliver.ts.68990a0e1.png | Bin 0 -> 2742 bytes .../css/css-display/sliver.ts.68990a0e2.png | Bin 0 -> 2752 bytes .../specs/css/css-display/sliver.ts | 47 ++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 integration_tests/snapshots/css/css-display/sliver.ts.68990a0e1.png create mode 100644 integration_tests/snapshots/css/css-display/sliver.ts.68990a0e2.png diff --git a/integration_tests/snapshots/css/css-display/sliver.ts.68990a0e1.png b/integration_tests/snapshots/css/css-display/sliver.ts.68990a0e1.png new file mode 100644 index 0000000000000000000000000000000000000000..8c6aac0480637234a77dc3925be79fd96c083b24 GIT binary patch literal 2742 zcmeAS@N?(olHy`uVBq!ia0y~yV9a1(U~1rC1Bz^vdbAu!F%}28J29*~C-V}>VJUX< z4B-HR8jh3>1_rJKPZ!6KiaBrZZ0w4C%GCBS|51djNOnJqPvL=6P8$SVgCZhsM9jU` zy}_*a`y<{A)y$TXC=MkdgVX~O9@8JOY;%<{^eH+OssF>q;AmWR*ZN5P?)v<1H}l?_ zJlN?Mzr)tz+}6eH3?FKkcm-51Fa!dr(tl@G|Ie-7@b=`k*Y0N+9~`>uaQ|*u_3gj* z>;Glne$KATlyLi~;PK4cy}9%Aet(}>%&_Cb7tUYy_5X5j``?z6HDzz$wo_s05XxxK zXk-Xnv&y%&~e=z@b>;2}s|9{_peEjzJ z`}I{qQ+~*q9It%X-qJ6hzkXlU)L@1myPvcDIyd)iyZpA<=Ig8Y7{v8t3;%q({p}$8 z?Zxi5_4ntj-&=OYM5Exy!NPO@|2=wm`0e8UZNI@pFS>S zo}ZV$r|^u0#XRGTzT$76&s*>Pdae8a!`b=U`tA4aa=f5)LFPFBx5NKI!BlsaZ!|gK z$x-LFU%n{KP{HR9$@0*gKT5&Mz|oKx4GDOKIGPwn6T@D}4^qRFR@r*GBVJUX< z4B-HR8jh3>1_rJyPZ!6KiaBrZ7FqDUVul?bOfHmfzk9Fj z|M}JH^L~GK>}I%8R(PcFR$21x=ePe=K9OP2v#D+RcYS{S?Qi#U`}<}wA7GUiVuBe5 zH5E$5@_o6vtv=?j0K>e}x|gTK+1D^U5OSAc2xQ_FP`yAWs!?teE-w%gF{hNgD zH$St`|1;sk$H#BKUSD6t#ZdqKj>G+(oB#fJT=w}{=y}Emhn6b$-LJcC_ba3NOUCb~ z)8F=*_swEs?X$2r_w# { await snapshot(); }); + it('should works with height of sliver child changes', async (done) => { + let div; + let div1; + let div2; + div = createElement( + 'div', + { + style: { + display: 'sliver', + width: '200px', + height: '200px', + backgroundColor: 'red' + }, + }, [ + (div1 = createElement('div', { + style: { + positive: 'relative', + width: '200px', + height: '100px', + backgroundColor: 'green' + } + }, [ + createText('1') + ])), + (div2 = createElement('div', { + style: { + positive: 'relative', + width: '200px', + height: '100px', + backgroundColor: 'yellow' + } + }, [ + createText('2') + ])) + ] + ); + BODY.appendChild(div); + + await snapshot(); + + requestAnimationFrame(async () => { + div1.style.height = '50px'; + div2.style.height = '50px'; + await snapshot(); + done(); + }); + }); }); From b3a4735bbe092794068c2342afa4978bc977be3c Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 19:19:11 +0800 Subject: [PATCH 065/167] refactor: move intrinsic width/height/ratio to renderStyle --- kraken/lib/src/css/render_style.dart | 46 +++++++++------- kraken/lib/src/css/sizing.dart | 33 ++++++++++++ kraken/lib/src/dom/elements/img.dart | 8 +-- kraken/lib/src/rendering/box_model.dart | 71 +++---------------------- kraken/lib/src/rendering/flex.dart | 20 +++---- kraken/lib/src/rendering/intrinsic.dart | 9 ++-- 6 files changed, 84 insertions(+), 103 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index aa5ca7e580..3c119abbde 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -303,8 +303,6 @@ class RenderStyle break; } - double? intrinsicRatio = current.intrinsicRatio; - // Get width by intrinsic ratio for replaced element if width is auto. if (logicalWidth == null && intrinsicRatio != null) { logicalWidth = renderStyle.getWidthByIntrinsicRatio(); @@ -371,7 +369,6 @@ class RenderStyle } } } - double? intrinsicRatio = current.intrinsicRatio; // Get height by intrinsic ratio for replaced element if height is auto. if (logicalHeight == null && intrinsicRatio != null) { @@ -545,7 +542,9 @@ class RenderStyle if (contentBoxLogicalWidth == null) { return null; } - return contentBoxLogicalWidth! + paddingLeft.computedValue + paddingRight.computedValue; + return contentBoxLogicalWidth! + + paddingLeft.computedValue + + paddingRight.computedValue; } // Padding box height calculated from renderStyle tree. @@ -554,7 +553,9 @@ class RenderStyle if (contentBoxLogicalHeight == null) { return null; } - return contentBoxLogicalHeight! + paddingTop.computedValue + paddingBottom.computedValue; + return contentBoxLogicalHeight! + + paddingTop.computedValue + + paddingBottom.computedValue; } // Border box width calculated from renderStyle tree. @@ -563,7 +564,9 @@ class RenderStyle if (paddingBoxLogicalWidth == null) { return null; } - return paddingBoxLogicalWidth! + effectiveBorderLeftWidth.computedValue + effectiveBorderRightWidth.computedValue; + return paddingBoxLogicalWidth! + + effectiveBorderLeftWidth.computedValue + + effectiveBorderRightWidth.computedValue; } // Border box height calculated from renderStyle tree. @@ -572,7 +575,9 @@ class RenderStyle if (paddingBoxLogicalHeight == null) { return null; } - return paddingBoxLogicalHeight! + effectiveBorderTopWidth.computedValue + effectiveBorderBottomWidth.computedValue; + return paddingBoxLogicalHeight! + + effectiveBorderTopWidth.computedValue + + effectiveBorderBottomWidth.computedValue; } // Content box width of renderBoxModel after it was rendered. @@ -581,7 +586,9 @@ class RenderStyle if (paddingBoxWidth == null) { return null; } - return paddingBoxWidth! - paddingLeft.computedValue - paddingRight.computedValue; + return paddingBoxWidth! + - paddingLeft.computedValue + - paddingRight.computedValue; } // Content box height of renderBoxModel after it was rendered. @@ -590,7 +597,9 @@ class RenderStyle if (paddingBoxHeight == null) { return null; } - return paddingBoxHeight! - paddingTop.computedValue - paddingBottom.computedValue; + return paddingBoxHeight! + - paddingTop.computedValue + - paddingBottom.computedValue; } // Padding box width of renderBoxModel after it was rendered. @@ -599,7 +608,9 @@ class RenderStyle if (borderBoxWidth == null) { return null; } - return borderBoxWidth! - effectiveBorderLeftWidth.computedValue - effectiveBorderRightWidth.computedValue; + return borderBoxWidth! + - effectiveBorderLeftWidth.computedValue + - effectiveBorderRightWidth.computedValue; } // Padding box height of renderBoxModel after it was rendered. @@ -608,7 +619,9 @@ class RenderStyle if (borderBoxHeight == null) { return null; } - return borderBoxHeight! - effectiveBorderTopWidth.computedValue - effectiveBorderBottomWidth.computedValue; + return borderBoxHeight! + - effectiveBorderTopWidth.computedValue + - effectiveBorderBottomWidth.computedValue; } // Border box width of renderBoxModel after it was rendered. @@ -691,9 +704,6 @@ class RenderStyle /// Get height of replaced element by intrinsic ratio if height is not defined double getHeightByIntrinsicRatio() { - // @TODO: move intrinsic width/height to renderStyle - double? intrinsicWidth = renderBoxModel!.intrinsicWidth; - double intrinsicRatio = renderBoxModel!.intrinsicRatio!; double? realWidth = width.isAuto ? intrinsicWidth : width.computedValue; if (minWidth.isNotAuto && realWidth! < minWidth.computedValue) { realWidth = minWidth.computedValue; @@ -701,16 +711,12 @@ class RenderStyle if (maxWidth.isNotNone && realWidth! > maxWidth.computedValue) { realWidth = maxWidth.computedValue; } - double realHeight = realWidth! * intrinsicRatio; + double realHeight = realWidth! * intrinsicRatio!; return realHeight; } /// Get width of replaced element by intrinsic ratio if width is not defined double getWidthByIntrinsicRatio() { - // @TODO: move intrinsic width/height to renderStyle - double? intrinsicHeight = renderBoxModel!.intrinsicHeight; - double intrinsicRatio = renderBoxModel!.intrinsicRatio!; - double? realHeight = height.isAuto ? intrinsicHeight : height.computedValue; if (!minHeight.isAuto && realHeight! < minHeight.computedValue) { realHeight = minHeight.computedValue; @@ -718,7 +724,7 @@ class RenderStyle if (!maxHeight.isNone && realHeight! > maxHeight.computedValue) { realHeight = maxHeight.computedValue; } - double realWidth = realHeight! / intrinsicRatio; + double realWidth = realHeight! / intrinsicRatio!; return realWidth; } diff --git a/kraken/lib/src/css/sizing.dart b/kraken/lib/src/css/sizing.dart index 0ecd8f2b08..0c6441ec8c 100644 --- a/kraken/lib/src/css/sizing.dart +++ b/kraken/lib/src/css/sizing.dart @@ -125,6 +125,39 @@ mixin CSSSizingMixin on RenderStyleBase { _markSelfAndParentNeedsLayout(); } + // Intrinsic width of replaced element. + double? _intrinsicWidth; + double? get intrinsicWidth { + return _intrinsicWidth; + } + set intrinsicWidth(double? value) { + if (_intrinsicWidth == value) return; + _intrinsicWidth = value; + _markSelfAndParentNeedsLayout(); + } + + // Intrinsic height of replaced element. + double? _intrinsicHeight; + double? get intrinsicHeight { + return _intrinsicHeight; + } + set intrinsicHeight(double? value) { + if (_intrinsicHeight == value) return; + _intrinsicHeight = value; + _markSelfAndParentNeedsLayout(); + } + + // Aspect ratio of replaced element. + double? _intrinsicRatio; + double? get intrinsicRatio { + return _intrinsicRatio; + } + set intrinsicRatio(double? value) { + if (_intrinsicRatio == value) return; + _intrinsicRatio = value; + _markSelfAndParentNeedsLayout(); + } + void _markSelfAndParentNeedsLayout() { if (renderBoxModel == null) return; RenderBoxModel boxModel = renderBoxModel!; diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 55b8ba4010..b936d54ade 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -259,13 +259,13 @@ class ImageElement extends Element { _renderImage?.width = width; _renderImage?.height = height; - renderBoxModel!.intrinsicWidth = naturalWidth; - renderBoxModel!.intrinsicHeight = naturalHeight; + renderStyle.intrinsicWidth = naturalWidth; + renderStyle.intrinsicHeight = naturalHeight; if (naturalWidth == 0.0 || naturalHeight == 0.0) { - renderBoxModel!.intrinsicRatio = null; + renderStyle.intrinsicRatio = null; } else { - renderBoxModel!.intrinsicRatio = naturalHeight / naturalWidth; + renderStyle.intrinsicRatio = naturalHeight / naturalWidth; } } diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index 06251ab77a..4c82724773 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -626,18 +626,6 @@ class RenderBoxModel extends RenderBox return _contentConstraints; } - /// Used when setting percentage line-height style, it needs to be calculated when node attached - /// where it needs to know the font-size of its own element - bool _shouldLazyCalLineHeight = false; - - bool get shouldLazyCalLineHeight => _shouldLazyCalLineHeight; - - set shouldLazyCalLineHeight(bool value) { - if (_shouldLazyCalLineHeight != value) { - _shouldLazyCalLineHeight = value; - } - } - // When RenderBoxModel is scrolling box, contentConstraints are always equal to BoxConstraints(); bool isScrollingContentBox = false; @@ -733,53 +721,6 @@ class RenderBoxModel extends RenderBox ..parentData = parentData; } - // Boxes which have intrinsic ratio - double? _intrinsicWidth; - - double? get intrinsicWidth { - return _intrinsicWidth; - } - - set intrinsicWidth(double? value) { - if (_intrinsicWidth == value) return; - _intrinsicWidth = value; - _markSelfAndParentNeedsLayout(); - } - - // Boxes which have intrinsic ratio - double? _intrinsicHeight; - - double? get intrinsicHeight { - return _intrinsicHeight; - } - - set intrinsicHeight(double? value) { - if (_intrinsicHeight == value) return; - _intrinsicHeight = value; - _markSelfAndParentNeedsLayout(); - } - - double? _intrinsicRatio; - - double? get intrinsicRatio { - return _intrinsicRatio; - } - - set intrinsicRatio(double? value) { - if (_intrinsicRatio == value) return; - _intrinsicRatio = value; - _markSelfAndParentNeedsLayout(); - } - - // Sizing may affect parent size, mark parent as needsLayout in case - // renderBoxModel has tight constraints which will prevent parent from marking. - void _markSelfAndParentNeedsLayout() { - markNeedsLayout(); - if (parent is RenderBoxModel) { - (parent as RenderBoxModel).markNeedsLayout(); - } - } - /// Whether current box is the root of the document which corresponds to HTML element in dom tree. bool get isDocumentRootBox { // Get the outer box of overflow scroll box @@ -1423,12 +1364,12 @@ class RenderBoxModel extends RenderBox if (renderPositionPlaceholder != null) properties.add( DiagnosticsProperty('renderPositionHolder', renderPositionPlaceholder)); - if (intrinsicWidth != null) - properties.add(DiagnosticsProperty('intrinsicWidth', intrinsicWidth)); - if (intrinsicHeight != null) - properties.add(DiagnosticsProperty('intrinsicHeight', intrinsicHeight)); - if (intrinsicRatio != null) - properties.add(DiagnosticsProperty('intrinsicRatio', intrinsicRatio)); + if (renderStyle.intrinsicWidth != null) + properties.add(DiagnosticsProperty('intrinsicWidth', renderStyle.intrinsicWidth)); + if (renderStyle.intrinsicHeight != null) + properties.add(DiagnosticsProperty('intrinsicHeight', renderStyle.intrinsicHeight)); + if (renderStyle.intrinsicRatio != null) + properties.add(DiagnosticsProperty('intrinsicRatio', renderStyle.intrinsicRatio)); debugBoxDecorationProperties(properties); debugVisibilityProperties(properties); diff --git a/kraken/lib/src/rendering/flex.dart b/kraken/lib/src/rendering/flex.dart index 7d8b8bb348..031bced03d 100644 --- a/kraken/lib/src/rendering/flex.dart +++ b/kraken/lib/src/rendering/flex.dart @@ -493,20 +493,20 @@ class RenderFlexLayout extends RenderLayoutBox { : minHeight; if (child is RenderIntrinsic && - child.intrinsicRatio != null && + childRenderStyle.intrinsicRatio != null && _isHorizontalFlexDirection && childRenderStyle.width.isAuto) { double transferredSize = childRenderStyle.height.isNotAuto - ? childRenderStyle.height.computedValue * child.intrinsicRatio! - : child.intrinsicWidth!; + ? childRenderStyle.height.computedValue * childRenderStyle.intrinsicRatio! + : childRenderStyle.intrinsicWidth!; minMainSize = math.min(contentSize, transferredSize); } else if (child is RenderIntrinsic && - child.intrinsicRatio != null && + childRenderStyle.intrinsicRatio != null && !_isHorizontalFlexDirection && childRenderStyle.height.isAuto) { double transferredSize = childRenderStyle.width.isNotAuto - ? childRenderStyle.width.computedValue / child.intrinsicRatio! - : child.intrinsicHeight!; + ? childRenderStyle.width.computedValue / childRenderStyle.intrinsicRatio! + : childRenderStyle.intrinsicHeight!; minMainSize = math.min(contentSize, transferredSize); } else if (child is RenderBoxModel) { double? specifiedMainSize = _isHorizontalFlexDirection @@ -1528,9 +1528,9 @@ class RenderFlexLayout extends RenderLayoutBox { if (child is RenderIntrinsic && child.renderStyle.width.isAuto && child.renderStyle.minWidth.isAuto && - child.intrinsicRatio != null + child.renderStyle.intrinsicRatio != null ) { - minConstraintWidth = maxConstraintWidth = minConstraintHeight / child.intrinsicRatio!; + minConstraintWidth = maxConstraintWidth = minConstraintHeight / child.renderStyle.intrinsicRatio!; } } else { CSSLengthValue marginLeft = childRenderStyle.marginLeft; @@ -1566,9 +1566,9 @@ class RenderFlexLayout extends RenderLayoutBox { if (child is RenderIntrinsic && child.renderStyle.height.isAuto && child.renderStyle.minHeight.isAuto && - child.intrinsicRatio != null + child.renderStyle.intrinsicRatio != null ) { - minConstraintHeight = maxConstraintHeight = minConstraintWidth * child.intrinsicRatio!; + minConstraintHeight = maxConstraintHeight = minConstraintWidth * child.renderStyle.intrinsicRatio!; } } } diff --git a/kraken/lib/src/rendering/intrinsic.dart b/kraken/lib/src/rendering/intrinsic.dart index 55296f16f6..99d219e32f 100644 --- a/kraken/lib/src/rendering/intrinsic.dart +++ b/kraken/lib/src/rendering/intrinsic.dart @@ -66,6 +66,7 @@ class RenderIntrinsic extends RenderBoxModel double? maxWidth = renderStyle.maxWidth.isNone ? null : renderStyle.maxWidth.computedValue; double? minHeight = renderStyle.minHeight.isAuto ? null : renderStyle.minHeight.computedValue; double? maxHeight = renderStyle.maxHeight.isNone ? null : renderStyle.maxHeight.computedValue; + double? intrinsicRatio = renderStyle.intrinsicRatio; if (child != null) { late DateTime childLayoutStart; @@ -97,7 +98,7 @@ class RenderIntrinsic extends RenderBoxModel // max-height should respect intrinsic ratio with max-width if (intrinsicRatio != null && maxHeight == null) { - constraintHeight = constraintWidth * intrinsicRatio!; + constraintHeight = constraintWidth * intrinsicRatio; } } else if (isInlineLevel && minWidth != null && width == null) { constraintWidth = @@ -105,7 +106,7 @@ class RenderIntrinsic extends RenderBoxModel // max-height should respect intrinsic ratio with max-width if (intrinsicRatio != null && minHeight == null) { - constraintHeight = constraintWidth * intrinsicRatio!; + constraintHeight = constraintWidth * intrinsicRatio; } } @@ -116,7 +117,7 @@ class RenderIntrinsic extends RenderBoxModel // max-width should respect intrinsic ratio with max-height if (intrinsicRatio != null && maxWidth == null) { - constraintWidth = constraintHeight / intrinsicRatio!; + constraintWidth = constraintHeight / intrinsicRatio; } } else if (isInlineLevel && minHeight != null && height == null) { constraintHeight = @@ -124,7 +125,7 @@ class RenderIntrinsic extends RenderBoxModel // max-width should respect intrinsic ratio with max-height if (intrinsicRatio != null && minWidth == null) { - constraintWidth = constraintHeight / intrinsicRatio!; + constraintWidth = constraintHeight / intrinsicRatio; } } From dd36f25a5edca756fa0b420bcd3111845e109c26 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 19:21:34 +0800 Subject: [PATCH 066/167] fix: lint --- kraken/lib/src/css/render_style.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 3c119abbde..04e152cba8 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -338,7 +338,6 @@ class RenderStyle // Compute the content box height from render style. void computeContentBoxLogicalHeight() { - RenderBoxModel current = renderBoxModel!; RenderStyle renderStyle = this; double? logicalHeight; @@ -702,7 +701,7 @@ class RenderStyle return null; } - /// Get height of replaced element by intrinsic ratio if height is not defined + // Get height of replaced element by intrinsic ratio if height is not defined double getHeightByIntrinsicRatio() { double? realWidth = width.isAuto ? intrinsicWidth : width.computedValue; if (minWidth.isNotAuto && realWidth! < minWidth.computedValue) { @@ -715,7 +714,7 @@ class RenderStyle return realHeight; } - /// Get width of replaced element by intrinsic ratio if width is not defined + // Get width of replaced element by intrinsic ratio if width is not defined double getWidthByIntrinsicRatio() { double? realHeight = height.isAuto ? intrinsicHeight : height.computedValue; if (!minHeight.isAuto && realHeight! < minHeight.computedValue) { From 0940c4120b724c3c0152b272a69740d669101ca2 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 24 Nov 2021 21:38:14 +0800 Subject: [PATCH 067/167] refactor: content max constraint width calculation logic --- kraken/lib/src/css/render_style.dart | 249 +++++++++++------------- kraken/lib/src/rendering/box_model.dart | 12 +- 2 files changed, 125 insertions(+), 136 deletions(-) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 04e152cba8..14a1d2e334 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -239,19 +239,6 @@ class RenderStyle } } - // Find ancestor render style with display of not inline. - RenderStyle? _findAncestoreWithNoDisplayInline() { - RenderStyle renderStyle = this; - RenderStyle? parentRenderStyle = renderStyle.parent; - while(parentRenderStyle != null) { - if (parentRenderStyle.effectiveDisplay != CSSDisplay.inline) { - break; - } - parentRenderStyle = parentRenderStyle.parent; - } - return parentRenderStyle; - } - // Compute the content box width from render style. void computeContentBoxLogicalWidth() { RenderBoxModel current = renderBoxModel!; @@ -277,10 +264,10 @@ class RenderStyle // Block element (except replaced element) will stretch to the content width of its parent in flow layout. // Replaced element also stretch in flex layout if align-items is stretch. } else if (current is! RenderIntrinsic || parent is RenderFlexLayout) { - RenderStyle? targetParentRenderStyle = _findAncestoreWithNoDisplayInline(); + RenderStyle? ancestorRenderStyle = _findAncestorWithNoDisplayInline(); // Should ignore renderStyle of display inline when searching for ancestors to stretch width. - if (targetParentRenderStyle != null) { - logicalWidth = targetParentRenderStyle.contentBoxLogicalWidth; + if (ancestorRenderStyle != null) { + logicalWidth = ancestorRenderStyle.contentBoxLogicalWidth; // Should subtract horizontal margin of own from its parent content width. if (logicalWidth != null) { logicalWidth -= renderStyle.margin.horizontal; @@ -437,8 +424,8 @@ class RenderStyle return isStretch; } - // Max constraints width of content, used in calculating the remaining space for line wrapping - // in the stage of layout. + + // Max width to constrain its children, used in deciding the line wrapping timing of layout. double get contentMaxConstraintsWidth { // If renderBoxModel definite content constraints, use it as max constrains width of content. BoxConstraints? contentConstraints = renderBoxModel!.contentConstraints; @@ -446,63 +433,28 @@ class RenderStyle return contentConstraints.maxWidth; } - // If renderBoxModel has no logical content width (eg display is inline-block/inline-flex and - // has no width), find its ancestors with logical width set to calculate the remaining space. double contentMaxConstraintsWidth = double.infinity; - double cropWidth = 0; - - RenderStyle currentRenderStyle = this; - - // Get the nearest width of ancestor with width - while (true) { - RenderStyle? parentRenderStyle = currentRenderStyle.parent; - CSSDisplay? effectiveDisplay = currentRenderStyle.effectiveDisplay; - - // Flex item with flex-shrink 0 and no width/max-width will have infinity constraints - // even if parents have width - if (parentRenderStyle != null && (parentRenderStyle.display == CSSDisplay.flex || - parentRenderStyle.display == CSSDisplay.inlineFlex) - ) { - if (currentRenderStyle.flexShrink == 0 && - currentRenderStyle.width.isAuto && - currentRenderStyle.maxWidth.isNone) { - break; - } - } - - // Get width if width exists and element is not inline - if (effectiveDisplay != CSSDisplay.inline && - (currentRenderStyle.width.isNotAuto || currentRenderStyle.maxWidth.isNotNone)) { - // Get the min width between width and max-width - contentMaxConstraintsWidth = math.min( - (currentRenderStyle.width.isAuto ? null : currentRenderStyle.width.computedValue) ?? double.infinity, - (currentRenderStyle.maxWidth.isNone ? null : currentRenderStyle.maxWidth.computedValue) ?? double.infinity - ); - cropWidth = _getCropWidthByPaddingBorder(currentRenderStyle, cropWidth); - break; - } - - if (parentRenderStyle != null) { - cropWidth += currentRenderStyle.margin.horizontal; - cropWidth = _getCropWidthByPaddingBorder(currentRenderStyle, cropWidth); - currentRenderStyle = parentRenderStyle; - } else { - break; - } - } + RenderStyle renderStyle = this; + double? borderBoxLogicalWidth; + RenderStyle? ancestorRenderStyle = _findAncestorWithContentBoxLogicalWidth(); - if (contentMaxConstraintsWidth != double.infinity) { - contentMaxConstraintsWidth = contentMaxConstraintsWidth - cropWidth; + // If renderBoxModel has no logical width (eg. display is inline-block/inline-flex and + // has no width), the child width is constrained by its closest ancestor who has definite logical content box width. + if (ancestorRenderStyle != null) { + borderBoxLogicalWidth = ancestorRenderStyle.contentBoxLogicalWidth; } - // Set contentMaxConstraintsWidth to 0 when it is negative in the case of - // renderBoxModel's width exceeds its ancestors. - //
- //
- //
- //
- if (contentMaxConstraintsWidth < 0) { - contentMaxConstraintsWidth = 0; + if (borderBoxLogicalWidth != null) { + contentMaxConstraintsWidth = borderBoxLogicalWidth - + renderStyle.border.horizontal - + renderStyle.padding.horizontal; + // Logical width may be smaller than its border and padding width, + // in this case, content width will be negative which is illegal. + //
+ //
+ //
+ //
+ contentMaxConstraintsWidth = math.max(0, contentMaxConstraintsWidth); } return contentMaxConstraintsWidth; @@ -579,26 +531,22 @@ class RenderStyle + effectiveBorderBottomWidth.computedValue; } - // Content box width of renderBoxModel after it was rendered. - // https://www.w3.org/TR/css-box-3/#valdef-box-content-box - double? get contentBoxWidth { - if (paddingBoxWidth == null) { - return null; + // Border box width of renderBoxModel after it was rendered. + // https://www.w3.org/TR/css-box-3/#valdef-box-border-box + double? get borderBoxWidth { + if (renderBoxModel!.hasSize && renderBoxModel!.boxSize != null) { + return renderBoxModel!.boxSize!.width; } - return paddingBoxWidth! - - paddingLeft.computedValue - - paddingRight.computedValue; + return null; } - // Content box height of renderBoxModel after it was rendered. - // https://www.w3.org/TR/css-box-3/#valdef-box-content-box - double? get contentBoxHeight { - if (paddingBoxHeight == null) { - return null; + // Border box height of renderBoxModel after it was rendered. + // https://www.w3.org/TR/css-box-3/#valdef-box-border-box + double? get borderBoxHeight { + if (renderBoxModel!.hasSize && renderBoxModel!.boxSize != null) { + return renderBoxModel!.boxSize!.height; } - return paddingBoxHeight! - - paddingTop.computedValue - - paddingBottom.computedValue; + return null; } // Padding box width of renderBoxModel after it was rendered. @@ -623,44 +571,48 @@ class RenderStyle - effectiveBorderBottomWidth.computedValue; } - // Border box width of renderBoxModel after it was rendered. - // https://www.w3.org/TR/css-box-3/#valdef-box-border-box - double? get borderBoxWidth { - if (renderBoxModel!.hasSize && renderBoxModel!.boxSize != null) { - return renderBoxModel!.boxSize!.width; - } - return null; - } - - // Border box height of renderBoxModel after it was rendered. - // https://www.w3.org/TR/css-box-3/#valdef-box-border-box - double? get borderBoxHeight { - if (renderBoxModel!.hasSize && renderBoxModel!.boxSize != null) { - return renderBoxModel!.boxSize!.height; - } - return null; - } - - // Content box width of renderBoxModel calculated from tight width constraints. - double? get contentBoxConstraintsWidth { - if (paddingBoxConstraintsWidth == null) { + // Content box width of renderBoxModel after it was rendered. + // https://www.w3.org/TR/css-box-3/#valdef-box-content-box + double? get contentBoxWidth { + if (paddingBoxWidth == null) { return null; } - return paddingBoxConstraintsWidth! + return paddingBoxWidth! - paddingLeft.computedValue - paddingRight.computedValue; } - // Content box height of renderBoxModel calculated from tight height constraints. - double? get contentBoxConstraintsHeight { - if (paddingBoxConstraintsHeight == null) { + // Content box height of renderBoxModel after it was rendered. + // https://www.w3.org/TR/css-box-3/#valdef-box-content-box + double? get contentBoxHeight { + if (paddingBoxHeight == null) { return null; } - return paddingBoxConstraintsHeight! + return paddingBoxHeight! - paddingTop.computedValue - paddingBottom.computedValue; } + // Border box width of renderBoxModel calculated from tight width constraints. + double? get borderBoxConstraintsWidth { + if (renderBoxModel!.hasSize && + renderBoxModel!.constraints.hasTightWidth + ) { + return renderBoxModel!.constraints.maxWidth; + } + return null; + } + + // Border box height of renderBoxModel calculated from tight height constraints. + double? get borderBoxConstraintsHeight { + if (renderBoxModel!.hasSize && + renderBoxModel!.constraints.hasTightHeight + ) { + return renderBoxModel!.constraints.maxHeight; + } + return null; + } + // Padding box width of renderBoxModel calculated from tight width constraints. double? get paddingBoxConstraintsWidth { if (borderBoxConstraintsWidth == null) { @@ -681,24 +633,24 @@ class RenderStyle - effectiveBorderBottomWidth.computedValue; } - // Border box width of renderBoxModel calculated from tight width constraints. - double? get borderBoxConstraintsWidth { - if (renderBoxModel!.hasSize && - renderBoxModel!.constraints.hasTightWidth - ) { - return renderBoxModel!.constraints.maxWidth; + // Content box width of renderBoxModel calculated from tight width constraints. + double? get contentBoxConstraintsWidth { + if (paddingBoxConstraintsWidth == null) { + return null; } - return null; + return paddingBoxConstraintsWidth! + - paddingLeft.computedValue + - paddingRight.computedValue; } - // Border box height of renderBoxModel calculated from tight height constraints. - double? get borderBoxConstraintsHeight { - if (renderBoxModel!.hasSize && - renderBoxModel!.constraints.hasTightHeight - ) { - return renderBoxModel!.constraints.maxHeight; + // Content box height of renderBoxModel calculated from tight height constraints. + double? get contentBoxConstraintsHeight { + if (paddingBoxConstraintsHeight == null) { + return null; } - return null; + return paddingBoxConstraintsHeight! + - paddingTop.computedValue + - paddingBottom.computedValue; } // Get height of replaced element by intrinsic ratio if height is not defined @@ -732,11 +684,44 @@ class RenderStyle // Clear reference to it's parent. parent = null; } -} -double _getCropWidthByPaddingBorder(RenderStyle renderStyle, double cropWidth) { - cropWidth += renderStyle.border.horizontal; - cropWidth += renderStyle.padding.horizontal; - return cropWidth; + // Find ancestor render style with display of not inline. + RenderStyle? _findAncestorWithNoDisplayInline() { + RenderStyle renderStyle = this; + RenderStyle? parentRenderStyle = renderStyle.parent; + while(parentRenderStyle != null) { + if (parentRenderStyle.effectiveDisplay != CSSDisplay.inline) { + break; + } + parentRenderStyle = parentRenderStyle.parent; + } + return parentRenderStyle; + } + + // Find ancestor render style with definite content box logical width. + RenderStyle? _findAncestorWithContentBoxLogicalWidth() { + RenderStyle renderStyle = this; + RenderStyle? parentRenderStyle = renderStyle.parent; + + while(parentRenderStyle != null) { + RenderStyle? grandParentRenderStyle = parentRenderStyle.parent; + // Flex item with flex-shrink 0 and no width/max-width will have infinity constraints + // even if parents have width. + if (grandParentRenderStyle != null) { + bool isGrandParentFlex = grandParentRenderStyle.display == CSSDisplay.flex || + grandParentRenderStyle.display == CSSDisplay.inlineFlex; + if (isGrandParentFlex && parentRenderStyle.flexShrink == 0) { + return null; + } + } + + if (parentRenderStyle.contentBoxLogicalWidth != null) { + break; + } + + parentRenderStyle = grandParentRenderStyle; + } + return parentRenderStyle; + } } diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index 4c82724773..35c91f639b 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -834,13 +834,17 @@ class RenderBoxModel extends RenderBox // Width should be not smaller than border and padding in horizontal direction // when box-sizing is border-box which is only supported. - double minConstraintWidth = renderStyle.effectiveBorderLeftWidth.computedValue + renderStyle.effectiveBorderRightWidth.computedValue + - renderStyle.paddingLeft.computedValue + renderStyle.paddingRight.computedValue; + double minConstraintWidth = renderStyle.effectiveBorderLeftWidth.computedValue + + renderStyle.effectiveBorderRightWidth.computedValue + + renderStyle.paddingLeft.computedValue + + renderStyle.paddingRight.computedValue; double maxConstraintWidth = renderStyle.borderBoxLogicalWidth ?? double.infinity; // Height should be not smaller than border and padding in vertical direction // when box-sizing is border-box which is only supported. - double minConstraintHeight = renderStyle.effectiveBorderTopWidth.computedValue + renderStyle.effectiveBorderBottomWidth.computedValue + - renderStyle.paddingTop.computedValue + renderStyle.paddingBottom.computedValue; + double minConstraintHeight = renderStyle.effectiveBorderTopWidth.computedValue + + renderStyle.effectiveBorderBottomWidth.computedValue + + renderStyle.paddingTop.computedValue + + renderStyle.paddingBottom.computedValue; double maxConstraintHeight = renderStyle.borderBoxLogicalHeight ?? double.infinity; if (parent is RenderFlexLayout) { From ffe0eb1e59abd23cc37a242ca70b398238a2e66f Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 25 Nov 2021 14:10:57 +0800 Subject: [PATCH 068/167] :art: chore: add line. --- bridge/bindings/qjs/html_parser.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/bridge/bindings/qjs/html_parser.cc b/bridge/bindings/qjs/html_parser.cc index 32e89e5821..28ab1cc66b 100644 --- a/bridge/bindings/qjs/html_parser.cc +++ b/bridge/bindings/qjs/html_parser.cc @@ -91,6 +91,7 @@ bool HTMLParser::parseHTML(const char* code, size_t codeLength, NodeInstance* ro std::string html = std::string(code, codeLength); return parseHTML(html, rootNode); } + void HTMLParser::parseProperty(ElementInstance* element, GumboElement* gumboElement) { JSContext* context = element->context(); QjsContext* ctx = context->ctx(); From 61893339621d2d3202b461bb6f1da0e8327da74b Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 25 Nov 2021 14:13:35 +0800 Subject: [PATCH 069/167] :art: chore: format html file. --- integration_tests/specs/dom/elements/div.html | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/integration_tests/specs/dom/elements/div.html b/integration_tests/specs/dom/elements/div.html index 0ddf03d04b..93b42cd274 100644 --- a/integration_tests/specs/dom/elements/div.html +++ b/integration_tests/specs/dom/elements/div.html @@ -1,4 +1,23 @@ -
Welcome to Your Kraken AppMore information about KrakenVisit http://openkraken.com/
+ + +
+ Welcome to Your Kraken AppMore information about KrakenVisit http://openkraken.com/ +
+ From d6a761832ef4f2a9fb0a351c1f23ed494913febe Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Fri, 26 Nov 2021 14:05:41 +0800 Subject: [PATCH 070/167] feat: resize image if image set size by javascript. --- kraken/lib/src/dom/elements/img.dart | 246 ++++++++++++------ .../src/painting/image_provider_factory.dart | 80 ++++-- 2 files changed, 219 insertions(+), 107 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 47197f0f1b..37d5e32e02 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -5,7 +5,7 @@ import 'dart:async'; import 'dart:ffi'; -import 'dart:ui' as ui show Image; +import 'dart:ui' as ui; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; @@ -26,22 +26,18 @@ const Map _defaultStyle = { class ImageElement extends Element { // The render box to draw image. RenderImage? _renderImage; - - // The image source url. - String? get _source => getProperty('src'); - + bool _isListeningToStream = false; ImageProvider? _imageProvider; ImageStream? _imageStream; ImageInfo? _imageInfo; - late ImageStreamListener _renderStreamListener; - double? _propertyWidth; double? _propertyHeight; ui.Image? get image => _imageInfo?.image; + bool get renderImageAttached => _renderImage?.image != null; /// Number of image frame, used to identify multi frame image after loaded. int _frameCount = 0; @@ -49,6 +45,7 @@ class ImageElement extends Element { bool _isInLazyLoading = false; bool get _shouldLazyLoading => properties['loading'] == 'lazy'; + ImageStreamCompleterHandle? _completerHandle; ImageElement(int targetId, Pointer nativeEventTarget, ElementManager elementManager) : super( @@ -57,7 +54,6 @@ class ImageElement extends Element { elementManager, isIntrinsicBox: true, defaultStyle: _defaultStyle) { - _renderStreamListener = ImageStreamListener(_renderImageStream, onError: _onImageError); } @override @@ -80,10 +76,11 @@ class ImageElement extends Element { renderBoxModel!.addIntersectionChangeListener(_handleIntersectionChange); } else { _constructImageChild(); - _loadImage(); + _attachImage(); + _resolveImage(); + _listenToStream(); } } - _resize(); } @override @@ -91,34 +88,38 @@ class ImageElement extends Element { super.didDetachRenderer(); style.removeStyleChangeListener(_stylePropertyChanged); - _resetImage(); + _stopListeningToStream(keepStreamAlive: true); + } - // Unlink image render box, which has been detached. - _renderImage = null; + ImageStreamListener? _imageStreamListener; + ImageStreamListener _getListener({bool recreateListener = false}) { + if(_imageStreamListener == null || recreateListener) { + _imageStreamListener = ImageStreamListener( + _handleImageFrame, + onError: _onImageError + ); + } + return _imageStreamListener!; } - void _resetImage() { - _imageInfo = null; + void _listenToStream() { + if (_isListeningToStream) + return; - // @NOTE: Evict image cache, make multi frame image can replay. - // https://github.com/flutter/flutter/issues/51775 - _imageProvider?.evict(); - _imageProvider = null; + _imageStream!.addListener(_getListener()); + _completerHandle?.dispose(); + _completerHandle = null; - _renderImage?.image = null; + _isListeningToStream = true; } @override void dispose() { super.dispose(); - - _imageProvider?.evict(); + _stopListeningToStream(); + _completerHandle?.dispose(); + _replaceImage(info: null); _imageProvider = null; - - _imageStream?.removeListener(_renderStreamListener); - _imageStream = null; - - _renderImage = null; } double get width { @@ -156,7 +157,7 @@ class ImageElement extends Element { // Once appear remove the listener _resetLazyLoading(); _constructImageChild(); - _loadImage(); + _resolveImage(); } } @@ -195,31 +196,6 @@ class ImageElement extends Element { @override bool get hasRepaintBoundary => _frameCount > 2 || super.hasRepaintBoundary; - void _renderImageStream(ImageInfo imageInfo, bool synchronousCall) { - _frameCount++; - _imageInfo = imageInfo; - - // Only trigger load once. - if (!_loaded) { - _loaded = true; - - if (synchronousCall) { - // `synchronousCall` happens when caches image and calling `addListener`. - scheduleMicrotask(_handleEventAfterImageLoaded); - } else { - _handleEventAfterImageLoaded(); - } - } - - if (isRendererAttached) { - _resize(); - _renderImage?.image = image; - } - } - - // Mark if the same src loaded. - bool _loaded = false; - void _onImageError(Object exception, StackTrace? stackTrace) { dispatchEvent(Event(EVENT_ERROR)); } @@ -286,11 +262,135 @@ class ImageElement extends Element { void removeProperty(String key) { super.removeProperty(key); if (key == 'src') { - _resetImage(); - _loaded = false; + _stopListeningToStream(keepStreamAlive: true); } else if (key == 'loading' && _isInLazyLoading && _imageProvider == null) { _resetLazyLoading(); + _stopListeningToStream(keepStreamAlive: true); + } + } + + /// Stops listening to the image stream, if this state object has attached a + /// listener. + /// + /// If the listener from this state is the last listener on the stream, the + /// stream will be disposed. To keep the stream alive, set `keepStreamAlive` + /// to true, which create [ImageStreamCompleterHandle] to keep the completer + /// alive and is compatible with the [TickerMode] being off. + void _stopListeningToStream({bool keepStreamAlive = false}) { + if (!_isListeningToStream) + return; + + if (keepStreamAlive && _completerHandle == null && _imageStream?.completer != null) { + _completerHandle = _imageStream!.completer!.keepAlive(); + } + + _imageStream!.removeListener(_getListener()); + _isListeningToStream = false; + } + + Uri? _resolveSrc() { + String? src = properties['src']; + if (src != null && src.isNotEmpty) { + Uri base = Uri.parse(elementManager.controller.href); + return elementManager.controller.uriParser!.resolve(base, Uri.parse(src)); } + return null; + } + + void _updateSourceStream(ImageStream newStream) { + if (_imageStream?.key == newStream.key) return; + + if (_isListeningToStream) { + _imageStream!.removeListener(_getListener()); + } + + _frameCount = 0; + _imageStream = newStream; + + if (_isListeningToStream) { + _imageStream!.addListener(_getListener()); + } + } + + void _resolveImage() { + final Uri? resolvedUri = _resolveSrc(); + if (resolvedUri == null) return; + + double? width = null; + double? height = null; + + if (isRendererAttached) { + width = renderStyle.width.isAuto ? _propertyWidth : renderStyle.width.computedValue; + height = renderStyle.height.isAuto ? _propertyHeight : renderStyle.height.computedValue; + } else { + width = _propertyWidth; + height = _propertyHeight; + } + + int? cachedWidth = width != null ? (width * ui.window.devicePixelRatio).toInt() : null; + int? cachedHeight = height != null ? (height * ui.window.devicePixelRatio).toInt() : null; + + ImageProvider? provider = _imageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); + if (provider == null) return; + final ImageStream newStream = provider.resolve(ImageConfiguration.empty); + _updateSourceStream(newStream); + } + + void _replaceImage({required ImageInfo? info}) { + _imageInfo = info; + } + + void _attachImage() { + assert(isRendererAttached); + assert(_renderImage != null); + if (_imageInfo == null) return; + _renderImage!.image = image; + } + + void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) { + _replaceImage(info: imageInfo); + _frameCount++; + + if (synchronousCall) { + // `synchronousCall` happens when caches image and calling `addListener`. + scheduleMicrotask(_handleEventAfterImageLoaded); + } else { + _handleEventAfterImageLoaded(); + } + + + print(image?.debugDisposed); + try { + _attachImage(); + } catch (e, stack) { + print('$e\n$stack'); + } + print(3); + _resize(); + } + + // Prefetches an image into the image cache. + void _precacheImage() { + final ImageConfiguration config = ImageConfiguration.empty; + final Uri? resolvedUri = _resolveSrc(); + if (resolvedUri == null) return; + final ImageProvider? provider = _imageProvider = getImageProvider(resolvedUri); + if (provider == null) return; + _frameCount = 0; + final ImageStream stream = provider.resolve(config); + ImageStreamListener? listener; + listener = ImageStreamListener( + (ImageInfo? image, bool sync) { + // Give callers until at least the end of the frame to subscribe to the + // image stream. + // See ImageCache._liveImages + SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) { + stream.removeListener(listener!); + }); + }, + onError: _onImageError + ); + stream.addListener(listener); } @override @@ -298,42 +398,18 @@ class ImageElement extends Element { bool propertyChanged = properties[key] != value; super.setProperty(key, value); // Reset frame number to zero when image needs to reload - _frameCount = 0; if (key == 'src' && propertyChanged && !_shouldLazyLoading) { - // Loads the image immediately. - _loaded = false; - _loadImage(); - } else if (key == 'loading' && _isInLazyLoading) { - // Should reset lazy when value change. + _precacheImage(); + } else if (key == 'loading' && propertyChanged && _isInLazyLoading) { _resetLazyLoading(); } else if (key == WIDTH) { _propertyWidth = CSSNumber.parseNumber(value); - _resize(); + _resolveImage(); } else if (key == HEIGHT) { - _propertyHeight = CSSNumber.parseNumber(value); - _resize(); + _propertyWidth = CSSNumber.parseNumber(value); + _resolveImage(); } - } - - void _loadImage() { - _resetImage(); - - if (_source != null && _source!.isNotEmpty) { - Uri base = Uri.parse(elementManager.controller.href); - Uri resolvedUri = elementManager.controller.uriParser!.resolve(base, Uri.parse(_source!)); - ImageProvider? imageProvider = getImageProvider(resolvedUri, - cache: properties['caching'], - contextId: elementManager.contextId - ); - - if (imageProvider != null) { - _imageProvider = imageProvider; - _imageStream = imageProvider - .resolve(ImageConfiguration.empty) - ..addListener(_renderStreamListener); - } - } } @override @@ -354,7 +430,7 @@ class ImageElement extends Element { void _stylePropertyChanged(String property, String? original, String present) { if (property == WIDTH || property == HEIGHT) { - _resize(); + _resolveImage(); } else if (property == OBJECT_FIT && _renderImage != null) { _renderImage!.fit = renderBoxModel!.renderStyle.objectFit; } else if (property == OBJECT_POSITION && _renderImage != null) { diff --git a/kraken/lib/src/painting/image_provider_factory.dart b/kraken/lib/src/painting/image_provider_factory.dart index 67395d2370..a563cec604 100644 --- a/kraken/lib/src/painting/image_provider_factory.dart +++ b/kraken/lib/src/painting/image_provider_factory.dart @@ -3,7 +3,6 @@ * Author: Kraken Team. */ - import 'dart:io'; import 'dart:typed_data'; @@ -16,25 +15,38 @@ import 'package:kraken/painting.dart'; /// This class allows user to customize Kraken's image loading. -class ImageProviderParams {} +class ImageProviderParams { + int? cachedWidth; + int? cachedHeight; + + ImageProviderParams({this.cachedWidth, this.cachedHeight}); +} class CachedNetworkImageProviderParams extends ImageProviderParams { int? contextId; - CachedNetworkImageProviderParams(this.contextId); + + CachedNetworkImageProviderParams(this.contextId, + {int? cachedWidth, int? cachedHeight}) + : super(cachedWidth: cachedWidth, cachedHeight: cachedHeight); } class FileImageProviderParams extends ImageProviderParams { File file; - FileImageProviderParams(this.file); + + FileImageProviderParams(this.file, {int? cachedWidth, int? cachedHeight}) + : super(cachedWidth: cachedWidth, cachedHeight: cachedHeight); } class DataUrlImageProviderParams extends ImageProviderParams { Uint8List bytes; - DataUrlImageProviderParams(this.bytes); + + DataUrlImageProviderParams(this.bytes, {int? cachedWidth, int? cachedHeight}) + : super(cachedWidth: cachedWidth, cachedHeight: cachedHeight); } /// A factory function allow user to build an customized ImageProvider class. -typedef ImageProviderFactory = ImageProvider? Function(Uri uri, ImageProviderParams params); +typedef ImageProviderFactory = ImageProvider? Function( + Uri uri, ImageProviderParams params); /// defines the types of supported image source. enum ImageType { @@ -93,31 +105,47 @@ ImageProviderFactory _dataUrlProviderFactory = defaultDataUrlProviderFactory; ImageProviderFactory _blobProviderFactory = defaultBlobProviderFactory; ImageProviderFactory _assetsProviderFactory = defaultAssetsProvider; -ImageProvider? getImageProvider(Uri resolvedUri, { int? contextId, cache = 'auto' }) { +ImageProvider? getImageProvider(Uri resolvedUri, + {int? contextId, cache = 'auto', int? cachedWidth, int? cachedHeight}) { ImageType imageType = CSSUrl.parseImageUrl(resolvedUri, cache: cache); ImageProviderFactory factory = _getImageProviderFactory(imageType); - switch(imageType) { + switch (imageType) { case ImageType.cached: - return factory(resolvedUri, CachedNetworkImageProviderParams(contextId)); + return factory( + resolvedUri, + CachedNetworkImageProviderParams(contextId, + cachedWidth: cachedWidth, cachedHeight: cachedHeight)); case ImageType.network: - return factory(resolvedUri, CachedNetworkImageProviderParams(contextId)); + return factory( + resolvedUri, + CachedNetworkImageProviderParams(contextId, + cachedWidth: cachedWidth, cachedHeight: cachedHeight)); case ImageType.file: File file = File.fromUri(resolvedUri); - return factory(resolvedUri, FileImageProviderParams(file)); + return factory( + resolvedUri, + FileImageProviderParams(file, + cachedWidth: cachedWidth, cachedHeight: cachedHeight)); case ImageType.dataUrl: - // Data URL: https://tools.ietf.org/html/rfc2397 - // dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + // Data URL: https://tools.ietf.org/html/rfc2397 + // dataurl := "data:" [ mediatype ] [ ";base64" ] "," data UriData data = UriData.fromUri(resolvedUri); if (data.isBase64) { - return factory(resolvedUri, DataUrlImageProviderParams(data.contentAsBytes())); + return factory( + resolvedUri, + DataUrlImageProviderParams(data.contentAsBytes(), + cachedWidth: cachedWidth, cachedHeight: cachedHeight)); } return null; case ImageType.blob: - // TODO: support blob data type + // TODO: support blob data type return null; case ImageType.assets: - return factory(resolvedUri, ImageProviderParams()); + return factory( + resolvedUri, + ImageProviderParams( + cachedWidth: cachedWidth, cachedHeight: cachedHeight)); } } @@ -165,26 +193,34 @@ void setCustomImageProviderFactory(ImageType imageType, ImageProviderFactory cus /// default ImageProviderFactory implementation of [ImageType.cached] ImageProvider defaultCachedProviderFactory(Uri uri, ImageProviderParams params) { - return CachedNetworkImage(uri.toString(), contextId: (params as CachedNetworkImageProviderParams).contextId); + return ResizeImage.resizeIfNeeded( + params.cachedWidth, + params.cachedHeight, + CachedNetworkImage(uri.toString(), + contextId: (params as CachedNetworkImageProviderParams).contextId)); } /// default ImageProviderFactory implementation of [ImageType.network] ImageProvider defaultNetworkProviderFactory(Uri uri, ImageProviderParams params) { NetworkImage networkImage = NetworkImage(uri.toString(), headers: { HttpHeaders.userAgentHeader: getKrakenInfo().userAgent, - HttpHeaderContext: (params as CachedNetworkImageProviderParams).contextId.toString(), + HttpHeaderContext: + (params as CachedNetworkImageProviderParams).contextId.toString(), }); - return networkImage; + return ResizeImage.resizeIfNeeded( + params.cachedWidth, params.cachedHeight, networkImage); } /// default ImageProviderFactory implementation of [ImageType.file] ImageProvider? defaultFileProviderFactory(Uri uri, ImageProviderParams params) { - return FileImage((params as FileImageProviderParams).file); + return ResizeImage.resizeIfNeeded( + params.cachedWidth, params.cachedHeight, FileImage((params as FileImageProviderParams).file)); } /// default ImageProviderFactory implementation of [ImageType.dataUrl]. ImageProvider? defaultDataUrlProviderFactory(Uri uri, ImageProviderParams params) { - return MemoryImage((params as DataUrlImageProviderParams).bytes); + return ResizeImage.resizeIfNeeded( + params.cachedWidth, params.cachedHeight, MemoryImage((params as DataUrlImageProviderParams).bytes)); } /// default ImageProviderFactory implementation of [ImageType.blob]. @@ -195,5 +231,5 @@ ImageProvider? defaultBlobProviderFactory(Uri uri, ImageProviderParams params) { /// default ImageProviderFactory implementation of [ImageType.assets]. ImageProvider defaultAssetsProvider(Uri uri, ImageProviderParams params) { - return AssetImage(uri.toString()); + return ResizeImage.resizeIfNeeded(params.cachedWidth, params.cachedHeight, AssetImage(uri.toString())); } From 3cfbdf89bb95d1d9a73281aab6882a9d5a37b74d Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Fri, 26 Nov 2021 15:03:05 +0800 Subject: [PATCH 071/167] chore: remove unused code. --- kraken/lib/src/dom/elements/img.dart | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 3022eb80b7..8f1dcb368e 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -37,7 +37,6 @@ class ImageElement extends Element { double? _propertyHeight; ui.Image? get image => _imageInfo?.image; - bool get renderImageAttached => _renderImage?.image != null; /// Number of image frame, used to identify multi frame image after loaded. int _frameCount = 0; @@ -92,13 +91,11 @@ class ImageElement extends Element { } ImageStreamListener? _imageStreamListener; - ImageStreamListener _getListener({bool recreateListener = false}) { - if(_imageStreamListener == null || recreateListener) { - _imageStreamListener = ImageStreamListener( - _handleImageFrame, - onError: _onImageError - ); - } + ImageStreamListener _getListener() { + _imageStreamListener ??= ImageStreamListener( + _handleImageFrame, + onError: _onImageError + ); return _imageStreamListener!; } From 63b06b02d1fed2aec9a8a52ee2103270860771b1 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Fri, 26 Nov 2021 15:03:31 +0800 Subject: [PATCH 072/167] fix: fix image load event trigger multiple times. --- kraken/lib/src/dom/elements/img.dart | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 8f1dcb368e..662b303244 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -42,6 +42,7 @@ class ImageElement extends Element { int _frameCount = 0; bool _isInLazyLoading = false; + bool _imageLoaded = false; bool get _shouldLazyLoading => properties['loading'] == 'lazy'; ImageStreamCompleterHandle? _completerHandle; @@ -344,11 +345,14 @@ class ImageElement extends Element { _replaceImage(info: imageInfo); _frameCount++; - if (synchronousCall) { - // `synchronousCall` happens when caches image and calling `addListener`. - scheduleMicrotask(_handleEventAfterImageLoaded); - } else { - _handleEventAfterImageLoaded(); + if (!_imageLoaded) { + _imageLoaded = true; + if (synchronousCall) { + // `synchronousCall` happens when caches image and calling `addListener`. + scheduleMicrotask(_handleEventAfterImageLoaded); + } else { + _handleEventAfterImageLoaded(); + } } // Multi frame image should convert to repaint boundary. From fe0e262634cd7bd44a5e3e66c7fb850896e8029b Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Fri, 26 Nov 2021 15:05:00 +0800 Subject: [PATCH 073/167] chore: _stopListeningToStream -> _stopListeningStream --- kraken/lib/src/dom/elements/img.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 662b303244..f4d1b5a920 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -88,7 +88,7 @@ class ImageElement extends Element { super.didDetachRenderer(); style.removeStyleChangeListener(_stylePropertyChanged); - _stopListeningToStream(keepStreamAlive: true); + _stopListeningStream(keepStreamAlive: true); } ImageStreamListener? _imageStreamListener; @@ -114,7 +114,7 @@ class ImageElement extends Element { @override void dispose() { super.dispose(); - _stopListeningToStream(); + _stopListeningStream(); _completerHandle?.dispose(); _replaceImage(info: null); _imageProvider = null; @@ -256,10 +256,10 @@ class ImageElement extends Element { void removeProperty(String key) { super.removeProperty(key); if (key == 'src') { - _stopListeningToStream(keepStreamAlive: true); + _stopListeningStream(keepStreamAlive: true); } else if (key == 'loading' && _isInLazyLoading && _imageProvider == null) { _resetLazyLoading(); - _stopListeningToStream(keepStreamAlive: true); + _stopListeningStream(keepStreamAlive: true); } } @@ -269,8 +269,8 @@ class ImageElement extends Element { /// If the listener from this state is the last listener on the stream, the /// stream will be disposed. To keep the stream alive, set `keepStreamAlive` /// to true, which create [ImageStreamCompleterHandle] to keep the completer - /// alive and is compatible with the [TickerMode] being off. - void _stopListeningToStream({bool keepStreamAlive = false}) { + /// alive. + void _stopListeningStream({bool keepStreamAlive = false}) { if (!_isListeningToStream) return; From 497029872cf27c4cfbcb043adbeca7481499bbb8 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Fri, 26 Nov 2021 15:10:57 +0800 Subject: [PATCH 074/167] refactor: move CSS.parseImageUrl to imageProvider. --- kraken/lib/css.dart | 1 - kraken/lib/src/css/values/url.dart | 25 ------------------- .../src/painting/image_provider_factory.dart | 19 ++++++++++++-- 3 files changed, 17 insertions(+), 28 deletions(-) delete mode 100644 kraken/lib/src/css/values/url.dart diff --git a/kraken/lib/css.dart b/kraken/lib/css.dart index 0691dd583c..d7574ae1fc 100644 --- a/kraken/lib/css.dart +++ b/kraken/lib/css.dart @@ -54,6 +54,5 @@ export 'src/css/values/number.dart'; export 'src/css/values/integer.dart'; export 'src/css/values/position.dart'; export 'src/css/values/time.dart'; -export 'src/css/values/url.dart'; export 'src/css/values/percentage.dart'; export 'src/css/values/textual.dart'; diff --git a/kraken/lib/src/css/values/url.dart b/kraken/lib/src/css/values/url.dart deleted file mode 100644 index f29adf987a..0000000000 --- a/kraken/lib/src/css/values/url.dart +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2019-present Alibaba Inc. All rights reserved. - * Author: Kraken Team. - */ - -import 'package:kraken/painting.dart'; - -// CSS Values and Units: https://drafts.csswg.org/css-values-3/#urls -class CSSUrl { - static ImageType parseImageUrl(Uri resolvedUri, { cache = 'auto' }) { - if (resolvedUri.isScheme('HTTP') || resolvedUri.isScheme('HTTPS')) { - return (cache == 'store' || cache == 'auto') - ? ImageType.cached - : ImageType.network; - } else if (resolvedUri.isScheme('FILE')) { - return ImageType.file; - } else if (resolvedUri.isScheme('DATA')) { - return ImageType.dataUrl; - } else if (resolvedUri.isScheme('BLOB')) { - return ImageType.blob; - } else { - return ImageType.assets; - } - } -} diff --git a/kraken/lib/src/painting/image_provider_factory.dart b/kraken/lib/src/painting/image_provider_factory.dart index a563cec604..63dd388b58 100644 --- a/kraken/lib/src/painting/image_provider_factory.dart +++ b/kraken/lib/src/painting/image_provider_factory.dart @@ -9,7 +9,6 @@ import 'dart:typed_data'; import 'package:flutter/cupertino.dart'; import 'package:flutter/painting.dart'; import 'package:kraken/bridge.dart'; -import 'package:kraken/css.dart'; import 'package:kraken/foundation.dart'; import 'package:kraken/painting.dart'; @@ -105,9 +104,25 @@ ImageProviderFactory _dataUrlProviderFactory = defaultDataUrlProviderFactory; ImageProviderFactory _blobProviderFactory = defaultBlobProviderFactory; ImageProviderFactory _assetsProviderFactory = defaultAssetsProvider; +ImageType parseImageUrl(Uri resolvedUri, { cache = 'auto' }) { + if (resolvedUri.isScheme('HTTP') || resolvedUri.isScheme('HTTPS')) { + return (cache == 'store' || cache == 'auto') + ? ImageType.cached + : ImageType.network; + } else if (resolvedUri.isScheme('FILE')) { + return ImageType.file; + } else if (resolvedUri.isScheme('DATA')) { + return ImageType.dataUrl; + } else if (resolvedUri.isScheme('BLOB')) { + return ImageType.blob; + } else { + return ImageType.assets; + } +} + ImageProvider? getImageProvider(Uri resolvedUri, {int? contextId, cache = 'auto', int? cachedWidth, int? cachedHeight}) { - ImageType imageType = CSSUrl.parseImageUrl(resolvedUri, cache: cache); + ImageType imageType = parseImageUrl(resolvedUri, cache: cache); ImageProviderFactory factory = _getImageProviderFactory(imageType); switch (imageType) { From 17385a8d8eb91532c4a413987a3780bc4e5dd640 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Fri, 26 Nov 2021 23:07:01 +0800 Subject: [PATCH 075/167] fix: style fail after resize --- kraken/lib/widget.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index ff55f60dea..d75007fbad 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -941,6 +941,9 @@ This situation often happened when you trying creating kraken when FlutterView n if (viewportWidthHasChanged || viewportHeightHasChanged) { traverseElement(controller.view.document!.documentElement, (element) { if (element.isRendererAttached) { + element.inlineStyle.forEach((key, value) { + element.setProperty(key, value); + }); element.style.flushPendingProperties(); element.renderBoxModel?.markNeedsLayout(); } From 8d8087d0d30ad3db47d06540f96a234285d50626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Mon, 29 Nov 2021 11:36:37 +0800 Subject: [PATCH 076/167] fix: sliver child when not attached not to flushPendingProperties --- kraken/lib/src/css/overflow.dart | 5 ++++- kraken/lib/src/css/style_declaration.dart | 9 ++++++++- kraken/lib/src/dom/elements/head.dart | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/kraken/lib/src/css/overflow.dart b/kraken/lib/src/css/overflow.dart index c8bd91249b..a327a70e2e 100644 --- a/kraken/lib/src/css/overflow.dart +++ b/kraken/lib/src/css/overflow.dart @@ -217,7 +217,10 @@ mixin ElementOverflowMixin on ElementBase { } void scrollingContentBoxStyleListener(String property, String? original, String present) { - RenderLayoutBox scrollingContentBox = (renderBoxModel as RenderLayoutBox).renderScrollingContent!; + RenderLayoutBox? scrollingContentBox = (renderBoxModel as RenderLayoutBox).renderScrollingContent; + // Sliver content has no multi scroll content box. + if (scrollingContentBox == null) return; + RenderStyle scrollingContentRenderStyle = scrollingContentBox.renderStyle; switch (property) { diff --git a/kraken/lib/src/css/style_declaration.dart b/kraken/lib/src/css/style_declaration.dart index 09864ecbc7..6a153535e4 100644 --- a/kraken/lib/src/css/style_declaration.dart +++ b/kraken/lib/src/css/style_declaration.dart @@ -367,7 +367,14 @@ class CSSStyleDeclaration { } void flushPendingProperties() { - if (target?.parentNode?.renderer == null) return; + Element? _target = target; + // If style target element not exists, no need to do flush operation. + if (_target == null) return; + // If target's renderer has created, but not adopted by a parent, + // in most cases it's a sliver child orphan, should not flush styles. + if (_target.renderer?.parent == null) return; + // If target's parent element has no renderer attached, no need to flush. + if (_target.parentNode?.isRendererAttached == false) return; // Display change from none to other value that the renderBoxModel is null. if (_pendingProperties.containsKey(DISPLAY) && target!.isConnected) { diff --git a/kraken/lib/src/dom/elements/head.dart b/kraken/lib/src/dom/elements/head.dart index f4cd8a8ece..1fb059d517 100644 --- a/kraken/lib/src/dom/elements/head.dart +++ b/kraken/lib/src/dom/elements/head.dart @@ -131,6 +131,7 @@ class StyleElement extends Element { type = value.toString().toLowerCase().trim(); } } + @override void connectedCallback() { if (type == _CSS_MIME) { From 5f298102837c090cd402e0a8fba7ba115d998a00 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Mon, 29 Nov 2021 12:22:03 +0800 Subject: [PATCH 077/167] fix: fix image resize and intersection observer. --- kraken/lib/src/dom/elements/img.dart | 48 ++++++++++++++++++---------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index f4d1b5a920..9c13641c30 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -32,6 +32,7 @@ class ImageElement extends Element { ImageStream? _imageStream; ImageInfo? _imageInfo; + Uri? _resolvedUri; double? _propertyWidth; double? _propertyHeight; @@ -76,8 +77,10 @@ class ImageElement extends Element { renderBoxModel!.addIntersectionChangeListener(_handleIntersectionChange); } else { _constructImageChild(); + // Try to attach image if image had cached. _attachImage(); - _resolveImage(); + _resizeImage(); + _resolveImage(_resolvedUri); _listenToStream(); } } @@ -104,7 +107,7 @@ class ImageElement extends Element { if (_isListeningToStream) return; - _imageStream!.addListener(_getListener()); + _imageStream?.addListener(_getListener()); _completerHandle?.dispose(); _completerHandle = null; @@ -155,7 +158,11 @@ class ImageElement extends Element { // Once appear remove the listener _resetLazyLoading(); _constructImageChild(); - _resolveImage(); + // Try to attach image if image had cached. + _attachImage(); + _resizeImage(); + _resolveImage(_resolvedUri); + _listenToStream(); } } @@ -194,10 +201,8 @@ class ImageElement extends Element { dispatchEvent(Event(EVENT_ERROR)); } - void _resize() { - if (!isRendererAttached) { - return; - } + void _resizeImage() { + assert(isRendererAttached); double? width = renderStyle.width.isAuto ? _propertyWidth : renderStyle.width.computedValue; double? height = renderStyle.height.isAuto ? _propertyHeight : renderStyle.height.computedValue; @@ -228,6 +233,7 @@ class ImageElement extends Element { width = 0.0; } + // Try to update image size if image already resolved. _renderImage?.width = width; _renderImage?.height = height; renderBoxModel!.intrinsicWidth = naturalWidth; @@ -306,8 +312,7 @@ class ImageElement extends Element { } } - void _resolveImage() { - final Uri? resolvedUri = _resolveSrc(); + void _resolveImage(Uri? resolvedUri) { if (resolvedUri == null) return; double? width = null; @@ -321,8 +326,8 @@ class ImageElement extends Element { height = _propertyHeight; } - int? cachedWidth = width != null ? (width * ui.window.devicePixelRatio).toInt() : null; - int? cachedHeight = height != null ? (height * ui.window.devicePixelRatio).toInt() : null; + int? cachedWidth = (width != null && width > 0) ? (width * ui.window.devicePixelRatio).toInt() : null; + int? cachedHeight = (height != null && height > 0) ? (height * ui.window.devicePixelRatio).toInt() : null; ImageProvider? provider = _imageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); if (provider == null) return; @@ -361,7 +366,7 @@ class ImageElement extends Element { } _attachImage(); - _resize(); + _resizeImage(); } // Prefetches an image into the image cache. @@ -393,16 +398,22 @@ class ImageElement extends Element { bool propertyChanged = properties[key] != value; super.setProperty(key, value); // Reset frame number to zero when image needs to reload - if (key == 'src' && propertyChanged && !_shouldLazyLoading) { - _precacheImage(); + if (key == 'src' && propertyChanged) { + // Update image source if image already attached. + if (isRendererAttached) { + final Uri? resolvedUri = _resolveSrc(); + _resolveImage(resolvedUri); + } else { + _precacheImage(); + } } else if (key == 'loading' && propertyChanged && _isInLazyLoading) { _resetLazyLoading(); } else if (key == WIDTH) { _propertyWidth = CSSNumber.parseNumber(value); - _resolveImage(); + _resolveImage(_resolvedUri); } else if (key == HEIGHT) { _propertyWidth = CSSNumber.parseNumber(value); - _resolveImage(); + _resolveImage(_resolvedUri); } } @@ -425,7 +436,10 @@ class ImageElement extends Element { void _stylePropertyChanged(String property, String? original, String present) { if (property == WIDTH || property == HEIGHT) { - _resolveImage(); + // Resize renderBox + if (isRendererAttached) _resizeImage(); + // Resize image + _resolveImage(_resolvedUri); } else if (property == OBJECT_FIT && _renderImage != null) { _renderImage!.fit = renderBoxModel!.renderStyle.objectFit; } else if (property == OBJECT_POSITION && _renderImage != null) { From bd0e3a836e75ad96fa39322728f7e721da5567c5 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Mon, 29 Nov 2021 14:34:59 +0800 Subject: [PATCH 078/167] fix: fix image resolvedUri. --- kraken/lib/src/dom/elements/img.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 9c13641c30..66f84ac915 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -284,7 +284,7 @@ class ImageElement extends Element { _completerHandle = _imageStream!.completer!.keepAlive(); } - _imageStream!.removeListener(_getListener()); + _imageStream?.removeListener(_getListener()); _isListeningToStream = false; } @@ -301,7 +301,7 @@ class ImageElement extends Element { if (_imageStream?.key == newStream.key) return; if (_isListeningToStream) { - _imageStream!.removeListener(_getListener()); + _imageStream?.removeListener(_getListener()); } _frameCount = 0; @@ -372,7 +372,7 @@ class ImageElement extends Element { // Prefetches an image into the image cache. void _precacheImage() { final ImageConfiguration config = ImageConfiguration.empty; - final Uri? resolvedUri = _resolveSrc(); + final Uri? resolvedUri = _resolvedUri = _resolveSrc(); if (resolvedUri == null) return; final ImageProvider? provider = _imageProvider = getImageProvider(resolvedUri); if (provider == null) return; @@ -401,7 +401,7 @@ class ImageElement extends Element { if (key == 'src' && propertyChanged) { // Update image source if image already attached. if (isRendererAttached) { - final Uri? resolvedUri = _resolveSrc(); + final Uri? resolvedUri = _resolvedUri = _resolveSrc(); _resolveImage(resolvedUri); } else { _precacheImage(); From 643acede3a029eff5f8615ea04bb5671c4b12796 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Mon, 29 Nov 2021 14:42:52 +0800 Subject: [PATCH 079/167] refactor: refactor image. --- kraken/lib/src/dom/elements/img.dart | 34 ++++++++++++---------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 66f84ac915..9187578fd8 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -76,16 +76,20 @@ class ImageElement extends Element { // When detach renderer, all listeners will be cleared. renderBoxModel!.addIntersectionChangeListener(_handleIntersectionChange); } else { - _constructImageChild(); - // Try to attach image if image had cached. - _attachImage(); - _resizeImage(); - _resolveImage(_resolvedUri); - _listenToStream(); + _loadImage(); } } } + void _loadImage() { + _constructImage(); + // Try to attach image if image had cached. + _attachImage(); + _resizeImage(); + _resolveImage(_resolvedUri); + _listenToStream(); + } + @override void didDetachRenderer() async { super.didDetachRenderer(); @@ -157,12 +161,7 @@ class ImageElement extends Element { if (entry.isIntersecting) { // Once appear remove the listener _resetLazyLoading(); - _constructImageChild(); - // Try to attach image if image had cached. - _attachImage(); - _resizeImage(); - _resolveImage(_resolvedUri); - _listenToStream(); + _loadImage(); } } @@ -171,12 +170,9 @@ class ImageElement extends Element { renderBoxModel!.removeIntersectionChangeListener(_handleIntersectionChange); } - void _constructImageChild() { - _renderImage = createRenderImageBox(); - - if (childNodes.isEmpty) { - addChild(_renderImage!); - } + void _constructImage() { + RenderImage image = _renderImage = _createRenderImageBox(); + addChild(image); } void _dispatchLoadEvent() { @@ -246,7 +242,7 @@ class ImageElement extends Element { } } - RenderImage createRenderImageBox() { + RenderImage _createRenderImageBox() { RenderStyle renderStyle = renderBoxModel!.renderStyle; BoxFit objectFit = renderStyle.objectFit; Alignment objectPosition = renderStyle.objectPosition; From 784aa119ef91357ddc2af8511bfd6e2edd35265a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Mon, 29 Nov 2021 15:12:42 +0800 Subject: [PATCH 080/167] fix: positioned child in sliver list --- kraken/lib/src/css/positioned.dart | 13 ++++++----- kraken/lib/src/dom/element.dart | 1 - kraken/lib/src/rendering/box_model.dart | 2 +- kraken/lib/src/rendering/flex.dart | 30 ++++++++++++++----------- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/kraken/lib/src/css/positioned.dart b/kraken/lib/src/css/positioned.dart index 3932227c3c..a3b9ce0363 100644 --- a/kraken/lib/src/css/positioned.dart +++ b/kraken/lib/src/css/positioned.dart @@ -31,13 +31,16 @@ BoxSizeType? _getChildHeightSizeType(RenderBox child) { // RenderPositionHolder may be affected by overflow: scroller offset. // We need to reset these offset to keep positioned elements render at their original position. +// @NOTE: Attention that renderObjects in tree may not all subtype of RenderBoxModel, use `is` to identify. Offset? _getRenderPositionHolderScrollOffset(RenderPositionPlaceholder holder, RenderObject root) { - RenderBoxModel? parent = holder.parent as RenderBoxModel?; - while (parent != null && parent != root) { - if (parent.clipX || parent.clipY) { - return Offset(parent.scrollLeft, parent.scrollTop); + AbstractNode? current = holder.parent; + while (current != null && current != root) { + if (current is RenderBoxModel) { + if (current.clipX || current.clipY) { + return Offset(current.scrollLeft, current.scrollTop); + } } - parent = parent.parent as RenderBoxModel?; + current = current.parent; } return null; } diff --git a/kraken/lib/src/dom/element.dart b/kraken/lib/src/dom/element.dart index 4b06742a5e..72180d1be0 100644 --- a/kraken/lib/src/dom/element.dart +++ b/kraken/lib/src/dom/element.dart @@ -501,7 +501,6 @@ class Element extends Node // Detach renderBoxModel from original parent. _detachRenderBoxModel(_renderBoxModel); - _updateRenderBoxModel(); _addToContainingBlock(after: previousSibling); diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index b4f21eb906..1517d0021e 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -551,7 +551,7 @@ class RenderLayoutBox extends RenderBoxModel super.dispose(); stickyChildren.clear(); - _paintingOrder = null; + _paintingOrder = null; } } diff --git a/kraken/lib/src/rendering/flex.dart b/kraken/lib/src/rendering/flex.dart index 2b9b52721c..ffd4625b47 100644 --- a/kraken/lib/src/rendering/flex.dart +++ b/kraken/lib/src/rendering/flex.dart @@ -920,19 +920,23 @@ class RenderFlexLayout extends RenderLayoutBox { childNodeId = child.hashCode; } - if (child is RenderPositionPlaceholder && isPlaceholderPositioned(child)) { - RenderBoxModel realDisplayedBox = child.positioned!; - // Flutter only allow access size of direct children, so cannot use realDisplayedBox.size - Size realDisplayedBoxSize = - realDisplayedBox.getBoxSize(realDisplayedBox.contentSize); - double realDisplayedBoxWidth = realDisplayedBoxSize.width; - double realDisplayedBoxHeight = realDisplayedBoxSize.height; - childConstraints = BoxConstraints( - minWidth: realDisplayedBoxWidth, - maxWidth: realDisplayedBoxWidth, - minHeight: realDisplayedBoxHeight, - maxHeight: realDisplayedBoxHeight, - ); + if (isPlaceholderPositioned(child)) { + RenderBoxModel realDisplayedBox = (child as RenderPositionPlaceholder).positioned!; + if (realDisplayedBox.hasSize) { + // Flutter only allow access size of direct children, so cannot use realDisplayedBox.size + Size realDisplayedBoxSize = + realDisplayedBox.getBoxSize(realDisplayedBox.contentSize); + double realDisplayedBoxWidth = realDisplayedBoxSize.width; + double realDisplayedBoxHeight = realDisplayedBoxSize.height; + childConstraints = BoxConstraints( + minWidth: realDisplayedBoxWidth, + maxWidth: realDisplayedBoxWidth, + minHeight: realDisplayedBoxHeight, + maxHeight: realDisplayedBoxHeight, + ); + } else { + childConstraints = BoxConstraints(); + } } else if (child is RenderBoxModel) { childConstraints = child.getConstraints(); } else if (child is RenderTextBox) { From f6118e220322a95fb8ce963e26419eb4b641fa8f Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Mon, 29 Nov 2021 15:46:23 +0800 Subject: [PATCH 081/167] feat: strip android binary in release build --- scripts/tasks.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/scripts/tasks.js b/scripts/tasks.js index b8078bf90c..30feb3ef2b 100644 --- a/scripts/tasks.js +++ b/scripts/tasks.js @@ -623,6 +623,10 @@ task('build-android-kraken-lib', (done) => { externCmakeArgs.push('-DENABLE_ASAN=true'); } + const soFileNames = [ + 'libkraken', + 'libquickjs' + ]; const cmakeGeneratorTemplate = platform == 'win32' ? 'Ninja' : 'Unix Makefiles'; archs.forEach(arch => { @@ -656,6 +660,18 @@ task('build-android-kraken-lib', (done) => { stdio: 'inherit' }); + // Strip release binary in release mode. + if (buildMode === 'Release' || buildMode === 'RelWithDebInfo') { + const strip = path.join(ndkDir, `./toolchains/llvm/prebuilt/${os.platform()}-x86_64/${toolChainMap[arch]}/bin/strip`); + const objcopy = path.join(ndkDir, `./toolchains/llvm/prebuilt/${os.platform()}-x86_64/${toolChainMap[arch]}/bin/objcopy`); + + for (let soFileName of soFileNames) { + const soBinaryFile = path.join(soBinaryDirectory, soFileName + '.so'); + execSync(`${objcopy} --only-keep-debug "${soBinaryFile}" "${soBinaryDirectory}/${soFileName}.debug"`); + execSync(`${strip} --strip-debug --strip-unneeded "${soBinaryFile}"`) + } + } + // Copy libc++_shared.so to dist from NDK. const libcppSharedPath = path.join(ndkDir, `./toolchains/llvm/prebuilt/${os.platform()}-x86_64/sysroot/usr/lib/${toolChainMap[arch]}/libc++_shared.so`); execSync(`cp ${libcppSharedPath} ${soBinaryDirectory}`); From 56dfab3f68f7aff7901484d948fc868309c5ed8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Mon, 29 Nov 2021 16:28:58 +0800 Subject: [PATCH 082/167] test: add test case for positioned child in sliver. --- integration_tests/specs/css/css-display/sliver.ts | 13 +++++++++++++ kraken/lib/src/css/display.dart | 4 ++-- kraken/lib/src/css/style_declaration.dart | 3 --- kraken/lib/src/dom/element.dart | 5 +++++ 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/integration_tests/specs/css/css-display/sliver.ts b/integration_tests/specs/css/css-display/sliver.ts index da7093e96a..193dbdf77f 100644 --- a/integration_tests/specs/css/css-display/sliver.ts +++ b/integration_tests/specs/css/css-display/sliver.ts @@ -199,4 +199,17 @@ describe('display sliver', () => { await simulateClick(50, 20); // Will trigger done. }); + + it('sliver child with none-static position not throw errors', () => { + var container = createSliverBasicCase(); + var firstChild = container.firstChild; // should be element. + firstChild.style.position = 'relative'; + + var innerChild = document.createElement('div'); + innerChild.appendChild(document.createTextNode('helloworld')); + innerChild.style.position = 'relative'; + innerChild.style.top = innerChild.style.left = '15px'; + firstChild?.appendChild(innerChild); + await snapshot(); + }); }); diff --git a/kraken/lib/src/css/display.dart b/kraken/lib/src/css/display.dart index 36b38969e6..0cb592983e 100644 --- a/kraken/lib/src/css/display.dart +++ b/kraken/lib/src/css/display.dart @@ -14,7 +14,7 @@ enum CSSDisplay { flex, inlineFlex, - sliver, // @TODO temp name. + sliver, none } @@ -26,7 +26,7 @@ mixin CSSDisplayMixin on RenderStyleBase { set display(CSSDisplay value) { if (_display != value) { _display = value; - + // Display effect the stacking context. RenderBoxModel? parentRenderer = (this as RenderStyle).parent?.renderBoxModel; if (parentRenderer is RenderLayoutBox) { diff --git a/kraken/lib/src/css/style_declaration.dart b/kraken/lib/src/css/style_declaration.dart index 6a153535e4..f984dbd27e 100644 --- a/kraken/lib/src/css/style_declaration.dart +++ b/kraken/lib/src/css/style_declaration.dart @@ -370,9 +370,6 @@ class CSSStyleDeclaration { Element? _target = target; // If style target element not exists, no need to do flush operation. if (_target == null) return; - // If target's renderer has created, but not adopted by a parent, - // in most cases it's a sliver child orphan, should not flush styles. - if (_target.renderer?.parent == null) return; // If target's parent element has no renderer attached, no need to flush. if (_target.parentNode?.isRendererAttached == false) return; diff --git a/kraken/lib/src/dom/element.dart b/kraken/lib/src/dom/element.dart index 72180d1be0..9b42a851e7 100644 --- a/kraken/lib/src/dom/element.dart +++ b/kraken/lib/src/dom/element.dart @@ -477,6 +477,11 @@ class Element extends Node RenderBoxModel _renderBoxModel = renderBoxModel!; CSSPositionType currentPosition = renderStyle.position; + // If renderer itself not attached, no need to re-attach it. + if (!_renderBoxModel.attached) { + return; + } + // Remove fixed children before convert to non repaint boundary renderObject if (currentPosition != CSSPositionType.fixed) { _removeFixedChild(_renderBoxModel, elementManager.viewportElement._renderLayoutBox!); From b68114720ff4325ab55108e0f1e6007c4f2cbd7e Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Mon, 29 Nov 2021 16:56:56 +0800 Subject: [PATCH 083/167] fix: disable ios bitecode --- scripts/tasks.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/scripts/tasks.js b/scripts/tasks.js index 30feb3ef2b..6274a8a853 100644 --- a/scripts/tasks.js +++ b/scripts/tasks.js @@ -439,7 +439,7 @@ task(`build-ios-kraken-lib`, (done) => { -DDEPLOYMENT_TARGET=9.0 \ ${isProfile ? '-DENABLE_PROFILE=TRUE \\' : '\\'} ${externCmakeArgs.join(' ')} \ - -DENABLE_BITCODE=TRUE -G "Unix Makefiles" -B ${paths.bridge}/cmake-build-ios-x64 -S ${paths.bridge}`, { + -DENABLE_BITCODE=FALSE -G "Unix Makefiles" -B ${paths.bridge}/cmake-build-ios-x64 -S ${paths.bridge}`, { cwd: paths.bridge, stdio: 'inherit', env: { @@ -462,7 +462,7 @@ task(`build-ios-kraken-lib`, (done) => { -DDEPLOYMENT_TARGET=9.0 \ ${externCmakeArgs.join(' ')} \ ${isProfile ? '-DENABLE_PROFILE=TRUE \\' : '\\'} - -DENABLE_BITCODE=TRUE -G "Unix Makefiles" -B ${paths.bridge}/cmake-build-ios-arm -S ${paths.bridge}`, { + -DENABLE_BITCODE=FALSE -G "Unix Makefiles" -B ${paths.bridge}/cmake-build-ios-arm -S ${paths.bridge}`, { cwd: paths.bridge, stdio: 'inherit', env: { @@ -484,7 +484,7 @@ task(`build-ios-kraken-lib`, (done) => { -DDEPLOYMENT_TARGET=9.0 \ ${externCmakeArgs.join(' ')} \ ${isProfile ? '-DENABLE_PROFILE=TRUE \\' : '\\'} - -DENABLE_BITCODE=TRUE -G "Unix Makefiles" -B ${paths.bridge}/cmake-build-ios-arm64 -S ${paths.bridge}`, { + -DENABLE_BITCODE=FALSE -G "Unix Makefiles" -B ${paths.bridge}/cmake-build-ios-arm64 -S ${paths.bridge}`, { cwd: paths.bridge, stdio: 'inherit', env: { @@ -517,12 +517,12 @@ task(`build-ios-kraken-lib`, (done) => { const frameworkPath = `${targetDynamicSDKPath}/${target}.xcframework`; mkdirp.sync(targetDynamicSDKPath); - if (buildMode === 'RelWithDebInfo') { - // Create dSYM for x86_64 - execSync(`dsymutil ${x64DynamicSDKPath}/${target}`, { stdio: 'inherit' }); + // Create dSYM for x86_64 + execSync(`dsymutil ${x64DynamicSDKPath}/${target} --out ${x64DynamicSDKPath}/../${target}.dSYM`, { stdio: 'inherit' }); + // Create dSYM for arm64,armv7 + execSync(`dsymutil ${armDynamicSDKPath}/${target} --out ${armDynamicSDKPath}/../${target}.dSYM`, { stdio: 'inherit' }); - // Create dSYM for arm64,armv7 - execSync(`dsymutil ${armDynamicSDKPath}/${target}`, { stdio: 'inherit' }); + if (buildMode === 'RelWithDebInfo') { execSync(`xcodebuild -create-xcframework \ -framework ${x64DynamicSDKPath} -debug-symbols ${x64DynamicSDKPath}/${target}.dSYM \ -framework ${armDynamicSDKPath} -debug-symbols ${armDynamicSDKPath}/${target}.dSYM -output ${frameworkPath}`, { @@ -625,7 +625,8 @@ task('build-android-kraken-lib', (done) => { const soFileNames = [ 'libkraken', - 'libquickjs' + 'libquickjs', + 'libc++_shared' ]; const cmakeGeneratorTemplate = platform == 'win32' ? 'Ninja' : 'Unix Makefiles'; @@ -660,6 +661,10 @@ task('build-android-kraken-lib', (done) => { stdio: 'inherit' }); + // Copy libc++_shared.so to dist from NDK. + const libcppSharedPath = path.join(ndkDir, `./toolchains/llvm/prebuilt/${os.platform()}-x86_64/sysroot/usr/lib/${toolChainMap[arch]}/libc++_shared.so`); + execSync(`cp ${libcppSharedPath} ${soBinaryDirectory}`); + // Strip release binary in release mode. if (buildMode === 'Release' || buildMode === 'RelWithDebInfo') { const strip = path.join(ndkDir, `./toolchains/llvm/prebuilt/${os.platform()}-x86_64/${toolChainMap[arch]}/bin/strip`); @@ -671,10 +676,6 @@ task('build-android-kraken-lib', (done) => { execSync(`${strip} --strip-debug --strip-unneeded "${soBinaryFile}"`) } } - - // Copy libc++_shared.so to dist from NDK. - const libcppSharedPath = path.join(ndkDir, `./toolchains/llvm/prebuilt/${os.platform()}-x86_64/sysroot/usr/lib/${toolChainMap[arch]}/libc++_shared.so`); - execSync(`cp ${libcppSharedPath} ${soBinaryDirectory}`); }); done(); From 43214cde105a214391b4dc01921a063b2e795705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Mon, 29 Nov 2021 17:38:37 +0800 Subject: [PATCH 084/167] fix: test case await need async --- integration_tests/specs/css/css-display/sliver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/specs/css/css-display/sliver.ts b/integration_tests/specs/css/css-display/sliver.ts index 193dbdf77f..22985be835 100644 --- a/integration_tests/specs/css/css-display/sliver.ts +++ b/integration_tests/specs/css/css-display/sliver.ts @@ -200,7 +200,7 @@ describe('display sliver', () => { await simulateClick(50, 20); // Will trigger done. }); - it('sliver child with none-static position not throw errors', () => { + it('sliver child with none-static position not throw errors', async () => { var container = createSliverBasicCase(); var firstChild = container.firstChild; // should be element. firstChild.style.position = 'relative'; From 3dd5128b8aaa6cbb6141526c55fe66d5bdd91869 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Mon, 29 Nov 2021 21:21:53 +0800 Subject: [PATCH 085/167] fix: fix set property height. --- kraken/lib/src/dom/elements/img.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 9187578fd8..18546ce93e 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -408,7 +408,7 @@ class ImageElement extends Element { _propertyWidth = CSSNumber.parseNumber(value); _resolveImage(_resolvedUri); } else if (key == HEIGHT) { - _propertyWidth = CSSNumber.parseNumber(value); + _propertyHeight = CSSNumber.parseNumber(value); _resolveImage(_resolvedUri); } From 74416f4bb260abeab894703a914a2668bbd2ea04 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 13:35:28 +0800 Subject: [PATCH 086/167] fix: fix precache image. --- kraken/lib/src/dom/elements/img.dart | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 18546ce93e..407198fa76 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -376,7 +376,19 @@ class ImageElement extends Element { final ImageStream stream = provider.resolve(config); ImageStreamListener? listener; listener = ImageStreamListener( - (ImageInfo? image, bool sync) { + (ImageInfo? imageInfo, bool sync) { + _replaceImage(info: imageInfo); + _frameCount++; + + if (!_imageLoaded) { + _imageLoaded = true; + if (sync) { + // `synchronousCall` happens when caches image and calling `addListener`. + scheduleMicrotask(_handleEventAfterImageLoaded); + } else { + _handleEventAfterImageLoaded(); + } + } // Give callers until at least the end of the frame to subscribe to the // image stream. // See ImageCache._liveImages From 75e11cd76c72a520b2d0e08608e82e9a4c81740f Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 14:08:38 +0800 Subject: [PATCH 087/167] fix: handle json parse exception. --- bridge/bindings/qjs/module_manager.cc | 17 +++---- bridge/bindings/qjs/module_manager_test.cc | 48 +++++++++++++++++++ bridge/bindings/qjs/native_value.cc | 2 + .../code_generator/src/genereate_source.ts | 1 + bridge/test/test.cmake | 3 +- 5 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 bridge/bindings/qjs/module_manager_test.cc diff --git a/bridge/bindings/qjs/module_manager.cc b/bridge/bindings/qjs/module_manager.cc index 3b4d217392..74bfc9f88b 100644 --- a/bridge/bindings/qjs/module_manager.cc +++ b/bridge/bindings/qjs/module_manager.cc @@ -100,23 +100,24 @@ JSValue krakenInvokeModule(QjsContext* ctx, JSValueConst this_val, int argc, JSV callbackValue = argv[3]; } - if (getDartMethod()->invokeModule == nullptr) { -#if FLUTTER_BACKEND - return JS_ThrowTypeError(ctx, "Failed to execute '__kraken_invoke_module__': dart method (invokeModule) is not registered."); -#else - return JS_NULL; -#endif - } - NativeString* moduleName = jsValueToNativeString(ctx, moduleNameValue); NativeString* method = jsValueToNativeString(ctx, methodValue); NativeString* params = nullptr; if (!JS_IsNull(paramsValue)) { JSValue stringifyedValue = JS_JSONStringify(ctx, paramsValue, JS_NULL, JS_NULL); + if (JS_IsException(stringifyedValue)) return stringifyedValue; params = jsValueToNativeString(ctx, stringifyedValue); JS_FreeValue(ctx, stringifyedValue); } + if (getDartMethod()->invokeModule == nullptr) { +#if FLUTTER_BACKEND + return JS_ThrowTypeError(ctx, "Failed to execute '__kraken_invoke_module__': dart method (invokeModule) is not registered."); +#else + return JS_NULL; +#endif + } + ModuleContext* moduleContext; if (JS_IsNull(callbackValue)) { auto emptyFunction = [](QjsContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) -> JSValue { return JS_NULL; }; diff --git a/bridge/bindings/qjs/module_manager_test.cc b/bridge/bindings/qjs/module_manager_test.cc new file mode 100644 index 0000000000..aa93be401d --- /dev/null +++ b/bridge/bindings/qjs/module_manager_test.cc @@ -0,0 +1,48 @@ +/* +* Copyright (C) 2021 Alibaba Inc. All rights reserved. +* Author: Kraken Team. +*/ + +#include "host_object.h" +#include +#include "bridge_qjs.h" +#include "js_context.h" + +namespace kraken::binding::qjs { + +TEST(ModuleManager, shouldThrowErrorWhenBadJSON) { + bool static errorCalled = false; + auto* bridge = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) { + EXPECT_STREQ(errmsg, "TypeError: circular reference\n" + " at __kraken_invoke_module__ (native)\n" + " at (internal://)\n" + " at Promise (native)\n" + " at invokeMethod (internal://)\n" + " at (vm://:12)\n"); + errorCalled = true; + }); + kraken::JSBridge::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) { + }; + + auto& context = bridge->getContext(); + + std::string code = std::string(R"( +let object = { + key: { + v: { + a: { + other: null + } + } + } +}; +object.other = object; +kraken.methodChannel.invokeMethod('abc', 'fn', object); +)"); + context->evaluateJavaScript(code.c_str(), code.size(), "vm://", 0); + delete bridge; + EXPECT_EQ(errorCalled, true); +} + + +} // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/native_value.cc b/bridge/bindings/qjs/native_value.cc index 164caf3154..a2eccc7fe6 100644 --- a/bridge/bindings/qjs/native_value.cc +++ b/bridge/bindings/qjs/native_value.cc @@ -62,6 +62,8 @@ NativeValue Native_NewInt32(int32_t value) { NativeValue Native_NewJSON(JSContext* context, JSValue& value) { JSValue stringifiedValue = JS_JSONStringify(context->ctx(), value, JS_UNDEFINED, JS_UNDEFINED); + if (JS_IsException(stringifiedValue)) return Native_NewNull(); + NativeString* string = jsValueToNativeString(context->ctx(), stringifiedValue); NativeValue result = (NativeValue){ 0, diff --git a/bridge/scripts/code_generator/src/genereate_source.ts b/bridge/scripts/code_generator/src/genereate_source.ts index 2384dd3789..19e1194fbb 100644 --- a/bridge/scripts/code_generator/src/genereate_source.ts +++ b/bridge/scripts/code_generator/src/genereate_source.ts @@ -340,6 +340,7 @@ function generateEventInstanceConstructorCode(object: ClassObject) { } else if (p.kind === PropsDeclarationKind.object) { propApplyCode = addIndent(`JSValue v = JS_GetProperty(m_ctx, eventInit, ${p.name}Atom); JSValue json = JS_JSONStringify(m_ctx, v, JS_NULL, JS_NULL); + if (JS_IsException(json)) return json; nativeEvent->${p.name} = jsValueToNativeString(m_ctx, json); JS_FreeValue(m_ctx, json); JS_FreeValue(m_ctx, v);`, 0); diff --git a/bridge/test/test.cmake b/bridge/test/test.cmake index 46b6167494..393b27dc91 100644 --- a/bridge/test/test.cmake +++ b/bridge/test/test.cmake @@ -31,10 +31,11 @@ elseif($ENV{KRAKEN_JS_ENGINE} MATCHES "quickjs") ./bindings/qjs/dom/text_node_test.cc ./bindings/qjs/bom/window_test.cc ./bindings/qjs/dom/custom_event_test.cc + ./bindings/qjs/module_manager_test.cc ) ### kraken_unit_test executable - add_executable(kraken_unit_test ${KRAKEN_UNIT_TEST_SOURCE} ${KRAKEN_TEST_SOURCE} ${BRIDGE_SOURCE} ../bindings/qjs/html_parser.cc ../bindings/qjs/html_parser.h) + add_executable(kraken_unit_test ${KRAKEN_UNIT_TEST_SOURCE} ${KRAKEN_TEST_SOURCE} ${BRIDGE_SOURCE} ../bindings/qjs/html_parser.cc ../bindings/qjs/html_parser.h ../bindings/qjs/module_manager_test.cc) target_include_directories(kraken_unit_test PUBLIC ./third_party/googletest/googletest/include ${BRIDGE_INCLUDE}) target_link_libraries(kraken_unit_test gtest gtest_main ${BRIDGE_LINK_LIBS}) From d070ede0ea3f77118a6280a07afdfb49992fa2ab Mon Sep 17 00:00:00 2001 From: openkraken-bot Date: Tue, 30 Nov 2021 06:09:33 +0000 Subject: [PATCH 088/167] Committing clang-format changes --- bridge/bindings/qjs/module_manager.cc | 3 ++- bridge/bindings/qjs/module_manager_test.cc | 15 +++++++-------- bridge/bindings/qjs/native_value.cc | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/bridge/bindings/qjs/module_manager.cc b/bridge/bindings/qjs/module_manager.cc index 74bfc9f88b..0241fcc62a 100644 --- a/bridge/bindings/qjs/module_manager.cc +++ b/bridge/bindings/qjs/module_manager.cc @@ -105,7 +105,8 @@ JSValue krakenInvokeModule(QjsContext* ctx, JSValueConst this_val, int argc, JSV NativeString* params = nullptr; if (!JS_IsNull(paramsValue)) { JSValue stringifyedValue = JS_JSONStringify(ctx, paramsValue, JS_NULL, JS_NULL); - if (JS_IsException(stringifyedValue)) return stringifyedValue; + if (JS_IsException(stringifyedValue)) + return stringifyedValue; params = jsValueToNativeString(ctx, stringifyedValue); JS_FreeValue(ctx, stringifyedValue); } diff --git a/bridge/bindings/qjs/module_manager_test.cc b/bridge/bindings/qjs/module_manager_test.cc index aa93be401d..fde716594d 100644 --- a/bridge/bindings/qjs/module_manager_test.cc +++ b/bridge/bindings/qjs/module_manager_test.cc @@ -1,11 +1,11 @@ /* -* Copyright (C) 2021 Alibaba Inc. All rights reserved. -* Author: Kraken Team. -*/ + * Copyright (C) 2021 Alibaba Inc. All rights reserved. + * Author: Kraken Team. + */ -#include "host_object.h" #include #include "bridge_qjs.h" +#include "host_object.h" #include "js_context.h" namespace kraken::binding::qjs { @@ -13,7 +13,8 @@ namespace kraken::binding::qjs { TEST(ModuleManager, shouldThrowErrorWhenBadJSON) { bool static errorCalled = false; auto* bridge = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) { - EXPECT_STREQ(errmsg, "TypeError: circular reference\n" + EXPECT_STREQ(errmsg, + "TypeError: circular reference\n" " at __kraken_invoke_module__ (native)\n" " at (internal://)\n" " at Promise (native)\n" @@ -21,8 +22,7 @@ TEST(ModuleManager, shouldThrowErrorWhenBadJSON) { " at (vm://:12)\n"); errorCalled = true; }); - kraken::JSBridge::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) { - }; + kraken::JSBridge::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) {}; auto& context = bridge->getContext(); @@ -44,5 +44,4 @@ kraken.methodChannel.invokeMethod('abc', 'fn', object); EXPECT_EQ(errorCalled, true); } - } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/native_value.cc b/bridge/bindings/qjs/native_value.cc index a2eccc7fe6..cd168cdf60 100644 --- a/bridge/bindings/qjs/native_value.cc +++ b/bridge/bindings/qjs/native_value.cc @@ -62,7 +62,8 @@ NativeValue Native_NewInt32(int32_t value) { NativeValue Native_NewJSON(JSContext* context, JSValue& value) { JSValue stringifiedValue = JS_JSONStringify(context->ctx(), value, JS_UNDEFINED, JS_UNDEFINED); - if (JS_IsException(stringifiedValue)) return Native_NewNull(); + if (JS_IsException(stringifiedValue)) + return Native_NewNull(); NativeString* string = jsValueToNativeString(context->ctx(), stringifiedValue); NativeValue result = (NativeValue){ From 4fe4a1f04f8605004e9aa6bb7530b3487835edf4 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Tue, 30 Nov 2021 14:32:47 +0800 Subject: [PATCH 089/167] fix: insert before position fixed element --- kraken/lib/src/dom/element.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kraken/lib/src/dom/element.dart b/kraken/lib/src/dom/element.dart index 85484add46..1cbf6392ea 100644 --- a/kraken/lib/src/dom/element.dart +++ b/kraken/lib/src/dom/element.dart @@ -693,7 +693,17 @@ class Element extends Node RenderBox? afterRenderObject; // `referenceNode` should not be null, or `referenceIndex` can only be -1. if (referenceIndex != -1 && referenceNode.isRendererAttached) { - afterRenderObject = (referenceNode.renderer!.parentData as ContainerParentDataMixin).previousSibling; + RenderBox renderer = referenceNode.renderer!; + // Renderer of referenceNode may not moved to a difference place compared to its original place + // in the dom tree due to position absolute/fixed. + // Use the renderPositionPlaceholder to get the same place as dom tree in this case. + if (renderer is RenderBoxModel) { + RenderBox? renderPositionPlaceholder = renderer.renderPositionPlaceholder; + if (renderPositionPlaceholder != null) { + renderer = renderPositionPlaceholder; + } + } + afterRenderObject = (renderer.parentData as ContainerParentDataMixin).previousSibling; } child.attachTo(this, after: afterRenderObject); } From 8e5e7c11511dfc1db4535c710a88a9cad6bd8eac Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Tue, 30 Nov 2021 14:44:48 +0800 Subject: [PATCH 090/167] test: add test for fixed element insert bug --- .../css/css-position/fixed.ts.616d7c7b1.png | Bin 0 -> 2384 bytes .../specs/css/css-position/fixed.ts | 35 ++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 integration_tests/snapshots/css/css-position/fixed.ts.616d7c7b1.png diff --git a/integration_tests/snapshots/css/css-position/fixed.ts.616d7c7b1.png b/integration_tests/snapshots/css/css-position/fixed.ts.616d7c7b1.png new file mode 100644 index 0000000000000000000000000000000000000000..035183ef79e3d3dfc4c6c86d121e7519ac1d24a8 GIT binary patch literal 2384 zcmeAS@N?(olHy`uVBq!ia0y~yV9a1(U~1rC1Bz^vdbAu!F%}28J29*~C-V}>VJUX< z4B-HR8jh3>1_q8_o-U3d6?5KRbL2W~Ai#34^u+$j^G{!Askv_#vNs_iaB=p&; { cont.style.position = 'static'; await snapshot(); }); + + it('insert fixed element before fixed element', async () => { + let child1 = createElement('div', { + style: { + position: 'fixed', + top: '100px', + width: '100px', + height: '100px', + background: 'red' + } + }); + let child2; + const container = createElement('div', { + style: { + width: '200px', + height: '200px', + background: 'yellow' + } + }, [ + (child2 = createElement('div', { + style: { + position: 'fixed', + width: '100px', + height: '100px', + background: 'green' + } + })) + ]); + + document.body.appendChild(container); + + container.insertBefore(child1, child2); + + await snapshot(); + }); }); From 1964cc803c0df19f3dc9becbc72d151e0702048b Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 14:48:41 +0800 Subject: [PATCH 091/167] fix: free event targets properties by gc mark. --- bridge/bindings/qjs/dom/event_target.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/bridge/bindings/qjs/dom/event_target.cc b/bridge/bindings/qjs/dom/event_target.cc index 3728e29857..26a8eefa64 100644 --- a/bridge/bindings/qjs/dom/event_target.cc +++ b/bridge/bindings/qjs/dom/event_target.cc @@ -285,9 +285,6 @@ EventTargetInstance::~EventTargetInstance() { #if FLUTTER_BACKEND getDartMethod()->flushUICommand(); #endif - JS_FreeValue(m_ctx, m_properties); - JS_FreeValue(m_ctx, m_eventHandlers); - JS_FreeValue(m_ctx, m_propertyEventHandler); } int EventTargetInstance::hasProperty(QjsContext* ctx, JSValue obj, JSAtom atom) { From 314be5a08492baa3fd297e2e610ad0e00e1e8e00 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Tue, 30 Nov 2021 15:02:21 +0800 Subject: [PATCH 092/167] test: modify test name --- ...ixed.ts.616d7c7b1.png => fixed.ts.086b15501.png} | Bin integration_tests/specs/css/css-position/fixed.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename integration_tests/snapshots/css/css-position/{fixed.ts.616d7c7b1.png => fixed.ts.086b15501.png} (100%) diff --git a/integration_tests/snapshots/css/css-position/fixed.ts.616d7c7b1.png b/integration_tests/snapshots/css/css-position/fixed.ts.086b15501.png similarity index 100% rename from integration_tests/snapshots/css/css-position/fixed.ts.616d7c7b1.png rename to integration_tests/snapshots/css/css-position/fixed.ts.086b15501.png diff --git a/integration_tests/specs/css/css-position/fixed.ts b/integration_tests/specs/css/css-position/fixed.ts index 64ea179f62..dc16a564b0 100644 --- a/integration_tests/specs/css/css-position/fixed.ts +++ b/integration_tests/specs/css/css-position/fixed.ts @@ -288,7 +288,7 @@ describe('Position fixed', () => { await snapshot(); }); - it('insert fixed element before fixed element', async () => { + it('insert before position fixed element', async () => { let child1 = createElement('div', { style: { position: 'fixed', From e2d2833f1d057274a1b04b3f8659c8d1603e319a Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Tue, 30 Nov 2021 15:12:42 +0800 Subject: [PATCH 093/167] test: move test to insertBefore --- .../nodes/insert-before.ts.1a0a72251.png} | Bin .../dom/nodes/insert-before.ts.370933ef1.png | Bin 0 -> 2378 bytes .../specs/css/css-position/fixed.ts | 35 --------- .../specs/dom/nodes/insert-before.ts | 67 ++++++++++++++++++ 4 files changed, 67 insertions(+), 35 deletions(-) rename integration_tests/snapshots/{css/css-position/fixed.ts.086b15501.png => dom/nodes/insert-before.ts.1a0a72251.png} (100%) create mode 100644 integration_tests/snapshots/dom/nodes/insert-before.ts.370933ef1.png diff --git a/integration_tests/snapshots/css/css-position/fixed.ts.086b15501.png b/integration_tests/snapshots/dom/nodes/insert-before.ts.1a0a72251.png similarity index 100% rename from integration_tests/snapshots/css/css-position/fixed.ts.086b15501.png rename to integration_tests/snapshots/dom/nodes/insert-before.ts.1a0a72251.png diff --git a/integration_tests/snapshots/dom/nodes/insert-before.ts.370933ef1.png b/integration_tests/snapshots/dom/nodes/insert-before.ts.370933ef1.png new file mode 100644 index 0000000000000000000000000000000000000000..73319fcceb3e722135a60263bf7e7b817f9356f5 GIT binary patch literal 2378 zcmeAS@N?(olHy`uVBq!ia0y~yV9a1(U~1rC1Bz^vdbAu!F%}28J29*~C-V}>VJUX< z4B-HR8jh3>1_q8Vo-U3d6?5KR+sNtc$isTj`thN=JLlAGD9$wqKFEAL^9kAo#j({##C)v)>pL4zrJjE{zgcX?of=cZLtAM?-it q3y+p!qa`?1OYl*{hD@X2D=SwNUnTFXHJ^a(AqG!ZKbLh*2~7YdIPbXt literal 0 HcmV?d00001 diff --git a/integration_tests/specs/css/css-position/fixed.ts b/integration_tests/specs/css/css-position/fixed.ts index dc16a564b0..4448c75e46 100644 --- a/integration_tests/specs/css/css-position/fixed.ts +++ b/integration_tests/specs/css/css-position/fixed.ts @@ -287,39 +287,4 @@ describe('Position fixed', () => { cont.style.position = 'static'; await snapshot(); }); - - it('insert before position fixed element', async () => { - let child1 = createElement('div', { - style: { - position: 'fixed', - top: '100px', - width: '100px', - height: '100px', - background: 'red' - } - }); - let child2; - const container = createElement('div', { - style: { - width: '200px', - height: '200px', - background: 'yellow' - } - }, [ - (child2 = createElement('div', { - style: { - position: 'fixed', - width: '100px', - height: '100px', - background: 'green' - } - })) - ]); - - document.body.appendChild(container); - - container.insertBefore(child1, child2); - - await snapshot(); - }); }); diff --git a/integration_tests/specs/dom/nodes/insert-before.ts b/integration_tests/specs/dom/nodes/insert-before.ts index 2e0587eb2a..772a219be6 100644 --- a/integration_tests/specs/dom/nodes/insert-before.ts +++ b/integration_tests/specs/dom/nodes/insert-before.ts @@ -129,4 +129,71 @@ describe('Insert before', () => { await snapshot(); }); + it('insert before position fixed element', async () => { + let child1 = createElement('div', { + style: { + position: 'fixed', + top: '100px', + width: '100px', + height: '100px', + background: 'red' + } + }); + let child2; + const container = createElement('div', { + style: { + width: '200px', + height: '200px', + background: 'yellow' + } + }, [ + (child2 = createElement('div', { + style: { + position: 'fixed', + width: '100px', + height: '100px', + background: 'green' + } + })) + ]); + + document.body.appendChild(container); + + container.insertBefore(child1, child2); + + await snapshot(); + }); + + it('insert before position absolute element', async () => { + let child1 = createElement('div', { + style: { + width: '100px', + height: '100px', + background: 'red' + } + }); + let child2; + const container = createElement('div', { + style: { + width: '200px', + height: '200px', + background: 'yellow' + } + }, [ + (child2 = createElement('div', { + style: { + position: 'absolute', + width: '100px', + height: '100px', + background: 'green' + } + })) + ]); + + document.body.appendChild(container); + + container.insertBefore(child1, child2); + + await snapshot(); + }); }); From ec547528faaf0c6ea2b887abc3ce5515ed9c8346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Tue, 30 Nov 2021 15:31:21 +0800 Subject: [PATCH 094/167] fix: test cases --- integration_tests/specs/window/global.ts | 3 ++- kraken/lib/src/css/style_declaration.dart | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/integration_tests/specs/window/global.ts b/integration_tests/specs/window/global.ts index 6329c8ab92..85fb17804b 100644 --- a/integration_tests/specs/window/global.ts +++ b/integration_tests/specs/window/global.ts @@ -1,4 +1,4 @@ -describe('windowisglobal', () => { +describe('window', () => { it('window equal to globalThis', () => { expect(window).toBe(globalThis as any); }); @@ -7,6 +7,7 @@ describe('windowisglobal', () => { // @ts-ignore expect(typeof window.kraken).toBe('object'); }); + it('equal to this', () => { function f() { // @ts-ignore diff --git a/kraken/lib/src/css/style_declaration.dart b/kraken/lib/src/css/style_declaration.dart index f984dbd27e..05d910fc52 100644 --- a/kraken/lib/src/css/style_declaration.dart +++ b/kraken/lib/src/css/style_declaration.dart @@ -371,10 +371,10 @@ class CSSStyleDeclaration { // If style target element not exists, no need to do flush operation. if (_target == null) return; // If target's parent element has no renderer attached, no need to flush. - if (_target.parentNode?.isRendererAttached == false) return; + if (_target.parentNode?.renderer == null) return; // Display change from none to other value that the renderBoxModel is null. - if (_pendingProperties.containsKey(DISPLAY) && target!.isConnected) { + if (_pendingProperties.containsKey(DISPLAY) && _target.isConnected) { String? prevValue = _properties[DISPLAY]; String currentValue = _pendingProperties[DISPLAY]!; _properties[DISPLAY] = currentValue; @@ -382,7 +382,7 @@ class CSSStyleDeclaration { _emitPropertyChanged(DISPLAY, prevValue, currentValue); } - RenderBoxModel? renderBoxModel = target!.renderBoxModel; + RenderBoxModel? renderBoxModel = _target.renderBoxModel; if (_pendingProperties.isEmpty || renderBoxModel == null) { return; } From df165c097566a4461eccc372a0606712503fdaa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Tue, 30 Nov 2021 16:47:31 +0800 Subject: [PATCH 095/167] fix: Root element creation should flush after renderobject attached. --- kraken/lib/src/css/style_declaration.dart | 4 +-- kraken/lib/src/dom/element.dart | 6 +++- kraken/lib/src/rendering/flex.dart | 41 +++++++++++------------ 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/kraken/lib/src/css/style_declaration.dart b/kraken/lib/src/css/style_declaration.dart index 05d910fc52..85297bcbe6 100644 --- a/kraken/lib/src/css/style_declaration.dart +++ b/kraken/lib/src/css/style_declaration.dart @@ -370,8 +370,8 @@ class CSSStyleDeclaration { Element? _target = target; // If style target element not exists, no need to do flush operation. if (_target == null) return; - // If target's parent element has no renderer attached, no need to flush. - if (_target.parentNode?.renderer == null) return; + // If target has no renderer attached, skip flush. + if (!_target.isRendererAttached) return; // Display change from none to other value that the renderBoxModel is null. if (_pendingProperties.containsKey(DISPLAY) && _target.isConnected) { diff --git a/kraken/lib/src/dom/element.dart b/kraken/lib/src/dom/element.dart index ffaa6fcf23..6bff618716 100644 --- a/kraken/lib/src/dom/element.dart +++ b/kraken/lib/src/dom/element.dart @@ -192,7 +192,11 @@ class Element extends Node RenderBox? after; if (previousRenderBoxModel != null) { parentRenderBox = previousRenderBoxModel.parent as RenderBox?; - after = (previousRenderBoxModel.parentData as ContainerParentDataMixin?)?.previousSibling; + + if (previousRenderBoxModel.parentData is ContainerParentDataMixin) { + after = (previousRenderBoxModel.parentData as ContainerParentDataMixin).previousSibling; + } + _detachRenderBoxModel(previousRenderBoxModel); if (parentRenderBox != null) { diff --git a/kraken/lib/src/rendering/flex.dart b/kraken/lib/src/rendering/flex.dart index fb897be109..29b0b8dbaa 100644 --- a/kraken/lib/src/rendering/flex.dart +++ b/kraken/lib/src/rendering/flex.dart @@ -667,7 +667,7 @@ class RenderFlexLayout extends RenderLayoutBox { // Layout placeholder of positioned element(absolute/fixed) in new layer if (child is RenderBoxModel && childParentData.isPositioned) { CSSPositionedLayout.layoutPositionedChild(this, child); - } else if (child is RenderPositionPlaceholder && isPlaceholderPositioned(child)) { + } else if (child is RenderPositionPlaceholder && _isPlaceholderPositioned(child)) { _layoutChildren(child); } @@ -716,19 +716,6 @@ class RenderFlexLayout extends RenderLayoutBox { didLayout(); } - - bool isPlaceholderPositioned(RenderObject child) { - if (child is RenderPositionPlaceholder) { - RenderBoxModel realDisplayedBox = child.positioned!; - RenderLayoutParentData parentData = - realDisplayedBox.parentData as RenderLayoutParentData; - if (parentData.isPositioned) { - return true; - } - } - return false; - } - /// There are 4 stages when layouting children /// 1. Layout children in flow order to calculate flex lines according to its constaints and flex-wrap property /// 2. Relayout children according to flex-grow and flex-shrink factor @@ -906,7 +893,7 @@ class RenderFlexLayout extends RenderLayoutBox { // Exclude positioned placeholder renderObject when layout non placeholder object // and positioned renderObject if (placeholderChild == null && - (isPlaceholderPositioned(child) || childParentData!.isPositioned)) { + (_isPlaceholderPositioned(child) || childParentData!.isPositioned)) { child = childParentData!.nextSibling; continue; } @@ -920,12 +907,11 @@ class RenderFlexLayout extends RenderLayoutBox { childNodeId = child.hashCode; } - if (isPlaceholderPositioned(child)) { - RenderBoxModel realDisplayedBox = (child as RenderPositionPlaceholder).positioned!; - if (realDisplayedBox.hasSize) { + if (_isPlaceholderPositioned(child)) { + RenderBoxModel positionedBox = (child as RenderPositionPlaceholder).positioned!; + if (positionedBox.hasSize) { // Flutter only allow access size of direct children, so cannot use realDisplayedBox.size - Size realDisplayedBoxSize = - realDisplayedBox.getBoxSize(realDisplayedBox.contentSize); + Size realDisplayedBoxSize = positionedBox.getBoxSize(positionedBox.contentSize); double realDisplayedBoxWidth = realDisplayedBoxSize.width; double realDisplayedBoxHeight = realDisplayedBoxSize.height; childConstraints = BoxConstraints( @@ -1349,7 +1335,7 @@ class RenderFlexLayout extends RenderLayoutBox { // Whether child is positioned placeholder or positioned renderObject bool isChildPositioned = placeholderChild == null && - (isPlaceholderPositioned(child) || childParentData!.isPositioned); + (_isPlaceholderPositioned(child) || childParentData!.isPositioned); // Whether child cross size should be changed based on cross axis alignment change bool isCrossSizeChanged = false; @@ -2012,7 +1998,7 @@ class RenderFlexLayout extends RenderLayoutBox { // Exclude positioned placeholder renderObject when layout non placeholder object // and positioned renderObject if (placeholderChild == null && - (isPlaceholderPositioned(child) || childParentData!.isPositioned)) { + (_isPlaceholderPositioned(child) || childParentData!.isPositioned)) { child = childParentData!.nextSibling; continue; } @@ -2500,6 +2486,17 @@ class RenderFlexLayout extends RenderLayoutBox { properties .add(DiagnosticsProperty('flexWrap', renderStyle.flexWrap)); } + + static bool _isPlaceholderPositioned(RenderObject child) { + if (child is RenderPositionPlaceholder) { + RenderBoxModel realDisplayedBox = child.positioned!; + RenderLayoutParentData parentData = realDisplayedBox.parentData as RenderLayoutParentData; + if (parentData.isPositioned) { + return true; + } + } + return false; + } } // Render flex layout with self repaint boundary. From 50df3c138de1c6d6a7f310f913a9ea649121a027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Tue, 30 Nov 2021 16:47:41 +0800 Subject: [PATCH 096/167] fix: Root element creation should flush after renderobject attached. --- kraken/lib/src/dom/elements/html.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kraken/lib/src/dom/elements/html.dart b/kraken/lib/src/dom/elements/html.dart index a4943ae1d5..fca82b0029 100644 --- a/kraken/lib/src/dom/elements/html.dart +++ b/kraken/lib/src/dom/elements/html.dart @@ -41,6 +41,8 @@ class HTMLElement extends Element { super.attachTo(parent); if (renderBoxModel != null) { elementManager.viewport.child = renderBoxModel!; + // Flush pending style immediately. + style.flushPendingProperties(); } } From 43a8a6083d9921278c0e70f6a6e21626e6fe63fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Tue, 30 Nov 2021 17:08:48 +0800 Subject: [PATCH 097/167] fix: Root element creation should flush after renderobject attached. --- kraken/lib/src/dom/element.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/kraken/lib/src/dom/element.dart b/kraken/lib/src/dom/element.dart index 6bff618716..c60973aa11 100644 --- a/kraken/lib/src/dom/element.dart +++ b/kraken/lib/src/dom/element.dart @@ -481,11 +481,6 @@ class Element extends Node RenderBoxModel _renderBoxModel = renderBoxModel!; CSSPositionType currentPosition = renderStyle.position; - // If renderer itself not attached, no need to re-attach it. - if (!_renderBoxModel.attached) { - return; - } - // Remove fixed children before convert to non repaint boundary renderObject if (currentPosition != CSSPositionType.fixed) { _removeFixedChild(_renderBoxModel, elementManager.viewportElement._renderLayoutBox!); From a4914c66ae6712ea4a1c006a6dca38e82b08f20f Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 17:31:02 +0800 Subject: [PATCH 098/167] fix: fix imageCache not cache image. --- kraken/lib/src/dom/elements/img.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 407198fa76..2534e6926b 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -124,6 +124,7 @@ class ImageElement extends Element { _stopListeningStream(); _completerHandle?.dispose(); _replaceImage(info: null); + _imageProvider?.evict(); _imageProvider = null; } @@ -325,7 +326,10 @@ class ImageElement extends Element { int? cachedWidth = (width != null && width > 0) ? (width * ui.window.devicePixelRatio).toInt() : null; int? cachedHeight = (height != null && height > 0) ? (height * ui.window.devicePixelRatio).toInt() : null; - ImageProvider? provider = _imageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); + ImageProvider? provider = _imageProvider; + if (_imageProvider == null) { + provider = _imageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); + } if (provider == null) return; final ImageStream newStream = provider.resolve(ImageConfiguration.empty); _updateSourceStream(newStream); From 8b7962ed44af1fb7dad876cf7d66751905aa9d83 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 17:31:33 +0800 Subject: [PATCH 099/167] fix: fix lazy image load event. --- kraken/lib/src/dom/elements/img.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 2534e6926b..166448dfe9 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -384,7 +384,7 @@ class ImageElement extends Element { _replaceImage(info: imageInfo); _frameCount++; - if (!_imageLoaded) { + if (!_imageLoaded && !_shouldLazyLoading) { _imageLoaded = true; if (sync) { // `synchronousCall` happens when caches image and calling `addListener`. From ade787d13b027f7e2b522b89c0ee0a885201587c Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 18:09:04 +0800 Subject: [PATCH 100/167] fix: fix test spec --- bridge/bindings/qjs/module_manager_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridge/bindings/qjs/module_manager_test.cc b/bridge/bindings/qjs/module_manager_test.cc index fde716594d..394fb2bdde 100644 --- a/bridge/bindings/qjs/module_manager_test.cc +++ b/bridge/bindings/qjs/module_manager_test.cc @@ -16,9 +16,9 @@ TEST(ModuleManager, shouldThrowErrorWhenBadJSON) { EXPECT_STREQ(errmsg, "TypeError: circular reference\n" " at __kraken_invoke_module__ (native)\n" - " at (internal://)\n" + " at (internal://:616)\n" " at Promise (native)\n" - " at invokeMethod (internal://)\n" + " at invokeMethod (internal://:617)\n" " at (vm://:12)\n"); errorCalled = true; }); From 2c8f0e74f1deea75c65f0537f18571c06e4888f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Tue, 30 Nov 2021 19:39:07 +0800 Subject: [PATCH 101/167] fix: sliver child. --- kraken/lib/src/css/style_declaration.dart | 8 ++++-- kraken/lib/src/dom/element.dart | 34 ++++++++++++----------- kraken/lib/src/dom/elements/html.dart | 3 ++ kraken/lib/src/dom/sliver_manager.dart | 10 +++---- 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/kraken/lib/src/css/style_declaration.dart b/kraken/lib/src/css/style_declaration.dart index 85297bcbe6..e01389de05 100644 --- a/kraken/lib/src/css/style_declaration.dart +++ b/kraken/lib/src/css/style_declaration.dart @@ -370,11 +370,10 @@ class CSSStyleDeclaration { Element? _target = target; // If style target element not exists, no need to do flush operation. if (_target == null) return; - // If target has no renderer attached, skip flush. - if (!_target.isRendererAttached) return; // Display change from none to other value that the renderBoxModel is null. - if (_pendingProperties.containsKey(DISPLAY) && _target.isConnected) { + if (_pendingProperties.containsKey(DISPLAY) && _target.isConnected && + _target.parentElement?.renderStyle.display != CSSDisplay.sliver) { String? prevValue = _properties[DISPLAY]; String currentValue = _pendingProperties[DISPLAY]!; _properties[DISPLAY] = currentValue; @@ -382,6 +381,9 @@ class CSSStyleDeclaration { _emitPropertyChanged(DISPLAY, prevValue, currentValue); } + // If target has no renderer attached, no need to flush. + if (!_target.isRendererAttached) return; + RenderBoxModel? renderBoxModel = _target.renderBoxModel; if (_pendingProperties.isEmpty || renderBoxModel == null) { return; diff --git a/kraken/lib/src/dom/element.dart b/kraken/lib/src/dom/element.dart index c60973aa11..af7a946a35 100644 --- a/kraken/lib/src/dom/element.dart +++ b/kraken/lib/src/dom/element.dart @@ -188,10 +188,10 @@ class Element extends Node RenderBox? previousRenderBoxModel = renderBoxModel; if (nextRenderBoxModel != previousRenderBoxModel) { - RenderBox? parentRenderBox; + RenderObject? parentRenderObject; RenderBox? after; if (previousRenderBoxModel != null) { - parentRenderBox = previousRenderBoxModel.parent as RenderBox?; + parentRenderObject = previousRenderBoxModel.parent as RenderObject?; if (previousRenderBoxModel.parentData is ContainerParentDataMixin) { after = (previousRenderBoxModel.parentData as ContainerParentDataMixin).previousSibling; @@ -199,8 +199,8 @@ class Element extends Node _detachRenderBoxModel(previousRenderBoxModel); - if (parentRenderBox != null) { - _attachRenderBoxModel(parentRenderBox, nextRenderBoxModel, after: after); + if (parentRenderObject != null) { + _attachRenderBoxModel(parentRenderObject, nextRenderBoxModel, after: after); } } renderBoxModel = nextRenderBoxModel; @@ -579,7 +579,10 @@ class Element extends Node void attachTo(Node parent, {RenderBox? after}) { _applyStyle(style); - willAttachRenderer(); + // @NOTE: Sliver should not create renderer here. + if (parentElement?.renderStyle.display != CSSDisplay.sliver) { + willAttachRenderer(); + } if (renderer != null) { // HTML element override attachTo method to attach renderObject to viewportBox. @@ -802,28 +805,27 @@ class Element extends Node // Update renderBoxModel. _updateRenderBoxModel(); // Attach renderBoxModel to parent if change from `display: none` to other values. - if (renderBoxModel!.parent == null) { + if (!isRendererAttached && parentElement != null && parentElement!.isRendererAttached) { _addToContainingBlock(after: previousSibling?.renderer); ensureChildAttached(); } } - void _attachRenderBoxModel(RenderBox parentRenderBox, RenderBox renderBox, {RenderObject? after, bool isLast = false}) { + void _attachRenderBoxModel(RenderObject parentRenderObject, RenderBox renderBox, {RenderObject? after, bool isLast = false}) { if (isLast) { assert(after == null); } - if (parentRenderBox is RenderObjectWithChildMixin) { // RenderViewportBox - (parentRenderBox as RenderObjectWithChildMixin).child = renderBox; - } else if (parentRenderBox is ContainerRenderObjectMixin) { // RenderLayoutBox or RenderSliverList + if (parentRenderObject is RenderObjectWithChildMixin) { // RenderViewportBox + parentRenderObject.child = renderBox; + } else if (parentRenderObject is ContainerRenderObjectMixin) { // RenderLayoutBox or RenderSliverList // Should attach to renderScrollingContent if it is scrollable. - if (parentRenderBox is RenderLayoutBox) { - parentRenderBox = parentRenderBox.renderScrollingContent ?? parentRenderBox; + if (parentRenderObject is RenderLayoutBox) { + parentRenderObject = parentRenderObject.renderScrollingContent ?? parentRenderObject; } if (isLast) { - after = (parentRenderBox as ContainerRenderObjectMixin).lastChild; + after = parentRenderObject.lastChild; } - (parentRenderBox as ContainerRenderObjectMixin).insert(renderBox, after: after); - + parentRenderObject.insert(renderBox, after: after); } } @@ -1708,7 +1710,7 @@ bool _hasIntersectionObserverEvent(Map eventHandlers) { eventHandlers.containsKey('intersectionchange'); } -void _detachRenderBoxModel(RenderBox renderBox) { +void _detachRenderBoxModel(RenderObject renderBox) { if (renderBox.parent == null) return; // Remove reference from parent diff --git a/kraken/lib/src/dom/elements/html.dart b/kraken/lib/src/dom/elements/html.dart index fca82b0029..0d51f1d93f 100644 --- a/kraken/lib/src/dom/elements/html.dart +++ b/kraken/lib/src/dom/elements/html.dart @@ -64,4 +64,7 @@ class HTMLElement extends Element { @override String get tagName => HTML; + + @override + bool get isRendererAttached => true; } diff --git a/kraken/lib/src/dom/sliver_manager.dart b/kraken/lib/src/dom/sliver_manager.dart index 72ce6f5d79..4307175e7b 100644 --- a/kraken/lib/src/dom/sliver_manager.dart +++ b/kraken/lib/src/dom/sliver_manager.dart @@ -49,11 +49,6 @@ class RenderSliverElementChildManager implements RenderSliverBoxChildManager { childNode.willAttachRenderer(); RenderBox? child; - - if (childNode is Element) { - childNode.style.flushPendingProperties(); - } - if (childNode is Node) { child = childNode.renderer; } else { @@ -68,8 +63,11 @@ class RenderSliverElementChildManager implements RenderSliverBoxChildManager { _sliverListLayout.insertSliverChild(child, after: after); } + if (childNode is Element) { + childNode.style.flushPendingProperties(); + } + childNode.didAttachRenderer(); - childNode.ensureChildAttached(); } RenderBox _createEmptyRenderObject() { From 628db97963cf78f9d6b6c4783c17a767c41022ea Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 19:54:51 +0800 Subject: [PATCH 102/167] fix: fix img specs and snapshots. --- .../dom/elements/img.ts.56fc70fa1.png | Bin 1475 -> 1407 bytes .../dom/elements/img.ts.7bcfa1081.png | Bin 0 -> 9553 bytes .../dom/elements/img.ts.7bcfa1082.png | Bin 0 -> 9241 bytes .../dom/elements/img.ts.ea354b921.png | Bin 1475 -> 1407 bytes integration_tests/specs/dom/elements/img.ts | 18 +++++------------- 5 files changed, 5 insertions(+), 13 deletions(-) create mode 100644 integration_tests/snapshots/dom/elements/img.ts.7bcfa1081.png create mode 100644 integration_tests/snapshots/dom/elements/img.ts.7bcfa1082.png diff --git a/integration_tests/snapshots/dom/elements/img.ts.56fc70fa1.png b/integration_tests/snapshots/dom/elements/img.ts.56fc70fa1.png index 1711ff5635a101efba5254fdab99262899c73354..fc0a4cbee0b1c35e3a96eaab12b7986d7dcd2332 100644 GIT binary patch delta 1373 zcmV-j1)}=H3;zm`7Ya5A1^@s6n`inIks%;|DM>^@RA_mWX9wfEkcJ9EDI&YU}Q#!wJ@H!i+YwC;ZfJJ;8A=lYtSwHmTlI6{)|9#7ikR4%0BWBL@X)uM+W@h7=_&1|$*?!L|(jgTdVTt3bST zU>d^2g2aMEg7y{LG85&!`?JYQYQ9^4e}DJgD#nJu+z~ILw21|i3MQQ-OgnhE*(e(Q{`Pn&q5&ZbF=m65Vy z<;j+;Kx{n8SfJ}5G5-0mjkdr5j(&XvoF4H~o$Op+Q!$_lwrQ{p!=U33>+b=7PjcX( z6?tV-14eu_A0He6Nd%liDu_V$?xUp>FWva=r)w(K2P@k&sQPFV*nO=UrTM5ln){!vt4$3$&?o0F_@LEy(2gg2Nq`Kbm8+hY z+1MdgK?MbjRod6KgR5P;!Mz)QFWif)yE=N%jF?!BG$P2{anQ2)w>t(wQSt69CRf-n zx*sAW@t`Ak@a1#f$_pvV&{`JedhHh z0KJ{#2`;Ii>c_;UimD(H$3aXK6V3nh*Ht?>;l0luBWho#18HIO@g|!OG+MYZT?cEUM<45-A5va zW}c`?vpUVy!uhxUxVpGjO~RT9s33{Mp6h`YS9lql`y#0A(ue|oitEIv6OOivhlTdF zZ8>e?l?mVf`Xv=R6{Hko!G3lY>Jl&WZskr6zTH$zo`Z5AR?&e$jFLQGY%VV{bWwz^ zGDy`wOT&GuC}PRg^@>0Ky+QNk?#b^K#4_AH+`?nbH2Z9?&?^tOHhv7&e?e3yto%H$ zcs!SNV&^U1tlWQpMbvVcgIkxCIUA(pVzHIiX-4XLE#rsC=SU>wls??r{Ij-gGY}?dMZ+xKRDn)D)2bB-DpE ft5nhdSXKNB_ciV8Yi?Bh00000NkvXXu0mjf*omb9 delta 1442 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 UvVZ;uT#Ew9NhwKIi5ms{4{b7UjsO4v literal 0 HcmV?d00001 diff --git a/integration_tests/snapshots/dom/elements/img.ts.7bcfa1082.png b/integration_tests/snapshots/dom/elements/img.ts.7bcfa1082.png new file mode 100644 index 0000000000000000000000000000000000000000..241632d3c15ec20f00d4e0b073eb9aa699a6d224 GIT binary patch literal 9241 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 delta 1442 zcmV;T1zr083d0MK7Ya8B1^@s6P&+BHks%;|ZAnByRA_q)d~B0vbev1cLu0MT(?G2m}(L1JOg_0)%23$`lA_phz$XEMI~xd|1BQ znMW~u=exwd+h0CAC|~JxtKFG5^PTV8dGF>O?Vgcy=(j7LN zHSt`7jgl4vwr$bA4qX};C%n*rzK7I9GYV>tj$}ZZ0x9hMxC6|Nc%eZyZY(N)0#VDL z9yHaVD~ruciNL7{dLFEV?<8~W!Ju>50(c-*>}kcNhP^vGM^3zWE`4)RZ8J>X&KcC} zKG^|UQP{vD0hhV-XqyIz2x1gfff&SScvS@4rn`Ff?ccwv!0S+yUd_&X=hY_WQ46sN z?wnX?M+XnumarfWNV1z6oCBkOM2V-rYdQ1k-P+ZzZ1BMc7gVAVGe&}%4HG^xK7=F# zch=A@3bQa)LBYg|nDKqGeRaJy;R$FDcrfWqLRcjBMaq}uGgh~ zK9`(^BFgRDE=Ky`%%C8DI-jhl+uTNy2(Xpyb=Q`3l=LJerJz&Sqp*N|dI58~U5mMg zF-`I9FE725*H&f1om(q2d1)t+ju!N@ymo32OLd`2=$V8QOBlvU*R=qZHm%OB6&*%$ z1x)>-)U9-7g-sIDNYn0vKfXiBt<~ip^0cc&SQ3ZtS;TsLH%8)rdg5>y=zVuBPrFKl zbBx-NASpN7(eAAG*N zW11-_s1tC4I)m>O&$Ox%FVeTX!YXY5X?P^^;XC%ZH?HotOTRvRZ$LaRf!5l3Ty&wz zho!+%i6Yr?j6{Kdd~d_hxG?2?wp^a{_^4dHw8AntgR0xS?Dp$<3G$?CAflQj&#@Z+Rm}q2w*EujN($y56%j(0;@RtWH z;T+Tu0-lBnV}B=KGBOL&l?b1C?XD1k_A{j=6vv0eH7ttuHB=#T;HXL0va3ie!FAIS zi|KwHNn$}-LVx@Cm9Cu4Y^^Tq=$~1Duz5KkK6r$<<#OAvJL9Uw*w*TDVMUIM^sxoX zJmG49?%I-nZhdj-l{&_)hp^)|>>r8`3fjX%@=<+8{t8lrbbL|2uH)UV%~iHmmsPA{ zZ1}H+x={X9mF%7(lHGGzIJ1g6A!_D#?*YoXd9545Ki^zZ0M|F6{23|fp25WX!l(mP wAxb>1Cx{8veszqs%Zpi`m^{G~oG| { var imageURL = 'https://img.alicdn.com/tfs/TB1RRzFeKL2gK0jSZFmXXc7iXXa-200-200.png?network'; var img = document.createElement('img'); img.onload = function() { - expect(img.naturalWidth).toEqual(200); - expect(img.naturalWidth).toEqual(200); done(); }; img.src = imageURL; @@ -172,9 +170,6 @@ describe('Tags img', () => { expect(img.width).toEqual(20); expect(img.height).toEqual(20); - // Image has not been loaded. - expect(img.naturalWidth).toEqual(0); - expect(img.naturalWidth).toEqual(0); }); it('should work with loading=lazy', (done) => { @@ -303,7 +298,7 @@ describe('Tags img', () => { }, 100); }); - it('gif can replay', async (done) => { + it('gif can not replay by remove nodes', async (done) => { const imageURL = 'assets/sample-gif-40k.gif'; const img = document.createElement('img'); @@ -311,14 +306,11 @@ describe('Tags img', () => { await snapshot(img); document.body.removeChild(img); - setTimeout(() => { + setTimeout(async () => { + // When img re-append to document, to Gif image will continue to play. document.body.appendChild(img); - // After next frame that image has shown. - requestAnimationFrame(async () => { - // When replay, the image should be same as first frame. - await snapshot(img); - done(); - }); + await snapshot(img); + done(); // Delay 200ms to play gif. }, 200); }; From 1a1870b84027cd664ae98ce9d88c5d0f668f31c3 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 19:03:33 +0800 Subject: [PATCH 103/167] fix: fix image provider when propertyChanged. --- kraken/lib/src/dom/elements/img.dart | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 166448dfe9..c338199f00 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -309,7 +309,7 @@ class ImageElement extends Element { } } - void _resolveImage(Uri? resolvedUri) { + void _resolveImage(Uri? resolvedUri, { bool propertyChanged = false }) { if (resolvedUri == null) return; double? width = null; @@ -327,7 +327,12 @@ class ImageElement extends Element { int? cachedHeight = (height != null && height > 0) ? (height * ui.window.devicePixelRatio).toInt() : null; ImageProvider? provider = _imageProvider; - if (_imageProvider == null) { + if (propertyChanged) { + // When propertyChanges, we should release previous cached images for better memory usage. + if (provider != null) { + provider.evict(); + } + provider = _imageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); } if (provider == null) return; @@ -414,7 +419,7 @@ class ImageElement extends Element { // Update image source if image already attached. if (isRendererAttached) { final Uri? resolvedUri = _resolvedUri = _resolveSrc(); - _resolveImage(resolvedUri); + _resolveImage(resolvedUri, propertyChanged: true); } else { _precacheImage(); } @@ -422,10 +427,10 @@ class ImageElement extends Element { _resetLazyLoading(); } else if (key == WIDTH) { _propertyWidth = CSSNumber.parseNumber(value); - _resolveImage(_resolvedUri); + _resolveImage(_resolvedUri, propertyChanged: true); } else if (key == HEIGHT) { _propertyHeight = CSSNumber.parseNumber(value); - _resolveImage(_resolvedUri); + _resolveImage(_resolvedUri, propertyChanged: true); } } @@ -451,7 +456,7 @@ class ImageElement extends Element { // Resize renderBox if (isRendererAttached) _resizeImage(); // Resize image - _resolveImage(_resolvedUri); + _resolveImage(_resolvedUri, propertyChanged: true); } else if (property == OBJECT_FIT && _renderImage != null) { _renderImage!.fit = renderBoxModel!.renderStyle.objectFit; } else if (property == OBJECT_POSITION && _renderImage != null) { From 0b0eeb2a94353794b61659b6d5c001874a79fb91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=83=E5=BD=A6?= Date: Tue, 30 Nov 2021 19:42:30 +0800 Subject: [PATCH 104/167] chore: rename var --- kraken/lib/src/dom/elements/img.dart | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index c338199f00..35ff5cc698 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -26,7 +26,6 @@ const Map _defaultStyle = { class ImageElement extends Element { // The render box to draw image. RenderImage? _renderImage; - bool _isListeningToStream = false; ImageProvider? _imageProvider; ImageStream? _imageStream; @@ -42,8 +41,9 @@ class ImageElement extends Element { /// Number of image frame, used to identify multi frame image after loaded. int _frameCount = 0; + bool _isListeningStream = false; bool _isInLazyLoading = false; - bool _imageLoaded = false; + bool _isImageLoaded = false; bool get _shouldLazyLoading => properties['loading'] == 'lazy'; ImageStreamCompleterHandle? _completerHandle; @@ -83,7 +83,7 @@ class ImageElement extends Element { void _loadImage() { _constructImage(); - // Try to attach image if image had cached. + // Try to attach image if image is cached. _attachImage(); _resizeImage(); _resolveImage(_resolvedUri); @@ -108,14 +108,14 @@ class ImageElement extends Element { } void _listenToStream() { - if (_isListeningToStream) + if (_isListeningStream) return; _imageStream?.addListener(_getListener()); _completerHandle?.dispose(); _completerHandle = null; - _isListeningToStream = true; + _isListeningStream = true; } @override @@ -274,7 +274,7 @@ class ImageElement extends Element { /// to true, which create [ImageStreamCompleterHandle] to keep the completer /// alive. void _stopListeningStream({bool keepStreamAlive = false}) { - if (!_isListeningToStream) + if (!_isListeningStream) return; if (keepStreamAlive && _completerHandle == null && _imageStream?.completer != null) { @@ -282,7 +282,7 @@ class ImageElement extends Element { } _imageStream?.removeListener(_getListener()); - _isListeningToStream = false; + _isListeningStream = false; } Uri? _resolveSrc() { @@ -297,14 +297,14 @@ class ImageElement extends Element { void _updateSourceStream(ImageStream newStream) { if (_imageStream?.key == newStream.key) return; - if (_isListeningToStream) { + if (_isListeningStream) { _imageStream?.removeListener(_getListener()); } _frameCount = 0; _imageStream = newStream; - if (_isListeningToStream) { + if (_isListeningStream) { _imageStream!.addListener(_getListener()); } } @@ -355,8 +355,8 @@ class ImageElement extends Element { _replaceImage(info: imageInfo); _frameCount++; - if (!_imageLoaded) { - _imageLoaded = true; + if (!_isImageLoaded) { + _isImageLoaded = true; if (synchronousCall) { // `synchronousCall` happens when caches image and calling `addListener`. scheduleMicrotask(_handleEventAfterImageLoaded); @@ -389,8 +389,8 @@ class ImageElement extends Element { _replaceImage(info: imageInfo); _frameCount++; - if (!_imageLoaded && !_shouldLazyLoading) { - _imageLoaded = true; + if (!_isImageLoaded && !_shouldLazyLoading) { + _isImageLoaded = true; if (sync) { // `synchronousCall` happens when caches image and calling `addListener`. scheduleMicrotask(_handleEventAfterImageLoaded); From 72957c645ae4a491fa20cad7aba606ae60c1943b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=83=E5=BD=A6?= Date: Tue, 30 Nov 2021 19:45:29 +0800 Subject: [PATCH 105/167] chore: use complete --- kraken/lib/src/dom/elements/img.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 35ff5cc698..2a880e391c 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -43,7 +43,9 @@ class ImageElement extends Element { bool _isListeningStream = false; bool _isInLazyLoading = false; - bool _isImageLoaded = false; + // https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-complete-dev + // A boolean value which indicates whether or not the image has completely loaded. + bool complete = false; bool get _shouldLazyLoading => properties['loading'] == 'lazy'; ImageStreamCompleterHandle? _completerHandle; @@ -355,8 +357,8 @@ class ImageElement extends Element { _replaceImage(info: imageInfo); _frameCount++; - if (!_isImageLoaded) { - _isImageLoaded = true; + if (!complete) { + complete = true; if (synchronousCall) { // `synchronousCall` happens when caches image and calling `addListener`. scheduleMicrotask(_handleEventAfterImageLoaded); @@ -389,8 +391,8 @@ class ImageElement extends Element { _replaceImage(info: imageInfo); _frameCount++; - if (!_isImageLoaded && !_shouldLazyLoading) { - _isImageLoaded = true; + if (!complete && !_shouldLazyLoading) { + complete = true; if (sync) { // `synchronousCall` happens when caches image and calling `addListener`. scheduleMicrotask(_handleEventAfterImageLoaded); From 2792c6d81ffc4399d6471204af268af6e5f114cd Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 21:17:04 +0800 Subject: [PATCH 106/167] fix: fix image natural size. --- integration_tests/specs/dom/elements/img.ts | 5 + kraken/lib/src/dom/elements/img.dart | 16 ++- .../src/painting/image_provider_factory.dart | 116 +++++++++++++++--- 3 files changed, 119 insertions(+), 18 deletions(-) diff --git a/integration_tests/specs/dom/elements/img.ts b/integration_tests/specs/dom/elements/img.ts index dd82049d4c..394832ac04 100644 --- a/integration_tests/specs/dom/elements/img.ts +++ b/integration_tests/specs/dom/elements/img.ts @@ -157,6 +157,8 @@ describe('Tags img', () => { var imageURL = 'https://img.alicdn.com/tfs/TB1RRzFeKL2gK0jSZFmXXc7iXXa-200-200.png?network'; var img = document.createElement('img'); img.onload = function() { + expect(img.naturalWidth).toEqual(200); + expect(img.naturalHeight).toEqual(200); done(); }; img.src = imageURL; @@ -170,6 +172,9 @@ describe('Tags img', () => { expect(img.width).toEqual(20); expect(img.height).toEqual(20); + // Image has not been loaded. + expect(img.naturalWidth).toEqual(0); + expect(img.naturalHeight).toEqual(0); }); it('should work with loading=lazy', (done) => { diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 2a880e391c..8e1f6d2626 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -156,8 +156,20 @@ class ImageElement extends Element { return naturalHeight; } - double get naturalWidth => image?.width.toDouble() ?? 0; - double get naturalHeight => image?.height.toDouble() ?? 0; + double get naturalWidth { + ImageProvider? imageProvider = _imageProvider; + if (imageProvider is KrakenResizeImage) { + return imageProvider.naturalWidth.toDouble(); + } + return image?.width.toDouble() ?? 0; + } + double get naturalHeight { + ImageProvider? imageProvider = _imageProvider; + if (imageProvider is KrakenResizeImage) { + return imageProvider.naturalHeight.toDouble(); + } + return image?.height.toDouble() ?? 0; + } void _handleIntersectionChange(IntersectionObserverEntry entry) { // When appear diff --git a/kraken/lib/src/painting/image_provider_factory.dart b/kraken/lib/src/painting/image_provider_factory.dart index 63dd388b58..2cd58e1881 100644 --- a/kraken/lib/src/painting/image_provider_factory.dart +++ b/kraken/lib/src/painting/image_provider_factory.dart @@ -4,6 +4,7 @@ */ import 'dart:io'; +import 'dart:ui'; import 'dart:typed_data'; import 'package:flutter/cupertino.dart'; @@ -104,7 +105,7 @@ ImageProviderFactory _dataUrlProviderFactory = defaultDataUrlProviderFactory; ImageProviderFactory _blobProviderFactory = defaultBlobProviderFactory; ImageProviderFactory _assetsProviderFactory = defaultAssetsProvider; -ImageType parseImageUrl(Uri resolvedUri, { cache = 'auto' }) { +ImageType parseImageUrl(Uri resolvedUri, {cache = 'auto'}) { if (resolvedUri.isScheme('HTTP') || resolvedUri.isScheme('HTTPS')) { return (cache == 'store' || cache == 'auto') ? ImageType.cached @@ -143,8 +144,8 @@ ImageProvider? getImageProvider(Uri resolvedUri, FileImageProviderParams(file, cachedWidth: cachedWidth, cachedHeight: cachedHeight)); case ImageType.dataUrl: - // Data URL: https://tools.ietf.org/html/rfc2397 - // dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + // Data URL: https://tools.ietf.org/html/rfc2397 + // dataurl := "data:" [ mediatype ] [ ";base64" ] "," data UriData data = UriData.fromUri(resolvedUri); if (data.isBase64) { return factory( @@ -154,7 +155,7 @@ ImageProvider? getImageProvider(Uri resolvedUri, } return null; case ImageType.blob: - // TODO: support blob data type + // TODO: support blob data type return null; case ImageType.assets: return factory( @@ -182,7 +183,8 @@ ImageProviderFactory _getImageProviderFactory(ImageType imageType) { } } -void setCustomImageProviderFactory(ImageType imageType, ImageProviderFactory customImageProviderFactory) { +void setCustomImageProviderFactory( + ImageType imageType, ImageProviderFactory customImageProviderFactory) { switch (imageType) { case ImageType.cached: _cachedProviderFactory = customImageProviderFactory; @@ -206,9 +208,86 @@ void setCustomImageProviderFactory(ImageType imageType, ImageProviderFactory cus } } +class KrakenResizeImage extends ResizeImage { + KrakenResizeImage( + ImageProvider imageProvider, { + int? width, + int? height, + }) : super(imageProvider, width: width, height: height); + + static ImageProvider resizeIfNeeded( + int? cacheWidth, int? cacheHeight, ImageProvider provider) { + if (cacheWidth != null || cacheHeight != null) { + return KrakenResizeImage(provider, + width: cacheWidth, height: cacheHeight); + } + return provider; + } + + int naturalWidth = 0; + int naturalHeight = 0; + + @override + void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, + key, ImageErrorListener handleError) { + // This is an unusual edge case where someone has told us that they found + // the image we want before getting to this method. We should avoid calling + // load again, but still update the image cache with LRU information. + if (stream.completer != null) { + final ImageStreamCompleter? completer = + PaintingBinding.instance!.imageCache!.putIfAbsent( + key, + () => stream.completer!, + onError: handleError, + ); + assert(identical(completer, stream.completer)); + return; + } + final ImageStreamCompleter? completer = + PaintingBinding.instance!.imageCache!.putIfAbsent( + key, + () => load(key, instantiateImageCodec), + onError: handleError, + ); + if (completer != null) { + stream.setCompleter(completer); + } + } + + Future instantiateImageCodec( + Uint8List bytes, { + int? cacheWidth, + int? cacheHeight, + bool allowUpscaling = false, + }) async { + assert(cacheWidth == null || cacheWidth > 0); + assert(cacheHeight == null || cacheHeight > 0); + + final ImmutableBuffer buffer = await ImmutableBuffer.fromUint8List(bytes); + final ImageDescriptor descriptor = await ImageDescriptor.encoded(buffer); + if (!allowUpscaling) { + if (cacheWidth != null && cacheWidth > descriptor.width) { + cacheWidth = descriptor.width; + } + if (cacheHeight != null && cacheHeight > descriptor.height) { + cacheHeight = descriptor.height; + } + } + + naturalWidth = descriptor.width; + naturalHeight = descriptor.height; + + return descriptor.instantiateCodec( + targetWidth: cacheWidth, + targetHeight: cacheHeight, + ); + } +} + /// default ImageProviderFactory implementation of [ImageType.cached] -ImageProvider defaultCachedProviderFactory(Uri uri, ImageProviderParams params) { - return ResizeImage.resizeIfNeeded( +ImageProvider defaultCachedProviderFactory( + Uri uri, ImageProviderParams params) { + return KrakenResizeImage.resizeIfNeeded( params.cachedWidth, params.cachedHeight, CachedNetworkImage(uri.toString(), @@ -216,26 +295,30 @@ ImageProvider defaultCachedProviderFactory(Uri uri, ImageProviderParams params) } /// default ImageProviderFactory implementation of [ImageType.network] -ImageProvider defaultNetworkProviderFactory(Uri uri, ImageProviderParams params) { +ImageProvider defaultNetworkProviderFactory( + Uri uri, ImageProviderParams params) { NetworkImage networkImage = NetworkImage(uri.toString(), headers: { HttpHeaders.userAgentHeader: getKrakenInfo().userAgent, HttpHeaderContext: - (params as CachedNetworkImageProviderParams).contextId.toString(), + (params as CachedNetworkImageProviderParams).contextId.toString(), }); - return ResizeImage.resizeIfNeeded( + return KrakenResizeImage.resizeIfNeeded( params.cachedWidth, params.cachedHeight, networkImage); } /// default ImageProviderFactory implementation of [ImageType.file] ImageProvider? defaultFileProviderFactory(Uri uri, ImageProviderParams params) { - return ResizeImage.resizeIfNeeded( - params.cachedWidth, params.cachedHeight, FileImage((params as FileImageProviderParams).file)); + return KrakenResizeImage.resizeIfNeeded(params.cachedWidth, + params.cachedHeight, FileImage((params as FileImageProviderParams).file)); } /// default ImageProviderFactory implementation of [ImageType.dataUrl]. -ImageProvider? defaultDataUrlProviderFactory(Uri uri, ImageProviderParams params) { - return ResizeImage.resizeIfNeeded( - params.cachedWidth, params.cachedHeight, MemoryImage((params as DataUrlImageProviderParams).bytes)); +ImageProvider? defaultDataUrlProviderFactory( + Uri uri, ImageProviderParams params) { + return KrakenResizeImage.resizeIfNeeded( + params.cachedWidth, + params.cachedHeight, + MemoryImage((params as DataUrlImageProviderParams).bytes)); } /// default ImageProviderFactory implementation of [ImageType.blob]. @@ -246,5 +329,6 @@ ImageProvider? defaultBlobProviderFactory(Uri uri, ImageProviderParams params) { /// default ImageProviderFactory implementation of [ImageType.assets]. ImageProvider defaultAssetsProvider(Uri uri, ImageProviderParams params) { - return ResizeImage.resizeIfNeeded(params.cachedWidth, params.cachedHeight, AssetImage(uri.toString())); + return KrakenResizeImage.resizeIfNeeded( + params.cachedWidth, params.cachedHeight, AssetImage(uri.toString())); } From 93dc38db1b1135f02bfde9c295e6a8791d19cffc Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 22:11:07 +0800 Subject: [PATCH 107/167] fix: update snapshot images. --- .../dom/elements/img.ts.56fc70fa1.png | Bin 1407 -> 2154 bytes .../dom/elements/img.ts.7bcfa1082.png | Bin 9241 -> 6474 bytes .../dom/elements/img.ts.ea354b921.png | Bin 1407 -> 2154 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/integration_tests/snapshots/dom/elements/img.ts.56fc70fa1.png b/integration_tests/snapshots/dom/elements/img.ts.56fc70fa1.png index fc0a4cbee0b1c35e3a96eaab12b7986d7dcd2332..413d896838102f314a81bb153f6053906fff6910 100644 GIT binary patch delta 2127 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 FV1fqWFk%1z delta 1374 zcmV-k1)=)t5dR907Ya5A1^@s6n`inIks%*{1u02HK~!jg?V7ufTSXMce`n@C{D3Ht zRvSRtWI=?2BBG}W1r5+5@sA)RL_rZ~kx&E@sVEQ)DQH%R6yzU>P!Jsypl#Sk*i{k} zukAaBVy?ZmnLBg7`Ocg>bH-2*dp9n=Q?%}X20Pc+bm#h-p0yo+ zbs%M57$^ zbYL37#Dc_vM1uAe+cFd7z5BDtOKQG=u#L9B0FHir1e_l6Ql0EvUsExl3btvm4a1=05bN)M0Z($^ zpcQ##Qv*hPG#?)v0Z9a$LMn(r_wJ*m6EEHP?x$-i)(0!wG^qM$J%Pk{g){ZU#}NY# zFgfAACu|}`jCZ8Rcf0r2KiGY(8m0NDJevETt*cE9I?yNQFZiI?j8)p#wu7r(yTQGG8!z08th+jT(2ST^jWi<2+;Pyd`L{a;K~eGUEGAdj zFuETiB=Mjlc<|+O-pUIp^fE~8%pRpFV8N)v<@3QricQ?fjVrX@s84(;0b=KqHGSsw zCIG#i;|VUQpz6oOri!W{5ywGH6cf$=^w(88IN`m|FKO%Qe53pSSZwTn$ojdu*1NNb z@tLaUgZU30Xla!DkmL+65jjbMQyxt6tc+^ zQI&Ks!uvO`s6IYIPH~}Fg>+Db&AE1vcFN4rvZSNMRTg9at1Cehd7V2OI!?OIoSjA( zg>%!iKR*Fb6^j&qaQkw9;Q^^lc>m_Hx)KL*n3;nSqg=aZaj{aB@D-; z|NdssAXd?VL5z|-Uu-TfGIUXd zt};l~KTE@Xt0-d0)%A)${=Gr-0@$%ryIKuh1(Gw>EwZ)_*}%C#?KD zuXsF{bzsM5U5318iJ%b!3@;-$Sp~JP@btTFMwrj z2d7a_;Rv*^o@nHcQAzxBVy`@r&qEi%X^^fREDpcl)Zv_erJuJL0U6-D>JkZL5T6gT zAgP1;b2#=Y53ff{8SAel_2)<=<&-|$+WfP&Z8H!iXhp*;-c*53KGUuiCLOo3OUrQa zAg*(`wOQJ*xK(`b9MZ+lET>geY49!{O6_1HS4Cwgr!=h4acSqyhPq79f#5R#n~mWy zsLKR5$g?9!`E!6_ebv(=9yI(vxP4jG`JXJP0!|RCh gIIC3A|5#Q03->kc>}zgR`~Uy|07*qoM6N<$g5Y4KG5`Po diff --git a/integration_tests/snapshots/dom/elements/img.ts.7bcfa1082.png b/integration_tests/snapshots/dom/elements/img.ts.7bcfa1082.png index 241632d3c15ec20f00d4e0b073eb9aa699a6d224..271381af587fa5ab6faff859df3c1caa31f352eb 100644 GIT binary patch literal 6474 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 DIN61J literal 9241 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 FV1fqWFk%1z delta 1374 zcmV-k1)=)t5dR907Ya5A1^@s6n`inIks%*{1u02HK~!jg?V7ufTSXMce`n@C{D3Ht zRvSRtWI=?2BBG}W1r5+5@sA)RL_rZ~kx&E@sVEQ)DQH%R6yzU>P!Jsypl#Sk*i{k} zukAaBVy?ZmnLBg7`Ocg>bH-2*dp9n=Q?%}X20Pc+bm#h-p0yo+ zbs%M57$^ zbYL37#Dc_vM1uAe+cFd7z5BDtOKQG=u#L9B0FHir1e_l6Ql0EvUsExl3btvm4a1=05bN)M0Z($^ zpcQ##Qv*hPG#?)v0Z9a$LMn(r_wJ*m6EEHP?x$-i)(0!wG^qM$J%Pk{g){ZU#}NY# zFgfAACu|}`jCZ8Rcf0r2KiGY(8m0NDJevETt*cE9I?yNQFZiI?j8)p#wu7r(yTQGG8!z08th+jT(2ST^jWi<2+;Pyd`L{a;K~eGUEGAdj zFuETiB=Mjlc<|+O-pUIp^fE~8%pRpFV8N)v<@3QricQ?fjVrX@s84(;0b=KqHGSsw zCIG#i;|VUQpz6oOri!W{5ywGH6cf$=^w(88IN`m|FKO%Qe53pSSZwTn$ojdu*1NNb z@tLaUgZU30Xla!DkmL+65jjbMQyxt6tc+^ zQI&Ks!uvO`s6IYIPH~}Fg>+Db&AE1vcFN4rvZSNMRTg9at1Cehd7V2OI!?OIoSjA( zg>%!iKR*Fb6^j&qaQkw9;Q^^lc>m_Hx)KL*n3;nSqg=aZaj{aB@D-; z|NdssAXd?VL5z|-Uu-TfGIUXd zt};l~KTE@Xt0-d0)%A)${=Gr-0@$%ryIKuh1(Gw>EwZ)_*}%C#?KD zuXsF{bzsM5U5318iJ%b!3@;-$Sp~JP@btTFMwrj z2d7a_;Rv*^o@nHcQAzxBVy`@r&qEi%X^^fREDpcl)Zv_erJuJL0U6-D>JkZL5T6gT zAgP1;b2#=Y53ff{8SAel_2)<=<&-|$+WfP&Z8H!iXhp*;-c*53KGUuiCLOo3OUrQa zAg*(`wOQJ*xK(`b9MZ+lET>geY49!{O6_1HS4Cwgr!=h4acSqyhPq79f#5R#n~mWy zsLKR5$g?9!`E!6_ebv(=9yI(vxP4jG`JXJP0!|RCh gIIC3A|5#Q03->kc>}zgR`~Uy|07*qoM6N<$g5Y4KG5`Po From 00d3d0f65cdb976ee047f86adb9de0a6426a60ca Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 30 Nov 2021 22:51:38 +0800 Subject: [PATCH 108/167] fix: use Object.defineProperty to define object's properties. --- bridge/bindings/qjs/bom/window.cc | 4 +- bridge/bindings/qjs/bom/window.h | 5 +- bridge/bindings/qjs/dom/event_target.cc | 62 +++++++++++-------------- bridge/bindings/qjs/dom/event_target.h | 6 +-- bridge/bindings/qjs/js_context.h | 7 ++- 5 files changed, 39 insertions(+), 45 deletions(-) diff --git a/bridge/bindings/qjs/bom/window.cc b/bridge/bindings/qjs/bom/window.cc index 3f305116d7..238a7089fa 100644 --- a/bridge/bindings/qjs/bom/window.cc +++ b/bridge/bindings/qjs/bom/window.cc @@ -103,7 +103,7 @@ PROP_GETTER(Window, __location__)(QjsContext* ctx, JSValue this_val, int argc, J auto* window = static_cast(JS_GetOpaque(this_val, 1)); if (window == nullptr) return JS_UNDEFINED; - return JS_DupValue(ctx, window->m_location->jsObject); + return JS_DupValue(ctx, window->m_location.value()); } PROP_SETTER(Window, __location__)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { return JS_NULL; @@ -183,8 +183,6 @@ void WindowInstance::gcMark(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) EventTargetInstance::gcMark(rt, val, mark_func); // Should check object is already inited before gc mark. - if (JS_IsObject(m_location->jsObject)) - JS_MarkValue(rt, m_location->jsObject, mark_func); if (JS_IsObject(onerror)) JS_MarkValue(rt, onerror, mark_func); } diff --git a/bridge/bindings/qjs/bom/window.h b/bridge/bindings/qjs/bom/window.h index 3cbc13939e..fdfff90f56 100644 --- a/bridge/bindings/qjs/bom/window.h +++ b/bridge/bindings/qjs/bom/window.h @@ -47,12 +47,13 @@ class WindowInstance : public EventTargetInstance { public: WindowInstance() = delete; explicit WindowInstance(Window* window); - ~WindowInstance() { JS_FreeValue(m_ctx, onerror); } + ~WindowInstance() {} private: void gcMark(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) override; - Location* m_location{new Location(m_context)}; + ObjectProperty m_location{m_context, instanceObject, "m_location", (new Location(m_context))->jsObject}; + ObjectProperty m_onerror{m_context, instanceObject, "m_onerror", JS_NULL}; JSValue onerror{JS_NULL}; friend Window; friend JSContext; diff --git a/bridge/bindings/qjs/dom/event_target.cc b/bridge/bindings/qjs/dom/event_target.cc index 26a8eefa64..7544b368d3 100644 --- a/bridge/bindings/qjs/dom/event_target.cc +++ b/bridge/bindings/qjs/dom/event_target.cc @@ -69,18 +69,18 @@ JSValue EventTarget::addEventListener(QjsContext* ctx, JSValue this_val, int arg JSAtom eventTypeAtom = JS_ValueToAtom(ctx, eventTypeValue); // Init list. - if (!JS_HasProperty(ctx, eventTargetInstance->m_eventHandlers, eventTypeAtom)) { + if (!JS_HasProperty(ctx, eventTargetInstance->m_eventHandlers.value(), eventTypeAtom)) { JS_DupAtom(ctx, eventTypeAtom); auto* atomJob = new AtomJob{eventTypeAtom}; list_add_tail(&atomJob->link, &eventTargetInstance->m_context->atom_job_list); - JS_SetProperty(ctx, eventTargetInstance->m_eventHandlers, eventTypeAtom, JS_NewArray(ctx)); + JS_SetProperty(ctx, eventTargetInstance->m_eventHandlers.value(), eventTypeAtom, JS_NewArray(ctx)); } - JSValue eventHandlers = JS_GetProperty(ctx, eventTargetInstance->m_eventHandlers, eventTypeAtom); + JSValue eventHandlers = JS_GetProperty(ctx, eventTargetInstance->m_eventHandlers.value(), eventTypeAtom); int32_t eventHandlerLen = arrayGetLength(ctx, eventHandlers); // Dart needs to be notified for the first registration event. - if (eventHandlerLen == 0 || JS_HasProperty(ctx, eventTargetInstance->m_propertyEventHandler, eventTypeAtom)) { + if (eventHandlerLen == 0 || JS_HasProperty(ctx, eventTargetInstance->m_propertyEventHandler.value(), eventTypeAtom)) { int32_t contextId = eventTargetInstance->prototype()->contextId(); NativeString args_01{}; @@ -114,12 +114,12 @@ JSValue EventTarget::removeEventListener(QjsContext* ctx, JSValue this_val, int JSAtom eventTypeAtom = JS_ValueToAtom(ctx, eventTypeValue); - if (!JS_HasProperty(ctx, eventTargetInstance->m_eventHandlers, eventTypeAtom)) { + if (!JS_HasProperty(ctx, eventTargetInstance->m_eventHandlers.value(), eventTypeAtom)) { JS_FreeAtom(ctx, eventTypeAtom); return JS_UNDEFINED; } - JSValue eventHandlers = JS_GetProperty(ctx, eventTargetInstance->m_eventHandlers, eventTypeAtom); + JSValue eventHandlers = JS_GetProperty(ctx, eventTargetInstance->m_eventHandlers.value(), eventTypeAtom); int32_t targetIdx = arrayFindIdx(ctx, eventHandlers, callback); if (targetIdx != -1) { @@ -128,7 +128,7 @@ JSValue EventTarget::removeEventListener(QjsContext* ctx, JSValue this_val, int int32_t eventHandlersLen = arrayGetLength(ctx, eventHandlers); - if (eventHandlersLen && JS_HasProperty(ctx, eventTargetInstance->m_propertyEventHandler, eventTypeAtom)) { + if (eventHandlersLen && JS_HasProperty(ctx, eventTargetInstance->m_propertyEventHandler.value(), eventTypeAtom)) { // Dart needs to be notified for handles is empty. int32_t contextId = eventTargetInstance->prototype()->contextId(); @@ -207,8 +207,8 @@ bool EventTargetInstance::internalDispatchEvent(EventInstance* eventInstance) { JS_FreeValue(m_ctx, returnedValue); }; - if (JS_HasProperty(m_ctx, m_eventHandlers, eventTypeAtom)) { - JSValue eventHandlers = JS_GetProperty(m_ctx, m_eventHandlers, eventTypeAtom); + if (JS_HasProperty(m_ctx, m_eventHandlers.value(), eventTypeAtom)) { + JSValue eventHandlers = JS_GetProperty(m_ctx, m_eventHandlers.value(), eventTypeAtom); int32_t len = arrayGetLength(m_ctx, eventHandlers); for (int i = 0; i < len; i++) { @@ -221,7 +221,7 @@ bool EventTargetInstance::internalDispatchEvent(EventInstance* eventInstance) { } // Dispatch event listener white by 'on' prefix property. - if (JS_HasProperty(m_ctx, m_propertyEventHandler, eventTypeAtom)) { + if (JS_HasProperty(m_ctx, m_propertyEventHandler.value(), eventTypeAtom)) { if (eventType == "error") { auto _dispatchErrorEvent = [&eventInstance, this, eventType](JSValue& handler) { JSValue error = JS_GetPropertyStr(m_ctx, eventInstance->instanceObject, "error"); @@ -240,11 +240,11 @@ bool EventTargetInstance::internalDispatchEvent(EventInstance* eventInstance) { JS_FreeValue(m_ctx, lineNumberValue); JS_FreeValue(m_ctx, columnValue); }; - JSValue v = JS_GetProperty(m_ctx, m_propertyEventHandler, eventTypeAtom); + JSValue v = JS_GetProperty(m_ctx, m_propertyEventHandler.value(), eventTypeAtom); _dispatchErrorEvent(v); JS_FreeValue(m_ctx, v); } else { - JSValue v = JS_GetProperty(m_ctx, m_propertyEventHandler, eventTypeAtom); + JSValue v = JS_GetProperty(m_ctx, m_propertyEventHandler.value(), eventTypeAtom); _dispatchEvent(v); JS_FreeValue(m_ctx, v); } @@ -303,7 +303,7 @@ int EventTargetInstance::hasProperty(QjsContext* ctx, JSValue obj, JSAtom atom) return !JS_IsNull(eventTarget->getPropertyHandler(p)); } - return JS_HasProperty(ctx, eventTarget->m_properties, atom); + return JS_HasProperty(ctx, eventTarget->m_properties.value(), atom); } JSValue EventTargetInstance::getProperty(QjsContext* ctx, JSValue obj, JSAtom atom, JSValue receiver) { @@ -325,8 +325,8 @@ JSValue EventTargetInstance::getProperty(QjsContext* ctx, JSValue obj, JSAtom at return eventTarget->getPropertyHandler(p); } - if (JS_HasProperty(ctx, eventTarget->m_properties, atom)) { - return JS_GetProperty(ctx, eventTarget->m_properties, atom); + if (JS_HasProperty(ctx, eventTarget->m_properties.value(), atom)) { + return JS_GetProperty(ctx, eventTarget->m_properties.value(), atom); } // For plugin elements, try to auto generate properties and functions from dart response. @@ -354,14 +354,14 @@ int EventTargetInstance::setProperty(QjsContext* ctx, JSValue obj, JSAtom atom, if (!p->is_wide_char && p->u.str8[0] == 'o' && p->u.str8[1] == 'n') { eventTarget->setPropertyHandler(p, value); } else { - if (!JS_HasProperty(ctx, eventTarget->m_properties, atom)) { + if (!JS_HasProperty(ctx, eventTarget->m_properties.value(), atom)) { auto* atomJob = new AtomJob{atom}; list_add_tail(&atomJob->link, &eventTarget->m_context->atom_job_list); // Increase one reference count for atom to hold this atom value until eventTarget disposed. JS_DupAtom(ctx, atom); } - JS_SetProperty(ctx, eventTarget->m_properties, atom, JS_DupValue(ctx, value)); + JS_SetProperty(ctx, eventTarget->m_properties.value(), atom, JS_DupValue(ctx, value)); if (isJavaScriptExtensionElementInstance(eventTarget->context(), eventTarget->instanceObject) && !p->is_wide_char && p->u.str8[0] != '_') { NativeString* args_01 = atomToNativeString(ctx, atom); @@ -406,7 +406,7 @@ void EventTargetInstance::setPropertyHandler(JSString* p, JSValue value) { if (JS_IsNull(value)) { JS_FreeAtom(m_ctx, atom); list_del(&atomJob->link); - JS_DeleteProperty(m_ctx, m_propertyEventHandler, atom, 0); + JS_DeleteProperty(m_ctx, m_propertyEventHandler.value(), atom, 0); return; } @@ -417,9 +417,8 @@ void EventTargetInstance::setPropertyHandler(JSString* p, JSValue value) { } JSValue newCallback = JS_DupValue(m_ctx, value); - JS_SetProperty(m_ctx, m_propertyEventHandler, atom, newCallback); - - int32_t eventHandlerLen = arrayGetLength(m_ctx, m_eventHandlers); + JS_SetProperty(m_ctx, m_propertyEventHandler.value(), atom, newCallback); + int32_t eventHandlerLen = arrayGetLength(m_ctx, m_eventHandlers.value()); if (eventHandlerLen == 0) { int32_t contextId = m_context->getContextId(); NativeString* args_01 = atomToNativeString(m_ctx, atom); @@ -432,10 +431,10 @@ JSValue EventTargetInstance::getPropertyHandler(JSString* p) { char eventType[p->len + 1 - 2]; memcpy(eventType, &p->u.str8[2], p->len + 1 - 2); JSAtom atom = JS_NewAtom(m_ctx, eventType); - if (!JS_HasProperty(m_ctx, m_propertyEventHandler, atom)) { + if (!JS_HasProperty(m_ctx, m_propertyEventHandler.value(), atom)) { return JS_NULL; } - return JS_GetProperty(m_ctx, m_propertyEventHandler, atom); + return JS_GetProperty(m_ctx, m_propertyEventHandler.value(), atom); } void EventTargetInstance::finalize(JSRuntime* rt, JSValue val) { @@ -450,27 +449,18 @@ JSValue EventTargetInstance::getNativeProperty(const char* prop) { return result; } -void EventTargetInstance::gcMark(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) { - // Tell gc eventTargetInstance have these properties. - // Should check object is already inited before gc mark. - if (JS_IsObject(m_eventHandlers)) - JS_MarkValue(rt, m_eventHandlers, mark_func); - if (JS_IsObject(m_propertyEventHandler)) - JS_MarkValue(rt, m_propertyEventHandler, mark_func); - if (JS_IsObject(m_properties)) - JS_MarkValue(rt, m_properties, mark_func); -} +void EventTargetInstance::gcMark(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) {} void EventTargetInstance::copyNodeProperties(EventTargetInstance* newNode, EventTargetInstance* referenceNode) { QjsContext* ctx = referenceNode->m_ctx; - JSValue propKeys = objectGetKeys(ctx, referenceNode->m_properties); + JSValue propKeys = objectGetKeys(ctx, referenceNode->m_properties.value()); uint32_t propKeyLen = arrayGetLength(ctx, propKeys); for (int i = 0; i < propKeyLen; i++) { JSValue k = JS_GetPropertyUint32(ctx, propKeys, i); JSAtom kt = JS_ValueToAtom(ctx, k); - JSValue v = JS_GetProperty(ctx, referenceNode->m_properties, kt); - JS_SetProperty(ctx, newNode->m_properties, kt, JS_DupValue(ctx, v)); + JSValue v = JS_GetProperty(ctx, referenceNode->m_properties.value(), kt); + JS_SetProperty(ctx, newNode->m_properties.value(), kt, JS_DupValue(ctx, v)); JS_FreeAtom(ctx, kt); JS_FreeValue(ctx, k); diff --git a/bridge/bindings/qjs/dom/event_target.h b/bridge/bindings/qjs/dom/event_target.h index 6b4d028454..6e14e53562 100644 --- a/bridge/bindings/qjs/dom/event_target.h +++ b/bridge/bindings/qjs/dom/event_target.h @@ -84,9 +84,9 @@ class EventTargetInstance : public Instance { protected: int32_t m_eventTargetId; - JSValue m_eventHandlers{JS_NewObject(m_ctx)}; - JSValue m_propertyEventHandler{JS_NewObject(m_ctx)}; - JSValue m_properties{JS_NewObject(m_ctx)}; + ObjectProperty m_eventHandlers{m_context, instanceObject, "__eventHandlers", JS_NewObject(m_ctx)}; + ObjectProperty m_propertyEventHandler{m_context, instanceObject, "__propertyEventHandler", JS_NewObject(m_ctx)}; + ObjectProperty m_properties{m_context, instanceObject, "__properties", JS_NewObject(m_ctx)}; void gcMark(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) override; static void copyNodeProperties(EventTargetInstance* newNode, EventTargetInstance* referenceNode); diff --git a/bridge/bindings/qjs/js_context.h b/bridge/bindings/qjs/js_context.h index d482d9dc62..14c707fd4c 100644 --- a/bridge/bindings/qjs/js_context.h +++ b/bridge/bindings/qjs/js_context.h @@ -147,9 +147,14 @@ class ObjectProperty { JS_DefineProperty(context->ctx(), thisObject, key, JS_UNDEFINED, get, JS_UNDEFINED, JS_PROP_HAS_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_HAS_GET); JS_FreeAtom(context->ctx(), key); } - explicit ObjectProperty(JSContext* context, JSValueConst thisObject, const char* property, JSValue value) { + explicit ObjectProperty(JSContext* context, JSValueConst thisObject, const char* property, JSValue value) : m_value(value) { JS_DefinePropertyValueStr(context->ctx(), thisObject, property, value, JS_PROP_ENUMERABLE); } + + JSValue value() { return m_value; } + + private: + JSValue m_value{JS_NULL}; }; class ObjectFunction { From 182eecb2aaa74d2e7aa10bc78931e631544216d3 Mon Sep 17 00:00:00 2001 From: andycall Date: Wed, 1 Dec 2021 02:04:36 +0800 Subject: [PATCH 109/167] fix: fix document.createElement in multiple context --- bridge/bindings/qjs/dom/document.cc | 30 +++++++++++++++++------- bridge/bindings/qjs/dom/document.h | 7 ++++++ bridge/bindings/qjs/dom/document_test.cc | 29 +++++++++++++++++++++++ bridge/bindings/qjs/dom/element.cc | 18 ++++---------- bridge/bindings/qjs/dom/element.h | 5 ---- bridge/bindings/qjs/html_parser.cc | 3 ++- 6 files changed, 64 insertions(+), 28 deletions(-) diff --git a/bridge/bindings/qjs/dom/document.cc b/bridge/bindings/qjs/dom/document.cc index ad7f3399f2..dc50e42796 100644 --- a/bridge/bindings/qjs/dom/document.cc +++ b/bridge/bindings/qjs/dom/document.cc @@ -68,13 +68,13 @@ Document::Document(JSContext* context) : Node(context, "Document") { std::call_once(kDocumentInitOnceFlag, []() { JS_NewClassID(&kDocumentClassID); }); JS_SetPrototype(m_ctx, m_prototypeObject, Node::instance(m_context)->prototype()); if (!document_registered) { - Element::defineElement("img", ImageElement::instance(m_context)); - Element::defineElement("a", AnchorElement::instance(m_context)); - Element::defineElement("canvas", CanvasElement::instance(m_context)); - Element::defineElement("input", InputElement::instance(m_context)); - Element::defineElement("object", ObjectElement::instance(m_context)); - Element::defineElement("script", ScriptElement::instance(m_context)); - Element::defineElement("template", TemplateElement::instance(m_context)); + defineElement("img", ImageElement::instance(m_context)); + defineElement("a", AnchorElement::instance(m_context)); + defineElement("canvas", CanvasElement::instance(m_context)); + defineElement("input", InputElement::instance(m_context)); + defineElement("object", ObjectElement::instance(m_context)); + defineElement("script", ScriptElement::instance(m_context)); + defineElement("template", TemplateElement::instance(m_context)); document_registered = true; } @@ -164,7 +164,7 @@ JSValue Document::createElement(QjsContext* ctx, JSValue this_val, int argc, JSV auto document = static_cast(JS_GetOpaque(this_val, Document::classId())); auto* context = static_cast(JS_GetContextOpaque(ctx)); std::string tagName = jsValueToStdString(ctx, tagNameValue); - JSValue constructor = Element::getConstructor(document->m_context, tagName); + JSValue constructor = static_cast(document->prototype())->getElementConstructor(document->m_context, tagName); JSValue element = JS_CallConstructor(ctx, constructor, argc, argv); return element; @@ -286,6 +286,20 @@ JSValue Document::getElementsByClassName(QjsContext* ctx, JSValue this_val, int return array; } +void Document::defineElement(const std::string& tagName, Element* constructor) { + elementConstructorMap[tagName] = constructor; +} + +JSValue Document::getElementConstructor(JSContext* context, const std::string& tagName) { + if (elementConstructorMap.count(tagName) > 0) + return elementConstructorMap[tagName]->classObject; + return Element::instance(context)->classObject; +} + +bool Document::isCustomElement(const std::string& tagName) { + return elementConstructorMap.count(tagName) > 0; +} + PROP_GETTER(DocumentInstance, nodeName)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { return JS_NewString(ctx, "#document"); } diff --git a/bridge/bindings/qjs/dom/document.h b/bridge/bindings/qjs/dom/document.h index 41978dc579..9fc553bcee 100644 --- a/bridge/bindings/qjs/dom/document.h +++ b/bridge/bindings/qjs/dom/document.h @@ -39,7 +39,13 @@ class Document : public Node { static JSValue getElementsByTagName(QjsContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue getElementsByClassName(QjsContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + JSValue getElementConstructor(JSContext* context, const std::string& tagName); + bool isCustomElement(const std::string& tagName); + private: + + void defineElement(const std::string& tagName, Element* constructor); + ObjectFunction m_createEvent{m_context, m_prototypeObject, "createEvent", createEvent, 1}; ObjectFunction m_createElement{m_context, m_prototypeObject, "createElement", createElement, 1}; ObjectFunction m_createDocumentFragment{m_context, m_prototypeObject, "createDocumentFragment", createDocumentFragment, 0}; @@ -52,6 +58,7 @@ class Document : public Node { bool event_registered{false}; bool document_registered{false}; + std::unordered_map elementConstructorMap; }; class DocumentCookie { diff --git a/bridge/bindings/qjs/dom/document_test.cc b/bridge/bindings/qjs/dom/document_test.cc index 1d1acf9638..d6bede5fe4 100644 --- a/bridge/bindings/qjs/dom/document_test.cc +++ b/bridge/bindings/qjs/dom/document_test.cc @@ -50,3 +50,32 @@ TEST(Document, instanceofNode) { EXPECT_EQ(errorCalled, false); EXPECT_EQ(logCalled, true); } + +TEST(Document, createElementShouldWorkWithMultipleContext) { + kraken::JSBridge::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) { + }; + + kraken::JSBridge *bridge1; + + const char* code = "(() => { let img = document.createElement('img'); document.body.appendChild(img); })();"; + + { + auto* bridge = bridge1 = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) { + }); + auto& context = bridge->getContext(); + bridge->evaluateScript(code, strlen(code), "vm://", 0); + } + + { + auto* bridge = new kraken::JSBridge(1, [](int32_t contextId, const char* errmsg) { + }); + auto& context = bridge->getContext(); + const char* code = "(() => { let img = document.createElement('img'); document.body.appendChild(img); })();"; + bridge->evaluateScript(code, strlen(code), "vm://", 0); + delete bridge; + } + + bridge1->evaluateScript(code, strlen(code), "vm://", 0); + + delete bridge1; +} diff --git a/bridge/bindings/qjs/dom/element.cc b/bridge/bindings/qjs/dom/element.cc index 364befb7c2..0beb80482c 100644 --- a/bridge/bindings/qjs/dom/element.cc +++ b/bridge/bindings/qjs/dom/element.cc @@ -130,10 +130,12 @@ JSValue Element::instanceConstructor(QjsContext* ctx, JSValue func_obj, JSValue return JS_ThrowTypeError(ctx, "Illegal constructor"); } + auto *context = static_cast(JS_GetContextOpaque(ctx)); std::string name = jsValueToStdString(ctx, tagName); - if (elementConstructorMap.count(name) > 0) { - return JS_CallConstructor(ctx, elementConstructorMap[name]->classObject, argc, argv); + auto *Document = Document::instance(context); + if (Document->isCustomElement(name)) { + return JS_CallConstructor(ctx, Document->getElementConstructor(context, name), argc, argv); } ElementInstance* element; @@ -368,18 +370,6 @@ JSValue Element::scrollBy(QjsContext* ctx, JSValue this_val, int argc, JSValue* return element->callNativeMethods("scrollBy", 2, arguments); } -std::unordered_map Element::elementConstructorMap{}; - -void Element::defineElement(const std::string& tagName, Element* constructor) { - elementConstructorMap[tagName] = constructor; -} - -JSValue Element::getConstructor(JSContext* context, const std::string& tagName) { - if (elementConstructorMap.count(tagName) > 0) - return elementConstructorMap[tagName]->classObject; - return Element::instance(context)->classObject; -} - PROP_GETTER(ElementInstance, nodeName)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string tagName = element->tagName(); diff --git a/bridge/bindings/qjs/dom/element.h b/bridge/bindings/qjs/dom/element.h index 40309243c0..0bd0203782 100644 --- a/bridge/bindings/qjs/dom/element.h +++ b/bridge/bindings/qjs/dom/element.h @@ -87,11 +87,6 @@ class Element : public Node { static JSValue scroll(QjsContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue scrollBy(QjsContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static void defineElement(const std::string& tagName, Element* constructor); - static JSValue getConstructor(JSContext* context, const std::string& tagName); - - static std::unordered_map elementConstructorMap; - OBJECT_INSTANCE(Element); private: diff --git a/bridge/bindings/qjs/html_parser.cc b/bridge/bindings/qjs/html_parser.cc index e685dc3763..c346883ed1 100644 --- a/bridge/bindings/qjs/html_parser.cc +++ b/bridge/bindings/qjs/html_parser.cc @@ -36,7 +36,8 @@ void HTMLParser::traverseHTML(NodeInstance* root, GumboNode* node) { tagName = std::string(piece.data, piece.length); } - JSValue constructor = Element::getConstructor(context, tagName); + auto *Document = Document::instance(context); + JSValue constructor = Document->getElementConstructor(context, tagName); JSValue tagNameValue = JS_NewString(ctx, tagName.c_str()); JSValue argv[] = {tagNameValue}; From c6db2f1825c2e55aec3341847692f820153bf72e Mon Sep 17 00:00:00 2001 From: openkraken-bot Date: Tue, 30 Nov 2021 18:05:34 +0000 Subject: [PATCH 110/167] Committing clang-format changes --- bridge/bindings/qjs/dom/document.cc | 2 +- bridge/bindings/qjs/dom/document.h | 1 - bridge/bindings/qjs/dom/document_test.cc | 11 ++++------- bridge/bindings/qjs/dom/element.cc | 4 ++-- bridge/bindings/qjs/html_parser.cc | 2 +- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/bridge/bindings/qjs/dom/document.cc b/bridge/bindings/qjs/dom/document.cc index dc50e42796..b7f5cc0ece 100644 --- a/bridge/bindings/qjs/dom/document.cc +++ b/bridge/bindings/qjs/dom/document.cc @@ -164,7 +164,7 @@ JSValue Document::createElement(QjsContext* ctx, JSValue this_val, int argc, JSV auto document = static_cast(JS_GetOpaque(this_val, Document::classId())); auto* context = static_cast(JS_GetContextOpaque(ctx)); std::string tagName = jsValueToStdString(ctx, tagNameValue); - JSValue constructor = static_cast(document->prototype())->getElementConstructor(document->m_context, tagName); + JSValue constructor = static_cast(document->prototype())->getElementConstructor(document->m_context, tagName); JSValue element = JS_CallConstructor(ctx, constructor, argc, argv); return element; diff --git a/bridge/bindings/qjs/dom/document.h b/bridge/bindings/qjs/dom/document.h index 9fc553bcee..d4243bb25b 100644 --- a/bridge/bindings/qjs/dom/document.h +++ b/bridge/bindings/qjs/dom/document.h @@ -43,7 +43,6 @@ class Document : public Node { bool isCustomElement(const std::string& tagName); private: - void defineElement(const std::string& tagName, Element* constructor); ObjectFunction m_createEvent{m_context, m_prototypeObject, "createEvent", createEvent, 1}; diff --git a/bridge/bindings/qjs/dom/document_test.cc b/bridge/bindings/qjs/dom/document_test.cc index d6bede5fe4..2899394934 100644 --- a/bridge/bindings/qjs/dom/document_test.cc +++ b/bridge/bindings/qjs/dom/document_test.cc @@ -52,23 +52,20 @@ TEST(Document, instanceofNode) { } TEST(Document, createElementShouldWorkWithMultipleContext) { - kraken::JSBridge::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) { - }; + kraken::JSBridge::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) {}; - kraken::JSBridge *bridge1; + kraken::JSBridge* bridge1; const char* code = "(() => { let img = document.createElement('img'); document.body.appendChild(img); })();"; { - auto* bridge = bridge1 = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) { - }); + auto* bridge = bridge1 = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) {}); auto& context = bridge->getContext(); bridge->evaluateScript(code, strlen(code), "vm://", 0); } { - auto* bridge = new kraken::JSBridge(1, [](int32_t contextId, const char* errmsg) { - }); + auto* bridge = new kraken::JSBridge(1, [](int32_t contextId, const char* errmsg) {}); auto& context = bridge->getContext(); const char* code = "(() => { let img = document.createElement('img'); document.body.appendChild(img); })();"; bridge->evaluateScript(code, strlen(code), "vm://", 0); diff --git a/bridge/bindings/qjs/dom/element.cc b/bridge/bindings/qjs/dom/element.cc index 0beb80482c..8d3b3a55ef 100644 --- a/bridge/bindings/qjs/dom/element.cc +++ b/bridge/bindings/qjs/dom/element.cc @@ -130,10 +130,10 @@ JSValue Element::instanceConstructor(QjsContext* ctx, JSValue func_obj, JSValue return JS_ThrowTypeError(ctx, "Illegal constructor"); } - auto *context = static_cast(JS_GetContextOpaque(ctx)); + auto* context = static_cast(JS_GetContextOpaque(ctx)); std::string name = jsValueToStdString(ctx, tagName); - auto *Document = Document::instance(context); + auto* Document = Document::instance(context); if (Document->isCustomElement(name)) { return JS_CallConstructor(ctx, Document->getElementConstructor(context, name), argc, argv); } diff --git a/bridge/bindings/qjs/html_parser.cc b/bridge/bindings/qjs/html_parser.cc index c346883ed1..b63cce7983 100644 --- a/bridge/bindings/qjs/html_parser.cc +++ b/bridge/bindings/qjs/html_parser.cc @@ -36,7 +36,7 @@ void HTMLParser::traverseHTML(NodeInstance* root, GumboNode* node) { tagName = std::string(piece.data, piece.length); } - auto *Document = Document::instance(context); + auto* Document = Document::instance(context); JSValue constructor = Document->getElementConstructor(context, tagName); JSValue tagNameValue = JS_NewString(ctx, tagName.c_str()); From f26ab694ebad1138797897cd184ed14d203f6bae Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Wed, 1 Dec 2021 15:05:47 +0800 Subject: [PATCH 111/167] feat: obtains CachedImageKey to prevent repeat fetch files for same image url. --- kraken/lib/src/dom/elements/img.dart | 5 --- .../src/painting/cached_network_image.dart | 44 +++++++++++++++---- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 8e1f6d2626..c851b77796 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -342,11 +342,6 @@ class ImageElement extends Element { ImageProvider? provider = _imageProvider; if (propertyChanged) { - // When propertyChanges, we should release previous cached images for better memory usage. - if (provider != null) { - provider.evict(); - } - provider = _imageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); } if (provider == null) return; diff --git a/kraken/lib/src/painting/cached_network_image.dart b/kraken/lib/src/painting/cached_network_image.dart index fddaa8e39b..f401229c23 100644 --- a/kraken/lib/src/painting/cached_network_image.dart +++ b/kraken/lib/src/painting/cached_network_image.dart @@ -13,7 +13,30 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; import 'package:kraken/foundation.dart'; -class CachedNetworkImage extends ImageProvider { +class CachedNetworkImageKey { + CachedNetworkImageKey({ + required this.url, + required this.scale + }); + + final String url; + + final double scale; + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) + return false; + return other is CachedNetworkImageKey + && other.url == url + && other.scale == scale; + } + + @override + int get hashCode => hashValues(url, scale); +} + +class CachedNetworkImage extends ImageProvider { const CachedNetworkImage(this.url, {this.scale = 1.0, this.headers, this.contextId}); final String url; @@ -39,14 +62,14 @@ class CachedNetworkImage extends ImageProvider { return client; } - Future loadFile(CachedNetworkImage key, StreamController chunkEvents) async { + Future loadFile(CachedNetworkImageKey key, StreamController chunkEvents) async { HttpCacheController cacheController = HttpCacheController.instance( getOrigin(getReferrer(contextId))); Uri uri = Uri.parse(url); - HttpCacheObject? cacheObject = await cacheController.getCacheObject(uri); Uint8List? bytes; try { + HttpCacheObject? cacheObject = await cacheController.getCacheObject(uri); bytes = await cacheObject.toBinaryContent(); } catch (error, stackTrace) { print('Error while reading cache, $error\n$stackTrace'); @@ -59,7 +82,7 @@ class CachedNetworkImage extends ImageProvider { } Future _loadImage( - CachedNetworkImage key, DecoderCallback decode, StreamController chunkEvents) async { + CachedNetworkImageKey key, DecoderCallback decode, StreamController chunkEvents) async { Uint8List bytes = await loadFile(key, chunkEvents); if (bytes.isNotEmpty) { @@ -68,7 +91,7 @@ class CachedNetworkImage extends ImageProvider { return null; } - Future fetchFile(CachedNetworkImage key, + Future fetchFile(CachedNetworkImageKey key, StreamController chunkEvents, HttpCacheController cacheController) async { try { @@ -107,12 +130,15 @@ class CachedNetworkImage extends ImageProvider { } @override - Future obtainKey(ImageConfiguration configuration) { - return SynchronousFuture(this); + Future obtainKey(ImageConfiguration configuration) { + return SynchronousFuture(CachedNetworkImageKey( + url: url, + scale: scale + )); } @override - ImageStreamCompleter load(CachedNetworkImage key, DecoderCallback decode) { + ImageStreamCompleter load(CachedNetworkImageKey key, DecoderCallback decode) { // Ownership of this controller is handed off to [_loadAsync]; it is that // method's responsibility to close the controller's stream when the image // has been loaded or an error is thrown. @@ -125,7 +151,7 @@ class CachedNetworkImage extends ImageProvider { informationCollector: () { return [ DiagnosticsProperty('Image provider', this), - DiagnosticsProperty('Image key', key), + DiagnosticsProperty('Image key', key), ]; }); } From aed960017430965c6e4f602f99baa8a143f940af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Wed, 1 Dec 2021 15:06:01 +0800 Subject: [PATCH 112/167] refactor: rename RenderStyleBase to AbstractRenderStyle --- kraken/lib/src/css/animation.dart | 4 +- kraken/lib/src/css/background.dart | 7 +- kraken/lib/src/css/border.dart | 42 ++- kraken/lib/src/css/border_radius.dart | 7 +- kraken/lib/src/css/box.dart | 13 +- kraken/lib/src/css/box_shadow.dart | 3 +- kraken/lib/src/css/content_visibility.dart | 5 +- kraken/lib/src/css/display.dart | 14 +- kraken/lib/src/css/filter.dart | 14 +- kraken/lib/src/css/flexbox.dart | 31 +- kraken/lib/src/css/inline.dart | 5 +- kraken/lib/src/css/margin.dart | 11 +- kraken/lib/src/css/matrix.dart | 4 +- kraken/lib/src/css/object_fit.dart | 10 +- kraken/lib/src/css/object_position.dart | 9 +- kraken/lib/src/css/opacity.dart | 5 +- kraken/lib/src/css/overflow.dart | 12 +- kraken/lib/src/css/padding.dart | 11 +- kraken/lib/src/css/position.dart | 39 ++- kraken/lib/src/css/render_style.dart | 350 +++++++++++++++------ kraken/lib/src/css/sizing.dart | 40 ++- kraken/lib/src/css/sliver.dart | 7 +- kraken/lib/src/css/text.dart | 121 ++++--- kraken/lib/src/css/transform.dart | 6 +- kraken/lib/src/css/transition.dart | 19 +- kraken/lib/src/css/values/length.dart | 10 +- kraken/lib/src/css/visibility.dart | 3 +- kraken/lib/src/dom/element.dart | 2 +- kraken/lib/src/rendering/box_model.dart | 8 +- 29 files changed, 514 insertions(+), 298 deletions(-) diff --git a/kraken/lib/src/css/animation.dart b/kraken/lib/src/css/animation.dart index 928dc1d34e..e1d354ef30 100644 --- a/kraken/lib/src/css/animation.dart +++ b/kraken/lib/src/css/animation.dart @@ -441,7 +441,7 @@ class _Interpolation { } class KeyframeEffect extends AnimationEffect { - RenderStyle renderStyle; + AbstractRenderStyle renderStyle; Element? target; late List<_Interpolation> _interpolations; double? _progress; @@ -474,7 +474,7 @@ class KeyframeEffect extends AnimationEffect { return progress < 0.5 ? start : end; } - static List<_Interpolation> _makeInterpolations(Map> propertySpecificKeyframeGroups, RenderStyle? renderStyle) { + static List<_Interpolation> _makeInterpolations(Map> propertySpecificKeyframeGroups, AbstractRenderStyle? renderStyle) { List<_Interpolation> interpolations = []; propertySpecificKeyframeGroups.forEach((String property, List keyframes) { diff --git a/kraken/lib/src/css/background.dart b/kraken/lib/src/css/background.dart index e1fd43e19d..b8e8ea23a2 100644 --- a/kraken/lib/src/css/background.dart +++ b/kraken/lib/src/css/background.dart @@ -81,7 +81,7 @@ enum CSSBackgroundImageType { image, } -mixin CSSBackgroundMixin on RenderStyleBase { +mixin CSSBackgroundMixin on AbstractRenderStyle { static CSSBackgroundPosition DEFAULT_BACKGROUND_POSITION = CSSBackgroundPosition(percentage: -1); static CSSBackgroundSize DEFAULT_BACKGROUND_SIZE = CSSBackgroundSize(fit: BoxFit.none); @@ -103,6 +103,7 @@ mixin CSSBackgroundMixin on RenderStyleBase { renderBoxModel!.markNeedsPaint(); } + @override Color? get backgroundColor => _backgroundColor; Color? _backgroundColor; set backgroundColor(Color? value) { @@ -112,6 +113,7 @@ mixin CSSBackgroundMixin on RenderStyleBase { } /// Background-image + @override CSSBackgroundImage? get backgroundImage => _backgroundImage; CSSBackgroundImage? _backgroundImage; set backgroundImage(CSSBackgroundImage? value) { @@ -121,6 +123,7 @@ mixin CSSBackgroundMixin on RenderStyleBase { } /// Background-position-x + @override CSSBackgroundPosition get backgroundPositionX => _backgroundPositionX ?? DEFAULT_BACKGROUND_POSITION; CSSBackgroundPosition? _backgroundPositionX; set backgroundPositionX(CSSBackgroundPosition? value) { @@ -130,6 +133,7 @@ mixin CSSBackgroundMixin on RenderStyleBase { } /// Background-position-y + @override CSSBackgroundPosition get backgroundPositionY => _backgroundPositionY ?? DEFAULT_BACKGROUND_POSITION; CSSBackgroundPosition? _backgroundPositionY; set backgroundPositionY(CSSBackgroundPosition? value) { @@ -157,6 +161,7 @@ mixin CSSBackgroundMixin on RenderStyleBase { } /// Background-repeat + @override ImageRepeat get backgroundRepeat => _backgroundRepeat ?? ImageRepeat.repeat; ImageRepeat? _backgroundRepeat; set backgroundRepeat(ImageRepeat? value) { diff --git a/kraken/lib/src/css/border.dart b/kraken/lib/src/css/border.dart index 04078b940b..b8a9713113 100644 --- a/kraken/lib/src/css/border.dart +++ b/kraken/lib/src/css/border.dart @@ -24,10 +24,11 @@ enum CSSBorderStyleType { outset, } -mixin CSSBorderMixin on RenderStyleBase { +mixin CSSBorderMixin on AbstractRenderStyle { // Effective border widths. These are used to calculate the // dimensions of the border box. + @override EdgeInsets get border { // If has border, render padding should subtracting the edge of the border return EdgeInsets.fromLTRB( @@ -47,12 +48,12 @@ mixin CSSBorderMixin on RenderStyleBase { return constraints.deflate(border); } + @override List? get borderSides { - RenderStyle renderStyle = this as RenderStyle; - BorderSide? leftSide = CSSBorderSide.getBorderSide(renderStyle, CSSBorderSide.LEFT); - BorderSide? topSide = CSSBorderSide.getBorderSide(renderStyle, CSSBorderSide.TOP); - BorderSide? rightSide = CSSBorderSide.getBorderSide(renderStyle, CSSBorderSide.RIGHT); - BorderSide? bottomSide = CSSBorderSide.getBorderSide(renderStyle, CSSBorderSide.BOTTOM); + BorderSide? leftSide = CSSBorderSide._getBorderSide(this, CSSBorderSide.LEFT); + BorderSide? topSide = CSSBorderSide._getBorderSide(this, CSSBorderSide.TOP); + BorderSide? rightSide = CSSBorderSide._getBorderSide(this, CSSBorderSide.RIGHT); + BorderSide? bottomSide = CSSBorderSide._getBorderSide(this, CSSBorderSide.BOTTOM); bool hasBorder = leftSide != null || topSide != null || @@ -86,7 +87,10 @@ mixin CSSBorderMixin on RenderStyleBase { _borderTopWidth = value; renderBoxModel!.markNeedsLayout(); } + @override CSSLengthValue? get borderTopWidth => _borderTopWidth; + + @override CSSLengthValue get effectiveBorderTopWidth => borderTopStyle == BorderStyle.none ? CSSLengthValue.zero : (_borderTopWidth ?? _mediumWidth); CSSLengthValue? _borderRightWidth; @@ -95,7 +99,11 @@ mixin CSSBorderMixin on RenderStyleBase { _borderRightWidth = value; renderBoxModel!.markNeedsLayout(); } + + @override CSSLengthValue? get borderRightWidth => _borderRightWidth; + + @override CSSLengthValue get effectiveBorderRightWidth => borderRightStyle == BorderStyle.none ? CSSLengthValue.zero : (_borderRightWidth ?? _mediumWidth); CSSLengthValue? _borderBottomWidth; @@ -104,7 +112,11 @@ mixin CSSBorderMixin on RenderStyleBase { _borderBottomWidth = value; renderBoxModel!.markNeedsLayout(); } + + @override CSSLengthValue? get borderBottomWidth => _borderBottomWidth; + + @override CSSLengthValue get effectiveBorderBottomWidth => borderBottomStyle == BorderStyle.none ? CSSLengthValue.zero : (_borderBottomWidth ?? _mediumWidth); CSSLengthValue? _borderLeftWidth; @@ -113,10 +125,15 @@ mixin CSSBorderMixin on RenderStyleBase { _borderLeftWidth = value; renderBoxModel!.markNeedsLayout(); } + + @override CSSLengthValue? get borderLeftWidth => _borderLeftWidth; + + @override CSSLengthValue get effectiveBorderLeftWidth => borderLeftStyle == BorderStyle.none ? CSSLengthValue.zero : (_borderLeftWidth ?? _mediumWidth); /// Border-color + @override Color get borderTopColor => _borderTopColor ?? currentColor; Color? _borderTopColor; set borderTopColor(Color? value) { @@ -125,6 +142,7 @@ mixin CSSBorderMixin on RenderStyleBase { renderBoxModel!.markNeedsPaint(); } + @override Color get borderRightColor => _borderRightColor ?? currentColor; Color? _borderRightColor; set borderRightColor(Color? value) { @@ -133,6 +151,7 @@ mixin CSSBorderMixin on RenderStyleBase { renderBoxModel!.markNeedsPaint(); } + @override Color get borderBottomColor => _borderBottomColor ?? currentColor; Color? _borderBottomColor; set borderBottomColor(Color? value) { @@ -141,6 +160,7 @@ mixin CSSBorderMixin on RenderStyleBase { renderBoxModel!.markNeedsPaint(); } + @override Color get borderLeftColor => _borderLeftColor ?? currentColor; Color? _borderLeftColor; set borderLeftColor(Color? value) { @@ -150,6 +170,7 @@ mixin CSSBorderMixin on RenderStyleBase { } /// Border-style + @override BorderStyle get borderTopStyle => _borderTopStyle ?? BorderStyle.none; BorderStyle? _borderTopStyle; set borderTopStyle(BorderStyle? value) { @@ -158,6 +179,7 @@ mixin CSSBorderMixin on RenderStyleBase { renderBoxModel!.markNeedsPaint(); } + @override BorderStyle get borderRightStyle => _borderRightStyle ?? BorderStyle.none; BorderStyle? _borderRightStyle; set borderRightStyle(BorderStyle? value) { @@ -166,6 +188,7 @@ mixin CSSBorderMixin on RenderStyleBase { renderBoxModel!.markNeedsPaint(); } + @override BorderStyle get borderBottomStyle => _borderBottomStyle ?? BorderStyle.none; BorderStyle? _borderBottomStyle; set borderBottomStyle(BorderStyle? value) { @@ -174,8 +197,11 @@ mixin CSSBorderMixin on RenderStyleBase { renderBoxModel!.markNeedsPaint(); } - BorderStyle get borderLeftStyle => _borderLeftStyle ?? BorderStyle.none; BorderStyle? _borderLeftStyle; + + @override + BorderStyle get borderLeftStyle => _borderLeftStyle ?? BorderStyle.none; + set borderLeftStyle(BorderStyle? value) { if (value == _borderLeftStyle) return; _borderLeftStyle = value; @@ -242,7 +268,7 @@ class CSSBorderSide { static BorderSide none = BorderSide(color: defaultBorderColor, width: 0.0, style: BorderStyle.none); - static BorderSide? getBorderSide(RenderStyle renderStyle, String side) { + static BorderSide? _getBorderSide(AbstractRenderStyle renderStyle, String side) { BorderStyle? borderStyle; CSSLengthValue? borderWidth; Color? borderColor; diff --git a/kraken/lib/src/css/border_radius.dart b/kraken/lib/src/css/border_radius.dart index 56a95ecc22..c9ab1c2fcd 100644 --- a/kraken/lib/src/css/border_radius.dart +++ b/kraken/lib/src/css/border_radius.dart @@ -2,13 +2,14 @@ import 'dart:ui'; import 'package:kraken/css.dart'; -mixin CSSBorderRadiusMixin on RenderStyleBase { +mixin CSSBorderRadiusMixin on AbstractRenderStyle { CSSBorderRadius? _borderTopLeftRadius; set borderTopLeftRadius(CSSBorderRadius? value) { if (value == _borderTopLeftRadius) return; _borderTopLeftRadius = value; renderBoxModel!.markNeedsPaint(); } + @override CSSBorderRadius get borderTopLeftRadius => _borderTopLeftRadius ?? CSSBorderRadius.zero; CSSBorderRadius? _borderTopRightRadius; @@ -17,6 +18,7 @@ mixin CSSBorderRadiusMixin on RenderStyleBase { _borderTopRightRadius = value; renderBoxModel!.markNeedsPaint(); } + @override CSSBorderRadius get borderTopRightRadius => _borderTopRightRadius ?? CSSBorderRadius.zero; CSSBorderRadius? _borderBottomRightRadius; @@ -25,6 +27,7 @@ mixin CSSBorderRadiusMixin on RenderStyleBase { _borderBottomRightRadius = value; renderBoxModel!.markNeedsPaint(); } + @override CSSBorderRadius get borderBottomRightRadius => _borderBottomRightRadius ?? CSSBorderRadius.zero; CSSBorderRadius? _borderBottomLeftRadius; @@ -33,8 +36,10 @@ mixin CSSBorderRadiusMixin on RenderStyleBase { _borderBottomLeftRadius = value; renderBoxModel!.markNeedsPaint(); } + @override CSSBorderRadius get borderBottomLeftRadius => _borderBottomLeftRadius ?? CSSBorderRadius.zero; + @override List? get borderRadius { bool hasBorderRadius = borderTopLeftRadius != CSSBorderRadius.zero || borderTopRightRadius != CSSBorderRadius.zero || diff --git a/kraken/lib/src/css/box.dart b/kraken/lib/src/css/box.dart index 585a5ee628..269ef1d492 100644 --- a/kraken/lib/src/css/box.dart +++ b/kraken/lib/src/css/box.dart @@ -11,21 +11,16 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; // CSS Box Model: https://drafts.csswg.org/css-box-4/ -mixin CSSBoxMixin on RenderStyleBase { +mixin CSSBoxMixin on AbstractRenderStyle { final DecorationPosition decorationPosition = DecorationPosition.background; final ImageConfiguration imageConfiguration = ImageConfiguration.empty; /// What decoration to paint, should get value after layout. CSSBoxDecoration? get decoration { - RenderStyle renderStyle = this as RenderStyle; - List? radius = renderStyle.borderRadius; - List? borderSides = renderStyle.borderSides; - List? shadows = renderStyle.shadows; - var backgroundColor = renderStyle.backgroundColor; - var backgroundImage = renderStyle.backgroundImage; - var backgroundRepeat = renderStyle.backgroundRepeat; - + List? radius = this.borderRadius; + List? borderSides = this.borderSides; + if (backgroundColor == null && backgroundImage == null && borderSides == null && diff --git a/kraken/lib/src/css/box_shadow.dart b/kraken/lib/src/css/box_shadow.dart index f6943ca1ad..060fb77356 100644 --- a/kraken/lib/src/css/box_shadow.dart +++ b/kraken/lib/src/css/box_shadow.dart @@ -1,6 +1,6 @@ import 'package:kraken/css.dart'; -mixin CSSBoxShadowMixin on RenderStyleBase { +mixin CSSBoxShadowMixin on AbstractRenderStyle { List? _boxShadow; set boxShadow(List? value) { if (value == _boxShadow) return; @@ -9,6 +9,7 @@ mixin CSSBoxShadowMixin on RenderStyleBase { } List? get boxShadow => _boxShadow; + @override List? get shadows { if (boxShadow == null) { return null; diff --git a/kraken/lib/src/css/content_visibility.dart b/kraken/lib/src/css/content_visibility.dart index d5c582ef1d..44dfc7e5ce 100644 --- a/kraken/lib/src/css/content_visibility.dart +++ b/kraken/lib/src/css/content_visibility.dart @@ -13,7 +13,7 @@ enum ContentVisibility { visible } -mixin CSSContentVisibilityMixin on RenderStyleBase { +mixin CSSContentVisibilityMixin on AbstractRenderStyle { /// Whether the child is hidden from the rest of the tree. /// @@ -25,8 +25,9 @@ mixin CSSContentVisibilityMixin on RenderStyleBase { /// /// If ContentVisibility.auto, the framework will compute the intersection bounds and not to paint when child renderObject /// are no longer intersection with this renderObject. - ContentVisibility? _contentVisibility; + @override ContentVisibility? get contentVisibility => _contentVisibility; + ContentVisibility? _contentVisibility; set contentVisibility(ContentVisibility? value) { if (value == null) return; if (value == _contentVisibility) return; diff --git a/kraken/lib/src/css/display.dart b/kraken/lib/src/css/display.dart index c8ae1e6d0e..785052df26 100644 --- a/kraken/lib/src/css/display.dart +++ b/kraken/lib/src/css/display.dart @@ -19,9 +19,11 @@ enum CSSDisplay { none } -mixin CSSDisplayMixin on RenderStyleBase { +mixin CSSDisplayMixin on AbstractRenderStyle { CSSDisplay? _display; + + @override CSSDisplay get display => _display ?? CSSDisplay.inline; set display(CSSDisplay value) { if (_display != value) { @@ -59,12 +61,9 @@ mixin CSSDisplayMixin on RenderStyleBase { /// Some layout effects require blockification or inlinification of the box type /// https://www.w3.org/TR/css-display-3/#transformations CSSDisplay? get effectiveDisplay { - RenderStyle renderStyle = this as RenderStyle; - CSSDisplay? transformedDisplay = renderStyle.display; - - // Must take from style because it inited before flush pending properties. - CSSPositionType position = renderStyle.position; + CSSDisplay? transformedDisplay = display; + // Must take `position` from style because it inited before flush pending properties. // Display as inline-block when element is positioned if (position == CSSPositionType.absolute || position == CSSPositionType.fixed) { return CSSDisplay.inlineBlock; @@ -82,9 +81,6 @@ mixin CSSDisplayMixin on RenderStyleBase { RenderBoxModel parent = renderBoxModel!.parent as RenderBoxModel; RenderStyle parentRenderStyle = parent.renderStyle; - CSSLengthValue marginLeft = renderStyle.marginLeft; - CSSLengthValue marginRight = renderStyle.marginRight; - bool isVerticalDirection = parentRenderStyle.flexDirection == FlexDirection.column || parentRenderStyle.flexDirection == FlexDirection.columnReverse; // Flex item will not stretch in stretch alignment when flex wrap is set to wrap or wrap-reverse diff --git a/kraken/lib/src/css/filter.dart b/kraken/lib/src/css/filter.dart index e396be3add..1f6726d00d 100644 --- a/kraken/lib/src/css/filter.dart +++ b/kraken/lib/src/css/filter.dart @@ -91,7 +91,7 @@ List _multiplyMatrix5(List? a, List b) { /// Impl W3C Filter Effects Spec: /// https://www.w3.org/TR/filter-effects-1/#definitions -mixin CSSFilterEffectsMixin on RenderStyleBase { +mixin CSSFilterEffectsMixin on AbstractRenderStyle { // Get the color filter. // eg: 'grayscale(1) grayscale(0.5)' -> matrix5(grayscale(1)) · matrix5(grayscale(0.5)) @@ -138,13 +138,12 @@ mixin CSSFilterEffectsMixin on RenderStyleBase { // Get the image filter. ImageFilter? _parseImageFilters(List functions) { - RenderStyle renderStyle = this as RenderStyle; if (functions.isNotEmpty) { for (int i = 0; i < functions.length; i ++) { CSSFunctionalNotation f = functions[i]; switch (f.name.toLowerCase()) { case BLUR: - CSSLengthValue length = CSSLength.parseLength(f.args.first, renderStyle, FILTER); + CSSLengthValue length = CSSLength.parseLength(f.args.first, this, FILTER); double amount = length.computedValue; ImageFilter imageFilter = ImageFilter.blur(sigmaX: amount, sigmaY: amount); // Only length is not relative value will cached the image filter. @@ -159,6 +158,8 @@ mixin CSSFilterEffectsMixin on RenderStyleBase { } ColorFilter? _cachedColorFilter; + + @override ColorFilter? get colorFilter { if (_filter == null) { return null; @@ -170,6 +171,8 @@ mixin CSSFilterEffectsMixin on RenderStyleBase { } ImageFilter? _cachedImageFilter; + + @override ImageFilter? get imageFilter { if (_filter == null) { return null; @@ -180,8 +183,9 @@ mixin CSSFilterEffectsMixin on RenderStyleBase { } } - List? _filter; + @override List? get filter => _filter; + List? _filter; set filter(List? functions) { _filter = functions; // Clear cache when filter changed. @@ -189,7 +193,7 @@ mixin CSSFilterEffectsMixin on RenderStyleBase { _cachedImageFilter = null; // Filter effect the stacking context. - RenderBoxModel? parentRenderer = (this as RenderStyle).parent?.renderBoxModel; + RenderBoxModel? parentRenderer = parent?.renderBoxModel; if (parentRenderer is RenderLayoutBox) { parentRenderer.markChildrenNeedsSort(); } diff --git a/kraken/lib/src/css/flexbox.dart b/kraken/lib/src/css/flexbox.dart index db65e30e61..9bca5a81c3 100644 --- a/kraken/lib/src/css/flexbox.dart +++ b/kraken/lib/src/css/flexbox.dart @@ -173,10 +173,11 @@ enum AlignSelf { baseline } -mixin CSSFlexboxMixin on RenderStyleBase { +mixin CSSFlexboxMixin on AbstractRenderStyle { - FlexDirection? _flexDirection; + @override FlexDirection get flexDirection => _flexDirection ?? FlexDirection.row; + FlexDirection? _flexDirection; set flexDirection(FlexDirection? value) { if (value == _flexDirection) return; _flexDirection = value; @@ -185,8 +186,9 @@ mixin CSSFlexboxMixin on RenderStyleBase { } } - FlexWrap? _flexWrap; + @override FlexWrap get flexWrap => _flexWrap ?? FlexWrap.nowrap; + FlexWrap? _flexWrap; set flexWrap(FlexWrap? value) { if (_flexWrap == value) return; _flexWrap = value; @@ -195,9 +197,9 @@ mixin CSSFlexboxMixin on RenderStyleBase { } } - - JustifyContent? _justifyContent; + @override JustifyContent get justifyContent => _justifyContent ?? JustifyContent.flexStart; + JustifyContent? _justifyContent; set justifyContent(JustifyContent? value) { if (_justifyContent == value) return; _justifyContent = value; @@ -207,8 +209,9 @@ mixin CSSFlexboxMixin on RenderStyleBase { } - AlignItems? _alignItems; + @override AlignItems get alignItems => _alignItems ?? AlignItems.stretch; + AlignItems? _alignItems; set alignItems(AlignItems? value) { if (_alignItems == value) return; _alignItems = value; @@ -219,7 +222,6 @@ mixin CSSFlexboxMixin on RenderStyleBase { AlignItems get effectiveAlignItems { if (CSSFlex.isVerticalFlexDirection(flexDirection)) { - TextAlign textAlign = (this as RenderStyle).textAlign; if (textAlign == TextAlign.right) { return AlignItems.flexEnd; } else if (textAlign == TextAlign.center) { @@ -229,8 +231,9 @@ mixin CSSFlexboxMixin on RenderStyleBase { return alignItems; } - AlignContent? _alignContent; + @override AlignContent get alignContent => _alignContent ?? AlignContent.stretch; + AlignContent? _alignContent; set alignContent(AlignContent? value) { if (_alignContent == value) return; _alignContent = value; @@ -239,8 +242,9 @@ mixin CSSFlexboxMixin on RenderStyleBase { } } - AlignSelf? _alignSelf; + @override AlignSelf get alignSelf => _alignSelf ?? AlignSelf.auto; + AlignSelf? _alignSelf; set alignSelf(AlignSelf value) { if (_alignSelf == value) return; _alignSelf = value; @@ -249,8 +253,9 @@ mixin CSSFlexboxMixin on RenderStyleBase { } } - CSSLengthValue? _flexBasis; + @override CSSLengthValue? get flexBasis => _flexBasis; + CSSLengthValue? _flexBasis; set flexBasis(CSSLengthValue? value) { // Negative value is invalid. if ((value != null && ((value.value != null && value.value! < 0))) || @@ -264,8 +269,9 @@ mixin CSSFlexboxMixin on RenderStyleBase { } } - double? _flexGrow; + @override double get flexGrow => _flexGrow ?? 0.0; + double? _flexGrow; set flexGrow(double? value) { if (_flexGrow == value) return; _flexGrow = value; @@ -274,8 +280,9 @@ mixin CSSFlexboxMixin on RenderStyleBase { } } - double? _flexShrink; + @override double get flexShrink => _flexShrink ?? 1.0; + double? _flexShrink; set flexShrink(double? value) { if (_flexShrink == value) return; _flexShrink = value; diff --git a/kraken/lib/src/css/inline.dart b/kraken/lib/src/css/inline.dart index 456ca25fb8..4a5b033ff2 100644 --- a/kraken/lib/src/css/inline.dart +++ b/kraken/lib/src/css/inline.dart @@ -24,9 +24,10 @@ enum VerticalAlign { /// middle, } -mixin CSSInlineMixin on RenderStyleBase { - VerticalAlign _verticalAlign = VerticalAlign.baseline; +mixin CSSInlineMixin on AbstractRenderStyle { + @override VerticalAlign get verticalAlign => _verticalAlign; + VerticalAlign _verticalAlign = VerticalAlign.baseline; set verticalAlign(VerticalAlign value) { if (_verticalAlign != value) { renderBoxModel!.markNeedsLayout(); diff --git a/kraken/lib/src/css/margin.dart b/kraken/lib/src/css/margin.dart index 339cc5df8f..98e2f28113 100644 --- a/kraken/lib/src/css/margin.dart +++ b/kraken/lib/src/css/margin.dart @@ -9,12 +9,13 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; import 'package:kraken/rendering.dart'; -mixin CSSMarginMixin on RenderStyleBase { +mixin CSSMarginMixin on AbstractRenderStyle { /// The amount to margin the child in each dimension. /// /// If this is set to an [EdgeInsetsDirectional] object, then [textDirection] /// must not be null. + @override EdgeInsets get margin { EdgeInsets insets = EdgeInsets.only( left: marginLeft.computedValue, @@ -32,6 +33,8 @@ mixin CSSMarginMixin on RenderStyleBase { _marginLeft = value; _markSelfAndParentNeedsLayout(); } + + @override CSSLengthValue get marginLeft => _marginLeft ?? CSSLengthValue.zero; CSSLengthValue? _marginRight; @@ -40,6 +43,8 @@ mixin CSSMarginMixin on RenderStyleBase { _marginRight = value; _markSelfAndParentNeedsLayout(); } + + @override CSSLengthValue get marginRight => _marginRight ?? CSSLengthValue.zero; CSSLengthValue? _marginBottom; @@ -48,6 +53,8 @@ mixin CSSMarginMixin on RenderStyleBase { _marginBottom = value; _markSelfAndParentNeedsLayout(); } + + @override CSSLengthValue get marginBottom => _marginBottom ?? CSSLengthValue.zero; CSSLengthValue? _marginTop; @@ -56,6 +63,8 @@ mixin CSSMarginMixin on RenderStyleBase { _marginTop = value; _markSelfAndParentNeedsLayout(); } + + @override CSSLengthValue get marginTop => _marginTop ?? CSSLengthValue.zero; void _markSelfAndParentNeedsLayout() { diff --git a/kraken/lib/src/css/matrix.dart b/kraken/lib/src/css/matrix.dart index e6895b6d7c..2f8e2d5d57 100644 --- a/kraken/lib/src/css/matrix.dart +++ b/kraken/lib/src/css/matrix.dart @@ -577,7 +577,7 @@ class CSSMatrix { static Matrix4 initial = Matrix4.identity(); - static Matrix4? computeTransformMatrix(List transform, RenderStyle renderStyle) { + static Matrix4? computeTransformMatrix(List transform, AbstractRenderStyle renderStyle) { Matrix4? matrix4; for (CSSFunctionalNotation method in transform) { Matrix4? transform = _computeMatrix(method, renderStyle); @@ -592,7 +592,7 @@ class CSSMatrix { return matrix4; } - static Matrix4? _computeMatrix(CSSFunctionalNotation method, RenderStyle renderStyle) { + static Matrix4? _computeMatrix(CSSFunctionalNotation method, AbstractRenderStyle renderStyle) { switch (method.name) { case MATRIX: if (method.args.length == 6) { diff --git a/kraken/lib/src/css/object_fit.dart b/kraken/lib/src/css/object_fit.dart index f2f9fd5dd7..34fa5e67f2 100644 --- a/kraken/lib/src/css/object_fit.dart +++ b/kraken/lib/src/css/object_fit.dart @@ -6,15 +6,15 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; -mixin CSSObjectFitMixin on RenderStyleBase { +mixin CSSObjectFitMixin on AbstractRenderStyle { + + @override + BoxFit get objectFit => _objectFit; BoxFit _objectFit = BoxFit.fill; - BoxFit get objectFit { - return _objectFit; - } set objectFit(BoxFit value) { if (_objectFit == value) return; _objectFit = value; - renderBoxModel!.markNeedsLayout(); + renderBoxModel?.markNeedsLayout(); } static BoxFit resolveBoxFit(String fit) { diff --git a/kraken/lib/src/css/object_position.dart b/kraken/lib/src/css/object_position.dart index 87cce4cc65..6c33f486c0 100644 --- a/kraken/lib/src/css/object_position.dart +++ b/kraken/lib/src/css/object_position.dart @@ -6,15 +6,14 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; -mixin CSSObjectPositionMixin on RenderStyleBase { +mixin CSSObjectPositionMixin on AbstractRenderStyle { + @override + Alignment get objectPosition => _objectPosition; Alignment _objectPosition = Alignment.center; - Alignment get objectPosition { - return _objectPosition; - } set objectPosition(Alignment value) { if (_objectPosition == value) return; _objectPosition = value; - renderBoxModel!.markNeedsLayout(); + renderBoxModel?.markNeedsLayout(); } static Alignment resolveObjectPosition(String? position) { diff --git a/kraken/lib/src/css/opacity.dart b/kraken/lib/src/css/opacity.dart index a08cfdab6e..88fb74758e 100644 --- a/kraken/lib/src/css/opacity.dart +++ b/kraken/lib/src/css/opacity.dart @@ -8,7 +8,7 @@ import 'dart:ui' as ui; import 'package:kraken/css.dart'; import 'package:kraken/rendering.dart'; -mixin CSSOpacityMixin on RenderStyleBase { +mixin CSSOpacityMixin on AbstractRenderStyle { /// The fraction to scale the child's alpha value. /// @@ -20,6 +20,7 @@ mixin CSSOpacityMixin on RenderStyleBase { /// Values 1.0 and 0.0 are painted with a fast path. Other values /// require painting the child into an intermediate buffer, which is /// expensive. + @override double get opacity => _opacity; double _opacity = 1.0; set opacity(double? value) { @@ -34,7 +35,7 @@ mixin CSSOpacityMixin on RenderStyleBase { renderBoxModel!.markNeedsCompositingBitsUpdate(); // Opacity effect the stacking context. - RenderBoxModel? parentRenderer = (this as RenderStyle).parent?.renderBoxModel; + RenderBoxModel? parentRenderer = parent?.renderBoxModel; if (parentRenderer is RenderLayoutBox) { parentRenderer.markChildrenNeedsSort(); } diff --git a/kraken/lib/src/css/overflow.dart b/kraken/lib/src/css/overflow.dart index a327a70e2e..11eb2b9a51 100644 --- a/kraken/lib/src/css/overflow.dart +++ b/kraken/lib/src/css/overflow.dart @@ -48,20 +48,18 @@ List _scrollingContentBoxCopyStyles = [ LINE_CLAMP, ]; -mixin CSSOverflowMixin on RenderStyleBase { +mixin CSSOverflowMixin on AbstractRenderStyle { + @override + CSSOverflowType get overflowX => _overflowX ?? CSSOverflowType.visible; CSSOverflowType? _overflowX; - CSSOverflowType get overflowX { - return _overflowX ?? CSSOverflowType.visible; - } set overflowX(CSSOverflowType value) { if (_overflowX == value) return; _overflowX = value; } + @override + CSSOverflowType get overflowY => _overflowY ?? CSSOverflowType.visible; CSSOverflowType? _overflowY; - CSSOverflowType get overflowY { - return _overflowY ?? CSSOverflowType.visible; - } set overflowY(CSSOverflowType value) { if (_overflowY == value) return; _overflowY = value; diff --git a/kraken/lib/src/css/padding.dart b/kraken/lib/src/css/padding.dart index 7d5ea4df7b..2f27a11c38 100644 --- a/kraken/lib/src/css/padding.dart +++ b/kraken/lib/src/css/padding.dart @@ -9,11 +9,12 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; import 'package:kraken/rendering.dart'; -mixin CSSPaddingMixin on RenderStyleBase { +mixin CSSPaddingMixin on AbstractRenderStyle { /// The amount to pad the child in each dimension. /// /// If this is set to an [EdgeInsetsDirectional] object, then [textDirection] /// must not be null. + @override EdgeInsets get padding { EdgeInsets insets = EdgeInsets.only( left: paddingLeft.computedValue, @@ -31,6 +32,8 @@ mixin CSSPaddingMixin on RenderStyleBase { _paddingLeft = value; _markSelfAndParentNeedsLayout(); } + + @override CSSLengthValue get paddingLeft => _paddingLeft ?? CSSLengthValue.zero; CSSLengthValue? _paddingRight; @@ -39,6 +42,8 @@ mixin CSSPaddingMixin on RenderStyleBase { _paddingRight = value; _markSelfAndParentNeedsLayout(); } + + @override CSSLengthValue get paddingRight => _paddingRight ?? CSSLengthValue.zero; CSSLengthValue? _paddingBottom; @@ -47,6 +52,8 @@ mixin CSSPaddingMixin on RenderStyleBase { _paddingBottom = value; _markSelfAndParentNeedsLayout(); } + + @override CSSLengthValue get paddingBottom => _paddingBottom ?? CSSLengthValue.zero; CSSLengthValue? _paddingTop; @@ -55,6 +62,8 @@ mixin CSSPaddingMixin on RenderStyleBase { _paddingTop = value; _markSelfAndParentNeedsLayout(); } + + @override CSSLengthValue get paddingTop => _paddingTop ?? CSSLengthValue.zero; void _markSelfAndParentNeedsLayout() { diff --git a/kraken/lib/src/css/position.dart b/kraken/lib/src/css/position.dart index 0a5c153cd5..0fa61985a4 100644 --- a/kraken/lib/src/css/position.dart +++ b/kraken/lib/src/css/position.dart @@ -14,7 +14,7 @@ enum CSSPositionType { sticky, } -mixin CSSPositionMixin on RenderStyleBase { +mixin CSSPositionMixin on AbstractRenderStyle { static const CSSPositionType DEFAULT_POSITION_TYPE = CSSPositionType.static; @@ -28,10 +28,9 @@ mixin CSSPositionMixin on RenderStyleBase { // Computed value: the keyword auto or a computed value // Canonical order: per grammar // Animation type: by computed value type + @override + CSSLengthValue get top => _top ?? CSSLengthValue.auto; CSSLengthValue? _top; - CSSLengthValue get top { - return _top ?? CSSLengthValue.auto; - } set top(CSSLengthValue? value) { if (_top == value) { return; @@ -40,10 +39,9 @@ mixin CSSPositionMixin on RenderStyleBase { _markParentNeedsLayout(); } + @override + CSSLengthValue get bottom => _bottom ?? CSSLengthValue.auto; CSSLengthValue? _bottom; - CSSLengthValue get bottom { - return _bottom ?? CSSLengthValue.auto; - } set bottom(CSSLengthValue? value) { if (_bottom == value) { return; @@ -52,10 +50,9 @@ mixin CSSPositionMixin on RenderStyleBase { _markParentNeedsLayout(); } + @override + CSSLengthValue get left => _left ?? CSSLengthValue.auto; CSSLengthValue? _left; - CSSLengthValue get left { - return _left ?? CSSLengthValue.auto; - } set left(CSSLengthValue? value) { if (_left == value) { return; @@ -64,10 +61,9 @@ mixin CSSPositionMixin on RenderStyleBase { _markParentNeedsLayout(); } + @override + CSSLengthValue get right => _right ?? CSSLengthValue.auto; CSSLengthValue? _right; - CSSLengthValue get right { - return _right ?? CSSLengthValue.auto; - } set right(CSSLengthValue? value) { if (_right == value) { return; @@ -78,9 +74,10 @@ mixin CSSPositionMixin on RenderStyleBase { // The z-index property specifies the stack order of an element. // Only works on positioned elements(position: absolute/relative/fixed). int? _zIndex; - int? get zIndex { - return _zIndex; - } + + @override + int? get zIndex => _zIndex; + set zIndex(int? value) { if (_zIndex == value) return; _zIndex = value; @@ -89,13 +86,14 @@ mixin CSSPositionMixin on RenderStyleBase { } CSSPositionType _position = DEFAULT_POSITION_TYPE; - CSSPositionType get position { - return _position; - } + + @override + CSSPositionType get position => _position; + set position(CSSPositionType value) { if (_position == value) return; _position = value; - + // Position effect the stacking context. _markNeedsSort(); _markParentNeedsLayout(); @@ -154,5 +152,4 @@ mixin CSSPositionMixin on RenderStyleBase { return CSSPositionType.static; } } - } diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 05f146f28c..18152a786a 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -11,19 +11,158 @@ import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/rendering.dart'; -mixin RenderStyleBase { +/// The abstract class for render-style, declare the +/// getter interface for all available CSS rule. +abstract class AbstractRenderStyle { + // Common + Element get target; + AbstractRenderStyle? get parent; + getProperty(String key); + + // Geometry + CSSLengthValue get top; + CSSLengthValue get right; + CSSLengthValue get bottom; + CSSLengthValue get left; + int? get zIndex; + CSSLengthValue get width; + CSSLengthValue get height; + CSSLengthValue get minWidth; + CSSLengthValue get minHeight; + CSSLengthValue get maxWidth; + CSSLengthValue get maxHeight; + EdgeInsets get margin; + CSSLengthValue get marginLeft; + CSSLengthValue get marginRight; + CSSLengthValue get marginTop; + CSSLengthValue get marginBottom; + EdgeInsets get padding; + CSSLengthValue get paddingLeft; + CSSLengthValue get paddingRight; + CSSLengthValue get paddingBottom; + CSSLengthValue get paddingTop; + + // Border + EdgeInsets get border; + CSSLengthValue? get borderTopWidth; + CSSLengthValue? get borderRightWidth; + CSSLengthValue? get borderBottomWidth; + CSSLengthValue? get borderLeftWidth; + BorderStyle get borderLeftStyle; + BorderStyle get borderRightStyle; + BorderStyle get borderTopStyle; + BorderStyle get borderBottomStyle; + CSSLengthValue get effectiveBorderLeftWidth; + CSSLengthValue get effectiveBorderRightWidth; + CSSLengthValue get effectiveBorderTopWidth; + CSSLengthValue get effectiveBorderBottomWidth; + double? get logicalContentWidth; + double? get logicalContentHeight; + double get contentMaxConstraintsWidth; + Color get borderLeftColor; + Color get borderRightColor; + Color get borderTopColor; + Color get borderBottomColor; + List? get borderRadius; + CSSBorderRadius get borderTopLeftRadius; + CSSBorderRadius get borderTopRightRadius; + CSSBorderRadius get borderBottomRightRadius; + CSSBorderRadius get borderBottomLeftRadius; + List? get borderSides; + List? get shadows; + + // Decorations + Color? get backgroundColor; + CSSBackgroundImage? get backgroundImage; + ImageRepeat get backgroundRepeat; + CSSBackgroundPosition get backgroundPositionX; + CSSBackgroundPosition get backgroundPositionY; + + // Text + CSSLengthValue get fontSize; + FontWeight get fontWeight; + FontStyle get fontStyle; + List? get fontFamily; + List? get textShadow; + WhiteSpace get whiteSpace; + TextOverflow get textOverflow; + TextAlign get textAlign; + int? get lineClamp; + CSSLengthValue get lineHeight; + CSSLengthValue? get letterSpacing; + CSSLengthValue? get wordSpacing; + + // BoxModel + double? get borderBoxLogicalWidth; + double? get borderBoxLogicalHeight; + double? get borderBoxWidth; + double? get borderBoxHeight; + double? get paddingBoxLogicalWidth; + double? get paddingBoxLogicalHeight; + double? get paddingBoxWidth; + double? get paddingBoxHeight; + double? get contentBoxLogicalWidth; + double? get contentBoxLogicalHeight; + double? get contentBoxWidth; + double? get contentBoxHeight; + CSSPositionType get position; + CSSDisplay get display; + Alignment get objectPosition; + CSSOverflowType get overflowX; + CSSOverflowType get overflowY; + + // Flex + FlexDirection get flexDirection; + FlexWrap get flexWrap; + JustifyContent get justifyContent; + AlignItems get alignItems; + AlignContent get alignContent; + AlignSelf get alignSelf; + CSSLengthValue? get flexBasis; + double get flexGrow; + double get flexShrink; + + // Color + Color get color; + Color get currentColor; + + // Filter + ColorFilter? get colorFilter; + ImageFilter? get imageFilter; + List? get filter; + + // Misc + double get opacity; + Visibility get visibility; + ContentVisibility? get contentVisibility; + VerticalAlign get verticalAlign; + BoxFit get objectFit; + + // Transition + List get transitionProperty; + List get transitionDuration; + List get transitionTimingFunction; + List get transitionDelay; + + // Sliver + Axis get sliverDirection; + + void addFontRelativeProperty(String propertyName); + void addRootFontRelativeProperty(String propertyName); + String? removeAnimationProperty(String propertyName); + // Following properties used for exposing APIs - // for class that extends [RenderStyleBase]. - late Element target; + // for class that extends [AbstractRenderStyle]. RenderBoxModel? get renderBoxModel => target.renderBoxModel; + Size get viewportSize => target.elementManager.viewport.viewportSize; + double get rootFontSize => target.elementManager.getRootFontSize(); - Color get currentColor => (this as RenderStyle).color; } class RenderStyle + extends AbstractRenderStyle with - RenderStyleBase, CSSSizingMixin, CSSPaddingMixin, CSSBorderMixin, @@ -47,200 +186,199 @@ class RenderStyle CSSFilterEffectsMixin, CSSOpacityMixin, CSSTransitionMixin { + RenderStyle({ required this.target }); @override Element target; + @override RenderStyle? parent; - RenderStyle({ - required this.target, - }); - - dynamic getProperty(String name) { - RenderStyle renderStyle = this; + @override + getProperty(String name) { switch (name) { case DISPLAY: - return renderStyle.display; + return display; case Z_INDEX: - return renderStyle.zIndex; + return zIndex; case OVERFLOW_X: - return renderStyle.overflowX; + return overflowX; case OVERFLOW_Y: - return renderStyle.overflowY; + return overflowY; case OPACITY: - return renderStyle.opacity; + return opacity; case VISIBILITY: - return renderStyle.visibility; + return visibility; case CONTENT_VISIBILITY: - return renderStyle.contentVisibility; + return contentVisibility; case POSITION: - return renderStyle.position; + return position; case TOP: - return renderStyle.top; + return top; case LEFT: - return renderStyle.left; + return left; case BOTTOM: - return renderStyle.bottom; + return bottom; case RIGHT: - return renderStyle.right; + return right; // Size case WIDTH: - return renderStyle.width; + return width; case MIN_WIDTH: - return renderStyle.minWidth; + return minWidth; case MAX_WIDTH: - return renderStyle.maxWidth; + return maxWidth; case HEIGHT: - return renderStyle.height; + return height; case MIN_HEIGHT: - return renderStyle.minHeight; + return minHeight; case MAX_HEIGHT: - return renderStyle.maxHeight; + return maxHeight; // Flex case FLEX_DIRECTION: - return renderStyle.flexDirection; + return flexDirection; case FLEX_WRAP: - return renderStyle.flexWrap; + return flexWrap; case ALIGN_CONTENT: - return renderStyle.alignContent; + return alignContent; case ALIGN_ITEMS: - return renderStyle.alignItems; + return alignItems; case JUSTIFY_CONTENT: - return renderStyle.justifyContent; + return justifyContent; case ALIGN_SELF: - return renderStyle.alignSelf; + return alignSelf; case FLEX_GROW: - return renderStyle.flexGrow; + return flexGrow; case FLEX_SHRINK: - return renderStyle.flexShrink; + return flexShrink; case FLEX_BASIS: - return renderStyle.flexBasis; + return flexBasis; // Background case BACKGROUND_COLOR: - return renderStyle.backgroundColor; + return backgroundColor; case BACKGROUND_ATTACHMENT: - return renderStyle.backgroundAttachment; + return backgroundAttachment; case BACKGROUND_IMAGE: - return renderStyle.backgroundImage; + return backgroundImage; case BACKGROUND_REPEAT: - return renderStyle.backgroundRepeat; + return backgroundRepeat; case BACKGROUND_POSITION_X: - return renderStyle.backgroundPositionX; + return backgroundPositionX; case BACKGROUND_POSITION_Y: - return renderStyle.backgroundPositionY; + return backgroundPositionY; case BACKGROUND_SIZE: - return renderStyle.backgroundSize; + return backgroundSize; case BACKGROUND_CLIP: - return renderStyle.backgroundClip; + return backgroundClip; case BACKGROUND_ORIGIN: - return renderStyle.backgroundOrigin; + return backgroundOrigin; // Padding case PADDING_TOP: - return renderStyle.paddingTop; + return paddingTop; case PADDING_RIGHT: - return renderStyle.paddingRight; + return paddingRight; case PADDING_BOTTOM: - return renderStyle.paddingBottom; + return paddingBottom; case PADDING_LEFT: - return renderStyle.paddingLeft; + return paddingLeft; // Border case BORDER_LEFT_WIDTH: - return renderStyle.borderLeftWidth; + return borderLeftWidth; case BORDER_TOP_WIDTH: - return renderStyle.borderTopWidth; + return borderTopWidth; case BORDER_RIGHT_WIDTH: - return renderStyle.borderRightWidth; + return borderRightWidth; case BORDER_BOTTOM_WIDTH: - return renderStyle.borderBottomWidth; + return borderBottomWidth; case BORDER_LEFT_STYLE: - return renderStyle.borderLeftStyle; + return borderLeftStyle; case BORDER_TOP_STYLE: - return renderStyle.borderTopStyle; + return borderTopStyle; case BORDER_RIGHT_STYLE: - return renderStyle.borderRightStyle; + return borderRightStyle; case BORDER_BOTTOM_STYLE: - return renderStyle.borderBottomStyle; + return borderBottomStyle; case BORDER_LEFT_COLOR: - return renderStyle.borderLeftColor; + return borderLeftColor; case BORDER_TOP_COLOR: - return renderStyle.borderTopColor; + return borderTopColor; case BORDER_RIGHT_COLOR: - return renderStyle.borderRightColor; + return borderRightColor; case BORDER_BOTTOM_COLOR: - return renderStyle.borderBottomColor; + return borderBottomColor; case BOX_SHADOW: - return renderStyle.boxShadow; + return boxShadow; case BORDER_TOP_LEFT_RADIUS: - return renderStyle.borderTopLeftRadius; + return borderTopLeftRadius; case BORDER_TOP_RIGHT_RADIUS: - return renderStyle.borderTopRightRadius; + return borderTopRightRadius; case BORDER_BOTTOM_LEFT_RADIUS: - return renderStyle.borderBottomLeftRadius; + return borderBottomLeftRadius; case BORDER_BOTTOM_RIGHT_RADIUS: - return renderStyle.borderBottomRightRadius; + return borderBottomRightRadius; // Margin case MARGIN_LEFT: - return renderStyle.marginLeft; + return marginLeft; case MARGIN_TOP: - return renderStyle.marginTop; + return marginTop; case MARGIN_RIGHT: - return renderStyle.marginRight; + return marginRight; case MARGIN_BOTTOM: - return renderStyle.marginBottom; + return marginBottom; // Text case COLOR: - return renderStyle.color; + return color; case TEXT_DECORATION_LINE: - return renderStyle.textDecorationLine; + return textDecorationLine; case TEXT_DECORATION_STYLE: - return renderStyle.textDecorationStyle; + return textDecorationStyle; case TEXT_DECORATION_COLOR: - return renderStyle.textDecorationColor; + return textDecorationColor; case FONT_WEIGHT: - return renderStyle.fontWeight; + return fontWeight; case FONT_STYLE: - return renderStyle.fontStyle; + return fontStyle; case FONT_FAMILY: - return renderStyle.fontFamily; + return fontFamily; case FONT_SIZE: - return renderStyle.fontSize; + return fontSize; case LINE_HEIGHT: - return renderStyle.lineHeight; + return lineHeight; case LETTER_SPACING: - return renderStyle.letterSpacing; + return letterSpacing; case WORD_SPACING: - return renderStyle.wordSpacing; + return wordSpacing; case TEXT_SHADOW: - return renderStyle.textShadow; + return textShadow; case WHITE_SPACE: - return renderStyle.whiteSpace; + return whiteSpace; case TEXT_OVERFLOW: - return renderStyle.textOverflow; + return textOverflow; case LINE_CLAMP: - return renderStyle.lineClamp; + return lineClamp; case VERTICAL_ALIGN: - return renderStyle.verticalAlign; + return verticalAlign; case TEXT_ALIGN: - return renderStyle.textAlign; + return textAlign; // Transform case TRANSFORM: - return renderStyle.transform; + return transform; case TRANSFORM_ORIGIN: - return renderStyle.transformOrigin; + return transformOrigin; case SLIVER_DIRECTION: - return renderStyle.sliverDirection; + return sliverDirection; case OBJECT_FIT: - return renderStyle.objectFit; + return objectFit; case OBJECT_POSITION: - return renderStyle.objectPosition; + return objectPosition; case FILTER: - return renderStyle.filter; + return filter; } } // Content width of render box model calculated from style. - double? getLogicalContentWidth() { + @override + double? get logicalContentWidth { RenderStyle renderStyle = this; double? intrinsicRatio = renderBoxModel!.intrinsicRatio; CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay; @@ -341,7 +479,8 @@ class RenderStyle } // Content height of render box model calculated from style. - double? getLogicalContentHeight() { + @override + double? get logicalContentHeight { RenderStyle renderStyle = this; CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay; double? height = renderStyle.height.isAuto ? null : renderStyle.height.computedValue; @@ -414,6 +553,7 @@ class RenderStyle // Max constraints width of content, used in calculating the remaining space for line wrapping // in the stage of layout. + @override double get contentMaxConstraintsWidth { // If renderBoxModel definite content constraints, use it as max constrains width of content. BoxConstraints? contentConstraints = renderBoxModel!.contentConstraints; @@ -486,6 +626,7 @@ class RenderStyle // Content width calculated from renderStyle tree. // https://www.w3.org/TR/css-box-3/#valdef-box-content-box // @TODO: add cache to avoid recalculate every time. + @override double? get contentBoxLogicalWidth { // If renderBox has tight width, its logical size equals max size. if (renderBoxModel != null && @@ -496,12 +637,13 @@ class RenderStyle effectiveBorderLeftWidth.computedValue - effectiveBorderRightWidth.computedValue - paddingLeft.computedValue - paddingRight.computedValue; } - return getLogicalContentWidth(); + return logicalContentWidth; } // Content height calculated from renderStyle tree. // https://www.w3.org/TR/css-box-3/#valdef-box-content-box // @TODO: add cache to avoid recalculate every time. + @override double? get contentBoxLogicalHeight { // If renderBox has tight height, its logical size equals max size. if (renderBoxModel != null && @@ -512,11 +654,12 @@ class RenderStyle effectiveBorderTopWidth.computedValue - effectiveBorderBottomWidth.computedValue - paddingTop.computedValue - paddingBottom.computedValue; } - return getLogicalContentHeight(); + return logicalContentHeight; } // Padding box width calculated from renderStyle tree. // https://www.w3.org/TR/css-box-3/#valdef-box-padding-box + @override double? get paddingBoxLogicalWidth { if (contentBoxLogicalWidth == null) { return null; @@ -526,6 +669,7 @@ class RenderStyle // Padding box height calculated from renderStyle tree. // https://www.w3.org/TR/css-box-3/#valdef-box-padding-box + @override double? get paddingBoxLogicalHeight { if (contentBoxLogicalHeight == null) { return null; @@ -535,6 +679,7 @@ class RenderStyle // Border box width calculated from renderStyle tree. // https://www.w3.org/TR/css-box-3/#valdef-box-border-box + @override double? get borderBoxLogicalWidth { if (paddingBoxLogicalWidth == null) { return null; @@ -544,6 +689,7 @@ class RenderStyle // Border box height calculated from renderStyle tree. // https://www.w3.org/TR/css-box-3/#valdef-box-border-box + @override double? get borderBoxLogicalHeight { if (paddingBoxLogicalHeight == null) { return null; @@ -553,6 +699,7 @@ class RenderStyle // Content box width of renderBoxModel after it was rendered. // https://www.w3.org/TR/css-box-3/#valdef-box-content-box + @override double? get contentBoxWidth { if (paddingBoxWidth == null) { return null; @@ -562,6 +709,7 @@ class RenderStyle // Content box height of renderBoxModel after it was rendered. // https://www.w3.org/TR/css-box-3/#valdef-box-content-box + @override double? get contentBoxHeight { if (paddingBoxHeight == null) { return null; @@ -571,6 +719,7 @@ class RenderStyle // Padding box width of renderBoxModel after it was rendered. // https://www.w3.org/TR/css-box-3/#valdef-box-padding-box + @override double? get paddingBoxWidth { if (borderBoxWidth == null) { return null; @@ -580,6 +729,7 @@ class RenderStyle // Padding box height of renderBoxModel after it was rendered. // https://www.w3.org/TR/css-box-3/#valdef-box-padding-box + @override double? get paddingBoxHeight { if (borderBoxHeight == null) { return null; @@ -589,6 +739,7 @@ class RenderStyle // Border box width of renderBoxModel after it was rendered. // https://www.w3.org/TR/css-box-3/#valdef-box-border-box + @override double? get borderBoxWidth { if (renderBoxModel!.hasSize && renderBoxModel!.boxSize != null) { return renderBoxModel!.boxSize!.width; @@ -598,6 +749,7 @@ class RenderStyle // Border box height of renderBoxModel after it was rendered. // https://www.w3.org/TR/css-box-3/#valdef-box-border-box + @override double? get borderBoxHeight { if (renderBoxModel!.hasSize && renderBoxModel!.boxSize != null) { return renderBoxModel!.boxSize!.height; diff --git a/kraken/lib/src/css/sizing.dart b/kraken/lib/src/css/sizing.dart index 42b8de38ab..21064f3b4e 100644 --- a/kraken/lib/src/css/sizing.dart +++ b/kraken/lib/src/css/sizing.dart @@ -15,7 +15,7 @@ import 'package:kraken/rendering.dart'; /// - min-width /// - min-height -mixin CSSSizingMixin on RenderStyleBase { +mixin CSSSizingMixin on AbstractRenderStyle { // https://drafts.csswg.org/css-sizing-3/#preferred-size-properties // Name: width, height @@ -28,9 +28,10 @@ mixin CSSSizingMixin on RenderStyleBase { // Canonical order: per grammar // Animation type: by computed value type, recursing into fit-content() CSSLengthValue? _width; - CSSLengthValue get width { - return _width ?? CSSLengthValue.auto; - } + + @override + CSSLengthValue get width => _width ?? CSSLengthValue.auto; + set width(CSSLengthValue? value) { // Negative value is invalid, auto value is parsed at layout stage. if ((value != null && value.value != null && value.value! < 0) || width == value) { @@ -41,9 +42,10 @@ mixin CSSSizingMixin on RenderStyleBase { } CSSLengthValue? _height; - CSSLengthValue get height { - return _height ?? CSSLengthValue.auto; - } + + @override + CSSLengthValue get height => _height ?? CSSLengthValue.auto; + set height(CSSLengthValue? value) { // Negative value is invalid, auto value is parsed at layout stage. if ((value != null && value.value != null && value.value! < 0) || height == value) { @@ -64,9 +66,10 @@ mixin CSSSizingMixin on RenderStyleBase { // Canonical order: per grammar // Animatable: by computed value, recursing into fit-content() CSSLengthValue? _minWidth; - CSSLengthValue get minWidth { - return _minWidth ?? CSSLengthValue.auto; - } + + @override + CSSLengthValue get minWidth => _minWidth ?? CSSLengthValue.auto; + set minWidth(CSSLengthValue? value) { // Negative value is invalid, auto value is parsed at layout stage. if ((value != null && value.value != null && value.value! < 0) || minWidth == value) { @@ -77,9 +80,10 @@ mixin CSSSizingMixin on RenderStyleBase { } CSSLengthValue? _minHeight; - CSSLengthValue get minHeight { - return _minHeight ?? CSSLengthValue.auto; - } + + @override + CSSLengthValue get minHeight => _minHeight ?? CSSLengthValue.auto; + set minHeight(CSSLengthValue? value) { // Negative value is invalid, auto value is parsed at layout stage. if ((value != null && value.value != null && value.value! < 0) || minHeight == value) { @@ -100,9 +104,10 @@ mixin CSSSizingMixin on RenderStyleBase { // Canonical order: per grammar // Animatable: by computed value, recursing into fit-content() CSSLengthValue? _maxWidth; - CSSLengthValue get maxWidth { - return _maxWidth ?? CSSLengthValue.none; - } + + @override + CSSLengthValue get maxWidth => _maxWidth ?? CSSLengthValue.none; + set maxWidth(CSSLengthValue? value) { // Negative value is invalid, auto value is parsed at layout stage. if ((value != null && value.value != null && value.value! < 0) || maxWidth == value) { @@ -113,9 +118,12 @@ mixin CSSSizingMixin on RenderStyleBase { } CSSLengthValue? _maxHeight; + + @override CSSLengthValue get maxHeight { return _maxHeight ?? CSSLengthValue.none; } + set maxHeight(CSSLengthValue? value) { // Negative value is invalid, auto value is parsed at layout stage. if ((value != null && value.value != null && value.value! < 0) || maxHeight == value) { diff --git a/kraken/lib/src/css/sliver.dart b/kraken/lib/src/css/sliver.dart index 17ade19482..c24b4c95ae 100644 --- a/kraken/lib/src/css/sliver.dart +++ b/kraken/lib/src/css/sliver.dart @@ -6,14 +6,15 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; -mixin CSSSliverMixin on RenderStyleBase { +mixin CSSSliverMixin on AbstractRenderStyle { - Axis _sliverDirection = Axis.vertical; + @override Axis get sliverDirection => _sliverDirection; + Axis _sliverDirection = Axis.vertical; set sliverDirection(Axis value) { if (_sliverDirection == value) return; _sliverDirection = value; - renderBoxModel!.markNeedsLayout(); + renderBoxModel?.markNeedsLayout(); } static Axis resolveAxis(String sliverDirection) { diff --git a/kraken/lib/src/css/text.dart b/kraken/lib/src/css/text.dart index 83804fe1e9..3b0f2914e0 100644 --- a/kraken/lib/src/css/text.dart +++ b/kraken/lib/src/css/text.dart @@ -12,19 +12,20 @@ final RegExp _commaRegExp = RegExp(r'\s*,\s*'); // CSS Text: https://drafts.csswg.org/css-text-3/ // CSS Text Decoration: https://drafts.csswg.org/css-text-decor-3/ // CSS Box Alignment: https://drafts.csswg.org/css-align/ -mixin CSSTextMixin on RenderStyleBase { - +mixin CSSTextMixin on AbstractRenderStyle { bool get hasColor => _color != null; + @override + Color get currentColor => color; + Color? _color; + + @override Color get color { // Get style from self or closest parent if specified style property is not set // due to style inheritance. - if (_color == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.color; - } + if (_color == null && parent != null) { + return parent!.color; } // The root element has no color, and the color is initial. @@ -39,18 +40,17 @@ mixin CSSTextMixin on RenderStyleBase { } // Current not update the dependent property relative to the color. - final Map _colorRealativeProperties = {}; + final Map _colorRelativeProperties = {}; void addColorRelativeProperty(String propertyName) { - _colorRealativeProperties[propertyName] = true; + _colorRelativeProperties[propertyName] = true; } void updateColorRelativeProperty() { - if (_colorRealativeProperties.isEmpty) return; - RenderStyle renderStyle = this as RenderStyle; - _colorRealativeProperties.forEach((String propertyName, _) { + if (_colorRelativeProperties.isEmpty) return; + _colorRelativeProperties.forEach((String propertyName, _) { // TODO: use css color abstraction avoid re-parse the property string. - renderStyle.target.setRenderStyle(propertyName, renderStyle.target.style.getPropertyValue(propertyName)); + target.setRenderStyle(propertyName, target.style.getPropertyValue(propertyName)); }); } @@ -86,14 +86,12 @@ mixin CSSTextMixin on RenderStyleBase { } FontWeight? _fontWeight; + @override FontWeight get fontWeight { // Get style from self or closest parent if specified style property is not set // due to style inheritance. - if (_fontWeight == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.fontWeight; - } + if (_fontWeight == null && parent != null) { + return parent!.fontWeight; } // The root element has no fontWeight, and the fontWeight is initial. @@ -107,14 +105,13 @@ mixin CSSTextMixin on RenderStyleBase { } FontStyle? _fontStyle; + + @override FontStyle get fontStyle { // Get style from self or closest parent if specified style property is not set // due to style inheritance. - if (_fontStyle == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.fontStyle; - } + if (_fontStyle == null && parent != null) { + return parent!.fontStyle; } // The root element has no fontWeight, and the fontWeight is initial. @@ -128,14 +125,13 @@ mixin CSSTextMixin on RenderStyleBase { } List? _fontFamily; + + @override List? get fontFamily { // Get style from self or closest parent if specified style property is not set // due to style inheritance. - if (_fontFamily == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.fontFamily; - } + if (_fontFamily == null && parent != null) { + return parent!.fontFamily; } return _fontFamily ?? CSSText.DEFAULT_FONT_FAMILY_FALLBACK; } @@ -149,14 +145,13 @@ mixin CSSTextMixin on RenderStyleBase { bool get hasFontSize => _fontSize != null; CSSLengthValue? _fontSize; + + @override CSSLengthValue get fontSize { // Get style from self or closest parent if specified style property is not set // due to style inheritance. - if (_fontSize == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.fontSize; - } + if (_fontSize == null && parent != null) { + return parent!.fontSize; } return _fontSize ?? CSSText.DEFAULT_FONT_SIZE; } @@ -175,6 +170,7 @@ mixin CSSTextMixin on RenderStyleBase { final Map _fontRealativeProperties = {}; final Map _rootFontRealativeProperties = {}; + @override void addFontRelativeProperty(String propertyName) { _fontRealativeProperties[propertyName] = true; } @@ -184,6 +180,7 @@ mixin CSSTextMixin on RenderStyleBase { renderBoxModel?.markNeedsLayout(); } + @override void addRootFontRelativeProperty(String propertyName) { _rootFontRealativeProperties[propertyName] = true; } @@ -194,12 +191,11 @@ mixin CSSTextMixin on RenderStyleBase { } CSSLengthValue? _lineHeight; + + @override CSSLengthValue get lineHeight { - if (_lineHeight == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.lineHeight; - } + if (_lineHeight == null && parent != null) { + return parent!.lineHeight; } return _lineHeight ?? CSSText.DEFAULT_LINE_HEIGHT; @@ -213,14 +209,13 @@ mixin CSSTextMixin on RenderStyleBase { } CSSLengthValue? _letterSpacing; + + @override CSSLengthValue? get letterSpacing { // Get style from self or closest parent if specified style property is not set // due to style inheritance. - if (_letterSpacing == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.letterSpacing; - } + if (_letterSpacing == null && parent != null) { + return parent!.letterSpacing; } return _letterSpacing; } @@ -232,14 +227,13 @@ mixin CSSTextMixin on RenderStyleBase { } CSSLengthValue? _wordSpacing; + + @override CSSLengthValue? get wordSpacing { // Get style from self or closest parent if specified style property is not set // due to style inheritance. - if (_wordSpacing == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.wordSpacing; - } + if (_wordSpacing == null && parent != null) { + return parent!.wordSpacing; } return _wordSpacing; } @@ -251,14 +245,13 @@ mixin CSSTextMixin on RenderStyleBase { } List? _textShadow; + + @override List? get textShadow { // Get style from self or closest parent if specified style property is not set // due to style inheritance. - if (_textShadow == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.textShadow; - } + if (_textShadow == null && parent != null) { + return parent!.textShadow; } return _textShadow; } @@ -270,14 +263,13 @@ mixin CSSTextMixin on RenderStyleBase { } WhiteSpace? _whiteSpace; + + @override WhiteSpace get whiteSpace { // Get style from self or closest parent if specified style property is not set // due to style inheritance. - if (_whiteSpace == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.whiteSpace; - } + if (_whiteSpace == null && parent != null) { + return parent!.whiteSpace; } return _whiteSpace ?? WhiteSpace.normal; } @@ -289,6 +281,8 @@ mixin CSSTextMixin on RenderStyleBase { } TextOverflow _textOverflow = TextOverflow.clip; + + @override TextOverflow get textOverflow { return _textOverflow; } @@ -300,6 +294,8 @@ mixin CSSTextMixin on RenderStyleBase { } int? _lineClamp; + + @override int? get lineClamp { return _lineClamp; } @@ -311,14 +307,13 @@ mixin CSSTextMixin on RenderStyleBase { } TextAlign? _textAlign; + + @override TextAlign get textAlign { // Get style from self or closest parent if specified style property is not set // due to style inheritance. - if (_textAlign == null) { - RenderStyle renderStyle = this as RenderStyle; - if (renderStyle.parent != null) { - return renderStyle.parent!.textAlign; - } + if (_textAlign == null && parent != null) { + return parent!.textAlign; } return _textAlign ?? TextAlign.start; } diff --git a/kraken/lib/src/css/transform.dart b/kraken/lib/src/css/transform.dart index f55fe68ce9..894b6dd80a 100644 --- a/kraken/lib/src/css/transform.dart +++ b/kraken/lib/src/css/transform.dart @@ -9,7 +9,7 @@ import 'package:kraken/rendering.dart'; import 'package:vector_math/vector_math_64.dart'; // CSS Transforms: https://drafts.csswg.org/css-transforms/ -mixin CSSTransformMixin on RenderStyleBase { +mixin CSSTransformMixin on AbstractRenderStyle { static Offset DEFAULT_TRANSFORM_OFFSET = Offset(0, 0); static Alignment DEFAULT_TRANSFORM_ALIGNMENT = Alignment.center; @@ -35,7 +35,7 @@ mixin CSSTransformMixin on RenderStyleBase { _transformMatrix = null; // Transform effect the stacking context. - RenderBoxModel? parentRenderer = (this as RenderStyle).parent?.renderBoxModel; + RenderBoxModel? parentRenderer = parent?.renderBoxModel; if (parentRenderer is RenderLayoutBox) { parentRenderer.markChildrenNeedsSort(); } @@ -52,7 +52,7 @@ mixin CSSTransformMixin on RenderStyleBase { Matrix4? get transformMatrix { if (_transformMatrix == null && _transform != null) { // Illegal transform syntax will return null. - _transformMatrix = CSSMatrix.computeTransformMatrix(_transform!, this as RenderStyle); + _transformMatrix = CSSMatrix.computeTransformMatrix(_transform!, this); } return _transformMatrix; } diff --git a/kraken/lib/src/css/transition.dart b/kraken/lib/src/css/transition.dart index fdd87d66b0..e8a202aaeb 100644 --- a/kraken/lib/src/css/transition.dart +++ b/kraken/lib/src/css/transition.dart @@ -179,7 +179,7 @@ enum CSSTransitionEvent { cancel, } -mixin CSSTransitionMixin on RenderStyleBase { +mixin CSSTransitionMixin on AbstractRenderStyle { // https://drafts.csswg.org/css-transitions/#transition-property-property // Name: transition-property @@ -199,8 +199,9 @@ mixin CSSTransitionMixin on RenderStyleBase { // Any animation found in previousAnimations but not found in newAnimations is not longer current and should be canceled. // @HACK: There are no way to get animationList from styles(Webkit will create an new Style object when style changes, but Kraken not). // Therefore we should cancel all running transition to get thing works. - finishRunningTransiton(); + finishRunningTransition(); } + @override List get transitionProperty => _transitionProperty ?? const [ALL]; // https://drafts.csswg.org/css-transitions/#transition-duration-property @@ -218,6 +219,7 @@ mixin CSSTransitionMixin on RenderStyleBase { _transitionDuration = value; _effectiveTransitions = null; } + @override List get transitionDuration => _transitionDuration ?? const [_0s]; // https://drafts.csswg.org/css-transitions/#transition-timing-function-property @@ -235,6 +237,7 @@ mixin CSSTransitionMixin on RenderStyleBase { _transitionTimingFunction = value; _effectiveTransitions = null; } + @override List get transitionTimingFunction => _transitionTimingFunction ?? const [EASE]; // https://drafts.csswg.org/css-transitions/#transition-delay-property @@ -252,6 +255,7 @@ mixin CSSTransitionMixin on RenderStyleBase { _transitionDelay = value; _effectiveTransitions = null; } + @override List get transitionDelay => _transitionDelay ?? const [_0s]; Map? _effectiveTransitions; @@ -299,6 +303,7 @@ mixin CSSTransitionMixin on RenderStyleBase { return _propertyRunningTransition.containsKey(property); } + @override String? removeAnimationProperty(String propertyName) { String? prevValue = EMPTY_STRING; @@ -317,7 +322,7 @@ mixin CSSTransitionMixin on RenderStyleBase { // An Event fired when a CSS transition has been cancelled. target.dispatchEvent(Event(EVENT_TRANSITION_CANCEL)); - + // Maybe set transition twice in a same frame. should check animationProperties has contains propertyName. if (_animationProperties.containsKey(propertyName)) { begin = _animationProperties[propertyName]; @@ -337,7 +342,7 @@ mixin CSSTransitionMixin on RenderStyleBase { Keyframe(propertyName, begin, 0, LINEAR), Keyframe(propertyName, end, 1, LINEAR), ]; - KeyframeEffect effect = KeyframeEffect(this as RenderStyle, target, keyframes, options); + KeyframeEffect effect = KeyframeEffect(this, target, keyframes, options); Animation animation = Animation(effect); _propertyRunningTransition[propertyName] = animation; @@ -356,11 +361,11 @@ mixin CSSTransitionMixin on RenderStyleBase { }; target.dispatchEvent(Event(EVENT_TRANSITION_RUN)); - + animation.play(); } - void cancelRunningTransiton() { + void cancelRunningTransition() { if (_propertyRunningTransition.isNotEmpty) { for (String property in _propertyRunningTransition.keys) { _propertyRunningTransition[property]!.cancel(); @@ -369,7 +374,7 @@ mixin CSSTransitionMixin on RenderStyleBase { } } - void finishRunningTransiton() { + void finishRunningTransition() { if (_propertyRunningTransition.isNotEmpty) { for (String property in _propertyRunningTransition.keys) { _propertyRunningTransition[property]!.finish(); diff --git a/kraken/lib/src/css/values/length.dart b/kraken/lib/src/css/values/length.dart index 14bcacc90d..880974754b 100644 --- a/kraken/lib/src/css/values/length.dart +++ b/kraken/lib/src/css/values/length.dart @@ -65,7 +65,7 @@ class CSSLengthValue { // Length is applied in horizontal or vertical direction. Axis? axisType; - RenderStyle? renderStyle; + AbstractRenderStyle? renderStyle; String? propertyName; double? _computedValue; @@ -121,7 +121,7 @@ class CSSLengthValue { bool isPositioned = positionType == CSSPositionType.absolute || positionType == CSSPositionType.fixed; - RenderStyle? parentRenderStyle = renderStyle!.parent; + AbstractRenderStyle? parentRenderStyle = renderStyle!.parent; // Percentage relative width priority: logical width > renderer width double? relativeParentWidth = isPositioned ? @@ -166,7 +166,7 @@ class CSSLengthValue { // There are two exceptions when percentage height is resolved against actual render height of parent: // 1. positioned element // 2. parent is flex item - RenderStyle? grandParentRenderStyle = parentRenderStyle?.parent; + AbstractRenderStyle? grandParentRenderStyle = parentRenderStyle?.parent; bool isGrandParentFlexLayout = grandParentRenderStyle?.display == CSSDisplay.flex || grandParentRenderStyle?.display == CSSDisplay.inlineFlex; @@ -220,7 +220,7 @@ class CSSLengthValue { case FLEX_BASIS: // Flex-basis computation is called in RenderFlexLayout which // will ensure parent exists. - RenderStyle parentRenderStyle = renderStyle!.parent!; + AbstractRenderStyle parentRenderStyle = renderStyle!.parent!; double? mainContentSize = parentRenderStyle.flexDirection == FlexDirection.row ? parentRenderStyle.contentBoxLogicalWidth : parentRenderStyle.contentBoxLogicalHeight; @@ -416,7 +416,7 @@ class CSSLength { } } - static CSSLengthValue parseLength(String text, RenderStyle? renderStyle, [String? propertyName, Axis? axisType]) { + static CSSLengthValue parseLength(String text, AbstractRenderStyle? renderStyle, [String? propertyName, Axis? axisType]) { if (_cachedParsedLength.containsKey(text)) { return _cachedParsedLength[text]!; } diff --git a/kraken/lib/src/css/visibility.dart b/kraken/lib/src/css/visibility.dart index 67cfcc21db..5cfcfaa97e 100644 --- a/kraken/lib/src/css/visibility.dart +++ b/kraken/lib/src/css/visibility.dart @@ -9,7 +9,7 @@ enum Visibility { hidden, } -mixin CSSVisibilityMixin on RenderStyleBase { +mixin CSSVisibilityMixin on AbstractRenderStyle { Visibility _visibility = Visibility.visible; void set visibility(Visibility value) { @@ -18,6 +18,7 @@ mixin CSSVisibilityMixin on RenderStyleBase { renderBoxModel!.markNeedsPaint(); } + @override Visibility get visibility => _visibility; bool get isVisibilityHidden { diff --git a/kraken/lib/src/dom/element.dart b/kraken/lib/src/dom/element.dart index bb92c0678b..d4e2f00154 100644 --- a/kraken/lib/src/dom/element.dart +++ b/kraken/lib/src/dom/element.dart @@ -408,7 +408,7 @@ class Element extends Node @override void willDetachRenderer() { // Cancel running transition. - renderStyle.cancelRunningTransiton(); + renderStyle.cancelRunningTransition(); // Remove all intersection change listeners. renderBoxModel!.clearIntersectionChangeListeners(); diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index e14d13957f..49b92cc874 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -895,8 +895,8 @@ class RenderBoxModel extends RenderBox double? maxHeight = renderStyle.maxHeight.isNone ? null : renderStyle.maxHeight.computedValue; // Content size calculated from style - logicalContentWidth = renderStyle.getLogicalContentWidth(); - logicalContentHeight = renderStyle.getLogicalContentHeight(); + logicalContentWidth = renderStyle.logicalContentWidth; + logicalContentHeight = renderStyle.logicalContentHeight; // Box size calculated from style double? logicalWidth = logicalContentWidth != null @@ -1041,8 +1041,8 @@ class RenderBoxModel extends RenderBox // Deflate padding constraints. boxConstraints = renderStyle.deflatePaddingConstraints(boxConstraints); - logicalContentWidth = renderStyle.getLogicalContentWidth(); - logicalContentHeight = renderStyle.getLogicalContentHeight(); + logicalContentWidth = renderStyle.logicalContentWidth; + logicalContentHeight = renderStyle.logicalContentHeight; if (!isScrollingContentBox && (logicalContentWidth != null || logicalContentHeight != null)) { double minWidth; From 7cb071a2aa2d0ece5d561c08d32d2ea981106433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Wed, 1 Dec 2021 15:16:03 +0800 Subject: [PATCH 113/167] refactor: using renderBoxModel?.markNeedsXxxx instrad of renderBoxModel!.op --- kraken/lib/src/css/background.dart | 18 ++++++++-------- kraken/lib/src/css/border.dart | 24 +++++++++++----------- kraken/lib/src/css/border_radius.dart | 8 ++++---- kraken/lib/src/css/box_shadow.dart | 2 +- kraken/lib/src/css/content_visibility.dart | 2 +- kraken/lib/src/css/filter.dart | 2 +- kraken/lib/src/css/flexbox.dart | 8 ++++---- kraken/lib/src/css/inline.dart | 2 +- kraken/lib/src/css/opacity.dart | 7 ++++--- kraken/lib/src/css/position.dart | 4 ++-- kraken/lib/src/css/transform.dart | 8 ++++---- kraken/lib/src/css/visibility.dart | 2 +- 12 files changed, 44 insertions(+), 43 deletions(-) diff --git a/kraken/lib/src/css/background.dart b/kraken/lib/src/css/background.dart index b8e8ea23a2..170dd6b04b 100644 --- a/kraken/lib/src/css/background.dart +++ b/kraken/lib/src/css/background.dart @@ -91,7 +91,7 @@ mixin CSSBackgroundMixin on AbstractRenderStyle { set backgroundClip(BackgroundBoundary? value) { if (value == _backgroundClip) return; _backgroundClip = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } /// Background-origin @@ -100,7 +100,7 @@ mixin CSSBackgroundMixin on AbstractRenderStyle { set backgroundOrigin(BackgroundBoundary? value) { if (value == _backgroundOrigin) return; _backgroundOrigin = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override @@ -109,7 +109,7 @@ mixin CSSBackgroundMixin on AbstractRenderStyle { set backgroundColor(Color? value) { if (value == _backgroundColor) return; _backgroundColor = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } /// Background-image @@ -119,7 +119,7 @@ mixin CSSBackgroundMixin on AbstractRenderStyle { set backgroundImage(CSSBackgroundImage? value) { if (value == _backgroundImage) return; _backgroundImage = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } /// Background-position-x @@ -129,7 +129,7 @@ mixin CSSBackgroundMixin on AbstractRenderStyle { set backgroundPositionX(CSSBackgroundPosition? value) { if (value == _backgroundPositionX) return; _backgroundPositionX = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } /// Background-position-y @@ -139,7 +139,7 @@ mixin CSSBackgroundMixin on AbstractRenderStyle { set backgroundPositionY(CSSBackgroundPosition? value) { if (value == _backgroundPositionY) return; _backgroundPositionY = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } /// Background-size @@ -148,7 +148,7 @@ mixin CSSBackgroundMixin on AbstractRenderStyle { set backgroundSize(CSSBackgroundSize? value) { if (value == _backgroundSize) return; _backgroundSize = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } /// Background-attachment @@ -157,7 +157,7 @@ mixin CSSBackgroundMixin on AbstractRenderStyle { set backgroundAttachment(CSSBackgroundAttachmentType? value) { if (value == _backgroundAttachment) return; _backgroundAttachment = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } /// Background-repeat @@ -167,7 +167,7 @@ mixin CSSBackgroundMixin on AbstractRenderStyle { set backgroundRepeat(ImageRepeat? value) { if (value == _backgroundRepeat) return; _backgroundRepeat = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } } diff --git a/kraken/lib/src/css/border.dart b/kraken/lib/src/css/border.dart index b8a9713113..0da0839ce5 100644 --- a/kraken/lib/src/css/border.dart +++ b/kraken/lib/src/css/border.dart @@ -85,7 +85,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderTopWidth(CSSLengthValue? value) { if (value == _borderTopWidth) return; _borderTopWidth = value; - renderBoxModel!.markNeedsLayout(); + renderBoxModel?.markNeedsLayout(); } @override CSSLengthValue? get borderTopWidth => _borderTopWidth; @@ -97,7 +97,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderRightWidth(CSSLengthValue? value) { if (value == _borderRightWidth) return; _borderRightWidth = value; - renderBoxModel!.markNeedsLayout(); + renderBoxModel?.markNeedsLayout(); } @override @@ -110,7 +110,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderBottomWidth(CSSLengthValue? value) { if (value == _borderBottomWidth) return; _borderBottomWidth = value; - renderBoxModel!.markNeedsLayout(); + renderBoxModel?.markNeedsLayout(); } @override @@ -123,7 +123,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderLeftWidth(CSSLengthValue? value) { if (value == _borderLeftWidth) return; _borderLeftWidth = value; - renderBoxModel!.markNeedsLayout(); + renderBoxModel?.markNeedsLayout(); } @override @@ -139,7 +139,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderTopColor(Color? value) { if (value == _borderTopColor) return; _borderTopColor = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override @@ -148,7 +148,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderRightColor(Color? value) { if (value == _borderRightColor) return; _borderRightColor = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override @@ -157,7 +157,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderBottomColor(Color? value) { if (value == _borderBottomColor) return; _borderBottomColor = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override @@ -166,7 +166,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderLeftColor(Color? value) { if (value == _borderLeftColor) return; _borderLeftColor = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } /// Border-style @@ -176,7 +176,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderTopStyle(BorderStyle? value) { if (value == _borderTopStyle) return; _borderTopStyle = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override @@ -185,7 +185,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderRightStyle(BorderStyle? value) { if (value == _borderRightStyle) return; _borderRightStyle = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override @@ -194,7 +194,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderBottomStyle(BorderStyle? value) { if (value == _borderBottomStyle) return; _borderBottomStyle = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } BorderStyle? _borderLeftStyle; @@ -205,7 +205,7 @@ mixin CSSBorderMixin on AbstractRenderStyle { set borderLeftStyle(BorderStyle? value) { if (value == _borderLeftStyle) return; _borderLeftStyle = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } } diff --git a/kraken/lib/src/css/border_radius.dart b/kraken/lib/src/css/border_radius.dart index c9ab1c2fcd..d20e776149 100644 --- a/kraken/lib/src/css/border_radius.dart +++ b/kraken/lib/src/css/border_radius.dart @@ -7,7 +7,7 @@ mixin CSSBorderRadiusMixin on AbstractRenderStyle { set borderTopLeftRadius(CSSBorderRadius? value) { if (value == _borderTopLeftRadius) return; _borderTopLeftRadius = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override CSSBorderRadius get borderTopLeftRadius => _borderTopLeftRadius ?? CSSBorderRadius.zero; @@ -16,7 +16,7 @@ mixin CSSBorderRadiusMixin on AbstractRenderStyle { set borderTopRightRadius(CSSBorderRadius? value) { if (value == _borderTopRightRadius) return; _borderTopRightRadius = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override CSSBorderRadius get borderTopRightRadius => _borderTopRightRadius ?? CSSBorderRadius.zero; @@ -25,7 +25,7 @@ mixin CSSBorderRadiusMixin on AbstractRenderStyle { set borderBottomRightRadius(CSSBorderRadius? value) { if (value == _borderBottomRightRadius) return; _borderBottomRightRadius = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override CSSBorderRadius get borderBottomRightRadius => _borderBottomRightRadius ?? CSSBorderRadius.zero; @@ -34,7 +34,7 @@ mixin CSSBorderRadiusMixin on AbstractRenderStyle { set borderBottomLeftRadius(CSSBorderRadius? value) { if (value == _borderBottomLeftRadius) return; _borderBottomLeftRadius = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override CSSBorderRadius get borderBottomLeftRadius => _borderBottomLeftRadius ?? CSSBorderRadius.zero; diff --git a/kraken/lib/src/css/box_shadow.dart b/kraken/lib/src/css/box_shadow.dart index 060fb77356..2f681375f1 100644 --- a/kraken/lib/src/css/box_shadow.dart +++ b/kraken/lib/src/css/box_shadow.dart @@ -5,7 +5,7 @@ mixin CSSBoxShadowMixin on AbstractRenderStyle { set boxShadow(List? value) { if (value == _boxShadow) return; _boxShadow = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } List? get boxShadow => _boxShadow; diff --git a/kraken/lib/src/css/content_visibility.dart b/kraken/lib/src/css/content_visibility.dart index 44dfc7e5ce..aafb4c14d9 100644 --- a/kraken/lib/src/css/content_visibility.dart +++ b/kraken/lib/src/css/content_visibility.dart @@ -32,7 +32,7 @@ mixin CSSContentVisibilityMixin on AbstractRenderStyle { if (value == null) return; if (value == _contentVisibility) return; _contentVisibility = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } static ContentVisibility resolveContentVisibility(String value) { diff --git a/kraken/lib/src/css/filter.dart b/kraken/lib/src/css/filter.dart index 1f6726d00d..65fc348f57 100644 --- a/kraken/lib/src/css/filter.dart +++ b/kraken/lib/src/css/filter.dart @@ -198,7 +198,7 @@ mixin CSSFilterEffectsMixin on AbstractRenderStyle { parentRenderer.markChildrenNeedsSort(); } - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); if (!kReleaseMode && functions != null) { ColorFilter? colorFilter = _parseColorFilters(functions); diff --git a/kraken/lib/src/css/flexbox.dart b/kraken/lib/src/css/flexbox.dart index 9bca5a81c3..ce61f9e6d2 100644 --- a/kraken/lib/src/css/flexbox.dart +++ b/kraken/lib/src/css/flexbox.dart @@ -248,7 +248,7 @@ mixin CSSFlexboxMixin on AbstractRenderStyle { set alignSelf(AlignSelf value) { if (_alignSelf == value) return; _alignSelf = value; - if (renderBoxModel!.parent is RenderFlexLayout) { + if (renderBoxModel?.parent is RenderFlexLayout) { renderBoxModel!.markNeedsLayout(); } } @@ -264,7 +264,7 @@ mixin CSSFlexboxMixin on AbstractRenderStyle { return; } _flexBasis = value; - if (renderBoxModel!.parent is RenderFlexLayout) { + if (renderBoxModel?.parent is RenderFlexLayout) { renderBoxModel!.markNeedsLayout(); } } @@ -275,7 +275,7 @@ mixin CSSFlexboxMixin on AbstractRenderStyle { set flexGrow(double? value) { if (_flexGrow == value) return; _flexGrow = value; - if (renderBoxModel!.parent is RenderFlexLayout) { + if (renderBoxModel?.parent is RenderFlexLayout) { renderBoxModel!.markNeedsLayout(); } } @@ -286,7 +286,7 @@ mixin CSSFlexboxMixin on AbstractRenderStyle { set flexShrink(double? value) { if (_flexShrink == value) return; _flexShrink = value; - if (renderBoxModel!.parent is RenderFlexLayout) { + if (renderBoxModel?.parent is RenderFlexLayout) { renderBoxModel!.markNeedsLayout(); } } diff --git a/kraken/lib/src/css/inline.dart b/kraken/lib/src/css/inline.dart index 4a5b033ff2..5802b50d3d 100644 --- a/kraken/lib/src/css/inline.dart +++ b/kraken/lib/src/css/inline.dart @@ -30,8 +30,8 @@ mixin CSSInlineMixin on AbstractRenderStyle { VerticalAlign _verticalAlign = VerticalAlign.baseline; set verticalAlign(VerticalAlign value) { if (_verticalAlign != value) { - renderBoxModel!.markNeedsLayout(); _verticalAlign = value; + renderBoxModel?.markNeedsLayout(); } } diff --git a/kraken/lib/src/css/opacity.dart b/kraken/lib/src/css/opacity.dart index 88fb74758e..d7d6609ec7 100644 --- a/kraken/lib/src/css/opacity.dart +++ b/kraken/lib/src/css/opacity.dart @@ -31,8 +31,9 @@ mixin CSSOpacityMixin on AbstractRenderStyle { _opacity = value; int alpha = ui.Color.getAlphaFromOpacity(_opacity); renderBoxModel!.alpha = alpha; - if (alpha != 0 && alpha != 255) - renderBoxModel!.markNeedsCompositingBitsUpdate(); + if (alpha != 0 && alpha != 255) { + renderBoxModel?.markNeedsCompositingBitsUpdate(); + } // Opacity effect the stacking context. RenderBoxModel? parentRenderer = parent?.renderBoxModel; @@ -40,7 +41,7 @@ mixin CSSOpacityMixin on AbstractRenderStyle { parentRenderer.markChildrenNeedsSort(); } - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } static double? resolveOpacity(String value) { diff --git a/kraken/lib/src/css/position.dart b/kraken/lib/src/css/position.dart index 0fa61985a4..c290e8cfab 100644 --- a/kraken/lib/src/css/position.dart +++ b/kraken/lib/src/css/position.dart @@ -102,7 +102,7 @@ mixin CSSPositionMixin on AbstractRenderStyle { } void _markNeedsSort() { - if (renderBoxModel!.parentData is RenderLayoutParentData) { + if (renderBoxModel?.parentData is RenderLayoutParentData) { RenderLayoutBox parent = renderBoxModel!.parent as RenderLayoutBox; parent.markChildrenNeedsSort(); } @@ -112,7 +112,7 @@ mixin CSSPositionMixin on AbstractRenderStyle { // Should mark positioned element's containing block needs layout directly // cause RelayoutBoundary of positioned element will prevent the needsLayout flag // to bubble up in the RenderObject tree. - if (renderBoxModel!.parentData is RenderLayoutParentData) { + if (renderBoxModel?.parentData is RenderLayoutParentData) { RenderStyle renderStyle = renderBoxModel!.renderStyle; if (renderStyle.position != DEFAULT_POSITION_TYPE) { RenderBoxModel parent = renderBoxModel!.parent as RenderBoxModel; diff --git a/kraken/lib/src/css/transform.dart b/kraken/lib/src/css/transform.dart index 894b6dd80a..87abe62801 100644 --- a/kraken/lib/src/css/transform.dart +++ b/kraken/lib/src/css/transform.dart @@ -40,7 +40,7 @@ mixin CSSTransformMixin on AbstractRenderStyle { parentRenderer.markChildrenNeedsSort(); } - renderBoxModel!.markNeedsLayout(); + renderBoxModel?.markNeedsLayout(); } static List? resolveTransform(String present) { @@ -60,7 +60,7 @@ mixin CSSTransformMixin on AbstractRenderStyle { set transformMatrix(Matrix4? value) { if (value == null || _transformMatrix == value) return; _transformMatrix = value; - renderBoxModel!.markNeedsLayout(); + renderBoxModel?.markNeedsLayout(); } Offset get transformOffset => _transformOffset; @@ -68,7 +68,7 @@ mixin CSSTransformMixin on AbstractRenderStyle { set transformOffset(Offset value) { if (_transformOffset == value) return; _transformOffset = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } Alignment get transformAlignment => _transformAlignment; @@ -76,7 +76,7 @@ mixin CSSTransformMixin on AbstractRenderStyle { set transformAlignment(Alignment value) { if (_transformAlignment == value) return; _transformAlignment = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } CSSOrigin? _transformOrigin; diff --git a/kraken/lib/src/css/visibility.dart b/kraken/lib/src/css/visibility.dart index 5cfcfaa97e..33271f0efe 100644 --- a/kraken/lib/src/css/visibility.dart +++ b/kraken/lib/src/css/visibility.dart @@ -15,7 +15,7 @@ mixin CSSVisibilityMixin on AbstractRenderStyle { void set visibility(Visibility value) { if (_visibility == value) return; _visibility = value; - renderBoxModel!.markNeedsPaint(); + renderBoxModel?.markNeedsPaint(); } @override From 13bbe58356d029e63e9628bd5e3458a0f6ce6330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Wed, 1 Dec 2021 16:32:41 +0800 Subject: [PATCH 114/167] fix: rename to RenderStyleBase --- kraken/lib/src/css/animation.dart | 4 ++-- kraken/lib/src/css/background.dart | 2 +- kraken/lib/src/css/border.dart | 4 ++-- kraken/lib/src/css/border_radius.dart | 2 +- kraken/lib/src/css/box.dart | 2 +- kraken/lib/src/css/box_shadow.dart | 2 +- kraken/lib/src/css/content_visibility.dart | 2 +- kraken/lib/src/css/display.dart | 2 +- kraken/lib/src/css/filter.dart | 2 +- kraken/lib/src/css/flexbox.dart | 2 +- kraken/lib/src/css/inline.dart | 2 +- kraken/lib/src/css/margin.dart | 2 +- kraken/lib/src/css/matrix.dart | 4 ++-- kraken/lib/src/css/object_fit.dart | 2 +- kraken/lib/src/css/object_position.dart | 2 +- kraken/lib/src/css/opacity.dart | 2 +- kraken/lib/src/css/overflow.dart | 2 +- kraken/lib/src/css/padding.dart | 2 +- kraken/lib/src/css/position.dart | 2 +- kraken/lib/src/css/render_style.dart | 6 +++--- kraken/lib/src/css/sizing.dart | 2 +- kraken/lib/src/css/sliver.dart | 2 +- kraken/lib/src/css/text.dart | 2 +- kraken/lib/src/css/transform.dart | 2 +- kraken/lib/src/css/transition.dart | 2 +- kraken/lib/src/css/values/length.dart | 10 +++++----- kraken/lib/src/css/visibility.dart | 2 +- 27 files changed, 36 insertions(+), 36 deletions(-) diff --git a/kraken/lib/src/css/animation.dart b/kraken/lib/src/css/animation.dart index e1d354ef30..130bcc69ba 100644 --- a/kraken/lib/src/css/animation.dart +++ b/kraken/lib/src/css/animation.dart @@ -441,7 +441,7 @@ class _Interpolation { } class KeyframeEffect extends AnimationEffect { - AbstractRenderStyle renderStyle; + RenderStyleBase renderStyle; Element? target; late List<_Interpolation> _interpolations; double? _progress; @@ -474,7 +474,7 @@ class KeyframeEffect extends AnimationEffect { return progress < 0.5 ? start : end; } - static List<_Interpolation> _makeInterpolations(Map> propertySpecificKeyframeGroups, AbstractRenderStyle? renderStyle) { + static List<_Interpolation> _makeInterpolations(Map> propertySpecificKeyframeGroups, RenderStyleBase? renderStyle) { List<_Interpolation> interpolations = []; propertySpecificKeyframeGroups.forEach((String property, List keyframes) { diff --git a/kraken/lib/src/css/background.dart b/kraken/lib/src/css/background.dart index 170dd6b04b..eb6bf4b286 100644 --- a/kraken/lib/src/css/background.dart +++ b/kraken/lib/src/css/background.dart @@ -81,7 +81,7 @@ enum CSSBackgroundImageType { image, } -mixin CSSBackgroundMixin on AbstractRenderStyle { +mixin CSSBackgroundMixin on RenderStyleBase { static CSSBackgroundPosition DEFAULT_BACKGROUND_POSITION = CSSBackgroundPosition(percentage: -1); static CSSBackgroundSize DEFAULT_BACKGROUND_SIZE = CSSBackgroundSize(fit: BoxFit.none); diff --git a/kraken/lib/src/css/border.dart b/kraken/lib/src/css/border.dart index 0da0839ce5..4cecd08690 100644 --- a/kraken/lib/src/css/border.dart +++ b/kraken/lib/src/css/border.dart @@ -24,7 +24,7 @@ enum CSSBorderStyleType { outset, } -mixin CSSBorderMixin on AbstractRenderStyle { +mixin CSSBorderMixin on RenderStyleBase { // Effective border widths. These are used to calculate the // dimensions of the border box. @@ -268,7 +268,7 @@ class CSSBorderSide { static BorderSide none = BorderSide(color: defaultBorderColor, width: 0.0, style: BorderStyle.none); - static BorderSide? _getBorderSide(AbstractRenderStyle renderStyle, String side) { + static BorderSide? _getBorderSide(RenderStyleBase renderStyle, String side) { BorderStyle? borderStyle; CSSLengthValue? borderWidth; Color? borderColor; diff --git a/kraken/lib/src/css/border_radius.dart b/kraken/lib/src/css/border_radius.dart index d20e776149..49b0b9a35f 100644 --- a/kraken/lib/src/css/border_radius.dart +++ b/kraken/lib/src/css/border_radius.dart @@ -2,7 +2,7 @@ import 'dart:ui'; import 'package:kraken/css.dart'; -mixin CSSBorderRadiusMixin on AbstractRenderStyle { +mixin CSSBorderRadiusMixin on RenderStyleBase { CSSBorderRadius? _borderTopLeftRadius; set borderTopLeftRadius(CSSBorderRadius? value) { if (value == _borderTopLeftRadius) return; diff --git a/kraken/lib/src/css/box.dart b/kraken/lib/src/css/box.dart index 269ef1d492..5ab9417399 100644 --- a/kraken/lib/src/css/box.dart +++ b/kraken/lib/src/css/box.dart @@ -11,7 +11,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; // CSS Box Model: https://drafts.csswg.org/css-box-4/ -mixin CSSBoxMixin on AbstractRenderStyle { +mixin CSSBoxMixin on RenderStyleBase { final DecorationPosition decorationPosition = DecorationPosition.background; final ImageConfiguration imageConfiguration = ImageConfiguration.empty; diff --git a/kraken/lib/src/css/box_shadow.dart b/kraken/lib/src/css/box_shadow.dart index 2f681375f1..389c2aa578 100644 --- a/kraken/lib/src/css/box_shadow.dart +++ b/kraken/lib/src/css/box_shadow.dart @@ -1,6 +1,6 @@ import 'package:kraken/css.dart'; -mixin CSSBoxShadowMixin on AbstractRenderStyle { +mixin CSSBoxShadowMixin on RenderStyleBase { List? _boxShadow; set boxShadow(List? value) { if (value == _boxShadow) return; diff --git a/kraken/lib/src/css/content_visibility.dart b/kraken/lib/src/css/content_visibility.dart index aafb4c14d9..a1e41d67f6 100644 --- a/kraken/lib/src/css/content_visibility.dart +++ b/kraken/lib/src/css/content_visibility.dart @@ -13,7 +13,7 @@ enum ContentVisibility { visible } -mixin CSSContentVisibilityMixin on AbstractRenderStyle { +mixin CSSContentVisibilityMixin on RenderStyleBase { /// Whether the child is hidden from the rest of the tree. /// diff --git a/kraken/lib/src/css/display.dart b/kraken/lib/src/css/display.dart index 785052df26..7df971f2fc 100644 --- a/kraken/lib/src/css/display.dart +++ b/kraken/lib/src/css/display.dart @@ -19,7 +19,7 @@ enum CSSDisplay { none } -mixin CSSDisplayMixin on AbstractRenderStyle { +mixin CSSDisplayMixin on RenderStyleBase { CSSDisplay? _display; diff --git a/kraken/lib/src/css/filter.dart b/kraken/lib/src/css/filter.dart index 65fc348f57..d8a7c9f5e3 100644 --- a/kraken/lib/src/css/filter.dart +++ b/kraken/lib/src/css/filter.dart @@ -91,7 +91,7 @@ List _multiplyMatrix5(List? a, List b) { /// Impl W3C Filter Effects Spec: /// https://www.w3.org/TR/filter-effects-1/#definitions -mixin CSSFilterEffectsMixin on AbstractRenderStyle { +mixin CSSFilterEffectsMixin on RenderStyleBase { // Get the color filter. // eg: 'grayscale(1) grayscale(0.5)' -> matrix5(grayscale(1)) · matrix5(grayscale(0.5)) diff --git a/kraken/lib/src/css/flexbox.dart b/kraken/lib/src/css/flexbox.dart index ce61f9e6d2..398df9aaaf 100644 --- a/kraken/lib/src/css/flexbox.dart +++ b/kraken/lib/src/css/flexbox.dart @@ -173,7 +173,7 @@ enum AlignSelf { baseline } -mixin CSSFlexboxMixin on AbstractRenderStyle { +mixin CSSFlexboxMixin on RenderStyleBase { @override FlexDirection get flexDirection => _flexDirection ?? FlexDirection.row; diff --git a/kraken/lib/src/css/inline.dart b/kraken/lib/src/css/inline.dart index 5802b50d3d..878c50021d 100644 --- a/kraken/lib/src/css/inline.dart +++ b/kraken/lib/src/css/inline.dart @@ -24,7 +24,7 @@ enum VerticalAlign { /// middle, } -mixin CSSInlineMixin on AbstractRenderStyle { +mixin CSSInlineMixin on RenderStyleBase { @override VerticalAlign get verticalAlign => _verticalAlign; VerticalAlign _verticalAlign = VerticalAlign.baseline; diff --git a/kraken/lib/src/css/margin.dart b/kraken/lib/src/css/margin.dart index 98e2f28113..9650254749 100644 --- a/kraken/lib/src/css/margin.dart +++ b/kraken/lib/src/css/margin.dart @@ -9,7 +9,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; import 'package:kraken/rendering.dart'; -mixin CSSMarginMixin on AbstractRenderStyle { +mixin CSSMarginMixin on RenderStyleBase { /// The amount to margin the child in each dimension. /// diff --git a/kraken/lib/src/css/matrix.dart b/kraken/lib/src/css/matrix.dart index 2f8e2d5d57..77f5093bee 100644 --- a/kraken/lib/src/css/matrix.dart +++ b/kraken/lib/src/css/matrix.dart @@ -577,7 +577,7 @@ class CSSMatrix { static Matrix4 initial = Matrix4.identity(); - static Matrix4? computeTransformMatrix(List transform, AbstractRenderStyle renderStyle) { + static Matrix4? computeTransformMatrix(List transform, RenderStyleBase renderStyle) { Matrix4? matrix4; for (CSSFunctionalNotation method in transform) { Matrix4? transform = _computeMatrix(method, renderStyle); @@ -592,7 +592,7 @@ class CSSMatrix { return matrix4; } - static Matrix4? _computeMatrix(CSSFunctionalNotation method, AbstractRenderStyle renderStyle) { + static Matrix4? _computeMatrix(CSSFunctionalNotation method, RenderStyleBase renderStyle) { switch (method.name) { case MATRIX: if (method.args.length == 6) { diff --git a/kraken/lib/src/css/object_fit.dart b/kraken/lib/src/css/object_fit.dart index 34fa5e67f2..560c7b9688 100644 --- a/kraken/lib/src/css/object_fit.dart +++ b/kraken/lib/src/css/object_fit.dart @@ -6,7 +6,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; -mixin CSSObjectFitMixin on AbstractRenderStyle { +mixin CSSObjectFitMixin on RenderStyleBase { @override BoxFit get objectFit => _objectFit; diff --git a/kraken/lib/src/css/object_position.dart b/kraken/lib/src/css/object_position.dart index 6c33f486c0..70dd5eff72 100644 --- a/kraken/lib/src/css/object_position.dart +++ b/kraken/lib/src/css/object_position.dart @@ -6,7 +6,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; -mixin CSSObjectPositionMixin on AbstractRenderStyle { +mixin CSSObjectPositionMixin on RenderStyleBase { @override Alignment get objectPosition => _objectPosition; Alignment _objectPosition = Alignment.center; diff --git a/kraken/lib/src/css/opacity.dart b/kraken/lib/src/css/opacity.dart index d7d6609ec7..7192a7f286 100644 --- a/kraken/lib/src/css/opacity.dart +++ b/kraken/lib/src/css/opacity.dart @@ -8,7 +8,7 @@ import 'dart:ui' as ui; import 'package:kraken/css.dart'; import 'package:kraken/rendering.dart'; -mixin CSSOpacityMixin on AbstractRenderStyle { +mixin CSSOpacityMixin on RenderStyleBase { /// The fraction to scale the child's alpha value. /// diff --git a/kraken/lib/src/css/overflow.dart b/kraken/lib/src/css/overflow.dart index 11eb2b9a51..5d140798bd 100644 --- a/kraken/lib/src/css/overflow.dart +++ b/kraken/lib/src/css/overflow.dart @@ -48,7 +48,7 @@ List _scrollingContentBoxCopyStyles = [ LINE_CLAMP, ]; -mixin CSSOverflowMixin on AbstractRenderStyle { +mixin CSSOverflowMixin on RenderStyleBase { @override CSSOverflowType get overflowX => _overflowX ?? CSSOverflowType.visible; CSSOverflowType? _overflowX; diff --git a/kraken/lib/src/css/padding.dart b/kraken/lib/src/css/padding.dart index 2f27a11c38..d2a73e3605 100644 --- a/kraken/lib/src/css/padding.dart +++ b/kraken/lib/src/css/padding.dart @@ -9,7 +9,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; import 'package:kraken/rendering.dart'; -mixin CSSPaddingMixin on AbstractRenderStyle { +mixin CSSPaddingMixin on RenderStyleBase { /// The amount to pad the child in each dimension. /// /// If this is set to an [EdgeInsetsDirectional] object, then [textDirection] diff --git a/kraken/lib/src/css/position.dart b/kraken/lib/src/css/position.dart index c290e8cfab..4a40f4dd2e 100644 --- a/kraken/lib/src/css/position.dart +++ b/kraken/lib/src/css/position.dart @@ -14,7 +14,7 @@ enum CSSPositionType { sticky, } -mixin CSSPositionMixin on AbstractRenderStyle { +mixin CSSPositionMixin on RenderStyleBase { static const CSSPositionType DEFAULT_POSITION_TYPE = CSSPositionType.static; diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 18152a786a..ac4fb054d8 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -13,10 +13,10 @@ import 'package:kraken/rendering.dart'; /// The abstract class for render-style, declare the /// getter interface for all available CSS rule. -abstract class AbstractRenderStyle { +abstract class RenderStyleBase { // Common Element get target; - AbstractRenderStyle? get parent; + RenderStyleBase? get parent; getProperty(String key); // Geometry @@ -161,7 +161,7 @@ abstract class AbstractRenderStyle { } class RenderStyle - extends AbstractRenderStyle + extends RenderStyleBase with CSSSizingMixin, CSSPaddingMixin, diff --git a/kraken/lib/src/css/sizing.dart b/kraken/lib/src/css/sizing.dart index 21064f3b4e..2111c9041c 100644 --- a/kraken/lib/src/css/sizing.dart +++ b/kraken/lib/src/css/sizing.dart @@ -15,7 +15,7 @@ import 'package:kraken/rendering.dart'; /// - min-width /// - min-height -mixin CSSSizingMixin on AbstractRenderStyle { +mixin CSSSizingMixin on RenderStyleBase { // https://drafts.csswg.org/css-sizing-3/#preferred-size-properties // Name: width, height diff --git a/kraken/lib/src/css/sliver.dart b/kraken/lib/src/css/sliver.dart index c24b4c95ae..b8b444dd33 100644 --- a/kraken/lib/src/css/sliver.dart +++ b/kraken/lib/src/css/sliver.dart @@ -6,7 +6,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; -mixin CSSSliverMixin on AbstractRenderStyle { +mixin CSSSliverMixin on RenderStyleBase { @override Axis get sliverDirection => _sliverDirection; diff --git a/kraken/lib/src/css/text.dart b/kraken/lib/src/css/text.dart index 3b0f2914e0..f4af52faf0 100644 --- a/kraken/lib/src/css/text.dart +++ b/kraken/lib/src/css/text.dart @@ -12,7 +12,7 @@ final RegExp _commaRegExp = RegExp(r'\s*,\s*'); // CSS Text: https://drafts.csswg.org/css-text-3/ // CSS Text Decoration: https://drafts.csswg.org/css-text-decor-3/ // CSS Box Alignment: https://drafts.csswg.org/css-align/ -mixin CSSTextMixin on AbstractRenderStyle { +mixin CSSTextMixin on RenderStyleBase { bool get hasColor => _color != null; @override diff --git a/kraken/lib/src/css/transform.dart b/kraken/lib/src/css/transform.dart index 87abe62801..f67053f3e5 100644 --- a/kraken/lib/src/css/transform.dart +++ b/kraken/lib/src/css/transform.dart @@ -9,7 +9,7 @@ import 'package:kraken/rendering.dart'; import 'package:vector_math/vector_math_64.dart'; // CSS Transforms: https://drafts.csswg.org/css-transforms/ -mixin CSSTransformMixin on AbstractRenderStyle { +mixin CSSTransformMixin on RenderStyleBase { static Offset DEFAULT_TRANSFORM_OFFSET = Offset(0, 0); static Alignment DEFAULT_TRANSFORM_ALIGNMENT = Alignment.center; diff --git a/kraken/lib/src/css/transition.dart b/kraken/lib/src/css/transition.dart index e8a202aaeb..ff01489b82 100644 --- a/kraken/lib/src/css/transition.dart +++ b/kraken/lib/src/css/transition.dart @@ -179,7 +179,7 @@ enum CSSTransitionEvent { cancel, } -mixin CSSTransitionMixin on AbstractRenderStyle { +mixin CSSTransitionMixin on RenderStyleBase { // https://drafts.csswg.org/css-transitions/#transition-property-property // Name: transition-property diff --git a/kraken/lib/src/css/values/length.dart b/kraken/lib/src/css/values/length.dart index 880974754b..0f0a16e85d 100644 --- a/kraken/lib/src/css/values/length.dart +++ b/kraken/lib/src/css/values/length.dart @@ -65,7 +65,7 @@ class CSSLengthValue { // Length is applied in horizontal or vertical direction. Axis? axisType; - AbstractRenderStyle? renderStyle; + RenderStyleBase? renderStyle; String? propertyName; double? _computedValue; @@ -121,7 +121,7 @@ class CSSLengthValue { bool isPositioned = positionType == CSSPositionType.absolute || positionType == CSSPositionType.fixed; - AbstractRenderStyle? parentRenderStyle = renderStyle!.parent; + RenderStyleBase? parentRenderStyle = renderStyle!.parent; // Percentage relative width priority: logical width > renderer width double? relativeParentWidth = isPositioned ? @@ -166,7 +166,7 @@ class CSSLengthValue { // There are two exceptions when percentage height is resolved against actual render height of parent: // 1. positioned element // 2. parent is flex item - AbstractRenderStyle? grandParentRenderStyle = parentRenderStyle?.parent; + RenderStyleBase? grandParentRenderStyle = parentRenderStyle?.parent; bool isGrandParentFlexLayout = grandParentRenderStyle?.display == CSSDisplay.flex || grandParentRenderStyle?.display == CSSDisplay.inlineFlex; @@ -220,7 +220,7 @@ class CSSLengthValue { case FLEX_BASIS: // Flex-basis computation is called in RenderFlexLayout which // will ensure parent exists. - AbstractRenderStyle parentRenderStyle = renderStyle!.parent!; + RenderStyleBase parentRenderStyle = renderStyle!.parent!; double? mainContentSize = parentRenderStyle.flexDirection == FlexDirection.row ? parentRenderStyle.contentBoxLogicalWidth : parentRenderStyle.contentBoxLogicalHeight; @@ -416,7 +416,7 @@ class CSSLength { } } - static CSSLengthValue parseLength(String text, AbstractRenderStyle? renderStyle, [String? propertyName, Axis? axisType]) { + static CSSLengthValue parseLength(String text, RenderStyleBase? renderStyle, [String? propertyName, Axis? axisType]) { if (_cachedParsedLength.containsKey(text)) { return _cachedParsedLength[text]!; } diff --git a/kraken/lib/src/css/visibility.dart b/kraken/lib/src/css/visibility.dart index 33271f0efe..6fa23f7306 100644 --- a/kraken/lib/src/css/visibility.dart +++ b/kraken/lib/src/css/visibility.dart @@ -9,7 +9,7 @@ enum Visibility { hidden, } -mixin CSSVisibilityMixin on AbstractRenderStyle { +mixin CSSVisibilityMixin on RenderStyleBase { Visibility _visibility = Visibility.visible; void set visibility(Visibility value) { From 699f3133e7dab8e7591e6af2644a1a08b5c0e2f6 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Wed, 1 Dec 2021 19:14:13 +0800 Subject: [PATCH 115/167] fix: fix html parse memory leaks. --- bridge/bindings/qjs/html_parser.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bridge/bindings/qjs/html_parser.cc b/bridge/bindings/qjs/html_parser.cc index b63cce7983..0edcef6eb4 100644 --- a/bridge/bindings/qjs/html_parser.cc +++ b/bridge/bindings/qjs/html_parser.cc @@ -81,6 +81,8 @@ bool HTMLParser::parseHTML(const char* code, size_t codeLength, NodeInstance* ro size_t html_length = html.length(); auto* htmlTree = gumbo_parse_with_options(&kGumboDefaultOptions, html.c_str(), html_length); traverseHTML(rootNode, htmlTree->root); + // Free gumbo parse nodes. + gumbo_destroy_output(&kGumboDefaultOptions, htmlTree); } } else { KRAKEN_LOG(ERROR) << "Root node is null."; From 8661b8557e370b839af7192277e3a8c3568f68ba Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Wed, 1 Dec 2021 19:14:53 +0800 Subject: [PATCH 116/167] fix: fix nativeString memory leaks. --- bridge/bindings/qjs/dom/custom_event.cc | 2 +- bridge/bindings/qjs/dom/document.cc | 4 ++-- bridge/bindings/qjs/dom/element.cc | 12 +++++----- .../qjs/dom/elements/image_element.cc | 16 +++++++------- bridge/bindings/qjs/dom/event.cc | 6 ++--- bridge/bindings/qjs/dom/event_target.cc | 8 +++---- bridge/bindings/qjs/dom/events/touch_event.cc | 2 +- bridge/bindings/qjs/dom/node.cc | 14 ++++++------ bridge/bindings/qjs/dom/style_declaration.cc | 8 +++---- bridge/bindings/qjs/dom/text_node.cc | 6 ++--- bridge/bindings/qjs/js_context.cc | 19 ++++++++-------- bridge/bindings/qjs/js_context.h | 22 +++++++++++++++---- bridge/bindings/qjs/js_context_test.cc | 6 ++--- bridge/bindings/qjs/module_manager.cc | 21 +++++++++--------- bridge/bindings/qjs/native_value.cc | 11 ++++++---- bridge/bindings/qjs/qjs_patch.cc | 16 +++++++------- bridge/bindings/qjs/qjs_patch_test.cc | 1 + bridge/bridge_test_qjs.cc | 13 +++++------ bridge/include/kraken_bridge.h | 2 +- bridge/kraken_bridge.cc | 10 +++------ .../code_generator/src/genereate_source.ts | 12 +++++----- kraken/lib/src/bridge/from_native.dart | 2 +- 22 files changed, 113 insertions(+), 100 deletions(-) diff --git a/bridge/bindings/qjs/dom/custom_event.cc b/bridge/bindings/qjs/dom/custom_event.cc index 19e703e50b..c1f3727fac 100644 --- a/bridge/bindings/qjs/dom/custom_event.cc +++ b/bridge/bindings/qjs/dom/custom_event.cc @@ -27,7 +27,7 @@ JSValue CustomEvent::initCustomEvent(QjsContext* ctx, JSValue this_val, int argc } JSValue typeValue = argv[0]; - eventInstance->nativeEvent->type = jsValueToNativeString(ctx, typeValue); + eventInstance->nativeEvent->type = jsValueToNativeString(ctx, typeValue).release(); if (argc <= 2) { bool canBubble = JS_ToBool(ctx, argv[1]); diff --git a/bridge/bindings/qjs/dom/document.cc b/bridge/bindings/qjs/dom/document.cc index b7f5cc0ece..f440c1ccdd 100644 --- a/bridge/bindings/qjs/dom/document.cc +++ b/bridge/bindings/qjs/dom/document.cc @@ -140,8 +140,8 @@ JSValue Document::createEvent(QjsContext* ctx, JSValue this_val, int argc, JSVal JS_FreeCString(ctx, c_eventType); std::string eventType = std::string(c_eventType); if (eventType == "Event") { - NativeString* nativeEventType = jsValueToNativeString(ctx, eventTypeValue); - auto nativeEvent = new NativeEvent{nativeEventType}; + std::unique_ptr nativeEventType = jsValueToNativeString(ctx, eventTypeValue); + auto nativeEvent = new NativeEvent{nativeEventType.release()}; auto document = static_cast(JS_GetOpaque(this_val, Document::classId())); auto e = Event::buildEventInstance(eventType, document->context(), nativeEvent, false); diff --git a/bridge/bindings/qjs/dom/element.cc b/bridge/bindings/qjs/dom/element.cc index 8d3b3a55ef..5734b9f57c 100644 --- a/bridge/bindings/qjs/dom/element.cc +++ b/bridge/bindings/qjs/dom/element.cc @@ -213,8 +213,8 @@ JSValue Element::setAttribute(QjsContext* ctx, JSValue this_val, int argc, JSVal element->_didModifyAttribute(name, JS_ATOM_NULL, attributeAtom); } - NativeString* args_01 = stringToNativeString(name); - NativeString* args_02 = jsValueToNativeString(ctx, attributeString); + std::unique_ptr args_01 = stringToNativeString(name); + std::unique_ptr args_02 = jsValueToNativeString(ctx, attributeString); ::foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); @@ -267,7 +267,7 @@ JSValue Element::removeAttribute(QjsContext* ctx, JSValue this_val, int argc, JS element->m_attributes->removeAttribute(name); element->_didModifyAttribute(name, id, JS_ATOM_NULL); - NativeString* args_01 = stringToNativeString(name); + std::unique_ptr args_01 = stringToNativeString(name); ::foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::removeProperty, *args_01, nullptr); } @@ -399,8 +399,8 @@ PROP_SETTER(ElementInstance, className)(QjsContext* ctx, JSValue this_val, int a auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); JSAtom atom = JS_ValueToAtom(ctx, argv[0]); element->m_attributes->setAttribute("class", atom); - NativeString* args_01 = stringToNativeString("class"); - NativeString* args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString("class"); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); ::foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); JS_FreeAtom(ctx, atom); return JS_NULL; @@ -846,7 +846,7 @@ ElementInstance::ElementInstance(Element* element, std::string tagName, bool sho JS_DefinePropertyValueStr(m_ctx, instanceObject, "attributes", m_attributes->jsObject, JS_PROP_C_W_E); if (shouldAddUICommand) { - NativeString* args_01 = stringToNativeString(tagName); + std::unique_ptr args_01 = stringToNativeString(tagName); ::foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(m_eventTargetId, UICommand::createElement, *args_01, nativeEventTarget); } } diff --git a/bridge/bindings/qjs/dom/elements/image_element.cc b/bridge/bindings/qjs/dom/elements/image_element.cc index 6c237107bb..6d61ddd260 100644 --- a/bridge/bindings/qjs/dom/elements/image_element.cc +++ b/bridge/bindings/qjs/dom/elements/image_element.cc @@ -31,8 +31,8 @@ PROP_GETTER(ImageElementInstance, width)(QjsContext* ctx, JSValue this_val, int PROP_SETTER(ImageElementInstance, width)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string key = "width"; - NativeString* args_01 = stringToNativeString(key); - NativeString* args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); return JS_NULL; } @@ -44,8 +44,8 @@ PROP_GETTER(ImageElementInstance, height)(QjsContext* ctx, JSValue this_val, int PROP_SETTER(ImageElementInstance, height)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string key = "height"; - NativeString* args_01 = stringToNativeString(key); - NativeString* args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); return JS_NULL; } @@ -73,8 +73,8 @@ PROP_GETTER(ImageElementInstance, src)(QjsContext* ctx, JSValue this_val, int ar PROP_SETTER(ImageElementInstance, src)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string key = "src"; - NativeString* args_01 = stringToNativeString(key); - NativeString* args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); return JS_NULL; } @@ -86,8 +86,8 @@ PROP_GETTER(ImageElementInstance, loading)(QjsContext* ctx, JSValue this_val, in PROP_SETTER(ImageElementInstance, loading)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string key = "loading"; - NativeString* args_01 = stringToNativeString(key); - NativeString* args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); return JS_NULL; } diff --git a/bridge/bindings/qjs/dom/event.cc b/bridge/bindings/qjs/dom/event.cc index 1e3790ce75..677c08f580 100644 --- a/bridge/bindings/qjs/dom/event.cc +++ b/bridge/bindings/qjs/dom/event.cc @@ -32,7 +32,7 @@ JSValue Event::instanceConstructor(QjsContext* ctx, JSValue func_obj, JSValue th JSValue eventTypeValue = argv[0]; std::string eventType = jsValueToStdString(ctx, eventTypeValue); - auto* nativeEvent = new NativeEvent{stringToNativeString(eventType)}; + auto* nativeEvent = new NativeEvent{stringToNativeString(eventType).release()}; auto* event = Event::buildEventInstance(eventType, m_context, nativeEvent, false); return event->instanceObject; } @@ -200,7 +200,7 @@ JSValue Event::initEvent(QjsContext* ctx, JSValue this_val, int argc, JSValue* a } auto* event = static_cast(JS_GetOpaque(this_val, Event::kEventClassID)); - event->nativeEvent->type = jsValueToNativeString(ctx, typeValue); + event->nativeEvent->type = jsValueToNativeString(ctx, typeValue).release(); if (!JS_IsNull(bubblesValue)) { event->nativeEvent->bubbles = JS_IsBool(bubblesValue) ? 1 : 0; @@ -218,7 +218,7 @@ EventInstance* EventInstance::fromNativeEvent(Event* event, NativeEvent* nativeE EventInstance::EventInstance(Event* event, NativeEvent* nativeEvent) : nativeEvent(nativeEvent), Instance(event, "Event", nullptr, Event::kEventClassID, finalizer) {} EventInstance::EventInstance(Event* jsEvent, JSAtom eventType, JSValue eventInit) : Instance(jsEvent, "Event", nullptr, Event::kEventClassID, finalizer) { JSValue v = JS_AtomToValue(m_ctx, eventType); - nativeEvent = new NativeEvent{jsValueToNativeString(m_ctx, v)}; + nativeEvent = new NativeEvent{jsValueToNativeString(m_ctx, v).release()}; JS_FreeValue(m_ctx, v); auto ms = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); diff --git a/bridge/bindings/qjs/dom/event_target.cc b/bridge/bindings/qjs/dom/event_target.cc index 3728e29857..05a5fcfeeb 100644 --- a/bridge/bindings/qjs/dom/event_target.cc +++ b/bridge/bindings/qjs/dom/event_target.cc @@ -367,8 +367,8 @@ int EventTargetInstance::setProperty(QjsContext* ctx, JSValue obj, JSAtom atom, JS_SetProperty(ctx, eventTarget->m_properties, atom, JS_DupValue(ctx, value)); if (isJavaScriptExtensionElementInstance(eventTarget->context(), eventTarget->instanceObject) && !p->is_wide_char && p->u.str8[0] != '_') { - NativeString* args_01 = atomToNativeString(ctx, atom); - NativeString* args_02 = jsValueToNativeString(ctx, value); + std::unique_ptr args_01 = atomToNativeString(ctx, atom); + std::unique_ptr args_02 = jsValueToNativeString(ctx, value); foundation::UICommandBuffer::instance(eventTarget->m_contextId)->addCommand(eventTarget->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); } } @@ -390,7 +390,7 @@ JSValue EventTargetInstance::callNativeMethods(const char* method, int32_t argc, std::u16string methodString; fromUTF8(method, methodString); - NativeString m{reinterpret_cast(methodString.c_str()), static_cast(methodString.size())}; + NativeString m{reinterpret_cast(methodString.c_str()), static_cast(methodString.size())}; NativeValue nativeValue{}; nativeEventTarget->callNativeMethods(nativeEventTarget, &nativeValue, &m, argc, argv); @@ -425,7 +425,7 @@ void EventTargetInstance::setPropertyHandler(JSString* p, JSValue value) { int32_t eventHandlerLen = arrayGetLength(m_ctx, m_eventHandlers); if (eventHandlerLen == 0) { int32_t contextId = m_context->getContextId(); - NativeString* args_01 = atomToNativeString(m_ctx, atom); + std::unique_ptr args_01 = atomToNativeString(m_ctx, atom); int32_t type = JS_IsFunction(m_ctx, value) ? UICommand::addEvent : UICommand::removeEvent; foundation::UICommandBuffer::instance(contextId)->addCommand(m_eventTargetId, type, *args_01, nullptr); } diff --git a/bridge/bindings/qjs/dom/events/touch_event.cc b/bridge/bindings/qjs/dom/events/touch_event.cc index fee9b6c550..87abbece5a 100644 --- a/bridge/bindings/qjs/dom/events/touch_event.cc +++ b/bridge/bindings/qjs/dom/events/touch_event.cc @@ -162,7 +162,7 @@ JSValue TouchEvent::instanceConstructor(QjsContext* ctx, JSValue func_obj, JSVal } auto* nativeEvent = new NativeTouchEvent(); - nativeEvent->nativeEvent.type = jsValueToNativeString(ctx, eventTypeValue); + nativeEvent->nativeEvent.type = jsValueToNativeString(ctx, eventTypeValue).release(); if (JS_IsObject(eventInit)) { JSAtom touchesAtom = JS_NewAtom(m_ctx, "touches"); diff --git a/bridge/bindings/qjs/dom/node.cc b/bridge/bindings/qjs/dom/node.cc index 08e2bf08fa..5d83973894 100644 --- a/bridge/bindings/qjs/dom/node.cc +++ b/bridge/bindings/qjs/dom/node.cc @@ -275,7 +275,7 @@ JSValue Node::copyNodeValue(QjsContext* ctx, NodeInstance* node) { ElementInstance::copyNodeProperties(newElement, element); std::string newNodeEventTargetId = std::to_string(newElement->m_eventTargetId); - NativeString* args_01 = stringToNativeString(newNodeEventTargetId); + std::unique_ptr args_01 = stringToNativeString(newNodeEventTargetId); foundation::UICommandBuffer::instance(newElement->context()->getContextId())->addCommand(element->m_eventTargetId, UICommand::cloneNode, *args_01, nullptr); return newElement->instanceObject; @@ -443,8 +443,8 @@ void NodeInstance::internalAppendChild(NodeInstance* node) { std::string nodeEventTargetId = std::to_string(node->m_eventTargetId); std::string position = std::string("beforeend"); - NativeString* args_01 = stringToNativeString(nodeEventTargetId); - NativeString* args_02 = stringToNativeString(position); + std::unique_ptr args_01 = stringToNativeString(nodeEventTargetId); + std::unique_ptr args_02 = stringToNativeString(position); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(m_eventTargetId, UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); } @@ -505,8 +505,8 @@ JSValue NodeInstance::internalInsertBefore(NodeInstance* node, NodeInstance* ref std::string nodeEventTargetId = std::to_string(node->m_eventTargetId); std::string position = std::string("beforebegin"); - NativeString* args_01 = stringToNativeString(nodeEventTargetId); - NativeString* args_02 = stringToNativeString(position); + std::unique_ptr args_01 = stringToNativeString(nodeEventTargetId); + std::unique_ptr args_02 = stringToNativeString(position); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(referenceNode->m_eventTargetId, UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); } @@ -537,8 +537,8 @@ JSValue NodeInstance::internalReplaceChild(NodeInstance* newChild, NodeInstance* std::string newChildEventTargetId = std::to_string(newChild->m_eventTargetId); std::string position = std::string("afterend"); - NativeString* args_01 = stringToNativeString(newChildEventTargetId); - NativeString* args_02 = stringToNativeString(position); + std::unique_ptr args_01 = stringToNativeString(newChildEventTargetId); + std::unique_ptr args_02 = stringToNativeString(position); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(oldChild->m_eventTargetId, UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); diff --git a/bridge/bindings/qjs/dom/style_declaration.cc b/bridge/bindings/qjs/dom/style_declaration.cc index 1e0c7ba8af..f4aa051677 100644 --- a/bridge/bindings/qjs/dom/style_declaration.cc +++ b/bridge/bindings/qjs/dom/style_declaration.cc @@ -123,8 +123,8 @@ bool StyleDeclarationInstance::internalSetProperty(std::string& name, JSValue va properties[name] = jsValueToStdString(m_ctx, value); if (ownerEventTarget != nullptr) { - NativeString* args_01 = stringToNativeString(name); - NativeString* args_02 = jsValueToNativeString(m_ctx, value); + std::unique_ptr args_01 = stringToNativeString(name); + std::unique_ptr args_02 = jsValueToNativeString(m_ctx, value); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(ownerEventTarget->eventTargetId(), UICommand::setStyle, *args_01, *args_02, nullptr); } @@ -141,8 +141,8 @@ void StyleDeclarationInstance::internalRemoveProperty(std::string& name) { properties.erase(name); if (ownerEventTarget != nullptr) { - NativeString* args_01 = stringToNativeString(name); - NativeString* args_02 = jsValueToNativeString(m_ctx, JS_NULL); + std::unique_ptr args_01 = stringToNativeString(name); + std::unique_ptr args_02 = jsValueToNativeString(m_ctx, JS_NULL); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(ownerEventTarget->eventTargetId(), UICommand::setStyle, *args_01, *args_02, nullptr); } } diff --git a/bridge/bindings/qjs/dom/text_node.cc b/bridge/bindings/qjs/dom/text_node.cc index bbd4b660d7..b72a6996f7 100644 --- a/bridge/bindings/qjs/dom/text_node.cc +++ b/bridge/bindings/qjs/dom/text_node.cc @@ -65,7 +65,7 @@ PROP_SETTER(TextNodeInstance, nodeName)(QjsContext* ctx, JSValue this_val, int a TextNodeInstance::TextNodeInstance(TextNode* textNode, JSValue text) : NodeInstance(textNode, NodeType::TEXT_NODE, DocumentInstance::instance(Document::instance(textNode->m_context)), TextNode::classId(), "TextNode"), m_data(JS_DupValue(m_ctx, text)) { - NativeString* args_01 = jsValueToNativeString(m_ctx, m_data); + std::unique_ptr args_01 = jsValueToNativeString(m_ctx, m_data); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(m_eventTargetId, UICommand::createTextNode, *args_01, nativeEventTarget); } @@ -91,8 +91,8 @@ void TextNodeInstance::internalSetTextContent(JSValue content) { m_data = JS_DupValue(m_ctx, content); std::string key = "data"; - NativeString* args_01 = stringToNativeString(key); - NativeString* args_02 = jsValueToNativeString(m_ctx, content); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(m_ctx, content); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); } } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/js_context.cc b/bridge/bindings/qjs/js_context.cc index b76463ebc3..499fbaf1f5 100644 --- a/bridge/bindings/qjs/js_context.cc +++ b/bridge/bindings/qjs/js_context.cc @@ -332,7 +332,7 @@ void JSContext::promiseRejectTracker(QjsContext* ctx, JSValue promise, JSValue r context->dispatchGlobalPromiseRejectionEvent(promise, reason); } -NativeString* jsValueToNativeString(QjsContext* ctx, JSValue value) { +std::unique_ptr jsValueToNativeString(QjsContext* ctx, JSValue value) { bool isValueString = true; if (JS_IsNull(value)) { value = JS_NewString(ctx, ""); @@ -344,15 +344,14 @@ NativeString* jsValueToNativeString(QjsContext* ctx, JSValue value) { uint32_t length; uint16_t* buffer = JS_ToUnicode(ctx, value, &length); - NativeString tmp{}; - tmp.string = buffer; - tmp.length = length; - NativeString* cloneString = tmp.clone(); + std::unique_ptr ptr = std::make_unique(); + ptr->string = buffer; + ptr->length = length; if (!isValueString) { JS_FreeValue(ctx, value); } - return cloneString; + return ptr; } void buildUICommandArgs(QjsContext* ctx, JSValue key, NativeString& args_01) { @@ -365,18 +364,18 @@ void buildUICommandArgs(QjsContext* ctx, JSValue key, NativeString& args_01) { args_01.length = length; } -NativeString* stringToNativeString(const std::string& string) { +std::unique_ptr stringToNativeString(const std::string& string) { std::u16string utf16; fromUTF8(string, utf16); NativeString tmp{}; tmp.string = reinterpret_cast(utf16.c_str()); tmp.length = utf16.size(); - return tmp.clone(); + return std::unique_ptr(tmp.clone()); } -NativeString* atomToNativeString(QjsContext* ctx, JSAtom atom) { +std::unique_ptr atomToNativeString(QjsContext* ctx, JSAtom atom) { JSValue stringValue = JS_AtomToString(ctx, atom); - NativeString* string = jsValueToNativeString(ctx, stringValue); + std::unique_ptr string = jsValueToNativeString(ctx, stringValue); JS_FreeValue(ctx, stringValue); return string; } diff --git a/bridge/bindings/qjs/js_context.h b/bridge/bindings/qjs/js_context.h index d482d9dc62..5ab4b24f83 100644 --- a/bridge/bindings/qjs/js_context.h +++ b/bridge/bindings/qjs/js_context.h @@ -193,19 +193,33 @@ class JSValueHolder { }; std::unique_ptr createJSContext(int32_t contextId, const JSExceptionHandler& handler, void* owner); -NativeString* jsValueToNativeString(QjsContext* ctx, JSValue value); + +// Convert to string and return a full copy of NativeString from JSValue. +std::unique_ptr jsValueToNativeString(QjsContext* ctx, JSValue value); + void buildUICommandArgs(QjsContext* ctx, JSValue key, NativeString& args_01); -NativeString* stringToNativeString(const std::string& string); -NativeString* atomToNativeString(QjsContext* ctx, JSAtom atom); + +// Encode utf-8 to utf-16, and return a full copy of NativeString. +std::unique_ptr stringToNativeString(const std::string& string); + +// Return a full copy of NativeString form JSAtom. +std::unique_ptr atomToNativeString(QjsContext* ctx, JSAtom atom); + +// Convert to string and return a full copy of std::string from JSValue. std::string jsValueToStdString(QjsContext* ctx, JSValue& value); + +// Return a full copy of std::string form JSAtom. std::string jsAtomToStdString(QjsContext* ctx, JSAtom atom); -void extractErrorInfo(JSValueConst error); + +// JS array operation utilities. void arrayPushValue(QjsContext* ctx, JSValue array, JSValue val); void arrayInsert(QjsContext* ctx, JSValue array, uint32_t start, JSValue targetValue); int32_t arrayGetLength(QjsContext* ctx, JSValue array); int32_t arrayFindIdx(QjsContext* ctx, JSValue array, JSValue target); void arraySpliceValue(QjsContext* ctx, JSValue array, uint32_t start, uint32_t deleteCount); void arraySpliceValue(QjsContext* ctx, JSValue array, uint32_t start, uint32_t deleteCount, JSValue replacedValue); + +// JS object operation utilities. JSValue objectGetKeys(QjsContext* ctx, JSValue obj); } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/js_context_test.cc b/bridge/bindings/qjs/js_context_test.cc index fb8dd8f881..52d2aa4a3b 100644 --- a/bridge/bindings/qjs/js_context_test.cc +++ b/bridge/bindings/qjs/js_context_test.cc @@ -143,7 +143,7 @@ TEST(Context, evaluateByteCode) { TEST(jsValueToNativeString, utf8String) { auto bridge = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) {}); JSValue str = JS_NewString(bridge->getContext()->ctx(), "helloworld"); - NativeString* nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); + std::unique_ptr nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); EXPECT_EQ(nativeString->length, 10); uint8_t expectedString[10] = {104, 101, 108, 108, 111, 119, 111, 114, 108, 100}; for (int i = 0; i < 10; i++) { @@ -156,7 +156,7 @@ TEST(jsValueToNativeString, utf8String) { TEST(jsValueToNativeString, unicodeChinese) { auto bridge = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) {}); JSValue str = JS_NewString(bridge->getContext()->ctx(), "这是你的优乐美"); - NativeString* nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); + std::unique_ptr nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); std::u16string expectedString = u"这是你的优乐美"; EXPECT_EQ(nativeString->length, expectedString.size()); for (int i = 0; i < nativeString->length; i++) { @@ -169,7 +169,7 @@ TEST(jsValueToNativeString, unicodeChinese) { TEST(jsValueToNativeString, emoji) { auto bridge = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) {}); JSValue str = JS_NewString(bridge->getContext()->ctx(), "……🤪"); - NativeString* nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); + std::unique_ptr nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); std::u16string expectedString = u"……🤪"; EXPECT_EQ(nativeString->length, expectedString.length()); for (int i = 0; i < nativeString->length; i++) { diff --git a/bridge/bindings/qjs/module_manager.cc b/bridge/bindings/qjs/module_manager.cc index 3b4d217392..a6f20bdc1b 100644 --- a/bridge/bindings/qjs/module_manager.cc +++ b/bridge/bindings/qjs/module_manager.cc @@ -108,9 +108,9 @@ JSValue krakenInvokeModule(QjsContext* ctx, JSValueConst this_val, int argc, JSV #endif } - NativeString* moduleName = jsValueToNativeString(ctx, moduleNameValue); - NativeString* method = jsValueToNativeString(ctx, methodValue); - NativeString* params = nullptr; + std::unique_ptr moduleName = jsValueToNativeString(ctx, moduleNameValue); + std::unique_ptr method = jsValueToNativeString(ctx, methodValue); + std::unique_ptr params; if (!JS_IsNull(paramsValue)) { JSValue stringifyedValue = JS_JSONStringify(ctx, paramsValue, JS_NULL, JS_NULL); params = jsValueToNativeString(ctx, stringifyedValue); @@ -130,9 +130,15 @@ JSValue krakenInvokeModule(QjsContext* ctx, JSValueConst this_val, int argc, JSV NativeString* result; if (!JS_IsNull(callbackValue)) { - result = getDartMethod()->invokeModule(moduleContext, context->getContextId(), moduleName, method, params, handleInvokeModuleTransientCallback); + result = getDartMethod()->invokeModule(moduleContext, context->getContextId(), moduleName.get(), method.get(), params.get(), handleInvokeModuleTransientCallback); } else { - result = getDartMethod()->invokeModule(moduleContext, context->getContextId(), moduleName, method, params, handleInvokeModuleUnexpectedCallback); + result = getDartMethod()->invokeModule(moduleContext, context->getContextId(), moduleName.get(), method.get(), params.get(), handleInvokeModuleUnexpectedCallback); + } + + moduleName->free(); + method->free(); + if (params != nullptr) { + params->free(); } if (result == nullptr) { @@ -141,11 +147,6 @@ JSValue krakenInvokeModule(QjsContext* ctx, JSValueConst this_val, int argc, JSV JSValue resultString = JS_NewUnicodeString(context->runtime(), ctx, result->string, result->length); result->free(); - moduleName->free(); - method->free(); - if (params != nullptr) { - params->free(); - } return resultString; } diff --git a/bridge/bindings/qjs/native_value.cc b/bridge/bindings/qjs/native_value.cc index 164caf3154..9cb38e289f 100644 --- a/bridge/bindings/qjs/native_value.cc +++ b/bridge/bindings/qjs/native_value.cc @@ -28,8 +28,9 @@ NativeValue Native_NewString(NativeString* string) { } NativeValue Native_NewCString(std::string string) { - NativeString* nativeString = stringToNativeString(string); - return Native_NewString(nativeString); + std::unique_ptr nativeString = stringToNativeString(string); + // NativeString owned by NativeValue will be freed by users. + return Native_NewString(nativeString.release()); } NativeValue Native_NewFloat64(double value) { @@ -62,7 +63,8 @@ NativeValue Native_NewInt32(int32_t value) { NativeValue Native_NewJSON(JSContext* context, JSValue& value) { JSValue stringifiedValue = JS_JSONStringify(context->ctx(), value, JS_UNDEFINED, JS_UNDEFINED); - NativeString* string = jsValueToNativeString(context->ctx(), stringifiedValue); + // NativeString owned by NativeValue will be freed by users. + NativeString* string = jsValueToNativeString(context->ctx(), stringifiedValue).release(); NativeValue result = (NativeValue){ 0, .u = {.ptr = static_cast(string)}, @@ -110,7 +112,8 @@ NativeValue jsValueToNativeValue(QjsContext* ctx, JSValue& value) { return Native_NewInt32(v); } } else if (JS_IsString(value)) { - NativeString* string = jsValueToNativeString(ctx, value); + // NativeString owned by NativeValue will be freed by users. + NativeString* string = jsValueToNativeString(ctx, value).release(); return Native_NewString(string); } else if (JS_IsFunction(ctx, value)) { auto* context = static_cast(JS_GetContextOpaque(ctx)); diff --git a/bridge/bindings/qjs/qjs_patch.cc b/bridge/bindings/qjs/qjs_patch.cc index cacd8d9174..646ac1c490 100644 --- a/bridge/bindings/qjs/qjs_patch.cc +++ b/bridge/bindings/qjs/qjs_patch.cc @@ -233,30 +233,30 @@ uint16_t* JS_ToUnicode(JSContext* ctx, JSValueConst value, uint32_t* length) { if (JS_VALUE_GET_TAG(value) != JS_TAG_STRING) { value = JS_ToString(ctx, value); if (JS_IsException(value)) - return NULL; + return nullptr; } else { value = JS_DupValue(ctx, value); } + uint16_t* buffer; JSString* string = JS_VALUE_GET_STRING(value); if (!string->is_wide_char) { uint8_t* p = string->u.str8; uint32_t len = *length = string->len; - auto* newBuf = (uint16_t*)malloc(sizeof(uint16_t) * len * 2); + buffer = (uint16_t*)malloc(sizeof(uint16_t) * len * 2); for (size_t i = 0; i < len; i++) { - newBuf[i] = p[i]; - newBuf[i + 1] = 0x00; + buffer[i] = p[i]; + buffer[i + 1] = 0x00; } - JS_FreeValue(ctx, value); - return newBuf; } else { *length = string->len; + buffer = (uint16_t*)malloc(sizeof(uint16_t) * string->len); + memcpy(buffer, string->u.str16, sizeof(uint16_t) * string->len); } JS_FreeValue(ctx, value); - - return string->u.str16; + return buffer; } static JSString* js_alloc_string_rt(JSRuntime* rt, int max_len, int is_wide_char) { diff --git a/bridge/bindings/qjs/qjs_patch_test.cc b/bridge/bindings/qjs/qjs_patch_test.cc index ff3ebd2f7b..8ef564d28c 100644 --- a/bridge/bindings/qjs/qjs_patch_test.cc +++ b/bridge/bindings/qjs/qjs_patch_test.cc @@ -21,6 +21,7 @@ TEST(JS_ToUnicode, asciiWords) { JS_FreeValue(ctx, value); JS_FreeContext(ctx); JS_FreeRuntime(runtime); + delete buffer; } TEST(JS_ToUnicode, chineseWords) { diff --git a/bridge/bridge_test_qjs.cc b/bridge/bridge_test_qjs.cc index 35ddc8d226..9142d45f45 100644 --- a/bridge/bridge_test_qjs.cc +++ b/bridge/bridge_test_qjs.cc @@ -64,7 +64,7 @@ static JSValue matchImageSnapshot(QjsContext* ctx, JSValueConst this_val, int ar return JS_ThrowTypeError(ctx, "Failed to execute '__kraken_match_image_snapshot__': dart method (matchImageSnapshot) is not registered."); } - NativeString* screenShotNativeString = kraken::binding::qjs::jsValueToNativeString(ctx, screenShotValue); + std::unique_ptr screenShotNativeString = kraken::binding::qjs::jsValueToNativeString(ctx, screenShotValue); auto bridge = static_cast(static_cast(context->getOwner())->owner); auto* callbackContext = new ImageSnapShotContext{JS_DupValue(ctx, callbackValue), context}; list_add_tail(&callbackContext->link, &bridge->image_link); @@ -90,7 +90,7 @@ static JSValue matchImageSnapshot(QjsContext* ctx, JSValueConst this_val, int ar list_del(&callbackContext->link); }; - getDartMethod()->matchImageSnapshot(callbackContext, context->getContextId(), blob->bytes(), blob->size(), screenShotNativeString, fn); + getDartMethod()->matchImageSnapshot(callbackContext, context->getContextId(), blob->bytes(), blob->size(), screenShotNativeString.get(), fn); return JS_NULL; } @@ -178,8 +178,8 @@ static JSValue simulateInputText(QjsContext* ctx, JSValueConst this_val, int arg return JS_ThrowTypeError(ctx, "Failed to execute '__kraken_simulate_keypress__': first arguments should be a string"); } - NativeString* nativeString = kraken::binding::qjs::jsValueToNativeString(ctx, charStringValue); - getDartMethod()->simulateInputText(nativeString); + std::unique_ptr nativeString = kraken::binding::qjs::jsValueToNativeString(ctx, charStringValue); + getDartMethod()->simulateInputText(nativeString.get()); nativeString->free(); return JS_NULL; }; @@ -244,9 +244,8 @@ void JSBridgeTest::invokeExecuteTest(ExecuteCallback executeCallback) { return JS_ThrowTypeError(ctx, "failed to execute 'done': parameter 1 (status) is not a string"); } - NativeString* status = kraken::binding::qjs::jsValueToNativeString(ctx, statusValue); - callbackContext->executeCallback(callbackContext->context->getContextId(), status); - status->free(); + std::unique_ptr status = kraken::binding::qjs::jsValueToNativeString(ctx, statusValue); + callbackContext->executeCallback(callbackContext->context->getContextId(), status.get()); return JS_NULL; }; auto* callbackContext = new ExecuteCallbackContext(context.get(), executeCallback); diff --git a/bridge/include/kraken_bridge.h b/bridge/include/kraken_bridge.h index e7a375fc78..8cfb60928b 100644 --- a/bridge/include/kraken_bridge.h +++ b/bridge/include/kraken_bridge.h @@ -25,7 +25,7 @@ std::thread::id getUIThreadId(); struct NativeString { const uint16_t* string; - int32_t length; + uint32_t length; NativeString* clone(); void free(); diff --git a/bridge/kraken_bridge.cc b/bridge/kraken_bridge.cc index 8705282c8b..1ac0026bb4 100644 --- a/bridge/kraken_bridge.cc +++ b/bridge/kraken_bridge.cc @@ -251,13 +251,10 @@ int32_t profileModeEnabled() { } NativeString* NativeString::clone() { - NativeString* newNativeString = new NativeString(); - uint16_t* newString = new uint16_t[length]; - - for (size_t i = 0; i < length; i++) { - newString[i] = string[i]; - } + auto* newNativeString = new NativeString(); + auto* newString = new uint16_t[length]; + memcpy(newString, string, length * sizeof(uint16_t)); newNativeString->string = newString; newNativeString->length = length; return newNativeString; @@ -265,5 +262,4 @@ NativeString* NativeString::clone() { void NativeString::free() { delete[] string; - delete this; } diff --git a/bridge/scripts/code_generator/src/genereate_source.ts b/bridge/scripts/code_generator/src/genereate_source.ts index 2384dd3789..e3523a3177 100644 --- a/bridge/scripts/code_generator/src/genereate_source.ts +++ b/bridge/scripts/code_generator/src/genereate_source.ts @@ -27,7 +27,7 @@ JSValue ${object.name}::callNativeMethods(const char *method, int32_t argc, NativeString m{ reinterpret_cast(methodString.c_str()), - static_cast(methodString.size()) + static_cast(methodString.size()) }; NativeValue nativeValue{}; @@ -135,8 +135,8 @@ function generatePropsSetter(object: ClassObject, type: PropType, p: PropsDeclar let setterCode = ''; if (object.type == 'Element') { setterCode = `std::string key = "${p.name}"; - NativeString *args_01 = stringToNativeString(key); - NativeString *args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); foundation::UICommandBuffer::instance(${instanceName}->m_context->getContextId()) ->addCommand(${instanceName}->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); return JS_NULL;`; @@ -309,7 +309,7 @@ function generateEventConstructorCode(object: ClassObject) { } auto *nativeEvent = new Native${object.name}(); - nativeEvent->nativeEvent.type = jsValueToNativeString(ctx, eventTypeValue); + nativeEvent->nativeEvent.type = jsValueToNativeString(ctx, eventTypeValue).release(); ${generateEventInstanceConstructorCode(object)} @@ -333,14 +333,14 @@ function generateEventInstanceConstructorCode(object: ClassObject) { propApplyCode = `JS_ToInt32(m_ctx, reinterpret_cast(&nativeEvent->${p.name}), JS_GetProperty(m_ctx, eventInit, ${p.name}Atom));` } else if (p.kind === PropsDeclarationKind.string) { propApplyCode = addIndent(`JSValue v = JS_GetProperty(m_ctx, eventInit, ${p.name}Atom); - nativeEvent->${p.name} = jsValueToNativeString(m_ctx, v); + nativeEvent->${p.name} = jsValueToNativeString(m_ctx, v).release(); JS_FreeValue(m_ctx, v);`, 0); } else if (p.kind === PropsDeclarationKind.double) { propApplyCode = `JS_ToFloat64(m_ctx, &nativeEvent->${p.name}, JS_GetProperty(m_ctx, eventInit, ${p.name}Atom));`; } else if (p.kind === PropsDeclarationKind.object) { propApplyCode = addIndent(`JSValue v = JS_GetProperty(m_ctx, eventInit, ${p.name}Atom); JSValue json = JS_JSONStringify(m_ctx, v, JS_NULL, JS_NULL); - nativeEvent->${p.name} = jsValueToNativeString(m_ctx, json); + nativeEvent->${p.name} = jsValueToNativeString(m_ctx, json).release(); JS_FreeValue(m_ctx, json); JS_FreeValue(m_ctx, v);`, 0); } diff --git a/kraken/lib/src/bridge/from_native.dart b/kraken/lib/src/bridge/from_native.dart index 1e350abb59..6f23a4d843 100644 --- a/kraken/lib/src/bridge/from_native.dart +++ b/kraken/lib/src/bridge/from_native.dart @@ -25,7 +25,7 @@ import 'platform.dart'; class NativeString extends Struct { external Pointer string; - @Int32() + @Uint32() external int length; } From 12b3372ba30278e10e316814e22b099fdf4322a9 Mon Sep 17 00:00:00 2001 From: andycall Date: Thu, 2 Dec 2021 05:37:13 +0800 Subject: [PATCH 117/167] chore: trigger ci --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 61cd9c75a9..1e51155421 100644 --- a/README.md +++ b/README.md @@ -102,3 +102,4 @@ By contributing to Kraken, you agree that your contributions will be licensed un ```shell $ npm test ``` + From 656ed092bbb34d99b669ed2ffc0b7027393e045f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=93=E5=87=8C?= Date: Thu, 2 Dec 2021 11:23:08 +0800 Subject: [PATCH 118/167] refactor: using RenderStyle + CSSRenderStyle --- kraken/lib/src/css/animation.dart | 4 ++-- kraken/lib/src/css/background.dart | 2 +- kraken/lib/src/css/border.dart | 4 ++-- kraken/lib/src/css/border_radius.dart | 2 +- kraken/lib/src/css/box.dart | 2 +- kraken/lib/src/css/box_shadow.dart | 2 +- kraken/lib/src/css/content_visibility.dart | 2 +- kraken/lib/src/css/display.dart | 7 ++++--- kraken/lib/src/css/filter.dart | 2 +- kraken/lib/src/css/flexbox.dart | 3 ++- kraken/lib/src/css/inline.dart | 2 +- kraken/lib/src/css/margin.dart | 2 +- kraken/lib/src/css/matrix.dart | 4 ++-- kraken/lib/src/css/object_fit.dart | 2 +- kraken/lib/src/css/object_position.dart | 2 +- kraken/lib/src/css/opacity.dart | 2 +- kraken/lib/src/css/overflow.dart | 6 ++++-- kraken/lib/src/css/padding.dart | 2 +- kraken/lib/src/css/position.dart | 2 +- kraken/lib/src/css/render_style.dart | 21 +++++++++++++------ kraken/lib/src/css/sizing.dart | 2 +- kraken/lib/src/css/sliver.dart | 2 +- kraken/lib/src/css/text.dart | 5 +++-- kraken/lib/src/css/transform.dart | 2 +- kraken/lib/src/css/transition.dart | 10 ++++----- kraken/lib/src/css/values/length.dart | 10 ++++----- kraken/lib/src/css/visibility.dart | 2 +- kraken/lib/src/dom/element.dart | 17 +++++++-------- kraken/lib/src/dom/elements/body.dart | 3 +-- kraken/lib/src/dom/elements/input.dart | 2 +- .../src/rendering/box_decoration_painter.dart | 8 +++---- kraken/lib/src/rendering/box_model.dart | 12 ++++------- kraken/lib/src/rendering/flex.dart | 8 +++---- kraken/lib/src/rendering/flow.dart | 8 +++---- kraken/lib/src/rendering/intrinsic.dart | 12 ++--------- kraken/lib/src/rendering/sliver_list.dart | 2 +- kraken/lib/src/rendering/text.dart | 2 +- 37 files changed, 87 insertions(+), 95 deletions(-) diff --git a/kraken/lib/src/css/animation.dart b/kraken/lib/src/css/animation.dart index 130bcc69ba..928dc1d34e 100644 --- a/kraken/lib/src/css/animation.dart +++ b/kraken/lib/src/css/animation.dart @@ -441,7 +441,7 @@ class _Interpolation { } class KeyframeEffect extends AnimationEffect { - RenderStyleBase renderStyle; + RenderStyle renderStyle; Element? target; late List<_Interpolation> _interpolations; double? _progress; @@ -474,7 +474,7 @@ class KeyframeEffect extends AnimationEffect { return progress < 0.5 ? start : end; } - static List<_Interpolation> _makeInterpolations(Map> propertySpecificKeyframeGroups, RenderStyleBase? renderStyle) { + static List<_Interpolation> _makeInterpolations(Map> propertySpecificKeyframeGroups, RenderStyle? renderStyle) { List<_Interpolation> interpolations = []; propertySpecificKeyframeGroups.forEach((String property, List keyframes) { diff --git a/kraken/lib/src/css/background.dart b/kraken/lib/src/css/background.dart index eb6bf4b286..46d40e6f64 100644 --- a/kraken/lib/src/css/background.dart +++ b/kraken/lib/src/css/background.dart @@ -81,7 +81,7 @@ enum CSSBackgroundImageType { image, } -mixin CSSBackgroundMixin on RenderStyleBase { +mixin CSSBackgroundMixin on RenderStyle { static CSSBackgroundPosition DEFAULT_BACKGROUND_POSITION = CSSBackgroundPosition(percentage: -1); static CSSBackgroundSize DEFAULT_BACKGROUND_SIZE = CSSBackgroundSize(fit: BoxFit.none); diff --git a/kraken/lib/src/css/border.dart b/kraken/lib/src/css/border.dart index 4cecd08690..a10e2bee84 100644 --- a/kraken/lib/src/css/border.dart +++ b/kraken/lib/src/css/border.dart @@ -24,7 +24,7 @@ enum CSSBorderStyleType { outset, } -mixin CSSBorderMixin on RenderStyleBase { +mixin CSSBorderMixin on RenderStyle { // Effective border widths. These are used to calculate the // dimensions of the border box. @@ -268,7 +268,7 @@ class CSSBorderSide { static BorderSide none = BorderSide(color: defaultBorderColor, width: 0.0, style: BorderStyle.none); - static BorderSide? _getBorderSide(RenderStyleBase renderStyle, String side) { + static BorderSide? _getBorderSide(RenderStyle renderStyle, String side) { BorderStyle? borderStyle; CSSLengthValue? borderWidth; Color? borderColor; diff --git a/kraken/lib/src/css/border_radius.dart b/kraken/lib/src/css/border_radius.dart index 49b0b9a35f..5340944f56 100644 --- a/kraken/lib/src/css/border_radius.dart +++ b/kraken/lib/src/css/border_radius.dart @@ -2,7 +2,7 @@ import 'dart:ui'; import 'package:kraken/css.dart'; -mixin CSSBorderRadiusMixin on RenderStyleBase { +mixin CSSBorderRadiusMixin on RenderStyle { CSSBorderRadius? _borderTopLeftRadius; set borderTopLeftRadius(CSSBorderRadius? value) { if (value == _borderTopLeftRadius) return; diff --git a/kraken/lib/src/css/box.dart b/kraken/lib/src/css/box.dart index 5ab9417399..5ea710c727 100644 --- a/kraken/lib/src/css/box.dart +++ b/kraken/lib/src/css/box.dart @@ -11,7 +11,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; // CSS Box Model: https://drafts.csswg.org/css-box-4/ -mixin CSSBoxMixin on RenderStyleBase { +mixin CSSBoxMixin on RenderStyle { final DecorationPosition decorationPosition = DecorationPosition.background; final ImageConfiguration imageConfiguration = ImageConfiguration.empty; diff --git a/kraken/lib/src/css/box_shadow.dart b/kraken/lib/src/css/box_shadow.dart index 389c2aa578..43732a601c 100644 --- a/kraken/lib/src/css/box_shadow.dart +++ b/kraken/lib/src/css/box_shadow.dart @@ -1,6 +1,6 @@ import 'package:kraken/css.dart'; -mixin CSSBoxShadowMixin on RenderStyleBase { +mixin CSSBoxShadowMixin on RenderStyle { List? _boxShadow; set boxShadow(List? value) { if (value == _boxShadow) return; diff --git a/kraken/lib/src/css/content_visibility.dart b/kraken/lib/src/css/content_visibility.dart index a1e41d67f6..495368d3e9 100644 --- a/kraken/lib/src/css/content_visibility.dart +++ b/kraken/lib/src/css/content_visibility.dart @@ -13,7 +13,7 @@ enum ContentVisibility { visible } -mixin CSSContentVisibilityMixin on RenderStyleBase { +mixin CSSContentVisibilityMixin on RenderStyle { /// Whether the child is hidden from the rest of the tree. /// diff --git a/kraken/lib/src/css/display.dart b/kraken/lib/src/css/display.dart index 7df971f2fc..0d7efd53b3 100644 --- a/kraken/lib/src/css/display.dart +++ b/kraken/lib/src/css/display.dart @@ -19,7 +19,7 @@ enum CSSDisplay { none } -mixin CSSDisplayMixin on RenderStyleBase { +mixin CSSDisplayMixin on RenderStyle { CSSDisplay? _display; @@ -60,8 +60,9 @@ mixin CSSDisplayMixin on RenderStyleBase { /// Some layout effects require blockification or inlinification of the box type /// https://www.w3.org/TR/css-display-3/#transformations - CSSDisplay? get effectiveDisplay { - CSSDisplay? transformedDisplay = display; + @override + CSSDisplay get effectiveDisplay { + CSSDisplay transformedDisplay = display; // Must take `position` from style because it inited before flush pending properties. // Display as inline-block when element is positioned diff --git a/kraken/lib/src/css/filter.dart b/kraken/lib/src/css/filter.dart index d8a7c9f5e3..47515faa0d 100644 --- a/kraken/lib/src/css/filter.dart +++ b/kraken/lib/src/css/filter.dart @@ -91,7 +91,7 @@ List _multiplyMatrix5(List? a, List b) { /// Impl W3C Filter Effects Spec: /// https://www.w3.org/TR/filter-effects-1/#definitions -mixin CSSFilterEffectsMixin on RenderStyleBase { +mixin CSSFilterEffectsMixin on RenderStyle { // Get the color filter. // eg: 'grayscale(1) grayscale(0.5)' -> matrix5(grayscale(1)) · matrix5(grayscale(0.5)) diff --git a/kraken/lib/src/css/flexbox.dart b/kraken/lib/src/css/flexbox.dart index 398df9aaaf..1562a8e6f2 100644 --- a/kraken/lib/src/css/flexbox.dart +++ b/kraken/lib/src/css/flexbox.dart @@ -173,7 +173,7 @@ enum AlignSelf { baseline } -mixin CSSFlexboxMixin on RenderStyleBase { +mixin CSSFlexboxMixin on RenderStyle { @override FlexDirection get flexDirection => _flexDirection ?? FlexDirection.row; @@ -220,6 +220,7 @@ mixin CSSFlexboxMixin on RenderStyleBase { } } + @override AlignItems get effectiveAlignItems { if (CSSFlex.isVerticalFlexDirection(flexDirection)) { if (textAlign == TextAlign.right) { diff --git a/kraken/lib/src/css/inline.dart b/kraken/lib/src/css/inline.dart index 878c50021d..e30e4e114e 100644 --- a/kraken/lib/src/css/inline.dart +++ b/kraken/lib/src/css/inline.dart @@ -24,7 +24,7 @@ enum VerticalAlign { /// middle, } -mixin CSSInlineMixin on RenderStyleBase { +mixin CSSInlineMixin on RenderStyle { @override VerticalAlign get verticalAlign => _verticalAlign; VerticalAlign _verticalAlign = VerticalAlign.baseline; diff --git a/kraken/lib/src/css/margin.dart b/kraken/lib/src/css/margin.dart index 9650254749..2a8a4f5a89 100644 --- a/kraken/lib/src/css/margin.dart +++ b/kraken/lib/src/css/margin.dart @@ -9,7 +9,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; import 'package:kraken/rendering.dart'; -mixin CSSMarginMixin on RenderStyleBase { +mixin CSSMarginMixin on RenderStyle { /// The amount to margin the child in each dimension. /// diff --git a/kraken/lib/src/css/matrix.dart b/kraken/lib/src/css/matrix.dart index 77f5093bee..e6895b6d7c 100644 --- a/kraken/lib/src/css/matrix.dart +++ b/kraken/lib/src/css/matrix.dart @@ -577,7 +577,7 @@ class CSSMatrix { static Matrix4 initial = Matrix4.identity(); - static Matrix4? computeTransformMatrix(List transform, RenderStyleBase renderStyle) { + static Matrix4? computeTransformMatrix(List transform, RenderStyle renderStyle) { Matrix4? matrix4; for (CSSFunctionalNotation method in transform) { Matrix4? transform = _computeMatrix(method, renderStyle); @@ -592,7 +592,7 @@ class CSSMatrix { return matrix4; } - static Matrix4? _computeMatrix(CSSFunctionalNotation method, RenderStyleBase renderStyle) { + static Matrix4? _computeMatrix(CSSFunctionalNotation method, RenderStyle renderStyle) { switch (method.name) { case MATRIX: if (method.args.length == 6) { diff --git a/kraken/lib/src/css/object_fit.dart b/kraken/lib/src/css/object_fit.dart index 560c7b9688..adcf037f79 100644 --- a/kraken/lib/src/css/object_fit.dart +++ b/kraken/lib/src/css/object_fit.dart @@ -6,7 +6,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; -mixin CSSObjectFitMixin on RenderStyleBase { +mixin CSSObjectFitMixin on RenderStyle { @override BoxFit get objectFit => _objectFit; diff --git a/kraken/lib/src/css/object_position.dart b/kraken/lib/src/css/object_position.dart index 70dd5eff72..b6ca1c7898 100644 --- a/kraken/lib/src/css/object_position.dart +++ b/kraken/lib/src/css/object_position.dart @@ -6,7 +6,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; -mixin CSSObjectPositionMixin on RenderStyleBase { +mixin CSSObjectPositionMixin on RenderStyle { @override Alignment get objectPosition => _objectPosition; Alignment _objectPosition = Alignment.center; diff --git a/kraken/lib/src/css/opacity.dart b/kraken/lib/src/css/opacity.dart index 7192a7f286..23bb9172cc 100644 --- a/kraken/lib/src/css/opacity.dart +++ b/kraken/lib/src/css/opacity.dart @@ -8,7 +8,7 @@ import 'dart:ui' as ui; import 'package:kraken/css.dart'; import 'package:kraken/rendering.dart'; -mixin CSSOpacityMixin on RenderStyleBase { +mixin CSSOpacityMixin on RenderStyle { /// The fraction to scale the child's alpha value. /// diff --git a/kraken/lib/src/css/overflow.dart b/kraken/lib/src/css/overflow.dart index 5d140798bd..403c3d30da 100644 --- a/kraken/lib/src/css/overflow.dart +++ b/kraken/lib/src/css/overflow.dart @@ -48,7 +48,7 @@ List _scrollingContentBoxCopyStyles = [ LINE_CLAMP, ]; -mixin CSSOverflowMixin on RenderStyleBase { +mixin CSSOverflowMixin on RenderStyle { @override CSSOverflowType get overflowX => _overflowX ?? CSSOverflowType.visible; CSSOverflowType? _overflowX; @@ -65,6 +65,7 @@ mixin CSSOverflowMixin on RenderStyleBase { _overflowY = value; } + @override CSSOverflowType get effectiveOverflowX { if (overflowX == CSSOverflowType.visible && overflowY != CSSOverflowType.visible) { return CSSOverflowType.auto; @@ -72,6 +73,7 @@ mixin CSSOverflowMixin on RenderStyleBase { return overflowX; } + @override CSSOverflowType get effectiveOverflowY { if (overflowY == CSSOverflowType.visible && overflowX != CSSOverflowType.visible) { return CSSOverflowType.auto; @@ -219,7 +221,7 @@ mixin ElementOverflowMixin on ElementBase { // Sliver content has no multi scroll content box. if (scrollingContentBox == null) return; - RenderStyle scrollingContentRenderStyle = scrollingContentBox.renderStyle; + CSSRenderStyle scrollingContentRenderStyle = scrollingContentBox.renderStyle; switch (property) { case DISPLAY: diff --git a/kraken/lib/src/css/padding.dart b/kraken/lib/src/css/padding.dart index d2a73e3605..a9a6c2fe67 100644 --- a/kraken/lib/src/css/padding.dart +++ b/kraken/lib/src/css/padding.dart @@ -9,7 +9,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; import 'package:kraken/rendering.dart'; -mixin CSSPaddingMixin on RenderStyleBase { +mixin CSSPaddingMixin on RenderStyle { /// The amount to pad the child in each dimension. /// /// If this is set to an [EdgeInsetsDirectional] object, then [textDirection] diff --git a/kraken/lib/src/css/position.dart b/kraken/lib/src/css/position.dart index 4a40f4dd2e..c66318ff27 100644 --- a/kraken/lib/src/css/position.dart +++ b/kraken/lib/src/css/position.dart @@ -14,7 +14,7 @@ enum CSSPositionType { sticky, } -mixin CSSPositionMixin on RenderStyleBase { +mixin CSSPositionMixin on RenderStyle { static const CSSPositionType DEFAULT_POSITION_TYPE = CSSPositionType.static; diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index ac4fb054d8..ee4c86c3fc 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -13,10 +13,10 @@ import 'package:kraken/rendering.dart'; /// The abstract class for render-style, declare the /// getter interface for all available CSS rule. -abstract class RenderStyleBase { +abstract class RenderStyle { // Common Element get target; - RenderStyleBase? get parent; + RenderStyle? get parent; getProperty(String key); // Geometry @@ -107,15 +107,19 @@ abstract class RenderStyleBase { double? get contentBoxHeight; CSSPositionType get position; CSSDisplay get display; + CSSDisplay get effectiveDisplay; Alignment get objectPosition; CSSOverflowType get overflowX; CSSOverflowType get overflowY; + CSSOverflowType get effectiveOverflowX; + CSSOverflowType get effectiveOverflowY; // Flex FlexDirection get flexDirection; FlexWrap get flexWrap; JustifyContent get justifyContent; AlignItems get alignItems; + AlignItems get effectiveAlignItems; AlignContent get alignContent; AlignSelf get alignSelf; CSSLengthValue? get flexBasis; @@ -149,7 +153,10 @@ abstract class RenderStyleBase { void addFontRelativeProperty(String propertyName); void addRootFontRelativeProperty(String propertyName); + void addColorRelativeProperty(String propertyName); String? removeAnimationProperty(String propertyName); + double getWidthByIntrinsicRatio(); + double getHeightByIntrinsicRatio(); // Following properties used for exposing APIs // for class that extends [AbstractRenderStyle]. @@ -160,8 +167,8 @@ abstract class RenderStyleBase { double get rootFontSize => target.elementManager.getRootFontSize(); } -class RenderStyle - extends RenderStyleBase +class CSSRenderStyle + extends RenderStyle with CSSSizingMixin, CSSPaddingMixin, @@ -186,13 +193,13 @@ class RenderStyle CSSFilterEffectsMixin, CSSOpacityMixin, CSSTransitionMixin { - RenderStyle({ required this.target }); + CSSRenderStyle({ required this.target }); @override Element target; @override - RenderStyle? parent; + CSSRenderStyle? parent; @override getProperty(String name) { @@ -758,6 +765,7 @@ class RenderStyle } /// Get height of replaced element by intrinsic ratio if height is not defined + @override double getHeightByIntrinsicRatio() { // @TODO: move intrinsic width/height to renderStyle double? intrinsicWidth = renderBoxModel!.intrinsicWidth; @@ -774,6 +782,7 @@ class RenderStyle } /// Get width of replaced element by intrinsic ratio if width is not defined + @override double getWidthByIntrinsicRatio() { // @TODO: move intrinsic width/height to renderStyle double? intrinsicHeight = renderBoxModel!.intrinsicHeight; diff --git a/kraken/lib/src/css/sizing.dart b/kraken/lib/src/css/sizing.dart index 2111c9041c..2ad6d7cb40 100644 --- a/kraken/lib/src/css/sizing.dart +++ b/kraken/lib/src/css/sizing.dart @@ -15,7 +15,7 @@ import 'package:kraken/rendering.dart'; /// - min-width /// - min-height -mixin CSSSizingMixin on RenderStyleBase { +mixin CSSSizingMixin on RenderStyle { // https://drafts.csswg.org/css-sizing-3/#preferred-size-properties // Name: width, height diff --git a/kraken/lib/src/css/sliver.dart b/kraken/lib/src/css/sliver.dart index b8b444dd33..983d39e2fa 100644 --- a/kraken/lib/src/css/sliver.dart +++ b/kraken/lib/src/css/sliver.dart @@ -6,7 +6,7 @@ import 'package:flutter/rendering.dart'; import 'package:kraken/css.dart'; -mixin CSSSliverMixin on RenderStyleBase { +mixin CSSSliverMixin on RenderStyle { @override Axis get sliverDirection => _sliverDirection; diff --git a/kraken/lib/src/css/text.dart b/kraken/lib/src/css/text.dart index f4af52faf0..6d1be62b6e 100644 --- a/kraken/lib/src/css/text.dart +++ b/kraken/lib/src/css/text.dart @@ -12,7 +12,7 @@ final RegExp _commaRegExp = RegExp(r'\s*,\s*'); // CSS Text: https://drafts.csswg.org/css-text-3/ // CSS Text Decoration: https://drafts.csswg.org/css-text-decor-3/ // CSS Box Alignment: https://drafts.csswg.org/css-align/ -mixin CSSTextMixin on RenderStyleBase { +mixin CSSTextMixin on RenderStyle { bool get hasColor => _color != null; @override @@ -42,6 +42,7 @@ mixin CSSTextMixin on RenderStyleBase { // Current not update the dependent property relative to the color. final Map _colorRelativeProperties = {}; + @override void addColorRelativeProperty(String propertyName) { _colorRelativeProperties[propertyName] = true; } @@ -412,7 +413,7 @@ mixin CSSTextMixin on RenderStyleBase { return alignment; } - static TextSpan createTextSpan(String? text, RenderStyle renderStyle) { + static TextSpan createTextSpan(String? text, CSSRenderStyle renderStyle) { /// Creates a new TextStyle object. /// color: The color to use when painting the text. If this is specified, foreground must be null. /// decoration: The decorations to paint near the text (e.g., an underline). diff --git a/kraken/lib/src/css/transform.dart b/kraken/lib/src/css/transform.dart index f67053f3e5..14517cc5c2 100644 --- a/kraken/lib/src/css/transform.dart +++ b/kraken/lib/src/css/transform.dart @@ -9,7 +9,7 @@ import 'package:kraken/rendering.dart'; import 'package:vector_math/vector_math_64.dart'; // CSS Transforms: https://drafts.csswg.org/css-transforms/ -mixin CSSTransformMixin on RenderStyleBase { +mixin CSSTransformMixin on RenderStyle { static Offset DEFAULT_TRANSFORM_OFFSET = Offset(0, 0); static Alignment DEFAULT_TRANSFORM_ALIGNMENT = Alignment.center; diff --git a/kraken/lib/src/css/transition.dart b/kraken/lib/src/css/transition.dart index ff01489b82..93b343f799 100644 --- a/kraken/lib/src/css/transition.dart +++ b/kraken/lib/src/css/transition.dart @@ -58,7 +58,7 @@ double? _parseLength(String length, RenderStyle renderStyle, String property) { return CSSLength.parseLength(length, renderStyle, property).computedValue; } -void _updateLength(double oldLengthValue, double newLengthValue, double progress, String property, RenderStyle renderStyle) { +void _updateLength(double oldLengthValue, double newLengthValue, double progress, String property, CSSRenderStyle renderStyle) { double value = oldLengthValue * (1 - progress) + newLengthValue * progress; renderStyle.target.setRenderStyleProperty(property, CSSLengthValue(value, CSSLengthType.PX)); } @@ -67,7 +67,7 @@ FontWeight _parseFontWeight(String fontWeight, RenderStyle renderStyle, String p return CSSText.resolveFontWeight(fontWeight); } -void _updateFontWeight(FontWeight oldValue, FontWeight newValue, double progress, String property, RenderStyle renderStyle) { +void _updateFontWeight(FontWeight oldValue, FontWeight newValue, double progress, String property, CSSRenderStyle renderStyle) { FontWeight? fontWeight = FontWeight.lerp(oldValue, newValue, progress); switch (property) { case FONT_WEIGHT: @@ -96,7 +96,7 @@ double _parseLineHeight(String lineHeight, RenderStyle renderStyle, String prope return CSSLength.parseLength(lineHeight, renderStyle, LINE_HEIGHT).computedValue; } -void _updateLineHeight(double oldValue, double newValue, double progress, String property, RenderStyle renderStyle) { +void _updateLineHeight(double oldValue, double newValue, double progress, String property, CSSRenderStyle renderStyle) { renderStyle.lineHeight = CSSLengthValue(_getNumber(oldValue, newValue, progress), CSSLengthType.PX); } @@ -104,7 +104,7 @@ Matrix4? _parseTransform(String value, RenderStyle renderStyle, String property) return CSSMatrix.computeTransformMatrix(CSSFunction.parseFunction(value), renderStyle); } -void _updateTransform(Matrix4 begin, Matrix4 end, double t, String property, RenderStyle renderStyle) { +void _updateTransform(Matrix4 begin, Matrix4 end, double t, String property, CSSRenderStyle renderStyle) { Matrix4 newMatrix4 = CSSMatrix.lerpMatrix(begin, end, t); renderStyle.transformMatrix = newMatrix4; } @@ -179,7 +179,7 @@ enum CSSTransitionEvent { cancel, } -mixin CSSTransitionMixin on RenderStyleBase { +mixin CSSTransitionMixin on RenderStyle { // https://drafts.csswg.org/css-transitions/#transition-property-property // Name: transition-property diff --git a/kraken/lib/src/css/values/length.dart b/kraken/lib/src/css/values/length.dart index 0f0a16e85d..14bcacc90d 100644 --- a/kraken/lib/src/css/values/length.dart +++ b/kraken/lib/src/css/values/length.dart @@ -65,7 +65,7 @@ class CSSLengthValue { // Length is applied in horizontal or vertical direction. Axis? axisType; - RenderStyleBase? renderStyle; + RenderStyle? renderStyle; String? propertyName; double? _computedValue; @@ -121,7 +121,7 @@ class CSSLengthValue { bool isPositioned = positionType == CSSPositionType.absolute || positionType == CSSPositionType.fixed; - RenderStyleBase? parentRenderStyle = renderStyle!.parent; + RenderStyle? parentRenderStyle = renderStyle!.parent; // Percentage relative width priority: logical width > renderer width double? relativeParentWidth = isPositioned ? @@ -166,7 +166,7 @@ class CSSLengthValue { // There are two exceptions when percentage height is resolved against actual render height of parent: // 1. positioned element // 2. parent is flex item - RenderStyleBase? grandParentRenderStyle = parentRenderStyle?.parent; + RenderStyle? grandParentRenderStyle = parentRenderStyle?.parent; bool isGrandParentFlexLayout = grandParentRenderStyle?.display == CSSDisplay.flex || grandParentRenderStyle?.display == CSSDisplay.inlineFlex; @@ -220,7 +220,7 @@ class CSSLengthValue { case FLEX_BASIS: // Flex-basis computation is called in RenderFlexLayout which // will ensure parent exists. - RenderStyleBase parentRenderStyle = renderStyle!.parent!; + RenderStyle parentRenderStyle = renderStyle!.parent!; double? mainContentSize = parentRenderStyle.flexDirection == FlexDirection.row ? parentRenderStyle.contentBoxLogicalWidth : parentRenderStyle.contentBoxLogicalHeight; @@ -416,7 +416,7 @@ class CSSLength { } } - static CSSLengthValue parseLength(String text, RenderStyleBase? renderStyle, [String? propertyName, Axis? axisType]) { + static CSSLengthValue parseLength(String text, RenderStyle? renderStyle, [String? propertyName, Axis? axisType]) { if (_cachedParsedLength.containsKey(text)) { return _cachedParsedLength[text]!; } diff --git a/kraken/lib/src/css/visibility.dart b/kraken/lib/src/css/visibility.dart index 6fa23f7306..d8758519a2 100644 --- a/kraken/lib/src/css/visibility.dart +++ b/kraken/lib/src/css/visibility.dart @@ -9,7 +9,7 @@ enum Visibility { hidden, } -mixin CSSVisibilityMixin on RenderStyleBase { +mixin CSSVisibilityMixin on RenderStyle { Visibility _visibility = Visibility.visible; void set visibility(Visibility value) { diff --git a/kraken/lib/src/dom/element.dart b/kraken/lib/src/dom/element.dart index d4e2f00154..a17d60bafe 100644 --- a/kraken/lib/src/dom/element.dart +++ b/kraken/lib/src/dom/element.dart @@ -67,7 +67,7 @@ mixin ElementBase on Node { } } - late RenderStyle renderStyle; + late CSSRenderStyle renderStyle; } typedef BeforeRendererAttach = RenderObject Function(); @@ -160,7 +160,7 @@ class Element extends Node style = CSSStyleDeclaration.computedStyle(this, _defaultStyle, _onStyleChanged); // Init render style. - renderStyle = RenderStyle(target: this); + renderStyle = CSSRenderStyle(target: this); } @override @@ -250,7 +250,7 @@ class Element extends Node // Create renderLayoutBox if type changed and copy children if there has previous renderLayoutBox. RenderLayoutBox _createRenderLayout({ RenderLayoutBox? previousRenderLayoutBox, - RenderStyle? renderStyle, + CSSRenderStyle? renderStyle, bool isRepaintBoundary = false }) { renderStyle = renderStyle ?? this.renderStyle; @@ -1347,8 +1347,7 @@ class Element extends Node } void _updateColorRelativePropertyWithColor(Element element) { - RenderStyle renderStyle = element.renderStyle; - renderStyle.updateColorRelativeProperty(); + element.renderStyle.updateColorRelativeProperty(); if (element.children.isNotEmpty) { element.children.forEach((Element child) { if (!child.renderStyle.hasColor) { @@ -1369,8 +1368,7 @@ class Element extends Node } void _updateChildrenFontRelativeLength(Element element) { - RenderStyle renderStyle = element.renderStyle; - renderStyle.updateFontRelativeLength(); + element.renderStyle.updateFontRelativeLength(); if (element.children.isNotEmpty) { element.children.forEach((Element child) { if (!child.renderStyle.hasFontSize) { @@ -1381,8 +1379,7 @@ class Element extends Node } void _updateChildrenRootFontRelativeLength(Element element) { - RenderStyle renderStyle = element.renderStyle; - renderStyle.updateRootFontRelativeLength(); + element.renderStyle.updateRootFontRelativeLength(); if (element.children.isNotEmpty) { element.children.forEach((Element child) { _updateChildrenRootFontRelativeLength(child); @@ -1669,7 +1666,7 @@ class Element extends Node // Create a new RenderLayoutBox for the scrolling content. RenderLayoutBox createScrollingContentLayout() { // FIXME: Create an empty renderStyle for do not share renderStyle with element. - RenderStyle scrollingContentRenderStyle = RenderStyle(target: this); + CSSRenderStyle scrollingContentRenderStyle = CSSRenderStyle(target: this); // Scrolling content layout need to be share the same display with its outer layout box. scrollingContentRenderStyle.display = renderStyle.display; RenderLayoutBox scrollingContentLayoutBox = _createRenderLayout( diff --git a/kraken/lib/src/dom/elements/body.dart b/kraken/lib/src/dom/elements/body.dart index 7c75c40afe..a79b4bef9d 100644 --- a/kraken/lib/src/dom/elements/body.dart +++ b/kraken/lib/src/dom/elements/body.dart @@ -22,8 +22,7 @@ class BodyElement extends Element { @override void willAttachRenderer() { super.willAttachRenderer(); - RenderStyle renderStyle = renderBoxModel!.renderStyle; - renderStyle.width = CSSLengthValue(elementManager.viewportWidth, CSSLengthType.PX); + renderBoxModel!.renderStyle.width = CSSLengthValue(elementManager.viewportWidth, CSSLengthType.PX); } @override diff --git a/kraken/lib/src/dom/elements/input.dart b/kraken/lib/src/dom/elements/input.dart index 3490aa3503..59d51c9e20 100644 --- a/kraken/lib/src/dom/elements/input.dart +++ b/kraken/lib/src/dom/elements/input.dart @@ -364,7 +364,7 @@ class InputElement extends Element implements TextInputClient, TickerProvider { void _onStyleChanged(String property, String? original, String present) { if (_renderInputLeaderLayer != null && isRendererAttached) { - RenderStyle renderStyle = renderBoxModel!.renderStyle; + CSSRenderStyle renderStyle = renderBoxModel!.renderStyle; if (property == HEIGHT) { _renderInputLeaderLayer!.markNeedsLayout(); diff --git a/kraken/lib/src/rendering/box_decoration_painter.dart b/kraken/lib/src/rendering/box_decoration_painter.dart index 9005a7abdc..397627cf38 100644 --- a/kraken/lib/src/rendering/box_decoration_painter.dart +++ b/kraken/lib/src/rendering/box_decoration_painter.dart @@ -25,10 +25,8 @@ class BoxDecorationPainter extends BoxPainter { : super(onChanged); EdgeInsets? padding; - RenderStyle renderStyle; - CSSBoxDecoration get _decoration { - return renderStyle.decoration!; - } + CSSRenderStyle renderStyle; + CSSBoxDecoration get _decoration => renderStyle.decoration!; Paint? _cachedBackgroundPaint; Rect? _rectForCachedBackgroundPaint; @@ -480,7 +478,7 @@ class BoxDecorationImagePainter { this._onChanged ); - final RenderStyle _renderStyle; + final CSSRenderStyle _renderStyle; final DecorationImage _details; CSSBackgroundPosition get _backgroundPositionX { return _renderStyle.backgroundPositionX; diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index 49b92cc874..a648cd747c 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -155,11 +155,7 @@ class RenderLayoutBox extends RenderBoxModel ContainerBoxParentData>, RenderBoxContainerDefaultsMixin> { - RenderLayoutBox({ - required RenderStyle renderStyle, - }) : super( - renderStyle: renderStyle, - ); + RenderLayoutBox({required CSSRenderStyle renderStyle}) : super(renderStyle: renderStyle); // Host content which can be scrolled. RenderLayoutBox? get renderScrollingContent { @@ -559,7 +555,7 @@ class RenderLayoutBox extends RenderBoxModel } mixin RenderBoxModelBase on RenderBox { - late RenderStyle renderStyle; + late CSSRenderStyle renderStyle; Size? boxSize; } @@ -586,7 +582,7 @@ class RenderBoxModel extends RenderBox bool _debugShouldPaintOverlay = false; @override - late RenderStyle renderStyle; + late CSSRenderStyle renderStyle; bool get debugShouldPaintOverlay => _debugShouldPaintOverlay; @@ -1324,7 +1320,7 @@ class RenderBoxModel extends RenderBox return null; } - bool _hasLocalBackgroundImage(RenderStyle renderStyle) { + bool _hasLocalBackgroundImage(CSSRenderStyle renderStyle) { return renderStyle.backgroundImage != null && renderStyle.backgroundAttachment == CSSBackgroundAttachmentType.local; } diff --git a/kraken/lib/src/rendering/flex.dart b/kraken/lib/src/rendering/flex.dart index a37a72d920..e0bf2c0e0d 100644 --- a/kraken/lib/src/rendering/flex.dart +++ b/kraken/lib/src/rendering/flex.dart @@ -158,10 +158,8 @@ class RenderFlexLayout extends RenderLayoutBox { /// start of the main axis and the center of the cross axis. RenderFlexLayout({ List? children, - required RenderStyle renderStyle, - }) : super( - renderStyle: renderStyle, - ) { + required CSSRenderStyle renderStyle, + }) : super(renderStyle: renderStyle) { addAll(children); } @@ -2461,7 +2459,7 @@ class RenderFlexLayout extends RenderLayoutBox { class RenderRepaintBoundaryFlexLayout extends RenderFlexLayout { RenderRepaintBoundaryFlexLayout({ List? children, - required RenderStyle renderStyle, + required CSSRenderStyle renderStyle, }) : super( children: children, renderStyle: renderStyle, diff --git a/kraken/lib/src/rendering/flow.dart b/kraken/lib/src/rendering/flow.dart index d4c86eb323..91488d6faf 100644 --- a/kraken/lib/src/rendering/flow.dart +++ b/kraken/lib/src/rendering/flow.dart @@ -37,10 +37,8 @@ class _RunMetrics { class RenderFlowLayout extends RenderLayoutBox { RenderFlowLayout({ List? children, - required RenderStyle renderStyle, - }) : super( - renderStyle: renderStyle, - ) { + required CSSRenderStyle renderStyle, + }) : super(renderStyle: renderStyle) { addAll(children); } @@ -1589,7 +1587,7 @@ class RenderFlowLayout extends RenderLayoutBox { class RenderRepaintBoundaryFlowLayout extends RenderFlowLayout { RenderRepaintBoundaryFlowLayout({ List? children, - required RenderStyle renderStyle, + required CSSRenderStyle renderStyle, }) : super( children: children, renderStyle: renderStyle, diff --git a/kraken/lib/src/rendering/intrinsic.dart b/kraken/lib/src/rendering/intrinsic.dart index 55296f16f6..07d73f3125 100644 --- a/kraken/lib/src/rendering/intrinsic.dart +++ b/kraken/lib/src/rendering/intrinsic.dart @@ -12,11 +12,7 @@ import 'package:kraken/rendering.dart'; class RenderIntrinsic extends RenderBoxModel with RenderObjectWithChildMixin, RenderProxyBoxMixin { - RenderIntrinsic( - RenderStyle renderStyle, - ) : super( - renderStyle: renderStyle, - ); + RenderIntrinsic(CSSRenderStyle renderStyle) : super(renderStyle: renderStyle); @override BoxSizeType get widthSizeType { @@ -222,11 +218,7 @@ class RenderIntrinsic extends RenderBoxModel } class RenderRepaintBoundaryIntrinsic extends RenderIntrinsic { - RenderRepaintBoundaryIntrinsic( - RenderStyle renderStyle, - ) : super( - renderStyle, - ); + RenderRepaintBoundaryIntrinsic(CSSRenderStyle renderStyle) : super(renderStyle); @override bool get isRepaintBoundary => true; diff --git a/kraken/lib/src/rendering/sliver_list.dart b/kraken/lib/src/rendering/sliver_list.dart index a5d0119392..3317a67b6e 100644 --- a/kraken/lib/src/rendering/sliver_list.dart +++ b/kraken/lib/src/rendering/sliver_list.dart @@ -36,7 +36,7 @@ class RenderSliverListLayout extends RenderLayoutBox { final RenderSliverBoxChildManager _renderSliverBoxChildManager; RenderSliverListLayout({ - required RenderStyle renderStyle, + required CSSRenderStyle renderStyle, required RenderSliverElementChildManager manager, ScrollListener? onScroll, }) : _renderSliverBoxChildManager = manager, diff --git a/kraken/lib/src/rendering/text.dart b/kraken/lib/src/rendering/text.dart index 73c1223342..76c4900eda 100644 --- a/kraken/lib/src/rendering/text.dart +++ b/kraken/lib/src/rendering/text.dart @@ -27,7 +27,7 @@ class RenderTextBox extends RenderBox late String data; late KrakenRenderParagraph _renderParagraph; - RenderStyle renderStyle; + CSSRenderStyle renderStyle; BoxSizeType? widthSizeType; BoxSizeType? heightSizeType; From b8996d2199783771580867791e58849cdfde9512 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Thu, 2 Dec 2021 12:18:05 +0800 Subject: [PATCH 119/167] chore: add annotation. --- bridge/bindings/qjs/module_manager.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/bridge/bindings/qjs/module_manager.cc b/bridge/bindings/qjs/module_manager.cc index 0241fcc62a..3e2edc45f4 100644 --- a/bridge/bindings/qjs/module_manager.cc +++ b/bridge/bindings/qjs/module_manager.cc @@ -105,6 +105,7 @@ JSValue krakenInvokeModule(QjsContext* ctx, JSValueConst this_val, int argc, JSV NativeString* params = nullptr; if (!JS_IsNull(paramsValue)) { JSValue stringifyedValue = JS_JSONStringify(ctx, paramsValue, JS_NULL, JS_NULL); + // JS_JSONStringify may return JS_EXCEPTION if object is not valid. Return JS_EXCEPTION and let quickjs to handle it. if (JS_IsException(stringifyedValue)) return stringifyedValue; params = jsValueToNativeString(ctx, stringifyedValue); From ca7505041a09a277be25c78da8b41051401e8d71 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Thu, 2 Dec 2021 14:03:57 +0800 Subject: [PATCH 120/167] fix: fix nativeValue memory leaks. --- kraken/lib/src/bridge/native_value.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kraken/lib/src/bridge/native_value.dart b/kraken/lib/src/bridge/native_value.dart index f585802531..2357b728f3 100644 --- a/kraken/lib/src/bridge/native_value.dart +++ b/kraken/lib/src/bridge/native_value.dart @@ -70,7 +70,10 @@ dynamic fromNativeValue(Pointer nativeValue) { JSValueType type = JSValueType.values[nativeValue.ref.tag]; switch(type) { case JSValueType.TAG_STRING: - return nativeStringToString(Pointer.fromAddress(nativeValue.ref.u)); + Pointer nativeString = Pointer.fromAddress(nativeValue.ref.u); + String result = nativeStringToString(nativeString); + freeNativeString(nativeString); + return result; case JSValueType.TAG_INT: return nativeValue.ref.u; case JSValueType.TAG_BOOL: @@ -95,7 +98,10 @@ dynamic fromNativeValue(Pointer nativeValue) { case JSValueType.TAG_ASYNC_FUNCTION: break; case JSValueType.TAG_JSON: - return jsonDecode(nativeStringToString(Pointer.fromAddress(nativeValue.ref.u))); + Pointer nativeString = Pointer.fromAddress(nativeValue.ref.u); + dynamic value = jsonDecode(nativeStringToString(nativeString)); + freeNativeString(nativeString); + return value; } } From 6ed502e970bdc3957340f26aa4c48ab411cc675a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=99=E7=A8=B3?= Date: Thu, 2 Dec 2021 15:44:46 +0800 Subject: [PATCH 121/167] fix: add negative length validation for non negative css value (#938) * fix: add negative length validation for non negative css value * fix: margin parse * fix: negative zero * test: add test for invalid negative css value * fix: delete margin non negative assert * chore: extract common str in length regular expressions --- .../css/css-values/px.ts.e6b7b8b21.png | Bin 0 -> 2791 bytes integration_tests/specs/css/css-values/px.ts | 22 ++++++++++ kraken/lib/src/css/background.dart | 4 +- kraken/lib/src/css/border.dart | 2 +- kraken/lib/src/css/margin.dart | 1 - kraken/lib/src/css/style_declaration.dart | 18 +++++--- kraken/lib/src/css/style_property.dart | 40 +++++++++++++----- kraken/lib/src/css/text.dart | 4 +- kraken/lib/src/css/values/length.dart | 18 +++++++- kraken/lib/src/css/values/percentage.dart | 5 +++ 10 files changed, 90 insertions(+), 24 deletions(-) create mode 100644 integration_tests/snapshots/css/css-values/px.ts.e6b7b8b21.png diff --git a/integration_tests/snapshots/css/css-values/px.ts.e6b7b8b21.png b/integration_tests/snapshots/css/css-values/px.ts.e6b7b8b21.png new file mode 100644 index 0000000000000000000000000000000000000000..c4874bc9b13e2b1dd8a73b847249fa7750c3e4a0 GIT binary patch literal 2791 zcmeAS@N?(olHy`uVBq!ia0y~yV9a1(U~1rC1Bz^vdbAu!F%}28J29*~C-V}>VJUX< z4B-HR8jh3>1_rK)o-U3d6?5L+F5CrC6+)@|%w=bLv?rH@aE?`2Y5 zvx7`j)3je)kD}_BjxRX)HR0g}g{}t*yCgg`{0~pQx{z<~_648i>+E;zIA6TqygmI_ zamBgc`=U=9oo1M@x%P%R!-4z484VhZY#p~MKOcU5-S~dJ{pSx4&#=p%nPXWzV>|1C z_l^513ZLE1Prp}N{_Ia>Bb!AINV~uN**P|r#eaXD5s#PorpsvX`L^*3cK&PK#n1UZ zhcW!v{MLJV+PauXeZ80&mbF#S?(O~hYxny!yMGnWPE4G+cJ0@#>-W$5`E>f;{ZAG= zXRz3@-R^geUH#XrwDR(2pPtU_w~Lx@Z(n8d`C+?p{(jry$H%1J&##~N^VRB^Z{L2s zbLPz7-|W`x2im5kPRrjnGc|46-Mf*~Qs-u0zqZ%C|KE-1X_4pV#eR?9cjwIHnkMrM6``n~NE8((XIVx3T>ExZl`+{=2*TtIK2W*VjWO>VLi56}Q8n z`1!eCh9C6z&)Hd;{p{7%ndSFx0xgk_niiRNKkj~E;rn~{{wv(uV|n`HmC3$RvA29 L{an^LB{Ts5xFz(3 literal 0 HcmV?d00001 diff --git a/integration_tests/specs/css/css-values/px.ts b/integration_tests/specs/css/css-values/px.ts index 89ea66911f..d184a400ed 100644 --- a/integration_tests/specs/css/css-values/px.ts +++ b/integration_tests/specs/css/css-values/px.ts @@ -46,4 +46,26 @@ describe('px', () => { }, 600); }); }); + + it('negative css length value should not work', async () => { + const container = createElement('div', { + style: { + background: 'yellow', + width: '-100px', + height: '-100px', + minWidth: '-100px', + maxWidth: '-200px', + minHeight: '-100px', + maxHeight: '-200px', + padding: '-50px', + border: '-10px solid green', + } + }, [ + createText('foo') + ]); + + document.body.appendChild(container); + + await snapshot(); + }); }); diff --git a/kraken/lib/src/css/background.dart b/kraken/lib/src/css/background.dart index e1fd43e19d..068eab621c 100644 --- a/kraken/lib/src/css/background.dart +++ b/kraken/lib/src/css/background.dart @@ -405,8 +405,8 @@ class CSSBackground { value == FIT_HEIGTH || value == SCALE_DOWN || value == FILL || - CSSLength.isLength(value) || - CSSPercentage.isPercentage(value); + CSSLength.isNonNegativeLength(value) || + CSSPercentage.isNonNegativePercentage(value); } static bool isValidBackgroundAttachmentValue(String value) { diff --git a/kraken/lib/src/css/border.dart b/kraken/lib/src/css/border.dart index 04078b940b..57bb1f55d8 100644 --- a/kraken/lib/src/css/border.dart +++ b/kraken/lib/src/css/border.dart @@ -237,7 +237,7 @@ class CSSBorderSide { } static bool isValidBorderWidthValue(String value) { - return CSSLength.isLength(value) || value == THIN || value == MEDIUM || value == THICK; + return CSSLength.isNonNegativeLength(value) || value == THIN || value == MEDIUM || value == THICK; } static BorderSide none = BorderSide(color: defaultBorderColor, width: 0.0, style: BorderStyle.none); diff --git a/kraken/lib/src/css/margin.dart b/kraken/lib/src/css/margin.dart index 339cc5df8f..33c98c98bb 100644 --- a/kraken/lib/src/css/margin.dart +++ b/kraken/lib/src/css/margin.dart @@ -22,7 +22,6 @@ mixin CSSMarginMixin on RenderStyleBase { bottom: marginBottom.computedValue, top: marginTop.computedValue ).resolve(TextDirection.ltr); - assert(insets.isNonNegative); return insets; } diff --git a/kraken/lib/src/css/style_declaration.dart b/kraken/lib/src/css/style_declaration.dart index e01389de05..ea69b0b046 100644 --- a/kraken/lib/src/css/style_declaration.dart +++ b/kraken/lib/src/css/style_declaration.dart @@ -263,6 +263,14 @@ class CSSStyleDeclaration { switch (propertyName) { case WIDTH: case HEIGHT: + // Validation length type + if (!CSSLength.isNonNegativeLength(normalizedValue) && + !CSSLength.isAuto(normalizedValue) && + !CSSPercentage.isNonNegativePercentage(normalizedValue) + ) { + return false; + } + break; case TOP: case LEFT: case RIGHT: @@ -282,8 +290,8 @@ class CSSStyleDeclaration { case MAX_WIDTH: case MAX_HEIGHT: if (normalizedValue != NONE && - !CSSLength.isLength(normalizedValue) && - !CSSPercentage.isPercentage(normalizedValue) + !CSSLength.isNonNegativeLength(normalizedValue) && + !CSSPercentage.isNonNegativePercentage(normalizedValue) ) { return false; } @@ -294,8 +302,8 @@ class CSSStyleDeclaration { case PADDING_LEFT: case PADDING_BOTTOM: case PADDING_RIGHT: - if (!CSSLength.isLength(normalizedValue) && - !CSSPercentage.isPercentage(normalizedValue) + if (!CSSLength.isNonNegativeLength(normalizedValue) && + !CSSPercentage.isNonNegativePercentage(normalizedValue) ) { return false; } @@ -304,7 +312,7 @@ class CSSStyleDeclaration { case BORDER_TOP_WIDTH: case BORDER_LEFT_WIDTH: case BORDER_RIGHT_WIDTH: - if (!CSSLength.isLength(normalizedValue)) { + if (!CSSLength.isNonNegativeLength(normalizedValue)) { return false; } break; diff --git a/kraken/lib/src/css/style_property.dart b/kraken/lib/src/css/style_property.dart index 25e22b4740..1c43664a98 100644 --- a/kraken/lib/src/css/style_property.dart +++ b/kraken/lib/src/css/style_property.dart @@ -67,7 +67,7 @@ List _splitBySpace(String value) { class CSSStyleProperty { static void setShorthandPadding(Map properties, String shorthandValue) { - List? values = _getEdgeValues(shorthandValue); + List? values = _getEdgeValues(shorthandValue, isNonNegativeLengthOrPercentage: true); if (values == null) return; properties[PADDING_TOP] = values[0]; @@ -84,7 +84,7 @@ class CSSStyleProperty { } static void setShorthandMargin(Map properties, String shorthandValue) { - List? values = _getEdgeValues(shorthandValue, isLengthOrPercentage: false); + List? values = _getEdgeValues(shorthandValue); if (values == null) return; properties[MARGIN_TOP] = values[0]; @@ -289,7 +289,7 @@ class CSSStyleProperty { borderLeftColor = values[2]; } } else if (property == BORDER_WIDTH) { - List? values = _getEdgeValues(shorthandValue); + List? values = _getEdgeValues(shorthandValue, isNonNegativeLength: true); if (values == null) return; borderTopWidth = values[0]; @@ -298,7 +298,7 @@ class CSSStyleProperty { borderLeftWidth = values[3]; } else if (property == BORDER_STYLE) { // @TODO: validate value - List? values = _getEdgeValues(shorthandValue, isLengthOrPercentage: false); + List? values = _getEdgeValues(shorthandValue); if (values == null) return; borderTopStyle = values[0]; @@ -307,7 +307,7 @@ class CSSStyleProperty { borderLeftStyle = values[3]; } else if (property == BORDER_COLOR) { // @TODO: validate value - List? values = _getEdgeValues(shorthandValue, isLengthOrPercentage: false); + List? values = _getEdgeValues(shorthandValue); if (values == null) return; borderTopColor = values[0]; @@ -429,7 +429,7 @@ class CSSStyleProperty { static List? _getBorderRaidusValues(String shorthandProperty) { if (!shorthandProperty.contains('/')) { - return _getEdgeValues(shorthandProperty); + return _getEdgeValues(shorthandProperty, isNonNegativeLengthOrPercentage: true); } List radius = shorthandProperty.split(_slashRegExp); @@ -446,8 +446,8 @@ class CSSStyleProperty { String firstRadius = radius[0]; String secondRadius = radius[1]; - List firstValues = _getEdgeValues(firstRadius)!; - List secondValues = _getEdgeValues(secondRadius)!; + List firstValues = _getEdgeValues(firstRadius, isNonNegativeLengthOrPercentage: true)!; + List secondValues = _getEdgeValues(secondRadius, isNonNegativeLengthOrPercentage: true)!; return [ '${firstValues[0]} ${secondValues[0]}', @@ -550,7 +550,7 @@ class CSSStyleProperty { style = value; } else if (weight == null && CSSText.isValidFontWeightValue(value)) { weight = value; - } else if (size == null && CSSLength.isLength(value)) { + } else if (size == null && CSSLength.isNonNegativeLength(value)) { size = value; } else if (value == '/') { isSizeEndAndLineHeightStart = true; @@ -685,7 +685,7 @@ class CSSStyleProperty { grow = value; } else if (shrink == null && CSSNumber.isNumber(value)) { shrink = value; - } else if (basis == null && ((CSSLength.isLength(value) || value == AUTO))) { + } else if (basis == null && ((CSSLength.isNonNegativeLength(value) || value == AUTO))) { basis = value; } else { return null; @@ -718,7 +718,11 @@ class CSSStyleProperty { return [width, style, color]; } - static List? _getEdgeValues(String shorthandProperty, {bool isLengthOrPercentage = true}) { + static List? _getEdgeValues(String shorthandProperty, { + bool isLengthOrPercentage = false, + bool isNonNegativeLengthOrPercentage = false, + bool isNonNegativeLength = false, + }) { var properties = shorthandProperty.split(_spaceRegExp); String? topValue; @@ -749,6 +753,20 @@ class CSSStyleProperty { (!CSSLength.isLength(leftValue) && !CSSPercentage.isPercentage(leftValue))) { return null; } + } else if (isNonNegativeLengthOrPercentage) { + if ((!CSSLength.isNonNegativeLength(topValue) && !CSSPercentage.isNonNegativePercentage(topValue)) || + (!CSSLength.isNonNegativeLength(rightValue) && !CSSPercentage.isNonNegativePercentage(rightValue)) || + (!CSSLength.isNonNegativeLength(bottomValue) && !CSSPercentage.isNonNegativePercentage(bottomValue))|| + (!CSSLength.isNonNegativeLength(leftValue) && !CSSPercentage.isNonNegativePercentage(leftValue))) { + return null; + } + } else if (isNonNegativeLength) { + if ((!CSSLength.isNonNegativeLength(topValue)) || + (!CSSLength.isNonNegativeLength(rightValue)) || + (!CSSLength.isNonNegativeLength(bottomValue))|| + (!CSSLength.isNonNegativeLength(leftValue))) { + return null; + } } // Assume the properties are in the usual order top, right, bottom, left. diff --git a/kraken/lib/src/css/text.dart b/kraken/lib/src/css/text.dart index 83804fe1e9..bacf3a7e66 100644 --- a/kraken/lib/src/css/text.dart +++ b/kraken/lib/src/css/text.dart @@ -474,7 +474,7 @@ class CSSText { } static bool isValidLineHeightValue(String value) { - return CSSLength.isLength(value) || CSSPercentage.isPercentage(value) || + return CSSLength.isNonNegativeLength(value) || CSSPercentage.isNonNegativePercentage(value) || value == 'normal' || double.tryParse(value) != null; } @@ -489,7 +489,7 @@ class CSSText { static CSSLengthValue DEFAULT_LINE_HEIGHT = CSSLengthValue.normal; static CSSLengthValue? resolveLineHeight(String value, RenderStyle renderStyle, String propertyName) { if (value.isNotEmpty) { - if (CSSLength.isLength(value) || CSSPercentage.isPercentage(value)) { + if (CSSLength.isNonNegativeLength(value) || CSSPercentage.isNonNegativePercentage(value)) { CSSLengthValue lineHeight = CSSLength.parseLength(value, renderStyle, propertyName); // Line-height 0 and negative value is considered invalid. if (lineHeight.computedValue != double.infinity && lineHeight.computedValue > 0) { diff --git a/kraken/lib/src/css/values/length.dart b/kraken/lib/src/css/values/length.dart index 14bcacc90d..b97ddb4ca3 100644 --- a/kraken/lib/src/css/values/length.dart +++ b/kraken/lib/src/css/values/length.dart @@ -18,7 +18,10 @@ const _1Q = _1cm / 40; // 1Q = 1/40th of 1cm const _1pc = _1in / 6; // 1pc = 1/6th of 1in const _1pt = _1in / 72; // 1pt = 1/72th of 1in -final _lengthRegExp = RegExp(r'^[+-]?(\d+)?(\.\d+)?px|rpx|vw|vh|vmin|vmax|rem|em|in|cm|mm|pc|pt$', caseSensitive: false); +final String _unitRegStr = '(px|rpx|vw|vh|vmin|vmax|rem|em|in|cm|mm|pc|pt)'; +final _lengthRegExp = RegExp(r'^[+-]?(\d+)?(\.\d+)?' + _unitRegStr + r'$', caseSensitive: false); +final _negativeZeroRegExp = RegExp(r'^-(0+)?(\.0+)?' + _unitRegStr + r'$', caseSensitive: false); +final _nonNegativeLengthRegExp = RegExp(r'^[+]?(\d+)?(\.\d+)?' + _unitRegStr + r'$', caseSensitive: false); enum CSSLengthType { // absolute units @@ -404,7 +407,18 @@ class CSSLength { } static bool isLength(String? value) { - return value != null && (value == ZERO || _lengthRegExp.hasMatch(value)); + return value != null && ( + value == ZERO + || _lengthRegExp.hasMatch(value) + ); + } + + static bool isNonNegativeLength(String? value) { + return value != null && ( + value == ZERO + || _negativeZeroRegExp.hasMatch(value) // Negative zero is considered to be equal to zero. + || _nonNegativeLengthRegExp.hasMatch(value) + ); } static CSSLengthValue? resolveLength(String text, RenderStyle? renderStyle, String propertyName) { diff --git a/kraken/lib/src/css/values/percentage.dart b/kraken/lib/src/css/values/percentage.dart index 95eea282a3..d935db17fa 100644 --- a/kraken/lib/src/css/values/percentage.dart +++ b/kraken/lib/src/css/values/percentage.dart @@ -8,6 +8,7 @@ import 'package:quiver/collection.dart'; final _percentageRegExp = RegExp(r'^[+-]?\d+[\.]?\d*\%$', caseSensitive: false); +final _nonNegativePercentageRegExp = RegExp(r'^[+]?\d+[\.]?\d*\%$', caseSensitive: false); final LinkedLruHashMap _cachedParsedPercentage = LinkedLruHashMap(maximumSize: 100); class CSSPercentage { @@ -27,4 +28,8 @@ class CSSPercentage { static bool isPercentage(String? percentageValue) { return percentageValue != null && _percentageRegExp.hasMatch(percentageValue); } + + static bool isNonNegativePercentage(String? percentageValue) { + return percentageValue != null && _nonNegativePercentageRegExp.hasMatch(percentageValue); + } } From fa01dcca8a195f753989cf9be3bee8d8a4b414dc Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 2 Dec 2021 15:56:03 +0800 Subject: [PATCH 122/167] :sparkles: feat: scripts add to it. --- integration_tests/scripts/html_loader.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index ceceb65ae5..0753a67953 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -50,14 +50,10 @@ const loader = function(source) { // Use html_parse to parser html in html file. const html_parse = () => __kraken_parse_html__('${htmlString}'); - ${ - // Eval script of html. - scripts.length === 0 ? - 'it("should work", async () => {\ - html_parse();\ - await html_snapshot();\ - })' : scripts.join('\n') - } + it("should work", async () => {\ + html_parse();\ + ${scripts.length === 0 ? 'await html_snapshot();' : scripts.join('\n')} + }) }); `; }; From 0881b83846d1bd816d02c3671b0e62742a854cd8 Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 2 Dec 2021 16:12:02 +0800 Subject: [PATCH 123/167] :sparkles: feat Set attr of HTML can let the case use fit. For example: xxx --- integration_tests/scripts/html_loader.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index 0753a67953..e207b7fa12 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -34,6 +34,15 @@ const loader = function(source) { let root = HTMLParser.parse(source); traverseParseHTML(root); + + // Set attr of HTML can let the case use fit. For example: xxx + let isFit = false; + root.childNodes && root.childNodes.forEach(ele => { + if (ele.rawAttrs && ele.rawAttrs.indexOf('fit') >= 0) { + isFit = true; + } + }) + const htmlString = root.toString().replace(/\n/g, ''); return ` @@ -50,7 +59,7 @@ const loader = function(source) { // Use html_parse to parser html in html file. const html_parse = () => __kraken_parse_html__('${htmlString}'); - it("should work", async () => {\ + ${isFit ? 'fit' : 'it'}("should work", async () => {\ html_parse();\ ${scripts.length === 0 ? 'await html_snapshot();' : scripts.join('\n')} }) From b4e468bc69a080cf3fc6ea28f638f68cee51b83a Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 2 Dec 2021 16:12:31 +0800 Subject: [PATCH 124/167] :art: chore: add end. --- integration_tests/scripts/html_loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/scripts/html_loader.js b/integration_tests/scripts/html_loader.js index e207b7fa12..29f5414630 100644 --- a/integration_tests/scripts/html_loader.js +++ b/integration_tests/scripts/html_loader.js @@ -35,7 +35,7 @@ const loader = function(source) { let root = HTMLParser.parse(source); traverseParseHTML(root); - // Set attr of HTML can let the case use fit. For example: xxx + // Set attr of HTML can let the case use fit. For example: xxx . let isFit = false; root.childNodes && root.childNodes.forEach(ele => { if (ele.rawAttrs && ele.rawAttrs.indexOf('fit') >= 0) { From 000f30573c4c5f76656421ca81398b3a8527b18e Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 2 Dec 2021 16:42:32 +0800 Subject: [PATCH 125/167] :sparkles: feat: add assets protocol. --- kraken/lib/src/launcher/bundle.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index ab1db3b523..4fd9a20bb8 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -46,7 +46,7 @@ String getAcceptHeader() { } bool isAssetAbsolutePath(String path) { - return path.indexOf('assets/') == 0; + return path.startsWith('assets://'); } abstract class KrakenBundle { From 6204efc78d20abe4fd588469c59c80d2c650b37f Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Thu, 2 Dec 2021 16:52:49 +0800 Subject: [PATCH 126/167] fix: lint --- kraken/lib/src/css/render_style.dart | 18 ++++++++++++++++++ kraken/lib/src/css/sizing.dart | 3 +++ 2 files changed, 21 insertions(+) diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index c2a1c6f259..1a90baffb0 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -93,6 +93,8 @@ abstract class RenderStyle { // BoxModel double? get borderBoxLogicalWidth; double? get borderBoxLogicalHeight; + double? get borderBoxConstraintsWidth; + double? get borderBoxConstraintsHeight; double? get borderBoxWidth; double? get borderBoxHeight; double? get paddingBoxLogicalWidth; @@ -540,6 +542,7 @@ class CSSRenderStyle } // Whether height is stretched to fill its parent's content height. + @override bool get isHeightStretch { RenderStyle renderStyle = this; if (renderStyle.parent == null) { @@ -576,6 +579,7 @@ class CSSRenderStyle // Max width to constrain its children, used in deciding the line wrapping timing of layout. + @override double get contentMaxConstraintsWidth { // If renderBoxModel definite content constraints, use it as max constrains width of content. BoxConstraints? contentConstraints = renderBoxModel!.contentConstraints; @@ -614,6 +618,7 @@ class CSSRenderStyle // https://www.w3.org/TR/css-box-3/#valdef-box-content-box // Use double.infinity refers to the value is not computed yet. double? _contentBoxLogicalWidth = double.infinity; + @override double? get contentBoxLogicalWidth { // If renderBox has tight width, its logical size equals max size. // Compute logical width directly in case as renderBoxModel is not layouted yet, @@ -628,6 +633,7 @@ class CSSRenderStyle // https://www.w3.org/TR/css-box-3/#valdef-box-content-box // Use double.infinity refers to the value is not computed yet. double? _contentBoxLogicalHeight = double.infinity; + @override double? get contentBoxLogicalHeight { // Compute logical height directly in case as renderBoxModel is not layouted yet, // eg. compute percentage length before layout. @@ -687,6 +693,7 @@ class CSSRenderStyle // Border box width of renderBoxModel after it was rendered. // https://www.w3.org/TR/css-box-3/#valdef-box-border-box + @override double? get borderBoxWidth { if (renderBoxModel!.hasSize && renderBoxModel!.boxSize != null) { return renderBoxModel!.boxSize!.width; @@ -696,6 +703,7 @@ class CSSRenderStyle // Border box height of renderBoxModel after it was rendered. // https://www.w3.org/TR/css-box-3/#valdef-box-border-box + @override double? get borderBoxHeight { if (renderBoxModel!.hasSize && renderBoxModel!.boxSize != null) { return renderBoxModel!.boxSize!.height; @@ -729,6 +737,7 @@ class CSSRenderStyle // Content box width of renderBoxModel after it was rendered. // https://www.w3.org/TR/css-box-3/#valdef-box-content-box + @override double? get contentBoxWidth { if (paddingBoxWidth == null) { return null; @@ -740,6 +749,7 @@ class CSSRenderStyle // Content box height of renderBoxModel after it was rendered. // https://www.w3.org/TR/css-box-3/#valdef-box-content-box + @override double? get contentBoxHeight { if (paddingBoxHeight == null) { return null; @@ -750,6 +760,7 @@ class CSSRenderStyle } // Border box width of renderBoxModel calculated from tight width constraints. + @override double? get borderBoxConstraintsWidth { if (renderBoxModel!.hasSize && renderBoxModel!.constraints.hasTightWidth @@ -760,6 +771,7 @@ class CSSRenderStyle } // Border box height of renderBoxModel calculated from tight height constraints. + @override double? get borderBoxConstraintsHeight { if (renderBoxModel!.hasSize && renderBoxModel!.constraints.hasTightHeight @@ -770,6 +782,7 @@ class CSSRenderStyle } // Padding box width of renderBoxModel calculated from tight width constraints. + @override double? get paddingBoxConstraintsWidth { if (borderBoxConstraintsWidth == null) { return null; @@ -780,6 +793,7 @@ class CSSRenderStyle } // Padding box height of renderBoxModel calculated from tight height constraints. + @override double? get paddingBoxConstraintsHeight { if (borderBoxConstraintsHeight == null) { return null; @@ -790,6 +804,7 @@ class CSSRenderStyle } // Content box width of renderBoxModel calculated from tight width constraints. + @override double? get contentBoxConstraintsWidth { if (paddingBoxConstraintsWidth == null) { return null; @@ -800,6 +815,7 @@ class CSSRenderStyle } // Content box height of renderBoxModel calculated from tight height constraints. + @override double? get contentBoxConstraintsHeight { if (paddingBoxConstraintsHeight == null) { return null; @@ -810,6 +826,7 @@ class CSSRenderStyle } // Get height of replaced element by intrinsic ratio if height is not defined + @override double getHeightByIntrinsicRatio() { double? realWidth = width.isAuto ? intrinsicWidth : width.computedValue; if (minWidth.isNotAuto && realWidth! < minWidth.computedValue) { @@ -823,6 +840,7 @@ class CSSRenderStyle } // Get width of replaced element by intrinsic ratio if width is not defined + @override double getWidthByIntrinsicRatio() { double? realHeight = height.isAuto ? intrinsicHeight : height.computedValue; if (!minHeight.isAuto && realHeight! < minHeight.computedValue) { diff --git a/kraken/lib/src/css/sizing.dart b/kraken/lib/src/css/sizing.dart index 036fa7d81d..3b31e048c8 100644 --- a/kraken/lib/src/css/sizing.dart +++ b/kraken/lib/src/css/sizing.dart @@ -135,6 +135,7 @@ mixin CSSSizingMixin on RenderStyle { // Intrinsic width of replaced element. double? _intrinsicWidth; + @override double? get intrinsicWidth { return _intrinsicWidth; } @@ -146,6 +147,7 @@ mixin CSSSizingMixin on RenderStyle { // Intrinsic height of replaced element. double? _intrinsicHeight; + @override double? get intrinsicHeight { return _intrinsicHeight; } @@ -157,6 +159,7 @@ mixin CSSSizingMixin on RenderStyle { // Aspect ratio of replaced element. double? _intrinsicRatio; + @override double? get intrinsicRatio { return _intrinsicRatio; } From d8081f5886a24ec26bdec915e2714e13a032f94a Mon Sep 17 00:00:00 2001 From: answershuto Date: Thu, 2 Dec 2021 16:59:43 +0800 Subject: [PATCH 127/167] :sparkles: feat: support assets protocol for AssetsBundle. --- kraken/lib/src/launcher/bundle.dart | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 4fd9a20bb8..53ee03c79f 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -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]; } @@ -46,7 +48,7 @@ String getAcceptHeader() { } bool isAssetAbsolutePath(String path) { - return path.startsWith('assets://'); + return path.startsWith(ASSETS_PROROCOL); } abstract class KrakenBundle { @@ -88,11 +90,11 @@ abstract class KrakenBundle { isResolved = true; } - static KrakenBundle fromHref(String href) { - if (isAssetAbsolutePath(href)) { - return AssetsBundle(href); + static KrakenBundle fromHref(String url) { + if (isAssetAbsolutePath(url)) { + return AssetsBundle(url); } else { - return NetworkBundle(href); + return NetworkBundle(url); } } @@ -241,8 +243,11 @@ class AssetsBundle extends KrakenBundle { super.resolve(contextId); // JSBundle get default bundle manifest. manifest = AppManifest(); - String localPath = src; - rawBundle = await rootBundle.load(localPath); + if (isAssetAbsolutePath(src)) { + String localPath = src.substring(ASSETS_PROROCOL.length); + rawBundle = await rootBundle.load(localPath); + } + isResolved = true; return this; } From 692ae1059777c3cc84bbbc60464bc88aee8b57c8 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Thu, 2 Dec 2021 20:41:43 +0800 Subject: [PATCH 128/167] feat: use purely constraints to render image --- kraken/lib/rendering.dart | 1 + kraken/lib/src/dom/elements/body.dart | 6 ------ kraken/lib/src/dom/elements/img.dart | 8 +++----- kraken/lib/src/rendering/image.dart | 26 ++++++++++++++++++++++++++ kraken/lib/widget.dart | 12 ------------ 5 files changed, 30 insertions(+), 23 deletions(-) create mode 100644 kraken/lib/src/rendering/image.dart diff --git a/kraken/lib/rendering.dart b/kraken/lib/rendering.dart index 3e19a7ca1a..617e24b71c 100644 --- a/kraken/lib/rendering.dart +++ b/kraken/lib/rendering.dart @@ -23,3 +23,4 @@ export 'src/rendering/opacity.dart'; export 'src/rendering/transform.dart'; export 'src/rendering/viewport.dart'; export 'src/rendering/paragraph.dart'; +export 'src/rendering/image.dart'; diff --git a/kraken/lib/src/dom/elements/body.dart b/kraken/lib/src/dom/elements/body.dart index a79b4bef9d..ac5927f8e4 100644 --- a/kraken/lib/src/dom/elements/body.dart +++ b/kraken/lib/src/dom/elements/body.dart @@ -19,12 +19,6 @@ class BodyElement extends Element { BodyElement(int targetId, Pointer nativePtr, ElementManager elementManager) : super( targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); - @override - void willAttachRenderer() { - super.willAttachRenderer(); - renderBoxModel!.renderStyle.width = CSSLengthValue(elementManager.viewportWidth, CSSLengthType.PX); - } - @override void addEvent(String eventType) { // Scroll event not working on body. diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 55b8ba4010..e1ea8ced25 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -24,7 +24,7 @@ const Map _defaultStyle = { // The HTMLImageElement. class ImageElement extends Element { // The render box to draw image. - RenderImage? _renderImage; + KrakenRenderImage? _renderImage; // The image source url. String? get _source => getProperty('src'); @@ -257,8 +257,6 @@ class ImageElement extends Element { width = 0.0; } - _renderImage?.width = width; - _renderImage?.height = height; renderBoxModel!.intrinsicWidth = naturalWidth; renderBoxModel!.intrinsicHeight = naturalHeight; @@ -269,12 +267,12 @@ class ImageElement extends Element { } } - RenderImage createRenderImageBox() { + KrakenRenderImage createRenderImageBox() { RenderStyle renderStyle = renderBoxModel!.renderStyle; BoxFit objectFit = renderStyle.objectFit; Alignment objectPosition = renderStyle.objectPosition; - return RenderImage( + return KrakenRenderImage( image: _imageInfo?.image, fit: objectFit, alignment: objectPosition, diff --git a/kraken/lib/src/rendering/image.dart b/kraken/lib/src/rendering/image.dart new file mode 100644 index 0000000000..2fca93edc3 --- /dev/null +++ b/kraken/lib/src/rendering/image.dart @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019-present Alibaba Inc. All rights reserved. + * Author: Kraken Team. + */ +import 'dart:ui' as ui show Image; +import 'package:flutter/rendering.dart'; + +class KrakenRenderImage extends RenderImage { + KrakenRenderImage({ + ui.Image? image, + BoxFit? fit, + AlignmentGeometry alignment = Alignment.center, + }) : super( + image: image, + fit: fit, + alignment: alignment, + ); + + @override + void performLayout() { + Size trySize = constraints.biggest; + size = trySize.isInfinite ? constraints.smallest : trySize; + } +} + + diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index d75007fbad..93b92c1884 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -937,18 +937,6 @@ This situation often happened when you trying creating kraken when FlutterView n controller.view.viewportHeight = viewportHeight; controller.view.document!.documentElement.renderStyle.height = CSSLengthValue(viewportHeight, CSSLengthType.PX); } - - if (viewportWidthHasChanged || viewportHeightHasChanged) { - traverseElement(controller.view.document!.documentElement, (element) { - if (element.isRendererAttached) { - element.inlineStyle.forEach((key, value) { - element.setProperty(key, value); - }); - element.style.flushPendingProperties(); - element.renderBoxModel?.markNeedsLayout(); - } - }); - } } @override From 284f8f143ab0b637578f3347888658febcfd8738 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 11:20:03 +0800 Subject: [PATCH 129/167] :art: chore: modify fromHref to fromUrl. --- kraken/example/lib/main.dart | 4 ++-- kraken/lib/src/dom/elements/head.dart | 2 +- kraken/lib/src/launcher/bundle.dart | 2 +- kraken/lib/src/launcher/controller.dart | 2 +- kraken/lib/src/launcher/launcher.dart | 2 +- kraken/lib/widget.dart | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/kraken/example/lib/main.dart b/kraken/example/lib/main.dart index c6c5a5c203..11806788e6 100644 --- a/kraken/example/lib/main.dart +++ b/kraken/example/lib/main.dart @@ -63,7 +63,7 @@ class _MyHomePageState extends State { controller: textEditingController, onSubmitted: (value) { textEditingController.text = value; - _kraken?.loadHref(KrakenBundle.fromHref(value)); + _kraken?.loadHref(KrakenBundle.fromUrl(value)); }, decoration: InputDecoration( hintText: 'Enter a app url', @@ -92,7 +92,7 @@ class _MyHomePageState extends State { devToolsService: ChromeDevToolsService(), viewportWidth: viewportSize.width - queryData.padding.horizontal, viewportHeight: viewportSize.height - appBar.preferredSize.height - queryData.padding.vertical, - bundle: KrakenBundle.fromHref('assets/bundle.js'), + bundle: KrakenBundle.fromUrl('assets://assets/bundle.html'), ), )); } diff --git a/kraken/lib/src/dom/elements/head.dart b/kraken/lib/src/dom/elements/head.dart index cb4cb9b4fc..2d76d08225 100644 --- a/kraken/lib/src/dom/elements/head.dart +++ b/kraken/lib/src/dom/elements/head.dart @@ -74,7 +74,7 @@ class ScriptElement extends Element { // Must if (src.isNotEmpty && isConnected && (type == _JAVASCRIPT_MIME || type == _JAVASCRIPT_MODULE)) { try { - KrakenBundle bundle = KrakenBundle.fromHref(src); + KrakenBundle bundle = KrakenBundle.fromUrl(src); await bundle.resolve(elementManager.contextId); await bundle.eval(elementManager.contextId); // Successful load. diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 53ee03c79f..84b2dbce6c 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -90,7 +90,7 @@ abstract class KrakenBundle { isResolved = true; } - static KrakenBundle fromHref(String url) { + static KrakenBundle fromUrl(String url) { if (isAssetAbsolutePath(url)) { return AssetsBundle(url); } else { diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index 200809ddf6..43bcbb413a 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -662,7 +662,7 @@ class KrakenController { } await unload(); - await loadBundle(bundle: KrakenBundle.fromHref(url ?? href)); + await loadBundle(bundle: KrakenBundle.fromUrl(url ?? href)); await evalBundle(); if (devToolsService != null) { diff --git a/kraken/lib/src/launcher/launcher.dart b/kraken/lib/src/launcher/launcher.dart index 81280ee8ab..b26e23a9ca 100644 --- a/kraken/lib/src/launcher/launcher.dart +++ b/kraken/lib/src/launcher/launcher.dart @@ -55,7 +55,7 @@ void launch({ } else if (bundleContent != null) { bundle = KrakenBundle.fromHrefWithContent(bundleURL, bundleContent); } else { - bundle = KrakenBundle.fromHref(bundleURL); + bundle = KrakenBundle.fromUrl(bundleURL); } await controller.loadBundle(bundle: bundle); diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index 1012257703..a51f14b017 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -253,7 +253,7 @@ class Kraken extends StatefulWidget { } else if (bundleContent != null) { bundle = KrakenBundle.fromHrefWithContent(bundleURL, bundleContent); } else { - bundle = KrakenBundle.fromHref(bundleURL); + bundle = KrakenBundle.fromUrl(bundleURL); } await controller!.loadBundle( @@ -272,7 +272,7 @@ class Kraken extends StatefulWidget { } else if (bundleContent != null) { bundle = KrakenBundle.fromHrefWithContent(bundlePath, bundleContent); } else { - bundle = KrakenBundle.fromHref(bundlePath); + bundle = KrakenBundle.fromUrl(bundlePath); } await controller!.loadBundle( From 030b6caa853438fb87aa3e9574c5e427cdce5979 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 11:51:23 +0800 Subject: [PATCH 130/167] :art: chore: modify fromContentWithHref to fromWithContent. --- kraken/example/lib/main.dart | 2 +- kraken/lib/src/dom/elements/head.dart | 2 +- kraken/lib/src/launcher/bundle.dart | 4 ++-- kraken/lib/src/launcher/launcher.dart | 4 ++-- kraken/lib/widget.dart | 12 ++++++------ 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/kraken/example/lib/main.dart b/kraken/example/lib/main.dart index 11806788e6..e8616cf2fc 100644 --- a/kraken/example/lib/main.dart +++ b/kraken/example/lib/main.dart @@ -92,7 +92,7 @@ class _MyHomePageState extends State { devToolsService: ChromeDevToolsService(), viewportWidth: viewportSize.width - queryData.padding.horizontal, viewportHeight: viewportSize.height - appBar.preferredSize.height - queryData.padding.vertical, - bundle: KrakenBundle.fromUrl('assets://assets/bundle.html'), + bundle: KrakenBundle.fromUrl('assets://assets/bundle.js'), ), )); } diff --git a/kraken/lib/src/dom/elements/head.dart b/kraken/lib/src/dom/elements/head.dart index 2d76d08225..bcc08353f8 100644 --- a/kraken/lib/src/dom/elements/head.dart +++ b/kraken/lib/src/dom/elements/head.dart @@ -110,7 +110,7 @@ class ScriptElement extends Element { int contextId = elementManager.contextId; KrakenController? controller = KrakenController.getControllerOfJSContextId(contextId); if (controller != null) { - KrakenBundle bundle = KrakenBundle.fromHrefWithContent(controller.href, script); + KrakenBundle bundle = KrakenBundle.fromWithContent(script, url: controller.href); bundle.resolve(contextId); await bundle.eval(elementManager.contextId); } diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 84b2dbce6c..7eab2fd1e4 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -98,11 +98,11 @@ abstract class KrakenBundle { } } - static KrakenBundle fromHrefWithContent(String url, String content) { + static KrakenBundle fromWithContent(String content, { String url = '' }) { return RawBundle.fromString(content, url); } - static KrakenBundle fromHrefWithByteCode(String url, Uint8List bytecode) { + static KrakenBundle fromByteCode(Uint8List bytecode, { String url = '' }) { return RawBundle.fromByteCode(bytecode, url); } diff --git a/kraken/lib/src/launcher/launcher.dart b/kraken/lib/src/launcher/launcher.dart index b26e23a9ca..a38da845e2 100644 --- a/kraken/lib/src/launcher/launcher.dart +++ b/kraken/lib/src/launcher/launcher.dart @@ -51,9 +51,9 @@ void launch({ } else { KrakenBundle bundle; if (bundleByteCode != null) { - bundle = KrakenBundle.fromHrefWithByteCode(bundleURL, bundleByteCode); + bundle = KrakenBundle.fromByteCode(bundleByteCode, url: bundleURL); } else if (bundleContent != null) { - bundle = KrakenBundle.fromHrefWithContent(bundleURL, bundleContent); + bundle = KrakenBundle.fromWithContent(bundleContent, url: bundleURL); } else { bundle = KrakenBundle.fromUrl(bundleURL); } diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index a51f14b017..517934da5a 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -229,7 +229,7 @@ class Kraken extends StatefulWidget { loadContent(String bundleContent) async { await controller!.unload(); await controller!.loadBundle( - bundle: KrakenBundle.fromHrefWithContent('', bundleContent) + bundle: KrakenBundle.fromWithContent(bundleContent) ); _evalBundle(controller!, animationController); } @@ -238,7 +238,7 @@ class Kraken extends StatefulWidget { loadByteCode(Uint8List bundleByteCode) async { await controller!.unload(); await controller!.loadBundle( - bundle: KrakenBundle.fromHrefWithByteCode('', bundleByteCode) + bundle: KrakenBundle.fromByteCode(bundleByteCode) ); _evalBundle(controller!, animationController); } @@ -249,9 +249,9 @@ class Kraken extends StatefulWidget { KrakenBundle bundle; if (bundleByteCode != null) { - bundle = KrakenBundle.fromHrefWithByteCode(bundleURL, bundleByteCode); + bundle = KrakenBundle.fromByteCode(bundleByteCode, url: bundleURL); } else if (bundleContent != null) { - bundle = KrakenBundle.fromHrefWithContent(bundleURL, bundleContent); + bundle = KrakenBundle.fromWithContent(bundleContent, url: bundleURL); } else { bundle = KrakenBundle.fromUrl(bundleURL); } @@ -268,9 +268,9 @@ class Kraken extends StatefulWidget { KrakenBundle bundle; if (bundleByteCode != null) { - bundle = KrakenBundle.fromHrefWithByteCode(bundlePath, bundleByteCode); + bundle = KrakenBundle.fromByteCode(bundleByteCode, url: bundlePath); } else if (bundleContent != null) { - bundle = KrakenBundle.fromHrefWithContent(bundlePath, bundleContent); + bundle = KrakenBundle.fromWithContent(bundleContent, url: bundlePath); } else { bundle = KrakenBundle.fromUrl(bundlePath); } From c881c6a19ed649e4066d7fdd399fa6f78bf61dda Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Fri, 3 Dec 2021 14:19:51 +0800 Subject: [PATCH 131/167] fix: fix access nativeEventTarget after freed. --- kraken/lib/src/dom/element_manager.dart | 2 ++ kraken/lib/src/dom/event_target.dart | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/kraken/lib/src/dom/element_manager.dart b/kraken/lib/src/dom/element_manager.dart index d01e7b45a4..05ed35b1e5 100644 --- a/kraken/lib/src/dom/element_manager.dart +++ b/kraken/lib/src/dom/element_manager.dart @@ -9,6 +9,7 @@ import 'dart:ffi'; import 'dart:math' as math; import 'dart:ui'; +import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; @@ -39,6 +40,7 @@ class ElementManager implements WidgetsBindingObserver, ElementsBindingObserver EventTarget? eventTarget = controller.view.getEventTargetById(id); if (eventTarget == null) return; eventTarget.dispose(); + malloc.free(eventTarget.nativeEventTargetPtr); } // Alias defineElement export for kraken plugin diff --git a/kraken/lib/src/dom/event_target.dart b/kraken/lib/src/dom/event_target.dart index a07fc5944f..f20ffaee93 100644 --- a/kraken/lib/src/dom/event_target.dart +++ b/kraken/lib/src/dom/event_target.dart @@ -169,7 +169,6 @@ abstract class EventTarget { eventHandlers.clear(); _nativeMap.remove(nativeEventTargetPtr.address); _disposed = true; - malloc.free(nativeEventTargetPtr); if (kProfileMode) { PerformanceTiming.instance().mark(PERF_DISPOSE_EVENT_TARGET_END, uniqueId: targetId); From 6c882601258546dc12806745a42632a99bed8265 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 14:20:23 +0800 Subject: [PATCH 132/167] :sparkles: feat: add additionalHttpHeaders to fromUrl of KrakenBundle. --- kraken/lib/src/launcher/bundle.dart | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 7eab2fd1e4..5b3b039d19 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -90,11 +90,11 @@ abstract class KrakenBundle { isResolved = true; } - static KrakenBundle fromUrl(String url) { + static KrakenBundle fromUrl(String url, { Map? additionalHttpHeaders }) { if (isAssetAbsolutePath(url)) { return AssetsBundle(url); } else { - return NetworkBundle(url); + return NetworkBundle(url, additionalHttpHeaders: additionalHttpHeaders); } } @@ -160,15 +160,17 @@ class RawBundle extends KrakenBundle { } class NetworkBundle extends KrakenBundle { - NetworkBundle(String url) + NetworkBundle(String url, { this.additionalHttpHeaders }) : super(url); + Map? additionalHttpHeaders = {}; + @override Future 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, Uri.parse(src)), contextId: contextId); + NetworkAssetBundle bundle = NetworkAssetBundle(controller.uriParser!.resolve(baseUrl, Uri.parse(src)), contextId: contextId, additionalHttpHeaders: additionalHttpHeaders); bundle.httpClient.userAgent = getKrakenInfo().userAgent; String absoluteURL = src; rawBundle = await bundle.load(absoluteURL); @@ -189,13 +191,14 @@ 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, this.additionalHttpHeaders }) : _baseUrl = baseUrl, httpClient = HttpClient(); final Uri _baseUrl; final int contextId; final HttpClient httpClient; + final Map? additionalHttpHeaders; ContentType contentType = ContentType.binary; Uri _urlFromKey(String key) => _baseUrl.resolve(key); @@ -204,6 +207,11 @@ class NetworkAssetBundle extends AssetBundle { Future load(String key) async { final HttpClientRequest request = await httpClient.getUrl(_urlFromKey(key)); request.headers.set('Accept', getAcceptHeader()); + if (additionalHttpHeaders != null) { + additionalHttpHeaders?.forEach((key, value) { + request.headers.set(key, value); + }); + } KrakenHttpOverrides.setContextHeader(request.headers, contextId); final HttpClientResponse response = await request.close(); From 468c8f921dd2489017c54d9ee5bc634364d209e6 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Fri, 3 Dec 2021 14:21:04 +0800 Subject: [PATCH 133/167] fix: fix image stream conflict with precache. --- kraken/lib/src/dom/elements/img.dart | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index c851b77796..203d627182 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -323,7 +323,7 @@ class ImageElement extends Element { } } - void _resolveImage(Uri? resolvedUri, { bool propertyChanged = false }) { + void _resolveImage(Uri? resolvedUri, { bool updateImageProvider = false }) { if (resolvedUri == null) return; double? width = null; @@ -341,7 +341,7 @@ class ImageElement extends Element { int? cachedHeight = (height != null && height > 0) ? (height * ui.window.devicePixelRatio).toInt() : null; ImageProvider? provider = _imageProvider; - if (propertyChanged) { + if (updateImageProvider || provider == null) { provider = _imageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); } if (provider == null) return; @@ -407,12 +407,7 @@ class ImageElement extends Element { _handleEventAfterImageLoaded(); } } - // Give callers until at least the end of the frame to subscribe to the - // image stream. - // See ImageCache._liveImages - SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) { - stream.removeListener(listener!); - }); + stream.removeListener(listener!); }, onError: _onImageError ); @@ -425,10 +420,10 @@ class ImageElement extends Element { super.setProperty(key, value); // Reset frame number to zero when image needs to reload if (key == 'src' && propertyChanged) { + final Uri? resolvedUri = _resolvedUri = _resolveSrc(); // Update image source if image already attached. if (isRendererAttached) { - final Uri? resolvedUri = _resolvedUri = _resolveSrc(); - _resolveImage(resolvedUri, propertyChanged: true); + _resolveImage(resolvedUri, updateImageProvider: true); } else { _precacheImage(); } @@ -436,10 +431,10 @@ class ImageElement extends Element { _resetLazyLoading(); } else if (key == WIDTH) { _propertyWidth = CSSNumber.parseNumber(value); - _resolveImage(_resolvedUri, propertyChanged: true); + _resolveImage(_resolvedUri, updateImageProvider: true); } else if (key == HEIGHT) { _propertyHeight = CSSNumber.parseNumber(value); - _resolveImage(_resolvedUri, propertyChanged: true); + _resolveImage(_resolvedUri, updateImageProvider: true); } } @@ -465,7 +460,7 @@ class ImageElement extends Element { // Resize renderBox if (isRendererAttached) _resizeImage(); // Resize image - _resolveImage(_resolvedUri, propertyChanged: true); + _resolveImage(_resolvedUri, updateImageProvider: true); } else if (property == OBJECT_FIT && _renderImage != null) { _renderImage!.fit = renderBoxModel!.renderStyle.objectFit; } else if (property == OBJECT_POSITION && _renderImage != null) { From c1c674f990542800d0b7e367247db23f2c14820d Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 14:24:28 +0800 Subject: [PATCH 134/167] :art: chore: delete png and test of div.html. --- .../dom/elements/div.html.7e6f39401.png | Bin 15786 -> 0 bytes integration_tests/specs/dom/elements/div.html | 23 ------------------ 2 files changed, 23 deletions(-) delete mode 100644 integration_tests/snapshots/dom/elements/div.html.7e6f39401.png delete mode 100644 integration_tests/specs/dom/elements/div.html diff --git a/integration_tests/snapshots/dom/elements/div.html.7e6f39401.png b/integration_tests/snapshots/dom/elements/div.html.7e6f39401.png deleted file mode 100644 index 8f2cc29778826b13d944aa4222a196a8745f16b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15786 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 - - - -
- Welcome to Your Kraken AppMore information about KrakenVisit http://openkraken.com/ -
- - From 42cd8c2ed0f5d5a39c8b5521a0000a0c8b1259e8 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 14:37:48 +0800 Subject: [PATCH 135/167] :white_check_mark: test: modify main of test. --- integration_tests/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/lib/main.dart b/integration_tests/lib/main.dart index df026eac19..4f522e2ca3 100644 --- a/integration_tests/lib/main.dart +++ b/integration_tests/lib/main.dart @@ -83,7 +83,7 @@ void main() async { var kraken = krakenMap[i] = Kraken( viewportWidth: 360, viewportHeight: 640, - bundle: KrakenBundle.fromHrefWithContent('', 'console.log("Starting integration tests...")'), + bundle: KrakenBundle.fromWithContent('console.log("Starting integration tests...")'), disableViewportWidthAssertion: true, disableViewportHeightAssertion: true, javaScriptChannel: javaScriptChannel, From 9d8decef3006cc6f68350dd3cb7eb720ab3a7dce Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Fri, 3 Dec 2021 14:53:01 +0800 Subject: [PATCH 136/167] fix: fix test specs. --- .../dom/elements/img.ts.56fc70fa1.png | Bin 2154 -> 183 bytes .../dom/elements/img.ts.ea354b921.png | Bin 2154 -> 183 bytes integration_tests/specs/dom/elements/img.ts | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration_tests/snapshots/dom/elements/img.ts.56fc70fa1.png b/integration_tests/snapshots/dom/elements/img.ts.56fc70fa1.png index 413d896838102f314a81bb153f6053906fff6910..091b2aa0bed0ed2377e0b026a39f71cfdcee67ee 100644 GIT binary patch delta 140 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 FV1i}7FcAO% diff --git a/integration_tests/snapshots/dom/elements/img.ts.ea354b921.png b/integration_tests/snapshots/dom/elements/img.ts.ea354b921.png index 413d896838102f314a81bb153f6053906fff6910..091b2aa0bed0ed2377e0b026a39f71cfdcee67ee 100644 GIT binary patch delta 140 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 FV1i}7FcAO% diff --git a/integration_tests/specs/dom/elements/img.ts b/integration_tests/specs/dom/elements/img.ts index 394832ac04..c70e993131 100644 --- a/integration_tests/specs/dom/elements/img.ts +++ b/integration_tests/specs/dom/elements/img.ts @@ -8,7 +8,7 @@ describe('Tags img', () => { img.style.width = '60px'; img.setAttribute( 'src', - '//gw.alicdn.com/tfs/TB1MRC_cvb2gK0jSZK9XXaEgFXa-1701-1535.png' + 'assets/100x100-green.png' ); document.body.appendChild(img); @@ -181,7 +181,7 @@ describe('Tags img', () => { const img = document.createElement('img'); // Make image loading=lazy. img.setAttribute('loading', 'lazy'); - img.src = '//gw.alicdn.com/tfs/TB1MRC_cvb2gK0jSZK9XXaEgFXa-1701-1535.png'; + img.src = 'assets/100x100-green.png'; img.style.width = '60px'; document.body.appendChild(img); From 40df352b37a80afe4f0974b226de7f8cd45d3abf Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 16:04:10 +0800 Subject: [PATCH 137/167] :white_check_mark: test: modify script test. --- integration_tests/specs/dom/elements/script.ts | 2 +- kraken/lib/src/launcher/bundle.dart | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/integration_tests/specs/dom/elements/script.ts b/integration_tests/specs/dom/elements/script.ts index c65a8e34cf..4a3722b6a9 100644 --- a/integration_tests/specs/dom/elements/script.ts +++ b/integration_tests/specs/dom/elements/script.ts @@ -4,7 +4,7 @@ describe('script element', () => { const p =

Should see hello below:

; 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(); diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 5b3b039d19..835fbfea8b 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -255,8 +255,6 @@ class AssetsBundle extends KrakenBundle { String localPath = src.substring(ASSETS_PROROCOL.length); rawBundle = await rootBundle.load(localPath); } - - isResolved = true; return this; } } From 62cecb5ce9af6946319606bb8b371cab0b6bee71 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Fri, 3 Dec 2021 16:13:39 +0800 Subject: [PATCH 138/167] fix: fix image been disposed by set same image. --- kraken/lib/src/dom/elements/img.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 203d627182..9533709fb7 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -357,7 +357,7 @@ class ImageElement extends Element { assert(isRendererAttached); assert(_renderImage != null); if (_imageInfo == null) return; - _renderImage!.image = image; + _renderImage!.image = image?.clone(); } void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) { From 915eebae77f8ec852255e52b47ae7e6a47768b4c Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 16:22:00 +0800 Subject: [PATCH 139/167] :white_check_mark: test: modify test of plugin. --- integration_tests/lib/plugin.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/lib/plugin.dart b/integration_tests/lib/plugin.dart index 63fd0b8831..4f965b2010 100644 --- a/integration_tests/lib/plugin.dart +++ b/integration_tests/lib/plugin.dart @@ -69,7 +69,7 @@ void main() async { var kraken = krakenMap[i] = Kraken( viewportWidth: 360, viewportHeight: 640, - bundle: KrakenBundle.fromHrefWithContent('', 'console.log("Starting Plugin tests...")'), + bundle: KrakenBundle.fromWithContent('console.log("Starting Plugin tests...")'), disableViewportWidthAssertion: true, disableViewportHeightAssertion: true, uriParser: IntegrationTestUriParser(), From a97715a68ba02a1ce34dd17e01e68e44bb7e6e5a Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 16:32:18 +0800 Subject: [PATCH 140/167] :white_check_mark: test: modify test of performance. --- performance_tests/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/performance_tests/lib/main.dart b/performance_tests/lib/main.dart index d586fdb0a9..d5512bf63e 100644 --- a/performance_tests/lib/main.dart +++ b/performance_tests/lib/main.dart @@ -90,7 +90,7 @@ class _MyHomePageState extends State { child: _kraken = Kraken( viewportWidth: viewportSize.width - queryData.padding.horizontal, viewportHeight: viewportSize.height - appBar.preferredSize.height - queryData.padding.vertical, - bundle: KrakenBundle.fromHref('https://kraken.oss-cn-hangzhou.aliyuncs.com/data/cvd3r6f068.js'), + bundle: KrakenBundle.fromUrl('https://kraken.oss-cn-hangzhou.aliyuncs.com/data/cvd3r6f068.js'), onLoad: (KrakenController controller) { Timer(Duration(seconds: 4), () { exit(0); From f83a872de68ad1316c56d6ca18547e7e83a98021 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Fri, 3 Dec 2021 17:02:07 +0800 Subject: [PATCH 141/167] fix: intrinsicRatio lost after renderBoxModel convert to repaintBoundary --- kraken/lib/src/dom/elements/img.dart | 2 + kraken/lib/src/rendering/box_model.dart | 6 +++ kraken/lib/src/rendering/intrinsic.dart | 58 ++----------------------- 3 files changed, 11 insertions(+), 55 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index e1ea8ced25..eae6973775 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -257,6 +257,8 @@ class ImageElement extends Element { width = 0.0; } + _renderImage?.width = width; + _renderImage?.height = height; renderBoxModel!.intrinsicWidth = naturalWidth; renderBoxModel!.intrinsicHeight = naturalHeight; diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index a648cd747c..28d7baedaf 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -698,6 +698,12 @@ class RenderBoxModel extends RenderBox // Copy render style ..renderStyle = renderStyle + // Copy intrinsic info + // @TODO: Delete after intrinsic info is moved to renderStyle. + ..intrinsicWidth = intrinsicWidth + ..intrinsicHeight = intrinsicHeight + ..intrinsicRatio = intrinsicRatio + // Copy box decoration ..boxPainter = boxPainter diff --git a/kraken/lib/src/rendering/intrinsic.dart b/kraken/lib/src/rendering/intrinsic.dart index 07d73f3125..a3d959677d 100644 --- a/kraken/lib/src/rendering/intrinsic.dart +++ b/kraken/lib/src/rendering/intrinsic.dart @@ -56,13 +56,6 @@ class RenderIntrinsic extends RenderBoxModel beforeLayout(); - double? width = renderStyle.width.isAuto ? null : renderStyle.width.computedValue; - double? height = renderStyle.height.isAuto ? null : renderStyle.height.computedValue; - double? minWidth = renderStyle.minWidth.isAuto ? null : renderStyle.minWidth.computedValue; - double? maxWidth = renderStyle.maxWidth.isNone ? null : renderStyle.maxWidth.computedValue; - double? minHeight = renderStyle.minHeight.isAuto ? null : renderStyle.minHeight.computedValue; - double? maxHeight = renderStyle.maxHeight.isNone ? null : renderStyle.maxHeight.computedValue; - if (child != null) { late DateTime childLayoutStart; if (kProfileMode) { @@ -77,55 +70,10 @@ class RenderIntrinsic extends RenderBoxModel childLayoutStart.microsecondsSinceEpoch; } - setMaxScrollableSize(child!.size); - - CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay; - bool isInlineLevel = effectiveDisplay == CSSDisplay.inlineBlock || - effectiveDisplay == CSSDisplay.inlineFlex; - - double constraintWidth = child!.size.width; - double constraintHeight = child!.size.height; - - // Constrain to min-width or max-width if width not exists - if (isInlineLevel && maxWidth != null && width == null) { - constraintWidth = - constraintWidth > maxWidth ? maxWidth : constraintWidth; - - // max-height should respect intrinsic ratio with max-width - if (intrinsicRatio != null && maxHeight == null) { - constraintHeight = constraintWidth * intrinsicRatio!; - } - } else if (isInlineLevel && minWidth != null && width == null) { - constraintWidth = - constraintWidth < minWidth ? minWidth : constraintWidth; - - // max-height should respect intrinsic ratio with max-width - if (intrinsicRatio != null && minHeight == null) { - constraintHeight = constraintWidth * intrinsicRatio!; - } - } - - // Constrain to min-height or max-height if width not exists - if (isInlineLevel && maxHeight != null && height == null) { - constraintHeight = - constraintHeight > maxHeight ? maxHeight : constraintHeight; - - // max-width should respect intrinsic ratio with max-height - if (intrinsicRatio != null && maxWidth == null) { - constraintWidth = constraintHeight / intrinsicRatio!; - } - } else if (isInlineLevel && minHeight != null && height == null) { - constraintHeight = - constraintHeight < minHeight ? minHeight : constraintHeight; - - // max-width should respect intrinsic ratio with max-height - if (intrinsicRatio != null && minWidth == null) { - constraintWidth = constraintHeight / intrinsicRatio!; - } - } + Size childSize = child!.size; - Size contentSize = Size(constraintWidth, constraintHeight); - size = getBoxSize(contentSize); + setMaxScrollableSize(childSize); + size = getBoxSize(childSize); autoMinWidth = size.width; autoMinHeight = size.height; From cc4f5d591b4821ce5ab9397c704dfb837deee549 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Fri, 3 Dec 2021 17:06:23 +0800 Subject: [PATCH 142/167] chore: delete intrinsic width/height/ratio from renderBoxModel --- kraken/lib/src/rendering/box_model.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index 8ab3faf22b..59877d6341 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -684,12 +684,6 @@ class RenderBoxModel extends RenderBox // Copy render style ..renderStyle = renderStyle - // Copy intrinsic info - // @TODO: Delete after intrinsic info is moved to renderStyle. - ..intrinsicWidth = intrinsicWidth - ..intrinsicHeight = intrinsicHeight - ..intrinsicRatio = intrinsicRatio - // Copy box decoration ..boxPainter = boxPainter From 7ad8ada5eabd5b34c3b688f4f61400b000b2ec4b Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 17:43:22 +0800 Subject: [PATCH 143/167] :art: chore: modify additionalHttpHeaders to _additionalHttpHeaders. --- kraken/lib/src/launcher/bundle.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 835fbfea8b..9348acb786 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -191,14 +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, this.additionalHttpHeaders }) + NetworkAssetBundle(Uri baseUrl, { required this.contextId, additionalHttpHeaders }) : _baseUrl = baseUrl, + _additionalHttpHeaders = additionalHttpHeaders, httpClient = HttpClient(); final Uri _baseUrl; final int contextId; final HttpClient httpClient; - final Map? additionalHttpHeaders; + final Map? _additionalHttpHeaders; ContentType contentType = ContentType.binary; Uri _urlFromKey(String key) => _baseUrl.resolve(key); @@ -207,10 +208,8 @@ class NetworkAssetBundle extends AssetBundle { Future load(String key) async { final HttpClientRequest request = await httpClient.getUrl(_urlFromKey(key)); request.headers.set('Accept', getAcceptHeader()); - if (additionalHttpHeaders != null) { - additionalHttpHeaders?.forEach((key, value) { - request.headers.set(key, value); - }); + if (_additionalHttpHeaders != null) { + _additionalHttpHeaders?.forEach(request.headers.set); } KrakenHttpOverrides.setContextHeader(request.headers, contextId); From 37647c9da8285d8243988309938c3111e9063580 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 17:45:55 +0800 Subject: [PATCH 144/167] :art: chore: modify fromWithContent to fromContent. --- kraken/example/lib/main.dart | 2 +- kraken/lib/src/dom/elements/head.dart | 2 +- kraken/lib/src/launcher/bundle.dart | 2 +- kraken/lib/src/launcher/launcher.dart | 2 +- kraken/lib/widget.dart | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/kraken/example/lib/main.dart b/kraken/example/lib/main.dart index e8616cf2fc..b0cf2d27c5 100644 --- a/kraken/example/lib/main.dart +++ b/kraken/example/lib/main.dart @@ -63,7 +63,7 @@ class _MyHomePageState extends State { controller: textEditingController, onSubmitted: (value) { textEditingController.text = value; - _kraken?.loadHref(KrakenBundle.fromUrl(value)); + _kraken?.loadBundle(KrakenBundle.fromUrl(value)); }, decoration: InputDecoration( hintText: 'Enter a app url', diff --git a/kraken/lib/src/dom/elements/head.dart b/kraken/lib/src/dom/elements/head.dart index bcc08353f8..c3cbbdafe6 100644 --- a/kraken/lib/src/dom/elements/head.dart +++ b/kraken/lib/src/dom/elements/head.dart @@ -110,7 +110,7 @@ class ScriptElement extends Element { int contextId = elementManager.contextId; KrakenController? controller = KrakenController.getControllerOfJSContextId(contextId); if (controller != null) { - KrakenBundle bundle = KrakenBundle.fromWithContent(script, url: controller.href); + KrakenBundle bundle = KrakenBundle.fromContent(script, url: controller.href); bundle.resolve(contextId); await bundle.eval(elementManager.contextId); } diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 9348acb786..2de952638d 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -98,7 +98,7 @@ abstract class KrakenBundle { } } - static KrakenBundle fromWithContent(String content, { String url = '' }) { + static KrakenBundle fromContent(String content, { String url = '' }) { return RawBundle.fromString(content, url); } diff --git a/kraken/lib/src/launcher/launcher.dart b/kraken/lib/src/launcher/launcher.dart index a38da845e2..fa1a987dc3 100644 --- a/kraken/lib/src/launcher/launcher.dart +++ b/kraken/lib/src/launcher/launcher.dart @@ -53,7 +53,7 @@ void launch({ if (bundleByteCode != null) { bundle = KrakenBundle.fromByteCode(bundleByteCode, url: bundleURL); } else if (bundleContent != null) { - bundle = KrakenBundle.fromWithContent(bundleContent, url: bundleURL); + bundle = KrakenBundle.fromContent(bundleContent, url: bundleURL); } else { bundle = KrakenBundle.fromUrl(bundleURL); } diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index 517934da5a..8f70157f61 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -217,7 +217,7 @@ class Kraken extends StatefulWidget { defineElement(tagName.toUpperCase(), creator); } - loadHref(KrakenBundle bundle) async { + loadBundle(KrakenBundle bundle) async { await controller!.unload(); await controller!.loadBundle( bundle: bundle @@ -229,7 +229,7 @@ class Kraken extends StatefulWidget { loadContent(String bundleContent) async { await controller!.unload(); await controller!.loadBundle( - bundle: KrakenBundle.fromWithContent(bundleContent) + bundle: KrakenBundle.fromContent(bundleContent) ); _evalBundle(controller!, animationController); } @@ -251,7 +251,7 @@ class Kraken extends StatefulWidget { if (bundleByteCode != null) { bundle = KrakenBundle.fromByteCode(bundleByteCode, url: bundleURL); } else if (bundleContent != null) { - bundle = KrakenBundle.fromWithContent(bundleContent, url: bundleURL); + bundle = KrakenBundle.fromContent(bundleContent, url: bundleURL); } else { bundle = KrakenBundle.fromUrl(bundleURL); } @@ -270,7 +270,7 @@ class Kraken extends StatefulWidget { if (bundleByteCode != null) { bundle = KrakenBundle.fromByteCode(bundleByteCode, url: bundlePath); } else if (bundleContent != null) { - bundle = KrakenBundle.fromWithContent(bundleContent, url: bundlePath); + bundle = KrakenBundle.fromContent(bundleContent, url: bundlePath); } else { bundle = KrakenBundle.fromUrl(bundlePath); } From ae0dc1b440c7ed233d9ff6dbbb22abf4f69beabd Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 17:49:28 +0800 Subject: [PATCH 145/167] :art: chore: modify fromByteCode to fromBytecode. --- kraken/lib/src/launcher/bundle.dart | 6 +++--- kraken/lib/src/launcher/launcher.dart | 2 +- kraken/lib/widget.dart | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 2de952638d..d31e61ea42 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -102,8 +102,8 @@ abstract class KrakenBundle { return RawBundle.fromString(content, url); } - static KrakenBundle fromByteCode(Uint8List bytecode, { String url = '' }) { - return RawBundle.fromByteCode(bytecode, url); + static KrakenBundle fromBytecode(Uint8List bytecode, { String url = '' }) { + return RawBundle.fromBytecode(bytecode, url); } @@ -147,7 +147,7 @@ class RawBundle extends KrakenBundle { this.content = content; } - RawBundle.fromByteCode(Uint8List byteCode, String url) + RawBundle.fromBytecode(Uint8List byteCode, String url) : super(url) { this.byteCode = byteCode; } diff --git a/kraken/lib/src/launcher/launcher.dart b/kraken/lib/src/launcher/launcher.dart index fa1a987dc3..46ceeb8a1e 100644 --- a/kraken/lib/src/launcher/launcher.dart +++ b/kraken/lib/src/launcher/launcher.dart @@ -51,7 +51,7 @@ void launch({ } else { KrakenBundle bundle; if (bundleByteCode != null) { - bundle = KrakenBundle.fromByteCode(bundleByteCode, url: bundleURL); + bundle = KrakenBundle.fromBytecode(bundleByteCode, url: bundleURL); } else if (bundleContent != null) { bundle = KrakenBundle.fromContent(bundleContent, url: bundleURL); } else { diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index 8f70157f61..d0dab9879b 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -238,7 +238,7 @@ class Kraken extends StatefulWidget { loadByteCode(Uint8List bundleByteCode) async { await controller!.unload(); await controller!.loadBundle( - bundle: KrakenBundle.fromByteCode(bundleByteCode) + bundle: KrakenBundle.fromBytecode(bundleByteCode) ); _evalBundle(controller!, animationController); } @@ -249,7 +249,7 @@ class Kraken extends StatefulWidget { KrakenBundle bundle; if (bundleByteCode != null) { - bundle = KrakenBundle.fromByteCode(bundleByteCode, url: bundleURL); + bundle = KrakenBundle.fromBytecode(bundleByteCode, url: bundleURL); } else if (bundleContent != null) { bundle = KrakenBundle.fromContent(bundleContent, url: bundleURL); } else { @@ -268,7 +268,7 @@ class Kraken extends StatefulWidget { KrakenBundle bundle; if (bundleByteCode != null) { - bundle = KrakenBundle.fromByteCode(bundleByteCode, url: bundlePath); + bundle = KrakenBundle.fromBytecode(bundleByteCode, url: bundlePath); } else if (bundleContent != null) { bundle = KrakenBundle.fromContent(bundleContent, url: bundlePath); } else { From 7637bdc1a79ee1d30a4b099a3b244c6b9866dda6 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 17:51:23 +0800 Subject: [PATCH 146/167] :art: chore: modify byteCode to bytecode. --- kraken/lib/src/launcher/bundle.dart | 10 +++++----- kraken/lib/src/launcher/controller.dart | 4 ++-- kraken/lib/widget.dart | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index d31e61ea42..6a2105c26e 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -62,7 +62,7 @@ abstract class KrakenBundle { 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. @@ -117,8 +117,8 @@ abstract class KrakenBundle { // For raw javascript code or bytecode from API directly. if (content != null) { evaluateScripts(contextId, content!, src, lineOffset); - } else if (byteCode != null) { - evaluateQuickjsByteCode(contextId, byteCode!); + } else if (bytecode != null) { + evaluateQuickjsByteCode(contextId, bytecode!); } // For javascript code, HTML or bytecode from networks and hardware disk. @@ -147,9 +147,9 @@ class RawBundle extends KrakenBundle { this.content = content; } - RawBundle.fromBytecode(Uint8List byteCode, String url) + RawBundle.fromBytecode(Uint8List bytecode, String url) : super(url) { - this.byteCode = byteCode; + this.bytecode = bytecode; } @override diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index 43bcbb413a..714b0719a2 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -722,8 +722,8 @@ class KrakenController { } href = url ?? ''; - if (bundle.byteCode != null) { - bundleByteCode = bundle.byteCode; + if (bundle.bytecode != null) { + bundleByteCode = bundle.bytecode; } if (bundle.content != null) { diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index d0dab9879b..362f850439 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -988,7 +988,7 @@ class _KrakenRenderObjectElement extends SingleChildRenderObjectElement { KrakenController controller = (renderObject as RenderObjectWithControllerMixin).controller!; - if (controller.bundle == null || (controller.bundle?.content == null && controller.bundle?.byteCode == null && controller.bundle?.src == null)) { + if (controller.bundle == null || (controller.bundle?.content == null && controller.bundle?.bytecode == null && controller.bundle?.src == null)) { return; } From 2638f19cf2e1c3118fd3b614e0cea8f30b953a74 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 18:13:55 +0800 Subject: [PATCH 147/167] :sparkles: feat: modify bundle for launch. --- kraken/lib/src/launcher/launcher.dart | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/kraken/lib/src/launcher/launcher.dart b/kraken/lib/src/launcher/launcher.dart index 46ceeb8a1e..14501a1e59 100644 --- a/kraken/lib/src/launcher/launcher.dart +++ b/kraken/lib/src/launcher/launcher.dart @@ -20,10 +20,7 @@ typedef ConnectedCallback = void Function(); const _white = Color(0xFFFFFFFF); void launch({ - String? bundleURL, - String? bundlePath, - String? bundleContent, - Uint8List? bundleByteCode, + required KrakenBundle bundle, bool? debugEnableInspector, Color background = _white, DevToolsService? devToolsService, @@ -46,20 +43,7 @@ void launch({ controller.view.attachView(RendererBinding.instance!.renderView); - if (bundleURL == null) { - await controller.loadBundle(); - } else { - KrakenBundle bundle; - if (bundleByteCode != null) { - bundle = KrakenBundle.fromBytecode(bundleByteCode, url: bundleURL); - } else if (bundleContent != null) { - bundle = KrakenBundle.fromContent(bundleContent, url: bundleURL); - } else { - bundle = KrakenBundle.fromUrl(bundleURL); - } - - await controller.loadBundle(bundle: bundle); - } + await controller.loadBundle(bundle: bundle); await controller.evalBundle(); } From 23e9b9f9614276a0c2cee9d11ad0057fa427507c Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 18:15:39 +0800 Subject: [PATCH 148/167] :art: chore: remove the blank space. --- kraken/lib/src/module/navigation.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/src/module/navigation.dart b/kraken/lib/src/module/navigation.dart index 9e80144c03..37503c6d42 100644 --- a/kraken/lib/src/module/navigation.dart +++ b/kraken/lib/src/module/navigation.dart @@ -41,7 +41,7 @@ class NavigationModule extends BaseModule { String? sourceUrl = moduleManager!.controller.href; Uri targetUri = Uri.parse(targetUrl); - Uri sourceUri = Uri.parse(sourceUrl); + Uri sourceUri = Uri.parse(sourceUrl); if (targetUri != sourceUri) { await moduleManager!.controller.view.handleNavigationAction(sourceUrl, targetUrl, KrakenNavigationType.reload); From f2465b10b25010e4afab021d12177188f14ef72f Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 19:17:34 +0800 Subject: [PATCH 149/167] :art: chore: bundle is not required. --- kraken/lib/src/launcher/launcher.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/src/launcher/launcher.dart b/kraken/lib/src/launcher/launcher.dart index 14501a1e59..07f2a6f4ff 100644 --- a/kraken/lib/src/launcher/launcher.dart +++ b/kraken/lib/src/launcher/launcher.dart @@ -20,7 +20,7 @@ typedef ConnectedCallback = void Function(); const _white = Color(0xFFFFFFFF); void launch({ - required KrakenBundle bundle, + KrakenBundle? bundle, bool? debugEnableInspector, Color background = _white, DevToolsService? devToolsService, From 3b6fd5925928374dd3ad7e30f1bde1a193b48b02 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 19:19:34 +0800 Subject: [PATCH 150/167] :art: chore: deal with lint. --- kraken/lib/src/launcher/launcher.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/kraken/lib/src/launcher/launcher.dart b/kraken/lib/src/launcher/launcher.dart index 07f2a6f4ff..53e50a6387 100644 --- a/kraken/lib/src/launcher/launcher.dart +++ b/kraken/lib/src/launcher/launcher.dart @@ -6,7 +6,6 @@ import 'dart:io'; import 'dart:ui'; import 'dart:async'; -import 'dart:typed_data'; import 'package:flutter/rendering.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/kraken.dart'; From abf0dccc197482e688e92b7d7bcf42ecff0a52d4 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 20:32:27 +0800 Subject: [PATCH 151/167] :sparkles: feat: use bundle replace src. --- kraken/lib/src/launcher/controller.dart | 49 ++++-------------------- kraken/lib/src/module/history.dart | 50 ++++++++----------------- kraken/lib/src/module/navigation.dart | 4 +- 3 files changed, 24 insertions(+), 79 deletions(-) diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index 714b0719a2..0e1affc8f2 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -476,9 +476,6 @@ class KrakenController { double viewportHeight, { bool showPerformanceOverlay = false, enableDebug = false, - String? bundleURL, - String? bundlePath, - String? bundleContent, Color? background, GestureListener? gestureListener, KrakenNavigationDelegate? navigationDelegate, @@ -519,14 +516,9 @@ class KrakenController { _module = KrakenModuleController(this, contextId); - if (bundleURL != null) { - href = bundleURL; - } else if (bundlePath != null) { - href = bundlePath; - } - - if (bundleContent != null) { - this.bundleContent = bundleContent; + if (bundle != null) { + HistoryModule historyModule = module.moduleManager.getModule('History')!; + historyModule.bundle = bundle!; } assert(!_controllerMap.containsKey(contextId), @@ -627,30 +619,12 @@ class KrakenController { } set href(String value) { - HistoryModule historyModule = module.moduleManager.getModule('History')!; - historyModule.href = value; - } - - set bundleContent(String? value) { - if (value == null) return; - HistoryModule historyModule = module.moduleManager.getModule('History')!; - historyModule.bundleContent = value; + _addHistory(KrakenBundle.fromUrl(value)); } - String? get bundleContent { + _addHistory(KrakenBundle bundle) { HistoryModule historyModule = module.moduleManager.getModule('History')!; - return historyModule.bundleContent; - } - - set bundleByteCode(Uint8List? value) { - if (value == null) return; - HistoryModule historyModule = module.moduleManager.getModule('History')!; - historyModule.bundleByteCode = value; - } - - Uint8List? get bundleByteCode { - HistoryModule historyModule = module.moduleManager.getModule('History')!; - return historyModule.bundleByteCode; + historyModule.bundle = bundle; } // reload current kraken view. @@ -720,15 +694,8 @@ class KrakenController { if (url == null && methodChannel is KrakenNativeChannel) { url = await (methodChannel as KrakenNativeChannel).getUrl(); } - href = url ?? ''; - - if (bundle.bytecode != null) { - bundleByteCode = bundle.bytecode; - } - - if (bundle.content != null) { - bundleContent = bundle.content; - } + _addHistory(bundle); + this.bundle = bundle; } if (onLoadError != null) { diff --git a/kraken/lib/src/module/history.dart b/kraken/lib/src/module/history.dart index 96ce241c02..34e151a864 100644 --- a/kraken/lib/src/module/history.dart +++ b/kraken/lib/src/module/history.dart @@ -14,12 +14,10 @@ import 'package:kraken/kraken.dart'; import 'package:kraken/module.dart'; class HistoryItem { - HistoryItem(this.href, this.state, this.needJump, { this.bundleContent, this.bundleByteCode }); - final String href; + HistoryItem(this.bundle, this.state, this.needJump); + final KrakenBundle bundle; final dynamic state; final bool needJump; - String? bundleContent; - Uint8List? bundleByteCode; } class HistoryModule extends BaseModule { @@ -33,36 +31,16 @@ class HistoryModule extends BaseModule { String get href { if (_previousStack.isEmpty) return ''; - return _previousStack.first.href; + return _previousStack.first.bundle.src; } - set href(String value) { - HistoryItem history = HistoryItem(value, null, true); + set bundle(KrakenBundle bundle) { + HistoryItem history = HistoryItem(bundle, null, true); _addItem(history); } - set bundleContent(String? value) { - if (_previousStack.isEmpty || value == null) return; - _previousStack.first.bundleContent = value; - } - - String? get bundleContent { - if (_previousStack.isEmpty) return null; - return _previousStack.first.bundleContent; - } - - set bundleByteCode(Uint8List? value) { - if (_previousStack.isEmpty || value == null) return; - _previousStack.first.bundleByteCode = value; - } - - Uint8List? get bundleByteCode { - if (_previousStack.isEmpty) return Uint8List(0); - return _previousStack.first.bundleByteCode; - } - void _addItem(HistoryItem historyItem) { - if (_previousStack.isNotEmpty && historyItem.href == _previousStack.first.href) return; + if (_previousStack.isNotEmpty && historyItem.bundle.src == _previousStack.first.bundle.src) return; _previousStack.addFirst(historyItem); @@ -79,7 +57,7 @@ class HistoryModule extends BaseModule { _previousStack.removeFirst(); _nextStack.addFirst(currentItem); - await _goTo(_previousStack.first.href); + await _goTo(_previousStack.first.bundle.src); dynamic state = _previousStack.first.state; _dispatchPopStateEvent(state); @@ -92,7 +70,7 @@ class HistoryModule extends BaseModule { _nextStack.removeFirst(); _previousStack.addFirst(currentItem); - _goTo(currentItem.href); + _goTo(currentItem.bundle.src); _dispatchPopStateEvent(currentItem.state); } } @@ -121,7 +99,7 @@ class HistoryModule extends BaseModule { } } - _goTo(_previousStack.first.href); + _goTo(_previousStack.first.bundle.src); _dispatchPopStateEvent(_previousStack.first.state); } @@ -144,7 +122,7 @@ class HistoryModule extends BaseModule { if (params[2] != null) { url = params[2]; - String currentUrl = _previousStack.first.href; + String currentUrl = _previousStack.first.bundle.src; Uri currentUri = Uri.parse(currentUrl); Uri uri = Uri.parse(url!); @@ -156,7 +134,8 @@ class HistoryModule extends BaseModule { return; } - HistoryItem history = HistoryItem(uri.toString(), state, false); + KrakenBundle bundle = KrakenBundle.fromUrl(uri.toString()); + HistoryItem history = HistoryItem(bundle, state, false); _addItem(history); } } @@ -169,7 +148,7 @@ class HistoryModule extends BaseModule { if (params[2] != null) { url = params[2]; - String currentUrl = _previousStack.first.href; + String currentUrl = _previousStack.first.bundle.src; Uri currentUri = Uri.parse(currentUrl); Uri uri = Uri.parse(url!); @@ -181,7 +160,8 @@ class HistoryModule extends BaseModule { return; } - HistoryItem history = HistoryItem(uri.toString(), state, false); + KrakenBundle bundle = KrakenBundle.fromUrl(uri.toString()); + HistoryItem history = HistoryItem(bundle, state, false); _previousStack.removeFirst(); _previousStack.addFirst(history); diff --git a/kraken/lib/src/module/navigation.dart b/kraken/lib/src/module/navigation.dart index 37503c6d42..7d9f051a85 100644 --- a/kraken/lib/src/module/navigation.dart +++ b/kraken/lib/src/module/navigation.dart @@ -43,9 +43,7 @@ class NavigationModule extends BaseModule { Uri targetUri = Uri.parse(targetUrl); Uri sourceUri = Uri.parse(sourceUrl); - if (targetUri != sourceUri) { - await moduleManager!.controller.view.handleNavigationAction(sourceUrl, targetUrl, KrakenNavigationType.reload); - } + await moduleManager!.controller.view.handleNavigationAction(sourceUrl, targetUrl, targetUri == sourceUri ? KrakenNavigationType.reload : KrakenNavigationType.navigate); } @override From 281ccf07247e7cb6308c04d3aeab3883e8eff7a8 Mon Sep 17 00:00:00 2001 From: answershuto Date: Fri, 3 Dec 2021 20:38:38 +0800 Subject: [PATCH 152/167] :art: chore: dealwith lint. --- kraken/lib/src/module/history.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/kraken/lib/src/module/history.dart b/kraken/lib/src/module/history.dart index 34e151a864..380d9a6765 100644 --- a/kraken/lib/src/module/history.dart +++ b/kraken/lib/src/module/history.dart @@ -7,7 +7,6 @@ import 'dart:collection'; import 'dart:convert'; import 'dart:async'; -import 'dart:typed_data'; import 'package:kraken/dom.dart'; import 'package:kraken/kraken.dart'; From 5aa667147a6cf92d82d9ce85e8507fd518baf243 Mon Sep 17 00:00:00 2001 From: answershuto Date: Mon, 6 Dec 2021 11:37:49 +0800 Subject: [PATCH 153/167] :white_check_mark: test: modify test of plugin. --- integration_tests/lib/main.dart | 2 +- integration_tests/lib/plugin.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration_tests/lib/main.dart b/integration_tests/lib/main.dart index 4f522e2ca3..5dbfa1de02 100644 --- a/integration_tests/lib/main.dart +++ b/integration_tests/lib/main.dart @@ -83,7 +83,7 @@ void main() async { var kraken = krakenMap[i] = Kraken( viewportWidth: 360, viewportHeight: 640, - bundle: KrakenBundle.fromWithContent('console.log("Starting integration tests...")'), + bundle: KrakenBundle.fromContent('console.log("Starting integration tests...")'), disableViewportWidthAssertion: true, disableViewportHeightAssertion: true, javaScriptChannel: javaScriptChannel, diff --git a/integration_tests/lib/plugin.dart b/integration_tests/lib/plugin.dart index 4f965b2010..38c36dfa90 100644 --- a/integration_tests/lib/plugin.dart +++ b/integration_tests/lib/plugin.dart @@ -69,7 +69,7 @@ void main() async { var kraken = krakenMap[i] = Kraken( viewportWidth: 360, viewportHeight: 640, - bundle: KrakenBundle.fromWithContent('console.log("Starting Plugin tests...")'), + bundle: KrakenBundle.fromContent('console.log("Starting Plugin tests...")'), disableViewportWidthAssertion: true, disableViewportHeightAssertion: true, uriParser: IntegrationTestUriParser(), From d6ed08f77d08d1aff2742b72ce4f526b9819c20e Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Mon, 6 Dec 2021 14:28:07 +0800 Subject: [PATCH 154/167] fix: flexbox of align-items stretch and flex item of align-self flex-start should not stretch --- .../css-flexbox/align-items.ts.e81143e31.png | Bin 0 -> 2394 bytes .../css-flexbox/align-items.ts.e81143e32.png | Bin 0 -> 2372 bytes .../specs/css/css-flexbox/align-items.ts | 40 ++++++++++++++++++ kraken/lib/src/css/display.dart | 6 ++- 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 integration_tests/snapshots/css/css-flexbox/align-items.ts.e81143e31.png create mode 100644 integration_tests/snapshots/css/css-flexbox/align-items.ts.e81143e32.png diff --git a/integration_tests/snapshots/css/css-flexbox/align-items.ts.e81143e31.png b/integration_tests/snapshots/css/css-flexbox/align-items.ts.e81143e31.png new file mode 100644 index 0000000000000000000000000000000000000000..3969f2b5f5cc6ac88d088b579659b3b8431503eb GIT binary patch literal 2394 zcmeAS@N?(olHy`uVBq!ia0y~yV9a1(U~1rC1Bz^vdbAu!F%}28J29*~C-V}>VJUX< z4B-HR8jh3>1_n+RPZ!6KiaBquIr1I~U|@ChG6-b->fGcLvtWU`$b+UDmJ7e{FEQr2 zm-%_x^WVmL2NwR`c5dE*&e(K*h6ghom|Zwc9HtC*>dM>Q`_u9p*1a{XW>kOXI5Zqh zy-?Ln?lG)pVfb&8(V)@D)*&=XjRwJJDj3ZOqh-NpaX4BdWHhXhU$Rkv<5^nL3Sj$) N!PC{xWt~$(69Bi)(J=r3 literal 0 HcmV?d00001 diff --git a/integration_tests/snapshots/css/css-flexbox/align-items.ts.e81143e32.png b/integration_tests/snapshots/css/css-flexbox/align-items.ts.e81143e32.png new file mode 100644 index 0000000000000000000000000000000000000000..7169a15efaa884cda25700841bb0960bb0b1d44d GIT binary patch literal 2372 zcmeAS@N?(olHy`uVBq!ia0y~yV9a1(U~1rC1Bz^vdbAu!F%}28J29*~C-V}>VJUX< z4B-HR8jh3>1_q9Io-U3d6?5KRJILE&Ai#3)aZ$-6zambCe|ep&IG$admHpn<{=wC# z*I!ud-qcqyFuZ4xQt(>9xM-9bj6tw&=FH2=3>BY7BWN^@j%L%*l4`V+9*m{*XsirW btmIu}=lOoae8N; { await snapshot(); }); + it('does not work with stretch when align-self of flex item changed from auto to flex-start', async (done) => { + let flexbox; + let flexitem; + + flexbox = createElement( + 'div', + { + style: { + display: 'flex', + 'background-color': '#aaa', + position: 'relative', + flexDirection: 'column', + width: '200px', + height: '120px', + 'box-sizing': 'border-box', + }, + }, + [ + (flexitem = createElement('div', { + style: { + 'height': '50px', + 'background-color': 'lightblue', + 'box-sizing': 'border-box', + // 'align-self': 'flex-start', + }, + })), + ] + ); + + BODY.appendChild(flexbox); + + await snapshot(); + + requestAnimationFrame(async () => { + flexitem.style.alignSelf = 'flex-start'; + await snapshot(); + done(); + }); + }); + it('should works with img with no size set', async () => { const container = createElement( 'div', diff --git a/kraken/lib/src/css/display.dart b/kraken/lib/src/css/display.dart index 0d7efd53b3..73fb7ef35a 100644 --- a/kraken/lib/src/css/display.dart +++ b/kraken/lib/src/css/display.dart @@ -86,10 +86,12 @@ mixin CSSDisplayMixin on RenderStyle { parentRenderStyle.flexDirection == FlexDirection.columnReverse; // Flex item will not stretch in stretch alignment when flex wrap is set to wrap or wrap-reverse bool isFlexNoWrap = parentRenderStyle.flexWrap == FlexWrap.nowrap; - bool isAlignItemsStretch = parentRenderStyle.effectiveAlignItems == AlignItems.stretch; + bool isStretchSelf = alignSelf != AlignSelf.auto + ? alignSelf == AlignSelf.stretch + : parentRenderStyle.effectiveAlignItems == AlignItems.stretch; // Display as block if flex vertical layout children and stretch children - if (!marginLeft.isAuto && !marginRight.isAuto && isVerticalDirection && isFlexNoWrap && isAlignItemsStretch) { + if (!marginLeft.isAuto && !marginRight.isAuto && isVerticalDirection && isFlexNoWrap && isStretchSelf) { transformedDisplay = CSSDisplay.block; } } From 0a5fb7a1f96aa5c1ff1bd7d75c24e72808c1383b Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Mon, 6 Dec 2021 19:37:09 +0800 Subject: [PATCH 155/167] fix: image width/height property change not work when width/height style is not set --- kraken/lib/src/css/keywords.dart | 2 + kraken/lib/src/dom/elements/img.dart | 77 ++++++++++++---------------- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/kraken/lib/src/css/keywords.dart b/kraken/lib/src/css/keywords.dart index 9135d8fc28..e608dace69 100644 --- a/kraken/lib/src/css/keywords.dart +++ b/kraken/lib/src/css/keywords.dart @@ -19,6 +19,8 @@ const String MIN_HEIGHT = 'minHeight'; const String MAX_HEIGHT = 'maxHeight'; const String MIN_WIDTH = 'minWidth'; const String MAX_WIDTH = 'maxWidth'; +const String NATURAL_WIDTH = 'naturalWidth'; +const String NATURAL_HEIGHT = 'naturalHeight'; const String OVERFLOW = 'overflow'; const String OVERFLOW_X = 'overflowX'; diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 999be58b57..2310df1519 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -40,6 +40,9 @@ class ImageElement extends Element { double? _propertyWidth; double? _propertyHeight; + double? _styleWidth; + double? _styleHeight; + ui.Image? get image => _imageInfo?.image; /// Number of image frame, used to identify multi frame image after loaded. @@ -121,29 +124,33 @@ class ImageElement extends Element { } double get width { - if (_renderImage != null && _renderImage!.width != null) { - return _renderImage!.width!; - } + double? width = _styleWidth ?? _propertyWidth; - if (renderBoxModel != null && renderBoxModel!.hasSize) { - return renderBoxModel!.clientWidth; + if (width == null) { + width = naturalWidth; + double? height = _styleHeight ?? _propertyHeight; + + if (height != null && naturalHeight != 0) { + width = height * naturalWidth / naturalHeight; + } } - // Fallback to natural width, if image is not on screen. - return naturalWidth; + return width; } double get height { - if (_renderImage != null && _renderImage!.height != null) { - return _renderImage!.height!; - } + double? height = _styleHeight ?? _propertyHeight; + + if (height == null) { + height = naturalHeight; + double? width = _styleWidth ?? _propertyWidth; - if (renderBoxModel != null && renderBoxModel!.hasSize) { - return renderBoxModel!.clientHeight; + if (width != null && naturalWidth != 0) { + height = width * naturalHeight / naturalWidth; + } } - // Fallback to natural height, if image is not on screen. - return naturalHeight; + return height; } double get naturalWidth => image?.width.toDouble() ?? 0; @@ -228,37 +235,15 @@ class ImageElement extends Element { return; } - double? width = renderStyle.width.isAuto ? _propertyWidth : renderStyle.width.computedValue; - double? height = renderStyle.height.isAuto ? _propertyHeight : renderStyle.height.computedValue; - - if (renderStyle.width.isAuto && _propertyWidth != null) { + if (_styleWidth == null && _propertyWidth != null) { // The intrinsic width of the image in pixels. Must be an integer without a unit. renderStyle.width = CSSLengthValue(_propertyWidth, CSSLengthType.PX); } - if (renderStyle.height.isAuto && _propertyHeight != null) { + if (_styleHeight == null && _propertyHeight != null) { // The intrinsic height of the image, in pixels. Must be an integer without a unit. renderStyle.height = CSSLengthValue(_propertyHeight, CSSLengthType.PX); } - if (width == null && height == null) { - width = naturalWidth; - height = naturalHeight; - } else if (width != null && height == null && naturalWidth != 0) { - height = width * naturalHeight / naturalWidth; - } else if (width == null && height != null && naturalHeight != 0) { - width = height * naturalWidth / naturalHeight; - } - - if (height == null || !height.isFinite) { - height = 0.0; - } - - if (width == null || !width.isFinite) { - width = 0.0; - } - - _renderImage?.width = width; - _renderImage?.height = height; renderStyle.intrinsicWidth = naturalWidth; renderStyle.intrinsicHeight = naturalHeight; @@ -337,12 +322,12 @@ class ImageElement extends Element { dynamic getProperty(String key) { switch (key) { case WIDTH: - return _renderImage?.width ?? 0; + return width; case HEIGHT: - return _renderImage?.height ?? 0; - case 'naturalWidth': + return height; + case NATURAL_WIDTH: return naturalWidth; - case 'naturalHeight': + case NATURAL_HEIGHT: return naturalHeight; } @@ -350,7 +335,13 @@ class ImageElement extends Element { } void _stylePropertyChanged(String property, String? original, String present) { - if (property == WIDTH || property == HEIGHT) { + if (property == WIDTH) { + _styleWidth = renderStyle.width.value == null && renderStyle.width.isNotAuto + ? null : renderStyle.width.computedValue; + _resize(); + } else if (property == HEIGHT) { + _styleHeight = renderStyle.height.value == null && renderStyle.height.isNotAuto + ? null : renderStyle.height.computedValue; _resize(); } else if (property == OBJECT_FIT && _renderImage != null) { _renderImage!.fit = renderBoxModel!.renderStyle.objectFit; From 10ed9496511ef976b66cd498cc5b704c4f147564 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Mon, 6 Dec 2021 19:57:42 +0800 Subject: [PATCH 156/167] test: add image width property tests --- .../dom/elements/img.ts.56441b221.png | Bin 0 -> 2368 bytes .../dom/elements/img.ts.8ea40d5a1.png | Bin 0 -> 2368 bytes integration_tests/specs/dom/elements/img.ts | 30 ++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 integration_tests/snapshots/dom/elements/img.ts.56441b221.png create mode 100644 integration_tests/snapshots/dom/elements/img.ts.8ea40d5a1.png diff --git a/integration_tests/snapshots/dom/elements/img.ts.56441b221.png b/integration_tests/snapshots/dom/elements/img.ts.56441b221.png new file mode 100644 index 0000000000000000000000000000000000000000..b30d24113866aa4324e891d5a2b2f90447137386 GIT binary patch literal 2368 zcmeAS@N?(olHy`uVBq!ia0y~yV9a1(U~1rC1Bz^vdbAu!F%}28J29*~C-V}>VJUX< z4B-HR8jh3>1_q8-o-U3d6?5KR+bGyzz{BF`{P@C74Tk6Y61F$_mn_cSn=Q7%wEy#i zIlsR!FnnW?Qt(>9xM-B3ZV;@SIrFkI!;jC7%r2ZJ4pSUPsnH-9O$DPFVYDn5Ee=O( e1ZvfWwQ=b@-?esFEd;iL7(8A5T-G@yGywol3N&c| literal 0 HcmV?d00001 diff --git a/integration_tests/snapshots/dom/elements/img.ts.8ea40d5a1.png b/integration_tests/snapshots/dom/elements/img.ts.8ea40d5a1.png new file mode 100644 index 0000000000000000000000000000000000000000..b30d24113866aa4324e891d5a2b2f90447137386 GIT binary patch literal 2368 zcmeAS@N?(olHy`uVBq!ia0y~yV9a1(U~1rC1Bz^vdbAu!F%}28J29*~C-V}>VJUX< z4B-HR8jh3>1_q8-o-U3d6?5KR+bGyzz{BF`{P@C74Tk6Y61F$_mn_cSn=Q7%wEy#i zIlsR!FnnW?Qt(>9xM-B3ZV;@SIrFkI!;jC7%r2ZJ4pSUPsnH-9O$DPFVYDn5Ee=O( e1ZvfWwQ=b@-?esFEd;iL7(8A5T-G@yGywol3N&c| literal 0 HcmV?d00001 diff --git a/integration_tests/specs/dom/elements/img.ts b/integration_tests/specs/dom/elements/img.ts index 21f58f33d7..794c8f8029 100644 --- a/integration_tests/specs/dom/elements/img.ts +++ b/integration_tests/specs/dom/elements/img.ts @@ -326,4 +326,34 @@ describe('Tags img', () => { document.body.appendChild(img); img.src = imageURL; }); + + it('width property change should work when width of style is not set', async (done) => { + let img = createElement('img', { + src: 'assets/300x150-green.png', + width: 100, + height: 100, + }); + BODY.appendChild(img); + + requestAnimationFrame(async () => { + img.width = 200; + await snapshot(0.1); + done(); + }); + }); + + it('width property should not work when width of style is auto', async () => { + let img = createElement('img', { + src: 'assets/300x150-green.png', + width: 100, + height: 100, + style: { + width: 'auto' + } + }); + BODY.appendChild(img); + + await snapshot(0.1); + }); + }); From 5ddf5da90cd0428ffa67d8885f0b4ff58799ec9d Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Mon, 6 Dec 2021 22:56:58 +0800 Subject: [PATCH 157/167] fix: nested fixed element paint order --- kraken/lib/src/rendering/box_model.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kraken/lib/src/rendering/box_model.dart b/kraken/lib/src/rendering/box_model.dart index a648cd747c..9cc5a90460 100644 --- a/kraken/lib/src/rendering/box_model.dart +++ b/kraken/lib/src/rendering/box_model.dart @@ -222,6 +222,15 @@ class RenderLayoutBox extends RenderBoxModel List children = getChildren(); if (_childrenNeedsSort) { children.sort((RenderBox left, RenderBox right) { + // @FIXME: Add patch to handle nested fixed element paint priority, need to remove + // this logic after Kraken has implemented stacking context tree. + if(left is RenderBoxModel && left.renderStyle.position == CSSPositionType.fixed && + right is RenderBoxModel && right.renderStyle.position == CSSPositionType.fixed) { + // Child element who has higher depth always paint after parent element who has lower depth + // in the renderObject tree. + return left.renderPositionPlaceholder!.depth - right.renderPositionPlaceholder!.depth; + } + bool isLeftNeedsStacking = left is RenderBoxModel && left.needsStacking; bool isRightNeedsStacking = right is RenderBoxModel && right.needsStacking; if (!isLeftNeedsStacking && isRightNeedsStacking) { From 94ec69767c73e7bd9cac63c022c306dbc4ff0128 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Tue, 7 Dec 2021 11:11:17 +0800 Subject: [PATCH 158/167] test: add test for nested fixed element --- .../css/css-position/fixed.ts.ad01a2041.png | Bin 0 -> 2380 bytes .../specs/css/css-position/fixed.ts | 31 ++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 integration_tests/snapshots/css/css-position/fixed.ts.ad01a2041.png diff --git a/integration_tests/snapshots/css/css-position/fixed.ts.ad01a2041.png b/integration_tests/snapshots/css/css-position/fixed.ts.ad01a2041.png new file mode 100644 index 0000000000000000000000000000000000000000..648790c3777dbde21092040b0fd93d3d2dba9cd0 GIT binary patch literal 2380 zcmeAS@N?(olHy`uVBq!ia0y~yV9a1(U~1rC1Bz^vdbAu!F%}28J29*~C-V}>VJUX< z4B-HR8jh3>1_q9Ao-U3d6?5KR+sJ#^frrJ>tKfdv^Y=Fzit-GC530s|n3aBRLgBvK z+&`Y%+$hfHXLvBzf!T%A#9_)PMb#jOV literal 0 HcmV?d00001 diff --git a/integration_tests/specs/css/css-position/fixed.ts b/integration_tests/specs/css/css-position/fixed.ts index 4448c75e46..02d9c49767 100644 --- a/integration_tests/specs/css/css-position/fixed.ts +++ b/integration_tests/specs/css/css-position/fixed.ts @@ -287,4 +287,35 @@ describe('Position fixed', () => { cont.style.position = 'static'; await snapshot(); }); + + it('should work with parent zIndex of parent fixed element larger than zIndex of child fixed element', async () => { + let div; + div = createElement( + 'div', + { + style: { + position: 'fixed', + width: '200px', + height: '200px', + background: 'yellow', + zIndex: 1000, + }, + }, + [ + createElement('div', { + style: { + position: 'fixed', + width: '100px', + height: '100px', + 'background-color': 'green', + zIndex: 100, + }, + }), + ] + ); + + document.body.appendChild(div); + + await snapshot(); + }); }); From 45bc24b85a4ded23d35ea1a123ff66cc0565271f Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Tue, 7 Dec 2021 11:13:03 +0800 Subject: [PATCH 159/167] chore: move const location --- kraken/lib/src/css/keywords.dart | 2 -- kraken/lib/src/dom/elements/img.dart | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kraken/lib/src/css/keywords.dart b/kraken/lib/src/css/keywords.dart index e608dace69..9135d8fc28 100644 --- a/kraken/lib/src/css/keywords.dart +++ b/kraken/lib/src/css/keywords.dart @@ -19,8 +19,6 @@ const String MIN_HEIGHT = 'minHeight'; const String MAX_HEIGHT = 'maxHeight'; const String MIN_WIDTH = 'minWidth'; const String MAX_WIDTH = 'maxWidth'; -const String NATURAL_WIDTH = 'naturalWidth'; -const String NATURAL_HEIGHT = 'naturalHeight'; const String OVERFLOW = 'overflow'; const String OVERFLOW_X = 'overflowX'; diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 2310df1519..c37c66c5f6 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -15,6 +15,8 @@ import 'package:kraken/dom.dart'; import 'package:kraken/rendering.dart'; const String IMAGE = 'IMG'; +const String NATURAL_WIDTH = 'naturalWidth'; +const String NATURAL_HEIGHT = 'naturalHeight'; // FIXME: should be inline default. const Map _defaultStyle = { From 30c272df6d8679739694120d5f8ef43b3e8fcd18 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 7 Dec 2021 14:21:05 +0800 Subject: [PATCH 160/167] fix: fix image naturalSize when same url. --- kraken/lib/src/dom/elements/img.dart | 80 ++++++++++++------- .../src/painting/image_provider_factory.dart | 26 ++++-- 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 9533709fb7..b420bb9200 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -26,17 +26,18 @@ const Map _defaultStyle = { class ImageElement extends Element { // The render box to draw image. RenderImage? _renderImage; - ImageProvider? _imageProvider; - ImageStream? _imageStream; + ImageProvider? _cachedImageProvider; + dynamic _imageProviderKey; - ImageInfo? _imageInfo; + ImageStream? _cachedImageStream; + ImageInfo? _cachedImageInfo; Uri? _resolvedUri; double? _propertyWidth; double? _propertyHeight; - ui.Image? get image => _imageInfo?.image; + ui.Image? get image => _cachedImageInfo?.image; /// Number of image frame, used to identify multi frame image after loaded. int _frameCount = 0; @@ -113,7 +114,7 @@ class ImageElement extends Element { if (_isListeningStream) return; - _imageStream?.addListener(_getListener()); + _cachedImageStream?.addListener(_getListener()); _completerHandle?.dispose(); _completerHandle = null; @@ -126,8 +127,9 @@ class ImageElement extends Element { _stopListeningStream(); _completerHandle?.dispose(); _replaceImage(info: null); - _imageProvider?.evict(); - _imageProvider = null; + _cachedImageProvider?.evict(); + _cachedImageProvider = null; + _imageProviderKey = null; } double get width { @@ -157,16 +159,22 @@ class ImageElement extends Element { } double get naturalWidth { - ImageProvider? imageProvider = _imageProvider; + ImageProvider? imageProvider = _cachedImageProvider; if (imageProvider is KrakenResizeImage) { - return imageProvider.naturalWidth.toDouble(); + Size? size = KrakenResizeImage.getImageNaturalSize(_imageProviderKey); + if (size != null) { + return size.width; + } } return image?.width.toDouble() ?? 0; } double get naturalHeight { - ImageProvider? imageProvider = _imageProvider; + ImageProvider? imageProvider = _cachedImageProvider; if (imageProvider is KrakenResizeImage) { - return imageProvider.naturalHeight.toDouble(); + Size? size = KrakenResizeImage.getImageNaturalSize(_imageProviderKey); + if (size != null) { + return size.height; + } } return image?.height.toDouble() ?? 0; } @@ -263,7 +271,7 @@ class ImageElement extends Element { Alignment objectPosition = renderStyle.objectPosition; return RenderImage( - image: _imageInfo?.image, + image: _cachedImageInfo?.image, fit: objectFit, alignment: objectPosition, ); @@ -274,7 +282,7 @@ class ImageElement extends Element { super.removeProperty(key); if (key == 'src') { _stopListeningStream(keepStreamAlive: true); - } else if (key == 'loading' && _isInLazyLoading && _imageProvider == null) { + } else if (key == 'loading' && _isInLazyLoading && _cachedImageProvider == null) { _resetLazyLoading(); _stopListeningStream(keepStreamAlive: true); } @@ -291,11 +299,11 @@ class ImageElement extends Element { if (!_isListeningStream) return; - if (keepStreamAlive && _completerHandle == null && _imageStream?.completer != null) { - _completerHandle = _imageStream!.completer!.keepAlive(); + if (keepStreamAlive && _completerHandle == null && _cachedImageStream?.completer != null) { + _completerHandle = _cachedImageStream!.completer!.keepAlive(); } - _imageStream?.removeListener(_getListener()); + _cachedImageStream?.removeListener(_getListener()); _isListeningStream = false; } @@ -309,23 +317,27 @@ class ImageElement extends Element { } void _updateSourceStream(ImageStream newStream) { - if (_imageStream?.key == newStream.key) return; + if (_cachedImageStream?.key == newStream.key) return; if (_isListeningStream) { - _imageStream?.removeListener(_getListener()); + _cachedImageStream?.removeListener(_getListener()); } _frameCount = 0; - _imageStream = newStream; + _cachedImageStream = newStream; if (_isListeningStream) { - _imageStream!.addListener(_getListener()); + _cachedImageStream!.addListener(_getListener()); } } - void _resolveImage(Uri? resolvedUri, { bool updateImageProvider = false }) { + // Obtain image resource from resolvedUri, and create an ImageStream that loads the image streams. + // If imageElement has propertySize or width,height properties on renderStyle, + // The image will be encoded into a small size for better rasterization performance. + void _resolveImage(Uri? resolvedUri, { bool updateImageProvider = false }) async { if (resolvedUri == null) return; + // Try to make sure that this image can be encoded into a smaller size. double? width = null; double? height = null; @@ -340,26 +352,34 @@ class ImageElement extends Element { int? cachedWidth = (width != null && width > 0) ? (width * ui.window.devicePixelRatio).toInt() : null; int? cachedHeight = (height != null && height > 0) ? (height * ui.window.devicePixelRatio).toInt() : null; - ImageProvider? provider = _imageProvider; + ImageProvider? provider = _cachedImageProvider; if (updateImageProvider || provider == null) { - provider = _imageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); + // When cachedWidth or cachedHeight is not null, KrakenResizeImage will be returned. + provider = _cachedImageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); } if (provider == null) return; + + // Cached the key of imageProvider to read naturalSize of the image. + _imageProviderKey = await provider.obtainKey(ImageConfiguration.empty); final ImageStream newStream = provider.resolve(ImageConfiguration.empty); _updateSourceStream(newStream); } void _replaceImage({required ImageInfo? info}) { - _imageInfo = info; + _cachedImageInfo = info; } + // Attach image to renderImage box. void _attachImage() { assert(isRendererAttached); assert(_renderImage != null); - if (_imageInfo == null) return; + if (_cachedImageInfo == null) return; _renderImage!.image = image?.clone(); } + + // Callback when image are loaded, encoded and available to use. + // This callback may fire multiple times when image have multiple frames (such as an animated GIF). void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) { _replaceImage(info: imageInfo); _frameCount++; @@ -374,7 +394,7 @@ class ImageElement extends Element { } } - // Multi frame image should convert to repaint boundary. + // Multi frame image should wrap a repaint boundary for better composite performance. if (_frameCount > 2) { forceToRepaintBoundary = true; } @@ -383,13 +403,15 @@ class ImageElement extends Element { _resizeImage(); } - // Prefetches an image into the image cache. - void _precacheImage() { + // Prefetches an image into the image cache. When the imageElement is attached to the renderTree, the imageProvider can directly + // obtain the cached imageStream from imageCache instead of obtaining resources from I/O. + void _precacheImage() async { final ImageConfiguration config = ImageConfiguration.empty; final Uri? resolvedUri = _resolvedUri = _resolveSrc(); if (resolvedUri == null) return; - final ImageProvider? provider = _imageProvider = getImageProvider(resolvedUri); + final ImageProvider? provider = _cachedImageProvider = getImageProvider(resolvedUri); if (provider == null) return; + _imageProviderKey = await provider.obtainKey(ImageConfiguration.empty); _frameCount = 0; final ImageStream stream = provider.resolve(config); ImageStreamListener? listener; diff --git a/kraken/lib/src/painting/image_provider_factory.dart b/kraken/lib/src/painting/image_provider_factory.dart index 2cd58e1881..833833475e 100644 --- a/kraken/lib/src/painting/image_provider_factory.dart +++ b/kraken/lib/src/painting/image_provider_factory.dart @@ -12,6 +12,7 @@ import 'package:flutter/painting.dart'; import 'package:kraken/bridge.dart'; import 'package:kraken/foundation.dart'; import 'package:kraken/painting.dart'; +import 'package:quiver/collection.dart'; /// This class allows user to customize Kraken's image loading. @@ -215,6 +216,11 @@ class KrakenResizeImage extends ResizeImage { int? height, }) : super(imageProvider, width: width, height: height); + static final LinkedLruHashMap _imageNaturalSize = LinkedLruHashMap(maximumSize: 100); + static Size? getImageNaturalSize(dynamic key) { + return _imageNaturalSize[key]; + } + static ImageProvider resizeIfNeeded( int? cacheWidth, int? cacheHeight, ImageProvider provider) { if (cacheWidth != null || cacheHeight != null) { @@ -224,12 +230,8 @@ class KrakenResizeImage extends ResizeImage { return provider; } - int naturalWidth = 0; - int naturalHeight = 0; - @override - void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, - key, ImageErrorListener handleError) { + void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, key, ImageErrorListener handleError) { // This is an unusual edge case where someone has told us that they found // the image we want before getting to this method. We should avoid calling // load again, but still update the image cache with LRU information. @@ -274,14 +276,24 @@ class KrakenResizeImage extends ResizeImage { } } - naturalWidth = descriptor.width; - naturalHeight = descriptor.height; + // Cache the image's original size for element.naturalWidth and element.naturalHeight API. + dynamic key = await imageProvider.obtainKey(ImageConfiguration.empty); + _imageNaturalSize[key] = Size(descriptor.width.toDouble(), descriptor.height.toDouble()); return descriptor.instantiateCodec( targetWidth: cacheWidth, targetHeight: cacheHeight, ); } + + @override + Future evict({ImageCache? cache, ImageConfiguration configuration = ImageConfiguration.empty}) async { + Future result = super.evict(cache: cache, configuration: configuration); + // Clear _imageNaturalSize when imageProvider evicted. + final key = await obtainKey(configuration); + _imageNaturalSize.remove(key); + return result; + } } /// default ImageProviderFactory implementation of [ImageType.cached] From 0738c7f4fe067894995f0af9c90ea98cd8368e37 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 7 Dec 2021 14:21:16 +0800 Subject: [PATCH 161/167] test: add test spec. --- .../dom/elements/img.ts.ff2142cd1.png | Bin 0 -> 32989 bytes integration_tests/specs/dom/elements/img.ts | 39 ++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 integration_tests/snapshots/dom/elements/img.ts.ff2142cd1.png diff --git a/integration_tests/snapshots/dom/elements/img.ts.ff2142cd1.png b/integration_tests/snapshots/dom/elements/img.ts.ff2142cd1.png new file mode 100644 index 0000000000000000000000000000000000000000..c4c46f1f06fb15f695439a3074392a7aaaecb34b GIT binary patch literal 32989 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 literal 0 HcmV?d00001 diff --git a/integration_tests/specs/dom/elements/img.ts b/integration_tests/specs/dom/elements/img.ts index c70e993131..04668d8e2c 100644 --- a/integration_tests/specs/dom/elements/img.ts +++ b/integration_tests/specs/dom/elements/img.ts @@ -323,4 +323,43 @@ describe('Tags img', () => { document.body.appendChild(img); img.src = imageURL; }); + + it('can get natualSize from repeat image url', async (done) => { + const flutterContainer = document.createElement('div'); + flutterContainer.style.height = '100vh'; + flutterContainer.style.display = 'block'; + document.body.appendChild(flutterContainer); + + const colors = ['red', 'yellow', 'black', 'blue', 'green']; + const images = [ + 'https://img.alicdn.com/imgextra/i1/O1CN01OPE9Pw1yFUVfLc82O_!!6000000006549-2-tps-325-289.png', + 'https://img.alicdn.com/imgextra/i4/O1CN01Edx8Jk1ELTLfISD7H_!!6000000000335-2-tps-1000-1000.png', + 'https://img.alicdn.com/imgextra/i3/O1CN01KgGBlg1Y2xfB9wnnJ_!!6000000003002-2-tps-325-289.png', + ]; + + let loadedCount = 0; + let imgCount = 10; + + for (let i = 0; i < imgCount; i++) { + const div = document.createElement('div'); + div.style.width = '100px'; + div.style.height = '100px'; + div.style.border = `3px solid ${colors[i % colors.length]}` + div.appendChild(document.createTextNode(i)); + + const img = document.createElement('img'); + img.src = images[i % images.length]; + div.appendChild(img); + img.style.width = '100px'; + img.onload = async () => { + loadedCount++; + if (loadedCount == imgCount) { + await snapshot(); + done(); + } + }; + + flutterContainer.appendChild(div); + } + }); }); From db485ec9c3fdb3a01625dfb7c376b9bbf39f49c8 Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 7 Dec 2021 14:49:00 +0800 Subject: [PATCH 162/167] fix: fix imageProvider key. --- kraken/lib/src/painting/image_provider_factory.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/src/painting/image_provider_factory.dart b/kraken/lib/src/painting/image_provider_factory.dart index 833833475e..9755e5d8c6 100644 --- a/kraken/lib/src/painting/image_provider_factory.dart +++ b/kraken/lib/src/painting/image_provider_factory.dart @@ -277,7 +277,7 @@ class KrakenResizeImage extends ResizeImage { } // Cache the image's original size for element.naturalWidth and element.naturalHeight API. - dynamic key = await imageProvider.obtainKey(ImageConfiguration.empty); + dynamic key = await obtainKey(ImageConfiguration.empty); _imageNaturalSize[key] = Size(descriptor.width.toDouble(), descriptor.height.toDouble()); return descriptor.instantiateCodec( From 8a51db0d0b108a9384c69459203cb51fdf4d6efc Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 7 Dec 2021 15:39:25 +0800 Subject: [PATCH 163/167] test: update spec images. --- .../dom/elements/img.ts.ff2142cd1.png | Bin 32989 -> 4616 bytes integration_tests/specs/dom/elements/img.ts | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/integration_tests/snapshots/dom/elements/img.ts.ff2142cd1.png b/integration_tests/snapshots/dom/elements/img.ts.ff2142cd1.png index c4c46f1f06fb15f695439a3074392a7aaaecb34b..af50889cc5f8f9d78aec1c456a31841a1b3c0d67 100644 GIT binary patch literal 4616 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 literal 32989 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 diff --git a/integration_tests/specs/dom/elements/img.ts b/integration_tests/specs/dom/elements/img.ts index 04668d8e2c..dbeb08ebe3 100644 --- a/integration_tests/specs/dom/elements/img.ts +++ b/integration_tests/specs/dom/elements/img.ts @@ -332,9 +332,9 @@ describe('Tags img', () => { const colors = ['red', 'yellow', 'black', 'blue', 'green']; const images = [ - 'https://img.alicdn.com/imgextra/i1/O1CN01OPE9Pw1yFUVfLc82O_!!6000000006549-2-tps-325-289.png', - 'https://img.alicdn.com/imgextra/i4/O1CN01Edx8Jk1ELTLfISD7H_!!6000000000335-2-tps-1000-1000.png', - 'https://img.alicdn.com/imgextra/i3/O1CN01KgGBlg1Y2xfB9wnnJ_!!6000000003002-2-tps-325-289.png', + 'assets/100x100-green.png', + 'assets/200x200-green.png', + 'assets/60x60-gg-rr.png', ]; let loadedCount = 0; @@ -350,7 +350,7 @@ describe('Tags img', () => { const img = document.createElement('img'); img.src = images[i % images.length]; div.appendChild(img); - img.style.width = '100px'; + img.style.width = '80px'; img.onload = async () => { loadedCount++; if (loadedCount == imgCount) { From 15c28635702816741407e260ce03ffa1546ec21e Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Tue, 7 Dec 2021 16:11:31 +0800 Subject: [PATCH 164/167] fix: resize image logic --- kraken/lib/src/dom/elements/img.dart | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index d7f13e68fd..ed98013df8 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -314,19 +314,8 @@ class ImageElement extends Element { void _resolveImage(Uri? resolvedUri, { bool updateImageProvider = false }) { if (resolvedUri == null) return; - double? width = null; - double? height = null; - - if (isRendererAttached) { - width = renderStyle.width.isAuto ? _propertyWidth : renderStyle.width.computedValue; - height = renderStyle.height.isAuto ? _propertyHeight : renderStyle.height.computedValue; - } else { - width = _propertyWidth; - height = _propertyHeight; - } - - int? cachedWidth = (width != null && width > 0) ? (width * ui.window.devicePixelRatio).toInt() : null; - int? cachedHeight = (height != null && height > 0) ? (height * ui.window.devicePixelRatio).toInt() : null; + int? cachedWidth = width > 0 ? (width * ui.window.devicePixelRatio).toInt() : null; + int? cachedHeight = height > 0 ? (height * ui.window.devicePixelRatio).toInt() : null; ImageProvider? provider = _imageProvider; if (updateImageProvider || provider == null) { @@ -452,8 +441,6 @@ class ImageElement extends Element { _styleHeight = renderStyle.height.value == null && renderStyle.height.isNotAuto ? null : renderStyle.height.computedValue; } - // Resize renderBox - if (isRendererAttached) _resizeImage(); // Resize image _resolveImage(_resolvedUri, updateImageProvider: true); } else if (property == OBJECT_FIT && _renderImage != null) { From e28f68a863137bfa7fb37a483bdcd27cccc8b01b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=A4=A9=E6=88=90?= Date: Tue, 7 Dec 2021 17:58:15 +0800 Subject: [PATCH 165/167] Fix: image natural size with same url. (#948) * fix: fix image naturalSize when same url. * test: add test spec. * fix: fix imageProvider key. * test: update spec images. --- .../dom/elements/img.ts.ff2142cd1.png | Bin 0 -> 4616 bytes integration_tests/specs/dom/elements/img.ts | 39 +++++++++ kraken/lib/src/dom/elements/img.dart | 80 +++++++++++------- .../src/painting/image_provider_factory.dart | 26 ++++-- 4 files changed, 109 insertions(+), 36 deletions(-) create mode 100644 integration_tests/snapshots/dom/elements/img.ts.ff2142cd1.png diff --git a/integration_tests/snapshots/dom/elements/img.ts.ff2142cd1.png b/integration_tests/snapshots/dom/elements/img.ts.ff2142cd1.png new file mode 100644 index 0000000000000000000000000000000000000000..af50889cc5f8f9d78aec1c456a31841a1b3c0d67 GIT binary patch literal 4616 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 literal 0 HcmV?d00001 diff --git a/integration_tests/specs/dom/elements/img.ts b/integration_tests/specs/dom/elements/img.ts index c70e993131..dbeb08ebe3 100644 --- a/integration_tests/specs/dom/elements/img.ts +++ b/integration_tests/specs/dom/elements/img.ts @@ -323,4 +323,43 @@ describe('Tags img', () => { document.body.appendChild(img); img.src = imageURL; }); + + it('can get natualSize from repeat image url', async (done) => { + const flutterContainer = document.createElement('div'); + flutterContainer.style.height = '100vh'; + flutterContainer.style.display = 'block'; + document.body.appendChild(flutterContainer); + + const colors = ['red', 'yellow', 'black', 'blue', 'green']; + const images = [ + 'assets/100x100-green.png', + 'assets/200x200-green.png', + 'assets/60x60-gg-rr.png', + ]; + + let loadedCount = 0; + let imgCount = 10; + + for (let i = 0; i < imgCount; i++) { + const div = document.createElement('div'); + div.style.width = '100px'; + div.style.height = '100px'; + div.style.border = `3px solid ${colors[i % colors.length]}` + div.appendChild(document.createTextNode(i)); + + const img = document.createElement('img'); + img.src = images[i % images.length]; + div.appendChild(img); + img.style.width = '80px'; + img.onload = async () => { + loadedCount++; + if (loadedCount == imgCount) { + await snapshot(); + done(); + } + }; + + flutterContainer.appendChild(div); + } + }); }); diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 427461278a..f2c2f2114a 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -26,17 +26,18 @@ const Map _defaultStyle = { class ImageElement extends Element { // The render box to draw image. RenderImage? _renderImage; - ImageProvider? _imageProvider; - ImageStream? _imageStream; + ImageProvider? _cachedImageProvider; + dynamic _imageProviderKey; - ImageInfo? _imageInfo; + ImageStream? _cachedImageStream; + ImageInfo? _cachedImageInfo; Uri? _resolvedUri; double? _propertyWidth; double? _propertyHeight; - ui.Image? get image => _imageInfo?.image; + ui.Image? get image => _cachedImageInfo?.image; /// Number of image frame, used to identify multi frame image after loaded. int _frameCount = 0; @@ -113,7 +114,7 @@ class ImageElement extends Element { if (_isListeningStream) return; - _imageStream?.addListener(_getListener()); + _cachedImageStream?.addListener(_getListener()); _completerHandle?.dispose(); _completerHandle = null; @@ -126,8 +127,9 @@ class ImageElement extends Element { _stopListeningStream(); _completerHandle?.dispose(); _replaceImage(info: null); - _imageProvider?.evict(); - _imageProvider = null; + _cachedImageProvider?.evict(); + _cachedImageProvider = null; + _imageProviderKey = null; } double get width { @@ -157,16 +159,22 @@ class ImageElement extends Element { } double get naturalWidth { - ImageProvider? imageProvider = _imageProvider; + ImageProvider? imageProvider = _cachedImageProvider; if (imageProvider is KrakenResizeImage) { - return imageProvider.naturalWidth.toDouble(); + Size? size = KrakenResizeImage.getImageNaturalSize(_imageProviderKey); + if (size != null) { + return size.width; + } } return image?.width.toDouble() ?? 0; } double get naturalHeight { - ImageProvider? imageProvider = _imageProvider; + ImageProvider? imageProvider = _cachedImageProvider; if (imageProvider is KrakenResizeImage) { - return imageProvider.naturalHeight.toDouble(); + Size? size = KrakenResizeImage.getImageNaturalSize(_imageProviderKey); + if (size != null) { + return size.height; + } } return image?.height.toDouble() ?? 0; } @@ -263,7 +271,7 @@ class ImageElement extends Element { Alignment objectPosition = renderStyle.objectPosition; return RenderImage( - image: _imageInfo?.image, + image: _cachedImageInfo?.image, fit: objectFit, alignment: objectPosition, ); @@ -274,7 +282,7 @@ class ImageElement extends Element { super.removeProperty(key); if (key == 'src') { _stopListeningStream(keepStreamAlive: true); - } else if (key == 'loading' && _isInLazyLoading && _imageProvider == null) { + } else if (key == 'loading' && _isInLazyLoading && _cachedImageProvider == null) { _resetLazyLoading(); _stopListeningStream(keepStreamAlive: true); } @@ -291,11 +299,11 @@ class ImageElement extends Element { if (!_isListeningStream) return; - if (keepStreamAlive && _completerHandle == null && _imageStream?.completer != null) { - _completerHandle = _imageStream!.completer!.keepAlive(); + if (keepStreamAlive && _completerHandle == null && _cachedImageStream?.completer != null) { + _completerHandle = _cachedImageStream!.completer!.keepAlive(); } - _imageStream?.removeListener(_getListener()); + _cachedImageStream?.removeListener(_getListener()); _isListeningStream = false; } @@ -309,23 +317,27 @@ class ImageElement extends Element { } void _updateSourceStream(ImageStream newStream) { - if (_imageStream?.key == newStream.key) return; + if (_cachedImageStream?.key == newStream.key) return; if (_isListeningStream) { - _imageStream?.removeListener(_getListener()); + _cachedImageStream?.removeListener(_getListener()); } _frameCount = 0; - _imageStream = newStream; + _cachedImageStream = newStream; if (_isListeningStream) { - _imageStream!.addListener(_getListener()); + _cachedImageStream!.addListener(_getListener()); } } - void _resolveImage(Uri? resolvedUri, { bool updateImageProvider = false }) { + // Obtain image resource from resolvedUri, and create an ImageStream that loads the image streams. + // If imageElement has propertySize or width,height properties on renderStyle, + // The image will be encoded into a small size for better rasterization performance. + void _resolveImage(Uri? resolvedUri, { bool updateImageProvider = false }) async { if (resolvedUri == null) return; + // Try to make sure that this image can be encoded into a smaller size. double? width = null; double? height = null; @@ -340,26 +352,34 @@ class ImageElement extends Element { int? cachedWidth = (width != null && width > 0) ? (width * ui.window.devicePixelRatio).toInt() : null; int? cachedHeight = (height != null && height > 0) ? (height * ui.window.devicePixelRatio).toInt() : null; - ImageProvider? provider = _imageProvider; + ImageProvider? provider = _cachedImageProvider; if (updateImageProvider || provider == null) { - provider = _imageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); + // When cachedWidth or cachedHeight is not null, KrakenResizeImage will be returned. + provider = _cachedImageProvider = getImageProvider(resolvedUri, cachedWidth: cachedWidth, cachedHeight: cachedHeight); } if (provider == null) return; + + // Cached the key of imageProvider to read naturalSize of the image. + _imageProviderKey = await provider.obtainKey(ImageConfiguration.empty); final ImageStream newStream = provider.resolve(ImageConfiguration.empty); _updateSourceStream(newStream); } void _replaceImage({required ImageInfo? info}) { - _imageInfo = info; + _cachedImageInfo = info; } + // Attach image to renderImage box. void _attachImage() { assert(isRendererAttached); assert(_renderImage != null); - if (_imageInfo == null) return; + if (_cachedImageInfo == null) return; _renderImage!.image = image?.clone(); } + + // Callback when image are loaded, encoded and available to use. + // This callback may fire multiple times when image have multiple frames (such as an animated GIF). void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) { _replaceImage(info: imageInfo); _frameCount++; @@ -374,7 +394,7 @@ class ImageElement extends Element { } } - // Multi frame image should convert to repaint boundary. + // Multi frame image should wrap a repaint boundary for better composite performance. if (_frameCount > 2) { forceToRepaintBoundary = true; } @@ -383,13 +403,15 @@ class ImageElement extends Element { _resizeImage(); } - // Prefetches an image into the image cache. - void _precacheImage() { + // Prefetches an image into the image cache. When the imageElement is attached to the renderTree, the imageProvider can directly + // obtain the cached imageStream from imageCache instead of obtaining resources from I/O. + void _precacheImage() async { final ImageConfiguration config = ImageConfiguration.empty; final Uri? resolvedUri = _resolvedUri = _resolveSrc(); if (resolvedUri == null) return; - final ImageProvider? provider = _imageProvider = getImageProvider(resolvedUri); + final ImageProvider? provider = _cachedImageProvider = getImageProvider(resolvedUri); if (provider == null) return; + _imageProviderKey = await provider.obtainKey(ImageConfiguration.empty); _frameCount = 0; final ImageStream stream = provider.resolve(config); ImageStreamListener? listener; diff --git a/kraken/lib/src/painting/image_provider_factory.dart b/kraken/lib/src/painting/image_provider_factory.dart index 2cd58e1881..9755e5d8c6 100644 --- a/kraken/lib/src/painting/image_provider_factory.dart +++ b/kraken/lib/src/painting/image_provider_factory.dart @@ -12,6 +12,7 @@ import 'package:flutter/painting.dart'; import 'package:kraken/bridge.dart'; import 'package:kraken/foundation.dart'; import 'package:kraken/painting.dart'; +import 'package:quiver/collection.dart'; /// This class allows user to customize Kraken's image loading. @@ -215,6 +216,11 @@ class KrakenResizeImage extends ResizeImage { int? height, }) : super(imageProvider, width: width, height: height); + static final LinkedLruHashMap _imageNaturalSize = LinkedLruHashMap(maximumSize: 100); + static Size? getImageNaturalSize(dynamic key) { + return _imageNaturalSize[key]; + } + static ImageProvider resizeIfNeeded( int? cacheWidth, int? cacheHeight, ImageProvider provider) { if (cacheWidth != null || cacheHeight != null) { @@ -224,12 +230,8 @@ class KrakenResizeImage extends ResizeImage { return provider; } - int naturalWidth = 0; - int naturalHeight = 0; - @override - void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, - key, ImageErrorListener handleError) { + void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, key, ImageErrorListener handleError) { // This is an unusual edge case where someone has told us that they found // the image we want before getting to this method. We should avoid calling // load again, but still update the image cache with LRU information. @@ -274,14 +276,24 @@ class KrakenResizeImage extends ResizeImage { } } - naturalWidth = descriptor.width; - naturalHeight = descriptor.height; + // Cache the image's original size for element.naturalWidth and element.naturalHeight API. + dynamic key = await obtainKey(ImageConfiguration.empty); + _imageNaturalSize[key] = Size(descriptor.width.toDouble(), descriptor.height.toDouble()); return descriptor.instantiateCodec( targetWidth: cacheWidth, targetHeight: cacheHeight, ); } + + @override + Future evict({ImageCache? cache, ImageConfiguration configuration = ImageConfiguration.empty}) async { + Future result = super.evict(cache: cache, configuration: configuration); + // Clear _imageNaturalSize when imageProvider evicted. + final key = await obtainKey(configuration); + _imageNaturalSize.remove(key); + return result; + } } /// default ImageProviderFactory implementation of [ImageType.cached] From bddb41856c77fb2006f0b0c5bcbd0d4711c131f4 Mon Sep 17 00:00:00 2001 From: "zhanwen.zw" Date: Wed, 8 Dec 2021 22:47:10 +0800 Subject: [PATCH 166/167] fix: intersection observer cache --- kraken/lib/src/rendering/intersection_observer.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/src/rendering/intersection_observer.dart b/kraken/lib/src/rendering/intersection_observer.dart index a363d5b365..da5c9848c1 100644 --- a/kraken/lib/src/rendering/intersection_observer.dart +++ b/kraken/lib/src/rendering/intersection_observer.dart @@ -171,7 +171,7 @@ class IntersectionObserverLayer extends ContainerLayer { Matrix4? cachedTransform = _layerTransformCache[child.hashCode]; // Get the transform of parent layer to root layer directly if exists. if (cachedTransform != null) { - transform = cachedTransform; + transform = cachedTransform.clone(); } else { (parent as ContainerLayer).applyTransform(child, transform); // Cache the transform of parent layer to root layer. From fdc1f628ea6847d95f5801d47b1ffba7ae0630c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=83=E5=BD=A6?= Date: Thu, 9 Dec 2021 15:46:41 +0800 Subject: [PATCH 167/167] refactor: remove element manager (#940) * refactor: remove element manager * chore: fix error * Committing clang-format changes * fix: reset dom * fix: lint * fix: bridge error * Committing clang-format changes * fix: document.body * fix: ref error * fix: reset html * fix: specs * chore: update plugin deps version * chore: export defineElement api * chore: change context is optional * chore: export comment node * chore: update deps plugins * chore: remove performance overlay * fix: img snapshot Co-authored-by: openkraken-bot --- bridge/bindings/qjs/dom/document.cc | 179 +++++- bridge/bindings/qjs/dom/document.h | 4 +- bridge/bindings/qjs/dom/element.cc | 13 +- bridge/bindings/qjs/dom/event_target.cc | 1 + bridge/bindings/qjs/dom/node.cc | 9 +- bridge/bindings/qjs/module_manager_test.cc | 9 +- bridge/dart_methods.cc | 1 - bridge/include/dart_methods.h | 2 - bridge/include/kraken_foundation.h | 5 +- bridge/polyfill/src/dom.ts | 31 +- bridge/polyfill/src/test/index.js | 13 +- .../lib/custom/custom_element.dart | 26 +- integration_tests/lib/main.dart | 2 +- integration_tests/pubspec.yaml | 4 +- integration_tests/runtime/global.ts | 4 +- .../specs/css/css-flexbox/auto-margins.ts | 3 +- integration_tests/specs/css/css-values/rem.ts | 2 + .../specs/dom/events/touchEvent.ts | 4 +- kraken/lib/dom.dart | 4 +- kraken/lib/src/bridge/from_native.dart | 13 +- kraken/lib/src/bridge/to_native.dart | 2 +- kraken/lib/src/css/background.dart | 6 +- kraken/lib/src/css/border.dart | 6 +- kraken/lib/src/css/render_style.dart | 4 +- kraken/lib/src/dom/comment.dart | 22 + kraken/lib/src/dom/document.dart | 147 ++++- kraken/lib/src/dom/document_fragment.dart | 4 +- kraken/lib/src/dom/element.dart | 86 ++- ...{event_handler.dart => element_event.dart} | 4 +- kraken/lib/src/dom/element_manager.dart | 526 ------------------ kraken/lib/src/dom/element_registry.dart | 143 +++-- ..._native_methods.dart => element_view.dart} | 2 +- kraken/lib/src/dom/elements/a.dart | 14 +- kraken/lib/src/dom/elements/body.dart | 8 +- .../lib/src/dom/elements/canvas/canvas.dart | 8 +- .../elements/canvas/canvas_context_2d.dart | 2 +- kraken/lib/src/dom/elements/edits.dart | 12 +- kraken/lib/src/dom/elements/forms.dart | 12 +- .../src/dom/elements/grouping_content.dart | 51 +- kraken/lib/src/dom/elements/head.dart | 47 +- kraken/lib/src/dom/elements/headings.dart | 27 +- kraken/lib/src/dom/elements/html.dart | 50 +- kraken/lib/src/dom/elements/img.dart | 14 +- kraken/lib/src/dom/elements/input.dart | 18 +- kraken/lib/src/dom/elements/object.dart | 17 +- kraken/lib/src/dom/elements/sections.dart | 36 +- .../lib/src/dom/elements/semantics_text.dart | 84 ++- kraken/lib/src/dom/elements/template.dart | 8 +- kraken/lib/src/dom/event.dart | 4 +- kraken/lib/src/dom/event_target.dart | 73 ++- kraken/lib/src/dom/node.dart | 97 ++-- kraken/lib/src/dom/text_node.dart | 8 +- kraken/lib/src/dom/window.dart | 36 +- kraken/lib/src/launcher/bundle.dart | 9 +- kraken/lib/src/launcher/controller.dart | 487 ++++++++++++---- kraken/lib/src/launcher/launcher.dart | 2 +- kraken/lib/src/module/history.dart | 4 +- kraken/lib/widget.dart | 17 +- kraken/pubspec.yaml | 2 +- 59 files changed, 1139 insertions(+), 1289 deletions(-) create mode 100644 kraken/lib/src/dom/comment.dart rename kraken/lib/src/dom/{event_handler.dart => element_event.dart} (95%) delete mode 100644 kraken/lib/src/dom/element_manager.dart rename kraken/lib/src/dom/{element_native_methods.dart => element_view.dart} (99%) diff --git a/bridge/bindings/qjs/dom/document.cc b/bridge/bindings/qjs/dom/document.cc index f440c1ccdd..41eed2148a 100644 --- a/bridge/bindings/qjs/dom/document.cc +++ b/bridge/bindings/qjs/dom/document.cc @@ -179,15 +179,18 @@ JSValue Document::createTextNode(QjsContext* ctx, JSValue this_val, int argc, JS JSValue textNode = JS_CallConstructor(ctx, TextNode::instance(document->m_context)->classObject, argc, argv); return textNode; } + JSValue Document::createDocumentFragment(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); return JS_CallConstructor(ctx, DocumentFragment::instance(document->m_context)->classObject, 0, nullptr); } + JSValue Document::createComment(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); JSValue commentNode = JS_CallConstructor(ctx, Comment::instance(document->m_context)->classObject, argc, argv); return commentNode; } + JSValue Document::getElementById(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 1) { return JS_ThrowTypeError(ctx, "Uncaught TypeError: Failed to execute 'getElementById' on 'Document': 1 argument required, but only 0 present."); @@ -219,6 +222,7 @@ JSValue Document::getElementById(QjsContext* ctx, JSValue this_val, int argc, JS return JS_NULL; } + JSValue Document::getElementsByTagName(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 1) { return JS_ThrowTypeError(ctx, @@ -233,9 +237,9 @@ JSValue Document::getElementsByTagName(QjsContext* ctx, JSValue this_val, int ar std::vector elements; - traverseNode(document->m_documentElement, [tagName, &elements](NodeInstance* node) { + traverseNode(document, [tagName, &elements](NodeInstance* node) { if (node->nodeType == NodeType::ELEMENT_NODE) { - auto element = static_cast(node); + auto* element = static_cast(node); if (element->tagName() == tagName || tagName == "*") { elements.emplace_back(element); } @@ -264,7 +268,7 @@ JSValue Document::getElementsByClassName(QjsContext* ctx, JSValue this_val, int std::string className = jsValueToStdString(ctx, argv[0]); std::vector elements; - traverseNode(document->m_documentElement, [ctx, className, &elements](NodeInstance* node) { + traverseNode(document, [ctx, className, &elements](NodeInstance* node) { if (node->nodeType == NodeType::ELEMENT_NODE) { auto element = reinterpret_cast(node); if (element->classNames()->containsAll(className)) { @@ -303,21 +307,157 @@ bool Document::isCustomElement(const std::string& tagName) { PROP_GETTER(DocumentInstance, nodeName)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { return JS_NewString(ctx, "#document"); } + PROP_SETTER(DocumentInstance, nodeName)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { return JS_NULL; } +// document.documentElement +PROP_GETTER(DocumentInstance, documentElement)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + ElementInstance* documentElement = document->getDocumentElement(); + return documentElement == nullptr ? JS_NULL : documentElement->instanceObject; +} + +PROP_SETTER(DocumentInstance, documentElement)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { + return JS_NULL; +} + +// document.head +PROP_GETTER(DocumentInstance, head)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + ElementInstance* documentElement = document->getDocumentElement(); + int32_t len = arrayGetLength(ctx, documentElement->childNodes); + JSValue head = JS_NULL; + if (documentElement != nullptr) { + for (int i = 0; i < len; i++) { + JSValue v = JS_GetPropertyUint32(ctx, documentElement->childNodes, i); + auto* nodeInstance = static_cast(JS_GetOpaque(v, Node::classId(v))); + if (nodeInstance->nodeType == NodeType::ELEMENT_NODE) { + auto* elementInstance = static_cast(nodeInstance); + if (elementInstance->tagName() == "HEAD") { + head = elementInstance->instanceObject; + break; + } + } + JS_FreeValue(ctx, v); + } + + JS_FreeValue(ctx, documentElement->instanceObject); + } + + return head; +} + +PROP_SETTER(DocumentInstance, head)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { + return JS_NULL; +} + +// document.body: https://html.spec.whatwg.org/multipage/dom.html#dom-document-body-dev +PROP_GETTER(DocumentInstance, body)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + ElementInstance* documentElement = document->getDocumentElement(); + JSValue body = JS_NULL; + + if (documentElement != nullptr) { + int32_t len = arrayGetLength(ctx, documentElement->childNodes); + // The body element of a document is the first of the html documentElement's children that + // is either a body element or a frameset element, or null if there is no such element. + for (int i = 0; i < len; i++) { + JSValue v = JS_GetPropertyUint32(ctx, documentElement->childNodes, i); + auto* nodeInstance = static_cast(JS_GetOpaque(v, Node::classId(v))); + if (nodeInstance->nodeType == NodeType::ELEMENT_NODE) { + auto* elementInstance = static_cast(nodeInstance); + if (elementInstance->tagName() == "BODY") { + body = elementInstance->instanceObject; + break; + } + } + JS_FreeValue(ctx, v); + } + JS_FreeValue(ctx, documentElement->instanceObject); + } + return body; +} + +// The body property is settable, setting a new body on a document will effectively remove all +// the current children of the existing element. +PROP_SETTER(DocumentInstance, body)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + ElementInstance* documentElement = document->getDocumentElement(); + // If there is no document element, throw a Exception. + if (documentElement == nullptr) { + return JS_ThrowInternalError(ctx, "No document element exists"); + } + JSValue result = JS_NULL; + JSValue newBody = argv[0]; + // If the body element is not null, then replace the body element with the new value within the body element's parent and return. + if (JS_IsInstanceOf(ctx, newBody, Element::instance(document->m_context)->classObject)) { + auto* newElementInstance = static_cast(JS_GetOpaque(newBody, Element::classId())); + // If the new value is not a body element, then throw a Exception. + if (newElementInstance->tagName() == "BODY") { + JSValue oldBody = JS_GetPropertyStr(ctx, document->instanceObject, "body"); + if (JS_VALUE_GET_PTR(oldBody) != JS_VALUE_GET_PTR(newBody)) { + // If the new value is the same as the body element. + if (JS_IsNull(oldBody)) { + // The old body element is null, but there's a document element. Append the new value to the document element. + documentElement->internalAppendChild(newElementInstance); + } else { + // Otherwise, replace the body element with the new value within the body element's parent. + auto* oldElementInstance = static_cast(JS_GetOpaque(oldBody, Element::classId())); + documentElement->internalReplaceChild(newElementInstance, oldElementInstance); + } + } + JS_FreeValue(ctx, oldBody); + result = JS_DupValue(ctx, newBody); + } else { + result = JS_ThrowTypeError(ctx, "The new body element must be a 'BODY' element"); + } + } else { + result = JS_ThrowTypeError(ctx, "The 1st argument provided is either null, or an invalid HTMLElement"); + } + + JS_FreeValue(ctx, documentElement->instanceObject); + return result; +} + +// document.children +PROP_GETTER(DocumentInstance, children)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + JSValue array = JS_NewArray(ctx); + JSValue pushMethod = JS_GetPropertyStr(ctx, array, "push"); + + int32_t len = arrayGetLength(ctx, document->childNodes); + for (int i = 0; i < len; i++) { + JSValue v = JS_GetPropertyUint32(ctx, document->childNodes, i); + auto* instance = static_cast(JS_GetOpaque(v, Node::classId(v))); + if (instance->nodeType == NodeType::ELEMENT_NODE) { + JSValue arguments[] = {v}; + JS_Call(ctx, pushMethod, array, 1, arguments); + } + JS_FreeValue(ctx, v); + } + + JS_FreeValue(ctx, pushMethod); + return array; +} + +PROP_SETTER(DocumentInstance, children)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { + return JS_NULL; +} + PROP_GETTER(DocumentInstance, all)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); auto all = new AllCollection(document->m_context); - traverseNode(document->m_documentElement, [&all](NodeInstance* node) { + traverseNode(document, [&all](NodeInstance* node) { all->internalAdd(node, nullptr); return false; }); return all->jsObject; } + PROP_SETTER(DocumentInstance, all)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { return JS_NULL; } @@ -327,6 +467,7 @@ PROP_GETTER(DocumentInstance, cookie)(QjsContext* ctx, JSValue this_val, int arg std::string cookie = document->m_cookie->getCookie(); return JS_NewString(ctx, cookie.c_str()); } + PROP_SETTER(DocumentInstance, cookie)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); std::string value = jsValueToStdString(ctx, argv[0]); @@ -391,22 +532,7 @@ DocumentInstance::DocumentInstance(Document* document) : NodeInstance(document, m_instanceMap[Document::instance(m_context)] = this; m_eventTargetId = DOCUMENT_TARGET_ID; - JSAtom htmlTagName = JS_NewAtom(m_ctx, "HTML"); - JSValue htmlTagValue = JS_AtomToValue(m_ctx, htmlTagName); - JSValue htmlArgs[] = {htmlTagValue}; - JSValue documentElementValue = JS_CallConstructor(m_ctx, Element::instance(m_context)->classObject, 1, htmlArgs); - m_documentElement = static_cast(JS_GetOpaque(documentElementValue, Element::classId())); - m_documentElement->parentNode = JS_DupValue(m_ctx, instanceObject); - - JSAtom documentElementTag = JS_NewAtom(m_ctx, "documentElement"); - JS_SetProperty(m_ctx, instanceObject, documentElementTag, documentElementValue); - - JS_FreeAtom(m_ctx, documentElementTag); - JS_FreeAtom(m_ctx, htmlTagName); - JS_FreeValue(m_ctx, htmlTagValue); - #if FLUTTER_BACKEND - getDartMethod()->initHTML(m_context->getContextId(), m_documentElement->nativeEventTarget); getDartMethod()->initDocument(m_context->getContextId(), nativeEventTarget); #endif } @@ -437,8 +563,19 @@ void DocumentInstance::addElementById(JSAtom id, ElementInstance* element) { } } -ElementInstance* DocumentInstance::documentElement() { - return m_documentElement; +ElementInstance* DocumentInstance::getDocumentElement() { + int32_t len = arrayGetLength(m_ctx, childNodes); + + for (int i = 0; i < len; i++) { + JSValue v = JS_GetPropertyUint32(m_ctx, childNodes, i); + auto* instance = static_cast(JS_GetOpaque(v, Node::classId(v))); + if (instance->nodeType == NodeType::ELEMENT_NODE) { + return static_cast(instance); + } + JS_FreeValue(m_ctx, v); + } + + return nullptr; } } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/dom/document.h b/bridge/bindings/qjs/dom/document.h index d4243bb25b..1fb7af6fb7 100644 --- a/bridge/bindings/qjs/dom/document.h +++ b/bridge/bindings/qjs/dom/document.h @@ -77,7 +77,6 @@ class DocumentInstance : public NodeInstance { explicit DocumentInstance(Document* document); ~DocumentInstance(); static std::unordered_map m_instanceMap; - ElementInstance* documentElement(); static DocumentInstance* instance(Document* document) { if (m_instanceMap.count(document) == 0) { m_instanceMap[document] = new DocumentInstance(document); @@ -86,10 +85,11 @@ class DocumentInstance : public NodeInstance { } private: - DEFINE_HOST_CLASS_PROPERTY(3, nodeName, all, cookie); + DEFINE_HOST_CLASS_PROPERTY(7, nodeName, documentElement, body, head, children, all, cookie); void removeElementById(JSAtom id, ElementInstance* element); void addElementById(JSAtom id, ElementInstance* element); + ElementInstance* getDocumentElement(); std::unordered_map> m_elementMapById; ElementInstance* m_documentElement{nullptr}; std::unique_ptr m_cookie; diff --git a/bridge/bindings/qjs/dom/element.cc b/bridge/bindings/qjs/dom/element.cc index 5734b9f57c..4f0f3fda71 100644 --- a/bridge/bindings/qjs/dom/element.cc +++ b/bridge/bindings/qjs/dom/element.cc @@ -138,15 +138,7 @@ JSValue Element::instanceConstructor(QjsContext* ctx, JSValue func_obj, JSValue return JS_CallConstructor(ctx, Document->getElementConstructor(context, name), argc, argv); } - ElementInstance* element; - if (name == "HTML") { - element = new ElementInstance(this, name, false); - element->m_eventTargetId = HTML_TARGET_ID; - } else { - // Fallback to default Element class - element = new ElementInstance(this, name, true); - } - + auto* element = new ElementInstance(this, name, true); return element->instanceObject; } @@ -395,6 +387,7 @@ PROP_GETTER(ElementInstance, className)(QjsContext* ctx, JSValue this_val, int a JSAtom valueAtom = element->m_attributes->getAttribute("class"); return JS_AtomToString(ctx, valueAtom); } + PROP_SETTER(ElementInstance, className)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); JSAtom atom = JS_ValueToAtom(ctx, argv[0]); @@ -530,6 +523,7 @@ PROP_GETTER(ElementInstance, scrollWidth)(QjsContext* ctx, JSValue this_val, int NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::scrollWidth))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } + PROP_SETTER(ElementInstance, scrollWidth)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { return JS_NULL; } @@ -550,6 +544,7 @@ PROP_GETTER(ElementInstance, firstElementChild)(QjsContext* ctx, JSValue this_va return JS_NULL; } + PROP_SETTER(ElementInstance, firstElementChild)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { return JS_NULL; } diff --git a/bridge/bindings/qjs/dom/event_target.cc b/bridge/bindings/qjs/dom/event_target.cc index 381e7df65a..8a3d1d48ef 100644 --- a/bridge/bindings/qjs/dom/event_target.cc +++ b/bridge/bindings/qjs/dom/event_target.cc @@ -284,6 +284,7 @@ EventTargetInstance::~EventTargetInstance() { foundation::UICommandBuffer::instance(m_contextId)->addCommand(m_eventTargetId, UICommand::disposeEventTarget, nullptr, false); #if FLUTTER_BACKEND getDartMethod()->flushUICommand(); + delete nativeEventTarget; #endif } diff --git a/bridge/bindings/qjs/dom/node.cc b/bridge/bindings/qjs/dom/node.cc index 5d83973894..9a66df6aed 100644 --- a/bridge/bindings/qjs/dom/node.cc +++ b/bridge/bindings/qjs/dom/node.cc @@ -76,6 +76,7 @@ JSValue Node::cloneNode(QjsContext* ctx, JSValue this_val, int argc, JSValue* ar } return JS_NULL; } + JSValue Node::appendChild(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc != 1) { return JS_ThrowTypeError(ctx, "Failed to execute 'appendChild' on 'Node': first argument is required."); @@ -96,7 +97,7 @@ JSValue Node::appendChild(QjsContext* ctx, JSValue this_val, int argc, JSValue* return JS_ThrowTypeError(ctx, "Failed to execute 'appendChild' on 'Node': first arguments should be an Node type."); } - if (nodeInstance->m_eventTargetId == HTML_TARGET_ID || nodeInstance == selfInstance) { + if (nodeInstance == selfInstance) { return JS_ThrowTypeError(ctx, "Failed to execute 'appendChild' on 'Node': The new child element contains the parent."); } @@ -143,6 +144,7 @@ JSValue Node::removeChild(QjsContext* ctx, JSValue this_val, int argc, JSValue* auto removedNode = selfInstance->internalRemoveChild(nodeInstance); return JS_DupValue(ctx, removedNode->instanceObject); } + JSValue Node::insertBefore(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 2) { return JS_ThrowTypeError(ctx, "Failed to execute 'insertBefore' on 'Node': 2 arguments is required."); @@ -188,6 +190,7 @@ JSValue Node::insertBefore(QjsContext* ctx, JSValue this_val, int argc, JSValue* return JS_NULL; } + JSValue Node::replaceChild(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 2) { return JS_ThrowTypeError(ctx, "Uncaught TypeError: Failed to execute 'replaceChild' on 'Node': 2 arguments required"); @@ -369,11 +372,11 @@ PROP_SETTER(NodeInstance, textContent)(QjsContext* ctx, JSValue this_val, int ar } bool NodeInstance::isConnected() { - bool _isConnected = m_eventTargetId == HTML_TARGET_ID; + bool _isConnected = this == document(); auto parent = static_cast(JS_GetOpaque(parentNode, Node::classId(parentNode))); while (parent != nullptr && !_isConnected) { - _isConnected = parent->m_eventTargetId == HTML_TARGET_ID; + _isConnected = parent == document(); JSValue parentParentNode = parent->parentNode; parent = static_cast(JS_GetOpaque(parentParentNode, Node::classId(parentParentNode))); } diff --git a/bridge/bindings/qjs/module_manager_test.cc b/bridge/bindings/qjs/module_manager_test.cc index 394fb2bdde..52134ff0df 100644 --- a/bridge/bindings/qjs/module_manager_test.cc +++ b/bridge/bindings/qjs/module_manager_test.cc @@ -13,13 +13,8 @@ namespace kraken::binding::qjs { TEST(ModuleManager, shouldThrowErrorWhenBadJSON) { bool static errorCalled = false; auto* bridge = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) { - EXPECT_STREQ(errmsg, - "TypeError: circular reference\n" - " at __kraken_invoke_module__ (native)\n" - " at (internal://:616)\n" - " at Promise (native)\n" - " at invokeMethod (internal://:617)\n" - " at (vm://:12)\n"); + std::string stdErrorMsg = std::string(errmsg); + EXPECT_EQ(stdErrorMsg.find("TypeError: circular reference") != std::string::npos, true); errorCalled = true; }); kraken::JSBridge::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) {}; diff --git a/bridge/dart_methods.cc b/bridge/dart_methods.cc index 9bce4a036b..b815895f64 100644 --- a/bridge/dart_methods.cc +++ b/bridge/dart_methods.cc @@ -43,7 +43,6 @@ void registerDartMethods(uint64_t* methodBytes, int32_t length) { methodPointer->platformBrightness = reinterpret_cast(methodBytes[i++]); methodPointer->toBlob = reinterpret_cast(methodBytes[i++]); methodPointer->flushUICommand = reinterpret_cast(methodBytes[i++]); - methodPointer->initHTML = reinterpret_cast(methodBytes[i++]); methodPointer->initWindow = reinterpret_cast(methodBytes[i++]); methodPointer->initDocument = reinterpret_cast(methodBytes[i++]); diff --git a/bridge/include/dart_methods.h b/bridge/include/dart_methods.h index 4a58e332e6..d07c37b0ad 100644 --- a/bridge/include/dart_methods.h +++ b/bridge/include/dart_methods.h @@ -34,7 +34,6 @@ typedef NativeString* (*PlatformBrightness)(int32_t contextId); typedef void (*ToBlob)(void* callbackContext, int32_t contextId, AsyncBlobCallback blobCallback, int32_t elementId, double devicePixelRatio); typedef void (*OnJSError)(int32_t contextId, const char*); typedef void (*FlushUICommand)(); -typedef void (*InitHTML)(int32_t contextId, void* nativePtr); typedef void (*InitWindow)(int32_t contextId, void* nativePtr); typedef void (*InitDocument)(int32_t contextId, void* nativePtr); @@ -83,7 +82,6 @@ struct DartMethodPointer { #if ENABLE_PROFILE GetPerformanceEntries getPerformanceEntries{nullptr}; #endif - InitHTML initHTML{nullptr}; InitWindow initWindow{nullptr}; InitDocument initDocument{nullptr}; }; diff --git a/bridge/include/kraken_foundation.h b/bridge/include/kraken_foundation.h index 3932c515b8..72a6f48f25 100644 --- a/bridge/include/kraken_foundation.h +++ b/bridge/include/kraken_foundation.h @@ -16,9 +16,8 @@ #include #include #include -#define HTML_TARGET_ID -1 -#define WINDOW_TARGET_ID -2 -#define DOCUMENT_TARGET_ID -3 +#define WINDOW_TARGET_ID -1 +#define DOCUMENT_TARGET_ID -2 #define assert_m(exp, msg) assert(((void)msg, exp)) diff --git a/bridge/polyfill/src/dom.ts b/bridge/polyfill/src/dom.ts index 4afe2c7c8e..c64e6aa3c9 100644 --- a/bridge/polyfill/src/dom.ts +++ b/bridge/polyfill/src/dom.ts @@ -1,36 +1,11 @@ +let html = document.createElement('html'); +document.appendChild(html); + let head = document.createElement('head'); document.documentElement.appendChild(head); -Object.defineProperty(document, 'head', { - set(value: HTMLHeadElement) { - if (value == null || value.tagName != 'HEAD') { - throw TypeError(`Failed to set the 'head' property on 'Document': The new body element must be a 'head' element.`) - } - document.documentElement.replaceChild(value, head); - head = value; - }, - get() { - return head; - }, - enumerable: true, - configurable: false -}); let body = document.createElement('body'); document.documentElement.appendChild(body); -Object.defineProperty(document, 'body', { - set(value: HTMLBodyElement) { - if (value == null || value.tagName != 'BODY') { - throw TypeError(`Failed to set the 'body' property on 'Document': The new body element must be a 'BODY' element.`) - } - document.documentElement.replaceChild(value, body); - body = value; - }, - get() { - return body; - }, - enumerable: true, - configurable: false -}); // @ts-ignore class SVGElement extends Element { diff --git a/bridge/polyfill/src/test/index.js b/bridge/polyfill/src/test/index.js index ce3b72efa9..942ee9ae44 100644 --- a/bridge/polyfill/src/test/index.js +++ b/bridge/polyfill/src/test/index.js @@ -160,8 +160,17 @@ global.simulateInputText = __kraken_simulate_inputtext__; function resetDocumentElement() { window.scrollTo(0, 0); - document.head = document.createElement('head'); - document.body = document.createElement('body'); + document.removeChild(document.documentElement); + let html = document.createElement('html'); + document.appendChild(html); + + let head = document.createElement('head'); + document.documentElement.appendChild(head); + + let body = document.createElement('body'); + document.documentElement.appendChild(body); + + document.documentElement.style.backgroundColor = 'white'; } function traverseNode(node, handle) { diff --git a/integration_tests/lib/custom/custom_element.dart b/integration_tests/lib/custom/custom_element.dart index 5758536cf0..095c10ae2b 100644 --- a/integration_tests/lib/custom/custom_element.dart +++ b/integration_tests/lib/custom/custom_element.dart @@ -1,13 +1,11 @@ -import 'dart:ffi'; import 'dart:async'; -import 'package:kraken/bridge.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/widget.dart'; import 'package:flutter/material.dart' show TextDirection, TextStyle, Color, Image, Text, AssetImage, Widget, BuildContext hide Element; class TextWidgetElement extends WidgetElement { - TextWidgetElement(int targetId, Pointer nativeEventTarget, ElementManager elementManager) : - super(targetId, nativeEventTarget, elementManager); + TextWidgetElement(EventTargetContext? context) : + super(context); @override Widget build(BuildContext context, Map properties) { @@ -16,8 +14,8 @@ class TextWidgetElement extends WidgetElement { } class ImageWidgetElement extends WidgetElement { - ImageWidgetElement(int targetId, Pointer nativeEventTarget, ElementManager elementManager) : - super(targetId, nativeEventTarget, elementManager); + ImageWidgetElement(EventTargetContext? context) : + super(context); @override Widget build(BuildContext context, Map properties) { @@ -26,20 +24,20 @@ class ImageWidgetElement extends WidgetElement { } void defineKrakenCustomElements() { - Kraken.defineCustomElement('sample-element', (int targetId, Pointer nativeEventTarget, ElementManager elementManager) { - return SampleElement(targetId, nativeEventTarget, elementManager); + Kraken.defineCustomElement('sample-element', (EventTargetContext? context) { + return SampleElement(context); }); - Kraken.defineCustomElement('flutter-text', (targetId, nativeEventTarget, elementManager) { - return TextWidgetElement(targetId, nativeEventTarget, elementManager); + Kraken.defineCustomElement('flutter-text', (EventTargetContext? context) { + return TextWidgetElement(context); }); - Kraken.defineCustomElement('flutter-asset-image', (targetId, nativeEventTarget, elementManager) { - return ImageWidgetElement(targetId, nativeEventTarget, elementManager); + Kraken.defineCustomElement('flutter-asset-image', (EventTargetContext? context) { + return ImageWidgetElement(context); }); } class SampleElement extends Element { - SampleElement(int targetId, Pointer nativeEventTarget, ElementManager elementManager) - : super(targetId, nativeEventTarget, elementManager); + SampleElement(EventTargetContext? context) + : super(context); @override getProperty(String key) { diff --git a/integration_tests/lib/main.dart b/integration_tests/lib/main.dart index 5dbfa1de02..22aa5d74c1 100644 --- a/integration_tests/lib/main.dart +++ b/integration_tests/lib/main.dart @@ -91,7 +91,7 @@ void main() async { onDrag: (GestureEvent gestureEvent) { if (gestureEvent.state == EVENT_STATE_START) { var event = CustomEvent('nativegesture', CustomEventInit(detail: 'nativegesture')); - krakenMap[i]!.controller!.view.document!.documentElement.dispatchEvent(event); + krakenMap[i]!.controller!.view.document.documentElement?.dispatchEvent(event); } }, ), diff --git a/integration_tests/pubspec.yaml b/integration_tests/pubspec.yaml index 588519bcb6..8f8a195183 100644 --- a/integration_tests/pubspec.yaml +++ b/integration_tests/pubspec.yaml @@ -27,9 +27,9 @@ dependencies: sdk: flutter kraken: 0.8.0-dev.1 image: ^3.0.2 - kraken_video_player: ^1.1.0 + kraken_video_player: ^2.0.0-dev.0 kraken_websocket: ^1.0.0 - kraken_webview: ^1.1.1 + kraken_webview: ^2.0.0-dev.0 dev_dependencies: flutter_test: diff --git a/integration_tests/runtime/global.ts b/integration_tests/runtime/global.ts index bad3119eb0..17a4c4f078 100644 --- a/integration_tests/runtime/global.ts +++ b/integration_tests/runtime/global.ts @@ -192,7 +192,7 @@ async function simulatePointDown(x: number, y: number, pointer: number = 0) { } // Simulate an point up action. -async function simulatePoinrUp(x: number, y: number, pointer: number = 0) { +async function simulatePointUp(x: number, y: number, pointer: number = 0) { await simulatePointer([ [x, y, PointerChange.up], ], pointer); @@ -228,5 +228,5 @@ Object.assign(global, { sleep, snapshot, simulatePointDown, - simulatePoinrUp, + simulatePointUp, }); diff --git a/integration_tests/specs/css/css-flexbox/auto-margins.ts b/integration_tests/specs/css/css-flexbox/auto-margins.ts index 1d32bad8d3..0bb1abbdbf 100644 --- a/integration_tests/specs/css/css-flexbox/auto-margins.ts +++ b/integration_tests/specs/css/css-flexbox/auto-margins.ts @@ -52,8 +52,6 @@ describe('auto-margins', () => { }, [ img = createElement('img', { - src: - 'https://kraken.oss-cn-hangzhou.aliyuncs.com/images/300x150-green.png', style: { 'box-sizing': 'border-box', margin: 'auto', @@ -67,6 +65,7 @@ describe('auto-margins', () => { document.body.appendChild(div_1); await snapshot(); + img.src = 'assets/300x150-green.png'; img.onload = async () => { await snapshot(); diff --git a/integration_tests/specs/css/css-values/rem.ts b/integration_tests/specs/css/css-values/rem.ts index 83bd6ec913..449dda3f6b 100644 --- a/integration_tests/specs/css/css-values/rem.ts +++ b/integration_tests/specs/css/css-values/rem.ts @@ -47,6 +47,8 @@ describe("rem", () => { }); it("should works with style other than font-size of html", async () => { + document.documentElement.style.fontSize = '2rem'; + let div; let div2; let div3; diff --git a/integration_tests/specs/dom/events/touchEvent.ts b/integration_tests/specs/dom/events/touchEvent.ts index f27fd5e04f..0f3ca7b35b 100644 --- a/integration_tests/specs/dom/events/touchEvent.ts +++ b/integration_tests/specs/dom/events/touchEvent.ts @@ -77,7 +77,7 @@ describe('TouchEvent', () => { const func = async (e: TouchEvent) => { expect(e.touches.length).toBe(2); div.removeEventListener('touchend', func); - await simulatePoinrUp(20, 20); + await simulatePointUp(20, 20); done(); }; @@ -107,7 +107,7 @@ describe('TouchEvent', () => { const func = async (e: TouchEvent) => { expect(e.targetTouches.length).toBe(1); - await simulatePoinrUp(20, 20); + await simulatePointUp(20, 20); div2.removeEventListener('touchend', func); done(); }; diff --git a/kraken/lib/dom.dart b/kraken/lib/dom.dart index aef5507442..48426d3d41 100644 --- a/kraken/lib/dom.dart +++ b/kraken/lib/dom.dart @@ -5,8 +5,6 @@ export 'src/dom/binding.dart'; export 'src/dom/element.dart'; -export 'src/dom/element_manager.dart'; -export 'src/dom/event_handler.dart'; export 'src/dom/event.dart'; export 'src/dom/event_target.dart'; export 'src/dom/object_element_client.dart'; @@ -15,8 +13,10 @@ export 'src/dom/node.dart'; export 'src/dom/text_node.dart'; export 'src/dom/window.dart'; export 'src/dom/document.dart'; +export 'src/dom/comment.dart'; export 'src/dom/document_fragment.dart'; export 'src/dom/sliver_manager.dart'; +export 'src/dom/element_registry.dart'; // Elements export 'src/dom/elements/semantics_text.dart'; diff --git a/kraken/lib/src/bridge/from_native.dart b/kraken/lib/src/bridge/from_native.dart index 6f23a4d843..0766b2e7fd 100644 --- a/kraken/lib/src/bridge/from_native.dart +++ b/kraken/lib/src/bridge/from_native.dart @@ -13,7 +13,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; import 'package:flutter/services.dart'; import 'package:kraken/bridge.dart'; -import 'package:kraken/dom.dart'; import 'package:kraken/launcher.dart'; import 'package:kraken/module.dart'; import 'package:kraken/src/module/performance_timing.dart'; @@ -327,18 +326,11 @@ void _flushUICommand() { final Pointer> _nativeFlushUICommand = Pointer.fromFunction(_flushUICommand); -// HTML Element is special element which created at initialize time, so we can't use UICommandQueue to init. -typedef NativeInitHTML = Void Function(Int32 contextId, Pointer nativePtr); -void _initHTML(int contextId, Pointer nativePtr) { - ElementManager.htmlNativePtrMap[contextId] = nativePtr; -} -final Pointer> _nativeInitHTML = Pointer.fromFunction(_initHTML); - typedef NativeInitWindow = Void Function(Int32 contextId, Pointer nativePtr); typedef DartInitWindow = void Function(int contextId, Pointer nativePtr); void _initWindow(int contextId, Pointer nativePtr) { - ElementManager.windowNativePtrMap[contextId] = nativePtr; + KrakenViewController.windowNativePtrMap[contextId] = nativePtr; } final Pointer> _nativeInitWindow = Pointer.fromFunction(_initWindow); @@ -347,7 +339,7 @@ typedef NativeInitDocument = Void Function(Int32 contextId, Pointer nativePtr); void _initDocument(int contextId, Pointer nativePtr) { - ElementManager.documentNativePtrMap[contextId] = nativePtr; + KrakenViewController.documentNativePtrMap[contextId] = nativePtr; } final Pointer> _nativeInitDocument = Pointer.fromFunction(_initDocument); @@ -391,7 +383,6 @@ final List _dartNativeMethods = [ _nativePlatformBrightness.address, _nativeToBlob.address, _nativeFlushUICommand.address, - _nativeInitHTML.address, _nativeInitWindow.address, _nativeInitDocument.address, _nativeGetEntries.address, diff --git a/kraken/lib/src/bridge/to_native.dart b/kraken/lib/src/bridge/to_native.dart index 540f2388b6..f730457396 100644 --- a/kraken/lib/src/bridge/to_native.dart +++ b/kraken/lib/src/bridge/to_native.dart @@ -463,7 +463,7 @@ void flushUICommand() { controller.view.createComment(id, nativePtr.cast()); break; case UICommandType.disposeEventTarget: - ElementManager.disposeEventTarget(controller.view.contextId, id); + controller.view.disposeEventTarget(id); break; case UICommandType.addEvent: controller.view.addEvent(id, command.args[0]); diff --git a/kraken/lib/src/css/background.dart b/kraken/lib/src/css/background.dart index 3eae0f0850..aa3fea5cde 100644 --- a/kraken/lib/src/css/background.dart +++ b/kraken/lib/src/css/background.dart @@ -16,10 +16,6 @@ import 'package:kraken/rendering.dart'; // CSS Backgrounds: https://drafts.csswg.org/css-backgrounds/ // CSS Images: https://drafts.csswg.org/css-images-3/ -/// The [CSSBackgroundMixin] mixin used to handle background shorthand and compute -/// to single value of background -/// - final RegExp _splitRegExp = RegExp(r'\s+'); const String _singleQuote = '\''; const String _doubleQuote = '"'; @@ -82,6 +78,8 @@ enum CSSBackgroundImageType { image, } +/// The [CSSBackgroundMixin] mixin used to handle background shorthand and compute +/// to single value of background. mixin CSSBackgroundMixin on RenderStyle { static CSSBackgroundPosition DEFAULT_BACKGROUND_POSITION = CSSBackgroundPosition(percentage: -1); static CSSBackgroundSize DEFAULT_BACKGROUND_SIZE = CSSBackgroundSize(fit: BoxFit.none); diff --git a/kraken/lib/src/css/border.dart b/kraken/lib/src/css/border.dart index 63caff6476..074b100cf5 100644 --- a/kraken/lib/src/css/border.dart +++ b/kraken/lib/src/css/border.dart @@ -11,6 +11,9 @@ import 'package:kraken/css.dart'; final RegExp _splitRegExp = RegExp(r'\s+'); +// Initial border value: medium +final CSSLengthValue _mediumWidth = CSSLengthValue(3, CSSLengthType.PX); + enum CSSBorderStyleType { none, hidden, @@ -78,9 +81,6 @@ mixin CSSBorderMixin on RenderStyle { /// Border-width = | thin | medium | thick - // Initial value: medium - final CSSLengthValue _mediumWidth = CSSLengthValue(3, CSSLengthType.PX); - CSSLengthValue? _borderTopWidth; set borderTopWidth(CSSLengthValue? value) { if (value == _borderTopWidth) return; diff --git a/kraken/lib/src/css/render_style.dart b/kraken/lib/src/css/render_style.dart index 1a90baffb0..e9983b5f87 100644 --- a/kraken/lib/src/css/render_style.dart +++ b/kraken/lib/src/css/render_style.dart @@ -170,9 +170,9 @@ abstract class RenderStyle { // for class that extends [AbstractRenderStyle]. RenderBoxModel? get renderBoxModel => target.renderBoxModel; - Size get viewportSize => target.elementManager.viewport.viewportSize; + Size get viewportSize => target.ownerDocument.viewport.viewportSize; - double get rootFontSize => target.elementManager.getRootFontSize(); + double get rootFontSize => target.ownerDocument.documentElement!.renderStyle.fontSize.computedValue; } class CSSRenderStyle diff --git a/kraken/lib/src/dom/comment.dart b/kraken/lib/src/dom/comment.dart new file mode 100644 index 0000000000..1bc747bc65 --- /dev/null +++ b/kraken/lib/src/dom/comment.dart @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019-present Alibaba Inc. All rights reserved. + * Author: Kraken Team. + */ +import 'package:flutter/rendering.dart'; +import 'package:kraken/dom.dart'; + +class Comment extends Node { + Comment(EventTargetContext? context) + : super(NodeType.COMMENT_NODE, context); + + @override + String get nodeName => '#comment'; + + @override + RenderBox? get renderer => null; + + // @TODO: Get data from bridge side. + String get data => ''; + + int get length => data.length; +} diff --git a/kraken/lib/src/dom/document.dart b/kraken/lib/src/dom/document.dart index 5505f1f2bd..efe05ef567 100644 --- a/kraken/lib/src/dom/document.dart +++ b/kraken/lib/src/dom/document.dart @@ -2,24 +2,97 @@ * Copyright (C) 2021-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - import 'package:flutter/rendering.dart'; -import 'package:kraken/bridge.dart'; +import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; +import 'package:kraken/gesture.dart'; +import 'package:kraken/launcher.dart'; +import 'package:kraken/rendering.dart'; +import 'package:kraken/src/dom/element_registry.dart' as element_registry; +import 'package:kraken/widget.dart'; class Document extends Node { - final HTMLElement documentElement; - Document(int targetId, Pointer nativeEventTarget, ElementManager elementManager, this.documentElement) - : super(NodeType.DOCUMENT_NODE, targetId, nativeEventTarget, elementManager); + final RenderViewportBox viewport; + KrakenController controller; + GestureListener? gestureListener; + WidgetDelegate? widgetDelegate; + + Document(EventTargetContext? context, + { + required this.viewport, + required this.controller, + this.gestureListener, + this.widgetDelegate, + }) + : super(NodeType.DOCUMENT_NODE, context); @override String get nodeName => '#document'; @override - RenderBox? get renderer => elementManager.viewport; + RenderBox? get renderer => viewport; + + Element? _documentElement; + Element? get documentElement { + return _documentElement; + } + set documentElement(Element? element) { + if (_documentElement == element) { + return; + } + + if (element != null) { + element.attachTo(this); + // Should scrollable. + element.setRenderStyleProperty(OVERFLOW_X, CSSOverflowType.scroll); + element.setRenderStyleProperty(OVERFLOW_Y, CSSOverflowType.scroll); + // Init with viewport size. + element.renderStyle.width = CSSLengthValue(viewport.viewportSize.width, CSSLengthType.PX); + element.renderStyle.height = CSSLengthValue(viewport.viewportSize.height, CSSLengthType.PX); + } else { + // Detach document element. + viewport.child = null; + } + + _documentElement = element; + } + + @override + Node appendChild(Node child) { + if (child is Element) { + documentElement ??= child; + } else { + throw UnsupportedError('Only Element can be appended to Document'); + } + return super.appendChild(child); + } + + @override + Node insertBefore(Node child, Node referenceNode) { + if (child is Element) { + documentElement ??= child; + } else { + throw UnsupportedError('Only Element can be inserted to Document'); + } + return super.insertBefore(child, referenceNode); + } + + @override + Node removeChild(Node child) { + if (documentElement == child) { + documentElement = null; + } + return super.removeChild(child); + } + + @override + Node? replaceChild(Node newNode, Node oldNode) { + if (documentElement == oldNode) { + documentElement = newNode is Element ? newNode : null; + } + return super.replaceChild(newNode, oldNode); + } addEvent(String eventType) { if (eventHandlers.containsKey(eventType)) return; // Only listen once. @@ -27,12 +100,66 @@ class Document extends Node { switch (eventType) { case EVENT_SCROLL: // Fired at the Document or element when the viewport or element is scrolled, respectively. - return documentElement.addEventListener(eventType, dispatchEvent); + return documentElement?.addEventListener(eventType, dispatchEvent); default: // Events listened on the Window need to be proxied to the Document, because there is a RenderView on the Document, which can handle hitTest. // https://github.com/WebKit/WebKit/blob/main/Source/WebCore/page/VisualViewport.cpp#L61 - documentElement.addEvent(eventType); + documentElement?.addEvent(eventType); break; } } + + Element createElement(String type, EventTargetContext? context) { + Element element = element_registry.createElement(type, context); + element.ownerDocument = this; + return element; + } + + TextNode createTextNode(String data, EventTargetContext? context) { + TextNode textNode = TextNode(data, context); + textNode.ownerDocument = this; + return textNode; + } + + DocumentFragment createDocumentFragment(EventTargetContext? context) { + DocumentFragment documentFragment = DocumentFragment(context); + documentFragment.ownerDocument = this; + return documentFragment; + } + + Comment createComment(EventTargetContext? context) { + Comment comment = Comment(context); + comment.ownerDocument = this; + return comment; + } + + // TODO: https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets + List adoptedStyleSheets = []; + // The styleSheets attribute is readonly attribute. + final List styleSheets = []; + + void addStyleSheet(CSSStyleSheet sheet) { + styleSheets.add(sheet); + recalculateDocumentStyle(); + } + + void removeStyleSheet(CSSStyleSheet sheet) { + styleSheets.remove(sheet); + recalculateDocumentStyle(); + } + + void recalculateDocumentStyle() { + // Recalculate style for all nodes sync. + documentElement?.recalculateNestedStyle(); + } + + @override + void dispose() { + gestureListener = null; + widgetDelegate = null; + styleSheets.clear(); + adoptedStyleSheets.clear(); + documentElement?.dispose(); + super.dispose(); + } } diff --git a/kraken/lib/src/dom/document_fragment.dart b/kraken/lib/src/dom/document_fragment.dart index 3a99c2b462..148a3a81b6 100644 --- a/kraken/lib/src/dom/document_fragment.dart +++ b/kraken/lib/src/dom/document_fragment.dart @@ -9,8 +9,8 @@ import 'package:kraken/dom.dart'; const String DOCUMENT_FRAGMENT = 'DOCUMENTFRAGMENT'; class DocumentFragment extends Node { - DocumentFragment(int targetId, nativeNodePtr, ElementManager elementManager) - : super(NodeType.COMMENT_NODE, targetId, nativeNodePtr, elementManager); + DocumentFragment(EventTargetContext? context) + : super(NodeType.COMMENT_NODE, context); @override String get nodeName => '#documentfragment'; diff --git a/kraken/lib/src/dom/element.dart b/kraken/lib/src/dom/element.dart index a17d60bafe..c98afe2399 100644 --- a/kraken/lib/src/dom/element.dart +++ b/kraken/lib/src/dom/element.dart @@ -4,7 +4,6 @@ */ import 'dart:async'; -import 'dart:ffi'; import 'dart:typed_data'; import 'dart:ui'; @@ -13,14 +12,13 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/painting.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/rendering.dart'; +import 'package:kraken/src/dom/element_event.dart'; +import 'package:kraken/src/dom/element_view.dart'; import 'package:meta/meta.dart'; -import 'element_native_methods.dart'; - final RegExp _splitRegExp = RegExp(r'\s+'); const String _ONE_SPACE = ' '; const String _STYLE_PROPERTY = 'style'; @@ -31,6 +29,7 @@ const String _CLASS_NAME = 'class'; /// height is 150 in pixel. const String ELEMENT_DEFAULT_WIDTH = '300px'; const String ELEMENT_DEFAULT_HEIGHT = '150px'; +const String UNKNOWN = 'UNKNOWN'; typedef TestElement = bool Function(Element element); @@ -82,7 +81,7 @@ typedef GetRenderBoxModel = RenderBoxModel? Function(); class Element extends Node with ElementBase, - ElementNativeMethods, + ElementViewMixin, ElementEventMixin, ElementOverflowMixin { @@ -146,15 +145,18 @@ class Element extends Node _updateRenderBoxModel(); } - Element(int targetId, Pointer nativeEventTarget, ElementManager elementManager, - { Map defaultStyle = const {}, - // Whether element allows children. - bool isIntrinsicBox = false, - bool isDefaultRepaintBoundary = false}) - : _defaultStyle = defaultStyle, - _isIntrinsicBox = isIntrinsicBox, - _isDefaultRepaintBoundary = isDefaultRepaintBoundary, - super(NodeType.ELEMENT_NODE, targetId, nativeEventTarget, elementManager) { + Element( + EventTargetContext? context, + { + Map defaultStyle = const {}, + // Whether element allows children. + bool isIntrinsicBox = false, + bool isDefaultRepaintBoundary = false + }) + : _defaultStyle = defaultStyle, + _isIntrinsicBox = isIntrinsicBox, + _isDefaultRepaintBoundary = isDefaultRepaintBoundary, + super(NodeType.ELEMENT_NODE, context) { // Init style and add change listener. style = CSSStyleDeclaration.computedStyle(this, _defaultStyle, _onStyleChanged); @@ -413,7 +415,7 @@ class Element extends Node renderBoxModel!.clearIntersectionChangeListeners(); // Remove fixed children from root when element disposed. - _removeFixedChild(renderBoxModel!, elementManager.viewportElement._renderLayoutBox!); + _removeFixedChild(renderBoxModel!, ownerDocument.documentElement!._renderLayoutBox!); // Remove renderBox. _removeRenderBoxModel(renderBoxModel!); @@ -456,7 +458,7 @@ class Element extends Node /// So it needs to manually mark element needs paint and add scroll offset in paint stage void _applyFixedChildrenOffset(double scrollOffset, AxisDirection axisDirection) { // Only root element has fixed children - if (this == elementManager.viewportElement && renderBoxModel != null) { + if (this == ownerDocument.documentElement && renderBoxModel != null) { RenderBoxModel layoutBox = (renderBoxModel as RenderLayoutBox).renderScrollingContent ?? renderBoxModel!; for (RenderBoxModel child in layoutBox.fixedChildren) { // Save scrolling offset for paint @@ -483,7 +485,7 @@ class Element extends Node // Remove fixed children before convert to non repaint boundary renderObject if (currentPosition != CSSPositionType.fixed) { - _removeFixedChild(_renderBoxModel, elementManager.viewportElement._renderLayoutBox!); + _removeFixedChild(_renderBoxModel, ownerDocument.documentElement!._renderLayoutBox!); } RenderBox? previousSibling; @@ -510,24 +512,10 @@ class Element extends Node // Add fixed children after convert to repaint boundary renderObject. if (currentPosition == CSSPositionType.fixed) { - _addFixedChild(renderBoxModel!, elementManager.viewportElement._renderLayoutBox!); + _addFixedChild(renderBoxModel!, ownerDocument.documentElement!._renderLayoutBox!); } } - Element? getElementById(Element parentElement, int targetId) { - Element? result; - List childNodes = parentElement.childNodes; - - for (int i = 0; i < childNodes.length; i++) { - Element element = childNodes[i]; - if (element.targetId == targetId) { - result = element; - break; - } - } - return result; - } - void addChild(RenderBox child) { if (_renderLayoutBox != null) { RenderLayoutBox? scrollingContentBox = _renderLayoutBox!.renderScrollingContent; @@ -585,13 +573,8 @@ class Element extends Node } if (renderer != null) { - // HTML element override attachTo method to attach renderObject to viewportBox. - if (parent is Element) { - RenderLayoutBox? parentRenderLayoutBox = parentElement?._renderLayoutBox?.renderScrollingContent ?? parentElement?._renderLayoutBox; - parentRenderLayoutBox!.insert(renderBoxModel!, after: after); - } else if (parent is Document) { - parent.appendChild(this); - } + _attachRenderBoxModel(parent.renderer!, renderer!, after: after); + // Flush pending style before child attached. style.flushPendingProperties(); @@ -761,11 +744,11 @@ class Element extends Node // the padding boxes of the first and the last inline boxes generated for that element. // In CSS 2.1, if the inline element is split across multiple lines, the containing block is undefined. // 2. Otherwise, the containing block is formed by the padding edge of the ancestor. - containingBlockRenderBox = _findContainingBlock(this, elementManager.viewportElement)?._renderLayoutBox; + containingBlockRenderBox = _findContainingBlock(this, ownerDocument.documentElement!)?._renderLayoutBox; } else if (positionType == CSSPositionType.fixed) { // If the element has 'position: fixed', the containing block is established by the viewport // in the case of continuous media or the page area in the case of paged media. - containingBlockRenderBox = elementManager.viewportElement._renderLayoutBox; + containingBlockRenderBox = ownerDocument.documentElement!._renderLayoutBox; } if (containingBlockRenderBox == null) return; @@ -1215,7 +1198,7 @@ class Element extends Node value = CSSBackground.resolveBackgroundAttachment(present); break; case BACKGROUND_IMAGE: - value = CSSBackground.resolveBackgroundImage(present, renderStyle, property, elementManager.controller); + value = CSSBackground.resolveBackgroundImage(present, renderStyle, property, ownerDocument.controller); break; case BACKGROUND_REPEAT: value = CSSBackground.resolveBackgroundRepeat(present); @@ -1408,7 +1391,7 @@ class Element extends Node if (classList.isNotEmpty) { const String classSelectorPrefix = '.'; for (String className in classList) { - for (CSSStyleSheet sheet in elementManager.styleSheets) { + for (CSSStyleSheet sheet in ownerDocument.styleSheets) { List rules = sheet.cssRules; for (int i = 0; i < rules.length; i++) { CSSRule rule = rules[i]; @@ -1569,13 +1552,10 @@ class Element extends Node Offset getOffset(RenderBox renderBox) { // Need to flush layout to get correct size. - elementManager - .getRootRenderBox() - .owner! - .flushLayout(); + ownerDocument.documentElement!.renderBoxModel!.owner!.flushLayout(); - Element? element = _findContainingBlock(this, elementManager.viewportElement); - element ??= elementManager.viewportElement; + Element? element = _findContainingBlock(this, ownerDocument.documentElement!); + element ??= ownerDocument.documentElement!; return renderBox.localToGlobal(Offset.zero, ancestor: element.renderBoxModel); } @@ -1630,14 +1610,12 @@ class Element extends Node SchedulerBinding.instance!.addPostFrameCallback((_) async { Uint8List captured; - RenderBoxModel? renderObject = targetId == HTML_ID - ? elementManager.viewportElement.renderBoxModel - : renderBoxModel; - if (renderObject!.hasSize && renderObject.size.isEmpty) { + RenderBoxModel? _renderBoxModel = renderBoxModel; + if (_renderBoxModel!.hasSize && _renderBoxModel.size.isEmpty) { // Return a blob with zero length. captured = Uint8List(0); } else { - Image image = await renderObject.toImage(pixelRatio: devicePixelRatio!); + Image image = await _renderBoxModel.toImage(pixelRatio: devicePixelRatio!); ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); captured = byteData!.buffer.asUint8List(); } diff --git a/kraken/lib/src/dom/event_handler.dart b/kraken/lib/src/dom/element_event.dart similarity index 95% rename from kraken/lib/src/dom/event_handler.dart rename to kraken/lib/src/dom/element_event.dart index 2359bb6413..5d40bff937 100644 --- a/kraken/lib/src/dom/event_handler.dart +++ b/kraken/lib/src/dom/element_event.dart @@ -13,7 +13,7 @@ enum AppearEventType { disappear } -mixin ElementEventMixin on EventTarget { +mixin ElementEventMixin on ElementBase { AppearEventType prevAppearState = AppearEventType.none; void addEventResponder(RenderPointerListenerMixin renderBox) { @@ -45,7 +45,7 @@ mixin ElementEventMixin on EventTarget { } void handleMouseEvent(String eventType, TapUpDetails details) { - RenderBoxModel? root = elementManager.viewportElement.renderBoxModel; + RenderBoxModel? root = ownerDocument.documentElement!.renderBoxModel; if (root == null) { return; } diff --git a/kraken/lib/src/dom/element_manager.dart b/kraken/lib/src/dom/element_manager.dart deleted file mode 100644 index 05ed35b1e5..0000000000 --- a/kraken/lib/src/dom/element_manager.dart +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Copyright (C) 2019-present Alibaba Inc. All rights reserved. - * Author: Kraken Team. - */ - -import 'dart:async'; -import 'dart:core'; -import 'dart:ffi'; -import 'dart:math' as math; -import 'dart:ui'; - -import 'package:ffi/ffi.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart' show WidgetsBinding, WidgetsBindingObserver, RouteInformation; -import 'package:kraken/bridge.dart'; -import 'package:kraken/css.dart'; -import 'package:kraken/dom.dart'; -import 'package:kraken/gesture.dart'; -import 'package:kraken/launcher.dart'; -import 'package:kraken/module.dart'; -import 'package:kraken/rendering.dart'; -import 'package:kraken/scheduler.dart'; -import 'package:kraken/src/dom/element_registry.dart' as element_registry; -import 'package:kraken/widget.dart'; - -const String UNKNOWN = 'UNKNOWN'; - -const int HTML_ID = -1; -const int WINDOW_ID = -2; -const int DOCUMENT_ID = -3; - -typedef ElementCreator = Element Function(int targetId, Pointer nativeEventTarget, ElementManager elementManager); - -class ElementManager implements WidgetsBindingObserver, ElementsBindingObserver { - // Call from JS Bridge before JS side eventTarget object been Garbage collected. - static void disposeEventTarget(int contextId, int id) { - KrakenController controller = KrakenController.getControllerOfJSContextId(contextId)!; - EventTarget? eventTarget = controller.view.getEventTargetById(id); - if (eventTarget == null) return; - eventTarget.dispose(); - malloc.free(eventTarget.nativeEventTargetPtr); - } - - // Alias defineElement export for kraken plugin - static void defineElement(String type, ElementCreator creator) { - element_registry.defineElement(type, creator); - } - - static Map> htmlNativePtrMap = {}; - static Map> documentNativePtrMap = {}; - static Map> windowNativePtrMap = {}; - - static double FOCUS_VIEWINSET_BOTTOM_OVERALL = 32; - // Single child renderView. - late final RenderViewportBox viewport; - late final Document document; - late final Element viewportElement; - Map _eventTargets = {}; - bool? showPerformanceOverlayOverride; - KrakenController controller; - - double get viewportWidth => viewport.viewportSize.width; - double get viewportHeight => viewport.viewportSize.height; - - final int contextId; - - final List _detachCallbacks = []; - - GestureListener? gestureListener; - - WidgetDelegate? widgetDelegate; - - ElementManager({ - required this.contextId, - required this.viewport, - required this.controller, - this.showPerformanceOverlayOverride = false, - this.gestureListener, - this.widgetDelegate, - }) { - if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_ELEMENT_MANAGER_PROPERTY_INIT); - PerformanceTiming.instance().mark(PERF_ROOT_ELEMENT_INIT_START); - } - - HTMLElement documentElement = HTMLElement(HTML_ID, htmlNativePtrMap[contextId]!, this); - setEventTarget(documentElement); - - if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_ROOT_ELEMENT_INIT_END); - } - - _setupObserver(); - - Window window = Window(WINDOW_ID, windowNativePtrMap[contextId]!, this, viewportElement); - setEventTarget(window); - - document = Document(DOCUMENT_ID, documentNativePtrMap[contextId]!, this, documentElement); - setEventTarget(document); - - documentElement.attachTo(document); - - element_registry.defineBuiltInElements(); - - // Listeners need to be registered to window in order to dispatch events on demand. - if (gestureListener != null) { - if (gestureListener!.onTouchStart != null) { - window.addEvent(EVENT_TOUCH_START); - } - - if (gestureListener!.onTouchMove != null) { - window.addEvent(EVENT_TOUCH_MOVE); - } - - if (gestureListener!.onTouchEnd != null) { - window.addEvent(EVENT_TOUCH_END); - } - } - } - - void _setupObserver() { - if (ElementsBinding.instance != null) { - ElementsBinding.instance!.addObserver(this); - } else if (WidgetsBinding.instance != null) { - WidgetsBinding.instance!.addObserver(this); - } - } - - void _teardownObserver() { - if (ElementsBinding.instance != null) { - ElementsBinding.instance!.removeObserver(this); - } else if (WidgetsBinding.instance != null) { - WidgetsBinding.instance!.removeObserver(this); - } - } - - T? getEventTargetByTargetId(int targetId) { - EventTarget? target = _eventTargets[targetId]; - if (target is T) - return target as T; - else - return null; - } - - bool existsTarget(int id) { - return _eventTargets.containsKey(id); - } - - void removeTarget(EventTarget target) { - if (_eventTargets.containsKey(target.targetId)) { - _eventTargets.remove(target.targetId); - } - } - - void setDetachCallback(VoidCallback callback) { - _detachCallbacks.add(callback); - } - - void setEventTarget(EventTarget target) { - _eventTargets[target.targetId] = target; - } - - void clearTargets() { - // Set current eventTargets to a new object, clean old targets by gc. - _eventTargets = {}; - } - - Element createElement( - int id, Pointer nativePtr, String type, Map? props, List? events) { - assert(!existsTarget(id), 'ERROR: Can not create element with same id "$id"'); - - List eventList; - if (events != null) { - eventList = []; - for (var eventName in events) { - if (eventName is String) eventList.add(eventName); - } - } - - Element element = element_registry.createElement(id, nativePtr, type, this); - setEventTarget(element); - return element; - } - - void createTextNode(int id, Pointer nativePtr, String data) { - TextNode textNode = TextNode(id, nativePtr, data, this); - setEventTarget(textNode); - } - - void createDocumentFragment(int id, Pointer nativePtr) { - DocumentFragment documentFragment = DocumentFragment(id, nativePtr, this); - setEventTarget(documentFragment); - } - - void createComment(int id, Pointer nativePtr) { - EventTarget comment = Comment(id, nativePtr, this); - setEventTarget(comment); - } - - void cloneNode(int originalId, int newId) { - EventTarget originalTarget = getEventTargetByTargetId(originalId)!; - EventTarget newTarget = getEventTargetByTargetId(newId)!; - - // Current only element clone will process in dart. - if (originalTarget is Element) { - Element newElement = newTarget as Element; - // Copy inline style. - originalTarget.inlineStyle.forEach((key, value) { - newElement.setInlineStyle(key, value); - }); - // Copy element attributes. - originalTarget.properties.forEach((key, value) { - newElement.setProperty(key, value); - }); - } - } - - void removeNode(int targetId) { - assert(existsTarget(targetId), 'targetId: $targetId'); - - Node target = getEventTargetByTargetId(targetId)!; - - // Should detach renderObject. - target.disposeRenderObject(); - - target.parentNode?.removeChild(target); - - _debugDOMTreeChanged(); - } - - // TODO: https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets - List adoptedStyleSheets = []; - List styleSheets = []; - - void addStyleSheet(CSSStyleSheet sheet) { - styleSheets.add(sheet); - recalculateDocumentStyle(); - } - - void removeStyleSheet(CSSStyleSheet sheet) { - styleSheets.remove(sheet); - recalculateDocumentStyle(); - } - - void recalculateDocumentStyle() { - // Recalculate style for all nodes sync. - document.documentElement.recalculateNestedStyle(); - } - - void setProperty(int targetId, String key, dynamic value) { - assert(existsTarget(targetId), 'targetId: $targetId key: $key value: $value'); - Node target = getEventTargetByTargetId(targetId)!; - - if (target is Element) { - // Only Element has properties. - target.setProperty(key, value); - } else if (target is TextNode && key == 'data' || key == 'nodeValue') { - (target as TextNode).data = value; - } else { - debugPrint('Only element has properties, try setting $key to Node(#$targetId).'); - } - } - - dynamic getProperty(int targetId, String key) { - assert(existsTarget(targetId), 'targetId: $targetId key: $key'); - Node target = getEventTargetByTargetId(targetId)!; - - if (target is Element) { - // Only Element has properties - return target.getProperty(key); - } else if (target is TextNode && key == 'data' || key == 'nodeValue') { - return (target as TextNode).data; - } else { - return null; - } - } - - void removeProperty(int targetId, String key) { - assert(existsTarget(targetId), 'targetId: $targetId key: $key'); - Node target = getEventTargetByTargetId(targetId)!; - - if (target is Element) { - target.removeProperty(key); - } else if (target is TextNode && key == 'data' || key == 'nodeValue') { - (target as TextNode).data = ''; - } else { - debugPrint('Only element has properties, try removing $key from Node(#$targetId).'); - } - } - - void setInlineStyle(int targetId, String key, dynamic value) { - assert(existsTarget(targetId), 'id: $targetId key: $key value: $value'); - Node? target = getEventTargetByTargetId(targetId); - if (target == null) return; - - if (target is Element) { - target.setInlineStyle(key, value); - } else { - debugPrint('Only element has style, try setting style.$key from Node(#$targetId).'); - } - } - - void flushPendingStyleProperties(int targetId) { - if (!existsTarget(targetId)) return; - Node? target = getEventTargetByTargetId(targetId); - if (target == null) return; - - if (target is Element) { - target.style.flushPendingProperties(); - } else { - debugPrint('Only element has style, try flushPendingStyleProperties from Node(#$targetId).'); - } - } - - /// - ///

- /// - /// foo - /// - ///

- /// - void insertAdjacentNode(int targetId, String position, int newTargetId) { - assert(existsTarget(targetId), 'targetId: $targetId position: $position newTargetId: $newTargetId'); - assert(existsTarget(newTargetId), 'newTargetId: $newTargetId position: $position'); - - Node target = getEventTargetByTargetId(targetId)!; - Node newNode = getEventTargetByTargetId(newTargetId)!; - Node? targetParentNode = target.parentNode; - - switch (position) { - case 'beforebegin': - targetParentNode!.insertBefore(newNode, target); - break; - case 'afterbegin': - target.insertBefore(newNode, target.firstChild); - break; - case 'beforeend': - target.appendChild(newNode); - break; - case 'afterend': - if (targetParentNode!.lastChild == target) { - targetParentNode.appendChild(newNode); - } else { - targetParentNode.insertBefore( - newNode, - targetParentNode.childNodes[targetParentNode.childNodes.indexOf(target) + 1], - ); - } - - break; - } - - _debugDOMTreeChanged(); - } - - void addEvent(int targetId, String eventType) { - if (!existsTarget(targetId)) return; - EventTarget target = getEventTargetByTargetId(targetId)!; - - if (target is Element) { - target.addEvent(eventType); - } else if (target is Window) { - target.addEvent(eventType); - } else if (target is Document) { - target.addEvent(eventType); - } - } - - void removeEvent(int targetId, String eventType) { - assert(existsTarget(targetId), 'targetId: $targetId event: $eventType'); - - Element target = getEventTargetByTargetId(targetId)!; - - target.removeEvent(eventType); - } - - RenderBox getRootRenderBox() { - return viewport; - } - - double getRootFontSize() { - RenderBoxModel rootBoxModel = viewportElement.renderBoxModel!; - return rootBoxModel.renderStyle.fontSize.computedValue; - } - - bool showPerformanceOverlay = false; - - RenderBox buildRenderBox({bool showPerformanceOverlay = false}) { - this.showPerformanceOverlay = showPerformanceOverlay; - - RenderBox renderBox = getRootRenderBox(); - - // We need to add PerformanceOverlay of it's needed. - if (showPerformanceOverlayOverride != null) showPerformanceOverlay = showPerformanceOverlayOverride!; - - if (showPerformanceOverlay) { - RenderPerformanceOverlay renderPerformanceOverlay = - RenderPerformanceOverlay(optionsMask: 15, rasterizerThreshold: 0); - RenderConstrainedBox renderConstrainedPerformanceOverlayBox = RenderConstrainedBox( - child: renderPerformanceOverlay, - additionalConstraints: BoxConstraints.tight(Size( - math.min(350.0, window.physicalSize.width), - math.min(150.0, window.physicalSize.height), - )), - ); - RenderFpsOverlay renderFpsOverlayBox = RenderFpsOverlay(); - - renderBox = RenderStack( - children: [ - renderBox, - renderConstrainedPerformanceOverlayBox, - renderFpsOverlayBox, - ], - textDirection: TextDirection.ltr, - ); - } - - return renderBox; - } - - void attach(RenderObject parent, RenderObject? previousSibling, {bool showPerformanceOverlay = false}) { - RenderObject root = buildRenderBox(showPerformanceOverlay: showPerformanceOverlay); - - if (parent is ContainerRenderObjectMixin) { - parent.insert(root, after: previousSibling); - } else if (parent is RenderObjectWithChildMixin) { - parent.child = root; - } - } - - void detach() { - RenderObject? parent = viewport.parent as RenderObject?; - - if (parent == null) return; - - // Detach renderObjects - viewportElement.disposeRenderObject(); - - // run detachCallbacks - for (var callback in _detachCallbacks) { - callback(); - } - _detachCallbacks.clear(); - } - - // Hooks for DevTools. - VoidCallback? debugDOMTreeChanged; - void _debugDOMTreeChanged() { - VoidCallback? f = debugDOMTreeChanged; - if (f != null) { - f(); - } - } - - void dispose() { - _teardownObserver(); - debugDOMTreeChanged = null; - } - - @override - void didChangeAccessibilityFeatures() { } - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { } - - @override - void didChangeLocales(List? locale) { } - - WindowPadding _prevViewInsets = window.viewInsets; - - @override - void didChangeMetrics() { - double bottomInset = window.viewInsets.bottom / window.devicePixelRatio; - if (_prevViewInsets.bottom > window.viewInsets.bottom) { - // Hide keyboard - viewport.bottomInset = bottomInset; - } else { - bool shouldScrollByToCenter = false; - InputElement? focusInputElement = InputElement.focusInputElement; - if (focusInputElement != null) { - RenderBox? renderer = focusInputElement.renderer; - if (renderer != null && renderer.hasSize) { - Offset focusOffset = renderer.localToGlobal(Offset.zero); - // FOCUS_VIEWINSET_BOTTOM_OVERALL to meet border case. - if (focusOffset.dy > viewportHeight - bottomInset - FOCUS_VIEWINSET_BOTTOM_OVERALL) { - shouldScrollByToCenter = true; - } - } - } - // Show keyboard - viewport.bottomInset = bottomInset; - if (shouldScrollByToCenter) { - SchedulerBinding.instance!.addPostFrameCallback((_) { - viewportElement.scrollBy(dy: bottomInset); - }); - } - } - _prevViewInsets = window.viewInsets; - } - - @override - void didChangePlatformBrightness() { } - - @override - void didChangeTextScaleFactor() { } - - @override - void didHaveMemoryPressure() { } - - @override - Future didPopRoute() async { - return false; - } - - @override - Future didPushRoute(String route) async { - return false; - } - - @override - Future didPushRouteInformation(RouteInformation routeInformation) async { - return false; - } -} diff --git a/kraken/lib/src/dom/element_registry.dart b/kraken/lib/src/dom/element_registry.dart index fe25f33273..a98d5125df 100644 --- a/kraken/lib/src/dom/element_registry.dart +++ b/kraken/lib/src/dom/element_registry.dart @@ -2,12 +2,10 @@ * Copyright (C) 2021 Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - -import 'package:kraken/bridge.dart'; import 'package:kraken/dom.dart'; +typedef ElementCreator = Element Function(EventTargetContext? context); + final Map _elementRegistry = {}; void defineElement(String name, ElementCreator creator) { @@ -17,14 +15,14 @@ void defineElement(String name, ElementCreator creator) { _elementRegistry[name] = creator; } -Element createElement(int id, Pointer nativePtr, String name, ElementManager elementManager) { +Element createElement(String name, EventTargetContext? context){ ElementCreator? creator = _elementRegistry[name]; if (creator == null) { print('ERROR: unexpected element type "$name"'); - return Element(id, nativePtr, elementManager); + return Element(context); } - Element element = creator(id, nativePtr, elementManager); + Element element = creator(context); // Assign tagName, used by inspector. element.tagName = name; return element; @@ -35,77 +33,78 @@ void defineBuiltInElements() { if (_isDefined) return; _isDefined = true; // Inline text - defineElement(BR, (id, nativePtr, elementManager) => BRElement(id, nativePtr, elementManager)); - defineElement(B, (id, nativePtr, elementManager) => BringElement(id, nativePtr, elementManager)); - defineElement(ABBR, (id, nativePtr, elementManager) => AbbreviationElement(id, nativePtr, elementManager)); - defineElement(EM, (id, nativePtr, elementManager) => EmphasisElement(id, nativePtr, elementManager)); - defineElement(CITE, (id, nativePtr, elementManager) => CitationElement(id, nativePtr, elementManager)); - defineElement(I, (id, nativePtr, elementManager) => IdiomaticElement(id, nativePtr, elementManager)); - defineElement(CODE, (id, nativePtr, elementManager) => CodeElement(id, nativePtr, elementManager)); - defineElement(SAMP, (id, nativePtr, elementManager) => SampleElement(id, nativePtr, elementManager)); - defineElement(STRONG, (id, nativePtr, elementManager) => StrongElement(id, nativePtr, elementManager)); - defineElement(SMALL, (id, nativePtr, elementManager) => SmallElement(id, nativePtr, elementManager)); - defineElement(S, (id, nativePtr, elementManager) => StrikethroughElement(id, nativePtr, elementManager)); - defineElement(U, (id, nativePtr, elementManager) => UnarticulatedElement(id, nativePtr, elementManager)); - defineElement(VAR, (id, nativePtr, elementManager) => VariableElement(id, nativePtr, elementManager)); - defineElement(TIME, (id, nativePtr, elementManager) => TimeElement(id, nativePtr, elementManager)); - defineElement(DATA, (id, nativePtr, elementManager) => DataElement(id, nativePtr, elementManager)); - defineElement(MARK, (id, nativePtr, elementManager) => MarkElement(id, nativePtr, elementManager)); - defineElement(Q, (id, nativePtr, elementManager) => QuoteElement(id, nativePtr, elementManager)); - defineElement(KBD, (id, nativePtr, elementManager) => KeyboardElement(id, nativePtr, elementManager)); - defineElement(DFN, (id, nativePtr, elementManager) => DefinitionElement(id, nativePtr, elementManager)); - defineElement(SPAN, (id, nativePtr, elementManager) => SpanElement(id, nativePtr, elementManager)); - defineElement(ANCHOR, (id, nativePtr, elementManager) => AnchorElement(id, nativePtr, elementManager)); + defineElement(BR, (context) => BRElement(context)); + defineElement(B, (context) => BringElement(context)); + defineElement(ABBR, (context) => AbbreviationElement(context)); + defineElement(EM, (context) => EmphasisElement(context)); + defineElement(CITE, (context) => CitationElement(context)); + defineElement(I, (context) => IdiomaticElement(context)); + defineElement(CODE, (context) => CodeElement(context)); + defineElement(SAMP, (context) => SampleElement(context)); + defineElement(STRONG, (context) => StrongElement(context)); + defineElement(SMALL, (context) => SmallElement(context)); + defineElement(S, (context) => StrikethroughElement(context)); + defineElement(U, (context) => UnarticulatedElement(context)); + defineElement(VAR, (context) => VariableElement(context)); + defineElement(TIME, (context) => TimeElement(context)); + defineElement(DATA, (context) => DataElement(context)); + defineElement(MARK, (context) => MarkElement(context)); + defineElement(Q, (context) => QuoteElement(context)); + defineElement(KBD, (context) => KeyboardElement(context)); + defineElement(DFN, (context) => DefinitionElement(context)); + defineElement(SPAN, (context) => SpanElement(context)); + defineElement(ANCHOR, (context) => AnchorElement(context)); // Content - defineElement(PRE, (id, nativePtr, elementManager) => PreElement(id, nativePtr, elementManager)); - defineElement(PARAGRAPH, (id, nativePtr, elementManager) => ParagraphElement(id, nativePtr, elementManager)); - defineElement(DIV, (id, nativePtr, elementManager) => DivElement(id, nativePtr, elementManager)); - defineElement(UL, (id, nativePtr, elementManager) => UListElement(id, nativePtr, elementManager)); - defineElement(OL, (id, nativePtr, elementManager) => OListElement(id, nativePtr, elementManager)); - defineElement(LI, (id, nativePtr, elementManager) => LIElement(id, nativePtr, elementManager)); - defineElement(DL, (id, nativePtr, elementManager) => DListElement(id, nativePtr, elementManager)); - defineElement(DT, (id, nativePtr, elementManager) => DTElement(id, nativePtr, elementManager)); - defineElement(DD, (id, nativePtr, elementManager) => DDElement(id, nativePtr, elementManager)); - defineElement(FIGURE, (id, nativePtr, elementManager) => FigureElement(id, nativePtr, elementManager)); - defineElement(FIGCAPTION, (id, nativePtr, elementManager) => FigureCaptionElement(id, nativePtr, elementManager)); - defineElement(BLOCKQUOTE, (id, nativePtr, elementManager) => BlockQuotationElement(id, nativePtr, elementManager)); - defineElement(TEMPLATE, (id, nativePtr, elementManager) => TemplateElement(id, nativePtr, elementManager)); + defineElement(PRE, (context) => PreElement(context)); + defineElement(PARAGRAPH, (context) => ParagraphElement(context)); + defineElement(DIV, (context) => DivElement(context)); + defineElement(UL, (context) => UListElement(context)); + defineElement(OL, (context) => OListElement(context)); + defineElement(LI, (context) => LIElement(context)); + defineElement(DL, (context) => DListElement(context)); + defineElement(DT, (context) => DTElement(context)); + defineElement(DD, (context) => DDElement(context)); + defineElement(FIGURE, (context) => FigureElement(context)); + defineElement(FIGCAPTION, (context) => FigureCaptionElement(context)); + defineElement(BLOCKQUOTE, (context) => BlockQuotationElement(context)); + defineElement(TEMPLATE, (context) => TemplateElement(context)); // Sections - defineElement(ADDRESS, (id, nativePtr, elementManager) => AddressElement(id, nativePtr, elementManager)); - defineElement(ARTICLE, (id, nativePtr, elementManager) => ArticleElement(id, nativePtr, elementManager)); - defineElement(ASIDE, (id, nativePtr, elementManager) => AsideElement(id, nativePtr, elementManager)); - defineElement(FOOTER, (id, nativePtr, elementManager) => FooterElement(id, nativePtr, elementManager)); - defineElement(HEADER, (id, nativePtr, elementManager) => HeaderElement(id, nativePtr, elementManager)); - defineElement(MAIN, (id, nativePtr, elementManager) => MainElement(id, nativePtr, elementManager)); - defineElement(NAV, (id, nativePtr, elementManager) => NavElement(id, nativePtr, elementManager)); - defineElement(SECTION, (id, nativePtr, elementManager) => SectionElement(id, nativePtr, elementManager)); + defineElement(ADDRESS, (context) => AddressElement(context)); + defineElement(ARTICLE, (context) => ArticleElement(context)); + defineElement(ASIDE, (context) => AsideElement(context)); + defineElement(FOOTER, (context) => FooterElement(context)); + defineElement(HEADER, (context) => HeaderElement(context)); + defineElement(MAIN, (context) => MainElement(context)); + defineElement(NAV, (context) => NavElement(context)); + defineElement(SECTION, (context) => SectionElement(context)); // Headings - defineElement(H1, (id, nativePtr, elementManager) => H1Element(id, nativePtr, elementManager)); - defineElement(H2, (id, nativePtr, elementManager) => H2Element(id, nativePtr, elementManager)); - defineElement(H3, (id, nativePtr, elementManager) => H3Element(id, nativePtr, elementManager)); - defineElement(H4, (id, nativePtr, elementManager) => H4Element(id, nativePtr, elementManager)); - defineElement(H5, (id, nativePtr, elementManager) => H5Element(id, nativePtr, elementManager)); - defineElement(H6, (id, nativePtr, elementManager) => H6Element(id, nativePtr, elementManager)); + defineElement(H1, (context) => H1Element(context)); + defineElement(H2, (context) => H2Element(context)); + defineElement(H3, (context) => H3Element(context)); + defineElement(H4, (context) => H4Element(context)); + defineElement(H5, (context) => H5Element(context)); + defineElement(H6, (context) => H6Element(context)); // Forms - defineElement(LABEL, (id, nativePtr, elementManager) => LabelElement(id, nativePtr, elementManager)); - defineElement(BUTTON, (id, nativePtr, elementManager) => ButtonElement(id, nativePtr, elementManager)); - defineElement(INPUT, (id, nativePtr, elementManager) => InputElement(id, nativePtr, elementManager)); + defineElement(LABEL, (context) => LabelElement(context)); + defineElement(BUTTON, (context) => ButtonElement(context)); + defineElement(INPUT, (context) => InputElement(context)); // Edits - defineElement(DEL, (id, nativePtr, elementManager) => DelElement(id, nativePtr, elementManager)); - defineElement(INS, (id, nativePtr, elementManager) => InsElement(id, nativePtr, elementManager)); + defineElement(DEL, (context) => DelElement(context)); + defineElement(INS, (context) => InsElement(context)); // Head - defineElement(HEAD, (id, nativePtr, elementManager) => HeadElement(id, nativePtr, elementManager)); - defineElement(TITLE, (id, nativePtr, elementManager) => TitleElement(id, nativePtr, elementManager)); - defineElement(META, (id, nativePtr, elementManager) => MetaElement(id, nativePtr, elementManager)); - defineElement(LINK, (id, nativePtr, elementManager) => LinkElement(id, nativePtr, elementManager)); - defineElement(STYLE, (id, nativePtr, elementManager) => StyleElement(id, nativePtr, elementManager)); - defineElement(NOSCRIPT, (id, nativePtr, elementManager) => NoScriptElement(id, nativePtr, elementManager)); - defineElement(SCRIPT, (id, nativePtr, elementManager) => ScriptElement(id, nativePtr, elementManager)); + defineElement(HEAD, (context) => HeadElement(context)); + defineElement(TITLE, (context) => TitleElement(context)); + defineElement(META, (context) => MetaElement(context)); + defineElement(LINK, (context) => LinkElement(context)); + defineElement(STYLE, (context) => StyleElement(context)); + defineElement(NOSCRIPT, (context) => NoScriptElement(context)); + defineElement(SCRIPT, (context) => ScriptElement(context)); // Object - defineElement(OBJECT, (id, nativePtr, elementManager) => ObjectElement(id, nativePtr, elementManager)); - defineElement(PARAM, (id, nativePtr, elementManager) => ParamElement(id, nativePtr, elementManager)); + defineElement(OBJECT, (context) => ObjectElement(context)); + defineElement(PARAM, (context) => ParamElement(context)); // Others - defineElement(BODY, (id, nativePtr, elementManager) => BodyElement(id, nativePtr, elementManager)); - defineElement(IMAGE, (id, nativePtr, elementManager) => ImageElement(id, nativePtr, elementManager)); - defineElement(CANVAS, (id, nativePtr, elementManager) => CanvasElement(id, nativePtr, elementManager)); + defineElement(HTML, (context) => HTMLElement(context)); + defineElement(BODY, (context) => BodyElement(context)); + defineElement(IMAGE, (context) => ImageElement(context)); + defineElement(CANVAS, (context) => CanvasElement(context)); } diff --git a/kraken/lib/src/dom/element_native_methods.dart b/kraken/lib/src/dom/element_view.dart similarity index 99% rename from kraken/lib/src/dom/element_native_methods.dart rename to kraken/lib/src/dom/element_view.dart index 77fab31677..87879bf400 100644 --- a/kraken/lib/src/dom/element_native_methods.dart +++ b/kraken/lib/src/dom/element_view.dart @@ -27,7 +27,7 @@ enum ViewModuleProperty { scrollWidth } -mixin ElementNativeMethods on Node { +mixin ElementViewMixin on ElementBase { @override dynamic handleJSCall(String method, List argv) { Element element = (this as Element); diff --git a/kraken/lib/src/dom/elements/a.dart b/kraken/lib/src/dom/elements/a.dart index bd80e73195..70fbd826b4 100644 --- a/kraken/lib/src/dom/elements/a.dart +++ b/kraken/lib/src/dom/elements/a.dart @@ -2,11 +2,7 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - import 'package:flutter/gestures.dart'; -import 'package:kraken/bridge.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/kraken.dart'; import 'package:kraken/module.dart'; @@ -26,8 +22,8 @@ class AnchorElement extends Element { String? _target; - AnchorElement(int targetId, Pointer nativeEventTargetPtr, ElementManager elementManager) - : super(targetId, nativeEventTargetPtr, elementManager) { + AnchorElement(EventTargetContext? context) + : super(context) { addEvent(EVENT_CLICK); } @@ -106,10 +102,10 @@ class AnchorElement extends Element { String? href = _href; if (href.isNotEmpty) { - String baseUrl = elementManager.controller.href; + String baseUrl = ownerDocument.controller.href; Uri baseUri = Uri.parse(baseUrl); - Uri resolvedUri = elementManager.controller.uriParser!.resolve(baseUri, Uri.parse(href)); - elementManager.controller.view.handleNavigationAction( + Uri resolvedUri = ownerDocument.controller.uriParser!.resolve(baseUri, Uri.parse(href)); + ownerDocument.controller.view.handleNavigationAction( baseUrl, resolvedUri.toString(), _getNavigationType(resolvedUri.scheme)); } } diff --git a/kraken/lib/src/dom/elements/body.dart b/kraken/lib/src/dom/elements/body.dart index ac5927f8e4..76f35ce417 100644 --- a/kraken/lib/src/dom/elements/body.dart +++ b/kraken/lib/src/dom/elements/body.dart @@ -2,10 +2,6 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; @@ -16,8 +12,8 @@ const Map _defaultStyle = { }; class BodyElement extends Element { - BodyElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super( targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + BodyElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); @override void addEvent(String eventType) { diff --git a/kraken/lib/src/dom/elements/canvas/canvas.dart b/kraken/lib/src/dom/elements/canvas/canvas.dart index b162d5c61a..71a6f6dbb0 100644 --- a/kraken/lib/src/dom/elements/canvas/canvas.dart +++ b/kraken/lib/src/dom/elements/canvas/canvas.dart @@ -47,16 +47,14 @@ class CanvasElement extends Element { static Pointer _getContext( Pointer nativeCanvasElement, Pointer contextId) { - CanvasElement canvasElement = EventTarget.getEventTargetOfNativePtr(nativeCanvasElement) as CanvasElement; + CanvasElement canvasElement = EventTarget.getEventTargetByPointer(nativeCanvasElement) as CanvasElement; canvasElement.getContext(nativeStringToString(contextId)); return canvasElement.painter.context!.nativeCanvasRenderingContext2D; } - CanvasElement(int targetId, Pointer nativeEventTarget, ElementManager elementManager) + CanvasElement(EventTargetContext? context) : super( - targetId, - nativeEventTarget, - elementManager, + context, isIntrinsicBox: true, isDefaultRepaintBoundary: true, defaultStyle: _defaultStyle, diff --git a/kraken/lib/src/dom/elements/canvas/canvas_context_2d.dart b/kraken/lib/src/dom/elements/canvas/canvas_context_2d.dart index e44078dba1..8c7fa519e4 100644 --- a/kraken/lib/src/dom/elements/canvas/canvas_context_2d.dart +++ b/kraken/lib/src/dom/elements/canvas/canvas_context_2d.dart @@ -181,7 +181,7 @@ class CanvasRenderingContext2D { closePath(); break; case 'drawImage': - ImageElement imageElement = EventTarget.getEventTargetOfNativePtr(argv[0]) as ImageElement; + ImageElement imageElement = EventTarget.getEventTargetByPointer(argv[0]) as ImageElement; num sx = 0.0, sy = 0.0, sWidth = 0.0, sHeight = 0.0, dx = 0.0, dy = 0.0, dWidth = 0.0, dHeight = 0.0; if (argv.length == 3) { diff --git a/kraken/lib/src/dom/elements/edits.dart b/kraken/lib/src/dom/elements/edits.dart index c71a983184..3c8690f094 100644 --- a/kraken/lib/src/dom/elements/edits.dart +++ b/kraken/lib/src/dom/elements/edits.dart @@ -2,10 +2,6 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; @@ -22,11 +18,11 @@ const Map _delDefaultStyle = { }; class DelElement extends Element { - DelElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _delDefaultStyle); + DelElement(EventTargetContext? context) + : super(context, defaultStyle: _delDefaultStyle); } class InsElement extends Element { - InsElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _insDefaultStyle); + InsElement(EventTargetContext? context) + : super(context, defaultStyle: _insDefaultStyle); } diff --git a/kraken/lib/src/dom/elements/forms.dart b/kraken/lib/src/dom/elements/forms.dart index 2f0346fd48..a3d6bbac97 100644 --- a/kraken/lib/src/dom/elements/forms.dart +++ b/kraken/lib/src/dom/elements/forms.dart @@ -2,10 +2,6 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; @@ -17,11 +13,11 @@ const Map _defaultStyle = { }; class LabelElement extends Element { - LabelElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager); + LabelElement(EventTargetContext? context) + : super(context); } class ButtonElement extends Element { - ButtonElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + ButtonElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } diff --git a/kraken/lib/src/dom/elements/grouping_content.dart b/kraken/lib/src/dom/elements/grouping_content.dart index 13d297f9b9..27c8f28f6a 100644 --- a/kraken/lib/src/dom/elements/grouping_content.dart +++ b/kraken/lib/src/dom/elements/grouping_content.dart @@ -2,9 +2,6 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ -import 'dart:ffi'; - -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; @@ -60,68 +57,68 @@ const Map _lDefaultStyle = { class DivElement extends Element { - DivElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + DivElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class FigureElement extends Element { - FigureElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _bDefaultStyle); + FigureElement(EventTargetContext? context) + : super(context, defaultStyle: _bDefaultStyle); } class FigureCaptionElement extends Element { - FigureCaptionElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + FigureCaptionElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class BlockQuotationElement extends Element { - BlockQuotationElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _bDefaultStyle); + BlockQuotationElement(EventTargetContext? context) + : super(context, defaultStyle: _bDefaultStyle); } // https://html.spec.whatwg.org/multipage/grouping-content.html#htmlparagraphelement class ParagraphElement extends Element { static Map defaultStyle = _pDefaultStyle; - ParagraphElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: defaultStyle); + ParagraphElement(EventTargetContext? context) + : super(context, defaultStyle: defaultStyle); } // https://html.spec.whatwg.org/multipage/grouping-content.html#htmlulistelement class UListElement extends Element { - UListElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _lDefaultStyle); + UListElement(EventTargetContext? context) + : super(context, defaultStyle: _lDefaultStyle); } // https://html.spec.whatwg.org/multipage/grouping-content.html#htmlolistelement class OListElement extends Element { - OListElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _lDefaultStyle); + OListElement(EventTargetContext? context) + : super(context, defaultStyle: _lDefaultStyle); } class LIElement extends Element { - LIElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + LIElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } // https://html.spec.whatwg.org/multipage/grouping-content.html#htmlpreelement class PreElement extends Element { - PreElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _preDefaultStyle); + PreElement(EventTargetContext? context) + : super(context, defaultStyle: _preDefaultStyle); } // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd class DDElement extends Element { - DDElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _ddDefaultStyle); + DDElement(EventTargetContext? context) + : super(context, defaultStyle: _ddDefaultStyle); } class DTElement extends Element { - DTElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + DTElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } // https://html.spec.whatwg.org/multipage/grouping-content.html#htmldlistelement class DListElement extends Element { - DListElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _pDefaultStyle); + DListElement(EventTargetContext? context) + : super(context, defaultStyle: _pDefaultStyle); } diff --git a/kraken/lib/src/dom/elements/head.dart b/kraken/lib/src/dom/elements/head.dart index c3cbbdafe6..1e7bd3f796 100644 --- a/kraken/lib/src/dom/elements/head.dart +++ b/kraken/lib/src/dom/elements/head.dart @@ -2,11 +2,7 @@ * Copyright (C) 2021-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - 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'; @@ -26,36 +22,36 @@ const String NOSCRIPT = 'NOSCRIPT'; const String SCRIPT = 'SCRIPT'; class HeadElement extends Element { - HeadElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + HeadElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class LinkElement extends Element { - LinkElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + LinkElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class MetaElement extends Element { - MetaElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + MetaElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class TitleElement extends Element { - TitleElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + TitleElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class NoScriptElement extends Element { - NoScriptElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + NoScriptElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } const String _JAVASCRIPT_MIME = 'text/javascript'; const String _JAVASCRIPT_MODULE = 'module'; class ScriptElement extends Element { - ScriptElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle) { + ScriptElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle) { } String type = _JAVASCRIPT_MIME; @@ -71,12 +67,14 @@ class ScriptElement extends Element { } void _fetchBundle(String src) async { + int? contextId = ownerDocument.contextId; + if (contextId == null) return; // Must if (src.isNotEmpty && isConnected && (type == _JAVASCRIPT_MIME || type == _JAVASCRIPT_MODULE)) { try { KrakenBundle bundle = KrakenBundle.fromUrl(src); - await bundle.resolve(elementManager.contextId); - await bundle.eval(elementManager.contextId); + await bundle.resolve(contextId); + await bundle.eval(contextId); // Successful load. SchedulerBinding.instance!.addPostFrameCallback((_) { dispatchEvent(Event(EVENT_LOAD)); @@ -94,6 +92,8 @@ class ScriptElement extends Element { @override void connectedCallback() async { super.connectedCallback(); + int? contextId = ownerDocument.contextId; + if (contextId == null) return; String? src = getProperty('src'); if (src != null) { _fetchBundle(src); @@ -107,12 +107,11 @@ class ScriptElement extends Element { }); String script = buffer.toString(); if (script.isNotEmpty) { - int contextId = elementManager.contextId; KrakenController? controller = KrakenController.getControllerOfJSContextId(contextId); if (controller != null) { KrakenBundle bundle = KrakenBundle.fromContent(script, url: controller.href); bundle.resolve(contextId); - await bundle.eval(elementManager.contextId); + await bundle.eval(contextId); } } } @@ -122,8 +121,8 @@ class ScriptElement extends Element { const String _CSS_MIME = 'text/css'; class StyleElement extends Element { - StyleElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + StyleElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); String type = _CSS_MIME; CSSStyleSheet? _styleSheet; @@ -146,7 +145,7 @@ class StyleElement extends Element { }); String style = buffer.toString(); _styleSheet = CSSStyleSheet(style); - elementManager.addStyleSheet(_styleSheet!); + ownerDocument.addStyleSheet(_styleSheet!); } super.connectedCallback(); } @@ -154,7 +153,7 @@ class StyleElement extends Element { @override void disconnectedCallback() { if (_styleSheet != null) { - elementManager.removeStyleSheet(_styleSheet!); + ownerDocument.removeStyleSheet(_styleSheet!); } super.disconnectedCallback(); } diff --git a/kraken/lib/src/dom/elements/headings.dart b/kraken/lib/src/dom/elements/headings.dart index 641f239cec..6fa96a01c4 100644 --- a/kraken/lib/src/dom/elements/headings.dart +++ b/kraken/lib/src/dom/elements/headings.dart @@ -2,9 +2,6 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ -import 'dart:ffi'; - -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; @@ -64,31 +61,31 @@ const Map _h6DefaultStyle = { }; class H1Element extends Element { - H1Element(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _h1DefaultStyle); + H1Element(EventTargetContext? context) + : super(context, defaultStyle: _h1DefaultStyle); } class H2Element extends Element { - H2Element(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _h2DefaultStyle); + H2Element(EventTargetContext? context) + : super(context, defaultStyle: _h2DefaultStyle); } class H3Element extends Element { - H3Element(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _h3DefaultStyle); + H3Element(EventTargetContext? context) + : super(context, defaultStyle: _h3DefaultStyle); } class H4Element extends Element { - H4Element(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _h4DefaultStyle); + H4Element(EventTargetContext? context) + : super(context, defaultStyle: _h4DefaultStyle); } class H5Element extends Element { - H5Element(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _h5DefaultStyle); + H5Element(EventTargetContext? context) + : super(context, defaultStyle: _h5DefaultStyle); } class H6Element extends Element { - H6Element(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _h6DefaultStyle); + H6Element(EventTargetContext? context) + : super(context, defaultStyle: _h6DefaultStyle); } diff --git a/kraken/lib/src/dom/elements/html.dart b/kraken/lib/src/dom/elements/html.dart index 0d51f1d93f..60c3f9fd38 100644 --- a/kraken/lib/src/dom/elements/html.dart +++ b/kraken/lib/src/dom/elements/html.dart @@ -2,58 +2,18 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/rendering.dart'; -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; -import 'package:kraken/kraken.dart'; const String HTML = 'HTML'; const Map _defaultStyle = { DISPLAY: BLOCK, - OVERFLOW: AUTO }; class HTMLElement extends Element { static Map defaultStyle = _defaultStyle; - HTMLElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super( - targetId, - nativePtr, - elementManager, - defaultStyle: defaultStyle - ) { - if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_ROOT_ELEMENT_PROPERTY_INIT); - } - elementManager.viewportElement = this; - // Must init with viewport width. - renderStyle.width = CSSLengthValue(elementManager.viewportWidth, CSSLengthType.PX); - renderStyle.height = CSSLengthValue(elementManager.viewportHeight, CSSLengthType.PX); - } - - @override - void attachTo(Node parent, {RenderBox? after}) { - super.attachTo(parent); - if (renderBoxModel != null) { - elementManager.viewport.child = renderBoxModel!; - // Flush pending style immediately. - style.flushPendingProperties(); - } - } - - @override - void disposeRenderObject() { - super.disposeRenderObject(); - if (renderBoxModel != null) { - elementManager.viewport.child = null; - elementManager.viewport.dropChild(renderBoxModel!); - } - } + HTMLElement(EventTargetContext? context) + : super(context, defaultStyle: defaultStyle); @override void addEvent(String eventType) { @@ -61,10 +21,4 @@ class HTMLElement extends Element { if (eventType == EVENT_SCROLL) return; super.addEvent(eventType); } - - @override - String get tagName => HTML; - - @override - bool get isRendererAttached => true; } diff --git a/kraken/lib/src/dom/elements/img.dart b/kraken/lib/src/dom/elements/img.dart index 16d8a685c0..d9ffaa391a 100644 --- a/kraken/lib/src/dom/elements/img.dart +++ b/kraken/lib/src/dom/elements/img.dart @@ -2,14 +2,11 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - import 'dart:async'; -import 'dart:ffi'; import 'dart:ui' as ui; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/painting.dart'; @@ -58,11 +55,9 @@ class ImageElement extends Element { bool get _shouldLazyLoading => properties['loading'] == 'lazy'; ImageStreamCompleterHandle? _completerHandle; - ImageElement(int targetId, Pointer nativeEventTarget, ElementManager elementManager) + ImageElement(EventTargetContext? context) : super( - targetId, - nativeEventTarget, - elementManager, + context, isIntrinsicBox: true, defaultStyle: _defaultStyle) { } @@ -298,8 +293,8 @@ class ImageElement extends Element { Uri? _resolveSrc() { String? src = properties['src']; if (src != null && src.isNotEmpty) { - Uri base = Uri.parse(elementManager.controller.href); - return elementManager.controller.uriParser!.resolve(base, Uri.parse(src)); + Uri base = Uri.parse(ownerDocument.controller.href); + return ownerDocument.controller.uriParser!.resolve(base, Uri.parse(src)); } return null; } @@ -435,7 +430,6 @@ class ImageElement extends Element { _propertyHeight = CSSNumber.parseNumber(value); _resolveImage(_resolvedUri, updateImageProvider: true); } - } @override diff --git a/kraken/lib/src/dom/elements/input.dart b/kraken/lib/src/dom/elements/input.dart index 59d51c9e20..e96aacae7a 100644 --- a/kraken/lib/src/dom/elements/input.dart +++ b/kraken/lib/src/dom/elements/input.dart @@ -2,9 +2,7 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - import 'dart:async'; -import 'dart:ffi'; import 'dart:math' as math; import 'dart:ui'; @@ -16,7 +14,6 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart' show TextSelectionOverlay, TextSelectionControls, ClipboardStatusNotifier; -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/gesture.dart'; @@ -161,7 +158,7 @@ class InputElement extends Element implements TextInputClient, TickerProvider { static void setFocus(InputElement inputElement) { if (InputElement.focusInputElement != inputElement) { // Focus kraken widget to get focus from other widgets. - WidgetDelegate? widgetDelegate = inputElement.elementManager.widgetDelegate; + WidgetDelegate? widgetDelegate = inputElement.ownerDocument.widgetDelegate; if (widgetDelegate != null) { widgetDelegate.requestFocus(); } @@ -230,15 +227,12 @@ class InputElement extends Element implements TextInputClient, TickerProvider { TextInputConfiguration? _textInputConfiguration; - InputElement( - int targetId, - Pointer nativeEventTarget, - ElementManager elementManager, { + InputElement(EventTargetContext? context, { this.textAlign = TextAlign.left, this.textDirection = TextDirection.ltr, this.minLines = 1, this.maxLines = 1, - }) : super(targetId, nativeEventTarget, elementManager, defaultStyle: _defaultStyle, isIntrinsicBox: true) { + }) : super(context, defaultStyle: _defaultStyle, isIntrinsicBox: true) { _textSelectionDelegate = EditableTextDelegate(this); scrollOffsetX = _scrollableX.position; } @@ -581,7 +575,7 @@ class InputElement extends Element implements TextInputClient, TickerProvider { text = _buildPasswordTextSpan(text.text!); } - WidgetDelegate? widgetDelegate = elementManager.widgetDelegate; + WidgetDelegate? widgetDelegate = ownerDocument.widgetDelegate; if (widgetDelegate != null) { cursorColor = widgetDelegate.getCursorColor(); selectionColor = widgetDelegate.getSelectionColor(); @@ -810,7 +804,7 @@ class InputElement extends Element implements TextInputClient, TickerProvider { return; } - WidgetDelegate? widgetDelegate = elementManager.widgetDelegate; + WidgetDelegate? widgetDelegate = ownerDocument.widgetDelegate; if (_selectionControls == null) { _selectionOverlay?.hide(); @@ -851,7 +845,7 @@ class InputElement extends Element implements TextInputClient, TickerProvider { _showSelectionHandles = willShowSelectionHandles; } - WidgetDelegate? widgetDelegate = elementManager.widgetDelegate; + WidgetDelegate? widgetDelegate = ownerDocument.widgetDelegate; if (widgetDelegate != null) { TargetPlatform platform = widgetDelegate.getTargetPlatform(); switch (platform) { diff --git a/kraken/lib/src/dom/elements/object.dart b/kraken/lib/src/dom/elements/object.dart index 8c4980653a..0cb26d88bb 100644 --- a/kraken/lib/src/dom/elements/object.dart +++ b/kraken/lib/src/dom/elements/object.dart @@ -2,11 +2,7 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - import 'package:flutter/rendering.dart'; -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; @@ -25,8 +21,8 @@ const Map _paramStyle = { // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param class ParamElement extends Element { - ParamElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _paramStyle); + ParamElement(EventTargetContext? context) + : super(context, defaultStyle: _paramStyle); } _DefaultObjectElementClient _DefaultObjectElementClientFactory(ObjectElementHost objectElementHost) { @@ -39,11 +35,10 @@ class ObjectElement extends Element implements ObjectElementHost { late ObjectElementClientFactory _objectElementClientFactory; late ObjectElementClient _objectElementClient; - ObjectElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _objectStyle, isIntrinsicBox: true) { + ObjectElement(EventTargetContext? context) + : super(context, defaultStyle: _objectStyle, isIntrinsicBox: true) { initObjectClient(); initElementClient(); - initDetachCallback(elementManager); } void initObjectClient() { @@ -59,10 +54,6 @@ class ObjectElement extends Element implements ObjectElementHost { } } - void initDetachCallback(final ElementManager elementManager) { - elementManager.setDetachCallback(disposeClient); - } - @override void setProperty(String key, value) { super.setProperty(key, value); diff --git a/kraken/lib/src/dom/elements/sections.dart b/kraken/lib/src/dom/elements/sections.dart index f91a8b7ec9..269d79b993 100644 --- a/kraken/lib/src/dom/elements/sections.dart +++ b/kraken/lib/src/dom/elements/sections.dart @@ -2,10 +2,6 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; @@ -30,41 +26,41 @@ const Map _addressDefaultStyle = { }; class AddressElement extends Element { - AddressElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _addressDefaultStyle); + AddressElement(EventTargetContext? context) + : super(context, defaultStyle: _addressDefaultStyle); } class ArticleElement extends Element { - ArticleElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + ArticleElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class AsideElement extends Element { - AsideElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + AsideElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class FooterElement extends Element { - FooterElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + FooterElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class HeaderElement extends Element { - HeaderElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + HeaderElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class MainElement extends Element { - MainElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + MainElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class NavElement extends Element { - NavElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + NavElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class SectionElement extends Element { - SectionElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + SectionElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } diff --git a/kraken/lib/src/dom/elements/semantics_text.dart b/kraken/lib/src/dom/elements/semantics_text.dart index 3fee365748..dd30c2a8e9 100644 --- a/kraken/lib/src/dom/elements/semantics_text.dart +++ b/kraken/lib/src/dom/elements/semantics_text.dart @@ -2,10 +2,6 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; @@ -72,108 +68,108 @@ const Map _defaultStyle = { // https://html.spec.whatwg.org/multipage/text-level-semantics.html#htmlbrelement class BRElement extends Element { - BRElement(int targetId, Pointer nativePtr, ElementManager elementManager) + BRElement(EventTargetContext? context) : super( - targetId, nativePtr, elementManager, + context, defaultStyle: _breakDefaultStyle, isIntrinsicBox: true, ); } class BringElement extends Element { - BringElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _boldDefaultStyle); + BringElement(EventTargetContext? context) + : super(context, defaultStyle: _boldDefaultStyle); } class AbbreviationElement extends Element { - AbbreviationElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _abbrDefaultStyle); + AbbreviationElement(EventTargetContext? context) + : super(context, defaultStyle: _abbrDefaultStyle); } class EmphasisElement extends Element { - EmphasisElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + EmphasisElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class CitationElement extends Element { - CitationElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + CitationElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class DefinitionElement extends Element { - DefinitionElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + DefinitionElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i class IdiomaticElement extends Element { - IdiomaticElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + IdiomaticElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class CodeElement extends Element { - CodeElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _codeDefaultStyle); + CodeElement(EventTargetContext? context) + : super(context, defaultStyle: _codeDefaultStyle); } class SampleElement extends Element { - SampleElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _codeDefaultStyle); + SampleElement(EventTargetContext? context) + : super(context, defaultStyle: _codeDefaultStyle); } class KeyboardElement extends Element { - KeyboardElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _codeDefaultStyle); + KeyboardElement(EventTargetContext? context) + : super(context, defaultStyle: _codeDefaultStyle); } class SpanElement extends Element { - SpanElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager); + SpanElement(EventTargetContext? context) + : super(context); } class DataElement extends Element { - DataElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager); + DataElement(EventTargetContext? context) + : super(context); } // TODO: enclosed text is a short inline quotation class QuoteElement extends Element { - QuoteElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager); + QuoteElement(EventTargetContext? context) + : super(context); } class StrongElement extends Element { - StrongElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _boldDefaultStyle); + StrongElement(EventTargetContext? context) + : super(context, defaultStyle: _boldDefaultStyle); } class TimeElement extends Element { - TimeElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _boldDefaultStyle); + TimeElement(EventTargetContext? context) + : super(context, defaultStyle: _boldDefaultStyle); } class SmallElement extends Element { - SmallElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _smallDefaultStyle); + SmallElement(EventTargetContext? context) + : super(context, defaultStyle: _smallDefaultStyle); } class StrikethroughElement extends Element { - StrikethroughElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _sDefaultStyle); + StrikethroughElement(EventTargetContext? context) + : super(context, defaultStyle: _sDefaultStyle); } // https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-u-element class UnarticulatedElement extends Element { - UnarticulatedElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _uDefaultStyle); + UnarticulatedElement(EventTargetContext? context) + : super(context, defaultStyle: _uDefaultStyle); } class VariableElement extends Element { - VariableElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + VariableElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } class MarkElement extends Element { - MarkElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super(targetId, nativePtr, elementManager, defaultStyle: _markDefaultStyle); + MarkElement(EventTargetContext? context) + : super(context, defaultStyle: _markDefaultStyle); } diff --git a/kraken/lib/src/dom/elements/template.dart b/kraken/lib/src/dom/elements/template.dart index a166e1e86b..7ef2d3fddf 100644 --- a/kraken/lib/src/dom/elements/template.dart +++ b/kraken/lib/src/dom/elements/template.dart @@ -2,10 +2,6 @@ * Copyright (C) 2021-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; @@ -16,6 +12,6 @@ const Map _defaultStyle = { }; class TemplateElement extends Element { - TemplateElement(int targetId, Pointer nativePtr, ElementManager elementManager) - : super( targetId, nativePtr, elementManager, defaultStyle: _defaultStyle); + TemplateElement(EventTargetContext? context) + : super(context, defaultStyle: _defaultStyle); } diff --git a/kraken/lib/src/dom/event.dart b/kraken/lib/src/dom/event.dart index 322e8bb9c1..e51058d9c4 100644 --- a/kraken/lib/src/dom/event.dart +++ b/kraken/lib/src/dom/event.dart @@ -103,7 +103,7 @@ class Event { cancelable ? 1 : 0, timeStamp, defaultPrevented ? 1 : 0, - _target != null ? _target.nativeEventTargetPtr.address : nullptr.address, + (_target != null && _target.pointer != null) ? _target.pointer!.address : nullptr.address, nullptr.address ]; @@ -526,7 +526,7 @@ class Touch { Pointer toNative() { Pointer nativeTouch = malloc.allocate(sizeOf()); nativeTouch.ref.identifier = identifier; - nativeTouch.ref.target = target.nativeEventTargetPtr; + nativeTouch.ref.target = target.pointer!; nativeTouch.ref.clientX = clientX; nativeTouch.ref.clientY = clientY; nativeTouch.ref.screenX = screenX; diff --git a/kraken/lib/src/dom/event_target.dart b/kraken/lib/src/dom/event_target.dart index f20ffaee93..f85510f928 100644 --- a/kraken/lib/src/dom/event_target.dart +++ b/kraken/lib/src/dom/event_target.dart @@ -2,7 +2,6 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - import 'dart:collection'; import 'dart:ffi'; @@ -62,7 +61,7 @@ void _callNativeMethods(Pointer nativeEventTarget, Pointer re toNativeValue(returnedValue, null); } else { - EventTarget eventTarget = EventTarget.getEventTargetOfNativePtr(nativeEventTarget.cast()); + EventTarget eventTarget = EventTarget.getEventTargetByPointer(nativeEventTarget.cast()); try { if (method.startsWith(GetPropertyCallPreFix) && values.isEmpty) { String key = method.substring(GetPropertyCallPreFix.length); @@ -85,32 +84,38 @@ String jsMethodToKey(String method) { Pointer> _nativeCallNativeMethods = Pointer.fromFunction(_callNativeMethods); +class EventTargetContext { + final int contextId; + final Pointer pointer; + const EventTargetContext(this.contextId, this.pointer); +} + abstract class EventTarget { static final SplayTreeMap _nativeMap = SplayTreeMap(); - static EventTarget getEventTargetOfNativePtr(Pointer nativePtr) { - EventTarget? target = _nativeMap[nativePtr.address]; - if (target == null) throw FlutterError('Can not get eventTarget of nativePtr: $nativePtr'); + static EventTarget getEventTargetByPointer(Pointer pointer) { + EventTarget? target = _nativeMap[pointer.address]; + if (target == null) throw FlutterError('Can not get eventTarget by pointer: $pointer'); return target; } - // A unique target identifier. - final int targetId; + // JS side context id. + int? contextId; + // JS side EventTarget object pointer. + Pointer? pointer; bool _disposed = false; bool get disposed => _disposed; - // The Add - final Pointer nativeEventTargetPtr; - - // the self reference the ElementManager - ElementManager elementManager; - @protected Map> eventHandlers = {}; - EventTarget(this.targetId, this.nativeEventTargetPtr, this.elementManager) { - nativeEventTargetPtr.ref.callNativeMethods = _nativeCallNativeMethods; - _nativeMap[nativeEventTargetPtr.address] = this; + EventTarget(EventTargetContext? context) { + if (context != null) { + contextId = context.contextId; + pointer = context.pointer; + pointer!.ref.callNativeMethods = _nativeCallNativeMethods; + _nativeMap[pointer!.address] = this; + } } void addEventListener(String eventType, EventHandler eventHandler) { @@ -129,25 +134,12 @@ abstract class EventTarget { currentHandlers.remove(eventHandler); } + @mustCallSuper void dispatchEvent(Event event) { if (disposed) return; - event.target = this; - - emitUIEvent(elementManager.controller.view.contextId, nativeEventTargetPtr, event); - // Dispatch listener for widget. - if (elementManager.gestureListener != null) { - if (elementManager.gestureListener?.onTouchStart != null && event.type == EVENT_TOUCH_START) { - elementManager.gestureListener?.onTouchStart!(event as TouchEvent); - } - - if (elementManager.gestureListener?.onTouchMove != null && event.type == EVENT_TOUCH_MOVE) { - elementManager.gestureListener?.onTouchMove!(event as TouchEvent); - } - - if (elementManager.gestureListener?.onTouchEnd != null && event.type == EVENT_TOUCH_END) { - elementManager.gestureListener?.onTouchEnd!(event as TouchEvent); - } + if (contextId != null && pointer != null) { + emitUIEvent(contextId!, pointer!, event); } } @@ -156,24 +148,23 @@ abstract class EventTarget { } @mustCallSuper - dynamic handleJSCall(String method, List argv) { - } + dynamic handleJSCall(String method, List argv) {} @mustCallSuper void dispose() { if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_DISPOSE_EVENT_TARGET_START, uniqueId: targetId); + PerformanceTiming.instance().mark(PERF_DISPOSE_EVENT_TARGET_START, uniqueId: hashCode); } - elementManager.removeTarget(this); - eventHandlers.clear(); - _nativeMap.remove(nativeEventTargetPtr.address); _disposed = true; + eventHandlers.clear(); + + if (pointer != null) { + _nativeMap.remove(pointer!.address); + } if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_DISPOSE_EVENT_TARGET_END, uniqueId: targetId); + PerformanceTiming.instance().mark(PERF_DISPOSE_EVENT_TARGET_END, uniqueId: hashCode); } } - - // void addEvent(String eventType) {} } diff --git a/kraken/lib/src/dom/node.dart b/kraken/lib/src/dom/node.dart index 3ce4d6703b..1af4b86187 100644 --- a/kraken/lib/src/dom/node.dart +++ b/kraken/lib/src/dom/node.dart @@ -2,10 +2,8 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ -import 'dart:ffi'; - +import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; -import 'package:kraken/bridge.dart'; import 'package:kraken/dom.dart'; import 'package:meta/meta.dart'; @@ -17,22 +15,6 @@ enum NodeType { DOCUMENT_FRAGMENT_NODE, } -class Comment extends Node { - Comment(int targetId, Pointer nativeEventTarget, ElementManager elementManager) - : super(NodeType.COMMENT_NODE, targetId, nativeEventTarget, elementManager); - - @override - String get nodeName => '#comment'; - - @override - RenderBox? get renderer => null; - - // @TODO: Get data from bridge side. - String get data => ''; - - int get length => data.length; -} - /// [RenderObjectNode] provide the renderObject related abstract life cycle for /// [Node] or [Element]s, which wrap [RenderObject]s, which provide the actual /// rendering of the application. @@ -95,6 +77,9 @@ abstract class Node extends EventTarget implements RenderObjectNode, LifecycleCa NodeType nodeType; String get nodeName; + // The read-only ownerDocument property of the Node interface returns the top-level document object of the node. + late Document ownerDocument; + /// The Node.parentElement read-only property returns the DOM node's parent Element, /// or null if the node either has no parent, or its parent isn't a DOM Element. Element? get parentElement { @@ -112,8 +97,8 @@ abstract class Node extends EventTarget implements RenderObjectNode, LifecycleCa return _children; } - Node(this.nodeType, int targetId, Pointer nativeEventTarget, ElementManager elementManager) - : super(targetId, nativeEventTarget, elementManager); + Node(this.nodeType, EventTargetContext? context) + : super(context); // If node is on the tree, the root parent is body. bool get isConnected { @@ -121,8 +106,7 @@ abstract class Node extends EventTarget implements RenderObjectNode, LifecycleCa while (parent.parentNode != null) { parent = parent.parentNode!; } - Document document = elementManager.document; - return this == document || parent == document; + return parent == ownerDocument; } Node get firstChild => childNodes.first; @@ -145,6 +129,10 @@ abstract class Node extends EventTarget implements RenderObjectNode, LifecycleCa // Is child renderObject attached. bool get isRendererAttached => renderer != null && renderer!.attached; + bool contains(Node child) { + return childNodes.contains(child); + } + /// Attach a renderObject to parent. void attachTo(Element parent, {RenderBox? after}) {} @@ -191,38 +179,31 @@ abstract class Node extends EventTarget implements RenderObjectNode, LifecycleCa return child; } - bool contains(Node child) { - return childNodes.contains(child); - } - - Node getRootNode() { - Node root = this; - while (root.parentNode != null) { - root = root.parentNode as Node; - } - return root; - } - @mustCallSuper - Node insertBefore(Node newNode, Node referenceNode) { - newNode._ensureOrphan(); + Node insertBefore(Node child, Node referenceNode) { + child._ensureOrphan(); int referenceIndex = childNodes.indexOf(referenceNode); if (referenceIndex == -1) { - return appendChild(newNode); + return appendChild(child); } else { - newNode.parentNode = this; - childNodes.insert(referenceIndex, newNode); - if (newNode.isConnected) newNode.connectedCallback(); - return newNode; + child.parentNode = this; + childNodes.insert(referenceIndex, child); + if (child.isConnected) { + child.connectedCallback(); + } + return child; } } @mustCallSuper Node removeChild(Node child) { if (childNodes.contains(child)) { + bool isConnected = child.isConnected; childNodes.remove(child); child.parentNode = null; - child.disconnectedCallback(); + if (isConnected) { + child.disconnectedCallback(); + } } return child; } @@ -231,16 +212,17 @@ abstract class Node extends EventTarget implements RenderObjectNode, LifecycleCa Node? replaceChild(Node newNode, Node oldNode) { Node? replacedNode; if (childNodes.contains(oldNode)) { + newNode._ensureOrphan(); + bool isOldNodeConnected = oldNode.isConnected; int referenceIndex = childNodes.indexOf(oldNode); oldNode.parentNode = null; replacedNode = oldNode; childNodes[referenceIndex] = newNode; - if (newNode.isConnected) { - newNode.disconnectedCallback(); + + if (isOldNodeConnected) { + oldNode.disconnectedCallback(); newNode.connectedCallback(); } - } else { - appendChild(newNode); } return replacedNode; } @@ -269,6 +251,27 @@ abstract class Node extends EventTarget implements RenderObjectNode, LifecycleCa child.disconnectedCallback(); } } + + @override + void dispatchEvent(Event event) { + if (disposed) return; + super.dispatchEvent(event); + + // Dispatch listener for widget. + if (ownerDocument.gestureListener != null) { + if (ownerDocument.gestureListener?.onTouchStart != null && event.type == EVENT_TOUCH_START) { + ownerDocument.gestureListener?.onTouchStart!(event as TouchEvent); + } + + if (ownerDocument.gestureListener?.onTouchMove != null && event.type == EVENT_TOUCH_MOVE) { + ownerDocument.gestureListener?.onTouchMove!(event as TouchEvent); + } + + if (ownerDocument.gestureListener?.onTouchEnd != null && event.type == EVENT_TOUCH_END) { + ownerDocument.gestureListener?.onTouchEnd!(event as TouchEvent); + } + } + } } /// https://dom.spec.whatwg.org/#dom-node-nodetype diff --git a/kraken/lib/src/dom/text_node.dart b/kraken/lib/src/dom/text_node.dart index 7fca4795c7..98ed6cbd22 100644 --- a/kraken/lib/src/dom/text_node.dart +++ b/kraken/lib/src/dom/text_node.dart @@ -2,11 +2,7 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; - import 'package:flutter/rendering.dart'; -import 'package:kraken/bridge.dart'; import 'package:kraken/css.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/rendering.dart'; @@ -18,8 +14,8 @@ const String RETURN_CHAR = '\r'; const String TAB_CHAR = '\t'; class TextNode extends Node { - TextNode(int targetId, Pointer nativeEventTarget, this._data, ElementManager elementManager) - : super(NodeType.TEXT_NODE, targetId, nativeEventTarget, elementManager); + TextNode(this._data, EventTargetContext? context) + : super(NodeType.TEXT_NODE, context); // Must be existed after text node is attached, and all text update will after text attached. RenderTextBox? _renderTextBox; diff --git a/kraken/lib/src/dom/window.dart b/kraken/lib/src/dom/window.dart index b2270e8b57..8a5c184f96 100644 --- a/kraken/lib/src/dom/window.dart +++ b/kraken/lib/src/dom/window.dart @@ -2,11 +2,8 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - -import 'dart:ffi'; import 'dart:ui'; -import 'package:kraken/bridge.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/launcher.dart'; import 'package:kraken/module.dart'; @@ -14,38 +11,38 @@ import 'package:kraken/module.dart'; const String WINDOW = 'WINDOW'; class Window extends EventTarget { - final Element viewportElement; + final Document document; - Window(int targetId, Pointer nativeEventTarget, ElementManager elementManager, this.viewportElement) : super(targetId, nativeEventTarget, elementManager) { + Window(EventTargetContext context, this.document) : super(context) { window.onPlatformBrightnessChanged = () { ColorSchemeChangeEvent event = ColorSchemeChangeEvent((window.platformBrightness == Brightness.light) ? 'light' : 'dart'); dispatchEvent(event); }; } - static void _open(ElementManager elementManager, String url) { - KrakenController rootController = elementManager.controller.view.rootController; + void _open(String url) { + KrakenController rootController = document.controller.view.rootController; String? sourceUrl = rootController.href; - elementManager.controller.view.handleNavigationAction(sourceUrl, url, KrakenNavigationType.navigate); + document.controller.view.handleNavigationAction(sourceUrl, url, KrakenNavigationType.navigate); } double scrollX() { - return viewportElement.scrollLeft; + return document.documentElement!.scrollLeft; } double scrollY() { - return viewportElement.scrollTop; + return document.documentElement!.scrollTop; } void scrollTo(num x, num y) { - viewportElement.flushLayout(); - viewportElement.scrollTo(x: x, y: y, withAnimation: false); + document.documentElement!.flushLayout(); + document.documentElement!.scrollTo(x: x, y: y, withAnimation: false); } void scrollBy(num x, num y) { - viewportElement.flushLayout(); - viewportElement.scrollBy(dx: x, dy: y, withAnimation: false); + document.documentElement!.flushLayout(); + document.documentElement!.scrollBy(dx: x, dy: y, withAnimation: false); } void addEvent(String eventType) { @@ -58,23 +55,18 @@ class Window extends EventTarget { return addEventListener(eventType, dispatchEvent); case EVENT_SCROLL: // Fired at the Document or element when the viewport or element is scrolled, respectively. - return viewportElement.addEventListener(eventType, dispatchEvent); + return document.documentElement!.addEventListener(eventType, dispatchEvent); case EVENT_RESIZE: // TODO: Fired at the Window when the viewport is resized. break; default: // Events listened on the Window need to be proxied to the Document, because there is a RenderView on the Document, which can handle hitTest. // https://github.com/WebKit/WebKit/blob/main/Source/WebCore/page/VisualViewport.cpp#L61 - viewportElement.addEvent(eventType); + document.documentElement!.addEvent(eventType); break; } } - @override - void dispose() { - super.dispose(); - } - @override dynamic handleJSCall(String method, List argv) { switch(method) { @@ -88,7 +80,7 @@ class Window extends EventTarget { case 'scrollY': return scrollY(); case 'open': - return _open(elementManager, argv[0]); + return _open(argv[0]); default: super.handleJSCall(method, argv); } diff --git a/kraken/lib/src/launcher/bundle.dart b/kraken/lib/src/launcher/bundle.dart index 6a2105c26e..c23f45d009 100644 --- a/kraken/lib/src/launcher/bundle.dart +++ b/kraken/lib/src/launcher/bundle.dart @@ -191,13 +191,13 @@ 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, additionalHttpHeaders }) + NetworkAssetBundle(Uri baseUrl, {int? contextId, Map? additionalHttpHeaders }) : _baseUrl = baseUrl, _additionalHttpHeaders = additionalHttpHeaders, httpClient = HttpClient(); + int? contextId; final Uri _baseUrl; - final int contextId; final HttpClient httpClient; final Map? _additionalHttpHeaders; ContentType contentType = ContentType.binary; @@ -211,7 +211,10 @@ class NetworkAssetBundle extends AssetBundle { if (_additionalHttpHeaders != null) { _additionalHttpHeaders?.forEach(request.headers.set); } - KrakenHttpOverrides.setContextHeader(request.headers, contextId); + + if (contextId != null) { + KrakenHttpOverrides.setContextHeader(request.headers, contextId!); + } final HttpClientResponse response = await request.close(); if (response.statusCode != HttpStatus.ok) diff --git a/kraken/lib/src/launcher/controller.dart b/kraken/lib/src/launcher/controller.dart index 0e1affc8f2..efe55c8d3e 100644 --- a/kraken/lib/src/launcher/controller.dart +++ b/kraken/lib/src/launcher/controller.dart @@ -8,9 +8,12 @@ import 'dart:collection'; import 'dart:ffi'; import 'dart:io'; import 'dart:typed_data'; +import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter/widgets.dart' show RouteInformation, WidgetsBinding, WidgetsBindingObserver; import 'package:kraken/bridge.dart'; import 'package:kraken/dom.dart'; import 'package:kraken/foundation.dart'; @@ -18,9 +21,13 @@ import 'package:kraken/gesture.dart'; import 'package:kraken/module.dart'; import 'package:kraken/rendering.dart'; import 'package:kraken/widget.dart'; +import 'package:kraken/src/dom/element_registry.dart' as element_registry; import 'bundle.dart'; +const int WINDOW_ID = -1; +const int DOCUMENT_ID = -2; + // Error handler when load bundle failed. typedef LoadHandler = void Function(KrakenController controller); typedef LoadErrorHandler = void Function(FlutterError error, StackTrace stack); @@ -55,7 +62,11 @@ abstract class DevToolsService { } // An kraken View Controller designed for multiple kraken view control. -class KrakenViewController { +class KrakenViewController implements WidgetsBindingObserver, ElementsBindingObserver { + + static Map> documentNativePtrMap = {}; + static Map> windowNativePtrMap = {}; + KrakenController rootController; // The methods of the KrakenNavigateDelegation help you implement custom behaviors that are triggered @@ -90,7 +101,6 @@ class KrakenViewController { this._viewportWidth, this._viewportHeight, { this.background, - this.showPerformanceOverlay, this.enableDebug = false, int? contextId, required this.rootController, @@ -98,16 +108,12 @@ class KrakenViewController { this.gestureListener, this.widgetDelegate, }) { - if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_VIEW_CONTROLLER_PROPERTY_INIT); - } - if (enableDebug) { debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; debugPaintSizeEnabled = true; } - if (kProfileMode) { + PerformanceTiming.instance().mark(PERF_VIEW_CONTROLLER_PROPERTY_INIT); PerformanceTiming.instance().mark(PERF_BRIDGE_INIT_START); } @@ -115,9 +121,6 @@ class KrakenViewController { if (kProfileMode) { PerformanceTiming.instance().mark(PERF_BRIDGE_INIT_END); - } - - if (kProfileMode) { PerformanceTiming.instance().mark(PERF_CREATE_VIEWPORT_START); } @@ -133,63 +136,98 @@ class KrakenViewController { PerformanceTiming.instance().mark(PERF_ELEMENT_MANAGER_INIT_START); } - _elementManager = ElementManager( - contextId: _contextId, + _setupObserver(); + + element_registry.defineBuiltInElements(); + + document = Document( + EventTargetContext(_contextId, documentNativePtrMap[_contextId]!), viewport: viewport, - showPerformanceOverlayOverride: showPerformanceOverlay, controller: rootController, gestureListener: gestureListener, widgetDelegate: widgetDelegate, ); + _setEventTarget(DOCUMENT_ID, document); + + window = Window(EventTargetContext(_contextId, windowNativePtrMap[_contextId]!), document); + _setEventTarget(WINDOW_ID ,window); + + // Listeners need to be registered to window in order to dispatch events on demand. + if (gestureListener != null) { + if (gestureListener!.onTouchStart != null) { + window.addEvent(EVENT_TOUCH_START); + } + + if (gestureListener!.onTouchMove != null) { + window.addEvent(EVENT_TOUCH_MOVE); + } + + if (gestureListener!.onTouchEnd != null) { + window.addEvent(EVENT_TOUCH_END); + } + } if (kProfileMode) { PerformanceTiming.instance().mark(PERF_ELEMENT_MANAGER_INIT_END); } } - // the manager which controller all renderObjects of Kraken - late ElementManager _elementManager; - ElementManager get elementManager => _elementManager; - - // index value which identify javascript runtime context. + // Index value which identify javascript runtime context. late int _contextId; int get contextId => _contextId; - // should render performanceOverlay layer into the screen for performance profile. - bool? showPerformanceOverlay; - - // print debug message when rendering. + // Enable print debug message when rendering. bool enableDebug; - // Kraken have already disposed + // Kraken have already disposed. bool _disposed = false; bool get disposed => _disposed; late RenderViewportBox viewport; + late Document document; + late Window window; void evaluateJavaScripts(String code, [String source = 'vm://']) { assert(!_disposed, 'Kraken have already disposed'); evaluateScripts(_contextId, code, source); } - // attach kraken's renderObject to an renderObject. - void attachView(RenderObject parent, [RenderObject? previousSibling]) { - _elementManager.attach(parent, previousSibling, showPerformanceOverlay: showPerformanceOverlay ?? false); + void _setupObserver() { + if (ElementsBinding.instance != null) { + ElementsBinding.instance!.addObserver(this); + } else if (WidgetsBinding.instance != null) { + WidgetsBinding.instance!.addObserver(this); + } } - Window? get window => getEventTargetById(WINDOW_ID) as Window?; + void _teardownObserver() { + if (ElementsBinding.instance != null) { + ElementsBinding.instance!.removeObserver(this); + } else if (WidgetsBinding.instance != null) { + WidgetsBinding.instance!.removeObserver(this); + } + } - Document? get document => getEventTargetById(DOCUMENT_ID) as Document?; + // Attach kraken's renderObject to an renderObject. + void attachTo(RenderObject parent, [RenderObject? previousSibling]) { + if (parent is ContainerRenderObjectMixin) { + parent.insert(document.renderer!, after: previousSibling); + } else if (parent is RenderObjectWithChildMixin) { + parent.child = document.renderer; + } + } - // dispose controller and recycle all resources. + // Dispose controller and recycle all resources. void dispose() { - // break circle reference - (_elementManager.getRootRenderBox() as RenderObjectWithControllerMixin).controller = null; + // FIXME: for break circle reference + viewport.controller = null; + + debugDOMTreeChanged = null; - detachView(); + _teardownObserver(); - // should clear previous page cached ui commands + // Should clear previous page cached ui commands clearUICommand(_contextId); disposeContext(_contextId); @@ -197,22 +235,52 @@ class KrakenViewController { // DisposeEventTarget command will created when js context disposed, should flush them all. flushUICommand(); - _elementManager.dispose(); + _clearTargets(); + document.dispose(); + window.dispose(); _disposed = true; } + Map _eventTargets = {}; + + T? _getEventTargetById(int targetId) { + EventTarget? target = _eventTargets[targetId]; + if (target is T) + return target as T; + else + return null; + } + + bool _existsTarget(int id) { + return _eventTargets.containsKey(id); + } + + void _removeTarget(int targetId) { + if (_eventTargets.containsKey(targetId)) { + _eventTargets.remove(targetId); + } + } + + void _setEventTarget(int targetId, EventTarget target) { + _eventTargets[targetId] = target; + } + + void _clearTargets() { + // Set current eventTargets to a new object, clean old targets by gc. + _eventTargets = {}; + } + // export Uint8List bytes from rendered result. - Future toImage(double devicePixelRatio, [int eventTargetId = HTML_ID]) { + Future toImage(double devicePixelRatio, [int? eventTargetId]) { assert(!_disposed, 'Kraken have already disposed'); Completer completer = Completer(); try { - if (!_elementManager.existsTarget(eventTargetId)) { + if (eventTargetId != null && !_existsTarget(eventTargetId)) { String msg = 'toImage: unknown node id: $eventTargetId'; completer.completeError(Exception(msg)); return completer.future; } - - var node = _elementManager.getEventTargetByTargetId(eventTargetId); + var node = eventTargetId == null ? document.documentElement : _getEventTargetById(eventTargetId); if (node is Element) { if (!node.isRendererAttached) { String msg = 'toImage: the element is not attached to document tree.'; @@ -236,34 +304,48 @@ class KrakenViewController { return completer.future; } - Element createElement(int id, Pointer nativePtr, String tagName) { + void createElement(int targetId, Pointer nativePtr, String tagName) { if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_CREATE_ELEMENT_START, uniqueId: id); + PerformanceTiming.instance().mark(PERF_CREATE_ELEMENT_START, uniqueId: targetId); } - Element result = _elementManager.createElement(id, nativePtr, tagName.toUpperCase(), null, null); + assert(!_existsTarget(targetId), 'ERROR: Can not create element with same id "$targetId"'); + Element element = document.createElement(tagName.toUpperCase(), EventTargetContext(_contextId, nativePtr)); + _setEventTarget(targetId, element); if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_CREATE_ELEMENT_END, uniqueId: id); + PerformanceTiming.instance().mark(PERF_CREATE_ELEMENT_END, uniqueId: targetId); } - return result; } - void createTextNode(int id, Pointer nativePtr, String data) { + void createTextNode(int targetId, Pointer nativePtr, String data) { if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_CREATE_TEXT_NODE_START, uniqueId: id); + PerformanceTiming.instance().mark(PERF_CREATE_TEXT_NODE_START, uniqueId: targetId); } - _elementManager.createTextNode(id, nativePtr, data); + TextNode textNode = document.createTextNode(data, EventTargetContext(_contextId, nativePtr)); + _setEventTarget(targetId, textNode); if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_CREATE_TEXT_NODE_END, uniqueId: id); + PerformanceTiming.instance().mark(PERF_CREATE_TEXT_NODE_END, uniqueId: targetId); } } - void createComment(int id, Pointer nativePtr) { + void createComment(int targetId, Pointer nativePtr) { if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_CREATE_COMMENT_START, uniqueId: id); + PerformanceTiming.instance().mark(PERF_CREATE_COMMENT_START, uniqueId: targetId); } - _elementManager.createComment(id, nativePtr); + Comment comment = document.createComment(EventTargetContext(_contextId, nativePtr)); + _setEventTarget(targetId, comment); if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_CREATE_COMMENT_END, uniqueId: id); + PerformanceTiming.instance().mark(PERF_CREATE_COMMENT_END, uniqueId: targetId); + } + } + + void createDocumentFragment(int targetId, Pointer nativePtr) { + if (kProfileMode) { + PerformanceTiming.instance().mark(PERF_CREATE_DOCUMENT_FRAGMENT_START, uniqueId: targetId); + } + DocumentFragment fragment = document.createDocumentFragment(EventTargetContext(_contextId, nativePtr)); + _setEventTarget(targetId, fragment); + if (kProfileMode) { + PerformanceTiming.instance().mark(PERF_CREATE_DOCUMENT_FRAGMENT_END, uniqueId: targetId); } } @@ -271,7 +353,17 @@ class KrakenViewController { if (kProfileMode) { PerformanceTiming.instance().mark(PERF_ADD_EVENT_START, uniqueId: targetId); } - _elementManager.addEvent(targetId, eventType); + if (!_existsTarget(targetId)) return; + EventTarget target = _getEventTargetById(targetId)!; + + if (target is Element) { + target.addEvent(eventType); + } else if (target is Window) { + target.addEvent(eventType); + } else if (target is Document) { + target.addEvent(eventType); + } + if (kProfileMode) { PerformanceTiming.instance().mark(PERF_ADD_EVENT_END, uniqueId: targetId); } @@ -281,19 +373,31 @@ class KrakenViewController { if (kProfileMode) { PerformanceTiming.instance().mark(PERF_REMOVE_EVENT_START, uniqueId: targetId); } - _elementManager.removeEvent(targetId, eventType); + assert(_existsTarget(targetId), 'targetId: $targetId event: $eventType'); + + Element target = _getEventTargetById(targetId)!; + + target.removeEvent(eventType); if (kProfileMode) { PerformanceTiming.instance().mark(PERF_REMOVE_EVENT_END, uniqueId: targetId); } } - void insertAdjacentNode(int targetId, String position, int childId) { - if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_INSERT_ADJACENT_NODE_START, uniqueId: targetId); - } - _elementManager.insertAdjacentNode(targetId, position, childId); - if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_INSERT_ADJACENT_NODE_END, uniqueId: targetId); + void cloneNode(int originalId, int newId) { + EventTarget originalTarget = _getEventTargetById(originalId)!; + EventTarget newTarget = _getEventTargetById(newId)!; + + // Current only element clone will process in dart. + if (originalTarget is Element) { + Element newElement = newTarget as Element; + // Copy inline style. + originalTarget.inlineStyle.forEach((key, value) { + newElement.setInlineStyle(key, value); + }); + // Copy element attributes. + originalTarget.properties.forEach((key, value) { + newElement.setProperty(key, value); + }); } } @@ -301,62 +405,159 @@ class KrakenViewController { if (kProfileMode) { PerformanceTiming.instance().mark(PERF_REMOVE_NODE_START, uniqueId: targetId); } - _elementManager.removeNode(targetId); + + assert(_existsTarget(targetId), 'targetId: $targetId'); + + Node target = _getEventTargetById(targetId)!; + target.parentNode?.removeChild(target); + + _debugDOMTreeChanged(); + if (kProfileMode) { PerformanceTiming.instance().mark(PERF_REMOVE_NODE_END, uniqueId: targetId); } } - void cloneNode(int oldId, int newId) { - _elementManager.cloneNode(oldId, newId); - } - - void setInlineStyle(int targetId, String key, String value) { + /// + ///

+ /// + /// foo + /// + ///

+ /// + void insertAdjacentNode(int targetId, String position, int newTargetId) { if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_SET_STYLE_START, uniqueId: targetId); + PerformanceTiming.instance().mark(PERF_INSERT_ADJACENT_NODE_START, uniqueId: targetId); } - _elementManager.setInlineStyle(targetId, key, value); - if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_SET_STYLE_END, uniqueId: targetId); + + assert(_existsTarget(targetId), 'targetId: $targetId position: $position newTargetId: $newTargetId'); + assert(_existsTarget(newTargetId), 'newTargetId: $newTargetId position: $position'); + + Node target = _getEventTargetById(targetId)!; + Node newNode = _getEventTargetById(newTargetId)!; + Node? targetParentNode = target.parentNode; + + switch (position) { + case 'beforebegin': + targetParentNode!.insertBefore(newNode, target); + break; + case 'afterbegin': + target.insertBefore(newNode, target.firstChild); + break; + case 'beforeend': + target.appendChild(newNode); + break; + case 'afterend': + if (targetParentNode!.lastChild == target) { + targetParentNode.appendChild(newNode); + } else { + targetParentNode.insertBefore( + newNode, + targetParentNode.childNodes[targetParentNode.childNodes.indexOf(target) + 1], + ); + } + break; } - } - void flushPendingStyleProperties(int targetId) { - _elementManager.flushPendingStyleProperties(targetId); + _debugDOMTreeChanged(); + + if (kProfileMode) { + PerformanceTiming.instance().mark(PERF_INSERT_ADJACENT_NODE_END, uniqueId: targetId); + } } - void setProperty(int targetId, String key, String value) { + void setProperty(int targetId, String key, dynamic value) { if (kProfileMode) { PerformanceTiming.instance().mark(PERF_SET_PROPERTIES_START, uniqueId: targetId); } - _elementManager.setProperty(targetId, key, value); + + assert(_existsTarget(targetId), 'targetId: $targetId key: $key value: $value'); + Node target = _getEventTargetById(targetId)!; + + if (target is Element) { + // Only Element has properties. + target.setProperty(key, value); + } else if (target is TextNode && key == 'data' || key == 'nodeValue') { + (target as TextNode).data = value; + } else { + debugPrint('Only element has properties, try setting $key to Node(#$targetId).'); + } + if (kProfileMode) { PerformanceTiming.instance().mark(PERF_SET_PROPERTIES_END, uniqueId: targetId); } } + dynamic getProperty(int targetId, String key) { + assert(_existsTarget(targetId), 'targetId: $targetId key: $key'); + Node target = _getEventTargetById(targetId)!; + + if (target is Element) { + // Only Element has properties + return target.getProperty(key); + } else if (target is TextNode && key == 'data' || key == 'nodeValue') { + return (target as TextNode).data; + } else { + return null; + } + } + void removeProperty(int targetId, String key) { if (kProfileMode) { PerformanceTiming.instance().mark(PERF_SET_PROPERTIES_START, uniqueId: targetId); } - _elementManager.removeProperty(targetId, key); + assert(_existsTarget(targetId), 'targetId: $targetId key: $key'); + Node target = _getEventTargetById(targetId)!; + + if (target is Element) { + target.removeProperty(key); + } else if (target is TextNode && key == 'data' || key == 'nodeValue') { + (target as TextNode).data = ''; + } else { + debugPrint('Only element has properties, try removing $key from Node(#$targetId).'); + } if (kProfileMode) { PerformanceTiming.instance().mark(PERF_SET_PROPERTIES_END, uniqueId: targetId); } } - void createDocumentFragment(int targetId, Pointer nativePtr) { + void setInlineStyle(int targetId, String key, dynamic value) { if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_CREATE_DOCUMENT_FRAGMENT_START, uniqueId: targetId); + PerformanceTiming.instance().mark(PERF_SET_STYLE_START, uniqueId: targetId); + } + assert(_existsTarget(targetId), 'id: $targetId key: $key value: $value'); + Node? target = _getEventTargetById(targetId); + if (target == null) return; + + if (target is Element) { + target.setInlineStyle(key, value); + } else { + debugPrint('Only element has style, try setting style.$key from Node(#$targetId).'); } - _elementManager.createDocumentFragment(targetId, nativePtr); if (kProfileMode) { - PerformanceTiming.instance().mark(PERF_CREATE_DOCUMENT_FRAGMENT_END, uniqueId: targetId); + PerformanceTiming.instance().mark(PERF_SET_STYLE_END, uniqueId: targetId); + } + } + + void flushPendingStyleProperties(int targetId) { + if (!_existsTarget(targetId)) return; + Node? target = _getEventTargetById(targetId); + if (target == null) return; + + if (target is Element) { + target.style.flushPendingProperties(); + } else { + debugPrint('Only element has style, try flushPendingStyleProperties from Node(#$targetId).'); } } - EventTarget? getEventTargetById(int id) { - return _elementManager.getEventTargetByTargetId(id); + // Hooks for DevTools. + VoidCallback? debugDOMTreeChanged; + void _debugDOMTreeChanged() { + VoidCallback? f = debugDOMTreeChanged; + if (f != null) { + f(); + } } Future handleNavigationAction(String? sourceUrl, String targetUrl, KrakenNavigationType navigationType) async { @@ -387,13 +588,98 @@ class KrakenViewController { } } - // detach renderObject from parent but keep everything in active. - void detachView() { - _elementManager.detach(); + // Call from JS Bridge before JS side eventTarget object been Garbage collected. + void disposeEventTarget(int targetId) { + Node? target = _getEventTargetById(targetId); + if (target == null) return; + + _removeTarget(targetId); + target.dispose(); } RenderObject getRootRenderObject() { - return _elementManager.getRootRenderBox(); + return viewport; + } + + @override + void didChangeAccessibilityFeatures() { + // TODO: implement didChangeAccessibilityFeatures + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + // TODO: implement didChangeAppLifecycleState + } + + @override + void didChangeLocales(List? locales) { + // TODO: implement didChangeLocales + } + + ui.WindowPadding _prevViewInsets = ui.window.viewInsets; + static double FOCUS_VIEWINSET_BOTTOM_OVERALL = 32; + + @override + void didChangeMetrics() { + double bottomInset = ui.window.viewInsets.bottom / ui.window.devicePixelRatio; + if (_prevViewInsets.bottom > ui.window.viewInsets.bottom) { + // Hide keyboard + viewport.bottomInset = bottomInset; + } else { + bool shouldScrollByToCenter = false; + InputElement? focusInputElement = InputElement.focusInputElement; + if (focusInputElement != null) { + RenderBox? renderer = focusInputElement.renderer; + if (renderer != null && renderer.hasSize) { + Offset focusOffset = renderer.localToGlobal(Offset.zero); + // FOCUS_VIEWINSET_BOTTOM_OVERALL to meet border case. + if (focusOffset.dy > viewportHeight - bottomInset - FOCUS_VIEWINSET_BOTTOM_OVERALL) { + shouldScrollByToCenter = true; + } + } + } + // Show keyboard + viewport.bottomInset = bottomInset; + if (shouldScrollByToCenter) { + SchedulerBinding.instance!.addPostFrameCallback((_) { + window.scrollBy(0, bottomInset); + }); + } + } + _prevViewInsets = ui.window.viewInsets; + } + + @override + void didChangePlatformBrightness() { + // TODO: implement didChangePlatformBrightness + } + + @override + void didChangeTextScaleFactor() { + // TODO: implement didChangeTextScaleFactor + } + + @override + void didHaveMemoryPressure() { + // TODO: implement didHaveMemoryPressure + } + + @override + Future didPopRoute() async { + // TODO: implement didPopRoute + return false; + } + + @override + Future didPushRoute(String route) async { + // TODO: implement didPushRoute + return false; + } + + @override + Future didPushRouteInformation(RouteInformation routeInformation) async { + // TODO: implement didPushRouteInformation + return false; } } @@ -500,7 +786,6 @@ class KrakenController { _view = KrakenViewController(viewportWidth, viewportHeight, background: background, - showPerformanceOverlay: showPerformanceOverlay, enableDebug: enableDebug, rootController: this, navigationDelegate: navigationDelegate ?? KrakenNavigationDelegate(), @@ -574,14 +859,8 @@ class KrakenController { Future unload() async { assert(!_view._disposed, 'Kraken have already disposed'); - RenderObject root = _view.getRootRenderObject(); - RenderObject? parent = root.parent as RenderObject?; - RenderObject? previousSibling; - if (parent is ContainerRenderObjectMixin) { - previousSibling = (root.parentData as ContainerParentDataMixin).previousSibling; - } _module.dispose(); - _view.detachView(); + _view.dispose(); // Should clear previous page cached ui commands clearUICommand(_view.contextId); @@ -596,14 +875,12 @@ class KrakenController { allocateNewContext(_view.contextId); - _view = KrakenViewController(view._elementManager.viewportWidth, view._elementManager.viewportHeight, + _view = KrakenViewController(view.viewportWidth, view.viewportHeight, background: _view.background, - showPerformanceOverlay: _view.showPerformanceOverlay, enableDebug: _view.enableDebug, contextId: _view.contextId, rootController: this, navigationDelegate: _view.navigationDelegate); - _view.attachView(parent!, previousSibling); _module = KrakenModuleController(this, _view.contextId); @@ -721,16 +998,14 @@ class KrakenController { // trigger DOMContentLoaded event module.requestAnimationFrame((_) { Event event = Event(EVENT_DOM_CONTENT_LOADED); - EventTarget? window = view.getEventTargetById(WINDOW_ID); - if (window != null) { + EventTarget window = view.window; + window.dispatchEvent(event); + // @HACK: window.load should trigger after all image had loaded. + // Someone needs to fix this in the future. + module.requestAnimationFrame((_) { + Event event = Event(EVENT_LOAD); window.dispatchEvent(event); - // @HACK: window.load should trigger after all image had loaded. - // Someone needs to fix this in the future. - module.requestAnimationFrame((_) { - Event event = Event(EVENT_LOAD); - window.dispatchEvent(event); - }); - } + }); }); if (onLoad != null) { diff --git a/kraken/lib/src/launcher/launcher.dart b/kraken/lib/src/launcher/launcher.dart index 53e50a6387..ec278d21a4 100644 --- a/kraken/lib/src/launcher/launcher.dart +++ b/kraken/lib/src/launcher/launcher.dart @@ -40,7 +40,7 @@ void launch({ httpClientInterceptor: httpClientInterceptor ); - controller.view.attachView(RendererBinding.instance!.renderView); + controller.view.attachTo(RendererBinding.instance!.renderView); await controller.loadBundle(bundle: bundle); diff --git a/kraken/lib/src/module/history.dart b/kraken/lib/src/module/history.dart index 380d9a6765..70f495de12 100644 --- a/kraken/lib/src/module/history.dart +++ b/kraken/lib/src/module/history.dart @@ -2,8 +2,6 @@ * Copyright (C) 2019-present Alibaba Inc. All rights reserved. * Author: Kraken Team. */ - - import 'dart:collection'; import 'dart:convert'; import 'dart:async'; @@ -110,7 +108,7 @@ class HistoryModule extends BaseModule { void _dispatchPopStateEvent(dynamic state) { PopStateEventInit init = PopStateEventInit(state); PopStateEvent popStateEvent = PopStateEvent(init); - moduleManager!.controller.view.window!.dispatchEvent(popStateEvent); + moduleManager!.controller.view.window.dispatchEvent(popStateEvent); } void _pushState(List params) { diff --git a/kraken/lib/widget.dart b/kraken/lib/widget.dart index bbc6cc4f9e..272a57be6e 100644 --- a/kraken/lib/widget.dart +++ b/kraken/lib/widget.dart @@ -4,7 +4,6 @@ */ import 'dart:io'; import 'dart:ui'; -import 'dart:ffi'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; @@ -20,8 +19,6 @@ import 'package:kraken/module.dart'; import 'package:kraken/gesture.dart'; import 'package:kraken/css.dart'; import 'package:kraken/src/dom/element_registry.dart'; -import 'package:kraken/src/dom/element_manager.dart'; -import 'package:kraken/bridge.dart'; /// Get context of current widget. typedef GetContext = BuildContext Function(); @@ -68,11 +65,9 @@ abstract class WidgetElement extends dom.Element { late BuildOwner _buildOwner; late Widget _widget; _KrakenAdapterWidgetPropertiesState? _propertiesState; - WidgetElement(int targetId, Pointer nativeEventTarget, dom.ElementManager elementManager) + WidgetElement(dom.EventTargetContext? context) : super( - targetId, - nativeEventTarget, - elementManager, + context, isIntrinsicBox: true, defaultStyle: _defaultStyle ); @@ -840,7 +835,7 @@ class _KrakenState extends State { RenderObject? _rootRenderObject = context.findRenderObject(); RenderViewportBox? renderViewportBox = _findRenderViewportBox(_rootRenderObject!); KrakenController controller = (renderViewportBox as RenderObjectWithControllerMixin).controller!; - dom.Element documentElement = controller.view.document!.documentElement; + dom.Element documentElement = controller.view.document.documentElement!; return documentElement; } @@ -946,14 +941,16 @@ This situation often happened when you trying creating kraken when FlutterView n double viewportWidth = _krakenWidget.viewportWidth ?? window.physicalSize.width / window.devicePixelRatio; double viewportHeight = _krakenWidget.viewportHeight ?? window.physicalSize.height / window.devicePixelRatio; + if (controller.view.document.documentElement == null) return; + if (viewportWidthHasChanged) { controller.view.viewportWidth = viewportWidth; - controller.view.document!.documentElement.renderStyle.width = CSSLengthValue(viewportWidth, CSSLengthType.PX); + controller.view.document.documentElement!.renderStyle.width = CSSLengthValue(viewportWidth, CSSLengthType.PX); } if (viewportHeightHasChanged) { controller.view.viewportHeight = viewportHeight; - controller.view.document!.documentElement.renderStyle.height = CSSLengthValue(viewportHeight, CSSLengthType.PX); + controller.view.document.documentElement!.renderStyle.height = CSSLengthValue(viewportHeight, CSSLengthType.PX); } } diff --git a/kraken/pubspec.yaml b/kraken/pubspec.yaml index 736a90c360..2301bde0c4 100644 --- a/kraken/pubspec.yaml +++ b/kraken/pubspec.yaml @@ -1,6 +1,6 @@ name: kraken description: A high-performance, web standards-compliant rendering engine. -version: 0.9.1-rc +version: 0.9.1-rc.1 homepage: https://openkraken.com environment: