`

MiNiWeb浏览器核心技术详解

阅读更多
MiNiWeb浏览器核心技术详解
 
 

目录

1简介

2目标、挑战和解决方案

捕捉脚本错误

过滤弹出窗口

添加多标签页或多窗口浏览功能

当一个窗口是由脚本关闭时,需要确认

3创建webbrowser扩展控件

实现 iwebbrowser2接口

实现 dwebbrowserevents2接口

4使用该扩展控件

捕捉脚本错误

过滤弹出窗口并添加多标签页或多窗口浏览功能

使用退出事件

 

1简介

.net 2.0system.windows.forms命名空间中新增了webbrowser控件,该控件本身是非常有用的,但是它没有提供在某些情况下需要的事件。这篇文章描述了如何扩展webbrowser控件并增加一些功能,例如:屏蔽弹出窗口、捕捉脚本错误以及捕捉新窗口并将其显示在多标签浏览窗口环境中。
在扩展webbrowser控件时,某些功能没有写入.net framework的帮助文件,不用理会“这个方法是用于支持.net基础架构的,不推荐直接用于您的代码中”的提示信息,我们可以创建一个实现iwebbrowser2接口的对象,并使用浏览器对象的全部功能,此外,使用dwebbrowserevents2接口可以向控件中添加事件。

我们假设你已经了解了iwebbrowser2接口和dwebbrowserevents2接口,对com的互操作和相关的接口知识也是需要了解的。

2、目标、挑战和解决方案

这个组件要实现的目标是:

用简洁的方式捕捉脚本错误

过滤弹出窗口

加入多标签页浏览或多窗口浏览功能

当窗口被脚本关闭时需要确认

这一节简要讲解实现这些目标所碰到的问题和相关的解决方案,下一节中会给出更多的代码细节。

捕捉脚本错误

webbrowser控件有一个scripterrorsuppressed属性,将这个属性设置为true时,该控件确实会比原来多做了一点事情,它不仅禁用了脚本出错的对话框,而且还禁用了登陆到需要用户证书的安全站点时出现的登陆对话框。但是假如我们仍然需要这个功能,或者我们想获得脚本出错的通知,或者我们想知道全部的脚本出错的细节时该怎么办呢?

脚本错误可以在htmlwindow.error事件中捕捉,这个事件会在脚本发生错误时触发并包含全部的错误细节信息,但是难点在于htmlwindow是需要通过htmldocument对象才能访问,而该对象并不是什么时候都有效,htmldocument对象只在navigated事件触发时才有效,而假如用户是按f5键刷新浏览器时呢,抱歉,navigated事件是不会触发的。在经过了很多的尝试后,我发现唯一可行的方法是使用并不是默认webbrowser控件一部分的downloadcomplete事件。

解决方案:

1.实现dwebbrowserevents接口

2.创建一个downloadcomplete事件

3.downloadcomplete事件触发时,订阅htmlwindow.error事件

4.利用这个error事件来获得脚本出错的具体信息

5.设置handled属性为true来阻止脚本出错

过滤弹出窗口

弹出窗口大部分情况都是不怎么受欢迎的或者是不适宜的,屏蔽这些弹出窗口需要一些额外的信息。当用户使用windows xp sp2或者windows 2003 sp1 或更高版本时,newwindow3事件可以提供这些辅助信息,假如这个事件没有触发,那么newwindows2事件会替代该事件。当newwindow3事件触发时,你可以检查以下内容:

l是否是用户的操作才导致了新开窗口

l用户是否按住了覆盖键(ctrl 键)

l是否因为当一个窗口正在关闭才导致显示弹出窗口

l获得将要打开窗口的url地址

l更多...

使用newwindows3事件可以很明显的实现这个目的,假如要使用这个事件,就必须实现dwebbrowserevents2接口。

解决方案:

1.实现dwebbrowserevents2接口

2.创建一个新的事件和一个新的事件参数类

3.执行这个事件并附带适当的信息

4.当这个事件触发后,检查这次的导航是否需要取消

添加多标签页或多窗口浏览功能

多标签页方式浏览在目前似乎变得越来越流行,例如在ie7中,这就是一个新增功能。实现多标签页方式浏览的难点是,你需要在当脚本或者超链接创建一个新窗口的时候去创建相应的新的标签页或子窗口,除此之外,还需要解析出多窗口或者多标签页的窗口名称。(例如:<a href=”http://somesite” target=”somewindowname”/>)要实现这一点,一些自动化对象(如:newwindowx事件中的ppdispiwebbrowser2接口中的application)就需要从新开窗口传回到该事件中。而访问application属性需要获得iwebbrowser2接口的引用。

解决方案:

1.重载attachinterfacesdetachinterfaces接口

2.保存iwebbrowser2接口对象的引用

3.创建一个application属性来暴露该接口中的application属性

4.实现dwebbrowserevent2接口

5.监听newwindows2/newwindow3事件

6.当一个事件触发时,创建一个新的browser控件的实例

7.ppdisp事件参数指派给新实例的application属性

当一个窗口被脚本关闭时需要确认

当你在jscript中调用window.close()方法,webbrowser控件很可能出现假死。因为某种原因,他不能用于导航页面,也不能做其他任何事情。假如我们知道它什么时候发生可能会好一些。当它发生时会触发一系列的事件,但是这些事件没有给我们需要的信息。重载wndproc方法并检测父窗口是否通知该浏览器已经被销毁是唯一可行的解决方法(假如谁知道如何得到windowsclosing事件来实现这一点是更好的方法)

解决方案:

1.重载wndproc方法

2.检查wm_parentnotify消息

3.检查wm_destriy参数

4.假如检测到了上述的内容,则触发一个新的事件(这个事件在例子中称为quit

3、创建webbrowser扩展组件

从上一节中,我们可以发现上述的所有内容都基本可以归结为两件事:

1.实现一个iwebbrowser2类型的对象,从中获得application属性

2.实现dwebbrowserevents2接口来触发事件

实现iwebbrowser2接口

 

using System;

using System.Security;

using System.Runtime.InteropServices;

using System.Windows.Forms;

using System.Security.Permissions;

 

namespace MiniBrowser

{

    ///<summary>

    /// An extended version of the <see cref="WebBrowser"/> control.

    ///</summary>

    class ExtendedWebBrowser : System.Windows.Forms.WebBrowser

    {

 

        private UnsafeNativeMethods.IWebBrowser2 axIWebBrowser2;

 

        ///<summary>

        /// This method supports the .NET Framework infrastructure and is not intended to be used directly from your code. 

        /// Called by the control when the underlying ActiveX control is created. 

        ///</summary>

        ///<param name="nativeActiveXObject"></param>

        [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]

        protected override void AttachInterfaces(object nativeActiveXObject)

        {

            this.axIWebBrowser2 = (UnsafeNativeMethods.IWebBrowser2)nativeActiveXObject;

            base.AttachInterfaces(nativeActiveXObject);

        }

 

        ///<summary>

        /// This method supports the .NET Framework infrastructure and is not intended to be used directly from your code. 

        /// Called by the control when the underlying ActiveX control is discarded. 

        ///</summary>

        [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]

        protected override void DetachInterfaces()

        {

            this.axIWebBrowser2 = null;

            base.DetachInterfaces();

        }

 

        ///<summary>

        /// Returns the automation object for the web browser

        ///</summary>

        public object Application

        {

            get { return axIWebBrowser2.Application; }

        }

 

 

        System.Windows.Forms.AxHost.ConnectionPointCookie cookie;

        WebBrowserExtendedEvents events;

 

        ///<summary>

        /// This method will be called to give you a chance to create your own event sink

        ///</summary>

        [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]

        protected override void CreateSink()

        {

            // Make sure to call the base class or the normal events won't fire

            base.CreateSink();

            events = new WebBrowserExtendedEvents(this);

            cookie = new AxHost.ConnectionPointCookie(this.ActiveXInstance, events, typeof(UnsafeNativeMethods.DWebBrowserEvents2));

        }

 

        ///<summary>

        /// Detaches the event sink

        ///</summary>

        [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]

        protected override void DetachSink()

        {

            if (null != cookie)

            {

                cookie.Disconnect();

                cookie = null;

            }

        }

 

        ///<summary>

        /// Fires when downloading of a document begins

        ///</summary>

        public event EventHandler Downloading;

 

        ///<summary>

        /// Raises the <see cref="Downloading"/> event

        ///</summary>

        ///<param name="e">Empty <see cref="EventArgs"/></param>

        ///<remarks>

        /// You could start an animation or a notification that downloading is starting

        ///</remarks>

        protected void OnDownloading(EventArgs e)

        {

            if (Downloading != null)

                Downloading(this, e);

        }

 

        ///<summary>

        /// Fires when downloading is completed

        ///</summary>

        ///<remarks>

        /// Here you could start monitoring for script errors. 

        ///</remarks>

        public event EventHandler DownloadComplete;

        ///<summary>

        /// Raises the <see cref="DownloadComplete"/> event

        ///</summary>

        ///<param name="e">Empty <see cref="EventArgs"/></param>

        protected virtual void OnDownloadComplete(EventArgs e)

        {

            if (DownloadComplete != null)

                DownloadComplete(this, e);

        }

       ……

    }

}

 

webbrowser控件有两个尚未公开的接口:attachinterfaces()detachinterfaces()。这些方法用于获得iwebbrowser2接口的引用。

下一步,我们可以添加application属性。

 

         ///<summary>

        /// Returns the automation object for the web browser

        ///</summary>

        public object Application

        {

            get { return axIWebBrowser2.Application; }

        }

 

这个属性可以用来创建一个新窗口,并且当创建新窗口事件触发时将浏览器重定向到这个新窗口。

实现dwebbrowserevents2接口

在这个例子中实现了下列事件:

lnewwindow2newwindow3(用于屏蔽弹出窗口和创建新窗口)

ldownloadbegindownloadcomplete(用于捕捉脚本错误)

lbeforenavigate2(用于在导航到一个页面前查看即将导航到的地址)

为了简洁的实现dwebbrowserevents接口,最好的方法是在组件中建立一个私有的嵌入类。这样,所有需要的事件都在一个地方并且轻易查找。当我们实例化这个类的时候,我们可以给调用者提供一个引用,利用该引用可以调用方法来触发我们需要的事件。

在组件的构造过程中并没有附带这些事件,而是稍微晚一点。这里有两个方法来实现它并且它们是可以重载的。它们是createsink()detachsink()。当我们将这些都添加完以后,我们的代码会像下面这样(注重有些代码为了阅读方便而删掉了)

/// <summary>

/// An extended version of the <see cref="WebBrowser"/> control.

/// </summary>    

public class extendedwebbrowser : system.windows.forms.webbrowser

{

  //   (MORE CODE HERE)

 

 SYSTEM.WINDOWS.FORMS.AXHOST.CONNECTIONPOINTCOOKIE COOKIE;

 WEBBROWSEREXTENDEDEVENTS EVENTS;

 

  /// <SUMMARY>

 /// THIS METHOD WILL BE CALLED TO GIVE

 /// YOU A CHANCE TO CREATE YOUR OWN EVENT SINK

 /// </SUMMARY>

 [PERMISSIONSET(SECURITYACTION.LINKDEMAND, NAME = "FULLTRUST")]

 PROTECTED OVERRIDE VOID CREATESINK()

 {

   // MAKE SURE TO CALL THE BASE CLASS OR THE NORMAL EVENTS WON'T FIRE

   BASE.CREATESINK();

   EVENTS = NEW WEBBROWSEREXTENDEDEVENTS(THIS);

   COOKIE = NEW AXHOST.CONNECTIONPOINTCOOKIE(THIS.ACTIVEXINSTANCE, 

                     EVENTS, TYPEOF(UNSAFENATIVEMETHODS.DWEBBROWSEREVENTS2));

 }

 

  /// <SUMMARY>

 /// DETACHES THE EVENT SINK

 /// </SUMMARY>

 [PERMISSIONSET(SECURITYACTION.LINKDEMAND, NAME = "FULLTRUST")]

 PROTECTED OVERRIDE VOID DETACHSINK()

  {

   IF (NULL != COOKIE)

       {

        COOKIE.DISCONNECT();

        COOKIE = NULL;

   }

 }

 

  /// <SUMMARY>

 /// FIRES WHEN DOWNLOADING OF A DOCUMENT BEGINS

 /// </SUMMARY>

 PUBLIC EVENT EVENTHANDLER DOWNLOADING;

 

  /// <SUMMARY>

 /// RAISES THE <SEE CREF="DOWNLOADING"/> EVENT

 /// </SUMMARY>

 /// <PARAM NAME="E">EMPTY <SEE CREF="EVENTARGS"/></PARAM>

 /// <REMARKS>

 /// YOU COULD START AN ANIMATION

 /// OR A NOTIFICATION THAT DOWNLOADING IS STARTING

 /// </REMARKS>

 PROTECTED VOID ONDOWNLOADING(EVENTARGS E)

  {

   IF (DOWNLOADING != NULL)

        DOWNLOADING(THIS, E);

 }

 

 //   (MORE CODE HERE)

 

  THE IMPLEMENTATION OF DWEBBROWSEREVENTS2 FOR FIRING EXTRA EVENTS
}

 

 

4、使用这个组件

上一节,我们创建了一个新的组件。现在,我们来使用这些新的事件并尽可能多的挖掘浏览器的功能。针对每一个目标,具体的解释如下:

捕捉脚本错误

在示例程序中,有一个工具窗口简单的显示了发生错误的列表并附带了错误的具体内容。一个单一实例类把握了脚本错误的信息并且当这个信息发生改变时通知所有订阅者,为了捕捉这些脚本错误,browsercontrol首先附加到downloadcomplete事件,其次它订阅了htmlwindow.error事件。当这个事件触发时,我们注册这个脚本错误并设置handled属性为true

 

public partial class browsercontrol : usercontrol
    {

     public browsercontrol()

      {

       initializecomponent();

       _browser = new extendedwebbrowser();

       _browser.dock = dockstyle.fill;

 

       // here's the new downloadcomplete event

       _browser.downloadcomplete += 

         new eventhandler(_browser_downloadcomplete);

       // some more code here 

       this.containerpanel.controls.add(_browser);

       // some more code here 

     }

 

 

     void _browser_downloadcomplete(object sender, eventargs e)

      {

       // check wheter the document is available (it should be)

       if (this.webbrowser.document != null)

         // subscribe to the error event

         this.webbrowser.document.window.error += 

           new htmlelementerroreventhandler(window_error);

     }

 

     void window_error(object sender, htmlelementerroreventargs e)

      {

       // we got a script error, record it

       scripterrormanager.instance.registerscripterror(e.url, 

                                e.description, e.linenumber);

       // let the browser know we handled this error.

       e.handled = true;

     }

 

     // some more code here

   }

 

过滤弹出窗口,并且增加多标签页或多窗口浏览功能

捕捉弹出窗口必须可以由用户来进行配置。为了示范的目的,我实现了四个级别,从不屏蔽任何窗口到屏蔽所有新窗口。下面的代码是browsercontorl的一部分,用来展现如何实现这一点。当一个新建窗口被答应后,示例程序展现了如何让新建窗口实现窗口名称的解决方案。

void _browser_startnewwindow(object sender, 

 browserextendednavigatingeventargs e)

  {

   // here we do the pop-up blocker work

 

   // note that in windows 2000 or lower this event will fire, but the

   // event arguments will not contain any useful information

   // for blocking pop-ups.

 

   // there are 4 filter levels.

   // none: allow all pop-ups

   // low: allow pop-ups from secure sites

   // medium: block most pop-ups

   // high: block all pop-ups (use ctrl to override)

 

   // we need the instance of the main form,

   // because this holds the instance

   // to the windowmanager.

   mainform mf = getmainformfromcontrol(sender as control);

   if (mf == null)

     return;

 

   // allow a popup when there is no information

   // available or when the ctrl key is pressed

   bool allowpopup = (e.navigationcontext == urlcontext.none) 

        || ((e.navigationcontext & 

        urlcontext.overridekey) == urlcontext.overridekey);

 

   if (!allowpopup)

    {

     // give none, low & medium still a chance.

     switch (settingshelper.current.filterlevel)

      {

       case popupblockerfilterlevel.none:

         allowpopup = true;

         break;

       case popupblockerfilterlevel.low:

         // see if this is a secure site

         if (this.webbrowser.encryptionlevel != 

                  webbrowserencryptionlevel.insecure)

           allowpopup = true;

         else

           // not a secure site, handle this like the medium filter

           goto case popupblockerfilterlevel.medium;

         break;

       case popupblockerfilterlevel.medium:

         // this is the most dificult one.

         // only when the user first inited

         // and the new window is user inited

         if ((e.navigationcontext & urlcontext.userfirstinited) 

              == urlcontext.userfirstinited && 

              (e.navigationcontext & urlcontext.userinited) 

              == urlcontext.userinited)

           allowpopup = true;

         break;

     }

   }

 

   if (allowpopup)

    {

     // check wheter it's a html dialog box.

     // if so, allow the popup but do not open a new tab

     if (!((e.navigationcontext & 

            urlcontext.htmldialog) == urlcontext.htmldialog))

      {

       extendedwebbrowser ewb = mf.windowmanager.new(false);

       // the (in)famous application object

       e.automationobject = ewb.application;

     }

   }

   else

     // here you could notify the user that the pop-up was blocked

     e.cancel = true;

 } 

 

这个事件称为startnewwindow的原因是编码设计规则不答应一个事件的名称以“before”或者“after”开头。“newwindowing”事件并没有在这一范围内。

使用quit事件

quit事件触发时,我们只需要找到正确的窗口或者标签页将其关闭,并且”dispose”这个示例即可。

分享到:
评论

相关推荐

    app-项目开发手机浏览器

    【Android手机浏览器开发详解】 ...开发者应关注最新的Web技术,如WebGL、WebRTC等,以及Android API的更新,确保浏览器兼容性和性能。同时,注重用户反馈,持续改进功能和界面设计,以适应不断变化的市场需求。

    Google 开源 Chromium 浏览器

    **Chromium浏览器详解** Chromium是一款由Google主导开发的开源网页浏览器项目,它的存在为全球开发者提供了一个自由、开放的平台,以便共同构建和改进网络浏览体验。Chromium的源代码是基于 Blink 渲染引擎(由...

    UC web 黑莓

    ### 一、UC Web浏览器特点 1. **快速浏览**:UC Web采用了先进的数据压缩技术,能够在低带宽环境下实现快速加载网页,显著减少流量消耗,提高浏览效率。 2. **智能预读取**:浏览器能够预测用户可能访问的页面并...

    text-generation-webui-main

    《基于Transformer的文本生成WebUI实现详解》 在当今的自然语言处理(NLP)领域,文本生成技术已经成为一个热门的研究方向,它广泛应用于智能助手、自动摘要、创意写作等多个场景。本文将深入探讨“text-generation...

    mjpg-streamer-mini2440-bin-r6

    mjpg-streamer的核心功能在于实时处理摄像头的MJPEG(Motion JPEG)帧,将其转换为HTTP流,供网络上的客户端(如Web浏览器或移动设备)接收。其优点在于对硬件资源的需求较低,可以在低功耗、低性能的设备上运行,...

    前端开源库-mini-bench

    本文将深入探讨“mini-bench”的核心特点、功能及其在实际开发中的应用。 一、mini-bench概述 “mini-bench”是一款轻量级的JavaScript基准测试框架,它允许开发者对代码进行基准测试,从而发现性能瓶颈,优化代码...

    mjpg-streamer-mini2440-read-only.rar_Mjpg_mini2440_mjpg streamer

    输出插件则将接收到的图像流通过网络发送出去,例如HTTP或RTSP插件,使得用户可以通过Web浏览器或者流媒体播放器实时查看图像。在这个压缩包中,我们期待找到这些源代码文件,以便进一步了解和定制系统。 "Mjpg-...

    tnxweb:互联网技术解决方案:Web技术框架

    《tnxweb:互联网技术解决方案——Web技术框架详解》 在当今互联网技术日新月异的时代,Web开发框架成为构建高效、稳定、易维护的Web应用的重要工具。tnxweb作为一个专注于Web技术解决方案的框架,它集合了多种前端...

    PHP实例开发源码-MiniAJAX聊天室程序beta.zip

    下面我们将深入探讨这个程序的核心技术和实现方式。 一、PHP基础 PHP(Hypertext Preprocessor)是一种服务器端脚本语言,广泛用于网页开发。在这个实例中,PHP主要负责处理客户端发送的请求,如用户的发言,以及...

    mjpg-streamer-mini2440-bin.tar.gz

    - 启动mjpg-streamer服务后,用户可以通过Web浏览器访问指定的URL来查看视频流,也可以通过其他支持HTTP或RTSP的客户端应用程序进行连接。 - 调试和优化可能包括调整编码质量、处理延迟、网络带宽等因素,以达到...

    前端开源库-ipfs-mini

    **前端开源库-ipfs-mini详解** `ipfs-mini` 是一个专门为前端开发者设计的轻量级模块,它允许用户在Node.js环境以及浏览器环境中轻松地与InterPlanetary File System(IPFS)网络进行交互。IPFS是一种去中心化的...

    JS-H5-CSS跨平台桌面应用技术集.pdf

    - **PWA概念**:Progressive Web Apps(渐进式网络应用),一种将Web应用提升到接近原生应用水平的技术。 - **影响分析**:讨论PWA技术对Electron未来发展方向的影响,特别是在移动设备上的应用潜力。 **12. 前端...

    都说Djnago框架重,那就让哥用15行代码写个django web程序.pdf

    6. **运行与测试**:在命令行中运行`python miniWeb.py runserver`启动开发服务器,然后在浏览器中访问`http://localhost:8000/`,即可看到简化的Django Web程序。 7. **Django与Flask、Bottle比较**:相比于Flask...

    微信HTML5在线朋友圈游戏源码带安装部署教程-mini炫斗.zip

    1. **HTML5核心技术** - **HTML(超文本标记语言)**:提供页面结构,通过`&lt;div&gt;`, `&lt;canvas&gt;`等元素构建游戏场景。 - **CSS(层叠样式表)**:负责游戏界面的样式设计,包括颜色、布局和动画效果。 - **...

    miniblink pc端应用开发框架

    **miniblink PC端应用开发框架详解** Miniblink是一个高度优化且轻量级的开源浏览器内核,专为开发者设计,便于将Web功能集成到桌面应用程序中。它基于Chromium项目,但经过裁剪和优化,使得体积更小,性能更优,...

    GBrowse安装包

    GBrowse的用户界面友好,允许用户通过简单的Web浏览器访问。用户可以浏览不同层次的数据,如基因、转录本、CDS、SNP等,并可以自定义显示样式。此外,GBrowse支持多种数据源的导入,包括BED、GFF、GBK等常见格式,也...

    JMDB.rar_JMDB dll_javascript

    JMDB(JavaScript Mini Database)就是这样一种轻量级的、用于JavaScript环境的数据库解决方案,它允许开发者在用户浏览器中存储数据,而无需服务器的参与。 **JMDB的特性与优势** 1. **离线存储**:JMDB的核心...

    前端开源库-meteor-minimongo

    ** Meteor Minimongo 知识点详解 ** Meteor Minimongo是前端开发中的一款重要开源库,它在Meteor框架中扮演着核心角色。这个库的主要目的是在客户端模拟MongoDB数据库的功能,提供数据存储和查询的能力,使得前端...

    vue相关配置文件详解及多环境配置详细步骤

    `package.json` 文件是每个Node.js项目的核心配置文件之一,它记录了项目的基本信息和依赖关系。在Vue项目中,该文件同样扮演着至关重要的角色。 - **文件结构示例**: ```json { "name": "sop-vue", "version...

    百度ueditor编辑器jsp版本

    "ueditor_mini1_0_0-utf8-jsp"这个压缩包包含了UEditor的JSP版本,解压后,开发者需要将相关静态资源(如js、css、image文件)部署到Web应用的相应目录下。 3.2 配置与初始化 在JSP页面中,通过JavaScript代码调用...

Global site tag (gtag.js) - Google Analytics