mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-09 18:10:29 +00:00
Isolate extension WebView profiles and follow host proxy settings
This commit is contained in:
1 parent
cee5f3336e
commit
a0e84a75b1
556 files changed
+71097
-31
No files matched your search
@@ -0,0 +1,83 @@
|
||||
## 1.1.2
|
||||
|
||||
- Updated flutter_inappwebview_platform_interface version to ^1.3.0
|
||||
|
||||
## 1.1.1
|
||||
|
||||
- Updated flutter_inappwebview_platform_interface version to ^1.2.0
|
||||
|
||||
## 1.1.0+3
|
||||
|
||||
- Updated flutter_inappwebview_platform_interface version
|
||||
|
||||
## ## 1.1.0+2
|
||||
|
||||
- Updated pubspec.yaml
|
||||
|
||||
## 1.1.0+1
|
||||
|
||||
- Fixed "v6.1.0 fails to compile on Xcode 15" [#2288](https://github.com/pichillilorenzo/flutter_inappwebview/issues/2288)
|
||||
|
||||
## 1.1.0
|
||||
|
||||
- Added `InAppWebView` support
|
||||
- Added privacy manifest
|
||||
- Updates minimum supported SDK version to Flutter 3.24/Dart 3.5.
|
||||
- Fixed "[MACOS] launching InAppBrowser with 'hidden: true' calls onExit immediately" [#1939](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1939)
|
||||
- Fixed XCode 16 build
|
||||
|
||||
## 1.0.11
|
||||
|
||||
- Updated `flutter_inappwebview_platform_interface` version dependency to `^1.0.10`
|
||||
|
||||
## 1.0.10
|
||||
|
||||
- Updated `flutter_inappwebview_platform_interface` version dependency to `^1.0.9`
|
||||
- Fix typos and other code improvements (thanks to [michalsrutek](https://github.com/michalsrutek))
|
||||
- Fixed "runtime issue of SecTrustCopyExceptions 'This method should not be called on the main thread as it may lead to UI unresponsiveness.' when using onReceivedServerTrustAuthRequest" [#1924](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1924)
|
||||
|
||||
## 1.0.9
|
||||
|
||||
- Updated `flutter_inappwebview_platform_interface` version dependency to `^1.0.8`
|
||||
|
||||
## 1.0.8
|
||||
|
||||
- Updated `flutter_inappwebview_platform_interface` version dependency to `^1.0.7`
|
||||
- Implemented `InAppBrowser.onMainWindowWillClose` event
|
||||
|
||||
## 1.0.7
|
||||
|
||||
- Implemented `InAppWebViewSettings.interceptOnlyAsyncAjaxRequests`
|
||||
- Updated `useShouldInterceptAjaxRequest` automatic infer logic
|
||||
- Updated `CookieManager` methods return value
|
||||
- Fixed "iOS crash at public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)" [#1912](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1912)
|
||||
|
||||
## 1.0.6
|
||||
|
||||
- Fixed error in InterceptAjaxRequestJS 'Failed to set responseType property'
|
||||
- Fixed shouldInterceptAjaxRequest javascript code when overriding XMLHttpRequest.open method parameters
|
||||
|
||||
## 1.0.5
|
||||
|
||||
- Fixed "getFavicons: _TypeError: type '_Map<String, dynamic>' is not a subtype of type 'Iterable<dynamic>'" [#1897](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1897)
|
||||
|
||||
## 1.0.4
|
||||
|
||||
- Possible fix for "iOS Fatal Crash" [#1894](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1894)
|
||||
|
||||
## 1.0.3
|
||||
|
||||
- Call `super.dispose();` on `InAppBrowser` implementation
|
||||
|
||||
## 1.0.2
|
||||
|
||||
- Fixed "Cloudflare Turnstile failure" [#1738](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1738)
|
||||
|
||||
## 1.0.1
|
||||
|
||||
- Added `PlatformPrintJobController.onComplete` setter
|
||||
- Updated `flutter_inappwebview_platform_interface` version dependency to `^1.0.2`
|
||||
|
||||
## 1.0.0
|
||||
|
||||
Initial release.
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2023 Lorenzo Pichilli
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,13 @@
|
||||
# flutter\_inappwebview\_macos
|
||||
|
||||
The Apple macOS WKWebView implementation of [`flutter_inappwebview`](https://pub.dev/packages/flutter_inappwebview).
|
||||
|
||||
## Usage
|
||||
|
||||
This package is [endorsed](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin),
|
||||
which means you can simply use `flutter_inappwebview`
|
||||
normally. This package will be automatically included in your app when you do,
|
||||
so you do not need to add it to your `pubspec.yaml`.
|
||||
|
||||
However, if you `import` this package to use any of its APIs directly, you
|
||||
should add it to your `pubspec.yaml` as usual.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
library flutter_inappwebview_macos;
|
||||
|
||||
export 'src/main.dart';
|
||||
+433
@@ -0,0 +1,433 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
import 'in_app_webview/headless_in_app_webview.dart';
|
||||
import 'platform_util.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSCookieManager].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformCookieManagerCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSCookieManagerCreationParams
|
||||
extends PlatformCookieManagerCreationParams {
|
||||
/// Creates a new [MacOSCookieManagerCreationParams] instance.
|
||||
const MacOSCookieManagerCreationParams(
|
||||
// This parameter prevents breaking changes later.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformCookieManagerCreationParams params,
|
||||
) : super();
|
||||
|
||||
/// Creates a [MacOSCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams].
|
||||
factory MacOSCookieManagerCreationParams.fromPlatformCookieManagerCreationParams(
|
||||
PlatformCookieManagerCreationParams params) {
|
||||
return MacOSCookieManagerCreationParams(params);
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager}
|
||||
class MacOSCookieManager extends PlatformCookieManager with ChannelController {
|
||||
/// Creates a new [MacOSCookieManager].
|
||||
MacOSCookieManager(PlatformCookieManagerCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSCookieManagerCreationParams
|
||||
? params
|
||||
: MacOSCookieManagerCreationParams
|
||||
.fromPlatformCookieManagerCreationParams(params),
|
||||
) {
|
||||
channel = const MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_cookiemanager');
|
||||
handler = handleMethod;
|
||||
initMethodCallHandler();
|
||||
}
|
||||
|
||||
static MacOSCookieManager? _instance;
|
||||
|
||||
///Gets the [MacOSCookieManager] shared instance.
|
||||
static MacOSCookieManager instance() {
|
||||
return (_instance != null) ? _instance! : _init();
|
||||
}
|
||||
|
||||
static MacOSCookieManager _init() {
|
||||
_instance = MacOSCookieManager(MacOSCookieManagerCreationParams(
|
||||
const PlatformCookieManagerCreationParams()));
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {}
|
||||
|
||||
@override
|
||||
Future<bool> setCookie(
|
||||
{required WebUri url,
|
||||
required String name,
|
||||
required String value,
|
||||
String path = "/",
|
||||
String? domain,
|
||||
int? expiresDate,
|
||||
int? maxAge,
|
||||
bool? isSecure,
|
||||
bool? isHttpOnly,
|
||||
HTTPCookieSameSitePolicy? sameSite,
|
||||
@Deprecated("Use webViewController instead")
|
||||
PlatformInAppWebViewController? iosBelow11WebViewController,
|
||||
PlatformInAppWebViewController? webViewController}) async {
|
||||
webViewController = webViewController ?? iosBelow11WebViewController;
|
||||
|
||||
assert(url.toString().isNotEmpty);
|
||||
assert(name.isNotEmpty);
|
||||
assert(value.isNotEmpty);
|
||||
assert(path.isNotEmpty);
|
||||
|
||||
if (await _shouldUseJavascript()) {
|
||||
await _setCookieWithJavaScript(
|
||||
url: url,
|
||||
name: name,
|
||||
value: value,
|
||||
domain: domain,
|
||||
path: path,
|
||||
expiresDate: expiresDate,
|
||||
maxAge: maxAge,
|
||||
isSecure: isSecure,
|
||||
sameSite: sameSite,
|
||||
webViewController: webViewController);
|
||||
return true;
|
||||
}
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('url', () => url.toString());
|
||||
args.putIfAbsent('name', () => name);
|
||||
args.putIfAbsent('value', () => value);
|
||||
args.putIfAbsent('domain', () => domain);
|
||||
args.putIfAbsent('path', () => path);
|
||||
args.putIfAbsent('expiresDate', () => expiresDate?.toString());
|
||||
args.putIfAbsent('maxAge', () => maxAge);
|
||||
args.putIfAbsent('isSecure', () => isSecure);
|
||||
args.putIfAbsent('isHttpOnly', () => isHttpOnly);
|
||||
args.putIfAbsent('sameSite', () => sameSite?.toNativeValue());
|
||||
|
||||
return await channel?.invokeMethod<bool>('setCookie', args) ?? false;
|
||||
}
|
||||
|
||||
Future<void> _setCookieWithJavaScript(
|
||||
{required WebUri url,
|
||||
required String name,
|
||||
required String value,
|
||||
String path = "/",
|
||||
String? domain,
|
||||
int? expiresDate,
|
||||
int? maxAge,
|
||||
bool? isSecure,
|
||||
HTTPCookieSameSitePolicy? sameSite,
|
||||
PlatformInAppWebViewController? webViewController}) async {
|
||||
var cookieValue = name + "=" + value + "; Path=" + path;
|
||||
|
||||
if (domain != null) cookieValue += "; Domain=" + domain;
|
||||
|
||||
if (expiresDate != null)
|
||||
cookieValue += "; Expires=" + await _getCookieExpirationDate(expiresDate);
|
||||
|
||||
if (maxAge != null) cookieValue += "; Max-Age=" + maxAge.toString();
|
||||
|
||||
if (isSecure != null && isSecure) cookieValue += "; Secure";
|
||||
|
||||
if (sameSite != null)
|
||||
cookieValue += "; SameSite=" + sameSite.toNativeValue();
|
||||
|
||||
cookieValue += ";";
|
||||
|
||||
if (webViewController != null) {
|
||||
final javaScriptEnabled =
|
||||
(await webViewController.getSettings())?.javaScriptEnabled ?? false;
|
||||
if (javaScriptEnabled) {
|
||||
await webViewController.evaluateJavascript(
|
||||
source: 'document.cookie="$cookieValue"');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final setCookieCompleter = Completer<void>();
|
||||
final headlessWebView =
|
||||
MacOSHeadlessInAppWebView(MacOSHeadlessInAppWebViewCreationParams(
|
||||
initialUrlRequest: URLRequest(url: url),
|
||||
onLoadStop: (controller, url) async {
|
||||
await controller.evaluateJavascript(
|
||||
source: 'document.cookie="$cookieValue"');
|
||||
setCookieCompleter.complete();
|
||||
}));
|
||||
await headlessWebView.run();
|
||||
await setCookieCompleter.future;
|
||||
await headlessWebView.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Cookie>> getCookies(
|
||||
{required WebUri url,
|
||||
@Deprecated("Use webViewController instead")
|
||||
PlatformInAppWebViewController? iosBelow11WebViewController,
|
||||
PlatformInAppWebViewController? webViewController}) async {
|
||||
assert(url.toString().isNotEmpty);
|
||||
|
||||
webViewController = webViewController ?? iosBelow11WebViewController;
|
||||
|
||||
if (await _shouldUseJavascript()) {
|
||||
return await _getCookiesWithJavaScript(
|
||||
url: url, webViewController: webViewController);
|
||||
}
|
||||
|
||||
List<Cookie> cookies = [];
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('url', () => url.toString());
|
||||
List<dynamic> cookieListMap =
|
||||
await channel?.invokeMethod<List>('getCookies', args) ?? [];
|
||||
cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>();
|
||||
|
||||
cookieListMap.forEach((cookieMap) {
|
||||
cookies.add(Cookie(
|
||||
name: cookieMap["name"],
|
||||
value: cookieMap["value"],
|
||||
expiresDate: cookieMap["expiresDate"],
|
||||
isSessionOnly: cookieMap["isSessionOnly"],
|
||||
domain: cookieMap["domain"],
|
||||
sameSite:
|
||||
HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]),
|
||||
isSecure: cookieMap["isSecure"],
|
||||
isHttpOnly: cookieMap["isHttpOnly"],
|
||||
path: cookieMap["path"]));
|
||||
});
|
||||
return cookies;
|
||||
}
|
||||
|
||||
Future<List<Cookie>> _getCookiesWithJavaScript(
|
||||
{required WebUri url,
|
||||
PlatformInAppWebViewController? webViewController}) async {
|
||||
assert(url.toString().isNotEmpty);
|
||||
|
||||
List<Cookie> cookies = [];
|
||||
|
||||
if (webViewController != null) {
|
||||
final javaScriptEnabled =
|
||||
(await webViewController.getSettings())?.javaScriptEnabled ?? false;
|
||||
if (javaScriptEnabled) {
|
||||
List<String> documentCookies = (await webViewController
|
||||
.evaluateJavascript(source: 'document.cookie') as String)
|
||||
.split(';')
|
||||
.map((documentCookie) => documentCookie.trim())
|
||||
.toList();
|
||||
documentCookies.forEach((documentCookie) {
|
||||
List<String> cookie = documentCookie.split('=');
|
||||
if (cookie.length > 1) {
|
||||
cookies.add(Cookie(
|
||||
name: cookie[0],
|
||||
value: cookie[1],
|
||||
));
|
||||
}
|
||||
});
|
||||
return cookies;
|
||||
}
|
||||
}
|
||||
|
||||
final pageLoaded = Completer<void>();
|
||||
final headlessWebView =
|
||||
MacOSHeadlessInAppWebView(MacOSHeadlessInAppWebViewCreationParams(
|
||||
initialUrlRequest: URLRequest(url: url),
|
||||
onLoadStop: (controller, url) async {
|
||||
pageLoaded.complete();
|
||||
},
|
||||
));
|
||||
await headlessWebView.run();
|
||||
await pageLoaded.future;
|
||||
|
||||
List<String> documentCookies = (await headlessWebView.webViewController!
|
||||
.evaluateJavascript(source: 'document.cookie') as String)
|
||||
.split(';')
|
||||
.map((documentCookie) => documentCookie.trim())
|
||||
.toList();
|
||||
documentCookies.forEach((documentCookie) {
|
||||
List<String> cookie = documentCookie.split('=');
|
||||
if (cookie.length > 1) {
|
||||
cookies.add(Cookie(
|
||||
name: cookie[0],
|
||||
value: cookie[1],
|
||||
));
|
||||
}
|
||||
});
|
||||
await headlessWebView.dispose();
|
||||
return cookies;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Cookie?> getCookie(
|
||||
{required WebUri url,
|
||||
required String name,
|
||||
@Deprecated("Use webViewController instead")
|
||||
PlatformInAppWebViewController? iosBelow11WebViewController,
|
||||
PlatformInAppWebViewController? webViewController}) async {
|
||||
assert(url.toString().isNotEmpty);
|
||||
assert(name.isNotEmpty);
|
||||
|
||||
webViewController = webViewController ?? iosBelow11WebViewController;
|
||||
|
||||
if (await _shouldUseJavascript()) {
|
||||
List<Cookie> cookies = await _getCookiesWithJavaScript(
|
||||
url: url, webViewController: webViewController);
|
||||
return cookies
|
||||
.cast<Cookie?>()
|
||||
.firstWhere((cookie) => cookie!.name == name, orElse: () => null);
|
||||
}
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('url', () => url.toString());
|
||||
List<dynamic> cookies =
|
||||
await channel?.invokeMethod<List>('getCookies', args) ?? [];
|
||||
cookies = cookies.cast<Map<dynamic, dynamic>>();
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
cookies[i] = cookies[i].cast<String, dynamic>();
|
||||
if (cookies[i]["name"] == name)
|
||||
return Cookie(
|
||||
name: cookies[i]["name"],
|
||||
value: cookies[i]["value"],
|
||||
expiresDate: cookies[i]["expiresDate"],
|
||||
isSessionOnly: cookies[i]["isSessionOnly"],
|
||||
domain: cookies[i]["domain"],
|
||||
sameSite: HTTPCookieSameSitePolicy.fromNativeValue(
|
||||
cookies[i]["sameSite"]),
|
||||
isSecure: cookies[i]["isSecure"],
|
||||
isHttpOnly: cookies[i]["isHttpOnly"],
|
||||
path: cookies[i]["path"]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> deleteCookie(
|
||||
{required WebUri url,
|
||||
required String name,
|
||||
String path = "/",
|
||||
String? domain,
|
||||
@Deprecated("Use webViewController instead")
|
||||
PlatformInAppWebViewController? iosBelow11WebViewController,
|
||||
PlatformInAppWebViewController? webViewController}) async {
|
||||
assert(url.toString().isNotEmpty);
|
||||
assert(name.isNotEmpty);
|
||||
|
||||
webViewController = webViewController ?? iosBelow11WebViewController;
|
||||
|
||||
if (await _shouldUseJavascript()) {
|
||||
await _setCookieWithJavaScript(
|
||||
url: url,
|
||||
name: name,
|
||||
value: "",
|
||||
path: path,
|
||||
domain: domain,
|
||||
maxAge: -1,
|
||||
webViewController: webViewController);
|
||||
return true;
|
||||
}
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('url', () => url.toString());
|
||||
args.putIfAbsent('name', () => name);
|
||||
args.putIfAbsent('domain', () => domain);
|
||||
args.putIfAbsent('path', () => path);
|
||||
return await channel?.invokeMethod<bool>('deleteCookie', args) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> deleteCookies(
|
||||
{required WebUri url,
|
||||
String path = "/",
|
||||
String? domain,
|
||||
@Deprecated("Use webViewController instead")
|
||||
PlatformInAppWebViewController? iosBelow11WebViewController,
|
||||
PlatformInAppWebViewController? webViewController}) async {
|
||||
assert(url.toString().isNotEmpty);
|
||||
|
||||
webViewController = webViewController ?? iosBelow11WebViewController;
|
||||
|
||||
if (await _shouldUseJavascript()) {
|
||||
List<Cookie> cookies = await _getCookiesWithJavaScript(
|
||||
url: url, webViewController: webViewController);
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
await _setCookieWithJavaScript(
|
||||
url: url,
|
||||
name: cookies[i].name,
|
||||
value: "",
|
||||
path: path,
|
||||
domain: domain,
|
||||
maxAge: -1,
|
||||
webViewController: webViewController);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('url', () => url.toString());
|
||||
args.putIfAbsent('domain', () => domain);
|
||||
args.putIfAbsent('path', () => path);
|
||||
return await channel?.invokeMethod<bool>('deleteCookies', args) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> deleteAllCookies() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await channel?.invokeMethod<bool>('deleteAllCookies', args) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Cookie>> getAllCookies() async {
|
||||
List<Cookie> cookies = [];
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
List<dynamic> cookieListMap =
|
||||
await channel?.invokeMethod<List>('getAllCookies', args) ?? [];
|
||||
cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>();
|
||||
|
||||
cookieListMap.forEach((cookieMap) {
|
||||
cookies.add(Cookie(
|
||||
name: cookieMap["name"],
|
||||
value: cookieMap["value"],
|
||||
expiresDate: cookieMap["expiresDate"],
|
||||
isSessionOnly: cookieMap["isSessionOnly"],
|
||||
domain: cookieMap["domain"],
|
||||
sameSite:
|
||||
HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]),
|
||||
isSecure: cookieMap["isSecure"],
|
||||
isHttpOnly: cookieMap["isHttpOnly"],
|
||||
path: cookieMap["path"]));
|
||||
});
|
||||
return cookies;
|
||||
}
|
||||
|
||||
Future<String> _getCookieExpirationDate(int expiresDate) async {
|
||||
var platformUtil = PlatformUtil.instance();
|
||||
var dateTime = DateTime.fromMillisecondsSinceEpoch(expiresDate).toUtc();
|
||||
return !kIsWeb
|
||||
? await platformUtil.formatDate(
|
||||
date: dateTime,
|
||||
format: 'EEE, dd MMM yyyy hh:mm:ss z',
|
||||
locale: 'en_US',
|
||||
timezone: 'GMT')
|
||||
: await platformUtil.getWebCookieExpirationDate(date: dateTime);
|
||||
}
|
||||
|
||||
Future<bool> _shouldUseJavascript() async {
|
||||
final platformUtil = PlatformUtil.instance();
|
||||
final systemVersion = await platformUtil.getSystemVersion();
|
||||
return systemVersion.compareTo("11") == -1;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
extension InternalCookieManager on MacOSCookieManager {
|
||||
get handleMethod => _handleMethod;
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSFindInteractionController].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformFindInteractionControllerCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSFindInteractionControllerCreationParams
|
||||
extends PlatformFindInteractionControllerCreationParams {
|
||||
/// Creates a new [MacOSFindInteractionControllerCreationParams] instance.
|
||||
const MacOSFindInteractionControllerCreationParams(
|
||||
{super.onFindResultReceived});
|
||||
|
||||
/// Creates a [MacOSFindInteractionControllerCreationParams] instance based on [PlatformFindInteractionControllerCreationParams].
|
||||
factory MacOSFindInteractionControllerCreationParams.fromPlatformFindInteractionControllerCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformFindInteractionControllerCreationParams params) {
|
||||
return MacOSFindInteractionControllerCreationParams(
|
||||
onFindResultReceived: params.onFindResultReceived);
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController}
|
||||
class MacOSFindInteractionController extends PlatformFindInteractionController
|
||||
with ChannelController {
|
||||
/// Constructs a [MacOSFindInteractionController].
|
||||
MacOSFindInteractionController(
|
||||
PlatformFindInteractionControllerCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSFindInteractionControllerCreationParams
|
||||
? params
|
||||
: MacOSFindInteractionControllerCreationParams
|
||||
.fromPlatformFindInteractionControllerCreationParams(params),
|
||||
);
|
||||
|
||||
_debugLog(String method, dynamic args) {
|
||||
debugLog(
|
||||
className: this.runtimeType.toString(),
|
||||
debugLoggingSettings:
|
||||
PlatformFindInteractionController.debugLoggingSettings,
|
||||
method: method,
|
||||
args: args);
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
_debugLog(call.method, call.arguments);
|
||||
|
||||
switch (call.method) {
|
||||
case "onFindResultReceived":
|
||||
if (onFindResultReceived != null) {
|
||||
int activeMatchOrdinal = call.arguments["activeMatchOrdinal"];
|
||||
int numberOfMatches = call.arguments["numberOfMatches"];
|
||||
bool isDoneCounting = call.arguments["isDoneCounting"];
|
||||
onFindResultReceived!(
|
||||
this, activeMatchOrdinal, numberOfMatches, isDoneCounting);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.findAll}
|
||||
Future<void> findAll({String? find}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('find', () => find);
|
||||
await channel?.invokeMethod('findAll', args);
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.findNext}
|
||||
Future<void> findNext({bool forward = true}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('forward', () => forward);
|
||||
await channel?.invokeMethod('findNext', args);
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.clearMatches}
|
||||
Future<void> clearMatches() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await channel?.invokeMethod('clearMatches', args);
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.setSearchText}
|
||||
Future<void> setSearchText(String? searchText) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('searchText', () => searchText);
|
||||
await channel?.invokeMethod('setSearchText', args);
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.getSearchText}
|
||||
Future<String?> getSearchText() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await channel?.invokeMethod<String?>('getSearchText', args);
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.getActiveFindSession}
|
||||
Future<FindSession?> getActiveFindSession() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
Map<String, dynamic>? result =
|
||||
(await channel?.invokeMethod('getActiveFindSession', args))
|
||||
?.cast<String, dynamic>();
|
||||
return FindSession.fromMap(result);
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.dispose}
|
||||
@override
|
||||
void dispose({bool isKeepAlive = false}) {
|
||||
disposeChannel(removeMethodCallHandler: !isKeepAlive);
|
||||
}
|
||||
}
|
||||
|
||||
extension InternalFindInteractionController on MacOSFindInteractionController {
|
||||
void init(dynamic id) {
|
||||
channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id');
|
||||
handler = _handleMethod;
|
||||
initMethodCallHandler();
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export 'find_interaction_controller.dart'
|
||||
hide InternalFindInteractionController;
|
||||
Vendored
Executable
+155
@@ -0,0 +1,155 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSHttpAuthCredentialDatabase].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformHttpAuthCredentialDatabaseCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSHttpAuthCredentialDatabaseCreationParams
|
||||
extends PlatformHttpAuthCredentialDatabaseCreationParams {
|
||||
/// Creates a new [MacOSHttpAuthCredentialDatabaseCreationParams] instance.
|
||||
const MacOSHttpAuthCredentialDatabaseCreationParams(
|
||||
// This parameter prevents breaking changes later.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformHttpAuthCredentialDatabaseCreationParams params,
|
||||
) : super();
|
||||
|
||||
/// Creates a [MacOSHttpAuthCredentialDatabaseCreationParams] instance based on [PlatformHttpAuthCredentialDatabaseCreationParams].
|
||||
factory MacOSHttpAuthCredentialDatabaseCreationParams.fromPlatformHttpAuthCredentialDatabaseCreationParams(
|
||||
PlatformHttpAuthCredentialDatabaseCreationParams params) {
|
||||
return MacOSHttpAuthCredentialDatabaseCreationParams(params);
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformHttpAuthCredentialDatabase}
|
||||
class MacOSHttpAuthCredentialDatabase extends PlatformHttpAuthCredentialDatabase
|
||||
with ChannelController {
|
||||
/// Creates a new [MacOSHttpAuthCredentialDatabase].
|
||||
MacOSHttpAuthCredentialDatabase(
|
||||
PlatformHttpAuthCredentialDatabaseCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSHttpAuthCredentialDatabaseCreationParams
|
||||
? params
|
||||
: MacOSHttpAuthCredentialDatabaseCreationParams
|
||||
.fromPlatformHttpAuthCredentialDatabaseCreationParams(params),
|
||||
) {
|
||||
channel = const MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_credential_database');
|
||||
handler = handleMethod;
|
||||
initMethodCallHandler();
|
||||
}
|
||||
|
||||
static MacOSHttpAuthCredentialDatabase? _instance;
|
||||
|
||||
///Gets the database shared instance.
|
||||
static MacOSHttpAuthCredentialDatabase instance() {
|
||||
return (_instance != null) ? _instance! : _init();
|
||||
}
|
||||
|
||||
static MacOSHttpAuthCredentialDatabase _init() {
|
||||
_instance = MacOSHttpAuthCredentialDatabase(
|
||||
MacOSHttpAuthCredentialDatabaseCreationParams(
|
||||
const PlatformHttpAuthCredentialDatabaseCreationParams()));
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {}
|
||||
|
||||
@override
|
||||
Future<List<URLProtectionSpaceHttpAuthCredentials>>
|
||||
getAllAuthCredentials() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
List<dynamic> allCredentials =
|
||||
await channel?.invokeMethod<List>('getAllAuthCredentials', args) ?? [];
|
||||
|
||||
List<URLProtectionSpaceHttpAuthCredentials> result = [];
|
||||
|
||||
for (Map<dynamic, dynamic> map in allCredentials) {
|
||||
var element = URLProtectionSpaceHttpAuthCredentials.fromMap(
|
||||
map.cast<String, dynamic>());
|
||||
if (element != null) {
|
||||
result.add(element);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<URLCredential>> getHttpAuthCredentials(
|
||||
{required URLProtectionSpace protectionSpace}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("host", () => protectionSpace.host);
|
||||
args.putIfAbsent("protocol", () => protectionSpace.protocol);
|
||||
args.putIfAbsent("realm", () => protectionSpace.realm);
|
||||
args.putIfAbsent("port", () => protectionSpace.port);
|
||||
List<dynamic> credentialList =
|
||||
await channel?.invokeMethod<List>('getHttpAuthCredentials', args) ?? [];
|
||||
List<URLCredential> credentials = [];
|
||||
for (Map<dynamic, dynamic> map in credentialList) {
|
||||
var credential = URLCredential.fromMap(map.cast<String, dynamic>());
|
||||
if (credential != null) {
|
||||
credentials.add(credential);
|
||||
}
|
||||
}
|
||||
return credentials;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setHttpAuthCredential(
|
||||
{required URLProtectionSpace protectionSpace,
|
||||
required URLCredential credential}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("host", () => protectionSpace.host);
|
||||
args.putIfAbsent("protocol", () => protectionSpace.protocol);
|
||||
args.putIfAbsent("realm", () => protectionSpace.realm);
|
||||
args.putIfAbsent("port", () => protectionSpace.port);
|
||||
args.putIfAbsent("username", () => credential.username);
|
||||
args.putIfAbsent("password", () => credential.password);
|
||||
await channel?.invokeMethod('setHttpAuthCredential', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeHttpAuthCredential(
|
||||
{required URLProtectionSpace protectionSpace,
|
||||
required URLCredential credential}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("host", () => protectionSpace.host);
|
||||
args.putIfAbsent("protocol", () => protectionSpace.protocol);
|
||||
args.putIfAbsent("realm", () => protectionSpace.realm);
|
||||
args.putIfAbsent("port", () => protectionSpace.port);
|
||||
args.putIfAbsent("username", () => credential.username);
|
||||
args.putIfAbsent("password", () => credential.password);
|
||||
await channel?.invokeMethod('removeHttpAuthCredential', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeHttpAuthCredentials(
|
||||
{required URLProtectionSpace protectionSpace}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("host", () => protectionSpace.host);
|
||||
args.putIfAbsent("protocol", () => protectionSpace.protocol);
|
||||
args.putIfAbsent("realm", () => protectionSpace.realm);
|
||||
args.putIfAbsent("port", () => protectionSpace.port);
|
||||
await channel?.invokeMethod('removeHttpAuthCredentials', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clearAllAuthCredentials() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await channel?.invokeMethod('clearAllAuthCredentials', args);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
extension InternalHttpAuthCredentialDatabase
|
||||
on MacOSHttpAuthCredentialDatabase {
|
||||
get handleMethod => _handleMethod;
|
||||
}
|
||||
Vendored
Executable
+373
@@ -0,0 +1,373 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
import '../find_interaction/find_interaction_controller.dart';
|
||||
import '../in_app_webview/in_app_webview_controller.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSInAppBrowser].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformInAppBrowserCreationParams] for
|
||||
/// more information.
|
||||
class MacOSInAppBrowserCreationParams
|
||||
extends PlatformInAppBrowserCreationParams {
|
||||
/// Creates a new [MacOSInAppBrowserCreationParams] instance.
|
||||
MacOSInAppBrowserCreationParams(
|
||||
{super.contextMenu,
|
||||
super.pullToRefreshController,
|
||||
this.findInteractionController,
|
||||
super.initialUserScripts,
|
||||
super.windowId});
|
||||
|
||||
/// Creates a [MacOSInAppBrowserCreationParams] instance based on [PlatformInAppBrowserCreationParams].
|
||||
factory MacOSInAppBrowserCreationParams.fromPlatformInAppBrowserCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformInAppBrowserCreationParams params) {
|
||||
return MacOSInAppBrowserCreationParams(
|
||||
contextMenu: params.contextMenu,
|
||||
pullToRefreshController: params.pullToRefreshController,
|
||||
findInteractionController:
|
||||
params.findInteractionController as MacOSFindInteractionController?,
|
||||
initialUserScripts: params.initialUserScripts,
|
||||
windowId: params.windowId);
|
||||
}
|
||||
|
||||
@override
|
||||
final MacOSFindInteractionController? findInteractionController;
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser}
|
||||
class MacOSInAppBrowser extends PlatformInAppBrowser with ChannelController {
|
||||
@override
|
||||
final String id = IdGenerator.generate();
|
||||
|
||||
/// Constructs a [MacOSInAppBrowser].
|
||||
MacOSInAppBrowser(PlatformInAppBrowserCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSInAppBrowserCreationParams
|
||||
? params
|
||||
: MacOSInAppBrowserCreationParams
|
||||
.fromPlatformInAppBrowserCreationParams(params),
|
||||
) {
|
||||
_contextMenu = params.contextMenu;
|
||||
}
|
||||
|
||||
static final MacOSInAppBrowser _staticValue =
|
||||
MacOSInAppBrowser(MacOSInAppBrowserCreationParams());
|
||||
|
||||
/// Provide static access.
|
||||
factory MacOSInAppBrowser.static() {
|
||||
return _staticValue;
|
||||
}
|
||||
|
||||
MacOSInAppBrowserCreationParams get _macosParams =>
|
||||
params as MacOSInAppBrowserCreationParams;
|
||||
|
||||
static const MethodChannel _staticChannel =
|
||||
const MethodChannel('com.pichillilorenzo/flutter_inappbrowser');
|
||||
|
||||
ContextMenu? _contextMenu;
|
||||
|
||||
@override
|
||||
ContextMenu? get contextMenu => _contextMenu;
|
||||
|
||||
Map<int, InAppBrowserMenuItem> _menuItems = HashMap();
|
||||
bool _isOpened = false;
|
||||
MacOSInAppWebViewController? _webViewController;
|
||||
|
||||
@override
|
||||
MacOSInAppWebViewController? get webViewController {
|
||||
return _isOpened ? _webViewController : null;
|
||||
}
|
||||
|
||||
_init() {
|
||||
channel = MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$id');
|
||||
handler = _handleMethod;
|
||||
initMethodCallHandler();
|
||||
|
||||
_webViewController = MacOSInAppWebViewController.fromInAppBrowser(
|
||||
MacOSInAppWebViewControllerCreationParams(id: id),
|
||||
channel!,
|
||||
this,
|
||||
this.initialUserScripts);
|
||||
_macosParams.findInteractionController?.init(id);
|
||||
}
|
||||
|
||||
_debugLog(String method, dynamic args) {
|
||||
debugLog(
|
||||
className: this.runtimeType.toString(),
|
||||
id: id,
|
||||
debugLoggingSettings: PlatformInAppBrowser.debugLoggingSettings,
|
||||
method: method,
|
||||
args: args);
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "onBrowserCreated":
|
||||
_debugLog(call.method, call.arguments);
|
||||
eventHandler?.onBrowserCreated();
|
||||
break;
|
||||
case "onMenuItemClicked":
|
||||
_debugLog(call.method, call.arguments);
|
||||
int id = call.arguments["id"].toInt();
|
||||
if (this._menuItems[id] != null) {
|
||||
if (this._menuItems[id]?.onClick != null) {
|
||||
this._menuItems[id]?.onClick!();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "onMainWindowWillClose":
|
||||
_debugLog(call.method, call.arguments);
|
||||
eventHandler?.onMainWindowWillClose();
|
||||
break;
|
||||
case "onExit":
|
||||
_debugLog(call.method, call.arguments);
|
||||
_isOpened = false;
|
||||
final onExit = eventHandler?.onExit;
|
||||
dispose();
|
||||
onExit?.call();
|
||||
break;
|
||||
default:
|
||||
return _webViewController?.handleMethod(call);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _prepareOpenRequest(
|
||||
{@Deprecated('Use settings instead') InAppBrowserClassOptions? options,
|
||||
InAppBrowserClassSettings? settings}) {
|
||||
assert(!_isOpened, 'The browser is already opened.');
|
||||
_isOpened = true;
|
||||
_init();
|
||||
|
||||
var initialSettings = settings?.toMap() ??
|
||||
options?.toMap() ??
|
||||
InAppBrowserClassSettings().toMap();
|
||||
|
||||
Map<String, dynamic> pullToRefreshSettings =
|
||||
pullToRefreshController?.settings.toMap() ??
|
||||
pullToRefreshController?.options.toMap() ??
|
||||
PullToRefreshSettings(enabled: false).toMap();
|
||||
|
||||
List<Map<String, dynamic>> menuItemList = [];
|
||||
_menuItems.forEach((key, value) {
|
||||
menuItemList.add(value.toMap());
|
||||
});
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('id', () => id);
|
||||
args.putIfAbsent('settings', () => initialSettings);
|
||||
args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {});
|
||||
args.putIfAbsent('windowId', () => windowId);
|
||||
args.putIfAbsent('initialUserScripts',
|
||||
() => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
|
||||
args.putIfAbsent('pullToRefreshSettings', () => pullToRefreshSettings);
|
||||
args.putIfAbsent('menuItems', () => menuItemList);
|
||||
return args;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> openUrlRequest(
|
||||
{required URLRequest urlRequest,
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
@Deprecated('Use settings instead') InAppBrowserClassOptions? options,
|
||||
InAppBrowserClassSettings? settings}) async {
|
||||
assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty);
|
||||
|
||||
Map<String, dynamic> args =
|
||||
_prepareOpenRequest(options: options, settings: settings);
|
||||
args.putIfAbsent('urlRequest', () => urlRequest.toMap());
|
||||
await _staticChannel.invokeMethod('open', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> openFile(
|
||||
{required String assetFilePath,
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
@Deprecated('Use settings instead') InAppBrowserClassOptions? options,
|
||||
InAppBrowserClassSettings? settings}) async {
|
||||
assert(assetFilePath.isNotEmpty);
|
||||
|
||||
Map<String, dynamic> args =
|
||||
_prepareOpenRequest(options: options, settings: settings);
|
||||
args.putIfAbsent('assetFilePath', () => assetFilePath);
|
||||
await _staticChannel.invokeMethod('open', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> openData(
|
||||
{required String data,
|
||||
String mimeType = "text/html",
|
||||
String encoding = "utf8",
|
||||
WebUri? baseUrl,
|
||||
@Deprecated("Use historyUrl instead") Uri? androidHistoryUrl,
|
||||
WebUri? historyUrl,
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
@Deprecated('Use settings instead') InAppBrowserClassOptions? options,
|
||||
InAppBrowserClassSettings? settings}) async {
|
||||
Map<String, dynamic> args =
|
||||
_prepareOpenRequest(options: options, settings: settings);
|
||||
args.putIfAbsent('data', () => data);
|
||||
args.putIfAbsent('mimeType', () => mimeType);
|
||||
args.putIfAbsent('encoding', () => encoding);
|
||||
args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank");
|
||||
args.putIfAbsent('historyUrl',
|
||||
() => (historyUrl ?? androidHistoryUrl)?.toString() ?? "about:blank");
|
||||
await _staticChannel.invokeMethod('open', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> openWithSystemBrowser({required WebUri url}) async {
|
||||
assert(url.toString().isNotEmpty);
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('url', () => url.toString());
|
||||
return await _staticChannel.invokeMethod('openWithSystemBrowser', args);
|
||||
}
|
||||
|
||||
@override
|
||||
void addMenuItem(InAppBrowserMenuItem menuItem) {
|
||||
_menuItems[menuItem.id] = menuItem;
|
||||
}
|
||||
|
||||
@override
|
||||
void addMenuItems(List<InAppBrowserMenuItem> menuItems) {
|
||||
menuItems.forEach((menuItem) {
|
||||
_menuItems[menuItem.id] = menuItem;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
bool removeMenuItem(InAppBrowserMenuItem menuItem) {
|
||||
return _menuItems.remove(menuItem.id) != null;
|
||||
}
|
||||
|
||||
@override
|
||||
void removeMenuItems(List<InAppBrowserMenuItem> menuItems) {
|
||||
for (final menuItem in menuItems) {
|
||||
removeMenuItem(menuItem);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void removeAllMenuItem() {
|
||||
_menuItems.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
bool hasMenuItem(InAppBrowserMenuItem menuItem) {
|
||||
return _menuItems.containsKey(menuItem.id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> show() async {
|
||||
assert(_isOpened, 'The browser is not opened.');
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await channel?.invokeMethod('show', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> hide() async {
|
||||
assert(_isOpened, 'The browser is not opened.');
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await channel?.invokeMethod('hide', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
assert(_isOpened, 'The browser is not opened.');
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await channel?.invokeMethod('close', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isHidden() async {
|
||||
assert(_isOpened, 'The browser is not opened.');
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await channel?.invokeMethod<bool>('isHidden', args) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
@Deprecated('Use setSettings instead')
|
||||
Future<void> setOptions({required InAppBrowserClassOptions options}) async {
|
||||
assert(_isOpened, 'The browser is not opened.');
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('settings', () => options.toMap());
|
||||
await channel?.invokeMethod('setSettings', args);
|
||||
}
|
||||
|
||||
@override
|
||||
@Deprecated('Use getSettings instead')
|
||||
Future<InAppBrowserClassOptions?> getOptions() async {
|
||||
assert(_isOpened, 'The browser is not opened.');
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
|
||||
Map<dynamic, dynamic>? options =
|
||||
await channel?.invokeMethod('getSettings', args);
|
||||
if (options != null) {
|
||||
options = options.cast<String, dynamic>();
|
||||
return InAppBrowserClassOptions.fromMap(options as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setSettings(
|
||||
{required InAppBrowserClassSettings settings}) async {
|
||||
assert(_isOpened, 'The browser is not opened.');
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('settings', () => settings.toMap());
|
||||
await channel?.invokeMethod('setSettings', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<InAppBrowserClassSettings?> getSettings() async {
|
||||
assert(_isOpened, 'The browser is not opened.');
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
|
||||
Map<dynamic, dynamic>? settings =
|
||||
await channel?.invokeMethod('getSettings', args);
|
||||
if (settings != null) {
|
||||
settings = settings.cast<String, dynamic>();
|
||||
return InAppBrowserClassSettings.fromMap(
|
||||
settings as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isOpened() {
|
||||
return this._isOpened;
|
||||
}
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
disposeChannel();
|
||||
_webViewController?.dispose();
|
||||
_webViewController = null;
|
||||
pullToRefreshController?.dispose();
|
||||
findInteractionController?.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
extension InternalInAppBrowser on MacOSInAppBrowser {
|
||||
void setContextMenu(ContextMenu? contextMenu) {
|
||||
_contextMenu = contextMenu;
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export 'in_app_browser.dart' hide InternalInAppBrowser;
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
const IN_APP_WEBVIEW_STATIC_CHANNEL =
|
||||
MethodChannel('com.pichillilorenzo/flutter_inappwebview_manager');
|
||||
+433
@@ -0,0 +1,433 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
import '../find_interaction/find_interaction_controller.dart';
|
||||
import 'in_app_webview_controller.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSHeadlessInAppWebView].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformHeadlessInAppWebViewCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSHeadlessInAppWebViewCreationParams
|
||||
extends PlatformHeadlessInAppWebViewCreationParams {
|
||||
/// Creates a new [MacOSHeadlessInAppWebViewCreationParams] instance.
|
||||
MacOSHeadlessInAppWebViewCreationParams(
|
||||
{super.controllerFromPlatform,
|
||||
super.initialSize,
|
||||
super.windowId,
|
||||
super.onWebViewCreated,
|
||||
super.onLoadStart,
|
||||
super.onLoadStop,
|
||||
@Deprecated('Use onReceivedError instead') super.onLoadError,
|
||||
super.onReceivedError,
|
||||
@Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError,
|
||||
super.onReceivedHttpError,
|
||||
super.onProgressChanged,
|
||||
super.onConsoleMessage,
|
||||
super.shouldOverrideUrlLoading,
|
||||
super.onLoadResource,
|
||||
super.onScrollChanged,
|
||||
@Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart,
|
||||
super.onDownloadStartRequest,
|
||||
@Deprecated('Use onLoadResourceWithCustomScheme instead')
|
||||
super.onLoadResourceCustomScheme,
|
||||
super.onLoadResourceWithCustomScheme,
|
||||
super.onCreateWindow,
|
||||
super.onCloseWindow,
|
||||
super.onJsAlert,
|
||||
super.onJsConfirm,
|
||||
super.onJsPrompt,
|
||||
super.onReceivedHttpAuthRequest,
|
||||
super.onReceivedServerTrustAuthRequest,
|
||||
super.onReceivedClientCertRequest,
|
||||
@Deprecated('Use FindInteractionController.onFindResultReceived instead')
|
||||
super.onFindResultReceived,
|
||||
super.shouldInterceptAjaxRequest,
|
||||
super.onAjaxReadyStateChange,
|
||||
super.onAjaxProgress,
|
||||
super.shouldInterceptFetchRequest,
|
||||
super.onUpdateVisitedHistory,
|
||||
@Deprecated("Use onPrintRequest instead") super.onPrint,
|
||||
super.onPrintRequest,
|
||||
super.onLongPressHitTestResult,
|
||||
super.onEnterFullscreen,
|
||||
super.onExitFullscreen,
|
||||
super.onPageCommitVisible,
|
||||
super.onTitleChanged,
|
||||
super.onWindowFocus,
|
||||
super.onWindowBlur,
|
||||
super.onOverScrolled,
|
||||
super.onZoomScaleChanged,
|
||||
@Deprecated('Use onSafeBrowsingHit instead')
|
||||
super.androidOnSafeBrowsingHit,
|
||||
super.onSafeBrowsingHit,
|
||||
@Deprecated('Use onPermissionRequest instead')
|
||||
super.androidOnPermissionRequest,
|
||||
super.onPermissionRequest,
|
||||
@Deprecated('Use onGeolocationPermissionsShowPrompt instead')
|
||||
super.androidOnGeolocationPermissionsShowPrompt,
|
||||
super.onGeolocationPermissionsShowPrompt,
|
||||
@Deprecated('Use onGeolocationPermissionsHidePrompt instead')
|
||||
super.androidOnGeolocationPermissionsHidePrompt,
|
||||
super.onGeolocationPermissionsHidePrompt,
|
||||
@Deprecated('Use shouldInterceptRequest instead')
|
||||
super.androidShouldInterceptRequest,
|
||||
super.shouldInterceptRequest,
|
||||
@Deprecated('Use onRenderProcessGone instead')
|
||||
super.androidOnRenderProcessGone,
|
||||
super.onRenderProcessGone,
|
||||
@Deprecated('Use onRenderProcessResponsive instead')
|
||||
super.androidOnRenderProcessResponsive,
|
||||
super.onRenderProcessResponsive,
|
||||
@Deprecated('Use onRenderProcessUnresponsive instead')
|
||||
super.androidOnRenderProcessUnresponsive,
|
||||
super.onRenderProcessUnresponsive,
|
||||
@Deprecated('Use onFormResubmission instead')
|
||||
super.androidOnFormResubmission,
|
||||
super.onFormResubmission,
|
||||
@Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged,
|
||||
@Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon,
|
||||
super.onReceivedIcon,
|
||||
@Deprecated('Use onReceivedTouchIconUrl instead')
|
||||
super.androidOnReceivedTouchIconUrl,
|
||||
super.onReceivedTouchIconUrl,
|
||||
@Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload,
|
||||
super.onJsBeforeUnload,
|
||||
@Deprecated('Use onReceivedLoginRequest instead')
|
||||
super.androidOnReceivedLoginRequest,
|
||||
super.onReceivedLoginRequest,
|
||||
super.onPermissionRequestCanceled,
|
||||
super.onRequestFocus,
|
||||
@Deprecated('Use onWebContentProcessDidTerminate instead')
|
||||
super.iosOnWebContentProcessDidTerminate,
|
||||
super.onWebContentProcessDidTerminate,
|
||||
@Deprecated(
|
||||
'Use onDidReceiveServerRedirectForProvisionalNavigation instead')
|
||||
super.iosOnDidReceiveServerRedirectForProvisionalNavigation,
|
||||
super.onDidReceiveServerRedirectForProvisionalNavigation,
|
||||
@Deprecated('Use onNavigationResponse instead')
|
||||
super.iosOnNavigationResponse,
|
||||
super.onNavigationResponse,
|
||||
@Deprecated('Use shouldAllowDeprecatedTLS instead')
|
||||
super.iosShouldAllowDeprecatedTLS,
|
||||
super.shouldAllowDeprecatedTLS,
|
||||
super.onCameraCaptureStateChanged,
|
||||
super.onMicrophoneCaptureStateChanged,
|
||||
super.onContentSizeChanged,
|
||||
super.initialUrlRequest,
|
||||
super.initialFile,
|
||||
super.initialData,
|
||||
@Deprecated('Use initialSettings instead') super.initialOptions,
|
||||
super.initialSettings,
|
||||
super.contextMenu,
|
||||
super.initialUserScripts,
|
||||
super.pullToRefreshController,
|
||||
this.findInteractionController});
|
||||
|
||||
/// Creates a [MacOSHeadlessInAppWebViewCreationParams] instance based on [PlatformHeadlessInAppWebViewCreationParams].
|
||||
MacOSHeadlessInAppWebViewCreationParams.fromPlatformHeadlessInAppWebViewCreationParams(
|
||||
PlatformHeadlessInAppWebViewCreationParams params)
|
||||
: this(
|
||||
controllerFromPlatform: params.controllerFromPlatform,
|
||||
initialSize: params.initialSize,
|
||||
windowId: params.windowId,
|
||||
onWebViewCreated: params.onWebViewCreated,
|
||||
onLoadStart: params.onLoadStart,
|
||||
onLoadStop: params.onLoadStop,
|
||||
onLoadError: params.onLoadError,
|
||||
onReceivedError: params.onReceivedError,
|
||||
onLoadHttpError: params.onLoadHttpError,
|
||||
onReceivedHttpError: params.onReceivedHttpError,
|
||||
onProgressChanged: params.onProgressChanged,
|
||||
onConsoleMessage: params.onConsoleMessage,
|
||||
shouldOverrideUrlLoading: params.shouldOverrideUrlLoading,
|
||||
onLoadResource: params.onLoadResource,
|
||||
onScrollChanged: params.onScrollChanged,
|
||||
onDownloadStart: params.onDownloadStart,
|
||||
onDownloadStartRequest: params.onDownloadStartRequest,
|
||||
onLoadResourceCustomScheme: params.onLoadResourceCustomScheme,
|
||||
onLoadResourceWithCustomScheme:
|
||||
params.onLoadResourceWithCustomScheme,
|
||||
onCreateWindow: params.onCreateWindow,
|
||||
onCloseWindow: params.onCloseWindow,
|
||||
onJsAlert: params.onJsAlert,
|
||||
onJsConfirm: params.onJsConfirm,
|
||||
onJsPrompt: params.onJsPrompt,
|
||||
onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest,
|
||||
onReceivedServerTrustAuthRequest:
|
||||
params.onReceivedServerTrustAuthRequest,
|
||||
onReceivedClientCertRequest: params.onReceivedClientCertRequest,
|
||||
onFindResultReceived: params.onFindResultReceived,
|
||||
shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest,
|
||||
onAjaxReadyStateChange: params.onAjaxReadyStateChange,
|
||||
onAjaxProgress: params.onAjaxProgress,
|
||||
shouldInterceptFetchRequest: params.shouldInterceptFetchRequest,
|
||||
onUpdateVisitedHistory: params.onUpdateVisitedHistory,
|
||||
onPrint: params.onPrint,
|
||||
onPrintRequest: params.onPrintRequest,
|
||||
onLongPressHitTestResult: params.onLongPressHitTestResult,
|
||||
onEnterFullscreen: params.onEnterFullscreen,
|
||||
onExitFullscreen: params.onExitFullscreen,
|
||||
onPageCommitVisible: params.onPageCommitVisible,
|
||||
onTitleChanged: params.onTitleChanged,
|
||||
onWindowFocus: params.onWindowFocus,
|
||||
onWindowBlur: params.onWindowBlur,
|
||||
onOverScrolled: params.onOverScrolled,
|
||||
onZoomScaleChanged: params.onZoomScaleChanged,
|
||||
androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit,
|
||||
onSafeBrowsingHit: params.onSafeBrowsingHit,
|
||||
androidOnPermissionRequest: params.androidOnPermissionRequest,
|
||||
onPermissionRequest: params.onPermissionRequest,
|
||||
androidOnGeolocationPermissionsShowPrompt:
|
||||
params.androidOnGeolocationPermissionsShowPrompt,
|
||||
onGeolocationPermissionsShowPrompt:
|
||||
params.onGeolocationPermissionsShowPrompt,
|
||||
androidOnGeolocationPermissionsHidePrompt:
|
||||
params.androidOnGeolocationPermissionsHidePrompt,
|
||||
onGeolocationPermissionsHidePrompt:
|
||||
params.onGeolocationPermissionsHidePrompt,
|
||||
androidShouldInterceptRequest: params.androidShouldInterceptRequest,
|
||||
shouldInterceptRequest: params.shouldInterceptRequest,
|
||||
androidOnRenderProcessGone: params.androidOnRenderProcessGone,
|
||||
onRenderProcessGone: params.onRenderProcessGone,
|
||||
androidOnRenderProcessResponsive:
|
||||
params.androidOnRenderProcessResponsive,
|
||||
onRenderProcessResponsive: params.onRenderProcessResponsive,
|
||||
androidOnRenderProcessUnresponsive:
|
||||
params.androidOnRenderProcessUnresponsive,
|
||||
onRenderProcessUnresponsive: params.onRenderProcessUnresponsive,
|
||||
androidOnFormResubmission: params.androidOnFormResubmission,
|
||||
onFormResubmission: params.onFormResubmission,
|
||||
androidOnScaleChanged: params.androidOnScaleChanged,
|
||||
androidOnReceivedIcon: params.androidOnReceivedIcon,
|
||||
onReceivedIcon: params.onReceivedIcon,
|
||||
androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl,
|
||||
onReceivedTouchIconUrl: params.onReceivedTouchIconUrl,
|
||||
androidOnJsBeforeUnload: params.androidOnJsBeforeUnload,
|
||||
onJsBeforeUnload: params.onJsBeforeUnload,
|
||||
androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest,
|
||||
onReceivedLoginRequest: params.onReceivedLoginRequest,
|
||||
onPermissionRequestCanceled: params.onPermissionRequestCanceled,
|
||||
onRequestFocus: params.onRequestFocus,
|
||||
iosOnWebContentProcessDidTerminate:
|
||||
params.iosOnWebContentProcessDidTerminate,
|
||||
onWebContentProcessDidTerminate:
|
||||
params.onWebContentProcessDidTerminate,
|
||||
iosOnDidReceiveServerRedirectForProvisionalNavigation:
|
||||
params.iosOnDidReceiveServerRedirectForProvisionalNavigation,
|
||||
onDidReceiveServerRedirectForProvisionalNavigation:
|
||||
params.onDidReceiveServerRedirectForProvisionalNavigation,
|
||||
iosOnNavigationResponse: params.iosOnNavigationResponse,
|
||||
onNavigationResponse: params.onNavigationResponse,
|
||||
iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS,
|
||||
shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS,
|
||||
onCameraCaptureStateChanged: params.onCameraCaptureStateChanged,
|
||||
onMicrophoneCaptureStateChanged:
|
||||
params.onMicrophoneCaptureStateChanged,
|
||||
onContentSizeChanged: params.onContentSizeChanged,
|
||||
initialUrlRequest: params.initialUrlRequest,
|
||||
initialFile: params.initialFile,
|
||||
initialData: params.initialData,
|
||||
initialOptions: params.initialOptions,
|
||||
initialSettings: params.initialSettings,
|
||||
contextMenu: params.contextMenu,
|
||||
initialUserScripts: params.initialUserScripts,
|
||||
pullToRefreshController: params.pullToRefreshController,
|
||||
findInteractionController: params.findInteractionController
|
||||
as MacOSFindInteractionController?);
|
||||
|
||||
@override
|
||||
final MacOSFindInteractionController? findInteractionController;
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView}
|
||||
class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView
|
||||
with ChannelController {
|
||||
@override
|
||||
late final String id;
|
||||
|
||||
bool _started = false;
|
||||
bool _running = false;
|
||||
|
||||
static const MethodChannel _sharedChannel =
|
||||
const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
|
||||
|
||||
MacOSInAppWebViewController? _webViewController;
|
||||
|
||||
/// Constructs a [MacOSHeadlessInAppWebView].
|
||||
MacOSHeadlessInAppWebView(PlatformHeadlessInAppWebViewCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSHeadlessInAppWebViewCreationParams
|
||||
? params
|
||||
: MacOSHeadlessInAppWebViewCreationParams
|
||||
.fromPlatformHeadlessInAppWebViewCreationParams(params),
|
||||
) {
|
||||
id = IdGenerator.generate();
|
||||
}
|
||||
|
||||
@override
|
||||
MacOSInAppWebViewController? get webViewController => _webViewController;
|
||||
|
||||
dynamic _controllerFromPlatform;
|
||||
|
||||
MacOSHeadlessInAppWebViewCreationParams get _macosParams =>
|
||||
params as MacOSHeadlessInAppWebViewCreationParams;
|
||||
|
||||
_init() {
|
||||
_webViewController = MacOSInAppWebViewController(
|
||||
MacOSInAppWebViewControllerCreationParams(id: id, webviewParams: params),
|
||||
);
|
||||
_controllerFromPlatform =
|
||||
params.controllerFromPlatform?.call(_webViewController!) ??
|
||||
_webViewController!;
|
||||
_macosParams.findInteractionController?.init(id);
|
||||
channel =
|
||||
MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id');
|
||||
handler = _handleMethod;
|
||||
initMethodCallHandler();
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "onWebViewCreated":
|
||||
if (params.onWebViewCreated != null && _webViewController != null) {
|
||||
params.onWebViewCreated!(_controllerFromPlatform);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> run() async {
|
||||
if (_started) {
|
||||
return;
|
||||
}
|
||||
_started = true;
|
||||
_init();
|
||||
|
||||
final initialSettings = params.initialSettings ?? InAppWebViewSettings();
|
||||
_inferInitialSettings(initialSettings);
|
||||
|
||||
Map<String, dynamic> settingsMap =
|
||||
(params.initialSettings != null ? initialSettings.toMap() : null) ??
|
||||
params.initialOptions?.toMap() ??
|
||||
initialSettings.toMap();
|
||||
|
||||
Map<String, dynamic> pullToRefreshSettings =
|
||||
_macosParams.pullToRefreshController?.params.settings.toMap() ??
|
||||
_macosParams.pullToRefreshController?.params.options.toMap() ??
|
||||
PullToRefreshSettings(enabled: false).toMap();
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('id', () => id);
|
||||
args.putIfAbsent(
|
||||
'params',
|
||||
() => <String, dynamic>{
|
||||
'initialUrlRequest': params.initialUrlRequest?.toMap(),
|
||||
'initialFile': params.initialFile,
|
||||
'initialData': params.initialData?.toMap(),
|
||||
'initialSettings': settingsMap,
|
||||
'contextMenu': params.contextMenu?.toMap() ?? {},
|
||||
'windowId': params.windowId,
|
||||
'initialUserScripts':
|
||||
params.initialUserScripts?.map((e) => e.toMap()).toList() ??
|
||||
[],
|
||||
'pullToRefreshSettings': pullToRefreshSettings,
|
||||
'initialSize': params.initialSize.toMap()
|
||||
});
|
||||
await _sharedChannel.invokeMethod('run', args);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
void _inferInitialSettings(InAppWebViewSettings settings) {
|
||||
if (params.shouldOverrideUrlLoading != null &&
|
||||
settings.useShouldOverrideUrlLoading == null) {
|
||||
settings.useShouldOverrideUrlLoading = true;
|
||||
}
|
||||
if (params.onLoadResource != null && settings.useOnLoadResource == null) {
|
||||
settings.useOnLoadResource = true;
|
||||
}
|
||||
if (params.onDownloadStartRequest != null &&
|
||||
settings.useOnDownloadStart == null) {
|
||||
settings.useOnDownloadStart = true;
|
||||
}
|
||||
if (params.shouldInterceptAjaxRequest != null &&
|
||||
settings.useShouldInterceptAjaxRequest == null) {
|
||||
settings.useShouldInterceptAjaxRequest = true;
|
||||
}
|
||||
if (params.shouldInterceptFetchRequest != null &&
|
||||
settings.useShouldInterceptFetchRequest == null) {
|
||||
settings.useShouldInterceptFetchRequest = true;
|
||||
}
|
||||
if (params.shouldInterceptRequest != null &&
|
||||
settings.useShouldInterceptRequest == null) {
|
||||
settings.useShouldInterceptRequest = true;
|
||||
}
|
||||
if (params.onRenderProcessGone != null &&
|
||||
settings.useOnRenderProcessGone == null) {
|
||||
settings.useOnRenderProcessGone = true;
|
||||
}
|
||||
if (params.onNavigationResponse != null &&
|
||||
settings.useOnNavigationResponse == null) {
|
||||
settings.useOnNavigationResponse = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool isRunning() {
|
||||
return _running;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setSize(Size size) async {
|
||||
if (!_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('size', () => size.toMap());
|
||||
await channel?.invokeMethod('setSize', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Size?> getSize() async {
|
||||
if (!_running) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
Map<String, dynamic> sizeMap =
|
||||
(await channel?.invokeMethod('getSize', args))?.cast<String, dynamic>();
|
||||
return MapSize.fromMap(sizeMap);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
if (!_running) {
|
||||
return;
|
||||
}
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await channel?.invokeMethod('dispose', args);
|
||||
disposeChannel();
|
||||
_started = false;
|
||||
_running = false;
|
||||
_webViewController?.dispose();
|
||||
_webViewController = null;
|
||||
_controllerFromPlatform = null;
|
||||
_macosParams.pullToRefreshController?.dispose();
|
||||
_macosParams.findInteractionController?.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
extension InternalHeadlessInAppWebView on MacOSHeadlessInAppWebView {
|
||||
Future<void> internalDispose() async {
|
||||
_started = false;
|
||||
_running = false;
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+408
@@ -0,0 +1,408 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
import 'headless_in_app_webview.dart';
|
||||
|
||||
import '../find_interaction/find_interaction_controller.dart';
|
||||
import 'in_app_webview_controller.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [PlatformInAppWebViewWidget].
|
||||
///
|
||||
/// Platform specific implementations can add additional fields by extending
|
||||
/// this class.
|
||||
class MacOSInAppWebViewWidgetCreationParams
|
||||
extends PlatformInAppWebViewWidgetCreationParams {
|
||||
MacOSInAppWebViewWidgetCreationParams(
|
||||
{super.controllerFromPlatform,
|
||||
super.key,
|
||||
super.layoutDirection,
|
||||
super.gestureRecognizers,
|
||||
super.headlessWebView,
|
||||
super.keepAlive,
|
||||
super.preventGestureDelay,
|
||||
super.windowId,
|
||||
super.onWebViewCreated,
|
||||
super.onLoadStart,
|
||||
super.onLoadStop,
|
||||
@Deprecated('Use onReceivedError instead') super.onLoadError,
|
||||
super.onReceivedError,
|
||||
@Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError,
|
||||
super.onReceivedHttpError,
|
||||
super.onProgressChanged,
|
||||
super.onConsoleMessage,
|
||||
super.shouldOverrideUrlLoading,
|
||||
super.onLoadResource,
|
||||
super.onScrollChanged,
|
||||
@Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart,
|
||||
super.onDownloadStartRequest,
|
||||
@Deprecated('Use onLoadResourceWithCustomScheme instead')
|
||||
super.onLoadResourceCustomScheme,
|
||||
super.onLoadResourceWithCustomScheme,
|
||||
super.onCreateWindow,
|
||||
super.onCloseWindow,
|
||||
super.onJsAlert,
|
||||
super.onJsConfirm,
|
||||
super.onJsPrompt,
|
||||
super.onReceivedHttpAuthRequest,
|
||||
super.onReceivedServerTrustAuthRequest,
|
||||
super.onReceivedClientCertRequest,
|
||||
@Deprecated('Use FindInteractionController.onFindResultReceived instead')
|
||||
super.onFindResultReceived,
|
||||
super.shouldInterceptAjaxRequest,
|
||||
super.onAjaxReadyStateChange,
|
||||
super.onAjaxProgress,
|
||||
super.shouldInterceptFetchRequest,
|
||||
super.onUpdateVisitedHistory,
|
||||
@Deprecated("Use onPrintRequest instead") super.onPrint,
|
||||
super.onPrintRequest,
|
||||
super.onLongPressHitTestResult,
|
||||
super.onEnterFullscreen,
|
||||
super.onExitFullscreen,
|
||||
super.onPageCommitVisible,
|
||||
super.onTitleChanged,
|
||||
super.onWindowFocus,
|
||||
super.onWindowBlur,
|
||||
super.onOverScrolled,
|
||||
super.onZoomScaleChanged,
|
||||
@Deprecated('Use onSafeBrowsingHit instead')
|
||||
super.androidOnSafeBrowsingHit,
|
||||
super.onSafeBrowsingHit,
|
||||
@Deprecated('Use onPermissionRequest instead')
|
||||
super.androidOnPermissionRequest,
|
||||
super.onPermissionRequest,
|
||||
@Deprecated('Use onGeolocationPermissionsShowPrompt instead')
|
||||
super.androidOnGeolocationPermissionsShowPrompt,
|
||||
super.onGeolocationPermissionsShowPrompt,
|
||||
@Deprecated('Use onGeolocationPermissionsHidePrompt instead')
|
||||
super.androidOnGeolocationPermissionsHidePrompt,
|
||||
super.onGeolocationPermissionsHidePrompt,
|
||||
@Deprecated('Use shouldInterceptRequest instead')
|
||||
super.androidShouldInterceptRequest,
|
||||
super.shouldInterceptRequest,
|
||||
@Deprecated('Use onRenderProcessGone instead')
|
||||
super.androidOnRenderProcessGone,
|
||||
super.onRenderProcessGone,
|
||||
@Deprecated('Use onRenderProcessResponsive instead')
|
||||
super.androidOnRenderProcessResponsive,
|
||||
super.onRenderProcessResponsive,
|
||||
@Deprecated('Use onRenderProcessUnresponsive instead')
|
||||
super.androidOnRenderProcessUnresponsive,
|
||||
super.onRenderProcessUnresponsive,
|
||||
@Deprecated('Use onFormResubmission instead')
|
||||
super.androidOnFormResubmission,
|
||||
super.onFormResubmission,
|
||||
@Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged,
|
||||
@Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon,
|
||||
super.onReceivedIcon,
|
||||
@Deprecated('Use onReceivedTouchIconUrl instead')
|
||||
super.androidOnReceivedTouchIconUrl,
|
||||
super.onReceivedTouchIconUrl,
|
||||
@Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload,
|
||||
super.onJsBeforeUnload,
|
||||
@Deprecated('Use onReceivedLoginRequest instead')
|
||||
super.androidOnReceivedLoginRequest,
|
||||
super.onReceivedLoginRequest,
|
||||
super.onPermissionRequestCanceled,
|
||||
super.onRequestFocus,
|
||||
@Deprecated('Use onWebContentProcessDidTerminate instead')
|
||||
super.iosOnWebContentProcessDidTerminate,
|
||||
super.onWebContentProcessDidTerminate,
|
||||
@Deprecated(
|
||||
'Use onDidReceiveServerRedirectForProvisionalNavigation instead')
|
||||
super.iosOnDidReceiveServerRedirectForProvisionalNavigation,
|
||||
super.onDidReceiveServerRedirectForProvisionalNavigation,
|
||||
@Deprecated('Use onNavigationResponse instead')
|
||||
super.iosOnNavigationResponse,
|
||||
super.onNavigationResponse,
|
||||
@Deprecated('Use shouldAllowDeprecatedTLS instead')
|
||||
super.iosShouldAllowDeprecatedTLS,
|
||||
super.shouldAllowDeprecatedTLS,
|
||||
super.onCameraCaptureStateChanged,
|
||||
super.onMicrophoneCaptureStateChanged,
|
||||
super.onContentSizeChanged,
|
||||
super.initialUrlRequest,
|
||||
super.initialFile,
|
||||
super.initialData,
|
||||
@Deprecated('Use initialSettings instead') super.initialOptions,
|
||||
super.initialSettings,
|
||||
super.contextMenu,
|
||||
super.initialUserScripts,
|
||||
super.pullToRefreshController,
|
||||
this.findInteractionController});
|
||||
|
||||
/// Constructs a [MacOSInAppWebViewWidgetCreationParams] using a
|
||||
/// [PlatformInAppWebViewWidgetCreationParams].
|
||||
MacOSInAppWebViewWidgetCreationParams.fromPlatformInAppWebViewWidgetCreationParams(
|
||||
PlatformInAppWebViewWidgetCreationParams params)
|
||||
: this(
|
||||
controllerFromPlatform: params.controllerFromPlatform,
|
||||
key: params.key,
|
||||
layoutDirection: params.layoutDirection,
|
||||
gestureRecognizers: params.gestureRecognizers,
|
||||
headlessWebView: params.headlessWebView,
|
||||
keepAlive: params.keepAlive,
|
||||
preventGestureDelay: params.preventGestureDelay,
|
||||
windowId: params.windowId,
|
||||
onWebViewCreated: params.onWebViewCreated,
|
||||
onLoadStart: params.onLoadStart,
|
||||
onLoadStop: params.onLoadStop,
|
||||
onLoadError: params.onLoadError,
|
||||
onReceivedError: params.onReceivedError,
|
||||
onLoadHttpError: params.onLoadHttpError,
|
||||
onReceivedHttpError: params.onReceivedHttpError,
|
||||
onProgressChanged: params.onProgressChanged,
|
||||
onConsoleMessage: params.onConsoleMessage,
|
||||
shouldOverrideUrlLoading: params.shouldOverrideUrlLoading,
|
||||
onLoadResource: params.onLoadResource,
|
||||
onScrollChanged: params.onScrollChanged,
|
||||
onDownloadStart: params.onDownloadStart,
|
||||
onDownloadStartRequest: params.onDownloadStartRequest,
|
||||
onLoadResourceCustomScheme: params.onLoadResourceCustomScheme,
|
||||
onLoadResourceWithCustomScheme:
|
||||
params.onLoadResourceWithCustomScheme,
|
||||
onCreateWindow: params.onCreateWindow,
|
||||
onCloseWindow: params.onCloseWindow,
|
||||
onJsAlert: params.onJsAlert,
|
||||
onJsConfirm: params.onJsConfirm,
|
||||
onJsPrompt: params.onJsPrompt,
|
||||
onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest,
|
||||
onReceivedServerTrustAuthRequest:
|
||||
params.onReceivedServerTrustAuthRequest,
|
||||
onReceivedClientCertRequest: params.onReceivedClientCertRequest,
|
||||
onFindResultReceived: params.onFindResultReceived,
|
||||
shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest,
|
||||
onAjaxReadyStateChange: params.onAjaxReadyStateChange,
|
||||
onAjaxProgress: params.onAjaxProgress,
|
||||
shouldInterceptFetchRequest: params.shouldInterceptFetchRequest,
|
||||
onUpdateVisitedHistory: params.onUpdateVisitedHistory,
|
||||
onPrint: params.onPrint,
|
||||
onPrintRequest: params.onPrintRequest,
|
||||
onLongPressHitTestResult: params.onLongPressHitTestResult,
|
||||
onEnterFullscreen: params.onEnterFullscreen,
|
||||
onExitFullscreen: params.onExitFullscreen,
|
||||
onPageCommitVisible: params.onPageCommitVisible,
|
||||
onTitleChanged: params.onTitleChanged,
|
||||
onWindowFocus: params.onWindowFocus,
|
||||
onWindowBlur: params.onWindowBlur,
|
||||
onOverScrolled: params.onOverScrolled,
|
||||
onZoomScaleChanged: params.onZoomScaleChanged,
|
||||
androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit,
|
||||
onSafeBrowsingHit: params.onSafeBrowsingHit,
|
||||
androidOnPermissionRequest: params.androidOnPermissionRequest,
|
||||
onPermissionRequest: params.onPermissionRequest,
|
||||
androidOnGeolocationPermissionsShowPrompt:
|
||||
params.androidOnGeolocationPermissionsShowPrompt,
|
||||
onGeolocationPermissionsShowPrompt:
|
||||
params.onGeolocationPermissionsShowPrompt,
|
||||
androidOnGeolocationPermissionsHidePrompt:
|
||||
params.androidOnGeolocationPermissionsHidePrompt,
|
||||
onGeolocationPermissionsHidePrompt:
|
||||
params.onGeolocationPermissionsHidePrompt,
|
||||
androidShouldInterceptRequest: params.androidShouldInterceptRequest,
|
||||
shouldInterceptRequest: params.shouldInterceptRequest,
|
||||
androidOnRenderProcessGone: params.androidOnRenderProcessGone,
|
||||
onRenderProcessGone: params.onRenderProcessGone,
|
||||
androidOnRenderProcessResponsive:
|
||||
params.androidOnRenderProcessResponsive,
|
||||
onRenderProcessResponsive: params.onRenderProcessResponsive,
|
||||
androidOnRenderProcessUnresponsive:
|
||||
params.androidOnRenderProcessUnresponsive,
|
||||
onRenderProcessUnresponsive: params.onRenderProcessUnresponsive,
|
||||
androidOnFormResubmission: params.androidOnFormResubmission,
|
||||
onFormResubmission: params.onFormResubmission,
|
||||
androidOnScaleChanged: params.androidOnScaleChanged,
|
||||
androidOnReceivedIcon: params.androidOnReceivedIcon,
|
||||
onReceivedIcon: params.onReceivedIcon,
|
||||
androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl,
|
||||
onReceivedTouchIconUrl: params.onReceivedTouchIconUrl,
|
||||
androidOnJsBeforeUnload: params.androidOnJsBeforeUnload,
|
||||
onJsBeforeUnload: params.onJsBeforeUnload,
|
||||
androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest,
|
||||
onReceivedLoginRequest: params.onReceivedLoginRequest,
|
||||
onPermissionRequestCanceled: params.onPermissionRequestCanceled,
|
||||
onRequestFocus: params.onRequestFocus,
|
||||
iosOnWebContentProcessDidTerminate:
|
||||
params.iosOnWebContentProcessDidTerminate,
|
||||
onWebContentProcessDidTerminate:
|
||||
params.onWebContentProcessDidTerminate,
|
||||
iosOnDidReceiveServerRedirectForProvisionalNavigation:
|
||||
params.iosOnDidReceiveServerRedirectForProvisionalNavigation,
|
||||
onDidReceiveServerRedirectForProvisionalNavigation:
|
||||
params.onDidReceiveServerRedirectForProvisionalNavigation,
|
||||
iosOnNavigationResponse: params.iosOnNavigationResponse,
|
||||
onNavigationResponse: params.onNavigationResponse,
|
||||
iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS,
|
||||
shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS,
|
||||
onCameraCaptureStateChanged: params.onCameraCaptureStateChanged,
|
||||
onMicrophoneCaptureStateChanged:
|
||||
params.onMicrophoneCaptureStateChanged,
|
||||
onContentSizeChanged: params.onContentSizeChanged,
|
||||
initialUrlRequest: params.initialUrlRequest,
|
||||
initialFile: params.initialFile,
|
||||
initialData: params.initialData,
|
||||
initialOptions: params.initialOptions,
|
||||
initialSettings: params.initialSettings,
|
||||
contextMenu: params.contextMenu,
|
||||
initialUserScripts: params.initialUserScripts,
|
||||
pullToRefreshController: params.pullToRefreshController,
|
||||
findInteractionController: params.findInteractionController
|
||||
as MacOSFindInteractionController?);
|
||||
|
||||
@override
|
||||
final MacOSFindInteractionController? findInteractionController;
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget}
|
||||
class MacOSInAppWebViewWidget extends PlatformInAppWebViewWidget {
|
||||
/// Constructs a [MacOSInAppWebViewWidget].
|
||||
///
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget}
|
||||
MacOSInAppWebViewWidget(PlatformInAppWebViewWidgetCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSInAppWebViewWidgetCreationParams
|
||||
? params
|
||||
: MacOSInAppWebViewWidgetCreationParams
|
||||
.fromPlatformInAppWebViewWidgetCreationParams(params),
|
||||
);
|
||||
|
||||
MacOSInAppWebViewWidgetCreationParams get _macosParams =>
|
||||
params as MacOSInAppWebViewWidgetCreationParams;
|
||||
|
||||
MacOSInAppWebViewController? _controller;
|
||||
|
||||
MacOSHeadlessInAppWebView? get _macosHeadlessInAppWebView =>
|
||||
params.headlessWebView as MacOSHeadlessInAppWebView?;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final initialSettings = params.initialSettings ?? InAppWebViewSettings();
|
||||
_inferInitialSettings(initialSettings);
|
||||
|
||||
Map<String, dynamic> settingsMap =
|
||||
(params.initialSettings != null ? initialSettings.toMap() : null) ??
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
params.initialOptions?.toMap() ??
|
||||
initialSettings.toMap();
|
||||
|
||||
Map<String, dynamic> pullToRefreshSettings =
|
||||
params.pullToRefreshController?.params.settings.toMap() ??
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
params.pullToRefreshController?.params.options.toMap() ??
|
||||
PullToRefreshSettings(enabled: false).toMap();
|
||||
|
||||
if ((params.headlessWebView?.isRunning() ?? false) &&
|
||||
params.keepAlive != null) {
|
||||
final headlessId = params.headlessWebView?.id;
|
||||
if (headlessId != null) {
|
||||
// force keep alive id to match headless webview id
|
||||
params.keepAlive?.id = headlessId;
|
||||
}
|
||||
}
|
||||
|
||||
return AppKitView(
|
||||
viewType: 'com.pichillilorenzo/flutter_inappwebview',
|
||||
onPlatformViewCreated: _onPlatformViewCreated,
|
||||
gestureRecognizers: params.gestureRecognizers,
|
||||
creationParams: <String, dynamic>{
|
||||
'initialUrlRequest': params.initialUrlRequest?.toMap(),
|
||||
'initialFile': params.initialFile,
|
||||
'initialData': params.initialData?.toMap(),
|
||||
'initialSettings': settingsMap,
|
||||
'contextMenu': params.contextMenu?.toMap() ?? {},
|
||||
'windowId': params.windowId,
|
||||
'headlessWebViewId': params.headlessWebView?.isRunning() ?? false
|
||||
? params.headlessWebView?.id
|
||||
: null,
|
||||
'initialUserScripts':
|
||||
params.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
||||
'pullToRefreshSettings': pullToRefreshSettings,
|
||||
'keepAliveId': params.keepAlive?.id,
|
||||
'preventGestureDelay': params.preventGestureDelay
|
||||
},
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
);
|
||||
}
|
||||
|
||||
void _onPlatformViewCreated(int id) {
|
||||
dynamic viewId = id;
|
||||
if (params.headlessWebView?.isRunning() ?? false) {
|
||||
viewId = params.headlessWebView?.id;
|
||||
}
|
||||
viewId = params.keepAlive?.id ?? viewId ?? id;
|
||||
_macosHeadlessInAppWebView?.internalDispose();
|
||||
_controller = MacOSInAppWebViewController(
|
||||
PlatformInAppWebViewControllerCreationParams(
|
||||
id: viewId, webviewParams: params));
|
||||
_macosParams.findInteractionController?.init(viewId);
|
||||
debugLog(
|
||||
className: runtimeType.toString(),
|
||||
id: viewId?.toString(),
|
||||
debugLoggingSettings:
|
||||
PlatformInAppWebViewController.debugLoggingSettings,
|
||||
method: "onWebViewCreated",
|
||||
args: []);
|
||||
if (params.onWebViewCreated != null) {
|
||||
params.onWebViewCreated!(
|
||||
params.controllerFromPlatform?.call(_controller!) ?? _controller!);
|
||||
}
|
||||
}
|
||||
|
||||
void _inferInitialSettings(InAppWebViewSettings settings) {
|
||||
if (params.shouldOverrideUrlLoading != null &&
|
||||
settings.useShouldOverrideUrlLoading == null) {
|
||||
settings.useShouldOverrideUrlLoading = true;
|
||||
}
|
||||
if (params.onLoadResource != null && settings.useOnLoadResource == null) {
|
||||
settings.useOnLoadResource = true;
|
||||
}
|
||||
if (params.onDownloadStartRequest != null &&
|
||||
settings.useOnDownloadStart == null) {
|
||||
settings.useOnDownloadStart = true;
|
||||
}
|
||||
if ((params.shouldInterceptAjaxRequest != null ||
|
||||
params.onAjaxProgress != null ||
|
||||
params.onAjaxReadyStateChange != null) &&
|
||||
settings.useShouldInterceptAjaxRequest == null) {
|
||||
settings.useShouldInterceptAjaxRequest = true;
|
||||
}
|
||||
if (params.shouldInterceptFetchRequest != null &&
|
||||
settings.useShouldInterceptFetchRequest == null) {
|
||||
settings.useShouldInterceptFetchRequest = true;
|
||||
}
|
||||
if (params.shouldInterceptRequest != null &&
|
||||
settings.useShouldInterceptRequest == null) {
|
||||
settings.useShouldInterceptRequest = true;
|
||||
}
|
||||
if (params.onRenderProcessGone != null &&
|
||||
settings.useOnRenderProcessGone == null) {
|
||||
settings.useOnRenderProcessGone = true;
|
||||
}
|
||||
if (params.onNavigationResponse != null &&
|
||||
settings.useOnNavigationResponse == null) {
|
||||
settings.useOnNavigationResponse = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
dynamic viewId = _controller?.getViewId();
|
||||
debugLog(
|
||||
className: runtimeType.toString(),
|
||||
id: viewId?.toString(),
|
||||
debugLoggingSettings:
|
||||
PlatformInAppWebViewController.debugLoggingSettings,
|
||||
method: "dispose",
|
||||
args: []);
|
||||
final isKeepAlive = params.keepAlive != null;
|
||||
_controller?.dispose(isKeepAlive: isKeepAlive);
|
||||
_controller = null;
|
||||
params.findInteractionController?.dispose(isKeepAlive: isKeepAlive);
|
||||
}
|
||||
|
||||
@override
|
||||
T controllerFromPlatform<T>(PlatformInAppWebViewController controller) {
|
||||
// unused
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
+2689
File diff suppressed because it is too large.
Load diff
+3
@@ -0,0 +1,3 @@
|
||||
export 'in_app_webview_controller.dart' hide InternalInAppWebViewController;
|
||||
export 'in_app_webview.dart';
|
||||
export 'headless_in_app_webview.dart' hide InternalHeadlessInAppWebView;
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
import 'cookie_manager.dart';
|
||||
import 'http_auth_credentials_database.dart';
|
||||
import 'find_interaction/main.dart';
|
||||
import 'in_app_browser/in_app_browser.dart';
|
||||
import 'in_app_webview/main.dart';
|
||||
import 'print_job/main.dart';
|
||||
import 'web_message/main.dart';
|
||||
import 'web_storage/main.dart';
|
||||
import 'web_authentication_session/main.dart';
|
||||
|
||||
/// Implementation of [InAppWebViewPlatform] using the WebKit API.
|
||||
class MacOSInAppWebViewPlatform extends InAppWebViewPlatform {
|
||||
/// Registers this class as the default instance of [InAppWebViewPlatform].
|
||||
static void registerWith() {
|
||||
InAppWebViewPlatform.instance = MacOSInAppWebViewPlatform();
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSCookieManager].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [CookieManager] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSCookieManager createPlatformCookieManager(
|
||||
PlatformCookieManagerCreationParams params,
|
||||
) {
|
||||
return MacOSCookieManager(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSInAppWebViewController].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [InAppWebViewController] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSInAppWebViewController createPlatformInAppWebViewController(
|
||||
PlatformInAppWebViewControllerCreationParams params,
|
||||
) {
|
||||
return MacOSInAppWebViewController(params);
|
||||
}
|
||||
|
||||
/// Creates a new empty [MacOSInAppWebViewController] to access static methods.
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [InAppWebViewController] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSInAppWebViewController createPlatformInAppWebViewControllerStatic() {
|
||||
return MacOSInAppWebViewController.static();
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSInAppWebViewWidget].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [InAppWebView] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSInAppWebViewWidget createPlatformInAppWebViewWidget(
|
||||
PlatformInAppWebViewWidgetCreationParams params,
|
||||
) {
|
||||
return MacOSInAppWebViewWidget(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSFindInteractionController].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [FindInteractionController] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSFindInteractionController createPlatformFindInteractionController(
|
||||
PlatformFindInteractionControllerCreationParams params,
|
||||
) {
|
||||
return MacOSFindInteractionController(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSPrintJobController].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [PrintJobController] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSPrintJobController createPlatformPrintJobController(
|
||||
PlatformPrintJobControllerCreationParams params,
|
||||
) {
|
||||
return MacOSPrintJobController(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSWebMessageChannel].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [WebMessageChannel] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSWebMessageChannel createPlatformWebMessageChannel(
|
||||
PlatformWebMessageChannelCreationParams params,
|
||||
) {
|
||||
return MacOSWebMessageChannel(params);
|
||||
}
|
||||
|
||||
/// Creates a new empty [MacOSWebMessageChannel] to access static methods.
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [WebMessageChannel] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSWebMessageChannel createPlatformWebMessageChannelStatic() {
|
||||
return MacOSWebMessageChannel.static();
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSWebMessageListener].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [WebMessageListener] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSWebMessageListener createPlatformWebMessageListener(
|
||||
PlatformWebMessageListenerCreationParams params,
|
||||
) {
|
||||
return MacOSWebMessageListener(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSJavaScriptReplyProxy].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [JavaScriptReplyProxy] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSJavaScriptReplyProxy createPlatformJavaScriptReplyProxy(
|
||||
PlatformJavaScriptReplyProxyCreationParams params,
|
||||
) {
|
||||
return MacOSJavaScriptReplyProxy(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSWebMessagePort].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [WebMessagePort] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSWebMessagePort createPlatformWebMessagePort(
|
||||
PlatformWebMessagePortCreationParams params,
|
||||
) {
|
||||
return MacOSWebMessagePort(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSWebStorage].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [WebStorage] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSWebStorage createPlatformWebStorage(
|
||||
PlatformWebStorageCreationParams params,
|
||||
) {
|
||||
return MacOSWebStorage(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSLocalStorage].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [LocalStorage] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSLocalStorage createPlatformLocalStorage(
|
||||
PlatformLocalStorageCreationParams params,
|
||||
) {
|
||||
return MacOSLocalStorage(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSSessionStorage].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [SessionStorage] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSSessionStorage createPlatformSessionStorage(
|
||||
PlatformSessionStorageCreationParams params,
|
||||
) {
|
||||
return MacOSSessionStorage(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSHeadlessInAppWebView].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [HeadlessInAppWebView] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSHeadlessInAppWebView createPlatformHeadlessInAppWebView(
|
||||
PlatformHeadlessInAppWebViewCreationParams params,
|
||||
) {
|
||||
return MacOSHeadlessInAppWebView(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSHttpAuthCredentialDatabase].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [HttpAuthCredentialDatabase] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSHttpAuthCredentialDatabase createPlatformHttpAuthCredentialDatabase(
|
||||
PlatformHttpAuthCredentialDatabaseCreationParams params,
|
||||
) {
|
||||
return MacOSHttpAuthCredentialDatabase(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSInAppBrowser].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [InAppBrowser] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSInAppBrowser createPlatformInAppBrowser(
|
||||
PlatformInAppBrowserCreationParams params,
|
||||
) {
|
||||
return MacOSInAppBrowser(params);
|
||||
}
|
||||
|
||||
/// Creates a new empty [MacOSInAppBrowser] to access static methods.
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [InAppBrowser] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSInAppBrowser createPlatformInAppBrowserStatic() {
|
||||
return MacOSInAppBrowser.static();
|
||||
}
|
||||
|
||||
/// Creates a new empty [MacOSWebStorageManager] to access static methods.
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [WebStorageManager] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSWebStorageManager createPlatformWebStorageManager(
|
||||
PlatformWebStorageManagerCreationParams params) {
|
||||
return MacOSWebStorageManager(params);
|
||||
}
|
||||
|
||||
/// Creates a new [MacOSWebAuthenticationSession].
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [WebAuthenticationSession] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSWebAuthenticationSession createPlatformWebAuthenticationSession(
|
||||
PlatformWebAuthenticationSessionCreationParams params) {
|
||||
return MacOSWebAuthenticationSession(params);
|
||||
}
|
||||
|
||||
/// Creates a new empty [MacOSWebAuthenticationSession] to access static methods.
|
||||
///
|
||||
/// This function should only be called by the app-facing package.
|
||||
/// Look at using [WebAuthenticationSession] in `flutter_inappwebview` instead.
|
||||
@override
|
||||
MacOSWebAuthenticationSession createPlatformWebAuthenticationSessionStatic() {
|
||||
return MacOSWebAuthenticationSession.static();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export 'inappwebview_platform.dart';
|
||||
export 'in_app_webview/main.dart';
|
||||
export 'in_app_browser/main.dart';
|
||||
export 'web_storage/main.dart';
|
||||
export 'cookie_manager.dart' hide InternalCookieManager;
|
||||
export 'http_auth_credentials_database.dart'
|
||||
hide InternalHttpAuthCredentialDatabase;
|
||||
export 'web_message/main.dart';
|
||||
export 'print_job/main.dart';
|
||||
export 'find_interaction/main.dart';
|
||||
export 'web_authentication_session/main.dart';
|
||||
@@ -0,0 +1,64 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
///Platform native utilities
|
||||
class PlatformUtil {
|
||||
static PlatformUtil? _instance;
|
||||
static const MethodChannel _channel =
|
||||
MethodChannel('com.pichillilorenzo/flutter_inappwebview_platformutil');
|
||||
|
||||
PlatformUtil._();
|
||||
|
||||
///Get [PlatformUtil] instance.
|
||||
static PlatformUtil instance() {
|
||||
return (_instance != null) ? _instance! : _init();
|
||||
}
|
||||
|
||||
static PlatformUtil _init() {
|
||||
_channel.setMethodCallHandler((call) async {
|
||||
try {
|
||||
return await _handleMethod(call);
|
||||
} on Error catch (e) {
|
||||
print(e);
|
||||
print(e.stackTrace);
|
||||
}
|
||||
});
|
||||
_instance = PlatformUtil._();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
static Future<dynamic> _handleMethod(MethodCall call) async {}
|
||||
|
||||
String? _cachedSystemVersion;
|
||||
|
||||
///Get current platform system version.
|
||||
Future<String> getSystemVersion() async {
|
||||
if (_cachedSystemVersion != null) {
|
||||
return _cachedSystemVersion!;
|
||||
}
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
_cachedSystemVersion =
|
||||
await _channel.invokeMethod('getSystemVersion', args);
|
||||
return _cachedSystemVersion!;
|
||||
}
|
||||
|
||||
///Format date.
|
||||
Future<String> formatDate(
|
||||
{required DateTime date,
|
||||
required String format,
|
||||
String locale = "en_US",
|
||||
String timezone = "UTC"}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('date', () => date.millisecondsSinceEpoch);
|
||||
args.putIfAbsent('format', () => format);
|
||||
args.putIfAbsent('locale', () => locale);
|
||||
args.putIfAbsent('timezone', () => timezone);
|
||||
return await _channel.invokeMethod('formatDate', args);
|
||||
}
|
||||
|
||||
///Get cookie expiration date used by Web platform.
|
||||
Future<String> getWebCookieExpirationDate({required DateTime date}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('date', () => date.millisecondsSinceEpoch);
|
||||
return await _channel.invokeMethod('getWebCookieExpirationDate', args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export 'print_job_controller.dart';
|
||||
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSPrintJobController].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformPrintJobControllerCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSPrintJobControllerCreationParams
|
||||
extends PlatformPrintJobControllerCreationParams {
|
||||
/// Creates a new [MacOSPrintJobControllerCreationParams] instance.
|
||||
const MacOSPrintJobControllerCreationParams(
|
||||
{required super.id, super.onComplete});
|
||||
|
||||
/// Creates a [MacOSPrintJobControllerCreationParams] instance based on [PlatformPrintJobControllerCreationParams].
|
||||
factory MacOSPrintJobControllerCreationParams.fromPlatformPrintJobControllerCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformPrintJobControllerCreationParams params) {
|
||||
return MacOSPrintJobControllerCreationParams(
|
||||
id: params.id, onComplete: params.onComplete);
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformPrintJobController}
|
||||
class MacOSPrintJobController extends PlatformPrintJobController
|
||||
with ChannelController {
|
||||
/// Constructs a [MacOSPrintJobController].
|
||||
MacOSPrintJobController(PlatformPrintJobControllerCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSPrintJobControllerCreationParams
|
||||
? params
|
||||
: MacOSPrintJobControllerCreationParams
|
||||
.fromPlatformPrintJobControllerCreationParams(params),
|
||||
) {
|
||||
onComplete = params.onComplete;
|
||||
channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_printjobcontroller_${params.id}');
|
||||
handler = _handleMethod;
|
||||
initMethodCallHandler();
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "onComplete":
|
||||
bool completed = call.arguments["completed"];
|
||||
String? error = call.arguments["error"];
|
||||
if (onComplete != null) {
|
||||
onComplete!(completed, error);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PrintJobInfo?> getInfo() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
Map<String, dynamic>? infoMap =
|
||||
(await channel?.invokeMethod('getInfo', args))?.cast<String, dynamic>();
|
||||
return PrintJobInfo.fromMap(infoMap);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await channel?.invokeMethod('dispose', args);
|
||||
disposeChannel();
|
||||
}
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
export 'web_authenticate_session.dart';
|
||||
Vendored
Executable
+164
@@ -0,0 +1,164 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSWebAuthenticationSession].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformWebAuthenticationSessionCreationParams] for
|
||||
/// more information.
|
||||
class MacOSWebAuthenticationSessionCreationParams
|
||||
extends PlatformWebAuthenticationSessionCreationParams {
|
||||
/// Creates a new [MacOSWebAuthenticationSessionCreationParams] instance.
|
||||
const MacOSWebAuthenticationSessionCreationParams();
|
||||
|
||||
/// Creates a [MacOSWebAuthenticationSessionCreationParams] instance based on [PlatformWebAuthenticationSessionCreationParams].
|
||||
factory MacOSWebAuthenticationSessionCreationParams.fromPlatformWebAuthenticationSessionCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformWebAuthenticationSessionCreationParams params) {
|
||||
return MacOSWebAuthenticationSessionCreationParams();
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformWebAuthenticationSession}
|
||||
class MacOSWebAuthenticationSession extends PlatformWebAuthenticationSession
|
||||
with ChannelController {
|
||||
/// Constructs a [MacOSWebAuthenticationSession].
|
||||
MacOSWebAuthenticationSession(
|
||||
PlatformWebAuthenticationSessionCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSWebAuthenticationSessionCreationParams
|
||||
? params
|
||||
: MacOSWebAuthenticationSessionCreationParams
|
||||
.fromPlatformWebAuthenticationSessionCreationParams(params),
|
||||
);
|
||||
|
||||
static final MacOSWebAuthenticationSession _staticValue =
|
||||
MacOSWebAuthenticationSession(
|
||||
MacOSWebAuthenticationSessionCreationParams());
|
||||
|
||||
/// Provide static access.
|
||||
factory MacOSWebAuthenticationSession.static() {
|
||||
return _staticValue;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = IdGenerator.generate();
|
||||
|
||||
@override
|
||||
late final WebUri url;
|
||||
|
||||
@override
|
||||
late final String? callbackURLScheme;
|
||||
|
||||
@override
|
||||
late final WebAuthenticationSessionSettings? initialSettings;
|
||||
|
||||
@override
|
||||
late final WebAuthenticationSessionCompletionHandler onComplete;
|
||||
|
||||
static const MethodChannel _staticChannel = const MethodChannel(
|
||||
'com.pichillilorenzo/flutter_webauthenticationsession');
|
||||
|
||||
@override
|
||||
Future<MacOSWebAuthenticationSession> create(
|
||||
{required WebUri url,
|
||||
String? callbackURLScheme,
|
||||
WebAuthenticationSessionCompletionHandler onComplete,
|
||||
WebAuthenticationSessionSettings? initialSettings}) async {
|
||||
var session = MacOSWebAuthenticationSession._create(
|
||||
url: url,
|
||||
callbackURLScheme: callbackURLScheme,
|
||||
onComplete: onComplete,
|
||||
initialSettings: initialSettings);
|
||||
initialSettings =
|
||||
session.initialSettings ?? WebAuthenticationSessionSettings();
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("id", () => session.id);
|
||||
args.putIfAbsent("url", () => session.url.toString());
|
||||
args.putIfAbsent("callbackURLScheme", () => session.callbackURLScheme);
|
||||
args.putIfAbsent("initialSettings", () => initialSettings?.toMap());
|
||||
await _staticChannel.invokeMethod('create', args);
|
||||
return session;
|
||||
}
|
||||
|
||||
MacOSWebAuthenticationSession._create(
|
||||
{required this.url,
|
||||
this.callbackURLScheme,
|
||||
this.onComplete,
|
||||
WebAuthenticationSessionSettings? initialSettings})
|
||||
: super.implementation(MacOSWebAuthenticationSessionCreationParams()) {
|
||||
assert(url.toString().isNotEmpty);
|
||||
assert(['http', 'https'].contains(url.scheme),
|
||||
'The specified URL has an unsupported scheme. Only HTTP and HTTPS URLs are supported on iOS.');
|
||||
|
||||
this.initialSettings =
|
||||
initialSettings ?? WebAuthenticationSessionSettings();
|
||||
channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_webauthenticationsession_$id');
|
||||
handler = _handleMethod;
|
||||
initMethodCallHandler();
|
||||
}
|
||||
|
||||
_debugLog(String method, dynamic args) {
|
||||
debugLog(
|
||||
className: this.runtimeType.toString(),
|
||||
debugLoggingSettings:
|
||||
PlatformWebAuthenticationSession.debugLoggingSettings,
|
||||
id: id,
|
||||
method: method,
|
||||
args: args);
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
_debugLog(call.method, call.arguments);
|
||||
|
||||
switch (call.method) {
|
||||
case "onComplete":
|
||||
String? url = call.arguments["url"];
|
||||
WebUri? uri = url != null ? WebUri(url) : null;
|
||||
var error = WebAuthenticationSessionError.fromNativeValue(
|
||||
call.arguments["errorCode"]);
|
||||
if (onComplete != null) {
|
||||
onComplete!(uri, error);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> canStart() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await channel?.invokeMethod<bool>('canStart', args) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> start() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await channel?.invokeMethod<bool>('start', args) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> cancel() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await channel?.invokeMethod("cancel", args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await channel?.invokeMethod("dispose", args);
|
||||
disposeChannel();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isAvailable() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await _staticChannel.invokeMethod<bool>("isAvailable", args) ??
|
||||
false;
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export 'web_message_port.dart' hide InternalWebMessagePort;
|
||||
export 'web_message_channel.dart' hide InternalWebMessageChannel;
|
||||
export 'web_message_listener.dart';
|
||||
Vendored
+120
@@ -0,0 +1,120 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
import 'web_message_port.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSWebMessageChannel].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformWebMessageChannelCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSWebMessageChannelCreationParams
|
||||
extends PlatformWebMessageChannelCreationParams {
|
||||
/// Creates a new [MacOSWebMessageChannelCreationParams] instance.
|
||||
const MacOSWebMessageChannelCreationParams(
|
||||
{required super.id, required super.port1, required super.port2});
|
||||
|
||||
/// Creates a [MacOSWebMessageChannelCreationParams] instance based on [PlatformWebMessageChannelCreationParams].
|
||||
factory MacOSWebMessageChannelCreationParams.fromPlatformWebMessageChannelCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformWebMessageChannelCreationParams params) {
|
||||
return MacOSWebMessageChannelCreationParams(
|
||||
id: params.id, port1: params.port1, port2: params.port2);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MacOSWebMessageChannelCreationParams{id: $id, port1: $port1, port2: $port2}';
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageChannel}
|
||||
class MacOSWebMessageChannel extends PlatformWebMessageChannel
|
||||
with ChannelController {
|
||||
/// Constructs a [MacOSWebMessageChannel].
|
||||
MacOSWebMessageChannel(PlatformWebMessageChannelCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSWebMessageChannelCreationParams
|
||||
? params
|
||||
: MacOSWebMessageChannelCreationParams
|
||||
.fromPlatformWebMessageChannelCreationParams(params),
|
||||
) {
|
||||
channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_web_message_channel_${params.id}');
|
||||
handler = _handleMethod;
|
||||
initMethodCallHandler();
|
||||
}
|
||||
|
||||
static final MacOSWebMessageChannel _staticValue = MacOSWebMessageChannel(
|
||||
MacOSWebMessageChannelCreationParams(
|
||||
id: '',
|
||||
port1:
|
||||
MacOSWebMessagePort(MacOSWebMessagePortCreationParams(index: 0)),
|
||||
port2: MacOSWebMessagePort(
|
||||
MacOSWebMessagePortCreationParams(index: 1))));
|
||||
|
||||
/// Provide static access.
|
||||
factory MacOSWebMessageChannel.static() {
|
||||
return _staticValue;
|
||||
}
|
||||
|
||||
MacOSWebMessagePort get _macosPort1 => port1 as MacOSWebMessagePort;
|
||||
|
||||
MacOSWebMessagePort get _macosPort2 => port2 as MacOSWebMessagePort;
|
||||
|
||||
static MacOSWebMessageChannel? _fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
var webMessageChannel = MacOSWebMessageChannel(
|
||||
MacOSWebMessageChannelCreationParams(
|
||||
id: map["id"],
|
||||
port1: MacOSWebMessagePort(
|
||||
MacOSWebMessagePortCreationParams(index: 0)),
|
||||
port2: MacOSWebMessagePort(
|
||||
MacOSWebMessagePortCreationParams(index: 1))));
|
||||
webMessageChannel._macosPort1.webMessageChannel = webMessageChannel;
|
||||
webMessageChannel._macosPort2.webMessageChannel = webMessageChannel;
|
||||
return webMessageChannel;
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "onMessage":
|
||||
int index = call.arguments["index"];
|
||||
var port = index == 0 ? _macosPort1 : _macosPort2;
|
||||
if (port.onMessage != null) {
|
||||
WebMessage? message = call.arguments["message"] != null
|
||||
? WebMessage.fromMap(
|
||||
call.arguments["message"].cast<String, dynamic>())
|
||||
: null;
|
||||
port.onMessage!(message);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
MacOSWebMessageChannel? fromMap(Map<String, dynamic>? map) {
|
||||
return _fromMap(map);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposeChannel();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MacOSWebMessageChannel{id: $id, port1: $port1, port2: $port2}';
|
||||
}
|
||||
}
|
||||
|
||||
extension InternalWebMessageChannel on MacOSWebMessageChannel {
|
||||
MethodChannel? get internalChannel => channel;
|
||||
}
|
||||
Vendored
+164
@@ -0,0 +1,164 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSWebMessageListener].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformWebMessageListenerCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSWebMessageListenerCreationParams
|
||||
extends PlatformWebMessageListenerCreationParams {
|
||||
/// Creates a new [MacOSWebMessageListenerCreationParams] instance.
|
||||
const MacOSWebMessageListenerCreationParams(
|
||||
{required this.allowedOriginRules,
|
||||
required super.jsObjectName,
|
||||
super.onPostMessage});
|
||||
|
||||
/// Creates a [MacOSWebMessageListenerCreationParams] instance based on [PlatformWebMessageListenerCreationParams].
|
||||
factory MacOSWebMessageListenerCreationParams.fromPlatformWebMessageListenerCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformWebMessageListenerCreationParams params) {
|
||||
return MacOSWebMessageListenerCreationParams(
|
||||
allowedOriginRules: params.allowedOriginRules ?? Set.from(["*"]),
|
||||
jsObjectName: params.jsObjectName,
|
||||
onPostMessage: params.onPostMessage);
|
||||
}
|
||||
|
||||
@override
|
||||
final Set<String> allowedOriginRules;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MacOSWebMessageListenerCreationParams{jsObjectName: $jsObjectName, allowedOriginRules: $allowedOriginRules, onPostMessage: $onPostMessage}';
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageListener}
|
||||
class MacOSWebMessageListener extends PlatformWebMessageListener
|
||||
with ChannelController {
|
||||
/// Constructs a [MacOSWebMessageListener].
|
||||
MacOSWebMessageListener(PlatformWebMessageListenerCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSWebMessageListenerCreationParams
|
||||
? params
|
||||
: MacOSWebMessageListenerCreationParams
|
||||
.fromPlatformWebMessageListenerCreationParams(params),
|
||||
) {
|
||||
assert(!this._macosParams.allowedOriginRules.contains(""),
|
||||
"allowedOriginRules cannot contain empty strings");
|
||||
channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_web_message_listener_${_id}_${params.jsObjectName}');
|
||||
handler = _handleMethod;
|
||||
initMethodCallHandler();
|
||||
}
|
||||
|
||||
///Message Listener ID used internally.
|
||||
final String _id = IdGenerator.generate();
|
||||
|
||||
MacOSJavaScriptReplyProxy? _replyProxy;
|
||||
|
||||
MacOSWebMessageListenerCreationParams get _macosParams =>
|
||||
params as MacOSWebMessageListenerCreationParams;
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "onPostMessage":
|
||||
if (_replyProxy == null) {
|
||||
_replyProxy = MacOSJavaScriptReplyProxy(
|
||||
PlatformJavaScriptReplyProxyCreationParams(
|
||||
webMessageListener: this));
|
||||
}
|
||||
if (onPostMessage != null) {
|
||||
WebMessage? message = call.arguments["message"] != null
|
||||
? WebMessage.fromMap(
|
||||
call.arguments["message"].cast<String, dynamic>())
|
||||
: null;
|
||||
WebUri? sourceOrigin = call.arguments["sourceOrigin"] != null
|
||||
? WebUri(call.arguments["sourceOrigin"])
|
||||
: null;
|
||||
bool isMainFrame = call.arguments["isMainFrame"];
|
||||
onPostMessage!(message, sourceOrigin, isMainFrame, _replyProxy!);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposeChannel();
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"id": _id,
|
||||
"jsObjectName": params.jsObjectName,
|
||||
"allowedOriginRules": _macosParams.allowedOriginRules.toList(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return this.toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MacOSWebMessageListener{id: ${_id}, jsObjectName: ${params.jsObjectName}, allowedOriginRules: ${params.allowedOriginRules}, replyProxy: $_replyProxy}';
|
||||
}
|
||||
}
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSJavaScriptReplyProxy].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformJavaScriptReplyProxyCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSJavaScriptReplyProxyCreationParams
|
||||
extends PlatformJavaScriptReplyProxyCreationParams {
|
||||
/// Creates a new [MacOSJavaScriptReplyProxyCreationParams] instance.
|
||||
const MacOSJavaScriptReplyProxyCreationParams(
|
||||
{required super.webMessageListener});
|
||||
|
||||
/// Creates a [MacOSJavaScriptReplyProxyCreationParams] instance based on [PlatformJavaScriptReplyProxyCreationParams].
|
||||
factory MacOSJavaScriptReplyProxyCreationParams.fromPlatformJavaScriptReplyProxyCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformJavaScriptReplyProxyCreationParams params) {
|
||||
return MacOSJavaScriptReplyProxyCreationParams(
|
||||
webMessageListener: params.webMessageListener);
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.JavaScriptReplyProxy}
|
||||
class MacOSJavaScriptReplyProxy extends PlatformJavaScriptReplyProxy {
|
||||
/// Constructs a [MacOSWebMessageListener].
|
||||
MacOSJavaScriptReplyProxy(PlatformJavaScriptReplyProxyCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSJavaScriptReplyProxyCreationParams
|
||||
? params
|
||||
: MacOSJavaScriptReplyProxyCreationParams
|
||||
.fromPlatformJavaScriptReplyProxyCreationParams(params),
|
||||
);
|
||||
|
||||
MacOSWebMessageListener get _macosWebMessageListener =>
|
||||
params.webMessageListener as MacOSWebMessageListener;
|
||||
|
||||
@override
|
||||
Future<void> postMessage(WebMessage message) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('message', () => message.toMap());
|
||||
await _macosWebMessageListener.channel?.invokeMethod('postMessage', args);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MacOSJavaScriptReplyProxy{}';
|
||||
}
|
||||
}
|
||||
Vendored
+95
@@ -0,0 +1,95 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
import 'web_message_channel.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSWebMessagePort].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformWebMessagePortCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSWebMessagePortCreationParams
|
||||
extends PlatformWebMessagePortCreationParams {
|
||||
/// Creates a new [MacOSWebMessagePortCreationParams] instance.
|
||||
const MacOSWebMessagePortCreationParams({required super.index});
|
||||
|
||||
/// Creates a [MacOSWebMessagePortCreationParams] instance based on [PlatformWebMessagePortCreationParams].
|
||||
factory MacOSWebMessagePortCreationParams.fromPlatformWebMessagePortCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformWebMessagePortCreationParams params) {
|
||||
return MacOSWebMessagePortCreationParams(index: params.index);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MacOSWebMessagePortCreationParams{index: $index}';
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformWebMessagePort}
|
||||
class MacOSWebMessagePort extends PlatformWebMessagePort {
|
||||
WebMessageCallback? _onMessage;
|
||||
late MacOSWebMessageChannel _webMessageChannel;
|
||||
|
||||
/// Constructs a [MacOSWebMessagePort].
|
||||
MacOSWebMessagePort(PlatformWebMessagePortCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSWebMessagePortCreationParams
|
||||
? params
|
||||
: MacOSWebMessagePortCreationParams
|
||||
.fromPlatformWebMessagePortCreationParams(params),
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> setWebMessageCallback(WebMessageCallback? onMessage) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('index', () => params.index);
|
||||
await _webMessageChannel.internalChannel
|
||||
?.invokeMethod('setWebMessageCallback', args);
|
||||
this._onMessage = onMessage;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> postMessage(WebMessage message) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('index', () => params.index);
|
||||
args.putIfAbsent('message', () => message.toMap());
|
||||
await _webMessageChannel.internalChannel?.invokeMethod('postMessage', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('index', () => params.index);
|
||||
await _webMessageChannel.internalChannel?.invokeMethod('close', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"index": params.index,
|
||||
"webMessageChannelId": this._webMessageChannel.params.id
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MacOSWebMessagePort{index: ${params.index}}';
|
||||
}
|
||||
}
|
||||
|
||||
extension InternalWebMessagePort on MacOSWebMessagePort {
|
||||
WebMessageCallback? get onMessage => _onMessage;
|
||||
void set onMessage(WebMessageCallback? value) => _onMessage = value;
|
||||
|
||||
MacOSWebMessageChannel get webMessageChannel => _webMessageChannel;
|
||||
void set webMessageChannel(MacOSWebMessageChannel value) =>
|
||||
_webMessageChannel = value;
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export 'web_storage.dart';
|
||||
export 'web_storage_manager.dart' hide InternalWebStorageManager;
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
import '../in_app_webview/in_app_webview_controller.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSWebStorage].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformWebStorageCreationParams] for
|
||||
/// more information.
|
||||
class MacOSWebStorageCreationParams extends PlatformWebStorageCreationParams {
|
||||
/// Creates a new [MacOSWebStorageCreationParams] instance.
|
||||
MacOSWebStorageCreationParams(
|
||||
{required super.localStorage, required super.sessionStorage});
|
||||
|
||||
/// Creates a [MacOSWebStorageCreationParams] instance based on [PlatformWebStorageCreationParams].
|
||||
factory MacOSWebStorageCreationParams.fromPlatformWebStorageCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformWebStorageCreationParams params) {
|
||||
return MacOSWebStorageCreationParams(
|
||||
localStorage: params.localStorage,
|
||||
sessionStorage: params.sessionStorage);
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformWebStorage}
|
||||
class MacOSWebStorage extends PlatformWebStorage {
|
||||
/// Constructs a [MacOSWebStorage].
|
||||
MacOSWebStorage(PlatformWebStorageCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSWebStorageCreationParams
|
||||
? params
|
||||
: MacOSWebStorageCreationParams
|
||||
.fromPlatformWebStorageCreationParams(params),
|
||||
);
|
||||
|
||||
@override
|
||||
PlatformLocalStorage get localStorage => params.localStorage;
|
||||
|
||||
@override
|
||||
PlatformSessionStorage get sessionStorage => params.sessionStorage;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
localStorage.dispose();
|
||||
sessionStorage.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSStorage].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformStorageCreationParams] for
|
||||
/// more information.
|
||||
class MacOSStorageCreationParams extends PlatformStorageCreationParams {
|
||||
/// Creates a new [MacOSStorageCreationParams] instance.
|
||||
MacOSStorageCreationParams(
|
||||
{required super.controller, required super.webStorageType});
|
||||
|
||||
/// Creates a [MacOSStorageCreationParams] instance based on [PlatformStorageCreationParams].
|
||||
factory MacOSStorageCreationParams.fromPlatformStorageCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformStorageCreationParams params) {
|
||||
return MacOSStorageCreationParams(
|
||||
controller: params.controller, webStorageType: params.webStorageType);
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformStorage}
|
||||
abstract mixin class MacOSStorage implements PlatformStorage {
|
||||
@override
|
||||
MacOSInAppWebViewController? controller;
|
||||
|
||||
@override
|
||||
Future<int?> length() async {
|
||||
var result = await controller?.evaluateJavascript(source: """
|
||||
window.$webStorageType.length;
|
||||
""");
|
||||
return result != null ? int.parse(json.decode(result)) : null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setItem({required String key, required dynamic value}) async {
|
||||
var encodedValue = json.encode(value);
|
||||
await controller?.evaluateJavascript(source: """
|
||||
window.$webStorageType.setItem("$key", ${value is String ? encodedValue : "JSON.stringify($encodedValue)"});
|
||||
""");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<dynamic> getItem({required String key}) async {
|
||||
var itemValue = await controller?.evaluateJavascript(source: """
|
||||
window.$webStorageType.getItem("$key");
|
||||
""");
|
||||
|
||||
if (itemValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return json.decode(itemValue);
|
||||
} catch (e) {}
|
||||
|
||||
return itemValue;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeItem({required String key}) async {
|
||||
await controller?.evaluateJavascript(source: """
|
||||
window.$webStorageType.removeItem("$key");
|
||||
""");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<WebStorageItem>> getItems() async {
|
||||
var webStorageItems = <WebStorageItem>[];
|
||||
|
||||
List<Map<dynamic, dynamic>>? items =
|
||||
(await controller?.evaluateJavascript(source: """
|
||||
(function() {
|
||||
var webStorageItems = [];
|
||||
for(var i = 0; i < window.$webStorageType.length; i++){
|
||||
var key = window.$webStorageType.key(i);
|
||||
webStorageItems.push(
|
||||
{
|
||||
key: key,
|
||||
value: window.$webStorageType.getItem(key)
|
||||
}
|
||||
);
|
||||
}
|
||||
return webStorageItems;
|
||||
})();
|
||||
"""))?.cast<Map<dynamic, dynamic>>();
|
||||
|
||||
if (items == null) {
|
||||
return webStorageItems;
|
||||
}
|
||||
|
||||
for (var item in items) {
|
||||
webStorageItems
|
||||
.add(WebStorageItem(key: item["key"], value: item["value"]));
|
||||
}
|
||||
|
||||
return webStorageItems;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clear() async {
|
||||
await controller?.evaluateJavascript(source: """
|
||||
window.$webStorageType.clear();
|
||||
""");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> key({required int index}) async {
|
||||
var result = await controller?.evaluateJavascript(source: """
|
||||
window.$webStorageType.key($index);
|
||||
""");
|
||||
return result != null ? json.decode(result) : null;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSLocalStorage].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformLocalStorageCreationParams] for
|
||||
/// more information.
|
||||
class MacOSLocalStorageCreationParams
|
||||
extends PlatformLocalStorageCreationParams {
|
||||
/// Creates a new [MacOSLocalStorageCreationParams] instance.
|
||||
MacOSLocalStorageCreationParams(super.params);
|
||||
|
||||
/// Creates a [MacOSLocalStorageCreationParams] instance based on [PlatformLocalStorageCreationParams].
|
||||
factory MacOSLocalStorageCreationParams.fromPlatformLocalStorageCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformLocalStorageCreationParams params) {
|
||||
return MacOSLocalStorageCreationParams(params);
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformLocalStorage}
|
||||
class MacOSLocalStorage extends PlatformLocalStorage with MacOSStorage {
|
||||
/// Constructs a [MacOSLocalStorage].
|
||||
MacOSLocalStorage(PlatformLocalStorageCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSLocalStorageCreationParams
|
||||
? params
|
||||
: MacOSLocalStorageCreationParams
|
||||
.fromPlatformLocalStorageCreationParams(params),
|
||||
);
|
||||
|
||||
/// Default storage
|
||||
factory MacOSLocalStorage.defaultStorage(
|
||||
{required PlatformInAppWebViewController? controller}) {
|
||||
return MacOSLocalStorage(MacOSLocalStorageCreationParams(
|
||||
PlatformLocalStorageCreationParams(PlatformStorageCreationParams(
|
||||
controller: controller,
|
||||
webStorageType: WebStorageType.LOCAL_STORAGE))));
|
||||
}
|
||||
|
||||
@override
|
||||
MacOSInAppWebViewController? get controller =>
|
||||
params.controller as MacOSInAppWebViewController?;
|
||||
}
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSSessionStorage].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformSessionStorageCreationParams] for
|
||||
/// more information.
|
||||
class MacOSSessionStorageCreationParams
|
||||
extends PlatformSessionStorageCreationParams {
|
||||
/// Creates a new [MacOSSessionStorageCreationParams] instance.
|
||||
MacOSSessionStorageCreationParams(super.params);
|
||||
|
||||
/// Creates a [MacOSSessionStorageCreationParams] instance based on [PlatformSessionStorageCreationParams].
|
||||
factory MacOSSessionStorageCreationParams.fromPlatformSessionStorageCreationParams(
|
||||
// Recommended placeholder to prevent being broken by platform interface.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformSessionStorageCreationParams params) {
|
||||
return MacOSSessionStorageCreationParams(params);
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformSessionStorage}
|
||||
class MacOSSessionStorage extends PlatformSessionStorage with MacOSStorage {
|
||||
/// Constructs a [MacOSSessionStorage].
|
||||
MacOSSessionStorage(PlatformSessionStorageCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSSessionStorageCreationParams
|
||||
? params
|
||||
: MacOSSessionStorageCreationParams
|
||||
.fromPlatformSessionStorageCreationParams(params),
|
||||
);
|
||||
|
||||
/// Default storage
|
||||
factory MacOSSessionStorage.defaultStorage(
|
||||
{required PlatformInAppWebViewController? controller}) {
|
||||
return MacOSSessionStorage(MacOSSessionStorageCreationParams(
|
||||
PlatformSessionStorageCreationParams(PlatformStorageCreationParams(
|
||||
controller: controller,
|
||||
webStorageType: WebStorageType.SESSION_STORAGE))));
|
||||
}
|
||||
|
||||
@override
|
||||
MacOSInAppWebViewController? get controller =>
|
||||
params.controller as MacOSInAppWebViewController?;
|
||||
}
|
||||
Vendored
Executable
+134
@@ -0,0 +1,134 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart';
|
||||
|
||||
/// Object specifying creation parameters for creating a [MacOSWebStorageManager].
|
||||
///
|
||||
/// When adding additional fields make sure they can be null or have a default
|
||||
/// value to avoid breaking changes. See [PlatformWebStorageManagerCreationParams] for
|
||||
/// more information.
|
||||
@immutable
|
||||
class MacOSWebStorageManagerCreationParams
|
||||
extends PlatformWebStorageManagerCreationParams {
|
||||
/// Creates a new [MacOSWebStorageManagerCreationParams] instance.
|
||||
const MacOSWebStorageManagerCreationParams(
|
||||
// This parameter prevents breaking changes later.
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
PlatformWebStorageManagerCreationParams params,
|
||||
) : super();
|
||||
|
||||
/// Creates a [MacOSWebStorageManagerCreationParams] instance based on [PlatformWebStorageManagerCreationParams].
|
||||
factory MacOSWebStorageManagerCreationParams.fromPlatformWebStorageManagerCreationParams(
|
||||
PlatformWebStorageManagerCreationParams params) {
|
||||
return MacOSWebStorageManagerCreationParams(params);
|
||||
}
|
||||
}
|
||||
|
||||
///{@macro flutter_inappwebview_platform_interface.PlatformWebStorageManager}
|
||||
class MacOSWebStorageManager extends PlatformWebStorageManager
|
||||
with ChannelController {
|
||||
/// Creates a new [MacOSWebStorageManager].
|
||||
MacOSWebStorageManager(PlatformWebStorageManagerCreationParams params)
|
||||
: super.implementation(
|
||||
params is MacOSWebStorageManagerCreationParams
|
||||
? params
|
||||
: MacOSWebStorageManagerCreationParams
|
||||
.fromPlatformWebStorageManagerCreationParams(params),
|
||||
) {
|
||||
channel = const MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_webstoragemanager');
|
||||
handler = handleMethod;
|
||||
initMethodCallHandler();
|
||||
}
|
||||
|
||||
static MacOSWebStorageManager? _instance;
|
||||
|
||||
///Gets the WebStorage manager shared instance.
|
||||
static MacOSWebStorageManager instance() {
|
||||
return (_instance != null) ? _instance! : _init();
|
||||
}
|
||||
|
||||
static MacOSWebStorageManager _init() {
|
||||
_instance = MacOSWebStorageManager(MacOSWebStorageManagerCreationParams(
|
||||
const PlatformWebStorageManagerCreationParams()));
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {}
|
||||
|
||||
@override
|
||||
Future<List<WebsiteDataRecord>> fetchDataRecords(
|
||||
{required Set<WebsiteDataType> dataTypes}) async {
|
||||
List<WebsiteDataRecord> recordList = [];
|
||||
List<String> dataTypesList = [];
|
||||
for (var dataType in dataTypes) {
|
||||
dataTypesList.add(dataType.toNativeValue());
|
||||
}
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("dataTypes", () => dataTypesList);
|
||||
List<Map<dynamic, dynamic>> records =
|
||||
(await channel?.invokeMethod<List>('fetchDataRecords', args))
|
||||
?.cast<Map<dynamic, dynamic>>() ??
|
||||
[];
|
||||
for (var record in records) {
|
||||
List<String> dataTypesString = record["dataTypes"].cast<String>();
|
||||
Set<WebsiteDataType> dataTypes = Set();
|
||||
for (var dataTypeValue in dataTypesString) {
|
||||
var dataType = WebsiteDataType.fromNativeValue(dataTypeValue);
|
||||
if (dataType != null) {
|
||||
dataTypes.add(dataType);
|
||||
}
|
||||
}
|
||||
recordList.add(WebsiteDataRecord(
|
||||
displayName: record["displayName"], dataTypes: dataTypes));
|
||||
}
|
||||
return recordList;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeDataFor(
|
||||
{required Set<WebsiteDataType> dataTypes,
|
||||
required List<WebsiteDataRecord> dataRecords}) async {
|
||||
List<String> dataTypesList = [];
|
||||
for (var dataType in dataTypes) {
|
||||
dataTypesList.add(dataType.toNativeValue());
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> recordList = [];
|
||||
for (var record in dataRecords) {
|
||||
recordList.add(record.toMap());
|
||||
}
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("dataTypes", () => dataTypesList);
|
||||
args.putIfAbsent("recordList", () => recordList);
|
||||
await channel?.invokeMethod('removeDataFor', args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeDataModifiedSince(
|
||||
{required Set<WebsiteDataType> dataTypes, required DateTime date}) async {
|
||||
List<String> dataTypesList = [];
|
||||
for (var dataType in dataTypes) {
|
||||
dataTypesList.add(dataType.toNativeValue());
|
||||
}
|
||||
|
||||
var timestamp = date.millisecondsSinceEpoch;
|
||||
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("dataTypes", () => dataTypesList);
|
||||
args.putIfAbsent("timestamp", () => timestamp);
|
||||
await channel?.invokeMethod('removeDataModifiedSince', args);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
extension InternalWebStorageManager on MacOSWebStorageManager {
|
||||
get handleMethod => _handleMethod;
|
||||
}
|
||||
Vendored
Executable
+172
@@ -0,0 +1,172 @@
|
||||
//
|
||||
// CredentialDatabase.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 29/10/2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class CredentialDatabase: ChannelDelegate {
|
||||
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_credential_database"
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
static var credentialStore = URLCredentialStorage.shared
|
||||
|
||||
init(plugin: InAppWebViewFlutterPlugin) {
|
||||
super.init(channel: FlutterMethodChannel(name: CredentialDatabase.METHOD_CHANNEL_NAME, binaryMessenger: plugin.registrar!.messenger))
|
||||
self.plugin = plugin
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
switch call.method {
|
||||
case "getAllAuthCredentials":
|
||||
var allCredentials: [[String: Any?]] = []
|
||||
|
||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore.allCredentials {
|
||||
var crendentials: [[String: Any?]] = []
|
||||
for c in credentials {
|
||||
let credential: [String: Any?] = c.value.toMap()
|
||||
crendentials.append(credential)
|
||||
}
|
||||
if crendentials.count > 0 {
|
||||
let dict: [String : Any] = [
|
||||
"protectionSpace": protectionSpace.toMap(),
|
||||
"credentials": crendentials
|
||||
]
|
||||
allCredentials.append(dict)
|
||||
}
|
||||
}
|
||||
result(allCredentials)
|
||||
break
|
||||
case "getHttpAuthCredentials":
|
||||
var crendentials: [[String: Any?]] = []
|
||||
|
||||
let host = arguments!["host"] as! String
|
||||
let urlProtocol = arguments!["protocol"] as? String
|
||||
let urlPort = arguments!["port"] as? Int ?? 0
|
||||
var realm = arguments!["realm"] as? String;
|
||||
if let r = realm, r.isEmpty {
|
||||
realm = nil
|
||||
}
|
||||
|
||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore.allCredentials {
|
||||
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
||||
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
|
||||
for c in credentials {
|
||||
crendentials.append(c.value.toMap())
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
result(crendentials)
|
||||
break
|
||||
case "setHttpAuthCredential":
|
||||
let host = arguments!["host"] as! String
|
||||
let urlProtocol = arguments!["protocol"] as? String
|
||||
let urlPort = arguments!["port"] as? Int ?? 0
|
||||
var realm = arguments!["realm"] as? String;
|
||||
if let r = realm, r.isEmpty {
|
||||
realm = nil
|
||||
}
|
||||
let username = arguments!["username"] as! String
|
||||
let password = arguments!["password"] as! String
|
||||
let credential = URLCredential(user: username, password: password, persistence: .permanent)
|
||||
CredentialDatabase.credentialStore.set(credential,
|
||||
for: URLProtectionSpace(host: host, port: urlPort, protocol: urlProtocol,
|
||||
realm: realm, authenticationMethod: NSURLAuthenticationMethodHTTPBasic))
|
||||
result(true)
|
||||
break
|
||||
case "removeHttpAuthCredential":
|
||||
let host = arguments!["host"] as! String
|
||||
let urlProtocol = arguments!["protocol"] as? String
|
||||
let urlPort = arguments!["port"] as? Int ?? 0
|
||||
var realm = arguments!["realm"] as? String;
|
||||
if let r = realm, r.isEmpty {
|
||||
realm = nil
|
||||
}
|
||||
let username = arguments!["username"] as! String
|
||||
let password = arguments!["password"] as! String
|
||||
|
||||
var credential: URLCredential? = nil;
|
||||
var protectionSpaceCredential: URLProtectionSpace? = nil
|
||||
|
||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore.allCredentials {
|
||||
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
||||
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
|
||||
for c in credentials {
|
||||
if c.value.user == username, c.value.password == password {
|
||||
credential = c.value
|
||||
protectionSpaceCredential = protectionSpace
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if credential != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let c = credential, let protectionSpace = protectionSpaceCredential {
|
||||
CredentialDatabase.credentialStore.remove(c, for: protectionSpace)
|
||||
}
|
||||
|
||||
result(true)
|
||||
break
|
||||
case "removeHttpAuthCredentials":
|
||||
let host = arguments!["host"] as! String
|
||||
let urlProtocol = arguments!["protocol"] as? String
|
||||
let urlPort = arguments!["port"] as? Int ?? 0
|
||||
var realm = arguments!["realm"] as? String;
|
||||
if let r = realm, r.isEmpty {
|
||||
realm = nil
|
||||
}
|
||||
|
||||
var credentialsToRemove: [URLCredential] = [];
|
||||
var protectionSpaceCredential: URLProtectionSpace? = nil
|
||||
|
||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore.allCredentials {
|
||||
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
||||
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
|
||||
protectionSpaceCredential = protectionSpace
|
||||
for c in credentials {
|
||||
if let _ = c.value.user, let _ = c.value.password {
|
||||
credentialsToRemove.append(c.value)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let protectionSpace = protectionSpaceCredential {
|
||||
for credential in credentialsToRemove {
|
||||
CredentialDatabase.credentialStore.remove(credential, for: protectionSpace)
|
||||
}
|
||||
}
|
||||
|
||||
result(true)
|
||||
break
|
||||
case "clearAllAuthCredentials":
|
||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore.allCredentials {
|
||||
for credential in credentials {
|
||||
CredentialDatabase.credentialStore.remove(credential.value, for: protectionSpace)
|
||||
}
|
||||
}
|
||||
result(true)
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// FindInteractionChannelDelegate.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class FindInteractionChannelDelegate: ChannelDelegate {
|
||||
private weak var findInteractionController: FindInteractionController?
|
||||
|
||||
public init(findInteractionController: FindInteractionController, channel: FlutterMethodChannel) {
|
||||
super.init(channel: channel)
|
||||
self.findInteractionController = findInteractionController
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "findAll":
|
||||
if let findInteractionController = findInteractionController {
|
||||
let find = arguments!["find"] as! String
|
||||
findInteractionController.findAll(find: find, completionHandler: {(value, error) in
|
||||
if error != nil {
|
||||
result(FlutterError(code: "FindInteractionChannelDelegate", message: error?.localizedDescription, details: nil))
|
||||
return
|
||||
}
|
||||
result(true)
|
||||
})
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "findNext":
|
||||
if let findInteractionController = findInteractionController {
|
||||
let forward = arguments!["forward"] as! Bool
|
||||
findInteractionController.findNext(forward: forward, completionHandler: {(value, error) in
|
||||
if error != nil {
|
||||
result(FlutterError(code: "FindInteractionChannelDelegate", message: error?.localizedDescription, details: nil))
|
||||
return
|
||||
}
|
||||
result(true)
|
||||
})
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "clearMatches":
|
||||
if let findInteractionController = findInteractionController {
|
||||
findInteractionController.clearMatches(completionHandler: {(value, error) in
|
||||
if error != nil {
|
||||
result(FlutterError(code: "FindInteractionChannelDelegate", message: error?.localizedDescription, details: nil))
|
||||
return
|
||||
}
|
||||
result(true)
|
||||
})
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "setSearchText":
|
||||
if let findInteractionController = findInteractionController {
|
||||
let searchText = arguments!["searchText"] as? String
|
||||
findInteractionController.searchText = searchText
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "getSearchText":
|
||||
result(findInteractionController?.searchText)
|
||||
break
|
||||
case "getActiveFindSession":
|
||||
if let findInteractionController = findInteractionController {
|
||||
result(findInteractionController.activeFindSession?.toMap())
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func onFindResultReceived(activeMatchOrdinal: Int, numberOfMatches: Int, isDoneCounting: Bool) {
|
||||
if isDoneCounting, let findInteractionController = findInteractionController {
|
||||
findInteractionController.activeFindSession = FindSession(resultCount: numberOfMatches,
|
||||
highlightedResultIndex: activeMatchOrdinal,
|
||||
searchResultDisplayStyle: 2) // matches UIFindSession.SearchResultDisplayStyle.none
|
||||
}
|
||||
|
||||
let arguments: [String : Any?] = [
|
||||
"activeMatchOrdinal": activeMatchOrdinal,
|
||||
"numberOfMatches": numberOfMatches,
|
||||
"isDoneCounting": isDoneCounting
|
||||
]
|
||||
channel?.invokeMethod("onFindResultReceived", arguments: arguments)
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
findInteractionController = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// FindInteractionController.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class FindInteractionController: NSObject, Disposable {
|
||||
|
||||
static var METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_find_interaction_";
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
var webView: InAppWebView?
|
||||
var channelDelegate: FindInteractionChannelDelegate?
|
||||
var settings: FindInteractionSettings?
|
||||
var shouldCallOnRefresh = false
|
||||
var searchText: String?
|
||||
var activeFindSession: FindSession?
|
||||
|
||||
public init(plugin: InAppWebViewFlutterPlugin, id: Any, webView: InAppWebView, settings: FindInteractionSettings?) {
|
||||
super.init()
|
||||
self.plugin = plugin
|
||||
self.webView = webView
|
||||
self.settings = settings
|
||||
if let registrar = plugin.registrar {
|
||||
let channel = FlutterMethodChannel(name: FindInteractionController.METHOD_CHANNEL_NAME_PREFIX + String(describing: id),
|
||||
binaryMessenger: registrar.messenger)
|
||||
self.channelDelegate = FindInteractionChannelDelegate(findInteractionController: self, channel: channel)
|
||||
}
|
||||
}
|
||||
|
||||
public func prepare() {
|
||||
// if let settings = settings {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
public func findAll(find: String?, completionHandler: ((Any?, Error?) -> Void)?) {
|
||||
guard let webView else {
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(nil, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var find = find
|
||||
if find == nil {
|
||||
find = searchText
|
||||
} else {
|
||||
// updated searchText
|
||||
searchText = find
|
||||
}
|
||||
|
||||
guard let find else {
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(nil, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if find != "" {
|
||||
let startSearch = "window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsync('\(find)');"
|
||||
webView.evaluateJavaScript(startSearch, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
public func findNext(forward: Bool, completionHandler: ((Any?, Error?) -> Void)?) {
|
||||
guard let webView else {
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(nil, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
webView.evaluateJavaScript("window.\(JAVASCRIPT_BRIDGE_NAME)._findNext(\(forward ? "true" : "false"));", completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func clearMatches(completionHandler: ((Any?, Error?) -> Void)?) {
|
||||
guard let webView else {
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(nil, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
webView.evaluateJavaScript("window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatches();", completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
channelDelegate?.dispose()
|
||||
channelDelegate = nil
|
||||
webView = nil
|
||||
activeFindSession = nil
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("FindInteractionControl - dealloc")
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// FindInteractionSettings.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class FindInteractionSettings: ISettings<FindInteractionController> {
|
||||
|
||||
override init(){
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func parse(settings: [String: Any?]) -> FindInteractionSettings {
|
||||
let _ = super.parse(settings: settings)
|
||||
return self
|
||||
}
|
||||
|
||||
override func getRealSettings(obj: FindInteractionController?) -> [String: Any?] {
|
||||
let realSettings: [String: Any?] = toMap()
|
||||
return realSettings
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Internal Gopeed host bridge. No extension-facing API.
|
||||
import Foundation
|
||||
import WebKit
|
||||
import Network
|
||||
import FlutterMacOS
|
||||
|
||||
final class GopeedProfiles {
|
||||
private static var stores: [String: WKWebsiteDataStore] = [:]
|
||||
static func store(_ identifier: String) -> WKWebsiteDataStore? { stores[identifier] }
|
||||
|
||||
static func prepare(arguments: NSDictionary?, result: @escaping FlutterResult) {
|
||||
guard #available(macOS 14.0, iOS 17.0, *),
|
||||
let identifier = arguments?["gopeedProfileId"] as? String,
|
||||
let uuid = UUID(uuidString: identifier),
|
||||
let proxyURL = arguments?["proxyUrl"] as? String,
|
||||
let url = URL(string: proxyURL), url.scheme == "socks5",
|
||||
let host = url.host, host == "127.0.0.1",
|
||||
let port = url.port, port > 0 && port <= 65535 else {
|
||||
result(FlutterError(code: "UNAVAILABLE", message: "Isolated WebView profiles and proxy require macOS 14 / iOS 17 or newer and valid host configuration", details: nil))
|
||||
return
|
||||
}
|
||||
let store = stores[identifier] ?? WKWebsiteDataStore(forIdentifier: uuid)
|
||||
let endpoint = nw_endpoint_create_host(host, String(port))
|
||||
let proxy = nw_proxy_config_create_socksv5(endpoint)
|
||||
store.__proxyConfigurations = [proxy]
|
||||
stores[identifier] = store
|
||||
result(true)
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// HeadlessInAppWebView.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 26/03/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class HeadlessInAppWebView: Disposable {
|
||||
static let METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_headless_inappwebview_"
|
||||
var id: String
|
||||
var channelDelegate: HeadlessWebViewChannelDelegate?
|
||||
var flutterWebView: FlutterWebViewController?
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
|
||||
public init(plugin: InAppWebViewFlutterPlugin, id: String, flutterWebView: FlutterWebViewController) {
|
||||
self.id = id
|
||||
self.flutterWebView = flutterWebView
|
||||
self.plugin = plugin
|
||||
let channel = FlutterMethodChannel(name: HeadlessInAppWebView.METHOD_CHANNEL_NAME_PREFIX + id,
|
||||
binaryMessenger: plugin.registrar!.messenger)
|
||||
self.channelDelegate = HeadlessWebViewChannelDelegate(headlessWebView: self, channel: channel)
|
||||
}
|
||||
|
||||
public func onWebViewCreated() {
|
||||
channelDelegate?.onWebViewCreated();
|
||||
}
|
||||
|
||||
public func prepare(params: NSDictionary) {
|
||||
if let view = flutterWebView?.view() {
|
||||
view.alphaValue = 0.01
|
||||
let initialSize = params["initialSize"] as? [String: Any?]
|
||||
if let size = Size2D.fromMap(map: initialSize) {
|
||||
setSize(size: size)
|
||||
} else {
|
||||
view.frame = CGRect(x: 0.0, y: 0.0, width: NSApplication.shared.mainWindow?.contentView?.bounds.width ?? 0.0,
|
||||
height: NSApplication.shared.mainWindow?.contentView?.bounds.height ?? 0.0)
|
||||
}
|
||||
/// Note: The WKWebView behaves very unreliable when rendering offscreen
|
||||
/// on a device. This is especially true with JavaScript, which simply
|
||||
/// won't be executed sometimes.
|
||||
/// So, add the headless WKWebView to the view hierarchy.
|
||||
/// This way is also possible to take screenshots.
|
||||
let wrapperView = NSView() // wrapper view with frame zero
|
||||
wrapperView.addSubview(view, positioned: .below, relativeTo: nil)
|
||||
NSApplication.shared.mainWindow?.contentView?.addSubview(wrapperView, positioned: .below, relativeTo: nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func setSize(size: Size2D) {
|
||||
if let view = flutterWebView?.view() {
|
||||
let width = size.width == -1.0 ? NSApplication.shared.mainWindow?.contentView?.bounds.width ?? 0.0 : CGFloat(size.width)
|
||||
let height = size.height == -1.0 ? NSApplication.shared.mainWindow?.contentView?.bounds.height ?? 0.0 : CGFloat(size.height)
|
||||
view.frame = CGRect(x: 0.0, y: 0.0, width: width, height: height)
|
||||
}
|
||||
}
|
||||
|
||||
public func getSize() -> Size2D? {
|
||||
if let view = flutterWebView?.view() {
|
||||
return Size2D(width: Double(view.frame.width), height: Double(view.frame.height))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func disposeAndGetFlutterWebView(withFrame frame: CGRect) -> FlutterWebViewController? {
|
||||
let newFlutterWebView = flutterWebView
|
||||
if let view = flutterWebView?.view() {
|
||||
// restore WebView frame and alpha
|
||||
view.frame = frame
|
||||
view.alphaValue = 1.0
|
||||
// remove from parent
|
||||
view.removeFromSuperview()
|
||||
dispose(disposeWebView: false)
|
||||
}
|
||||
return newFlutterWebView
|
||||
}
|
||||
|
||||
|
||||
public func dispose(disposeWebView: Bool) {
|
||||
channelDelegate?.dispose()
|
||||
channelDelegate = nil
|
||||
plugin?.headlessInAppWebViewManager?.webViews[id] = nil
|
||||
if disposeWebView {
|
||||
flutterWebView?.dispose(removeFromSuperview: true)
|
||||
}
|
||||
flutterWebView = nil
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
dispose(disposeWebView: true)
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("HeadlessInAppWebView - dealloc")
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// HeadlessInAppWebViewManager.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 10/05/2020.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import FlutterMacOS
|
||||
import AppKit
|
||||
import WebKit
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
public class HeadlessInAppWebViewManager: ChannelDelegate {
|
||||
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_headless_inappwebview"
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
var webViews: [String: HeadlessInAppWebView?] = [:]
|
||||
|
||||
init(plugin: InAppWebViewFlutterPlugin) {
|
||||
super.init(channel: FlutterMethodChannel(name: HeadlessInAppWebViewManager.METHOD_CHANNEL_NAME, binaryMessenger: plugin.registrar!.messenger))
|
||||
self.plugin = plugin
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
let id: String = arguments!["id"] as! String
|
||||
|
||||
switch call.method {
|
||||
case "run":
|
||||
let params = arguments!["params"] as! [String: Any?]
|
||||
run(id: id, params: params)
|
||||
result(true)
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func run(id: String, params: [String: Any?]) {
|
||||
guard let plugin = plugin else {
|
||||
return
|
||||
}
|
||||
let flutterWebView = FlutterWebViewController(plugin: plugin,
|
||||
withFrame: CGRect.zero,
|
||||
viewIdentifier: id,
|
||||
params: params as NSDictionary)
|
||||
let headlessInAppWebView = HeadlessInAppWebView(plugin: plugin, id: id, flutterWebView: flutterWebView)
|
||||
webViews[id] = headlessInAppWebView
|
||||
|
||||
headlessInAppWebView.prepare(params: params as NSDictionary)
|
||||
headlessInAppWebView.onWebViewCreated()
|
||||
flutterWebView.makeInitialLoad(params: params as NSDictionary)
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
let headlessWebViews = webViews.values
|
||||
headlessWebViews.forEach { (headlessWebView: HeadlessInAppWebView?) in
|
||||
headlessWebView?.dispose()
|
||||
}
|
||||
webViews.removeAll()
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// HeadlessWebViewChannelDelegate.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 05/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class HeadlessWebViewChannelDelegate: ChannelDelegate {
|
||||
private weak var headlessWebView: HeadlessInAppWebView?
|
||||
|
||||
public init(headlessWebView: HeadlessInAppWebView, channel: FlutterMethodChannel) {
|
||||
super.init(channel: channel)
|
||||
self.headlessWebView = headlessWebView
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "dispose":
|
||||
if let headlessWebView = headlessWebView {
|
||||
headlessWebView.dispose()
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "setSize":
|
||||
if let headlessWebView = headlessWebView {
|
||||
let sizeMap = arguments!["size"] as? [String: Any?]
|
||||
if let size = Size2D.fromMap(map: sizeMap) {
|
||||
headlessWebView.setSize(size: size)
|
||||
}
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "getSize":
|
||||
result(headlessWebView?.getSize()?.toMap())
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func onWebViewCreated() {
|
||||
let arguments: [String: Any?] = [:]
|
||||
channel?.invokeMethod("onWebViewCreated", arguments: arguments)
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
headlessWebView = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Options.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo on 26/09/18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers
|
||||
public class ISettings<T>: NSObject {
|
||||
|
||||
override init(){
|
||||
super.init()
|
||||
}
|
||||
|
||||
func parse(settings: [String: Any?]) -> ISettings<T> {
|
||||
for (key, value) in settings {
|
||||
if !(value is NSNull), value != nil {
|
||||
if self.responds(to: Selector(key)) {
|
||||
self.setValue(value, forKey: key)
|
||||
} else if self.responds(to: Selector("_" + key)) {
|
||||
self.setValue(value, forKey: "_" + key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func toMap() -> [String: Any?] {
|
||||
var settings: [String: Any?] = [:]
|
||||
let mirrored_object = Mirror(reflecting: self)
|
||||
for (_, attr) in mirrored_object.children.enumerated() {
|
||||
if let property_name = attr.label as String? {
|
||||
settings[property_name] = attr.value
|
||||
}
|
||||
}
|
||||
return settings
|
||||
}
|
||||
|
||||
func getRealSettings(obj: T?) -> [String: Any?] {
|
||||
let realSettings: [String: Any?] = toMap()
|
||||
return realSettings
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// InAppBrowserChannelDelegate.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 05/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class InAppBrowserChannelDelegate: ChannelDelegate {
|
||||
public override init(channel: FlutterMethodChannel) {
|
||||
super.init(channel: channel)
|
||||
}
|
||||
|
||||
public func onBrowserCreated() {
|
||||
let arguments: [String: Any?] = [:]
|
||||
channel?.invokeMethod("onBrowserCreated", arguments: arguments)
|
||||
}
|
||||
|
||||
public func onMenuItemClicked(menuItem: InAppBrowserMenuItem) {
|
||||
let arguments: [String: Any?] = [
|
||||
"id": menuItem.id
|
||||
]
|
||||
channel?.invokeMethod("onMenuItemClicked", arguments: arguments)
|
||||
}
|
||||
|
||||
public func onMainWindowWillClose() {
|
||||
let arguments: [String: Any?] = [:]
|
||||
channel?.invokeMethod("onMainWindowWillClose", arguments: arguments)
|
||||
}
|
||||
|
||||
public func onExit() {
|
||||
let arguments: [String: Any?] = [:]
|
||||
channel?.invokeMethod("onExit", arguments: arguments)
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// InAppBrowserDelegate.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 14/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol InAppBrowserDelegate {
|
||||
func didChangeTitle(title: String?)
|
||||
func didStartNavigation(url: URL?)
|
||||
func didUpdateVisitedHistory(url: URL?)
|
||||
func didFinishNavigation(url: URL?)
|
||||
func didFailNavigation(url: URL?, error: Error)
|
||||
func didChangeProgress(progress: Double)
|
||||
}
|
||||
Vendored
Executable
+122
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// InAppBrowserManager.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 18/12/2019.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import AppKit
|
||||
import WebKit
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
public class InAppBrowserManager: ChannelDelegate {
|
||||
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappbrowser"
|
||||
static let WEBVIEW_STORYBOARD = "WebView"
|
||||
static let WEBVIEW_STORYBOARD_CONTROLLER_ID = "viewController"
|
||||
static let NAV_STORYBOARD_CONTROLLER_ID = "navController"
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
|
||||
init(plugin: InAppWebViewFlutterPlugin) {
|
||||
super.init(channel: FlutterMethodChannel(name: InAppBrowserManager.METHOD_CHANNEL_NAME, binaryMessenger: plugin.registrar!.messenger))
|
||||
self.plugin = plugin
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "open":
|
||||
open(arguments: arguments!)
|
||||
result(true)
|
||||
break
|
||||
case "openWithSystemBrowser":
|
||||
let url = arguments!["url"] as! String
|
||||
openWithSystemBrowser(url: url, result: result)
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func open(arguments: NSDictionary) {
|
||||
let id = arguments["id"] as! String
|
||||
let urlRequest = arguments["urlRequest"] as? [String:Any?]
|
||||
let assetFilePath = arguments["assetFilePath"] as? String
|
||||
let data = arguments["data"] as? String
|
||||
let mimeType = arguments["mimeType"] as? String
|
||||
let encoding = arguments["encoding"] as? String
|
||||
let baseUrl = arguments["baseUrl"] as? String
|
||||
let settings = arguments["settings"] as! [String: Any?]
|
||||
let windowId = arguments["windowId"] as? Int64
|
||||
let initialUserScripts = arguments["initialUserScripts"] as? [[String: Any]]
|
||||
let menuItems = arguments["menuItems"] as! [[String: Any?]]
|
||||
|
||||
let browserSettings = InAppBrowserSettings()
|
||||
let _ = browserSettings.parse(settings: settings)
|
||||
|
||||
let webViewSettings = InAppWebViewSettings()
|
||||
let _ = webViewSettings.parse(settings: settings)
|
||||
|
||||
let webViewController = InAppBrowserWebViewController()
|
||||
webViewController.plugin = plugin
|
||||
webViewController.browserSettings = browserSettings
|
||||
webViewController.webViewSettings = webViewSettings
|
||||
|
||||
webViewController.id = id
|
||||
webViewController.initialUrlRequest = urlRequest != nil ? URLRequest.init(fromPluginMap: urlRequest!) : nil
|
||||
webViewController.initialFile = assetFilePath
|
||||
webViewController.initialData = data
|
||||
webViewController.initialMimeType = mimeType
|
||||
webViewController.initialEncoding = encoding
|
||||
webViewController.initialBaseUrl = baseUrl
|
||||
webViewController.windowId = windowId
|
||||
webViewController.initialUserScripts = initialUserScripts ?? []
|
||||
webViewController.isHidden = browserSettings.hidden
|
||||
|
||||
let window = InAppBrowserWindow(contentViewController: webViewController)
|
||||
window.browserSettings = browserSettings
|
||||
window.contentViewController = webViewController
|
||||
for menuItem in menuItems {
|
||||
window.menuItems.append(InAppBrowserMenuItem.fromMap(map: menuItem)!)
|
||||
}
|
||||
window.prepare()
|
||||
|
||||
if #available(macOS 10.12, *), browserSettings.windowType == .tabbed {
|
||||
NSApplication.shared.mainWindow?.addTabbedWindow(window, ordered: .above)
|
||||
} else if browserSettings.windowType == .child {
|
||||
NSApplication.shared.mainWindow?.addChildWindow(window, ordered: .above)
|
||||
} else {
|
||||
window.windowController?.showWindow(self)
|
||||
}
|
||||
|
||||
window.makeKeyAndOrderFront(self)
|
||||
if browserSettings.hidden {
|
||||
// https://github.com/pichillilorenzo/flutter_inappwebview/issues/1939
|
||||
// without calling first window.makeKeyAndOrderFront(self)
|
||||
// window.hide() would deallocate and dispose the InAppBrowserWindow
|
||||
window.hide()
|
||||
NSApplication.shared.mainWindow?.makeKeyAndOrderFront(self)
|
||||
}
|
||||
}
|
||||
|
||||
public func openWithSystemBrowser(url: String, result: @escaping FlutterResult) {
|
||||
let absoluteUrl = URL(string: url)!.absoluteURL
|
||||
if !NSWorkspace.shared.open(absoluteUrl) {
|
||||
result(FlutterError(code: "InAppBrowserManager", message: url + " cannot be opened!", details: nil))
|
||||
return
|
||||
}
|
||||
result(true)
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+91
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// InAppBrowserOptions.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo on 17/09/18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers
|
||||
public class InAppBrowserSettings: ISettings<InAppBrowserWebViewController> {
|
||||
|
||||
var hidden = false
|
||||
var hideToolbarTop = true
|
||||
var toolbarTopBackgroundColor: String?
|
||||
var hideUrlBar = false
|
||||
var hideProgressBar = false
|
||||
var toolbarTopFixedTitle: String?
|
||||
var windowType = InAppBrowserWindowType.window
|
||||
var windowAlphaValue = 1.0
|
||||
var _windowStyleMask: NSNumber?
|
||||
var windowStyleMask: NSWindow.StyleMask? {
|
||||
get {
|
||||
return _windowStyleMask != nil ?
|
||||
NSWindow.StyleMask.init(rawValue: _windowStyleMask!.uintValue) :
|
||||
nil
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_windowStyleMask = NSNumber.init(value: newValue.rawValue)
|
||||
} else {
|
||||
_windowStyleMask = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
var _windowTitlebarSeparatorStyle: NSNumber?
|
||||
@available(macOS 11.0, *)
|
||||
var windowTitlebarSeparatorStyle: NSTitlebarSeparatorStyle? {
|
||||
get {
|
||||
return _windowTitlebarSeparatorStyle != nil ?
|
||||
NSTitlebarSeparatorStyle.init(rawValue: _windowTitlebarSeparatorStyle!.intValue) :
|
||||
nil
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_windowTitlebarSeparatorStyle = NSNumber.init(value: newValue.rawValue)
|
||||
} else {
|
||||
_windowTitlebarSeparatorStyle = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
var windowFrame: NSRect?
|
||||
var hideDefaultMenuItems = false
|
||||
|
||||
override init(){
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func parse(settings: [String: Any?]) -> InAppBrowserSettings {
|
||||
let _ = super.parse(settings: settings)
|
||||
if let windowType = settings["windowType"] as? String {
|
||||
self.windowType = InAppBrowserWindowType.init(rawValue: windowType) ?? InAppBrowserWindowType.child
|
||||
}
|
||||
if let windowFrame = settings["windowFrame"] as? [String:Double] {
|
||||
self.windowFrame = NSRect(x: windowFrame["x"]!,
|
||||
y: windowFrame["y"]!,
|
||||
width: windowFrame["width"]!,
|
||||
height: windowFrame["height"]!)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
override func getRealSettings(obj: InAppBrowserWebViewController?) -> [String: Any?] {
|
||||
var realOptions: [String: Any?] = toMap()
|
||||
realOptions["windowType"] = windowType.rawValue
|
||||
if let inAppBrowserWebViewController = obj {
|
||||
realOptions["hidden"] = inAppBrowserWebViewController.isHidden
|
||||
realOptions["hideUrlBar"] = inAppBrowserWebViewController.window?.searchBar?.isHidden
|
||||
realOptions["hideProgressBar"] = inAppBrowserWebViewController.progressBar.isHidden
|
||||
realOptions["hideToolbarTop"] = !(inAppBrowserWebViewController.window?.toolbar?.isVisible ?? true)
|
||||
realOptions["toolbarTopBackgroundColor"] = inAppBrowserWebViewController.window?.backgroundColor.hexString
|
||||
realOptions["windowAlphaValue"] = inAppBrowserWebViewController.window?.alphaValue
|
||||
realOptions["windowStyleMask"] = inAppBrowserWebViewController.window?.styleMask.rawValue
|
||||
if #available(macOS 11.0, *) {
|
||||
realOptions["windowTitlebarSeparatorStyle"] = inAppBrowserWebViewController.window?.titlebarSeparatorStyle.rawValue
|
||||
}
|
||||
realOptions["windowFrame"] = inAppBrowserWebViewController.window?.frame.toMap()
|
||||
}
|
||||
return realOptions
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+339
@@ -0,0 +1,339 @@
|
||||
//
|
||||
// InAppBrowserWebViewController.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo on 17/09/18.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import AppKit
|
||||
import WebKit
|
||||
import Foundation
|
||||
|
||||
public class InAppBrowserWebViewController: NSViewController, InAppBrowserDelegate, Disposable {
|
||||
static var METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappbrowser_";
|
||||
|
||||
var progressBar: NSProgressIndicator!
|
||||
|
||||
var window: InAppBrowserWindow?
|
||||
var id: String = ""
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
var windowId: Int64?
|
||||
var webView: InAppWebView?
|
||||
var channelDelegate: InAppBrowserChannelDelegate?
|
||||
var initialUrlRequest: URLRequest?
|
||||
var initialFile: String?
|
||||
var browserSettings: InAppBrowserSettings?
|
||||
var webViewSettings: InAppWebViewSettings?
|
||||
var initialData: String?
|
||||
var initialMimeType: String?
|
||||
var initialEncoding: String?
|
||||
var initialBaseUrl: String?
|
||||
var initialUserScripts: [[String: Any]] = []
|
||||
var isHidden = false
|
||||
|
||||
public override func loadView() {
|
||||
guard let plugin = plugin, let registrar = plugin.registrar else {
|
||||
return
|
||||
}
|
||||
|
||||
let channel = FlutterMethodChannel(name: InAppBrowserWebViewController.METHOD_CHANNEL_NAME_PREFIX + id, binaryMessenger: registrar.messenger)
|
||||
channelDelegate = InAppBrowserChannelDelegate(channel: channel)
|
||||
|
||||
var userScripts: [UserScript] = []
|
||||
for initialUserScript in initialUserScripts {
|
||||
userScripts.append(UserScript.fromMap(map: initialUserScript, windowId: windowId)!)
|
||||
}
|
||||
|
||||
let preWebviewConfiguration = InAppWebView.preWKWebViewConfiguration(settings: webViewSettings)
|
||||
if let wId = windowId, let webViewTransport = plugin.inAppWebViewManager?.windowWebViews[wId] {
|
||||
webView = webViewTransport.webView
|
||||
webView!.initialUserScripts = userScripts
|
||||
} else {
|
||||
webView = InAppWebView(id: nil,
|
||||
plugin: nil,
|
||||
frame: .zero,
|
||||
configuration: preWebviewConfiguration,
|
||||
userScripts: userScripts)
|
||||
}
|
||||
|
||||
guard let webView = webView else {
|
||||
return
|
||||
}
|
||||
|
||||
webView.inAppBrowserDelegate = self
|
||||
webView.id = id
|
||||
webView.plugin = plugin
|
||||
webView.channelDelegate = WebViewChannelDelegate(webView: webView, channel: channel)
|
||||
|
||||
let findInteractionController = FindInteractionController(
|
||||
plugin: plugin,
|
||||
id: id, webView: webView, settings: nil)
|
||||
webView.findInteractionController = findInteractionController
|
||||
findInteractionController.prepare()
|
||||
|
||||
prepareWebView()
|
||||
webView.windowCreated = true
|
||||
|
||||
progressBar = NSProgressIndicator()
|
||||
progressBar.style = .bar
|
||||
progressBar.isIndeterminate = false
|
||||
progressBar.startAnimation(self)
|
||||
|
||||
view = NSView(frame: NSApplication.shared.mainWindow?.frame ?? .zero)
|
||||
view.addSubview(webView)
|
||||
view.addSubview(progressBar, positioned: .above, relativeTo: webView)
|
||||
}
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
webView?.translatesAutoresizingMaskIntoConstraints = false
|
||||
progressBar.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
webView?.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0.0).isActive = true
|
||||
webView?.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0.0).isActive = true
|
||||
webView?.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0.0).isActive = true
|
||||
webView?.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0.0).isActive = true
|
||||
|
||||
progressBar.topAnchor.constraint(equalTo: self.view.topAnchor, constant: -6.0).isActive = true
|
||||
progressBar.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0.0).isActive = true
|
||||
progressBar.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0.0).isActive = true
|
||||
|
||||
if let wId = windowId {
|
||||
channelDelegate?.onBrowserCreated()
|
||||
webView?.runWindowBeforeCreatedCallbacks()
|
||||
} else {
|
||||
if #available(macOS 10.13, *) {
|
||||
if let contentBlockers = webView?.settings?.contentBlockers, contentBlockers.count > 0 {
|
||||
do {
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: contentBlockers, options: [])
|
||||
let blockRules = String(data: jsonData, encoding: .utf8)
|
||||
WKContentRuleListStore.default().compileContentRuleList(
|
||||
forIdentifier: "ContentBlockingRules",
|
||||
encodedContentRuleList: blockRules) { (contentRuleList, error) in
|
||||
|
||||
if let error = error {
|
||||
print(error.localizedDescription)
|
||||
return
|
||||
}
|
||||
|
||||
let configuration = self.webView!.configuration
|
||||
configuration.userContentController.add(contentRuleList!)
|
||||
|
||||
self.initLoad()
|
||||
}
|
||||
return
|
||||
} catch {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initLoad()
|
||||
}
|
||||
}
|
||||
|
||||
public override func viewDidAppear() {
|
||||
super.viewDidAppear()
|
||||
window = view.window as? InAppBrowserWindow
|
||||
}
|
||||
|
||||
public func initLoad() {
|
||||
if let initialFile = initialFile {
|
||||
do {
|
||||
try webView?.loadFile(assetFilePath: initialFile)
|
||||
}
|
||||
catch let error as NSError {
|
||||
dump(error)
|
||||
}
|
||||
}
|
||||
else if let initialData = initialData {
|
||||
let baseUrl = URL(string: initialBaseUrl ?? "about:blank")!
|
||||
var allowingReadAccessToURL: URL? = nil
|
||||
if let allowingReadAccessTo = webView?.settings?.allowingReadAccessTo, baseUrl.scheme == "file" {
|
||||
allowingReadAccessToURL = URL(string: allowingReadAccessTo)
|
||||
if allowingReadAccessToURL?.scheme != "file" {
|
||||
allowingReadAccessToURL = nil
|
||||
}
|
||||
}
|
||||
webView?.loadData(data: initialData, mimeType: initialMimeType!, encoding: initialEncoding!, baseUrl: baseUrl, allowingReadAccessTo: allowingReadAccessToURL)
|
||||
}
|
||||
else if let initialUrlRequest = initialUrlRequest {
|
||||
var allowingReadAccessToURL: URL? = nil
|
||||
if let allowingReadAccessTo = webView?.settings?.allowingReadAccessTo, let url = initialUrlRequest.url, url.scheme == "file" {
|
||||
allowingReadAccessToURL = URL(string: allowingReadAccessTo)
|
||||
if allowingReadAccessToURL?.scheme != "file" {
|
||||
allowingReadAccessToURL = nil
|
||||
}
|
||||
}
|
||||
webView?.loadUrl(urlRequest: initialUrlRequest, allowingReadAccessTo: allowingReadAccessToURL)
|
||||
}
|
||||
|
||||
channelDelegate?.onBrowserCreated()
|
||||
}
|
||||
|
||||
public func prepareWebView() {
|
||||
webView?.settings = webViewSettings
|
||||
webView?.prepare()
|
||||
|
||||
if let browserSettings = browserSettings {
|
||||
if browserSettings.hideProgressBar {
|
||||
progressBar.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func didChangeTitle(title: String?) {
|
||||
guard let title = title else {
|
||||
return
|
||||
}
|
||||
if let browserSettings = browserSettings,
|
||||
let toolbarTopFixedTitle = browserSettings.toolbarTopFixedTitle {
|
||||
window?.title = toolbarTopFixedTitle
|
||||
} else {
|
||||
window?.title = title
|
||||
}
|
||||
window?.update()
|
||||
}
|
||||
|
||||
public func didStartNavigation(url: URL?) {
|
||||
window?.forwardButton?.isEnabled = webView?.canGoForward ?? false
|
||||
window?.backButton?.isEnabled = webView?.canGoBack ?? false
|
||||
progressBar.doubleValue = 0.0
|
||||
progressBar.isHidden = false
|
||||
guard let url = url else {
|
||||
return
|
||||
}
|
||||
window?.searchBar?.stringValue = url.absoluteString
|
||||
}
|
||||
|
||||
public func didUpdateVisitedHistory(url: URL?) {
|
||||
window?.forwardButton?.isEnabled = webView?.canGoForward ?? false
|
||||
window?.backButton?.isEnabled = webView?.canGoBack ?? false
|
||||
guard let url = url else {
|
||||
return
|
||||
}
|
||||
window?.searchBar?.stringValue = url.absoluteString
|
||||
}
|
||||
|
||||
public func didFinishNavigation(url: URL?) {
|
||||
window?.forwardButton?.isEnabled = webView?.canGoForward ?? false
|
||||
window?.backButton?.isEnabled = webView?.canGoBack ?? false
|
||||
progressBar.doubleValue = 0.0
|
||||
progressBar.isHidden = true
|
||||
guard let url = url else {
|
||||
return
|
||||
}
|
||||
window?.searchBar?.stringValue = url.absoluteString
|
||||
}
|
||||
|
||||
public func didFailNavigation(url: URL?, error: Error) {
|
||||
window?.forwardButton?.isEnabled = webView?.canGoForward ?? false
|
||||
window?.backButton?.isEnabled = webView?.canGoBack ?? false
|
||||
progressBar.doubleValue = 0.0
|
||||
progressBar.isHidden = true
|
||||
}
|
||||
|
||||
public func didChangeProgress(progress: Double) {
|
||||
progressBar.isHidden = false
|
||||
progressBar.doubleValue = progress * 100
|
||||
if progress == 100.0 {
|
||||
progressBar.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func reload() {
|
||||
webView?.reload()
|
||||
didUpdateVisitedHistory(url: webView?.url)
|
||||
}
|
||||
|
||||
@objc public func goBack() {
|
||||
if let webView = webView, webView.canGoBack {
|
||||
webView.goBack()
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func goForward() {
|
||||
if let webView = webView, webView.canGoForward {
|
||||
webView.goForward()
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func goBackOrForward(steps: Int) {
|
||||
webView?.goBackOrForward(steps: steps)
|
||||
}
|
||||
|
||||
@objc public func onMenuItemClicked(sender: Any?) {
|
||||
var identifier: String?
|
||||
if let sender = sender as? NSMenuItem {
|
||||
identifier = sender.identifier?.rawValue
|
||||
}
|
||||
if identifier == nil, let sender = sender as? NSButton {
|
||||
identifier = sender.identifier?.rawValue
|
||||
}
|
||||
if let identifier = identifier, let window = window {
|
||||
let menuItem = window.menuItems.first { item in
|
||||
return item.id == Int64(identifier)
|
||||
}
|
||||
if let menuItem = menuItem {
|
||||
channelDelegate?.onMenuItemClicked(menuItem: menuItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func setSettings(newSettings: InAppBrowserSettings, newSettingsMap: [String: Any]) {
|
||||
window?.setSettings(newSettings: newSettings, newSettingsMap: newSettingsMap)
|
||||
|
||||
let newInAppWebViewSettings = InAppWebViewSettings()
|
||||
let _ = newInAppWebViewSettings.parse(settings: newSettingsMap)
|
||||
webView?.setSettings(newSettings: newInAppWebViewSettings, newSettingsMap: newSettingsMap)
|
||||
|
||||
if newSettingsMap["hideProgressBar"] != nil, browserSettings?.hideProgressBar != newSettings.hideProgressBar {
|
||||
progressBar.isHidden = newSettings.hideProgressBar
|
||||
}
|
||||
|
||||
browserSettings = newSettings
|
||||
webViewSettings = newInAppWebViewSettings
|
||||
}
|
||||
|
||||
public func getSettings() -> [String: Any?]? {
|
||||
let webViewSettingsMap = webView?.getSettings()
|
||||
if (self.browserSettings == nil || webViewSettingsMap == nil) {
|
||||
return nil
|
||||
}
|
||||
var settingsMap = self.browserSettings!.getRealSettings(obj: self)
|
||||
settingsMap.merge(webViewSettingsMap!, uniquingKeysWith: { (current, _) in current })
|
||||
return settingsMap
|
||||
}
|
||||
|
||||
public func hide() {
|
||||
isHidden = true
|
||||
window?.hide()
|
||||
}
|
||||
|
||||
public func show() {
|
||||
isHidden = false
|
||||
window?.show()
|
||||
}
|
||||
|
||||
public func close() {
|
||||
window?.close()
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
channelDelegate?.onExit()
|
||||
channelDelegate?.dispose()
|
||||
channelDelegate = nil
|
||||
webView?.dispose()
|
||||
webView?.removeFromSuperview()
|
||||
webView = nil
|
||||
window = nil
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("InAppBrowserWebViewController - dealloc")
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+387
@@ -0,0 +1,387 @@
|
||||
//
|
||||
// InAppBrowserNavigationController.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 14/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct ToolbarIdentifiers {
|
||||
static let searchBar = NSToolbarItem.Identifier(rawValue: "SearchBar")
|
||||
static let backButton = NSToolbarItem.Identifier(rawValue: "BackButton")
|
||||
static let forwardButton = NSToolbarItem.Identifier(rawValue: "ForwardButton")
|
||||
static let reloadButton = NSToolbarItem.Identifier(rawValue: "ReloadButton")
|
||||
static let menuButton = NSToolbarItem.Identifier(rawValue: "MenuButton")
|
||||
}
|
||||
|
||||
public class InAppBrowserWindow: NSWindow, NSWindowDelegate, NSToolbarDelegate, NSSearchFieldDelegate {
|
||||
var searchItem: NSToolbarItem?
|
||||
var backItem: NSToolbarItem?
|
||||
var forwardItem: NSToolbarItem?
|
||||
var reloadItem: NSToolbarItem?
|
||||
var menuItem: NSToolbarItem?
|
||||
var actionItems: [NSToolbarItem] = []
|
||||
|
||||
var reloadButton: NSButton? {
|
||||
get {
|
||||
return reloadItem?.view as? NSButton
|
||||
}
|
||||
}
|
||||
var backButton: NSButton? {
|
||||
get {
|
||||
return backItem?.view as? NSButton
|
||||
}
|
||||
}
|
||||
var forwardButton: NSButton? {
|
||||
get {
|
||||
return forwardItem?.view as? NSButton
|
||||
}
|
||||
}
|
||||
var searchBar: NSSearchField? {
|
||||
get {
|
||||
if #available(macOS 11.0, *), let searchItem = searchItem as? NSSearchToolbarItem {
|
||||
return searchItem.searchField
|
||||
} else {
|
||||
return searchItem?.view as? NSSearchField
|
||||
}
|
||||
}
|
||||
}
|
||||
var menuButton: NSPopUpButton? {
|
||||
get {
|
||||
return menuItem?.view as? NSPopUpButton
|
||||
}
|
||||
}
|
||||
|
||||
var browserSettings: InAppBrowserSettings?
|
||||
var menuItems: [InAppBrowserMenuItem] = []
|
||||
|
||||
public func prepare() {
|
||||
title = ""
|
||||
collectionBehavior = .fullScreenPrimary
|
||||
delegate = self
|
||||
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(onMainWindowWillClose(_:)),
|
||||
name: NSWindow.willCloseNotification,
|
||||
object: NSApplication.shared.mainWindow)
|
||||
|
||||
if #available(macOS 10.13, *) {
|
||||
let windowToolbar = NSToolbar()
|
||||
windowToolbar.delegate = self
|
||||
if #available(macOS 11.0, *) {
|
||||
searchItem = NSSearchToolbarItem(itemIdentifier: ToolbarIdentifiers.searchBar)
|
||||
(searchItem as! NSSearchToolbarItem).searchField.delegate = self
|
||||
toolbarStyle = .expanded
|
||||
} else {
|
||||
searchItem = NSToolbarItem(itemIdentifier: ToolbarIdentifiers.searchBar)
|
||||
let textField = NSSearchField()
|
||||
textField.usesSingleLineMode = true
|
||||
textField.delegate = self
|
||||
searchItem?.view = textField
|
||||
}
|
||||
searchItem?.label = ""
|
||||
windowToolbar.displayMode = .default
|
||||
|
||||
backItem = NSToolbarItem(itemIdentifier: ToolbarIdentifiers.backButton)
|
||||
backItem?.label = ""
|
||||
if let webViewController = contentViewController as? InAppBrowserWebViewController {
|
||||
if #available(macOS 11.0, *) {
|
||||
backItem?.view = NSButton(image: NSImage(systemSymbolName: "chevron.left",
|
||||
accessibilityDescription: "Go Back")!,
|
||||
target: webViewController,
|
||||
action: #selector(InAppBrowserWebViewController.goBack))
|
||||
} else {
|
||||
backItem?.view = NSButton(title: "\u{2039}",
|
||||
target: webViewController,
|
||||
action: #selector(InAppBrowserWebViewController.goBack))
|
||||
}
|
||||
}
|
||||
|
||||
forwardItem = NSToolbarItem(itemIdentifier: ToolbarIdentifiers.forwardButton)
|
||||
forwardItem?.label = ""
|
||||
if let webViewController = contentViewController as? InAppBrowserWebViewController {
|
||||
if #available(macOS 11.0, *) {
|
||||
forwardItem?.view = NSButton(image: NSImage(systemSymbolName: "chevron.right",
|
||||
accessibilityDescription: "Go Forward")!,
|
||||
target: webViewController,
|
||||
action: #selector(InAppBrowserWebViewController.goForward))
|
||||
} else {
|
||||
forwardItem?.view = NSButton(title: "\u{203A}",
|
||||
target: webViewController,
|
||||
action: #selector(InAppBrowserWebViewController.goForward))
|
||||
}
|
||||
}
|
||||
|
||||
reloadItem = NSToolbarItem(itemIdentifier: ToolbarIdentifiers.reloadButton)
|
||||
reloadItem?.label = ""
|
||||
if let webViewController = contentViewController as? InAppBrowserWebViewController {
|
||||
if #available(macOS 11.0, *) {
|
||||
reloadItem?.view = NSButton(image: NSImage(systemSymbolName: "arrow.counterclockwise",
|
||||
accessibilityDescription: "Reload")!,
|
||||
target: webViewController,
|
||||
action: #selector(InAppBrowserWebViewController.reload))
|
||||
} else {
|
||||
reloadItem?.view = NSButton(title: "Reload",
|
||||
target: webViewController,
|
||||
action: #selector(InAppBrowserWebViewController.reload))
|
||||
}
|
||||
}
|
||||
|
||||
if #available(macOS 10.15, *), !menuItems.isEmpty {
|
||||
menuItem = NSMenuToolbarItem(itemIdentifier: ToolbarIdentifiers.menuButton)
|
||||
if let menuItem = menuItem as? NSMenuToolbarItem {
|
||||
menuItem.label = ""
|
||||
if #available(macOS 11.0, *) {
|
||||
menuItem.image = NSImage(systemSymbolName: "ellipsis.circle",
|
||||
accessibilityDescription: "Options")
|
||||
menuItem.showsIndicator = true
|
||||
menuItem.isBordered = true
|
||||
} else {
|
||||
menuItem.title = "Options"
|
||||
}
|
||||
let menu = NSMenu()
|
||||
menuItems = menuItems.sorted(by: {$0.order ?? 0 < $1.order ?? 0})
|
||||
for item in menuItems {
|
||||
if !item.showAsAction {
|
||||
let nsItem = NSMenuItem(title: item.title, action: #selector(InAppBrowserWebViewController.onMenuItemClicked), keyEquivalent: "")
|
||||
nsItem.identifier = NSUserInterfaceItemIdentifier.init(String(item.id))
|
||||
nsItem.image = item.icon
|
||||
menu.addItem(nsItem)
|
||||
} else {
|
||||
let actionItem = NSMenuToolbarItem(itemIdentifier: NSToolbarItem.Identifier(rawValue: String(item.id)))
|
||||
actionItem.label = ""
|
||||
if let webViewController = contentViewController as? InAppBrowserWebViewController {
|
||||
let actionButton = NSButton(title: item.title,
|
||||
target: webViewController,
|
||||
action: #selector(InAppBrowserWebViewController.onMenuItemClicked))
|
||||
actionButton.identifier = NSUserInterfaceItemIdentifier.init(String(item.id))
|
||||
actionButton.image = item.icon
|
||||
actionItem.view = actionButton
|
||||
}
|
||||
actionItems.append(actionItem)
|
||||
}
|
||||
}
|
||||
menuItem.menu = menu
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if #available(macOS 10.14, *) {
|
||||
windowToolbar.centeredItemIdentifier = ToolbarIdentifiers.searchBar
|
||||
}
|
||||
toolbar = windowToolbar
|
||||
}
|
||||
|
||||
forwardButton?.isEnabled = false
|
||||
backButton?.isEnabled = false
|
||||
|
||||
if let browserSettings = browserSettings {
|
||||
if let toolbarTopFixedTitle = browserSettings.toolbarTopFixedTitle {
|
||||
title = toolbarTopFixedTitle
|
||||
}
|
||||
if !browserSettings.hideToolbarTop {
|
||||
toolbar?.isVisible = true
|
||||
if browserSettings.hideUrlBar {
|
||||
if #available(macOS 11.0, *) {
|
||||
(searchItem as! NSSearchToolbarItem).searchField.isHidden = true
|
||||
} else {
|
||||
searchItem?.view?.isHidden = true
|
||||
}
|
||||
}
|
||||
if let bgColor = browserSettings.toolbarTopBackgroundColor, !bgColor.isEmpty {
|
||||
backgroundColor = NSColor(hexString: bgColor)
|
||||
}
|
||||
}
|
||||
else {
|
||||
toolbar?.isVisible = false
|
||||
}
|
||||
if #available(macOS 11.0, *), let windowTitlebarSeparatorStyle = browserSettings.windowTitlebarSeparatorStyle {
|
||||
titlebarSeparatorStyle = windowTitlebarSeparatorStyle
|
||||
}
|
||||
alphaValue = browserSettings.windowAlphaValue
|
||||
if let windowStyleMask = browserSettings.windowStyleMask {
|
||||
styleMask = windowStyleMask
|
||||
}
|
||||
if let windowFrame = browserSettings.windowFrame {
|
||||
setFrame(windowFrame, display: true)
|
||||
}
|
||||
reloadItem?.view?.isHidden = browserSettings.hideDefaultMenuItems
|
||||
backItem?.view?.isHidden = browserSettings.hideDefaultMenuItems
|
||||
forwardItem?.view?.isHidden = browserSettings.hideDefaultMenuItems
|
||||
}
|
||||
}
|
||||
|
||||
public func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||
return [[ ToolbarIdentifiers.menuButton,
|
||||
ToolbarIdentifiers.searchBar,
|
||||
ToolbarIdentifiers.backButton,
|
||||
ToolbarIdentifiers.forwardButton,
|
||||
ToolbarIdentifiers.reloadButton,
|
||||
.flexibleSpace ], actionItems.compactMap({ item in
|
||||
return item.itemIdentifier
|
||||
})].flatMap { $0 }
|
||||
}
|
||||
|
||||
public func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||
return [[.flexibleSpace,
|
||||
ToolbarIdentifiers.searchBar,
|
||||
.flexibleSpace,
|
||||
ToolbarIdentifiers.reloadButton,
|
||||
ToolbarIdentifiers.backButton,
|
||||
ToolbarIdentifiers.forwardButton],
|
||||
actionItems.compactMap({ item in
|
||||
return item.itemIdentifier
|
||||
}),
|
||||
[ToolbarIdentifiers.menuButton]].flatMap { $0 }
|
||||
}
|
||||
|
||||
public func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
|
||||
switch(itemIdentifier) {
|
||||
case ToolbarIdentifiers.searchBar:
|
||||
return searchItem
|
||||
case ToolbarIdentifiers.backButton:
|
||||
return backItem
|
||||
case ToolbarIdentifiers.forwardButton:
|
||||
return forwardItem
|
||||
case ToolbarIdentifiers.reloadButton:
|
||||
return reloadItem
|
||||
case ToolbarIdentifiers.menuButton:
|
||||
return menuItem
|
||||
default:
|
||||
let actionItem = actionItems.first { item in
|
||||
return item.itemIdentifier == itemIdentifier
|
||||
}
|
||||
return actionItem
|
||||
}
|
||||
}
|
||||
|
||||
public func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
|
||||
if (commandSelector == #selector(NSResponder.insertNewline(_:))) {
|
||||
// ENTER key
|
||||
var searchField: NSSearchField? = nil
|
||||
if #available(macOS 11.0, *), let searchBar = searchItem as? NSSearchToolbarItem {
|
||||
searchField = searchBar.searchField
|
||||
} else if let searchBar = searchItem {
|
||||
searchField = searchBar.view as? NSSearchField
|
||||
}
|
||||
|
||||
guard let searchField,
|
||||
let urlEncoded = searchField.stringValue.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
|
||||
let url = URL(string: urlEncoded) else {
|
||||
return false
|
||||
}
|
||||
|
||||
let request = URLRequest(url: url)
|
||||
(contentViewController as? InAppBrowserWebViewController)?.webView?.load(request)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
public func hide() {
|
||||
orderOut(self)
|
||||
}
|
||||
|
||||
public func show() {
|
||||
let mainWindow = parent ?? NSApplication.shared.mainWindow
|
||||
if #available(macOS 10.12, *),
|
||||
!(mainWindow?.tabbedWindows?.contains(self) ?? false),
|
||||
browserSettings?.windowType == .tabbed {
|
||||
mainWindow?.addTabbedWindow(self, ordered: .above)
|
||||
} else if !(mainWindow?.childWindows?.contains(self) ?? false) {
|
||||
mainWindow?.addChildWindow(self, ordered: .above)
|
||||
}
|
||||
makeKeyAndOrderFront(self)
|
||||
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
public func setSettings(newSettings: InAppBrowserSettings, newSettingsMap: [String: Any]) {
|
||||
if newSettingsMap["hidden"] != nil, browserSettings?.hidden != newSettings.hidden {
|
||||
if newSettings.hidden {
|
||||
hide()
|
||||
}
|
||||
else {
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
if newSettingsMap["hideUrlBar"] != nil, browserSettings?.hideUrlBar != newSettings.hideUrlBar {
|
||||
searchBar?.isHidden = newSettings.hideUrlBar
|
||||
}
|
||||
|
||||
if newSettingsMap["hideToolbarTop"] != nil, browserSettings?.hideToolbarTop != newSettings.hideToolbarTop {
|
||||
toolbar?.isVisible = !newSettings.hideToolbarTop
|
||||
}
|
||||
|
||||
if newSettingsMap["toolbarTopBackgroundColor"] != nil, browserSettings?.toolbarTopBackgroundColor != newSettings.toolbarTopBackgroundColor {
|
||||
if let bgColor = newSettings.toolbarTopBackgroundColor, !bgColor.isEmpty {
|
||||
backgroundColor = NSColor(hexString: bgColor)
|
||||
} else {
|
||||
backgroundColor = nil
|
||||
}
|
||||
}
|
||||
if #available(macOS 11.0, *), newSettingsMap["windowTitlebarSeparatorStyle"] != nil,
|
||||
browserSettings?.windowTitlebarSeparatorStyle != newSettings.windowTitlebarSeparatorStyle {
|
||||
titlebarSeparatorStyle = newSettings.windowTitlebarSeparatorStyle!
|
||||
}
|
||||
if newSettingsMap["windowAlphaValue"] != nil, browserSettings?.windowAlphaValue != newSettings.windowAlphaValue {
|
||||
alphaValue = newSettings.windowAlphaValue
|
||||
}
|
||||
if newSettingsMap["windowStyleMask"] != nil, browserSettings?.windowStyleMask != newSettings.windowStyleMask {
|
||||
styleMask = newSettings.windowStyleMask!
|
||||
}
|
||||
if newSettingsMap["windowFrame"] != nil, browserSettings?.windowFrame != newSettings.windowFrame {
|
||||
setFrame(newSettings.windowFrame!, display: true)
|
||||
}
|
||||
if newSettingsMap["hideDefaultMenuItems"] != nil, browserSettings?.hideDefaultMenuItems != newSettings.hideDefaultMenuItems {
|
||||
reloadItem?.view?.isHidden = newSettings.hideDefaultMenuItems
|
||||
backItem?.view?.isHidden = newSettings.hideDefaultMenuItems
|
||||
forwardItem?.view?.isHidden = newSettings.hideDefaultMenuItems
|
||||
}
|
||||
browserSettings = newSettings
|
||||
}
|
||||
|
||||
public func windowWillClose(_ notification: Notification) {
|
||||
dispose()
|
||||
}
|
||||
|
||||
@objc func onMainWindowWillClose(_ notification: Notification) {
|
||||
if let webViewController = contentViewController as? InAppBrowserWebViewController {
|
||||
webViewController.channelDelegate?.onMainWindowWillClose()
|
||||
}
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
delegate = nil
|
||||
NotificationCenter.default.removeObserver(self,
|
||||
name: NSWindow.willCloseNotification,
|
||||
object: NSApplication.shared.mainWindow)
|
||||
if let webViewController = contentViewController as? InAppBrowserWebViewController {
|
||||
webViewController.dispose()
|
||||
}
|
||||
if #available(macOS 11.0, *) {
|
||||
(searchItem as? NSSearchToolbarItem)?.searchField.delegate = nil
|
||||
} else {
|
||||
(searchItem?.view as? NSTextField)?.delegate = nil
|
||||
searchItem?.view = nil
|
||||
}
|
||||
searchItem = nil
|
||||
(backItem?.view as? NSButton)?.target = nil
|
||||
backItem?.view = nil
|
||||
backItem = nil
|
||||
(forwardItem?.view as? NSButton)?.target = nil
|
||||
forwardItem?.view = nil
|
||||
forwardItem = nil
|
||||
(reloadItem?.view as? NSButton)?.target = nil
|
||||
reloadItem?.view = nil
|
||||
reloadItem = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("InAppBrowserWindow - dealloc")
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// ContextMenuOptions.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 30/05/2020.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class ContextMenuSettings: ISettings<NSObject> {
|
||||
|
||||
var hideDefaultSystemContextMenuItems = false;
|
||||
|
||||
override init(){
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+45
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// CustomSchemeHandler.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 25/10/2019.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
import WebKit
|
||||
|
||||
@available(macOS 10.13, *)
|
||||
public class CustomSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
var schemeHandlers: [Int: WKURLSchemeTask] = [:]
|
||||
|
||||
public func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
|
||||
schemeHandlers[urlSchemeTask.hash] = urlSchemeTask
|
||||
let inAppWebView = webView as! InAppWebView
|
||||
let request = WebResourceRequest.init(fromURLRequest: urlSchemeTask.request)
|
||||
let callback = WebViewChannelDelegate.LoadResourceWithCustomSchemeCallback()
|
||||
callback.nonNullSuccess = { (response: CustomSchemeResponse) in
|
||||
if (self.schemeHandlers[urlSchemeTask.hash] != nil) {
|
||||
let urlResponse = URLResponse(url: request.url, mimeType: response.contentType, expectedContentLength: -1, textEncodingName: response.contentEncoding)
|
||||
urlSchemeTask.didReceive(urlResponse)
|
||||
urlSchemeTask.didReceive(response.data)
|
||||
urlSchemeTask.didFinish()
|
||||
self.schemeHandlers.removeValue(forKey: urlSchemeTask.hash)
|
||||
}
|
||||
return false
|
||||
}
|
||||
callback.error = { (code: String, message: String?, details: Any?) in
|
||||
print(code + ", " + (message ?? ""))
|
||||
}
|
||||
|
||||
if let channelDelegate = inAppWebView.channelDelegate {
|
||||
channelDelegate.onLoadResourceWithCustomScheme(request: request, callback: callback)
|
||||
} else {
|
||||
callback.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
|
||||
schemeHandlers.removeValue(forKey: urlSchemeTask.hash)
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+202
@@ -0,0 +1,202 @@
|
||||
//
|
||||
// FlutterWebViewController.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo on 13/11/18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
import FlutterMacOS
|
||||
|
||||
public class FlutterWebViewController: NSView, Disposable {
|
||||
|
||||
var keepAliveId: String?
|
||||
|
||||
init(plugin: InAppWebViewFlutterPlugin, withFrame frame: CGRect, viewIdentifier viewId: Any, params: NSDictionary) {
|
||||
super.init(frame: frame)
|
||||
|
||||
keepAliveId = params["keepAliveId"] as? String
|
||||
|
||||
let initialSettings = params["initialSettings"] as! [String: Any?]
|
||||
let windowId = params["windowId"] as? Int64
|
||||
let initialUserScripts = params["initialUserScripts"] as? [[String: Any]]
|
||||
|
||||
var userScripts: [UserScript] = []
|
||||
if let initialUserScripts = initialUserScripts {
|
||||
for initialUserScript in initialUserScripts {
|
||||
userScripts.append(UserScript.fromMap(map: initialUserScript, windowId: windowId)!)
|
||||
}
|
||||
}
|
||||
|
||||
let settings = InAppWebViewSettings()
|
||||
let _ = settings.parse(settings: initialSettings)
|
||||
let preWebviewConfiguration = InAppWebView.preWKWebViewConfiguration(settings: settings)
|
||||
|
||||
var webView: InAppWebView?
|
||||
|
||||
if let wId = windowId, let webViewTransport = plugin.inAppWebViewManager?.windowWebViews[wId] {
|
||||
webView = webViewTransport.webView
|
||||
webView!.id = viewId
|
||||
webView!.plugin = plugin
|
||||
if let registrar = plugin.registrar {
|
||||
let channel = FlutterMethodChannel(name: InAppWebView.METHOD_CHANNEL_NAME_PREFIX + String(describing: viewId),
|
||||
binaryMessenger: registrar.messenger)
|
||||
webView!.channelDelegate = WebViewChannelDelegate(webView: webView!, channel: channel)
|
||||
}
|
||||
webView!.frame = self.bounds
|
||||
webView!.initialUserScripts = userScripts
|
||||
} else {
|
||||
webView = InAppWebView(id: viewId,
|
||||
plugin: plugin,
|
||||
frame: self.bounds,
|
||||
configuration: preWebviewConfiguration,
|
||||
userScripts: userScripts)
|
||||
}
|
||||
|
||||
let findInteractionController = FindInteractionController(
|
||||
plugin: plugin,
|
||||
id: viewId, webView: webView!, settings: nil)
|
||||
webView!.findInteractionController = findInteractionController
|
||||
findInteractionController.prepare()
|
||||
|
||||
webView!.autoresizingMask = [.width, .height]
|
||||
self.autoresizesSubviews = true
|
||||
self.autoresizingMask = [.width, .height]
|
||||
self.addSubview(webView!)
|
||||
|
||||
webView!.settings = settings
|
||||
webView!.prepare()
|
||||
webView!.windowCreated = true
|
||||
}
|
||||
|
||||
required init?(coder nsCoder: NSCoder) {
|
||||
super.init(coder: nsCoder)
|
||||
}
|
||||
|
||||
public func webView() -> InAppWebView? {
|
||||
for subview in self.subviews
|
||||
{
|
||||
if let item = subview as? InAppWebView
|
||||
{
|
||||
return item
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func view() -> NSView {
|
||||
return self
|
||||
}
|
||||
|
||||
public func makeInitialLoad(params: NSDictionary) {
|
||||
guard let webView = webView() else {
|
||||
return
|
||||
}
|
||||
|
||||
let windowId = params["windowId"] as? Int64
|
||||
let initialUrlRequest = params["initialUrlRequest"] as? [String: Any?]
|
||||
let initialFile = params["initialFile"] as? String
|
||||
let initialData = params["initialData"] as? [String: String?]
|
||||
|
||||
if windowId == nil {
|
||||
if #available(macOS 10.13, *) {
|
||||
webView.configuration.userContentController.removeAllContentRuleLists()
|
||||
if let contentBlockers = webView.settings?.contentBlockers, contentBlockers.count > 0 {
|
||||
do {
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: contentBlockers, options: [])
|
||||
let blockRules = String(data: jsonData, encoding: .utf8)
|
||||
WKContentRuleListStore.default().compileContentRuleList(
|
||||
forIdentifier: "ContentBlockingRules",
|
||||
encodedContentRuleList: blockRules) { (contentRuleList, error) in
|
||||
|
||||
if let error = error {
|
||||
print(error.localizedDescription)
|
||||
return
|
||||
}
|
||||
|
||||
let configuration = webView.configuration
|
||||
configuration.userContentController.add(contentRuleList!)
|
||||
|
||||
self.load(initialUrlRequest: initialUrlRequest, initialFile: initialFile, initialData: initialData)
|
||||
}
|
||||
return
|
||||
} catch {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
load(initialUrlRequest: initialUrlRequest, initialFile: initialFile, initialData: initialData)
|
||||
}
|
||||
else if let wId = windowId {
|
||||
webView.runWindowBeforeCreatedCallbacks()
|
||||
}
|
||||
}
|
||||
|
||||
func load(initialUrlRequest: [String:Any?]?, initialFile: String?, initialData: [String: String?]?) {
|
||||
guard let webView = webView() else {
|
||||
return
|
||||
}
|
||||
|
||||
if let initialFile = initialFile {
|
||||
do {
|
||||
try webView.loadFile(assetFilePath: initialFile)
|
||||
}
|
||||
catch let error as NSError {
|
||||
dump(error)
|
||||
}
|
||||
}
|
||||
else if let initialData = initialData, let data = initialData["data"]!,
|
||||
let mimeType = initialData["mimeType"]!, let encoding = initialData["encoding"]!,
|
||||
let baseUrl = URL(string: initialData["baseUrl"]! ?? "about:blank") {
|
||||
var allowingReadAccessToURL: URL? = nil
|
||||
if let allowingReadAccessTo = webView.settings?.allowingReadAccessTo, baseUrl.scheme == "file" {
|
||||
allowingReadAccessToURL = URL(string: allowingReadAccessTo)
|
||||
if allowingReadAccessToURL?.scheme != "file" {
|
||||
allowingReadAccessToURL = nil
|
||||
}
|
||||
}
|
||||
webView.loadData(data: data,
|
||||
mimeType: mimeType,
|
||||
encoding: encoding,
|
||||
baseUrl: baseUrl,
|
||||
allowingReadAccessTo: allowingReadAccessToURL)
|
||||
}
|
||||
else if let initialUrlRequest = initialUrlRequest {
|
||||
let urlRequest = URLRequest.init(fromPluginMap: initialUrlRequest)
|
||||
var allowingReadAccessToURL: URL? = nil
|
||||
if let allowingReadAccessTo = webView.settings?.allowingReadAccessTo, let url = urlRequest.url, url.scheme == "file" {
|
||||
allowingReadAccessToURL = URL(string: allowingReadAccessTo)
|
||||
if allowingReadAccessToURL?.scheme != "file" {
|
||||
allowingReadAccessToURL = nil
|
||||
}
|
||||
}
|
||||
webView.loadUrl(urlRequest: urlRequest, allowingReadAccessTo: allowingReadAccessToURL)
|
||||
}
|
||||
}
|
||||
|
||||
// method added to fix:
|
||||
// https://github.com/pichillilorenzo/flutter_inappwebview/issues/1837
|
||||
public func dispose(removeFromSuperview: Bool) {
|
||||
if keepAliveId == nil {
|
||||
if let webView = webView() {
|
||||
webView.dispose()
|
||||
if removeFromSuperview {
|
||||
webView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
if removeFromSuperview {
|
||||
self.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
dispose(removeFromSuperview: false)
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("FlutterWebViewController - dealloc")
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+72
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// FlutterWebViewFactory.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo on 13/11/18.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
public class FlutterWebViewFactory: NSObject, FlutterPlatformViewFactory {
|
||||
static let VIEW_TYPE_ID = "com.pichillilorenzo/flutter_inappwebview"
|
||||
private var plugin: InAppWebViewFlutterPlugin
|
||||
|
||||
init(plugin: InAppWebViewFlutterPlugin) {
|
||||
self.plugin = plugin
|
||||
super.init()
|
||||
}
|
||||
|
||||
public func createArgsCodec() -> (FlutterMessageCodec & NSObjectProtocol)? {
|
||||
return FlutterStandardMessageCodec.sharedInstance()
|
||||
}
|
||||
|
||||
public func create(withViewIdentifier viewId: Int64, arguments args: Any?) -> NSView {
|
||||
let arguments = args as? NSDictionary
|
||||
var flutterWebView: FlutterWebViewController?
|
||||
var id: Any = viewId
|
||||
|
||||
let keepAliveId = arguments?["keepAliveId"] as? String
|
||||
let headlessWebViewId = arguments?["headlessWebViewId"] as? String
|
||||
|
||||
if let headlessWebViewId = headlessWebViewId,
|
||||
let headlessWebView = plugin.headlessInAppWebViewManager?.webViews[headlessWebViewId],
|
||||
let platformView = headlessWebView?.disposeAndGetFlutterWebView(withFrame: .zero) {
|
||||
flutterWebView = platformView
|
||||
flutterWebView?.keepAliveId = keepAliveId
|
||||
}
|
||||
|
||||
if let keepAliveId = keepAliveId,
|
||||
flutterWebView == nil,
|
||||
let keepAliveWebView = plugin.inAppWebViewManager?.keepAliveWebViews[keepAliveId] {
|
||||
flutterWebView = keepAliveWebView
|
||||
if let view = flutterWebView?.view() {
|
||||
// remove from parent
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
let shouldMakeInitialLoad = flutterWebView == nil
|
||||
if flutterWebView == nil {
|
||||
if let keepAliveId = keepAliveId {
|
||||
id = keepAliveId
|
||||
}
|
||||
flutterWebView = FlutterWebViewController(plugin: plugin,
|
||||
withFrame: .zero,
|
||||
viewIdentifier: id,
|
||||
params: arguments!)
|
||||
}
|
||||
|
||||
if let keepAliveId = keepAliveId {
|
||||
plugin.inAppWebViewManager?.keepAliveWebViews[keepAliveId] = flutterWebView!
|
||||
}
|
||||
|
||||
if shouldMakeInitialLoad {
|
||||
flutterWebView?.makeInitialLoad(params: arguments!)
|
||||
}
|
||||
|
||||
return flutterWebView!
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+2661
File diff suppressed because it is too large.
Load diff
Vendored
Executable
+124
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// InAppWebViewManager.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 08/12/2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
import FlutterMacOS
|
||||
|
||||
public class InAppWebViewManager: ChannelDelegate {
|
||||
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_manager"
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
var webViewForUserAgent: WKWebView?
|
||||
var defaultUserAgent: String?
|
||||
|
||||
var keepAliveWebViews: [String:FlutterWebViewController?] = [:]
|
||||
var windowWebViews: [Int64:WebViewTransport] = [:]
|
||||
var windowAutoincrementId: Int64 = 0
|
||||
|
||||
init(plugin: InAppWebViewFlutterPlugin) {
|
||||
super.init(channel: FlutterMethodChannel(name: InAppWebViewManager.METHOD_CHANNEL_NAME, binaryMessenger: plugin.registrar!.messenger))
|
||||
self.plugin = plugin
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "getDefaultUserAgent":
|
||||
getDefaultUserAgent(completionHandler: { (value) in
|
||||
result(value)
|
||||
})
|
||||
break
|
||||
case "handlesURLScheme":
|
||||
let urlScheme = arguments!["urlScheme"] as! String
|
||||
if #available(macOS 10.13, *) {
|
||||
result(WKWebView.handlesURLScheme(urlScheme))
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "disposeKeepAlive":
|
||||
let keepAliveId = arguments!["keepAliveId"] as! String
|
||||
disposeKeepAlive(keepAliveId: keepAliveId)
|
||||
result(true)
|
||||
break
|
||||
case "clearAllCache":
|
||||
let includeDiskFiles = arguments!["includeDiskFiles"] as! Bool
|
||||
clearAllCache(includeDiskFiles: includeDiskFiles, completionHandler: {
|
||||
result(true)
|
||||
})
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func getDefaultUserAgent(completionHandler: @escaping (_ value: String?) -> Void) {
|
||||
if defaultUserAgent == nil {
|
||||
webViewForUserAgent = WKWebView()
|
||||
webViewForUserAgent?.evaluateJavaScript("navigator.userAgent") { (value, error) in
|
||||
|
||||
if error != nil {
|
||||
print("Error occurred to get userAgent")
|
||||
self.webViewForUserAgent = nil
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
|
||||
if let unwrappedUserAgent = value as? String {
|
||||
self.defaultUserAgent = unwrappedUserAgent
|
||||
completionHandler(self.defaultUserAgent)
|
||||
} else {
|
||||
print("Failed to get userAgent")
|
||||
}
|
||||
self.webViewForUserAgent = nil
|
||||
}
|
||||
} else {
|
||||
completionHandler(defaultUserAgent)
|
||||
}
|
||||
}
|
||||
|
||||
public func disposeKeepAlive(keepAliveId: String) {
|
||||
if let flutterWebView = keepAliveWebViews[keepAliveId] as? FlutterWebViewController {
|
||||
flutterWebView.keepAliveId = nil
|
||||
flutterWebView.dispose(removeFromSuperview: true)
|
||||
keepAliveWebViews[keepAliveId] = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func clearAllCache(includeDiskFiles: Bool, completionHandler: @escaping () -> Void) {
|
||||
var websiteDataTypes = Set([WKWebsiteDataTypeMemoryCache])
|
||||
if includeDiskFiles {
|
||||
websiteDataTypes.insert(WKWebsiteDataTypeDiskCache)
|
||||
if #available(macOS 10.13.4, *) {
|
||||
websiteDataTypes.insert(WKWebsiteDataTypeFetchCache)
|
||||
}
|
||||
websiteDataTypes.insert(WKWebsiteDataTypeOfflineWebApplicationCache)
|
||||
}
|
||||
let date = NSDate(timeIntervalSince1970: 0)
|
||||
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes, modifiedSince: date as Date, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
let keepAliveWebViewValues = keepAliveWebViews.values
|
||||
keepAliveWebViewValues.forEach {(keepAliveWebView: FlutterWebViewController?) in
|
||||
if let keepAliveId = keepAliveWebView?.keepAliveId {
|
||||
disposeKeepAlive(keepAliveId: keepAliveId)
|
||||
}
|
||||
}
|
||||
keepAliveWebViews.removeAll()
|
||||
windowWebViews.removeAll()
|
||||
webViewForUserAgent = nil
|
||||
defaultUserAgent = nil
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+118
@@ -0,0 +1,118 @@
|
||||
//
|
||||
// InAppWebViewSettings.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo on 21/10/18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
|
||||
@objcMembers
|
||||
public class InAppWebViewSettings: ISettings<InAppWebView> {
|
||||
|
||||
var gopeedProfileId = ""
|
||||
var useShouldOverrideUrlLoading = false
|
||||
var useOnLoadResource = false
|
||||
var useOnDownloadStart = false
|
||||
@available(*, deprecated, message: "Use InAppWebViewManager.clearAllCache instead.")
|
||||
var clearCache = false
|
||||
var userAgent = ""
|
||||
var applicationNameForUserAgent = ""
|
||||
var javaScriptEnabled = true
|
||||
var javaScriptCanOpenWindowsAutomatically = false
|
||||
var mediaPlaybackRequiresUserGesture = true
|
||||
var resourceCustomSchemes: [String] = []
|
||||
var contentBlockers: [[String: [String : Any]]] = []
|
||||
var minimumFontSize = 0
|
||||
var useShouldInterceptAjaxRequest = false
|
||||
var interceptOnlyAsyncAjaxRequests = true
|
||||
var useShouldInterceptFetchRequest = false
|
||||
var incognito = false
|
||||
var cacheEnabled = true
|
||||
var transparentBackground = false
|
||||
var supportZoom = true
|
||||
var allowUniversalAccessFromFileURLs = false
|
||||
var allowFileAccessFromFileURLs = false
|
||||
|
||||
var enableViewportScale = false
|
||||
var suppressesIncrementalRendering = false
|
||||
var allowsAirPlayForMediaPlayback = true
|
||||
var allowsBackForwardNavigationGestures = true
|
||||
var allowsLinkPreview = true
|
||||
var isFraudulentWebsiteWarningEnabled = true
|
||||
var preferredContentMode = 0
|
||||
var sharedCookiesEnabled = false
|
||||
var mediaType: String? = nil
|
||||
var pageZoom = 1.0
|
||||
var limitsNavigationsToAppBoundDomains = false
|
||||
var useOnNavigationResponse = false
|
||||
var applePayAPIEnabled = false
|
||||
var allowingReadAccessTo: String? = nil
|
||||
var underPageBackgroundColor: String?
|
||||
var isTextInteractionEnabled = true
|
||||
var isSiteSpecificQuirksModeEnabled = true
|
||||
var upgradeKnownHostsToHTTPS = true
|
||||
var isElementFullscreenEnabled = true
|
||||
var isInspectable = false
|
||||
var shouldPrintBackgrounds = false
|
||||
|
||||
override init(){
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func parse(settings: [String: Any?]) -> InAppWebViewSettings {
|
||||
let _ = super.parse(settings: settings)
|
||||
if #available(macOS 10.15, *) {} else {
|
||||
applePayAPIEnabled = false
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
override func getRealSettings(obj: InAppWebView?) -> [String: Any?] {
|
||||
var realSettings: [String: Any?] = toMap()
|
||||
if let webView = obj {
|
||||
let configuration = webView.configuration
|
||||
realSettings["userAgent"] = webView.customUserAgent
|
||||
realSettings["applicationNameForUserAgent"] = configuration.applicationNameForUserAgent
|
||||
realSettings["allowsAirPlayForMediaPlayback"] = configuration.allowsAirPlayForMediaPlayback
|
||||
realSettings["allowsLinkPreview"] = webView.allowsLinkPreview
|
||||
realSettings["javaScriptCanOpenWindowsAutomatically"] = configuration.preferences.javaScriptCanOpenWindowsAutomatically
|
||||
if #available(macOS 10.12, *) {
|
||||
realSettings["mediaPlaybackRequiresUserGesture"] = configuration.mediaTypesRequiringUserActionForPlayback == .all
|
||||
}
|
||||
realSettings["minimumFontSize"] = Int(configuration.preferences.minimumFontSize)
|
||||
realSettings["suppressesIncrementalRendering"] = configuration.suppressesIncrementalRendering
|
||||
realSettings["allowsBackForwardNavigationGestures"] = webView.allowsBackForwardNavigationGestures
|
||||
if #available(macOS 10.15, *) {
|
||||
realSettings["isFraudulentWebsiteWarningEnabled"] = configuration.preferences.isFraudulentWebsiteWarningEnabled
|
||||
realSettings["preferredContentMode"] = configuration.defaultWebpagePreferences.preferredContentMode.rawValue
|
||||
}
|
||||
realSettings["allowUniversalAccessFromFileURLs"] = configuration.value(forKey: "allowUniversalAccessFromFileURLs") as? Bool
|
||||
realSettings["allowFileAccessFromFileURLs"] = configuration.preferences.value(forKey: "allowFileAccessFromFileURLs") as? Bool
|
||||
realSettings["javaScriptEnabled"] = configuration.preferences.javaScriptEnabled
|
||||
if #available(macOS 11.0, *) {
|
||||
realSettings["mediaType"] = webView.mediaType
|
||||
realSettings["pageZoom"] = Float(webView.pageZoom)
|
||||
realSettings["limitsNavigationsToAppBoundDomains"] = configuration.limitsNavigationsToAppBoundDomains
|
||||
realSettings["javaScriptEnabled"] = configuration.defaultWebpagePreferences.allowsContentJavaScript
|
||||
}
|
||||
if #available(macOS 11.3, *) {
|
||||
realSettings["isTextInteractionEnabled"] = configuration.preferences.isTextInteractionEnabled
|
||||
realSettings["upgradeKnownHostsToHTTPS"] = configuration.upgradeKnownHostsToHTTPS
|
||||
}
|
||||
if #available(macOS 12.0, *) {
|
||||
realSettings["underPageBackgroundColor"] = webView.underPageBackgroundColor.hexString
|
||||
}
|
||||
if #available(macOS 12.3, *) {
|
||||
realSettings["isSiteSpecificQuirksModeEnabled"] = configuration.preferences.isSiteSpecificQuirksModeEnabled
|
||||
realSettings["isElementFullscreenEnabled"] = configuration.preferences.isElementFullscreenEnabled
|
||||
}
|
||||
if #available(macOS 13.3, *) {
|
||||
realSettings["isInspectable"] = webView.isInspectable
|
||||
realSettings["shouldPrintBackgrounds"] = configuration.preferences.shouldPrintBackgrounds
|
||||
}
|
||||
}
|
||||
return realSettings
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// WebMessageChannel.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 10/03/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class WebMessageChannel: FlutterMethodCallDelegate {
|
||||
static var METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_web_message_channel_"
|
||||
var id: String
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
var channelDelegate: WebMessageChannelChannelDelegate?
|
||||
weak var webView: InAppWebView?
|
||||
var ports: [WebMessagePort] = []
|
||||
|
||||
public init(plugin: InAppWebViewFlutterPlugin, id: String) {
|
||||
self.id = id
|
||||
self.plugin = plugin
|
||||
super.init()
|
||||
if let registrar = plugin.registrar {
|
||||
let channel = FlutterMethodChannel(name: WebMessageChannel.METHOD_CHANNEL_NAME_PREFIX + id,
|
||||
binaryMessenger: registrar.messenger)
|
||||
self.channelDelegate = WebMessageChannelChannelDelegate(webMessageChannel: self, channel: channel)
|
||||
}
|
||||
self.ports = [
|
||||
WebMessagePort(name: "port1", index: 0, webMessageChannelId: self.id, webMessageChannel: self),
|
||||
WebMessagePort(name: "port2", index: 1, webMessageChannelId: self.id, webMessageChannel: self)
|
||||
]
|
||||
}
|
||||
|
||||
public func initJsInstance(webView: InAppWebView, completionHandler: ((WebMessageChannel?) -> Void)? = nil) {
|
||||
self.webView = webView
|
||||
if let webView = self.webView {
|
||||
webView.evaluateJavascript(source: """
|
||||
(function() {
|
||||
\(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"] = new MessageChannel();
|
||||
})();
|
||||
""") { (_) in
|
||||
completionHandler?(self)
|
||||
}
|
||||
} else {
|
||||
completionHandler?(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func toMap() -> [String:Any?] {
|
||||
return [
|
||||
"id": id
|
||||
]
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
channelDelegate?.dispose()
|
||||
channelDelegate = nil
|
||||
for port in ports {
|
||||
port.dispose()
|
||||
}
|
||||
ports.removeAll()
|
||||
webView?.evaluateJavascript(source: """
|
||||
(function() {
|
||||
var webMessageChannel = \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"];
|
||||
if (webMessageChannel != null) {
|
||||
webMessageChannel.port1.close();
|
||||
webMessageChannel.port2.close();
|
||||
delete \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(id)"];
|
||||
}
|
||||
})();
|
||||
""")
|
||||
webView = nil
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("WebMessageChannel - dealloc")
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// WebMessageChannelChannelDelegate.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class WebMessageChannelChannelDelegate: ChannelDelegate {
|
||||
private weak var webMessageChannel: WebMessageChannel?
|
||||
|
||||
public init(webMessageChannel: WebMessageChannel, channel: FlutterMethodChannel) {
|
||||
super.init(channel: channel)
|
||||
self.webMessageChannel = webMessageChannel
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "setWebMessageCallback":
|
||||
if let _ = webMessageChannel?.webView, let ports = webMessageChannel?.ports, ports.count > 0 {
|
||||
let index = arguments!["index"] as! Int
|
||||
let port = ports[index]
|
||||
do {
|
||||
try port.setWebMessageCallback { (_) in
|
||||
result(true)
|
||||
}
|
||||
} catch let error as NSError {
|
||||
result(FlutterError(code: "WebMessageChannel", message: error.domain, details: nil))
|
||||
}
|
||||
|
||||
} else {
|
||||
result(true)
|
||||
}
|
||||
break
|
||||
case "postMessage":
|
||||
if let webView = webMessageChannel?.webView, let ports = webMessageChannel?.ports, ports.count > 0 {
|
||||
let index = arguments!["index"] as! Int
|
||||
let port = ports[index]
|
||||
var message = WebMessage.fromMap(map: arguments!["message"] as! [String: Any?])
|
||||
|
||||
var ports: [WebMessagePort] = []
|
||||
if let notConnectedPorts = message.ports {
|
||||
for notConnectedPort in notConnectedPorts {
|
||||
if let webMessageChannel = webView.webMessageChannels[notConnectedPort.webMessageChannelId] {
|
||||
ports.append(webMessageChannel.ports[Int(notConnectedPort.index)])
|
||||
}
|
||||
}
|
||||
}
|
||||
message.ports = ports
|
||||
|
||||
do {
|
||||
try port.postMessage(message: message) { (_) in
|
||||
result(true)
|
||||
}
|
||||
} catch let error as NSError {
|
||||
result(FlutterError(code: "WebMessageChannel", message: error.domain, details: nil))
|
||||
}
|
||||
} else {
|
||||
result(true)
|
||||
}
|
||||
break
|
||||
case "close":
|
||||
if let _ = webMessageChannel?.webView, let ports = webMessageChannel?.ports, ports.count > 0 {
|
||||
let index = arguments!["index"] as! Int
|
||||
let port = ports[index]
|
||||
do {
|
||||
try port.close { (_) in
|
||||
result(true)
|
||||
}
|
||||
} catch let error as NSError {
|
||||
result(FlutterError(code: "WebMessageChannel", message: error.domain, details: nil))
|
||||
}
|
||||
} else {
|
||||
result(true)
|
||||
}
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func onMessage(index: Int64, message: WebMessage?) {
|
||||
let arguments: [String:Any?] = [
|
||||
"index": index,
|
||||
"message": message?.toMap()
|
||||
]
|
||||
channel?.invokeMethod("onMessage", arguments: arguments)
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
webMessageChannel = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// WebMessageListener.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 10/03/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
import FlutterMacOS
|
||||
|
||||
public class WebMessageListener: FlutterMethodCallDelegate {
|
||||
static var METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_web_message_listener_"
|
||||
var id: String
|
||||
var jsObjectName: String
|
||||
var allowedOriginRules: Set<String>
|
||||
var channelDelegate: WebMessageListenerChannelDelegate?
|
||||
weak var webView: InAppWebView?
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
|
||||
public init(plugin: InAppWebViewFlutterPlugin, id: String, jsObjectName: String, allowedOriginRules: Set<String>) {
|
||||
self.id = id
|
||||
self.plugin = plugin
|
||||
self.jsObjectName = jsObjectName
|
||||
self.allowedOriginRules = allowedOriginRules
|
||||
super.init()
|
||||
if let registrar = plugin.registrar {
|
||||
let channel = FlutterMethodChannel(name: WebMessageListener.METHOD_CHANNEL_NAME_PREFIX + self.id + "_" + self.jsObjectName,
|
||||
binaryMessenger: registrar.messenger)
|
||||
self.channelDelegate = WebMessageListenerChannelDelegate(webMessageListener: self, channel: channel)
|
||||
}
|
||||
}
|
||||
|
||||
public func assertOriginRulesValid() throws {
|
||||
for (index, originRule) in allowedOriginRules.enumerated() {
|
||||
if originRule.isEmpty {
|
||||
throw NSError(domain: "allowedOriginRules[\(index)] is empty", code: 0)
|
||||
}
|
||||
if originRule == "*" {
|
||||
continue
|
||||
}
|
||||
if let url = URL(string: originRule) {
|
||||
guard let scheme = url.scheme else {
|
||||
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||
}
|
||||
if scheme == "http" || scheme == "https", url.host == nil || url.host!.isEmpty {
|
||||
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||
}
|
||||
if scheme != "http", scheme != "https", url.host != nil || url.port != nil {
|
||||
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||
}
|
||||
if url.host == nil || url.host!.isEmpty, url.port != nil {
|
||||
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||
}
|
||||
if !url.path.isEmpty {
|
||||
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||
}
|
||||
if let hostname = url.host {
|
||||
if let firstIndex = hostname.firstIndex(of: "*") {
|
||||
let distance = hostname.distance(from: hostname.startIndex, to: firstIndex)
|
||||
if distance != 0 || (distance == 0 && hostname.prefix(2) != "*.") {
|
||||
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||
}
|
||||
}
|
||||
if hostname.hasPrefix("[") {
|
||||
if !hostname.hasSuffix("]") {
|
||||
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||
}
|
||||
let fromIndex = hostname.index(hostname.startIndex, offsetBy: 1)
|
||||
let toIndex = hostname.index(hostname.startIndex, offsetBy: hostname.count - 1)
|
||||
let indexRange = Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))
|
||||
let ipv6 = String(hostname[indexRange])
|
||||
if !Util.isIPv6(address: ipv6) {
|
||||
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw NSError(domain: "allowedOriginRules \(originRule) is invalid", code: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func initJsInstance(webView: InAppWebView) {
|
||||
self.webView = webView
|
||||
if let webView = self.webView {
|
||||
let jsObjectNameEscaped = jsObjectName.replacingOccurrences(of: "\'", with: "\\'")
|
||||
let allowedOriginRulesString = allowedOriginRules.map { (allowedOriginRule) -> String in
|
||||
if allowedOriginRule == "*" {
|
||||
return "'*'"
|
||||
}
|
||||
let rule = URL(string: allowedOriginRule)!
|
||||
let host = rule.host != nil ? "'" + rule.host!.replacingOccurrences(of: "\'", with: "\\'") + "'" : "null"
|
||||
return """
|
||||
{scheme: '\(rule.scheme!)', host: \(host), port: \(rule.port != nil ? String(rule.port!) : "null")}
|
||||
"""
|
||||
}.joined(separator: ", ")
|
||||
let source = """
|
||||
(function() {
|
||||
var allowedOriginRules = [\(allowedOriginRulesString)];
|
||||
var isPageBlank = window.location.href === "about:blank";
|
||||
var scheme = !isPageBlank ? window.location.protocol.replace(":", "") : null;
|
||||
var host = !isPageBlank ? window.location.hostname : null;
|
||||
var port = !isPageBlank ? window.location.port : null;
|
||||
if (window.\(JAVASCRIPT_BRIDGE_NAME)._isOriginAllowed(allowedOriginRules, scheme, host, port)) {
|
||||
window['\(jsObjectNameEscaped)'] = new FlutterInAppWebViewWebMessageListener('\(jsObjectNameEscaped)');
|
||||
}
|
||||
})();
|
||||
"""
|
||||
webView.configuration.userContentController.addPluginScript(PluginScript(
|
||||
groupName: "WebMessageListener-" + id + "-" + jsObjectName,
|
||||
source: source,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: false,
|
||||
requiredInAllContentWorlds: false,
|
||||
messageHandlerNames: ["onWebMessageListenerPostMessageReceived"]
|
||||
))
|
||||
webView.configuration.userContentController.sync(scriptMessageHandler: webView)
|
||||
}
|
||||
}
|
||||
|
||||
public static func fromMap(plugin: InAppWebViewFlutterPlugin, map: [String:Any?]?) -> WebMessageListener? {
|
||||
guard let map = map else {
|
||||
return nil
|
||||
}
|
||||
return WebMessageListener(
|
||||
plugin: plugin,
|
||||
id: map["id"] as! String,
|
||||
jsObjectName: map["jsObjectName"] as! String,
|
||||
allowedOriginRules: Set(map["allowedOriginRules"] as! [String])
|
||||
)
|
||||
}
|
||||
|
||||
public func isOriginAllowed(scheme: String?, host: String?, port: Int?) -> Bool {
|
||||
for allowedOriginRule in allowedOriginRules {
|
||||
if allowedOriginRule == "*" {
|
||||
return true
|
||||
}
|
||||
if scheme == nil || scheme!.isEmpty {
|
||||
continue
|
||||
}
|
||||
if scheme == nil || scheme!.isEmpty, host == nil || host!.isEmpty, port == nil || port == 0 {
|
||||
continue
|
||||
}
|
||||
if let rule = URL(string: allowedOriginRule) {
|
||||
let rulePort = rule.port == nil || rule.port == 0 ? (rule.scheme == "https" ? 443 : 80) : rule.port!
|
||||
let currentPort = port == nil || port == 0 ? (scheme == "https" ? 443 : 80) : port!
|
||||
var IPv6: String? = nil
|
||||
if let hostname = rule.host, hostname.hasPrefix("[") {
|
||||
let fromIndex = hostname.index(hostname.startIndex, offsetBy: 1)
|
||||
let toIndex = hostname.index(hostname.startIndex, offsetBy: hostname.count - 1)
|
||||
let indexRange = Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))
|
||||
do {
|
||||
IPv6 = try Util.normalizeIPv6(address: String(hostname[indexRange]))
|
||||
} catch {}
|
||||
}
|
||||
var hostIPv6: String? = nil
|
||||
if let host = host, Util.isIPv6(address: host) {
|
||||
do {
|
||||
hostIPv6 = try Util.normalizeIPv6(address: host)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
let schemeAllowed = scheme != nil && !scheme!.isEmpty && scheme == rule.scheme
|
||||
|
||||
let hostAllowed = rule.host == nil ||
|
||||
rule.host!.isEmpty ||
|
||||
host == rule.host ||
|
||||
(rule.host!.hasPrefix("*") && host != nil && host!.hasSuffix(rule.host!.split(separator: "*", omittingEmptySubsequences: false)[1])) ||
|
||||
(hostIPv6 != nil && IPv6 != nil && hostIPv6 == IPv6)
|
||||
|
||||
let portAllowed = rulePort == currentPort
|
||||
|
||||
if schemeAllowed, hostAllowed, portAllowed {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
channelDelegate?.dispose()
|
||||
channelDelegate = nil
|
||||
webView = nil
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("WebMessageListener - dealloc")
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// WebMessageListenerChannelDelegate.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class WebMessageListenerChannelDelegate: ChannelDelegate {
|
||||
private weak var webMessageListener: WebMessageListener?
|
||||
|
||||
public init(webMessageListener: WebMessageListener, channel: FlutterMethodChannel) {
|
||||
super.init(channel: channel)
|
||||
self.webMessageListener = webMessageListener
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "postMessage":
|
||||
if let webView = webMessageListener?.webView, let jsObjectName = webMessageListener?.jsObjectName {
|
||||
let jsObjectNameEscaped = jsObjectName.replacingOccurrences(of: "\'", with: "\\'")
|
||||
let message = WebMessage.fromMap(map: arguments!["message"] as! [String: Any?])
|
||||
|
||||
let source = """
|
||||
(function() {
|
||||
var webMessageListener = window['\(jsObjectNameEscaped)'];
|
||||
if (webMessageListener != null) {
|
||||
var event = {data: \(message.jsData)};
|
||||
if (webMessageListener.onmessage != null) {
|
||||
webMessageListener.onmessage(event);
|
||||
}
|
||||
for (var listener of webMessageListener.listeners) {
|
||||
listener(event);
|
||||
}
|
||||
}
|
||||
})();
|
||||
"""
|
||||
webView.evaluateJavascript(source: source) { (_) in
|
||||
result(true)
|
||||
}
|
||||
} else {
|
||||
result(true)
|
||||
}
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func onPostMessage(message: WebMessage?, sourceOrigin: URL?, isMainFrame: Bool) {
|
||||
let arguments: [String:Any?] = [
|
||||
"message": message?.toMap(),
|
||||
"sourceOrigin": sourceOrigin?.absoluteString,
|
||||
"isMainFrame": isMainFrame
|
||||
]
|
||||
channel?.invokeMethod("onPostMessage", arguments: arguments)
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
webMessageListener = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
+1178
File diff suppressed because it is too large.
Load diff
+87
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// WebViewChannelDelegateMethods.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 08/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum WebViewChannelDelegateMethods: String {
|
||||
case getUrl = "getUrl"
|
||||
case getTitle = "getTitle"
|
||||
case getProgress = "getProgress"
|
||||
case loadUrl = "loadUrl"
|
||||
case postUrl = "postUrl"
|
||||
case loadData = "loadData"
|
||||
case loadFile = "loadFile"
|
||||
case evaluateJavascript = "evaluateJavascript"
|
||||
case injectJavascriptFileFromUrl = "injectJavascriptFileFromUrl"
|
||||
case injectCSSCode = "injectCSSCode"
|
||||
case injectCSSFileFromUrl = "injectCSSFileFromUrl"
|
||||
case reload = "reload"
|
||||
case goBack = "goBack"
|
||||
case canGoBack = "canGoBack"
|
||||
case goForward = "goForward"
|
||||
case canGoForward = "canGoForward"
|
||||
case goBackOrForward = "goBackOrForward"
|
||||
case canGoBackOrForward = "canGoBackOrForward"
|
||||
case stopLoading = "stopLoading"
|
||||
case isLoading = "isLoading"
|
||||
case takeScreenshot = "takeScreenshot"
|
||||
case setSettings = "setSettings"
|
||||
case getSettings = "getSettings"
|
||||
case close = "close"
|
||||
case show = "show"
|
||||
case hide = "hide"
|
||||
case isHidden = "isHidden"
|
||||
case getCopyBackForwardList = "getCopyBackForwardList"
|
||||
@available(*, deprecated, message: "Use FindInteractionController.findAll instead.")
|
||||
case findAll = "findAll"
|
||||
@available(*, deprecated, message: "Use FindInteractionController.findNext instead.")
|
||||
case findNext = "findNext"
|
||||
@available(*, deprecated, message: "Use FindInteractionController.clearMatches instead.")
|
||||
case clearMatches = "clearMatches"
|
||||
@available(*, deprecated, message: "Use InAppWebViewManager.clearAllCache instead.")
|
||||
case clearCache = "clearCache"
|
||||
case scrollTo = "scrollTo"
|
||||
case scrollBy = "scrollBy"
|
||||
case pauseTimers = "pauseTimers"
|
||||
case resumeTimers = "resumeTimers"
|
||||
case printCurrentPage = "printCurrentPage"
|
||||
case getContentHeight = "getContentHeight"
|
||||
case getContentWidth = "getContentWidth"
|
||||
case reloadFromOrigin = "reloadFromOrigin"
|
||||
case getOriginalUrl = "getOriginalUrl"
|
||||
case getZoomScale = "getZoomScale"
|
||||
case hasOnlySecureContent = "hasOnlySecureContent"
|
||||
case getSelectedText = "getSelectedText"
|
||||
case getScrollX = "getScrollX"
|
||||
case getScrollY = "getScrollY"
|
||||
case getCertificate = "getCertificate"
|
||||
case addUserScript = "addUserScript"
|
||||
case removeUserScript = "removeUserScript"
|
||||
case removeUserScriptsByGroupName = "removeUserScriptsByGroupName"
|
||||
case removeAllUserScripts = "removeAllUserScripts"
|
||||
case callAsyncJavaScript = "callAsyncJavaScript"
|
||||
case createPdf = "createPdf"
|
||||
case createWebArchiveData = "createWebArchiveData"
|
||||
case saveWebArchive = "saveWebArchive"
|
||||
case isSecureContext = "isSecureContext"
|
||||
case createWebMessageChannel = "createWebMessageChannel"
|
||||
case postWebMessage = "postWebMessage"
|
||||
case addWebMessageListener = "addWebMessageListener"
|
||||
case canScrollVertically = "canScrollVertically"
|
||||
case canScrollHorizontally = "canScrollHorizontally"
|
||||
case pauseAllMediaPlayback = "pauseAllMediaPlayback"
|
||||
case setAllMediaPlaybackSuspended = "setAllMediaPlaybackSuspended"
|
||||
case closeAllMediaPresentations = "closeAllMediaPresentations"
|
||||
case requestMediaPlaybackState = "requestMediaPlaybackState"
|
||||
case getMetaThemeColor = "getMetaThemeColor"
|
||||
case isInFullscreen = "isInFullscreen"
|
||||
case getCameraCaptureState = "getCameraCaptureState"
|
||||
case setCameraCaptureState = "setCameraCaptureState"
|
||||
case getMicrophoneCaptureState = "getMicrophoneCaptureState"
|
||||
case setMicrophoneCaptureState = "setMicrophoneCaptureState"
|
||||
case loadSimulatedRequest = "loadSimulatedRequest"
|
||||
}
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import AppKit
|
||||
import WebKit
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
import SafariServices
|
||||
|
||||
public class InAppWebViewFlutterPlugin: NSObject, FlutterPlugin {
|
||||
|
||||
var registrar: FlutterPluginRegistrar?
|
||||
var platformUtil: PlatformUtil?
|
||||
var inAppWebViewManager: InAppWebViewManager?
|
||||
var myCookieManager: Any?
|
||||
var myWebStorageManager: MyWebStorageManager?
|
||||
var credentialDatabase: CredentialDatabase?
|
||||
var inAppBrowserManager: InAppBrowserManager?
|
||||
var headlessInAppWebViewManager: HeadlessInAppWebViewManager?
|
||||
var webAuthenticationSessionManager: WebAuthenticationSessionManager?
|
||||
var printJobManager: PrintJobManager?
|
||||
|
||||
var webViewControllers: [String: InAppBrowserWebViewController?] = [:]
|
||||
var safariViewControllers: [String: Any?] = [:]
|
||||
|
||||
public init(with registrar: FlutterPluginRegistrar) {
|
||||
super.init()
|
||||
self.registrar = registrar
|
||||
registrar.register(FlutterWebViewFactory(plugin: self) as FlutterPlatformViewFactory, withId: FlutterWebViewFactory.VIEW_TYPE_ID)
|
||||
|
||||
platformUtil = PlatformUtil(plugin: self)
|
||||
inAppBrowserManager = InAppBrowserManager(plugin: self)
|
||||
headlessInAppWebViewManager = HeadlessInAppWebViewManager(plugin: self)
|
||||
inAppWebViewManager = InAppWebViewManager(plugin: self)
|
||||
credentialDatabase = CredentialDatabase(plugin: self)
|
||||
if #available(macOS 10.13, *) {
|
||||
myCookieManager = MyCookieManager(plugin: self)
|
||||
}
|
||||
myWebStorageManager = MyWebStorageManager(plugin: self)
|
||||
webAuthenticationSessionManager = WebAuthenticationSessionManager(plugin: self)
|
||||
printJobManager = PrintJobManager(plugin: self)
|
||||
}
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let _ = InAppWebViewFlutterPlugin(with: registrar)
|
||||
}
|
||||
|
||||
public func detachFromEngine(for registrar: FlutterPluginRegistrar) {
|
||||
platformUtil?.dispose()
|
||||
platformUtil = nil
|
||||
inAppBrowserManager?.dispose()
|
||||
inAppBrowserManager = nil
|
||||
headlessInAppWebViewManager?.dispose()
|
||||
headlessInAppWebViewManager = nil
|
||||
inAppWebViewManager?.dispose()
|
||||
inAppWebViewManager = nil
|
||||
credentialDatabase?.dispose()
|
||||
credentialDatabase = nil
|
||||
if #available(macOS 10.13, *) {
|
||||
(myCookieManager as! MyCookieManager?)?.dispose()
|
||||
myCookieManager = nil
|
||||
}
|
||||
myWebStorageManager?.dispose()
|
||||
myWebStorageManager = nil
|
||||
webAuthenticationSessionManager?.dispose()
|
||||
webAuthenticationSessionManager = nil
|
||||
printJobManager?.dispose()
|
||||
printJobManager = nil
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+26
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// LeakAvoider.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 15/12/2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class LeakAvoider: NSObject {
|
||||
weak var delegate : FlutterMethodCallDelegate?
|
||||
|
||||
init(delegate: FlutterMethodCallDelegate) {
|
||||
super.init()
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
self.delegate?.handle(call, result: result)
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("LeakAvoider - dealloc")
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+305
@@ -0,0 +1,305 @@
|
||||
//
|
||||
// MyCookieManager.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo on 26/10/18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
import FlutterMacOS
|
||||
|
||||
@available(macOS 10.13, *)
|
||||
public class MyCookieManager: ChannelDelegate {
|
||||
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_cookiemanager"
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
static var httpCookieStore = WKWebsiteDataStore.default().httpCookieStore
|
||||
|
||||
init(plugin: InAppWebViewFlutterPlugin) {
|
||||
super.init(channel: FlutterMethodChannel(name: MyCookieManager.METHOD_CHANNEL_NAME, binaryMessenger: plugin.registrar!.messenger))
|
||||
self.plugin = plugin
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
if call.method == "gopeed.prepareProfile" {
|
||||
GopeedProfiles.prepare(arguments: arguments, result: result)
|
||||
return
|
||||
}
|
||||
let profileID = arguments?["gopeedProfileId"] as? String ?? ""
|
||||
let selectedStore = GopeedProfiles.store(profileID)
|
||||
if !profileID.isEmpty && selectedStore == nil {
|
||||
result(FlutterError(code: "UNAVAILABLE", message: "WebView profile is not initialized", details: nil))
|
||||
return
|
||||
}
|
||||
let cookieStore = (selectedStore ?? WKWebsiteDataStore.default()).httpCookieStore
|
||||
|
||||
switch call.method {
|
||||
case "setCookie":
|
||||
let url = arguments!["url"] as! String
|
||||
let name = arguments!["name"] as! String
|
||||
let value = arguments!["value"] as! String
|
||||
let path = arguments!["path"] as! String
|
||||
|
||||
var expiresDate: Int64?
|
||||
if let expiresDateString = arguments!["expiresDate"] as? String {
|
||||
expiresDate = Int64(expiresDateString)
|
||||
}
|
||||
|
||||
let maxAge = arguments!["maxAge"] as? Int64
|
||||
let isSecure = arguments!["isSecure"] as? Bool
|
||||
let isHttpOnly = arguments!["isHttpOnly"] as? Bool
|
||||
let sameSite = arguments!["sameSite"] as? String
|
||||
let domain = arguments!["domain"] as? String
|
||||
|
||||
MyCookieManager.setCookie(url: url,
|
||||
name: name,
|
||||
value: value,
|
||||
path: path,
|
||||
domain: domain,
|
||||
expiresDate: expiresDate,
|
||||
maxAge: maxAge,
|
||||
isSecure: isSecure,
|
||||
isHttpOnly: isHttpOnly,
|
||||
sameSite: sameSite,
|
||||
result: result, cookieStore: cookieStore)
|
||||
break
|
||||
case "getCookies":
|
||||
let url = arguments!["url"] as! String
|
||||
MyCookieManager.getCookies(url: url, result: result, cookieStore: cookieStore)
|
||||
break
|
||||
case "getAllCookies":
|
||||
MyCookieManager.getAllCookies(result: result, cookieStore: cookieStore)
|
||||
break
|
||||
case "deleteCookie":
|
||||
let url = arguments!["url"] as! String
|
||||
let name = arguments!["name"] as! String
|
||||
let path = arguments!["path"] as! String
|
||||
let domain = arguments!["domain"] as? String
|
||||
MyCookieManager.deleteCookie(url: url, name: name, path: path, domain: domain, result: result, cookieStore: cookieStore)
|
||||
break;
|
||||
case "deleteCookies":
|
||||
let url = arguments!["url"] as! String
|
||||
let path = arguments!["path"] as! String
|
||||
let domain = arguments!["domain"] as? String
|
||||
MyCookieManager.deleteCookies(url: url, path: path, domain: domain, result: result, cookieStore: cookieStore)
|
||||
break;
|
||||
case "deleteAllCookies":
|
||||
MyCookieManager.deleteAllCookies(result: result, cookieStore: cookieStore)
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public static func setCookie(url: String,
|
||||
name: String,
|
||||
value: String,
|
||||
path: String,
|
||||
domain: String?,
|
||||
expiresDate: Int64?,
|
||||
maxAge: Int64?,
|
||||
isSecure: Bool?,
|
||||
isHttpOnly: Bool?,
|
||||
sameSite: String?,
|
||||
result: @escaping FlutterResult, cookieStore: WKHTTPCookieStore = WKWebsiteDataStore.default().httpCookieStore) {
|
||||
var properties: [HTTPCookiePropertyKey: Any] = [:]
|
||||
properties[.originURL] = url
|
||||
properties[.name] = name
|
||||
properties[.value] = value
|
||||
properties[.path] = path
|
||||
|
||||
if domain != nil {
|
||||
properties[.domain] = domain
|
||||
}
|
||||
|
||||
if expiresDate != nil {
|
||||
// convert from milliseconds
|
||||
properties[.expires] = Date(timeIntervalSince1970: TimeInterval(Double(expiresDate!)/1000))
|
||||
}
|
||||
if maxAge != nil {
|
||||
properties[.maximumAge] = String(maxAge!)
|
||||
}
|
||||
if isSecure != nil && isSecure! {
|
||||
properties[.secure] = "TRUE"
|
||||
}
|
||||
if isHttpOnly != nil && isHttpOnly! {
|
||||
properties[.init("HttpOnly")] = "YES"
|
||||
}
|
||||
if sameSite != nil {
|
||||
if #available(macOS 10.15, *) {
|
||||
var sameSiteValue = HTTPCookieStringPolicy(rawValue: "None")
|
||||
switch sameSite {
|
||||
case "Lax":
|
||||
sameSiteValue = HTTPCookieStringPolicy.sameSiteLax
|
||||
case "Strict":
|
||||
sameSiteValue = HTTPCookieStringPolicy.sameSiteStrict
|
||||
default:
|
||||
break
|
||||
}
|
||||
properties[.sameSitePolicy] = sameSiteValue
|
||||
} else {
|
||||
properties[.init("SameSite")] = sameSite
|
||||
}
|
||||
}
|
||||
|
||||
if let cookie = HTTPCookie(properties: properties) {
|
||||
cookieStore.setCookie(cookie, completionHandler: {() in
|
||||
result(true)
|
||||
})
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
}
|
||||
|
||||
public static func getCookies(url: String, result: @escaping FlutterResult, cookieStore: WKHTTPCookieStore = WKWebsiteDataStore.default().httpCookieStore) {
|
||||
var cookieList: [[String: Any?]] = []
|
||||
|
||||
if let urlHost = URL(string: url)?.host {
|
||||
cookieStore.getAllCookies { (cookies) in
|
||||
for cookie in cookies {
|
||||
if urlHost.hasSuffix(cookie.domain) || ".\(urlHost)".hasSuffix(cookie.domain) {
|
||||
var sameSite: String? = nil
|
||||
if #available(macOS 10.15, *) {
|
||||
if let sameSiteValue = cookie.sameSitePolicy?.rawValue {
|
||||
sameSite = sameSiteValue.prefix(1).capitalized + sameSiteValue.dropFirst()
|
||||
}
|
||||
}
|
||||
|
||||
var expiresDateTimestamp: Int64 = -1
|
||||
if let expiresDate = cookie.expiresDate?.timeIntervalSince1970 {
|
||||
// convert to milliseconds
|
||||
expiresDateTimestamp = Int64(expiresDate * 1000)
|
||||
}
|
||||
|
||||
cookieList.append([
|
||||
"name": cookie.name,
|
||||
"value": cookie.value,
|
||||
"expiresDate": expiresDateTimestamp != -1 ? expiresDateTimestamp : nil,
|
||||
"isSessionOnly": cookie.isSessionOnly,
|
||||
"domain": cookie.domain,
|
||||
"sameSite": sameSite,
|
||||
"isSecure": cookie.isSecure,
|
||||
"isHttpOnly": cookie.isHTTPOnly,
|
||||
"path": cookie.path,
|
||||
])
|
||||
}
|
||||
}
|
||||
result(cookieList)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
print("Cannot get WebView cookies. No HOST found for URL: \(url)")
|
||||
}
|
||||
|
||||
result(cookieList)
|
||||
}
|
||||
|
||||
public static func getAllCookies(result: @escaping FlutterResult, cookieStore: WKHTTPCookieStore = WKWebsiteDataStore.default().httpCookieStore) {
|
||||
var cookieList: [[String: Any?]] = []
|
||||
|
||||
cookieStore.getAllCookies { (cookies) in
|
||||
for cookie in cookies {
|
||||
var sameSite: String? = nil
|
||||
if #available(macOS 10.15, *) {
|
||||
if let sameSiteValue = cookie.sameSitePolicy?.rawValue {
|
||||
sameSite = sameSiteValue.prefix(1).capitalized + sameSiteValue.dropFirst()
|
||||
}
|
||||
}
|
||||
|
||||
var expiresDateTimestamp: Int64 = -1
|
||||
if let expiresDate = cookie.expiresDate?.timeIntervalSince1970 {
|
||||
// convert to milliseconds
|
||||
expiresDateTimestamp = Int64(expiresDate * 1000)
|
||||
}
|
||||
|
||||
cookieList.append([
|
||||
"name": cookie.name,
|
||||
"value": cookie.value,
|
||||
"expiresDate": expiresDateTimestamp != -1 ? expiresDateTimestamp : nil,
|
||||
"isSessionOnly": cookie.isSessionOnly,
|
||||
"domain": cookie.domain,
|
||||
"sameSite": sameSite,
|
||||
"isSecure": cookie.isSecure,
|
||||
"isHttpOnly": cookie.isHTTPOnly,
|
||||
"path": cookie.path,
|
||||
])
|
||||
}
|
||||
result(cookieList)
|
||||
}
|
||||
}
|
||||
|
||||
public static func deleteCookie(url: String, name: String, path: String, domain: String?, result: @escaping FlutterResult, cookieStore: WKHTTPCookieStore = WKWebsiteDataStore.default().httpCookieStore) {
|
||||
var domain = domain
|
||||
cookieStore.getAllCookies { (cookies) in
|
||||
for cookie in cookies {
|
||||
var originURL = url
|
||||
if cookie.properties![.originURL] is String {
|
||||
originURL = cookie.properties![.originURL] as! String
|
||||
}
|
||||
else if cookie.properties![.originURL] is URL {
|
||||
originURL = (cookie.properties![.originURL] as! URL).absoluteString
|
||||
}
|
||||
if domain == nil, let domainUrl = URL(string: originURL) {
|
||||
domain = domainUrl.host
|
||||
}
|
||||
if let domain = domain, cookie.domain == domain, cookie.name == name, cookie.path == path {
|
||||
cookieStore.delete(cookie, completionHandler: {
|
||||
result(true)
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
result(false)
|
||||
}
|
||||
}
|
||||
|
||||
public static func deleteCookies(url: String, path: String, domain: String?, result: @escaping FlutterResult, cookieStore: WKHTTPCookieStore = WKWebsiteDataStore.default().httpCookieStore) {
|
||||
var domain = domain
|
||||
let dispatchGroup = DispatchGroup()
|
||||
cookieStore.getAllCookies { (cookies) in
|
||||
for cookie in cookies {
|
||||
var originURL = url
|
||||
if cookie.properties![.originURL] is String {
|
||||
originURL = cookie.properties![.originURL] as! String
|
||||
}
|
||||
else if cookie.properties![.originURL] is URL {
|
||||
originURL = (cookie.properties![.originURL] as! URL).absoluteString
|
||||
}
|
||||
if domain == nil, let domainUrl = URL(string: originURL) {
|
||||
domain = domainUrl.host
|
||||
}
|
||||
if let domain = domain, cookie.domain == domain, cookie.path == path {
|
||||
dispatchGroup.enter()
|
||||
cookieStore.delete(cookie) {
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
}
|
||||
}
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
result(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func deleteAllCookies(result: @escaping FlutterResult, cookieStore: WKHTTPCookieStore = WKWebsiteDataStore.default().httpCookieStore) {
|
||||
cookieStore.getAllCookies { cookies in
|
||||
let group = DispatchGroup()
|
||||
for cookie in cookies {
|
||||
group.enter()
|
||||
cookieStore.delete(cookie) { group.leave() }
|
||||
}
|
||||
group.notify(queue: .main) { result(true) }
|
||||
}
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+95
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// MyWebStorageManager.swift
|
||||
// connectivity
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/12/2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
import FlutterMacOS
|
||||
|
||||
public class MyWebStorageManager: ChannelDelegate {
|
||||
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_webstoragemanager"
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
static var websiteDataStore = WKWebsiteDataStore.default()
|
||||
|
||||
init(plugin: InAppWebViewFlutterPlugin) {
|
||||
super.init(channel: FlutterMethodChannel(name: MyWebStorageManager.METHOD_CHANNEL_NAME, binaryMessenger: plugin.registrar!.messenger))
|
||||
self.plugin = plugin
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
switch call.method {
|
||||
case "fetchDataRecords":
|
||||
let dataTypes = Set(arguments!["dataTypes"] as! [String])
|
||||
MyWebStorageManager.fetchDataRecords(dataTypes: dataTypes, result: result)
|
||||
break
|
||||
case "removeDataFor":
|
||||
let dataTypes = Set(arguments!["dataTypes"] as! [String])
|
||||
let recordList = arguments!["recordList"] as! [[String: Any?]]
|
||||
MyWebStorageManager.removeDataFor(dataTypes: dataTypes, recordList: recordList, result: result)
|
||||
break
|
||||
case "removeDataModifiedSince":
|
||||
let dataTypes = Set(arguments!["dataTypes"] as! [String])
|
||||
let timestamp = arguments!["timestamp"] as! Int64
|
||||
MyWebStorageManager.removeDataModifiedSince(dataTypes: dataTypes, timestamp: timestamp, result: result)
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public static func fetchDataRecords(dataTypes: Set<String>, result: @escaping FlutterResult) {
|
||||
var recordList: [[String: Any?]] = []
|
||||
|
||||
MyWebStorageManager.websiteDataStore.fetchDataRecords(ofTypes: dataTypes) { (data) in
|
||||
for record in data {
|
||||
recordList.append([
|
||||
"displayName": record.displayName,
|
||||
"dataTypes": record.dataTypes.map({ (dataType) -> String in
|
||||
return dataType
|
||||
})
|
||||
])
|
||||
}
|
||||
result(recordList)
|
||||
}
|
||||
}
|
||||
|
||||
public static func removeDataFor(dataTypes: Set<String>, recordList: [[String: Any?]], result: @escaping FlutterResult) {
|
||||
var records: [WKWebsiteDataRecord] = []
|
||||
|
||||
MyWebStorageManager.websiteDataStore.fetchDataRecords(ofTypes: dataTypes) { (data) in
|
||||
for record in data {
|
||||
for r in recordList {
|
||||
let displayName = r["displayName"] as! String
|
||||
if (record.displayName == displayName) {
|
||||
records.append(record)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
websiteDataStore.removeData(ofTypes: dataTypes, for: records) {
|
||||
result(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func removeDataModifiedSince(dataTypes: Set<String>, timestamp: Int64, result: @escaping FlutterResult) {
|
||||
let date = NSDate(timeIntervalSince1970: TimeInterval(timestamp))
|
||||
MyWebStorageManager.websiteDataStore.removeData(ofTypes: dataTypes, modifiedSince: date as Date) {
|
||||
result(true)
|
||||
}
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// PlatformUtil.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 01/03/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class PlatformUtil: ChannelDelegate {
|
||||
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_platformutil"
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
|
||||
init(plugin: InAppWebViewFlutterPlugin) {
|
||||
super.init(channel: FlutterMethodChannel(name: PlatformUtil.METHOD_CHANNEL_NAME, binaryMessenger: plugin.registrar!.messenger))
|
||||
self.plugin = plugin
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "getSystemVersion":
|
||||
let version = ProcessInfo.processInfo.operatingSystemVersion
|
||||
result("\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)")
|
||||
break
|
||||
case "formatDate":
|
||||
let date = arguments!["date"] as! Int64
|
||||
let format = arguments!["format"] as! String
|
||||
let locale = PlatformUtil.getLocaleFromString(locale: arguments!["locale"] as? String)
|
||||
let timezone = TimeZone.init(abbreviation: arguments!["timezone"] as? String ?? "UTC")!
|
||||
result(PlatformUtil.formatDate(date: date, format: format, locale: locale, timezone: timezone))
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
static public func getLocaleFromString(locale: String?) -> Locale {
|
||||
guard let locale = locale else {
|
||||
return Locale.init(identifier: "en_US")
|
||||
}
|
||||
return Locale.init(identifier: locale)
|
||||
}
|
||||
|
||||
static public func getDateFromMilliseconds(date: Int64) -> Date {
|
||||
return Date(timeIntervalSince1970: TimeInterval(Double(date)/1000))
|
||||
}
|
||||
|
||||
static public func formatDate(date: Int64, format: String, locale: Locale, timezone: TimeZone) -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = format
|
||||
formatter.timeZone = timezone
|
||||
return formatter.string(from: PlatformUtil.getDateFromMilliseconds(date: date))
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// CallAsyncJavaScriptBelowIOS14WrapperJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let CALL_ASYNC_JAVASCRIPT_BELOW_IOS_14_WRAPPER_JS = """
|
||||
(function(obj) {
|
||||
(async function(\(PluginScriptsUtil.VAR_FUNCTION_ARGUMENT_NAMES) {
|
||||
\(PluginScriptsUtil.VAR_FUNCTION_BODY)
|
||||
})(\(PluginScriptsUtil.VAR_FUNCTION_ARGUMENT_VALUES)).then(function(value) {
|
||||
window.webkit.messageHandlers['onCallAsyncJavaScriptResultBelowIOS14Received'].postMessage({'value': value, 'error': null, 'resultUuid': '\(PluginScriptsUtil.VAR_RESULT_UUID)'});
|
||||
}).catch(function(error) {
|
||||
window.webkit.messageHandlers['onCallAsyncJavaScriptResultBelowIOS14Received'].postMessage({'value': null, 'error': error + '', 'resultUuid': '\(PluginScriptsUtil.VAR_RESULT_UUID)'});
|
||||
});
|
||||
return null;
|
||||
})(\(PluginScriptsUtil.VAR_FUNCTION_ARGUMENTS_OBJ));
|
||||
"""
|
||||
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// ConsoleLogJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let CONSOLE_LOG_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_CONSOLE_LOG_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let CONSOLE_LOG_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: CONSOLE_LOG_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: CONSOLE_LOG_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: false,
|
||||
requiredInAllContentWorlds: true,
|
||||
messageHandlerNames: ["consoleLog", "consoleDebug", "consoleError", "consoleInfo", "consoleWarn"])
|
||||
|
||||
// the message needs to be concatenated with '' in order to have the same behavior like on Android
|
||||
let CONSOLE_LOG_JS_SOURCE = """
|
||||
(function(console) {
|
||||
|
||||
function _callHandler(oldLog, args) {
|
||||
var message = '';
|
||||
for (var i in args) {
|
||||
try {
|
||||
message += message === '' ? args[i] : ' ' + args[i];
|
||||
} catch(ignored) {}
|
||||
}
|
||||
var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE);
|
||||
window.webkit.messageHandlers[oldLog].postMessage({'message': message, '_windowId': _windowId});
|
||||
}
|
||||
|
||||
var oldLogs = {
|
||||
'consoleLog': console.log,
|
||||
'consoleDebug': console.debug,
|
||||
'consoleError': console.error,
|
||||
'consoleInfo': console.info,
|
||||
'consoleWarn': console.warn
|
||||
};
|
||||
|
||||
for (var k in oldLogs) {
|
||||
(function(oldLog) {
|
||||
console[oldLog.replace('console', '').toLowerCase()] = function() {
|
||||
oldLogs[oldLog].apply(null, arguments);
|
||||
_callHandler(oldLog, arguments);
|
||||
}
|
||||
})(k);
|
||||
}
|
||||
})(window.console);
|
||||
"""
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// EnableViewportScaleJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let ENABLE_VIEWPORT_SCALE_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_ENABLE_VIEWPORT_SCALE_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let ENABLE_VIEWPORT_SCALE_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: ENABLE_VIEWPORT_SCALE_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: ENABLE_VIEWPORT_SCALE_JS_SOURCE,
|
||||
injectionTime: .atDocumentEnd,
|
||||
forMainFrameOnly: true,
|
||||
requiredInAllContentWorlds: false,
|
||||
messageHandlerNames: [])
|
||||
|
||||
let ENABLE_VIEWPORT_SCALE_JS_SOURCE = """
|
||||
(function() {
|
||||
var meta = document.createElement('meta');
|
||||
meta.setAttribute('name', 'viewport');
|
||||
meta.setAttribute('content', 'width=device-width');
|
||||
document.getElementsByTagName('head')[0].appendChild(meta);
|
||||
})()
|
||||
"""
|
||||
|
||||
let NOT_ENABLE_VIEWPORT_SCALE_JS_SOURCE = """
|
||||
(function() {
|
||||
var meta = document.createElement('meta');
|
||||
meta.setAttribute('name', 'viewport');
|
||||
meta.setAttribute('content', window.\(JAVASCRIPT_BRIDGE_NAME)._originalViewPortMetaTagContent);
|
||||
document.getElementsByTagName('head')[0].appendChild(meta);
|
||||
})()
|
||||
"""
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// FindElementsAtPointJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let FIND_ELEMENTS_AT_POINT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_FIND_ELEMENTS_AT_POINT_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let FIND_ELEMENTS_AT_POINT_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: FIND_ELEMENTS_AT_POINT_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: FIND_ELEMENTS_AT_POINT_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: true,
|
||||
requiredInAllContentWorlds: false,
|
||||
messageHandlerNames: [])
|
||||
|
||||
/**
|
||||
https://developer.android.com/reference/android/webkit/WebView.HitTestResult
|
||||
*/
|
||||
let FIND_ELEMENTS_AT_POINT_JS_SOURCE = """
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._findElementsAtPoint = function(x, y) {
|
||||
var hitTestResultType = {
|
||||
UNKNOWN_TYPE: 0,
|
||||
PHONE_TYPE: 2,
|
||||
GEO_TYPE: 3,
|
||||
EMAIL_TYPE: 4,
|
||||
IMAGE_TYPE: 5,
|
||||
SRC_ANCHOR_TYPE: 7,
|
||||
SRC_IMAGE_ANCHOR_TYPE: 8,
|
||||
EDIT_TEXT_TYPE: 9
|
||||
};
|
||||
var element = document.elementFromPoint(x, y);
|
||||
var data = {
|
||||
type: 0,
|
||||
extra: null
|
||||
};
|
||||
while (element) {
|
||||
if (element.tagName === 'IMG' && element.src) {
|
||||
if (element.parentNode && element.parentNode.tagName === 'A' && element.parentNode.href) {
|
||||
data.type = hitTestResultType.SRC_IMAGE_ANCHOR_TYPE;
|
||||
} else {
|
||||
data.type = hitTestResultType.IMAGE_TYPE;
|
||||
}
|
||||
data.extra = element.src;
|
||||
break;
|
||||
} else if (element.tagName === 'A' && element.href) {
|
||||
if (element.href.indexOf('mailto:') === 0) {
|
||||
data.type = hitTestResultType.EMAIL_TYPE;
|
||||
data.extra = element.href.replace('mailto:', '');
|
||||
} else if (element.href.indexOf('tel:') === 0) {
|
||||
data.type = hitTestResultType.PHONE_TYPE;
|
||||
data.extra = element.href.replace('tel:', '');
|
||||
} else if (element.href.indexOf('geo:') === 0) {
|
||||
data.type = hitTestResultType.GEO_TYPE;
|
||||
data.extra = element.href.replace('geo:', '');
|
||||
} else {
|
||||
data.type = hitTestResultType.SRC_ANCHOR_TYPE;
|
||||
data.extra = element.href;
|
||||
}
|
||||
break;
|
||||
} else if (
|
||||
(element.tagName === 'INPUT' && ['text', 'email', 'password', 'number', 'search', 'tel', 'url'].indexOf(element.type) >= 0) ||
|
||||
element.tagName === 'TEXTAREA') {
|
||||
data.type = hitTestResultType.EDIT_TEXT_TYPE
|
||||
}
|
||||
element = element.parentNode;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
"""
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
//
|
||||
// FindTextHighlightJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let FIND_TEXT_HIGHLIGHT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_FIND_TEXT_HIGHLIGHT_JS_PLUGIN_SCRIPT"
|
||||
let FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE = "window.\(JAVASCRIPT_BRIDGE_NAME)._searchResultCount"
|
||||
let FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE = "window.\(JAVASCRIPT_BRIDGE_NAME)._currentHighlight"
|
||||
let FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE = "window.\(JAVASCRIPT_BRIDGE_NAME)._isDoneCounting"
|
||||
|
||||
let FIND_TEXT_HIGHLIGHT_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: FIND_TEXT_HIGHLIGHT_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: FIND_TEXT_HIGHLIGHT_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: true,
|
||||
requiredInAllContentWorlds: false,
|
||||
messageHandlerNames: ["onFindResultReceived"])
|
||||
|
||||
let FIND_TEXT_HIGHLIGHT_JS_SOURCE = """
|
||||
\(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) = 0;
|
||||
\(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE) = 0;
|
||||
\(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE) = false;
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsyncForElement = function(element, keyword) {
|
||||
if (element) {
|
||||
if (element.nodeType == 3) {
|
||||
// Text node
|
||||
|
||||
var elementTmp = element;
|
||||
while (true) {
|
||||
var value = elementTmp.nodeValue; // Search for keyword in text node
|
||||
var idx = value.toLowerCase().indexOf(keyword);
|
||||
|
||||
if (idx < 0) break;
|
||||
|
||||
var span = document.createElement("span");
|
||||
var text = document.createTextNode(value.substr(idx, keyword.length));
|
||||
span.appendChild(text);
|
||||
|
||||
span.setAttribute(
|
||||
"id",
|
||||
"\(JAVASCRIPT_BRIDGE_NAME)_SEARCH_WORD_" + \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE)
|
||||
);
|
||||
span.setAttribute("class", "\(JAVASCRIPT_BRIDGE_NAME)_Highlight");
|
||||
var backgroundColor = \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) == 0 ? "#FF9732" : "#FFFF00";
|
||||
span.setAttribute("style", "color: #000 !important; background: " + backgroundColor + " !important; padding: 0px !important; margin: 0px !important; border: 0px !important;");
|
||||
|
||||
text = document.createTextNode(value.substr(idx + keyword.length));
|
||||
element.deleteData(idx, value.length - idx);
|
||||
|
||||
var next = element.nextSibling;
|
||||
element.parentNode.insertBefore(span, next);
|
||||
element.parentNode.insertBefore(text, next);
|
||||
element = text;
|
||||
|
||||
\(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE)++;
|
||||
elementTmp = document.createTextNode(
|
||||
value.substr(idx + keyword.length)
|
||||
);
|
||||
|
||||
var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE);
|
||||
|
||||
window.webkit.messageHandlers["onFindResultReceived"].postMessage(
|
||||
{
|
||||
'findResult': {
|
||||
'activeMatchOrdinal': \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE),
|
||||
'numberOfMatches': \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE),
|
||||
'isDoneCounting': \(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE)
|
||||
},
|
||||
'_windowId': _windowId
|
||||
}
|
||||
);
|
||||
}
|
||||
} else if (element.nodeType == 1) {
|
||||
// Element node
|
||||
if (
|
||||
element.style.display != "none" &&
|
||||
element.nodeName.toLowerCase() != "select"
|
||||
) {
|
||||
for (var i = element.childNodes.length - 1; i >= 0; i--) {
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsyncForElement(
|
||||
element.childNodes[element.childNodes.length - 1 - i],
|
||||
keyword
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the main entry point to start the search
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsync = function(keyword) {
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatches();
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsyncForElement(document.body, keyword.toLowerCase());
|
||||
\(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE) = true;
|
||||
|
||||
var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE);
|
||||
|
||||
window.webkit.messageHandlers["onFindResultReceived"].postMessage(
|
||||
{
|
||||
'findResult': {
|
||||
'activeMatchOrdinal': \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE),
|
||||
'numberOfMatches': \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE),
|
||||
'isDoneCounting': \(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE)
|
||||
},
|
||||
'_windowId': _windowId
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// helper function, recursively removes the highlights in elements and their children
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatchesForElement = function(element) {
|
||||
if (element) {
|
||||
if (element.nodeType == 1) {
|
||||
if (element.getAttribute("class") == "\(JAVASCRIPT_BRIDGE_NAME)_Highlight") {
|
||||
var text = element.removeChild(element.firstChild);
|
||||
element.parentNode.insertBefore(text, element);
|
||||
element.parentNode.removeChild(element);
|
||||
return true;
|
||||
} else {
|
||||
var normalize = false;
|
||||
for (var i = element.childNodes.length - 1; i >= 0; i--) {
|
||||
if (window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatchesForElement(element.childNodes[i])) {
|
||||
normalize = true;
|
||||
}
|
||||
}
|
||||
if (normalize) {
|
||||
element.normalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// the main entry point to remove the highlights
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatches = function() {
|
||||
\(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) = 0;
|
||||
\(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE) = 0;
|
||||
\(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE) = false;
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatchesForElement(document.body);
|
||||
}
|
||||
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._findNext = function(forward) {
|
||||
if (\(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) <= 0) return;
|
||||
|
||||
var idx = \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE) + (forward ? +1 : -1);
|
||||
idx =
|
||||
idx < 0
|
||||
? \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) - 1
|
||||
: idx >= \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE)
|
||||
? 0
|
||||
: idx;
|
||||
\(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE) = idx;
|
||||
|
||||
var scrollTo = document.getElementById("\(JAVASCRIPT_BRIDGE_NAME)_SEARCH_WORD_" + idx);
|
||||
if (scrollTo) {
|
||||
var highlights = document.getElementsByClassName("\(JAVASCRIPT_BRIDGE_NAME)_Highlight");
|
||||
for (var i = 0; i < highlights.length; i++) {
|
||||
var span = highlights[i];
|
||||
span.style.backgroundColor = "#FFFF00";
|
||||
}
|
||||
scrollTo.style.backgroundColor = "#FF9732";
|
||||
|
||||
scrollTo.scrollIntoView({
|
||||
behavior: "auto",
|
||||
block: "center"
|
||||
});
|
||||
|
||||
var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE);
|
||||
|
||||
window.webkit.messageHandlers["onFindResultReceived"].postMessage(
|
||||
{
|
||||
'findResult': {
|
||||
'activeMatchOrdinal': \(FIND_TEXT_HIGHLIGHT_CURRENT_HIGHLIGHT_JS_SOURCE),
|
||||
'numberOfMatches': \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE),
|
||||
'isDoneCounting': \(FIND_TEXT_HIGHLIGHT_IS_DONE_COUNTING_JS_SOURCE)
|
||||
},
|
||||
'_windowId': _windowId
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
"""
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
//
|
||||
// InterceptAjaxRequestsJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let INTERCEPT_AJAX_REQUEST_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_INTERCEPT_AJAX_REQUEST_JS_PLUGIN_SCRIPT"
|
||||
let FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE = "window.\(JAVASCRIPT_BRIDGE_NAME)._useShouldInterceptAjaxRequest"
|
||||
|
||||
|
||||
let FLAG_VARIABLE_FOR_INTERCEPT_ONLY_ASYNC_AJAX_REQUESTS_JS_SOURCE = "window.\(JAVASCRIPT_BRIDGE_NAME)._interceptOnlyAsyncAjaxRequests";
|
||||
|
||||
let INTERCEPT_AJAX_REQUEST_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: INTERCEPT_AJAX_REQUEST_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: INTERCEPT_AJAX_REQUEST_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: false,
|
||||
requiredInAllContentWorlds: true,
|
||||
messageHandlerNames: [])
|
||||
|
||||
func createInterceptOnlyAsyncAjaxRequestsPluginScript(onlyAsync: Bool) -> PluginScript {
|
||||
return PluginScript(groupName: INTERCEPT_AJAX_REQUEST_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: "\(FLAG_VARIABLE_FOR_INTERCEPT_ONLY_ASYNC_AJAX_REQUESTS_JS_SOURCE) = \(onlyAsync);",
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: false,
|
||||
requiredInAllContentWorlds: true,
|
||||
messageHandlerNames: []
|
||||
);
|
||||
}
|
||||
|
||||
let INTERCEPT_AJAX_REQUEST_JS_SOURCE = """
|
||||
\(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE) = true;
|
||||
(function(ajax) {
|
||||
var send = ajax.prototype.send;
|
||||
var open = ajax.prototype.open;
|
||||
var setRequestHeader = ajax.prototype.setRequestHeader;
|
||||
ajax.prototype._flutter_inappwebview_url = null;
|
||||
ajax.prototype._flutter_inappwebview_method = null;
|
||||
ajax.prototype._flutter_inappwebview_isAsync = null;
|
||||
ajax.prototype._flutter_inappwebview_user = null;
|
||||
ajax.prototype._flutter_inappwebview_password = null;
|
||||
ajax.prototype._flutter_inappwebview_password = null;
|
||||
ajax.prototype._flutter_inappwebview_already_onreadystatechange_wrapped = false;
|
||||
ajax.prototype._flutter_inappwebview_request_headers = {};
|
||||
function convertRequestResponse(request, callback) {
|
||||
if (request.response != null && request.responseType != null) {
|
||||
switch (request.responseType) {
|
||||
case 'arraybuffer':
|
||||
callback(new Uint8Array(request.response));
|
||||
return;
|
||||
case 'blob':
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('loadend', function() {
|
||||
callback(new Uint8Array(reader.result));
|
||||
});
|
||||
reader.readAsArrayBuffer(blob);
|
||||
return;
|
||||
case 'document':
|
||||
callback(request.response.documentElement.outerHTML);
|
||||
return;
|
||||
case 'json':
|
||||
callback(request.response);
|
||||
return;
|
||||
};
|
||||
}
|
||||
callback(null);
|
||||
};
|
||||
ajax.prototype.open = function(method, url, isAsync, user, password) {
|
||||
isAsync = (isAsync != null) ? isAsync : true;
|
||||
this._flutter_inappwebview_url = url;
|
||||
this._flutter_inappwebview_method = method;
|
||||
this._flutter_inappwebview_isAsync = isAsync;
|
||||
this._flutter_inappwebview_user = user;
|
||||
this._flutter_inappwebview_password = password;
|
||||
this._flutter_inappwebview_request_headers = {};
|
||||
open.call(this, method, url, isAsync, user, password);
|
||||
};
|
||||
ajax.prototype.setRequestHeader = function(header, value) {
|
||||
this._flutter_inappwebview_request_headers[header] = value;
|
||||
setRequestHeader.call(this, header, value);
|
||||
};
|
||||
function handleEvent(e) {
|
||||
var self = this;
|
||||
if (\(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE) == null || \(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE) == true) {
|
||||
var headers = this.getAllResponseHeaders();
|
||||
var responseHeaders = {};
|
||||
if (headers != null) {
|
||||
var arr = headers.trim().split(/[\\r\\n]+/);
|
||||
arr.forEach(function (line) {
|
||||
var parts = line.split(': ');
|
||||
var header = parts.shift();
|
||||
var value = parts.join(': ');
|
||||
responseHeaders[header] = value;
|
||||
});
|
||||
}
|
||||
convertRequestResponse(this, function(response) {
|
||||
var ajaxRequest = {
|
||||
method: self._flutter_inappwebview_method,
|
||||
url: self._flutter_inappwebview_url,
|
||||
isAsync: self._flutter_inappwebview_isAsync,
|
||||
user: self._flutter_inappwebview_user,
|
||||
password: self._flutter_inappwebview_password,
|
||||
withCredentials: self.withCredentials,
|
||||
headers: self._flutter_inappwebview_request_headers,
|
||||
readyState: self.readyState,
|
||||
status: self.status,
|
||||
responseURL: self.responseURL,
|
||||
responseType: self.responseType,
|
||||
response: response,
|
||||
responseText: (self.responseType == 'text' || self.responseType == '') ? self.responseText : null,
|
||||
responseXML: (self.responseType == 'document' && self.responseXML != null) ? self.responseXML.documentElement.outerHTML : null,
|
||||
statusText: self.statusText,
|
||||
responseHeaders, responseHeaders,
|
||||
event: {
|
||||
type: e.type,
|
||||
loaded: e.loaded,
|
||||
lengthComputable: e.lengthComputable,
|
||||
total: e.total
|
||||
}
|
||||
};
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxProgress', ajaxRequest).then(function(result) {
|
||||
if (result != null) {
|
||||
switch (result) {
|
||||
case 0:
|
||||
self.abort();
|
||||
return;
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
ajax.prototype.send = function(data) {
|
||||
var self = this;
|
||||
var canBeIntercepted = self._flutter_inappwebview_isAsync || \(FLAG_VARIABLE_FOR_INTERCEPT_ONLY_ASYNC_AJAX_REQUESTS_JS_SOURCE) === false;
|
||||
if (canBeIntercepted && (\(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE) == null || \(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE) == true)) {
|
||||
if (!this._flutter_inappwebview_already_onreadystatechange_wrapped) {
|
||||
this._flutter_inappwebview_already_onreadystatechange_wrapped = true;
|
||||
var onreadystatechange = this.onreadystatechange;
|
||||
this.onreadystatechange = function() {
|
||||
if (\(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE) == null || \(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_AJAX_REQUEST_JS_SOURCE) == true) {
|
||||
var headers = this.getAllResponseHeaders();
|
||||
var responseHeaders = {};
|
||||
if (headers != null) {
|
||||
var arr = headers.trim().split(/[\\r\\n]+/);
|
||||
arr.forEach(function (line) {
|
||||
var parts = line.split(': ');
|
||||
var header = parts.shift();
|
||||
var value = parts.join(': ');
|
||||
responseHeaders[header] = value;
|
||||
});
|
||||
}
|
||||
convertRequestResponse(this, function(response) {
|
||||
var ajaxRequest = {
|
||||
method: self._flutter_inappwebview_method,
|
||||
url: self._flutter_inappwebview_url,
|
||||
isAsync: self._flutter_inappwebview_isAsync,
|
||||
user: self._flutter_inappwebview_user,
|
||||
password: self._flutter_inappwebview_password,
|
||||
withCredentials: self.withCredentials,
|
||||
headers: self._flutter_inappwebview_request_headers,
|
||||
readyState: self.readyState,
|
||||
status: self.status,
|
||||
responseURL: self.responseURL,
|
||||
responseType: self.responseType,
|
||||
response: response,
|
||||
responseText: (self.responseType == 'text' || self.responseType == '') ? self.responseText : null,
|
||||
responseXML: (self.responseType == 'document' && self.responseXML != null) ? self.responseXML.documentElement.outerHTML : null,
|
||||
statusText: self.statusText,
|
||||
responseHeaders: responseHeaders
|
||||
};
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxReadyStateChange', ajaxRequest).then(function(result) {
|
||||
if (result != null) {
|
||||
switch (result) {
|
||||
case 0:
|
||||
self.abort();
|
||||
return;
|
||||
};
|
||||
}
|
||||
if (onreadystatechange != null) {
|
||||
onreadystatechange();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (onreadystatechange != null) {
|
||||
onreadystatechange();
|
||||
}
|
||||
};
|
||||
}
|
||||
this.addEventListener('loadstart', handleEvent);
|
||||
this.addEventListener('load', handleEvent);
|
||||
this.addEventListener('loadend', handleEvent);
|
||||
this.addEventListener('progress', handleEvent);
|
||||
this.addEventListener('error', handleEvent);
|
||||
this.addEventListener('abort', handleEvent);
|
||||
this.addEventListener('timeout', handleEvent);
|
||||
\(JAVASCRIPT_UTIL_VAR_NAME).convertBodyRequest(data).then(function(data) {
|
||||
var ajaxRequest = {
|
||||
data: data,
|
||||
method: self._flutter_inappwebview_method,
|
||||
url: self._flutter_inappwebview_url,
|
||||
isAsync: self._flutter_inappwebview_isAsync,
|
||||
user: self._flutter_inappwebview_user,
|
||||
password: self._flutter_inappwebview_password,
|
||||
withCredentials: self.withCredentials,
|
||||
headers: self._flutter_inappwebview_request_headers,
|
||||
responseType: self.responseType
|
||||
};
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('shouldInterceptAjaxRequest', ajaxRequest).then(function(result) {
|
||||
if (result != null) {
|
||||
switch (result) {
|
||||
case 0:
|
||||
self.abort();
|
||||
return;
|
||||
};
|
||||
if (result.data != null && !\(JAVASCRIPT_UTIL_VAR_NAME).isString(result.data) && result.data.length > 0) {
|
||||
var bodyString = \(JAVASCRIPT_UTIL_VAR_NAME).arrayBufferToString(result.data);
|
||||
if (\(JAVASCRIPT_UTIL_VAR_NAME).isBodyFormData(bodyString)) {
|
||||
var formDataContentType = \(JAVASCRIPT_UTIL_VAR_NAME).getFormDataContentType(bodyString);
|
||||
if (result.headers != null) {
|
||||
result.headers['Content-Type'] = result.headers['Content-Type'] == null ? formDataContentType : result.headers['Content-Type'];
|
||||
} else {
|
||||
result.headers = { 'Content-Type': formDataContentType };
|
||||
}
|
||||
}
|
||||
}
|
||||
if (\(JAVASCRIPT_UTIL_VAR_NAME).isString(result.data) || result.data == null) {
|
||||
data = result.data;
|
||||
} else if (result.data.length > 0) {
|
||||
data = new Uint8Array(result.data);
|
||||
}
|
||||
self.withCredentials = result.withCredentials;
|
||||
if (result.responseType != null && self._flutter_inappwebview_isAsync) {
|
||||
self.responseType = result.responseType;
|
||||
};
|
||||
if (result.headers != null) {
|
||||
for (var header in result.headers) {
|
||||
var value = result.headers[header];
|
||||
var flutter_inappwebview_value = self._flutter_inappwebview_request_headers[header];
|
||||
if (flutter_inappwebview_value == null) {
|
||||
self._flutter_inappwebview_request_headers[header] = value;
|
||||
} else {
|
||||
self._flutter_inappwebview_request_headers[header] += ', ' + value;
|
||||
}
|
||||
setRequestHeader.call(self, header, value);
|
||||
};
|
||||
}
|
||||
if ((self._flutter_inappwebview_method != result.method && result.method != null) ||
|
||||
(self._flutter_inappwebview_url != result.url && result.url != null) ||
|
||||
(self._flutter_inappwebview_isAsync != result.isAsync && result.isAsync != null) ||
|
||||
(self._flutter_inappwebview_user != result.user && result.user != null) ||
|
||||
(self._flutter_inappwebview_password != result.password && result.password != null)) {
|
||||
self.abort();
|
||||
self.open(result.method, result.url, result.isAsync, result.user, result.password);
|
||||
}
|
||||
}
|
||||
send.call(self, data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
send.call(this, data);
|
||||
}
|
||||
};
|
||||
})(window.XMLHttpRequest);
|
||||
"""
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
//
|
||||
// InterceptFetchRequestsJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let INTERCEPT_FETCH_REQUEST_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_INTERCEPT_FETCH_REQUEST_JS_PLUGIN_SCRIPT"
|
||||
let FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE = "window.\(JAVASCRIPT_BRIDGE_NAME)._useShouldInterceptFetchRequest"
|
||||
|
||||
let INTERCEPT_FETCH_REQUEST_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: INTERCEPT_FETCH_REQUEST_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: INTERCEPT_FETCH_REQUEST_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: false,
|
||||
requiredInAllContentWorlds: true,
|
||||
messageHandlerNames: [])
|
||||
|
||||
let INTERCEPT_FETCH_REQUEST_JS_SOURCE = """
|
||||
\(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE) = true;
|
||||
(function(fetch) {
|
||||
if (fetch == null) {
|
||||
return;
|
||||
}
|
||||
window.fetch = async function(resource, init) {
|
||||
if (\(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE) == null || \(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE) == true) {
|
||||
var fetchRequest = {
|
||||
url: null,
|
||||
method: null,
|
||||
headers: null,
|
||||
body: null,
|
||||
mode: null,
|
||||
credentials: null,
|
||||
cache: null,
|
||||
redirect: null,
|
||||
referrer: null,
|
||||
referrerPolicy: null,
|
||||
integrity: null,
|
||||
keepalive: null
|
||||
};
|
||||
if (resource instanceof Request) {
|
||||
fetchRequest.url = resource.url;
|
||||
fetchRequest.method = resource.method;
|
||||
fetchRequest.headers = resource.headers;
|
||||
fetchRequest.body = resource.body;
|
||||
fetchRequest.mode = resource.mode;
|
||||
fetchRequest.credentials = resource.credentials;
|
||||
fetchRequest.cache = resource.cache;
|
||||
fetchRequest.redirect = resource.redirect;
|
||||
fetchRequest.referrer = resource.referrer;
|
||||
fetchRequest.referrerPolicy = resource.referrerPolicy;
|
||||
fetchRequest.integrity = resource.integrity;
|
||||
fetchRequest.keepalive = resource.keepalive;
|
||||
} else {
|
||||
fetchRequest.url = resource != null ? resource.toString() : null;
|
||||
if (init != null) {
|
||||
fetchRequest.method = init.method;
|
||||
fetchRequest.headers = init.headers;
|
||||
fetchRequest.body = init.body;
|
||||
fetchRequest.mode = init.mode;
|
||||
fetchRequest.credentials = init.credentials;
|
||||
fetchRequest.cache = init.cache;
|
||||
fetchRequest.redirect = init.redirect;
|
||||
fetchRequest.referrer = init.referrer;
|
||||
fetchRequest.referrerPolicy = init.referrerPolicy;
|
||||
fetchRequest.integrity = init.integrity;
|
||||
fetchRequest.keepalive = init.keepalive;
|
||||
}
|
||||
}
|
||||
if (fetchRequest.headers instanceof Headers) {
|
||||
fetchRequest.headers = \(JAVASCRIPT_UTIL_VAR_NAME).convertHeadersToJson(fetchRequest.headers);
|
||||
}
|
||||
fetchRequest.credentials = \(JAVASCRIPT_UTIL_VAR_NAME).convertCredentialsToJson(fetchRequest.credentials);
|
||||
return \(JAVASCRIPT_UTIL_VAR_NAME).convertBodyRequest(fetchRequest.body).then(function(body) {
|
||||
fetchRequest.body = body;
|
||||
return window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('shouldInterceptFetchRequest', fetchRequest).then(function(result) {
|
||||
if (result != null) {
|
||||
switch (result.action) {
|
||||
case 0:
|
||||
var controller = new AbortController();
|
||||
if (init != null) {
|
||||
init.signal = controller.signal;
|
||||
} else {
|
||||
init = {
|
||||
signal: controller.signal
|
||||
};
|
||||
}
|
||||
controller.abort();
|
||||
break;
|
||||
}
|
||||
if (result.body != null && !\(JAVASCRIPT_UTIL_VAR_NAME).isString(result.body) && result.body.length > 0) {
|
||||
var bodyString = \(JAVASCRIPT_UTIL_VAR_NAME).arrayBufferToString(result.body);
|
||||
if (\(JAVASCRIPT_UTIL_VAR_NAME).isBodyFormData(bodyString)) {
|
||||
var formDataContentType = \(JAVASCRIPT_UTIL_VAR_NAME).getFormDataContentType(bodyString);
|
||||
if (result.headers != null) {
|
||||
result.headers['Content-Type'] = result.headers['Content-Type'] == null ? formDataContentType : result.headers['Content-Type'];
|
||||
} else {
|
||||
result.headers = { 'Content-Type': formDataContentType };
|
||||
}
|
||||
}
|
||||
}
|
||||
resource = result.url;
|
||||
if (init == null) {
|
||||
init = {};
|
||||
}
|
||||
if (result.method != null && result.method.length > 0) {
|
||||
init.method = result.method;
|
||||
}
|
||||
if (result.headers != null && Object.keys(result.headers).length > 0) {
|
||||
init.headers = \(JAVASCRIPT_UTIL_VAR_NAME).convertJsonToHeaders(result.headers);
|
||||
}
|
||||
if (\(JAVASCRIPT_UTIL_VAR_NAME).isString(result.body) || result.body == null) {
|
||||
init.body = result.body;
|
||||
} else if (result.body.length > 0) {
|
||||
init.body = new Uint8Array(result.body);
|
||||
}
|
||||
if (result.mode != null && result.mode.length > 0) {
|
||||
init.mode = result.mode;
|
||||
}
|
||||
if (result.credentials != null) {
|
||||
init.credentials = \(JAVASCRIPT_UTIL_VAR_NAME).convertJsonToCredential(result.credentials);
|
||||
}
|
||||
if (result.cache != null && result.cache.length > 0) {
|
||||
init.cache = result.cache;
|
||||
}
|
||||
if (result.redirect != null && result.redirect.length > 0) {
|
||||
init.redirect = result.redirect;
|
||||
}
|
||||
if (result.referrer != null && result.referrer.length > 0) {
|
||||
init.referrer = result.referrer;
|
||||
}
|
||||
if (result.referrerPolicy != null && result.referrerPolicy.length > 0) {
|
||||
init.referrerPolicy = result.referrerPolicy;
|
||||
}
|
||||
if (result.integrity != null && result.integrity.length > 0) {
|
||||
init.integrity = result.integrity;
|
||||
}
|
||||
if (result.keepalive != null) {
|
||||
init.keepalive = result.keepalive;
|
||||
}
|
||||
return fetch(resource, init);
|
||||
}
|
||||
return fetch(resource, init);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return fetch(resource, init);
|
||||
}
|
||||
};
|
||||
})(window.fetch);
|
||||
"""
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
//
|
||||
// javaScriptBridgeJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let JAVASCRIPT_BRIDGE_NAME = "flutter_inappwebview"
|
||||
let JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: JAVASCRIPT_BRIDGE_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: false,
|
||||
requiredInAllContentWorlds: true,
|
||||
messageHandlerNames: ["callHandler"])
|
||||
|
||||
let JAVASCRIPT_BRIDGE_JS_SOURCE = """
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME) = {};
|
||||
\(WEB_MESSAGE_CHANNELS_VARIABLE_NAME) = {};
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
|
||||
var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE);
|
||||
var _callHandlerID = setTimeout(function(){});
|
||||
window.webkit.messageHandlers['callHandler'].postMessage( {'handlerName': arguments[0], '_callHandlerID': _callHandlerID, 'args': JSON.stringify(Array.prototype.slice.call(arguments, 1)), '_windowId': _windowId} );
|
||||
return new Promise(function(resolve, reject) {
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)[_callHandlerID] = {resolve: resolve, reject: reject};
|
||||
});
|
||||
};
|
||||
\(WEB_MESSAGE_LISTENER_JS_SOURCE)
|
||||
\(UTIL_JS_SOURCE)
|
||||
"""
|
||||
|
||||
let PLATFORM_READY_JS_SOURCE = "window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady'));";
|
||||
|
||||
let JAVASCRIPT_UTIL_VAR_NAME = "window.\(JAVASCRIPT_BRIDGE_NAME)._Util"
|
||||
|
||||
/*
|
||||
https://github.com/github/fetch/blob/master/fetch.js
|
||||
*/
|
||||
let UTIL_JS_SOURCE = """
|
||||
\(JAVASCRIPT_UTIL_VAR_NAME) = {
|
||||
support: {
|
||||
searchParams: 'URLSearchParams' in window,
|
||||
iterable: 'Symbol' in window && 'iterator' in Symbol,
|
||||
blob:
|
||||
'FileReader' in window &&
|
||||
'Blob' in window &&
|
||||
(function() {
|
||||
try {
|
||||
new Blob();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
formData: 'FormData' in window,
|
||||
arrayBuffer: 'ArrayBuffer' in window
|
||||
},
|
||||
isDataView: function(obj) {
|
||||
return obj && DataView.prototype.isPrototypeOf(obj);
|
||||
},
|
||||
fileReaderReady: function(reader) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
reader.onload = function() {
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.onerror = function() {
|
||||
reject(reader.error);
|
||||
};
|
||||
});
|
||||
},
|
||||
readBlobAsArrayBuffer: function(blob) {
|
||||
var reader = new FileReader();
|
||||
var promise = \(JAVASCRIPT_UTIL_VAR_NAME).fileReaderReady(reader);
|
||||
reader.readAsArrayBuffer(blob);
|
||||
return promise;
|
||||
},
|
||||
convertBodyToArrayBuffer: function(body) {
|
||||
var viewClasses = [
|
||||
'[object Int8Array]',
|
||||
'[object Uint8Array]',
|
||||
'[object Uint8ClampedArray]',
|
||||
'[object Int16Array]',
|
||||
'[object Uint16Array]',
|
||||
'[object Int32Array]',
|
||||
'[object Uint32Array]',
|
||||
'[object Float32Array]',
|
||||
'[object Float64Array]'
|
||||
];
|
||||
var isArrayBufferView = null;
|
||||
if (\(JAVASCRIPT_UTIL_VAR_NAME).support.arrayBuffer) {
|
||||
isArrayBufferView =
|
||||
ArrayBuffer.isView ||
|
||||
function(obj) {
|
||||
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
|
||||
};
|
||||
}
|
||||
|
||||
var bodyUsed = false;
|
||||
|
||||
this._bodyInit = body;
|
||||
if (!body) {
|
||||
this._bodyText = '';
|
||||
} else if (typeof body === 'string') {
|
||||
this._bodyText = body;
|
||||
} else if (\(JAVASCRIPT_UTIL_VAR_NAME).support.blob && Blob.prototype.isPrototypeOf(body)) {
|
||||
this._bodyBlob = body;
|
||||
} else if (\(JAVASCRIPT_UTIL_VAR_NAME).support.formData && FormData.prototype.isPrototypeOf(body)) {
|
||||
this._bodyFormData = body;
|
||||
} else if (\(JAVASCRIPT_UTIL_VAR_NAME).support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
|
||||
this._bodyText = body.toString();
|
||||
} else if (\(JAVASCRIPT_UTIL_VAR_NAME).support.arrayBuffer && \(JAVASCRIPT_UTIL_VAR_NAME).support.blob && \(JAVASCRIPT_UTIL_VAR_NAME).isDataView(body)) {
|
||||
this._bodyArrayBuffer = bufferClone(body.buffer);
|
||||
this._bodyInit = new Blob([this._bodyArrayBuffer]);
|
||||
} else if (\(JAVASCRIPT_UTIL_VAR_NAME).support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
|
||||
this._bodyArrayBuffer = bufferClone(body);
|
||||
} else {
|
||||
this._bodyText = body = Object.prototype.toString.call(body);
|
||||
}
|
||||
|
||||
this.blob = function () {
|
||||
if (bodyUsed) {
|
||||
return Promise.reject(new TypeError('Already read'));
|
||||
}
|
||||
bodyUsed = true;
|
||||
if (this._bodyBlob) {
|
||||
return Promise.resolve(this._bodyBlob);
|
||||
} else if (this._bodyArrayBuffer) {
|
||||
return Promise.resolve(new Blob([this._bodyArrayBuffer]));
|
||||
} else if (this._bodyFormData) {
|
||||
throw new Error('could not read FormData body as blob');
|
||||
} else {
|
||||
return Promise.resolve(new Blob([this._bodyText]));
|
||||
}
|
||||
};
|
||||
|
||||
if (this._bodyArrayBuffer) {
|
||||
if (bodyUsed) {
|
||||
return Promise.reject(new TypeError('Already read'));
|
||||
}
|
||||
bodyUsed = true;
|
||||
if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
|
||||
return Promise.resolve(
|
||||
this._bodyArrayBuffer.buffer.slice(
|
||||
this._bodyArrayBuffer.byteOffset,
|
||||
this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return Promise.resolve(this._bodyArrayBuffer);
|
||||
}
|
||||
}
|
||||
return this.blob().then(\(JAVASCRIPT_UTIL_VAR_NAME).readBlobAsArrayBuffer);
|
||||
},
|
||||
isString: function(variable) {
|
||||
return typeof variable === 'string' || variable instanceof String;
|
||||
},
|
||||
convertBodyRequest: function(body) {
|
||||
if (body == null) {
|
||||
return new Promise((resolve, reject) => resolve(null));
|
||||
}
|
||||
if (\(JAVASCRIPT_UTIL_VAR_NAME).isString(body) || (\(JAVASCRIPT_UTIL_VAR_NAME).support.searchParams && body instanceof URLSearchParams)) {
|
||||
return new Promise((resolve, reject) => resolve(body.toString()));
|
||||
}
|
||||
if (window.Response != null) {
|
||||
return new Response(body).arrayBuffer().then(function(arrayBuffer) {
|
||||
return Array.from(new Uint8Array(arrayBuffer));
|
||||
});
|
||||
}
|
||||
return \(JAVASCRIPT_UTIL_VAR_NAME).convertBodyToArrayBuffer(body).then(function(arrayBuffer) {
|
||||
return Array.from(new Uint8Array(arrayBuffer));
|
||||
});
|
||||
},
|
||||
arrayBufferToString: function(arrayBuffer) {
|
||||
var uint8Array = new Uint8Array(arrayBuffer);
|
||||
return uint8Array.reduce(function(acc, i) { return acc += String.fromCharCode.apply(null, [i]); }, '');
|
||||
},
|
||||
isBodyFormData: function(bodyString) {
|
||||
return bodyString.indexOf('------WebKitFormBoundary') >= 0;
|
||||
},
|
||||
getFormDataContentType: function(bodyString) {
|
||||
var boundary = bodyString.substr(2, 40);
|
||||
return 'multipart/form-data; boundary=' + boundary;
|
||||
},
|
||||
convertHeadersToJson: function(headers) {
|
||||
var headersObj = {};
|
||||
for (var header of headers.keys()) {
|
||||
var value = headers.get(header);
|
||||
headersObj[header] = value;
|
||||
}
|
||||
return headersObj;
|
||||
},
|
||||
convertJsonToHeaders: function(headersJson) {
|
||||
return new Headers(headersJson);
|
||||
},
|
||||
convertCredentialsToJson: function(credentials) {
|
||||
var credentialsObj = {};
|
||||
if (window.FederatedCredential != null && credentials instanceof FederatedCredential) {
|
||||
credentialsObj.type = credentials.type;
|
||||
credentialsObj.id = credentials.id;
|
||||
credentialsObj.name = credentials.name;
|
||||
credentialsObj.protocol = credentials.protocol;
|
||||
credentialsObj.provider = credentials.provider;
|
||||
credentialsObj.iconURL = credentials.iconURL;
|
||||
} else if (window.PasswordCredential != null && credentials instanceof PasswordCredential) {
|
||||
credentialsObj.type = credentials.type;
|
||||
credentialsObj.id = credentials.id;
|
||||
credentialsObj.name = credentials.name;
|
||||
credentialsObj.password = credentials.password;
|
||||
credentialsObj.iconURL = credentials.iconURL;
|
||||
} else {
|
||||
credentialsObj.type = 'default';
|
||||
credentialsObj.value = credentials;
|
||||
}
|
||||
return credentialsObj;
|
||||
},
|
||||
convertJsonToCredential: function(credentialsJson) {
|
||||
var credentials;
|
||||
if (window.FederatedCredential != null && credentialsJson.type === 'federated') {
|
||||
credentials = new FederatedCredential({
|
||||
id: credentialsJson.id,
|
||||
name: credentialsJson.name,
|
||||
protocol: credentialsJson.protocol,
|
||||
provider: credentialsJson.provider,
|
||||
iconURL: credentialsJson.iconURL
|
||||
});
|
||||
} else if (window.PasswordCredential != null && credentialsJson.type === 'password') {
|
||||
credentials = new PasswordCredential({
|
||||
id: credentialsJson.id,
|
||||
name: credentialsJson.name,
|
||||
password: credentialsJson.password,
|
||||
iconURL: credentialsJson.iconURL
|
||||
});
|
||||
} else {
|
||||
credentials = credentialsJson.value == null ? undefined : credentialsJson.value;
|
||||
}
|
||||
return credentials;
|
||||
}
|
||||
};
|
||||
"""
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// resourceObserverJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let ON_LOAD_RESOURCE_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_ON_LOAD_RESOURCE_JS_PLUGIN_SCRIPT"
|
||||
let FLAG_VARIABLE_FOR_ON_LOAD_RESOURCE_JS_SOURCE = "window.\(JAVASCRIPT_BRIDGE_NAME)._useOnLoadResource"
|
||||
|
||||
let ON_LOAD_RESOURCE_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: ON_LOAD_RESOURCE_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: ON_LOAD_RESOURCE_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: false,
|
||||
requiredInAllContentWorlds: false,
|
||||
messageHandlerNames: [])
|
||||
|
||||
let ON_LOAD_RESOURCE_JS_SOURCE = """
|
||||
\(FLAG_VARIABLE_FOR_ON_LOAD_RESOURCE_JS_SOURCE) = true;
|
||||
(function() {
|
||||
var observer = new PerformanceObserver(function(list) {
|
||||
list.getEntries().forEach(function(entry) {
|
||||
if (\(FLAG_VARIABLE_FOR_ON_LOAD_RESOURCE_JS_SOURCE) == null || \(FLAG_VARIABLE_FOR_ON_LOAD_RESOURCE_JS_SOURCE) == true) {
|
||||
var resource = {
|
||||
"url": entry.name,
|
||||
"initiatorType": entry.initiatorType,
|
||||
"startTime": entry.startTime,
|
||||
"duration": entry.duration
|
||||
};
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler("onLoadResource", resource);
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe({entryTypes: ['resource']});
|
||||
})();
|
||||
"""
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// OnScrollEvent.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let ON_SCROLL_CHANGED_EVENT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_ON_SCROLL_CHANGED_EVENT_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let ON_SCROLL_CHANGED_EVENT_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: ON_SCROLL_CHANGED_EVENT_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: ON_SCROLL_CHANGED_EVENT_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: true,
|
||||
requiredInAllContentWorlds: false,
|
||||
messageHandlerNames: ["onScrollChanged"])
|
||||
|
||||
let ON_SCROLL_CHANGED_EVENT_JS_SOURCE = """
|
||||
(function(){
|
||||
document.addEventListener('scroll', function(e) {
|
||||
var _windowId = \(WINDOW_ID_VARIABLE_JS_SOURCE);
|
||||
window.webkit.messageHandlers["onScrollChanged"].postMessage(
|
||||
{
|
||||
x: window.scrollX,
|
||||
y: window.scrollY,
|
||||
_windowId: _windowId
|
||||
}
|
||||
);
|
||||
});
|
||||
})();
|
||||
"""
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// OnWindowBlurEventJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let ON_WINDOW_BLUR_EVENT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_ON_WINDOW_BLUR_EVENT_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let ON_WINDOW_BLUR_EVENT_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: ON_WINDOW_BLUR_EVENT_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: ON_WINDOW_BLUR_EVENT_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: true,
|
||||
requiredInAllContentWorlds: false,
|
||||
messageHandlerNames: [])
|
||||
|
||||
let ON_WINDOW_BLUR_EVENT_JS_SOURCE = """
|
||||
(function(){
|
||||
window.addEventListener('blur', function(e) {
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onWindowBlur');
|
||||
});
|
||||
})();
|
||||
"""
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// OnWindowFocusEventJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let ON_WINDOW_FOCUS_EVENT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_ON_WINDOW_FOCUS_EVENT_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let ON_WINDOW_FOCUS_EVENT_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: ON_WINDOW_FOCUS_EVENT_JS_SOURCE,
|
||||
source: ON_WINDOW_FOCUS_EVENT_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: true,
|
||||
requiredInAllContentWorlds: false,
|
||||
messageHandlerNames: [])
|
||||
|
||||
let ON_WINDOW_FOCUS_EVENT_JS_SOURCE = """
|
||||
(function(){
|
||||
window.addEventListener('focus', function(e) {
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onWindowFocus');
|
||||
});
|
||||
})();
|
||||
"""
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// OriginalViewPortMetaTagContentJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let ORIGINAL_VIEWPORT_METATAG_CONTENT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_ORIGINAL_VIEWPORT_METATAG_CONTENT_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let ORIGINAL_VIEWPORT_METATAG_CONTENT_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: ORIGINAL_VIEWPORT_METATAG_CONTENT_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: ORIGINAL_VIEWPORT_METATAG_CONTENT_JS_SOURCE,
|
||||
injectionTime: .atDocumentEnd,
|
||||
forMainFrameOnly: true,
|
||||
requiredInAllContentWorlds: false,
|
||||
messageHandlerNames: [])
|
||||
|
||||
let ORIGINAL_VIEWPORT_METATAG_CONTENT_JS_SOURCE = """
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._originalViewPortMetaTagContent = "";
|
||||
(function() {
|
||||
var metaTagNodes = document.head.getElementsByTagName('meta');
|
||||
for (var i = 0; i < metaTagNodes.length; i++) {
|
||||
var metaTagNode = metaTagNodes[i];
|
||||
if (metaTagNode.name === "viewport") {
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._originalViewPortMetaTagContent = metaTagNode.content;
|
||||
}
|
||||
}
|
||||
})();
|
||||
"""
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// PluginScripts.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class PluginScriptsUtil {
|
||||
public static let VAR_PLACEHOLDER_VALUE = "$IN_APP_WEBVIEW_PLACEHOLDER_VALUE"
|
||||
public static let VAR_FUNCTION_ARGUMENT_NAMES = "$IN_APP_WEBVIEW_FUNCTION_ARGUMENT_NAMES"
|
||||
public static let VAR_FUNCTION_ARGUMENT_VALUES = "$IN_APP_WEBVIEW_FUNCTION_ARGUMENT_VALUES"
|
||||
public static let VAR_FUNCTION_ARGUMENTS_OBJ = "$IN_APP_WEBVIEW_FUNCTION_ARGUMENTS_OBJ"
|
||||
public static let VAR_FUNCTION_BODY = "$IN_APP_WEBVIEW_FUNCTION_BODY"
|
||||
public static let VAR_RESULT_UUID = "$IN_APP_WEBVIEW_RESULT_UUID"
|
||||
|
||||
public static let GET_SELECTED_TEXT_JS_SOURCE = """
|
||||
(function(){
|
||||
var txt;
|
||||
if (window.getSelection) {
|
||||
txt = window.getSelection().toString();
|
||||
} else if (window.document.getSelection) {
|
||||
txt = window.document.getSelection().toString();
|
||||
} else if (window.document.selection) {
|
||||
txt = window.document.selection.createRange().text;
|
||||
}
|
||||
return txt;
|
||||
})();
|
||||
"""
|
||||
}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// PrintJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let PRINT_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_PRINT_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let PRINT_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: PRINT_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: PRINT_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: false,
|
||||
requiredInAllContentWorlds: true,
|
||||
messageHandlerNames: [])
|
||||
|
||||
let PRINT_JS_SOURCE = """
|
||||
window.print = function() {
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler("onPrintRequest", window.location.href);
|
||||
}
|
||||
"""
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// PromisePolyfillJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
|
||||
let PROMISE_POLYFILL_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_PROMISE_POLYFILL_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let PROMISE_POLYFILL_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: PROMISE_POLYFILL_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: PROMISE_POLYFILL_JS_SOURCE,
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: false,
|
||||
requiredInAllContentWorlds: true,
|
||||
messageHandlerNames: [])
|
||||
|
||||
// https://github.com/tildeio/rsvp.js
|
||||
let PROMISE_POLYFILL_JS_SOURCE = """
|
||||
if (window.Promise == null) {
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.RSVP={})}(this,function(t){"use strict";function e(t){var e=t._promiseCallbacks;return e||(e=t._promiseCallbacks={}),e}var r={mixin:function(t){return t.on=this.on,t.off=this.off,t.trigger=this.trigger,t._promiseCallbacks=void 0,t},on:function(t,r){if("function"!=typeof r)throw new TypeError("Callback must be a function");var n=e(this),o=n[t];o||(o=n[t]=[]),-1===o.indexOf(r)&&o.push(r)},off:function(t,r){var n=e(this);if(r){var o=n[t],i=o.indexOf(r);-1!==i&&o.splice(i,1)}else n[t]=[]},trigger:function(t,r,n){var o=e(this)[t];if(o)for(var i=0;i<o.length;i++)(0,o[i])(r,n)}},n={instrument:!1};function o(t,e){if(2!==arguments.length)return n[t];n[t]=e}r.mixin(n);var i=[];function s(t,e,r){1===i.push({name:t,payload:{key:e._guidKey,id:e._id,eventName:t,detail:e._result,childId:r&&r._id,label:e._label,timeStamp:Date.now(),error:n["instrument-with-stack"]?new Error(e._label):null}})&&setTimeout(function(){for(var t=0;t<i.length;t++){var e=i[t],r=e.payload;r.guid=r.key+r.id,r.childGuid=r.key+r.childId,r.error&&(r.stack=r.error.stack),n.trigger(e.name,e.payload)}i.length=0},50)}function u(t,e){if(t&&"object"==typeof t&&t.constructor===this)return t;var r=new this(c,e);return m(r,t),r}function c(){}var a=void 0,f=1,l=2,h={error:null};function p(t){try{return t.then}catch(t){return h.error=t,h}}var y=void 0;function _(){try{var t=y;return y=null,t.apply(this,arguments)}catch(t){return h.error=t,h}}function v(t){return y=t,_}function d(t,e,r){if(e.constructor===t.constructor&&r===A&&t.constructor.resolve===u)!function(t,e){e._state===f?b(t,e._result):e._state===l?(e._onError=null,g(t,e._result)):j(e,void 0,function(r){e===r?b(t,r):m(t,r)},function(e){return g(t,e)})}(t,e);else if(r===h){var o=h.error;h.error=null,g(t,o)}else"function"==typeof r?function(t,e,r){n.async(function(t){var n=!1,o=v(r).call(e,function(r){n||(n=!0,e===r?b(t,r):m(t,r))},function(e){n||(n=!0,g(t,e))},"Settle: "+(t._label||" unknown promise"));if(!n&&o===h){n=!0;var i=h.error;h.error=null,g(t,i)}},t)}(t,e,r):b(t,e)}function m(t,e){var r,n;t===e?b(t,e):(n=typeof(r=e),null===r||"object"!==n&&"function"!==n?b(t,e):d(t,e,p(e)))}function w(t){t._onError&&t._onError(t._result),O(t)}function b(t,e){t._state===a&&(t._result=e,t._state=f,0===t._subscribers.length?n.instrument&&s("fulfilled",t):n.async(O,t))}function g(t,e){t._state===a&&(t._state=l,t._result=e,n.async(w,t))}function j(t,e,r,o){var i=t._subscribers,s=i.length;t._onError=null,i[s]=e,i[s+f]=r,i[s+l]=o,0===s&&t._state&&n.async(O,t)}function O(t){var e=t._subscribers,r=t._state;if(n.instrument&&s(r===f?"fulfilled":"rejected",t),0!==e.length){for(var o=void 0,i=void 0,u=t._result,c=0;c<e.length;c+=3)o=e[c],i=e[c+r],o?E(r,o,i,u):i(u);t._subscribers.length=0}}function E(t,e,r,n){var o="function"==typeof r,i=void 0;if(i=o?v(r)(n):n,e._state!==a);else if(i===e)g(e,new TypeError("A promises callback cannot return that same promise."));else if(i===h){var s=h.error;h.error=null,g(e,s)}else o?m(e,i):t===f?b(e,i):t===l&&g(e,i)}function A(t,e,r){var o=this._state;if(o===f&&!t||o===l&&!e)return n.instrument&&s("chained",this,this),this;this._onError=null;var i=new this.constructor(c,r),u=this._result;if(n.instrument&&s("chained",this,i),o===a)j(this,i,t,e);else{var h=o===f?t:e;n.async(function(){return E(o,i,h,u)})}return i}var T=function(){function t(t,e,r,n){this._instanceConstructor=t,this.promise=new t(c,n),this._abortOnReject=r,this._isUsingOwnPromise=t===k,this._isUsingOwnResolve=t.resolve===u,this._init.apply(this,arguments)}return t.prototype._init=function(t,e){var r=e.length||0;this.length=r,this._remaining=r,this._result=new Array(r),this._enumerate(e)},t.prototype._enumerate=function(t){for(var e=this.length,r=this.promise,n=0;r._state===a&&n<e;n++)this._eachEntry(t[n],n,!0);this._checkFullfillment()},t.prototype._checkFullfillment=function(){if(0===this._remaining){var t=this._result;b(this.promise,t),this._result=null}},t.prototype._settleMaybeThenable=function(t,e,r){var n=this._instanceConstructor;if(this._isUsingOwnResolve){var o=p(t);if(o===A&&t._state!==a)t._onError=null,this._settledAt(t._state,e,t._result,r);else if("function"!=typeof o)this._settledAt(f,e,t,r);else if(this._isUsingOwnPromise){var i=new n(c);d(i,t,o),this._willSettleAt(i,e,r)}else this._willSettleAt(new n(function(e){return e(t)}),e,r)}else this._willSettleAt(n.resolve(t),e,r)},t.prototype._eachEntry=function(t,e,r){null!==t&&"object"==typeof t?this._settleMaybeThenable(t,e,r):this._setResultAt(f,e,t,r)},t.prototype._settledAt=function(t,e,r,n){var o=this.promise;o._state===a&&(this._abortOnReject&&t===l?g(o,r):(this._setResultAt(t,e,r,n),this._checkFullfillment()))},t.prototype._setResultAt=function(t,e,r,n){this._remaining--,this._result[e]=r},t.prototype._willSettleAt=function(t,e,r){var n=this;j(t,void 0,function(t){return n._settledAt(f,e,t,r)},function(t){return n._sLine truncated
|
||||
window.Promise = RSVP.Promise;
|
||||
}
|
||||
"""
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// SupportZoomJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let NOT_SUPPORT_ZOOM_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_NOT_SUPPORT_ZOOM_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let NOT_SUPPORT_ZOOM_JS_PLUGIN_SCRIPT = PluginScript(
|
||||
groupName: NOT_SUPPORT_ZOOM_JS_PLUGIN_SCRIPT_GROUP_NAME,
|
||||
source: NOT_SUPPORT_ZOOM_JS_SOURCE,
|
||||
injectionTime: .atDocumentEnd,
|
||||
forMainFrameOnly: true,
|
||||
requiredInAllContentWorlds: false,
|
||||
messageHandlerNames: [])
|
||||
|
||||
let NOT_SUPPORT_ZOOM_JS_SOURCE = """
|
||||
(function() {
|
||||
var meta = document.createElement('meta');
|
||||
meta.setAttribute('name', 'viewport');
|
||||
meta.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no');
|
||||
document.getElementsByTagName('head')[0].appendChild(meta);
|
||||
})()
|
||||
"""
|
||||
|
||||
let SUPPORT_ZOOM_JS_SOURCE = """
|
||||
(function() {
|
||||
var meta = document.createElement('meta');
|
||||
meta.setAttribute('name', 'viewport');
|
||||
meta.setAttribute('content', window.\(JAVASCRIPT_BRIDGE_NAME)._originalViewPortMetaTagContent);
|
||||
document.getElementsByTagName('head')[0].appendChild(meta);
|
||||
})()
|
||||
"""
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
//
|
||||
// WebMessageChannelJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 10/03/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let WEB_MESSAGE_CHANNELS_VARIABLE_NAME = "window.\(JAVASCRIPT_BRIDGE_NAME)._webMessageChannels"
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// WebMessageListenerJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 10/03/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let WEB_MESSAGE_LISTENER_JS_SOURCE = """
|
||||
function FlutterInAppWebViewWebMessageListener(jsObjectName) {
|
||||
this.jsObjectName = jsObjectName;
|
||||
this.listeners = [];
|
||||
this.onmessage = null;
|
||||
}
|
||||
FlutterInAppWebViewWebMessageListener.prototype.postMessage = function(data) {
|
||||
var message = {
|
||||
"data": window.ArrayBuffer != null && data instanceof ArrayBuffer ? Array.from(new Uint8Array(data)) : (data != null ? data.toString() : null),
|
||||
"type": window.ArrayBuffer != null && data instanceof ArrayBuffer ? 1 : 0
|
||||
};
|
||||
window.webkit.messageHandlers['onWebMessageListenerPostMessageReceived'].postMessage({jsObjectName: this.jsObjectName, message: message});
|
||||
};
|
||||
FlutterInAppWebViewWebMessageListener.prototype.addEventListener = function(type, listener) {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
this.listeners.push(listener);
|
||||
};
|
||||
FlutterInAppWebViewWebMessageListener.prototype.removeEventListener = function(type, listener) {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
var index = this.listeners.indexOf(listener);
|
||||
if (index >= 0) {
|
||||
this.listeners.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._normalizeIPv6 = function(ip_string) {
|
||||
// replace ipv4 address if any
|
||||
var ipv4 = ip_string.match(/(.*:)([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)/);
|
||||
if (ipv4) {
|
||||
ip_string = ipv4[1];
|
||||
ipv4 = ipv4[2].match(/[0-9]+/g);
|
||||
for (var i = 0;i < 4;i ++) {
|
||||
var byte = parseInt(ipv4[i],10);
|
||||
ipv4[i] = ("0" + byte.toString(16)).substr(-2);
|
||||
}
|
||||
ip_string += ipv4[0] + ipv4[1] + ':' + ipv4[2] + ipv4[3];
|
||||
}
|
||||
|
||||
// take care of leading and trailing ::
|
||||
ip_string = ip_string.replace(/^:|:$/g, '');
|
||||
|
||||
var ipv6 = ip_string.split(':');
|
||||
|
||||
for (var i = 0; i < ipv6.length; i ++) {
|
||||
var hex = ipv6[i];
|
||||
if (hex != "") {
|
||||
// normalize leading zeros
|
||||
ipv6[i] = ("0000" + hex).substr(-4);
|
||||
}
|
||||
else {
|
||||
// normalize grouped zeros ::
|
||||
hex = [];
|
||||
for (var j = ipv6.length; j <= 8; j ++) {
|
||||
hex.push('0000');
|
||||
}
|
||||
ipv6[i] = hex.join(':');
|
||||
}
|
||||
}
|
||||
|
||||
return ipv6.join(':');
|
||||
}
|
||||
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._isOriginAllowed = function(allowedOriginRules, scheme, host, port) {
|
||||
for (var rule of allowedOriginRules) {
|
||||
if (rule === "*") {
|
||||
return true;
|
||||
}
|
||||
if (scheme == null || scheme === "") {
|
||||
continue;
|
||||
}
|
||||
if ((scheme == null || scheme === "") && (host == null || host === "") && (port === 0 || port === "" || port == null)) {
|
||||
continue;
|
||||
}
|
||||
var rulePort = rule.port == null || rule.port === 0 ? (rule.scheme == "https" ? 443 : 80) : rule.port;
|
||||
var currentPort = port === 0 || port === "" || port == null ? (scheme == "https" ? 443 : 80) : port;
|
||||
var IPv6 = null;
|
||||
if (rule.host != null && rule.host[0] === "[") {
|
||||
try {
|
||||
IPv6 = window.\(JAVASCRIPT_BRIDGE_NAME)._normalizeIPv6(rule.host.substring(1, rule.host.length - 1));
|
||||
} catch {}
|
||||
}
|
||||
var hostIPv6 = null;
|
||||
try {
|
||||
hostIPv6 = window.\(JAVASCRIPT_BRIDGE_NAME)._normalizeIPv6(host);
|
||||
} catch {}
|
||||
|
||||
var schemeAllowed = scheme == rule.scheme;
|
||||
|
||||
var hostAllowed = rule.host == null ||
|
||||
rule.host === "" ||
|
||||
host === rule.host ||
|
||||
(rule.host[0] === "*" && host != null && host.indexOf(rule.host.split("*")[1]) >= 0) ||
|
||||
(hostIPv6 != null && IPv6 != null && hostIPv6 === IPv6);
|
||||
|
||||
var portAllowed = rulePort === currentPort;
|
||||
|
||||
if (schemeAllowed && hostAllowed && portAllowed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
"""
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// WindowIdJS.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 16/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let WINDOW_ID_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_WINDOW_ID_JS_PLUGIN_SCRIPT"
|
||||
|
||||
let WINDOW_ID_VARIABLE_JS_SOURCE = "window._\(JAVASCRIPT_BRIDGE_NAME)_windowId"
|
||||
|
||||
let WINDOW_ID_INITIALIZE_JS_SOURCE = """
|
||||
(function() {
|
||||
\(WINDOW_ID_VARIABLE_JS_SOURCE) = \(PluginScriptsUtil.VAR_PLACEHOLDER_VALUE);
|
||||
return \(WINDOW_ID_VARIABLE_JS_SOURCE);
|
||||
})()
|
||||
"""
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// CustomUIPrintPageRenderer.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 10/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
//
|
||||
//public class CustomUIPrintPageRenderer: UIPrintPageRenderer {
|
||||
// private var _numberOfPages: Int?
|
||||
// private var forceRenderingQuality: Int?
|
||||
//
|
||||
// public init(numberOfPage: Int? = nil, forceRenderingQuality: Int? = nil) {
|
||||
// super.init()
|
||||
// self._numberOfPages = numberOfPage
|
||||
// self.forceRenderingQuality = forceRenderingQuality
|
||||
// }
|
||||
//
|
||||
// open override var numberOfPages: Int {
|
||||
// get {
|
||||
// return _numberOfPages ?? super.numberOfPages
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @available(iOS 14.5, *)
|
||||
// open override func currentRenderingQuality(forRequested requestedRenderingQuality: UIPrintRenderingQuality) -> UIPrintRenderingQuality {
|
||||
// if let forceRenderingQuality = forceRenderingQuality,
|
||||
// let quality = UIPrintRenderingQuality.init(rawValue: forceRenderingQuality) {
|
||||
// return quality
|
||||
// }
|
||||
// return super.currentRenderingQuality(forRequested: requestedRenderingQuality)
|
||||
// }
|
||||
//}
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// PrintAttributes.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 10/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class PrintAttributes: NSObject {
|
||||
var orientation: NSPrintInfo.PaperOrientation?
|
||||
var margins: NSEdgeInsets?
|
||||
var paperRect: CGRect?
|
||||
var colorMode: String?
|
||||
var duplex: Int?
|
||||
var paperName: String?
|
||||
var localizedPaperName: String?
|
||||
var horizontalPagination: UInt?
|
||||
var verticalPagination: UInt?
|
||||
var jobDisposition: String?
|
||||
var printableRect: NSRect?
|
||||
var isHorizontallyCentered: Bool?
|
||||
var isVerticallyCentered: Bool?
|
||||
var isSelectionOnly: Bool?
|
||||
var scalingFactor: CGFloat?
|
||||
var jobSavingURL: String?
|
||||
var detailedErrorReporting: Bool?
|
||||
var faxNumber: String?
|
||||
var headerAndFooter: Bool?
|
||||
var mustCollate: Bool?
|
||||
var pagesAcross: Int?
|
||||
var pagesDown: Int?
|
||||
var time: Int?
|
||||
|
||||
public init(fromPrintJobController: PrintJobController) {
|
||||
super.init()
|
||||
if let job = fromPrintJobController.job {
|
||||
let printInfo = job.printInfo
|
||||
let printInfoDictionary = printInfo.dictionary()
|
||||
orientation = printInfo.orientation
|
||||
margins = NSEdgeInsets(top: printInfo.topMargin,
|
||||
left: printInfo.leftMargin,
|
||||
bottom: printInfo.bottomMargin,
|
||||
right: printInfo.rightMargin)
|
||||
paperRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: printInfo.paperSize)
|
||||
colorMode = printInfo.printSettings["ColorModel"] as? String
|
||||
duplex = printInfo.printSettings["com_apple_print_PrintSettings_PMDuplexing"] as? Int
|
||||
paperName = printInfo.paperName?.rawValue
|
||||
localizedPaperName = printInfo.localizedPaperName
|
||||
horizontalPagination = printInfo.horizontalPagination.rawValue
|
||||
verticalPagination = printInfo.verticalPagination.rawValue
|
||||
jobDisposition = printInfo.jobDisposition.rawValue
|
||||
printableRect = printInfo.imageablePageBounds
|
||||
isHorizontallyCentered = printInfo.isHorizontallyCentered
|
||||
isVerticallyCentered = printInfo.isVerticallyCentered
|
||||
isSelectionOnly = printInfo.isSelectionOnly
|
||||
scalingFactor = printInfo.scalingFactor
|
||||
jobSavingURL = (printInfoDictionary[NSPrintInfo.AttributeKey.jobSavingURL] as? URL)?.absoluteString
|
||||
detailedErrorReporting = printInfoDictionary[NSPrintInfo.AttributeKey.detailedErrorReporting] as? Bool
|
||||
faxNumber = printInfoDictionary[NSPrintInfo.AttributeKey.faxNumber] as? String
|
||||
headerAndFooter = printInfoDictionary[NSPrintInfo.AttributeKey.headerAndFooter] as? Bool
|
||||
mustCollate = printInfoDictionary[NSPrintInfo.AttributeKey.mustCollate] as? Bool
|
||||
pagesAcross = printInfoDictionary[NSPrintInfo.AttributeKey.pagesAcross] as? Int
|
||||
pagesDown = printInfoDictionary[NSPrintInfo.AttributeKey.pagesDown] as? Int
|
||||
if let timestamp = (printInfoDictionary[NSPrintInfo.AttributeKey.time] as? Date)?.timeIntervalSince1970 {
|
||||
time = Int(timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func toMap () -> [String:Any?] {
|
||||
return [
|
||||
"paperRect": paperRect?.toMap(),
|
||||
"margins": margins?.toMap(),
|
||||
"orientation": orientation?.rawValue,
|
||||
"colorMode": colorMode,
|
||||
"duplex": duplex,
|
||||
"paperName": paperName,
|
||||
"localizedPaperName": localizedPaperName,
|
||||
"horizontalPagination": horizontalPagination,
|
||||
"verticalPagination": verticalPagination,
|
||||
"jobDisposition": jobDisposition,
|
||||
"printableRect": printableRect?.toMap(),
|
||||
"isHorizontallyCentered": isHorizontallyCentered,
|
||||
"isVerticallyCentered": isVerticallyCentered,
|
||||
"isSelectionOnly": isSelectionOnly,
|
||||
"scalingFactor": scalingFactor,
|
||||
"jobSavingURL": jobSavingURL,
|
||||
"detailedErrorReporting": detailedErrorReporting,
|
||||
"faxNumber": faxNumber,
|
||||
"headerAndFooter": headerAndFooter,
|
||||
"mustCollate": mustCollate,
|
||||
"pagesAcross": pagesAcross,
|
||||
"pagesDown": pagesDown,
|
||||
"time": time
|
||||
]
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// PrintJobChannelDelegate.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 09/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class PrintJobChannelDelegate: ChannelDelegate {
|
||||
private weak var printJobController: PrintJobController?
|
||||
|
||||
public init(printJobController: PrintJobController, channel: FlutterMethodChannel) {
|
||||
super.init(channel: channel)
|
||||
self.printJobController = printJobController
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "getInfo":
|
||||
if let printJobController = printJobController {
|
||||
result(printJobController.getInfo()?.toMap())
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "dispose":
|
||||
if let printJobController = printJobController {
|
||||
printJobController.dispose()
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func onComplete(completed: Bool, error: Error?) {
|
||||
let arguments: [String: Any?] = [
|
||||
"completed": completed,
|
||||
"error": error?.localizedDescription
|
||||
]
|
||||
channel?.invokeMethod("onComplete", arguments: arguments)
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
printJobController = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
Vendored
+93
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// PrintJob.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 09/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public enum PrintJobState: Int {
|
||||
case created = 1
|
||||
case started = 3
|
||||
case completed = 5
|
||||
case canceled = 7
|
||||
}
|
||||
|
||||
public class PrintJobController: NSObject, Disposable {
|
||||
static let METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_printjobcontroller_"
|
||||
var id: String
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
var job: NSPrintOperation?
|
||||
var settings: PrintJobSettings?
|
||||
var channelDelegate: PrintJobChannelDelegate?
|
||||
var state = PrintJobState.created
|
||||
var creationTime = Int64(Date().timeIntervalSince1970 * 1000)
|
||||
private var completionHandler: PrintJobController.CompletionHandler?
|
||||
|
||||
public typealias CompletionHandler = (_ printOperation: NSPrintOperation,
|
||||
_ success: Bool,
|
||||
_ contextInfo: UnsafeMutableRawPointer?) -> Void
|
||||
|
||||
public init(plugin: InAppWebViewFlutterPlugin, id: String, job: NSPrintOperation? = nil, settings: PrintJobSettings? = nil) {
|
||||
self.id = id
|
||||
self.plugin = plugin
|
||||
super.init()
|
||||
self.job = job
|
||||
self.settings = settings
|
||||
if let registrar = plugin.registrar {
|
||||
let channel = FlutterMethodChannel(name: PrintJobController.METHOD_CHANNEL_NAME_PREFIX + id,
|
||||
binaryMessenger: registrar.messenger)
|
||||
self.channelDelegate = PrintJobChannelDelegate(printJobController: self, channel: channel)
|
||||
}
|
||||
}
|
||||
|
||||
public func present(parentWindow: NSWindow? = nil, completionHandler: PrintJobController.CompletionHandler? = nil) {
|
||||
guard let job = job else {
|
||||
return
|
||||
}
|
||||
state = .started
|
||||
self.completionHandler = completionHandler
|
||||
if let mainWindow = parentWindow ?? NSApplication.shared.mainWindow {
|
||||
job.runModal(for: mainWindow, delegate: self, didRun: #selector(printOperationDidRun), contextInfo: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func printOperationDidRun(printOperation: NSPrintOperation,
|
||||
success: Bool,
|
||||
contextInfo: UnsafeMutableRawPointer?) {
|
||||
state = success ? .completed : .canceled
|
||||
channelDelegate?.onComplete(completed: success, error: nil)
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(printOperation, success, contextInfo)
|
||||
self.completionHandler = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func getInfo() -> PrintJobInfo? {
|
||||
guard let _ = job else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return PrintJobInfo.init(fromPrintJobController: self)
|
||||
}
|
||||
|
||||
public func disposeNoDismiss() {
|
||||
channelDelegate?.dispose()
|
||||
channelDelegate = nil
|
||||
completionHandler = nil
|
||||
job = nil
|
||||
plugin?.printJobManager?.jobs[id] = nil
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
channelDelegate?.dispose()
|
||||
channelDelegate = nil
|
||||
completionHandler = nil
|
||||
job = nil
|
||||
plugin?.printJobManager?.jobs[id] = nil
|
||||
plugin = nil
|
||||
}
|
||||
}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// PrintJobInfo.swift
|
||||
// flutter_downloader
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 10/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class PrintJobInfo: NSObject {
|
||||
var state: PrintJobState
|
||||
var attributes: PrintAttributes
|
||||
var creationTime: Int64
|
||||
var numberOfPages: Int?
|
||||
var copies: Int?
|
||||
var label: String?
|
||||
var printer: NSPrinter?
|
||||
var pageOrder: Int?
|
||||
var preferredRenderingQuality: Int?
|
||||
var showsProgressPanel: Bool?
|
||||
var showsPrintPanel: Bool?
|
||||
var canSpawnSeparateThread: Bool?
|
||||
var isCopyingOperation: Bool?
|
||||
var currentPage: Int?
|
||||
var firstPage: Int?
|
||||
var lastPage: Int?
|
||||
|
||||
public init(fromPrintJobController: PrintJobController) {
|
||||
state = fromPrintJobController.state
|
||||
creationTime = fromPrintJobController.creationTime
|
||||
attributes = PrintAttributes.init(fromPrintJobController: fromPrintJobController)
|
||||
super.init()
|
||||
if let job = fromPrintJobController.job {
|
||||
let printInfo = job.printInfo
|
||||
let printInfoDictionary = printInfo.dictionary()
|
||||
printer = printInfo.printer
|
||||
copies = printInfo.printSettings["com_apple_print_PrintSettings_PMCopies"] as? Int
|
||||
label = job.jobTitle
|
||||
firstPage = printInfoDictionary[NSPrintInfo.AttributeKey.firstPage] as? Int
|
||||
lastPage = printInfoDictionary[NSPrintInfo.AttributeKey.lastPage] as? Int
|
||||
if let firstPage = firstPage, let lastPage = lastPage {
|
||||
numberOfPages = lastPage - firstPage + 1
|
||||
}
|
||||
if numberOfPages == nil {
|
||||
numberOfPages = printInfo.printSettings["com_apple_print_PrintSettings_PMLastPage"] as? Int
|
||||
if numberOfPages == nil || numberOfPages! > job.pageRange.length {
|
||||
numberOfPages = job.pageRange.length
|
||||
}
|
||||
}
|
||||
pageOrder = job.pageOrder.rawValue
|
||||
preferredRenderingQuality = job.preferredRenderingQuality.rawValue
|
||||
showsProgressPanel = job.showsProgressPanel
|
||||
showsPrintPanel = job.showsPrintPanel
|
||||
canSpawnSeparateThread = job.canSpawnSeparateThread
|
||||
isCopyingOperation = job.isCopyingOperation
|
||||
currentPage = job.currentPage
|
||||
}
|
||||
}
|
||||
|
||||
public func toMap () -> [String:Any?] {
|
||||
return [
|
||||
"state": state.rawValue,
|
||||
"attributes": attributes.toMap(),
|
||||
"numberOfPages": numberOfPages,
|
||||
"copies": copies,
|
||||
"creationTime": creationTime,
|
||||
"label": label,
|
||||
"printer": printer?.toMap(),
|
||||
"pageOrder": pageOrder,
|
||||
"preferredRenderingQuality": preferredRenderingQuality,
|
||||
"showsProgressPanel": showsProgressPanel,
|
||||
"showsPrintPanel": showsPrintPanel,
|
||||
"canSpawnSeparateThread": canSpawnSeparateThread,
|
||||
"isCopyingOperation": isCopyingOperation,
|
||||
"currentPage": currentPage,
|
||||
"firstPage": firstPage,
|
||||
"lastPage": lastPage
|
||||
]
|
||||
}
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// PrintJobManager.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 09/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class PrintJobManager: NSObject, Disposable {
|
||||
var plugin: InAppWebViewFlutterPlugin?
|
||||
var jobs: [String: PrintJobController?] = [:]
|
||||
|
||||
public init(plugin: InAppWebViewFlutterPlugin?) {
|
||||
super.init()
|
||||
self.plugin = plugin
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
let jobValues = jobs.values
|
||||
jobValues.forEach { (job: PrintJobController?) in
|
||||
job?.dispose()
|
||||
}
|
||||
jobs.removeAll()
|
||||
plugin = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
Vendored
+210
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// PrintJobSettings.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 09/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers
|
||||
public class PrintJobSettings: ISettings<PrintJobController> {
|
||||
|
||||
public var handledByClient = false
|
||||
public var jobName: String?
|
||||
public var _orientation: NSNumber?
|
||||
public var orientation: Int? {
|
||||
get {
|
||||
return _orientation?.intValue
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_orientation = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_orientation = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var _numberOfPages: NSNumber?
|
||||
public var numberOfPages: Int64? {
|
||||
get {
|
||||
return _numberOfPages?.int64Value
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_numberOfPages = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_numberOfPages = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var margins: NSEdgeInsets?
|
||||
public var colorMode: String?
|
||||
public var showsNumberOfCopies = true
|
||||
public var showsPaperOrientation = true
|
||||
public var showsPaperSize = true
|
||||
public var showsScaling = true
|
||||
public var showsPageRange = true
|
||||
public var showsPageSetupAccessory = true
|
||||
public var showsPreview = true
|
||||
public var showsPrintSelection = true
|
||||
public var showsPrintPanel = true
|
||||
public var showsProgressPanel = true
|
||||
public var _scalingFactor: NSNumber?
|
||||
public var scalingFactor: Double? {
|
||||
get {
|
||||
return _scalingFactor?.doubleValue
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_scalingFactor = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_scalingFactor = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var jobDisposition: String?
|
||||
public var jobSavingURL: String?
|
||||
public var paperName: String?
|
||||
public var _horizontalPagination: NSNumber?
|
||||
public var horizontalPagination: UInt? {
|
||||
get {
|
||||
return _horizontalPagination?.uintValue
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_horizontalPagination = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_horizontalPagination = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var _verticalPagination: NSNumber?
|
||||
public var verticalPagination: UInt? {
|
||||
get {
|
||||
return _verticalPagination?.uintValue
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_verticalPagination = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_verticalPagination = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var isHorizontallyCentered = true
|
||||
public var isVerticallyCentered = true
|
||||
public var _pageOrder: NSNumber?
|
||||
public var pageOrder: Int? {
|
||||
get {
|
||||
return _pageOrder?.intValue
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_pageOrder = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_pageOrder = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var canSpawnSeparateThread = true
|
||||
public var copies = 1
|
||||
public var _firstPage: NSNumber?
|
||||
public var firstPage: Int64? {
|
||||
get {
|
||||
return _firstPage?.int64Value
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_firstPage = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_firstPage = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var _lastPage: NSNumber?
|
||||
public var lastPage: Int64? {
|
||||
get {
|
||||
return _lastPage?.int64Value
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_lastPage = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_lastPage = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var detailedErrorReporting = false
|
||||
public var faxNumber: String?
|
||||
public var headerAndFooter = true
|
||||
public var _mustCollate: NSNumber?
|
||||
public var mustCollate: Bool? {
|
||||
get {
|
||||
return _mustCollate?.boolValue
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_mustCollate = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_mustCollate = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var _pagesAcross: NSNumber?
|
||||
public var pagesAcross: Int64? {
|
||||
get {
|
||||
return _pagesAcross?.int64Value
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_pagesAcross = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_pagesAcross = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var _pagesDown: NSNumber?
|
||||
public var pagesDown: Int64? {
|
||||
get {
|
||||
return _pagesDown?.int64Value
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_pagesDown = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_pagesDown = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
public var _time: NSNumber?
|
||||
public var time: Int64? {
|
||||
get {
|
||||
return _time?.int64Value
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_time = NSNumber.init(value: newValue)
|
||||
} else {
|
||||
_time = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override init(){
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func parse(settings: [String: Any?]) -> PrintJobSettings {
|
||||
let _ = super.parse(settings: settings)
|
||||
if let marginsMap = settings["margins"] as? [String : Double] {
|
||||
margins = NSEdgeInsets.fromMap(map: marginsMap)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
override func getRealSettings(obj: PrintJobController?) -> [String: Any?] {
|
||||
var realOptions: [String: Any?] = toMap()
|
||||
return realOptions
|
||||
}
|
||||
}
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// BaseCallbackResult.swift
|
||||
// shared-apple
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 17/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class BaseCallbackResult<T>: CallbackResult<T> {
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
self.success = { [weak self] (obj: Any?) in
|
||||
let result: T? = self?.decodeResult(obj)
|
||||
var shouldRunDefaultBehaviour = false
|
||||
if let result = result {
|
||||
shouldRunDefaultBehaviour = self?.nonNullSuccess(result) ?? shouldRunDefaultBehaviour
|
||||
} else {
|
||||
shouldRunDefaultBehaviour = self?.nullSuccess() ?? shouldRunDefaultBehaviour
|
||||
}
|
||||
if shouldRunDefaultBehaviour {
|
||||
self?.defaultBehaviour(result)
|
||||
}
|
||||
}
|
||||
|
||||
self.notImplemented = { [weak self] in
|
||||
self?.defaultBehaviour(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// CGRect.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 10/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CGRect {
|
||||
public static func fromMap(map: [String: Double]) -> CGRect {
|
||||
return CGRect(x: map["x"]!, y: map["y"]!, width: map["width"]!, height: map["height"]!)
|
||||
}
|
||||
|
||||
public func toMap () -> [String:Any?] {
|
||||
return [
|
||||
"x": minX,
|
||||
"y": minY,
|
||||
"width": width,
|
||||
"height": height
|
||||
]
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// CallbackResult.swift
|
||||
// shared-apple
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 17/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class CallbackResult<T>: MethodChannelResult {
|
||||
public var notImplemented: () -> Void = {}
|
||||
public var success: (Any?) -> Void = {_ in }
|
||||
public var error: (String, String?, Any?) -> Void = {_,_,_ in }
|
||||
public var nonNullSuccess: (T) -> Bool = {_ in true}
|
||||
public var nullSuccess: () -> Bool = {true}
|
||||
public var defaultBehaviour: (T?) -> Void = {_ in }
|
||||
public var decodeResult: (Any?) -> T? = {_ in nil}
|
||||
}
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// ChannelDelegate.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 04/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class ChannelDelegate: FlutterMethodCallDelegate, Disposable {
|
||||
var channel: FlutterMethodChannel?
|
||||
|
||||
public init(channel: FlutterMethodChannel) {
|
||||
super.init()
|
||||
self.channel = channel
|
||||
self.channel?.setMethodCallHandler(handle)
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
channel?.setMethodCallHandler(nil)
|
||||
channel = nil
|
||||
}
|
||||
}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// ClientCertChallenge.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 15/02/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class ClientCertChallenge: NSObject {
|
||||
var protectionSpace: URLProtectionSpace!
|
||||
|
||||
public init(fromChallenge: URLAuthenticationChallenge) {
|
||||
protectionSpace = fromChallenge.protectionSpace
|
||||
}
|
||||
|
||||
public func toMap () -> [String:Any?] {
|
||||
return [
|
||||
"protectionSpace": protectionSpace.toMap(),
|
||||
]
|
||||
}
|
||||
}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// ClientCertResponse.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class ClientCertResponse: NSObject {
|
||||
var certificatePath: String
|
||||
var certificatePassword: String?
|
||||
var keyStoreType: String?
|
||||
var action: Int?
|
||||
|
||||
public init(certificatePath: String, certificatePassword: String? = nil, keyStoreType: String? = nil, action: Int? = nil) {
|
||||
self.certificatePath = certificatePath
|
||||
self.certificatePassword = certificatePassword
|
||||
self.keyStoreType = keyStoreType
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public static func fromMap(map: [String:Any?]?) -> ClientCertResponse? {
|
||||
guard let map = map else {
|
||||
return nil
|
||||
}
|
||||
let certificatePath = map["certificatePath"] as! String
|
||||
let certificatePassword = map["certificatePassword"] as? String
|
||||
let keyStoreType = map["keyStoreType"] as? String
|
||||
let action = map["action"] as? Int
|
||||
return ClientCertResponse(certificatePath: certificatePath, certificatePassword: certificatePassword, keyStoreType: keyStoreType, action: action)
|
||||
}
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// CreateWindowAction.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
|
||||
public class CreateWindowAction: NSObject {
|
||||
var navigationAction: WKNavigationAction
|
||||
var windowId: Int64
|
||||
var windowFeatures: WKWindowFeatures
|
||||
var isDialog: Bool?
|
||||
|
||||
public init(navigationAction: WKNavigationAction, windowId: Int64, windowFeatures: WKWindowFeatures, isDialog: Bool? = nil) {
|
||||
self.navigationAction = navigationAction
|
||||
self.windowId = windowId
|
||||
self.windowFeatures = windowFeatures
|
||||
self.isDialog = isDialog
|
||||
}
|
||||
|
||||
public func toMap () -> [String:Any?] {
|
||||
var map = navigationAction.toMap()
|
||||
map["windowId"] = windowId
|
||||
map["windowFeatures"] = windowFeatures.toMap()
|
||||
map["isDialog"] = isDialog
|
||||
return map
|
||||
}
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// CustomSchemeResponse.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class CustomSchemeResponse: NSObject {
|
||||
var data: Data
|
||||
var contentType: String
|
||||
var contentEncoding: String
|
||||
|
||||
public init(data: Data, contentType: String, contentEncoding: String) {
|
||||
self.data = data
|
||||
self.contentType = contentType
|
||||
self.contentEncoding = contentEncoding
|
||||
}
|
||||
|
||||
public static func fromMap(map: [String:Any?]?) -> CustomSchemeResponse? {
|
||||
guard let map = map else {
|
||||
return nil
|
||||
}
|
||||
let data = map["data"] as! FlutterStandardTypedData
|
||||
let contentType = map["contentType"] as! String
|
||||
let contentEncoding = map["contentEncoding"] as! String
|
||||
return CustomSchemeResponse(data: data.data, contentType: contentType, contentEncoding: contentEncoding)
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// Disposable.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 04/05/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol Disposable {
|
||||
func dispose() -> Void
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// DownloadStartRequest.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 17/04/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class DownloadStartRequest: NSObject {
|
||||
var url: String
|
||||
var userAgent: String?
|
||||
var contentDisposition: String?
|
||||
var mimeType: String?
|
||||
var contentLength: Int64
|
||||
var suggestedFilename: String?
|
||||
var textEncodingName: String?
|
||||
|
||||
public init(url: String, userAgent: String?, contentDisposition: String?,
|
||||
mimeType: String?, contentLength: Int64,
|
||||
suggestedFilename: String?, textEncodingName: String?) {
|
||||
self.url = url
|
||||
self.userAgent = userAgent
|
||||
self.contentDisposition = contentDisposition
|
||||
self.mimeType = mimeType
|
||||
self.contentLength = contentLength
|
||||
self.suggestedFilename = suggestedFilename
|
||||
self.textEncodingName = textEncodingName
|
||||
}
|
||||
|
||||
public func toMap () -> [String:Any?] {
|
||||
return [
|
||||
"url": url,
|
||||
"userAgent": userAgent,
|
||||
"contentDisposition": contentDisposition,
|
||||
"mimeType": mimeType,
|
||||
"contentLength": contentLength,
|
||||
"suggestedFilename": suggestedFilename,
|
||||
"textEncodingName": textEncodingName
|
||||
]
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// UIFindSession.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class FindSession: NSObject {
|
||||
var resultCount: Int
|
||||
var highlightedResultIndex: Int
|
||||
var searchResultDisplayStyle: Int
|
||||
|
||||
public init(resultCount: Int, highlightedResultIndex: Int, searchResultDisplayStyle: Int) {
|
||||
self.resultCount = resultCount
|
||||
self.highlightedResultIndex = highlightedResultIndex
|
||||
self.searchResultDisplayStyle = searchResultDisplayStyle
|
||||
}
|
||||
|
||||
public func toMap () -> [String:Any?] {
|
||||
return [
|
||||
"resultCount": resultCount,
|
||||
"highlightedResultIndex": highlightedResultIndex,
|
||||
"searchResultDisplayStyle": searchResultDisplayStyle
|
||||
]
|
||||
}
|
||||
}
|
||||
Loaded 100 of 556 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user