Stefan Goßner

Senior Escalation Engineer for SharePoint (WSS, SPS, MOSS, SP2010) and MCMS

Blogs

MCMS and the ActiveX activation behavior change in Internet Explorer (last update: December 23rd, 2008)

  • Comments 6
  • Likes

Microsoft had to modify the behavior of ActiveX controls due to an existing patent from Eolas on this technology. See here for more details on this.

After the change ActiveX controls embedded in the html code of a web page have to be activated manually by a user - e.g. through a click on the ActiveX control. This can be very annoying for users.

At least there is a workaround to prevent the need for an activation when following the coding guidelines outlined in the following document:

The workaround actually requires that the object tag for the ActiveX control has to be placed in an external script file.

This behavior change also affects the ActiveX control included in the MCMS HtmlPlaceholder editor component.

Unlike other ActiveX controls the problem is very small for this ActiveX control as you have to click on this ActiveX control anyway to use it. So clicking on it to activate it should not really be an issue.

Anyway: I have received the question about how to overcome the need to activate the ActiveX control for the HtmlPlaceholder editor component.

The MCMS product team already decided that this behavior will not be changed in a hotfix. So I looked into the details of how MCMS embeds the object tags into the html content and to identify a way to move this code into an external javascript file.

The solution I implemented uses a custom placeholder control derived from the original HtmlPlaceholderControl. This is required to modify the embedding of the object tags.

In addition I had to add an additional ASPX page to my project. This ASPX page is responsible to generate the external javascript code on the fly.

The placeholder control will remove the object tag and replace it with script tags which refer to the additional ASPX page. Using query string parameters to the ASPX the ASPX ensures that the correct javascript code is generated.

Here is the code for the custom placeholder control:

using System;
using System.IO;
using System.Web;
using System.Text.RegularExpressions;
using Microsoft.ContentManagement.Publishing;
using Microsoft.ContentManagement.WebControls;

namespace StefanG.PlaceholderControls
{
    // Customer placeholder control to modify the insertion of object tags to comply to 
    // IE behaviour changes
    public class AutoActivateHtmlPlaceholderControl : HtmlPlaceholderControl
    {
        public AutoActivateHtmlPlaceholderControl()
        { 
            // empty constructor
        }

        // register a client script blog that modifies the insertion of the object tag for the
        // editor toolbar
        protected override void OnLoad(EventArgs e)
        { 
            if (((WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringReedit) 
                ||(WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringNew)))
            { 
                string js = "<script language=\"javascript\" type=\"text/javascript\" src=\""+Page.Request.ApplicationPath+
                    "/CMS/WebAuthor/Client/PlaceholderControlSupport/AuthFormClientIE.js\"></script>";
                string vbs = "<script language=\"vbscript\" type=\"text/vbscript\" src=\""+Page.Request.ApplicationPath+
                    "/CMS/WebAuthor/Client/PlaceholderControlSupport/ActiveXEditing.vbs\"></script>";
                string activeX = "<script language=\"Javascript\" src=\""+Page.Request.ApplicationPath+
                    "/CreateIEBehaviorChangeJavascript.aspx?script=Toolbar\"></script>\n"+
                    "<script language=\"Javascript\">ActiveX_writeToolbar();</script>"
;
                Page.RegisterClientScriptBlock("ActiveXToolbarAndEditorSupportScript", js+"\n"+vbs+"\n"+activeX);
            } 
        } 

        // in the Render method we consume the generated html content from the base class
        // then we remove the object tag using a regular expression and replace it 
        // with a script tag that get's it content from the external 
        // CreateIEBehaviorChangeJavascript.ASPX page which 
        // generates the javascript code on the fly.
        protected override void Render(System.Web.UI.HtmlTextWriter output)
        {
            if (((WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringReedit) 
                ||(WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringNew)))
            {
                // in authoring mode catch the output of the original HtmlPlaceholderControl
                TextWriter tempWriter = new StringWriter();
                base.Render(new System.Web.UI.HtmlTextWriter(tempWriter));
                
                string orightml= tempWriter.ToString();

                // modify the html content
                string strRegExp = "<object[^\\0]*</object>";
                Regex r = new Regex( strRegExp, RegexOptions.IgnoreCase | RegexOptions.Compiled );
                string newhtml = r.Replace( orightml, 
                    "<script language=\"Javascript\" src=\""+Page.Request.ApplicationPath+
                          "/CreateIEBehaviorChangeJavascript.aspx"+
                    "?script=Placeholder"+
                    "&"+Page.Request.QueryString.ToString()+ 
                    "&PhName="+HttpUtility.UrlEncode(this.BoundPlaceholder.Name)+
                    "&Width="+this.EditControlWidth+
                    "&Height="+this.EditControlHeight+
                    "\"></script>\n"+
                    "<script lang='javascript'>ActiveX_placeholder_"+this.BoundPlaceholder.Name+"();</script>" );

                // return the modified html content
                output.Write(newhtml);
            }
            else
            {
                // not in authoring mode. Call the base render method.
                base.Render(output);
            }
        }

    }
}

Ok, so far so good. The custom placeholder control will now ensure that no object tag is generated in the html content and that an external javascript file generated by the CreateIEBehaviorChangeJavascript.ASPX javascript file which needs to be located in the root of the web application.

To create this ASP.NET webform please add a regular ASP.NET webform at the root of your template project (same level as the web.config file) and name it CreateIEBehaviorChangeJavascript.aspx.

In your solution you can also move this page to a different folder or give it a different name. But you need to ensure that the path in the placeholder control correctly points to this file.

Now double click somewhere on the new webform to get to the code behind file and add the following method:

protected override void Render( HtmlTextWriter writer)

    if (Request.QueryString["script"] == "Toolbar"
    { 
        // here we emit the object tag for the ActiveX Toolbar
        writer.Write("\n// IE Behavior Change adaption by Stefan Goßner\n"+
            "function ActiveX_writeToolbar()\n"+
            "{\n"+
            "document.writeln('<OBJECT ID=\"ToolbarInterface\" "+
               "CLASSID=\"CLSID:E99D3E39-5D92-4360-BA86-2C563B3CFFEB\" \\n');\n"
+
            "document.writeln('CODEBASE=\""+Page.Request.ApplicationPath+
               "/CMS/WebAuthor/Client/PlaceholderControlSupport/nrdhtml.cab\" "+
               "WIDTH=0 HEIGHT=0>\\n');\n"
+
            "document.writeln('<PARAM NAME=\"BaseUrl\" VALUE=\""+Page.Request.ApplicationPath+
               "/CMS/WebAuthor/Client/PlaceholderControlSupport/ToolbarImages/\">');\n"+
            "document.writeln('</OBJECT>');\n"+
            "}\n"); 
    }
    else if (Request.QueryString["script"] == "Placeholder")
    { 
        // we need to retrieve the original placeholder content to fill the placeholder control
        string EncodedHtmlContent = "";
        Posting p = CmsHttpContext.Current.Posting;
        if (p != null)
        {
            HtmlPlaceholder htmlPh = (p.Placeholders[HttpUtility.UrlDecode(Request.QueryString["PhName"])] as HtmlPlaceholder);
            if (htmlPh != null)
                EncodedHtmlContent = HttpUtility.HtmlEncode(htmlPh.Html);
        } 

        // here we emit the object tag for the ActiveX Editor Component
        writer.Write("\n// IE Behavior Change adaption by Stefan Goßner\n"+
            "function ActiveX_placeholder_"+Request.QueryString["PhName"]+"()\n"+
            "{\n"+
            "document.writeln('<OBJECT CODEBASE=\""+Page.Request.ApplicationPath+
               "/CMS/WebAuthor/Client/PlaceholderControlSupport/NRDHtml.cab\"');\n"+
            "document.writeln('ID=\"NCPHRICH_"+Request.QueryString["PhName"]+
               "\" CLASSID=\"CLSID:B33422AC-C567-4F7D-BB28-6583371EC4EE\" width=\""+
               Request.QueryString["Width"]+"\" height=\""+Request.QueryString["Height"]+"\">');\n"+
            "document.writeln('<PARAM NAME=\"PostUrl\" VALUE=\""+Page.Request.ApplicationPath+
               "/CMS/WebAuthor/Controls/ActiveXHtmlEditControl/ActiveXUpload.aspx?NRMODE=Update&NRNODEGUID="+
               Request.QueryString["NRNODEGUID"]+"&ph="+Request.QueryString["PhName"]+"\">');\n"+
            "document.writeln('   <PARAM NAME=\"HTML\" VALUE=\""+
                EncodedHtmlContent.Replace("\n"," ").Replace("\r"," ").Replace("'","&#039;")+"\"> ');\n"+
            "document.writeln('   <PARAM NAME=\"CodePage\" VALUE=\""+
                HttpContext.Current.Response.ContentEncoding.CodePage+"\"> ');\n"+
            "document.writeln('   <PARAM NAME=\"Charset\" VALUE=\""+
                HttpContext.Current.Response.ContentEncoding.WebName+"\"> ');\n"+
            "document.writeln('</OBJECT>');\n"+
            "}\n");
    }
    else
        writer.Write("SG-Error");
}

Now you have to replace all HtmlPlaceholderControls in your project with the AutoActivateHtmlPlaceholderControl. Also be aware that if you have custom placeholder controls in place which are also derived from the original HtmlPlaceholderControl now need to be derived from the AutoActivateHtmlPlaceholderControl to ensure that they are automatically activated.

Update: the sample code can now be downloaded from MSDN Code Gallery.

Comments
  • Hey Stephen

    I as just wondering why you are using an @ symbol in the regular expression:

    string strRegExp = "<object[^@]*</object>";

    Won't that cause problems with a placeholder that has an email address in it? e.g.

    <PARAM NAME="HTML" VALUE="someone@somewhere.com">

    Would "<object.*?</object>" be better?

    Also, what happens if someone edits a page, and you don't save it to the posting (e.g. it fails validation), when you reload the page, the changes you made to the control will be gone, as the javascript retrieves it from the posting object, which hasn't been updated with the submitted changes.

  • Hi Shaun,

    nice catch! I tested with .*? and .* but this did not do the trick. No idea why.
    I ended up ignoring null bytes which should not be inside html content: [^\0]

    Cheers,
    Stefan

  • Wow, what a rediculous effort to go through just to have an unnessecary interruption to a web experience vanish. Hey, here's an idea, why not add the ability to turn this off via the Internet Options panel? Huh, who would of thought it could be so simple.

    Looking for ways to simply life,

    Robert Joy

  • Hi Robert,

    it was not Microsoft's decision to do this change.

    Actually the was a court issue regarding patent rights which required this change. Microsoft is not allowed to add an option to turn this off using an option.

    Cheers,

    Stefan

  • We have the simple solution for elimination of a "Click to activate" IE problem!

    http://www.flash-extensions.net/products/removing-ie-activation/">http://www.flash-extensions.net/products/removing-ie-activation/

    Regards

    http://www.flash-extensions.net/

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
Raw Html Fix