likes
comments
collection
share

用Flutter InAppWebView创建WebView内容拦截器

作者站长头像
站长
· 阅读数 1

在这篇文章中,我们将学习如何使用 flutter_inappwebview 插件为我们的 WebView 实例创建一个自定义的内容拦截器。

内容拦截器通常用于阻止广告,但你也可以用它们来阻止任何其他内容。阻止行为包括隐藏元素、阻止加载,以及在 iOS 和 macOS 上从 WebView 请求中剥离 cookie。

请记住,在一般情况下,内容拦截器无法实现与 AdBlock 或 AdBlock Plus 等专门扩展相同级别的功能。内容拦截器是一组规则,当它发现需要阻止的内容时,它永远不会从 WebView 接收任何回调或通知。

通过 InAppWebViewSettings 类的 contentBlockers 属性,我们可以定义一个 WebView 将使用的 ContentBlocker 实例的列表。

ContentBlocker类

我们在 ContentBlocker 类中定义了内容拦截行为。每一个都包含一个 action(动作) 属性和一个 trigger(触发器) 属性。该 action 告诉 WebView 在遇到与 trigger 匹配的对象时该做什么。trigger 告诉 WebView 何时执行相应的操作。

下面是一个基本的例子:

initialSettings: InAppWebViewSettings(contentBlockers: [
 ContentBlocker(
   trigger: ContentBlockerTrigger(
     urlFilter: ".*",
     resourceType: [
       ContentBlockerTriggerResourceType.IMAGE,
       ContentBlockerTriggerResourceType.STYLE_SHEET
     ]
   ),
   action: ContentBlockerAction(
     type: ContentBlockerActionType.BLOCK
   )
 )
]),

在这个例子中,ContentBlocker 会阻止每个 URL 的每张图片和样式表的加载。

在内容拦截器中添加触发器

触发器必须定义所需的 urlFilter 属性,该属性将正则表达式指定为与URL匹配的字符串。其他属性是可选的——它们修改触发器的行为。例如,您可以将触发器限制到特定的域,或者当 WebView 找到与特定域匹配的时不应用该触发器。

下面是一个内容拦截器的例子,它可以触发 WebView 在任何域上找到的图像和样式表资源,除了那些指定的域:

initialSettings: InAppWebViewSettings(contentBlockers: [
 ContentBlocker(
   trigger: ContentBlockerTrigger(
     urlFilter: ".*",
     resourceType: [
       ContentBlockerTriggerResourceType.IMAGE,
       ContentBlockerTriggerResourceType.STYLE_SHEET
     ],
     unlessDomain: ["example.com", "github.com", "pub.dev"]
   ),
   action: ContentBlockerAction(
     type: ContentBlockerActionType.BLOCK
   )
 )
]),

对于更深入的触发器定制,你可以使用 ContentBlockerTrigger 的其他属性:

  • urlFilterIsCaseSensitive——如果URL匹配应该是大小写敏感的。默认情况下,它是不区分大小写的。

  • resourceType——一个 "ContentBlockerTriggerResourceType "的列表,代表该规则应该匹配的资源类型(浏览器打算如何使用该资源)。如果没有指定,该规则将匹配所有资源类型。

  • ifDomain——一个与URL的域相匹配的字符串列表;它将 action 限制在特定域的列表中。数值必须是小写的ASCII码,非ASCII码字符则为 Punycode。在前面添加 * 以匹配域和子域。它不能与 unlessDomain 一起使用。

  • loadType——一个 ContentBlockerTriggerLoadType 的列表,它可以包含两个互斥值之一。如果不指定,则匹配所有负载类型。只有当资源具有与主页面资源相同的方案、域和端口时,ContentBlockerTriggerLoadType.FIRST_PARTY 才会触发。如果资源与主页面资源不在同一域,则 ContentBlockerTriggerLoadType.THIRD_PARTY 将触发。

  • ifTopUrl——一个与整个主文件URL相匹配的字符串列表;它将 action 限制在一个特定的URL模式列表中。数值必须是小写的ASCII码,非ASCII码的字符必须是Punycode。它不能与 unlessTopUrl 一起使用。

  • unlessTopUrl——一个与整个主文件URL相匹配的字符串数组;它作用于任何网站,除了所提供的列表中的URL模式。数值必须是小写的ASCII码,非ASCII码的字符必须是Punycode。它不能与 ifTopUrl 一起使用。

  • loadContext——指定加载上下文的字符串数组。

  • ifFrameUrl——一个正则表达式的列表,用于匹配 iframes 的 URL。

查看每个特定属性的代码文档,以了解哪个平台支持该特性。

用Flutter InAppWebView创建WebView内容拦截器

向内容拦截器添加操作

当一个触发器匹配一个资源时,WebView会评估所有的触发器,并按顺序执行动作。

将具有类似操作的规则组合在一起以提高性能。例如,首先指定阻止内容加载的规则,然后指定阻止cookie的规则。

action 只有两个有效属性:typeselector,type 是必需的。

如果类型是 ContentBlockerActionType.CSS_DISPLAY_NONE,也需要一个选择器;否则, selector 是可选的。

这里有一个简单的例子:

initialSettings: InAppWebViewSettings(contentBlockers: [
 ContentBlocker(
   trigger: ContentBlockerTrigger(
     urlFilter: "https://flutter.dev/.*",
   ),
   action: ContentBlockerAction(
     type: ContentBlockerActionType.CSS_DISPLAY_NONE,
     selector: '.notification, .media, #developer-story'
   )
 )
]),

有效的类型是:

  • BLOCK——停止加载该资源,如果该资源被缓存了,则缓存被忽略。

  • BLOCK_COOKIES——在向服务器发送前,从标题中剥离 cookies。这只阻止 WebView 的隐私政策所能接受的 cookies。将 BLOCK_COOKIESIGNORE_PREVIOUS_RULES 结合起来,并不能覆盖浏览器的隐私设置。

  • CSS_DISPLAY_NONE——根据一个CSS选择器来隐藏页面的元素。一个选择器字段包含选择器列表。任何匹配的元素都将其显示属性设置为none,从而将其隐藏。

  • MAKE_HTTPS——将一个 URL 从 http 改为 https。具有指定(非默认)端口的URL和使用其他协议的链接不受影响。

  • IGNORE_PREVIOUS_RULES——忽略先前触发的行动。

检查每个具体类型的代码文档,以了解哪个平台支持它。

创建一个简单的广告拦截器

让我们使用所学知识创建一个简单的广告拦截器

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

Future main() async {
 WidgetsFlutterBinding.ensureInitialized();
 if (!kIsWeb &&
     kDebugMode &&
     defaultTargetPlatform == TargetPlatform.android) {
   await InAppWebViewController.setWebContentsDebuggingEnabled(kDebugMode);
 }
 runApp(const MaterialApp(home: MyApp()));
}

class MyApp extends StatefulWidget {
 const MyApp({Key? key}) : super(key: key);

 @override
 State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
 final GlobalKey webViewKey = GlobalKey();

 // list of ad URL filters to be used to block ads from loading
 final adUrlFilters = [
   ".*.doubleclick.net/.*",
   ".*.ads.pubmatic.com/.*",
   ".*.googlesyndication.com/.*",
   ".*.google-analytics.com/.*",
   ".*.adservice.google.*/.*",
   ".*.adbrite.com/.*",
   ".*.exponential.com/.*",
   ".*.quantserve.com/.*",
   ".*.scorecardresearch.com/.*",
   ".*.zedo.com/.*",
   ".*.adsafeprotected.com/.*",
   ".*.teads.tv/.*",
   ".*.outbrain.com/.*"
 ];

 final List<ContentBlocker> contentBlockers = [];
 var contentBlockerEnabled = true;

 InAppWebViewController? webViewController;

 @override
 void initState() {
   super.initState();

   // for each ad URL filter, add a Content Blocker to block its loading
   for (final adUrlFilter in adUrlFilters) {
     contentBlockers.add(ContentBlocker(
         trigger: ContentBlockerTrigger(
           urlFilter: adUrlFilter,
         ),
         action: ContentBlockerAction(
           type: ContentBlockerActionType.BLOCK,
         )));
   }

   // apply the "display: none" style to some HTML elements
   contentBlockers.add(ContentBlocker(
       trigger: ContentBlockerTrigger(
         urlFilter: ".*",
       ),
       action: ContentBlockerAction(
           type: ContentBlockerActionType.CSS_DISPLAY_NONE,
           selector: ".banner, .banners, .ads, .ad, .advert")));
 }

 @override
 Widget build(BuildContext context) {
   return Scaffold(
       appBar: AppBar(
         title: const Text("Ads Content Blocker"),
         actions: [
           TextButton(
             onPressed: () async {
               contentBlockerEnabled = !contentBlockerEnabled;
               if (contentBlockerEnabled) {
                 await webViewController?.setSettings(
                     settings: InAppWebViewSettings(
                         contentBlockers: contentBlockers));
               } else {
                 await webViewController?.setSettings(
                     settings: InAppWebViewSettings(contentBlockers: []));
               }
               webViewController?.reload();

               setState(() {});
             },
             style: TextButton.styleFrom(foregroundColor: Colors.white),
             child: Text(contentBlockerEnabled ? 'Disable' : 'Enable'),
           )
         ],
       ),
       body: SafeArea(
           child: Column(children: <Widget>[
         Expanded(
           child: Stack(
             children: [
               InAppWebView(
                 key: webViewKey,
                 initialUrlRequest:
                     URLRequest(url: WebUri('https://www.tomshardware.com/')),
                 initialSettings:
                     InAppWebViewSettings(contentBlockers: contentBlockers),
                 onWebViewCreated: (controller) {
                   webViewController = controller;
                 },
               ),
             ],
           ),
         ),
       ])));
 }
}

使用这些规则将阻止一堆广告的出现,如谷歌广告。

单击 "禁用/启用 "按钮,禁用或启用广告屏蔽功能。

用Flutter InAppWebView创建WebView内容拦截器

总结

内容拦截器允许我们在尊重用户隐私的情况下编写性能规则来拦截WebView中的内容。

完整的项目代码可在 GitHub 上找到。


原文:blog.codemagic.io/creating-co…