Export (0) Print
Expand All

A Moving and a Shaking

As of December 2011, this topic has been archived. As a result, it is no longer actively maintained. For more information, see Archived Content. For information, recommendations, and guidance regarding the current version of Internet Explorer, see Internet Explorer Developer Center.

Mark Davis, Heidi Housten, Dan Mohr, and Kusuma Vellanki
Microsoft Corporation

March 5, 2001

We have just about recovered our nerves after the first public demonstration of Microsoft Earthquake Beta 1. Perhaps we need to modify the output slightly, Salt Lake City might be a little farther than we need to reach for the beta.

The team has moved out of the barely contained chaos stage and into a good rhythm. We found some interesting questions for you this month ranging from how to generate element behaviors using XSL to User data persistence, to automating saving images, to… well read on and see for yourself!

Contents

Behaving in Style—Using XSL to output element behaviors
Save the Last GIF for Me—Saving images
Caching In on Storage—Storing data on the client
Get In the Zone—Zoning restrictions for hosted Web browser controls

The Web Team in Short

Behaving in Style

Dear Web Team:

DHTML element behaviors in Microsoft Internet Explorer 5.5 are really cool, but how can I use XSL to output the declarations that are required to use element behaviors?

Chan Wei Min

The Web Team replies:

Heard the buzz about XML? XSL (eXtensible Stylesheet Language) is a language for transforming the structure of an XML (eXtensible Markup Language) document. Converting XML to HTML is probably the most common application of XSL, in which XML is used to structure the data and XSL does the work of presenting the data in the form of HTML/CSS. This technique works well because it allows data structure and presentation to be separated and, since the languages are text-based, is ideal for Web applications.

Element behaviors were introduced with Internet Explorer 5.5, and extend the functionality of DHTML behaviors by allowing the developer to define custom elements that implement self-contained HTML objects.

The following sample code demonstrates a simple element behavior that is implemented using a script HTML component. For more information, please see the MSDN Online documentation for element behaviors.

<PUBLIC:COMPONENT TAGNAME="highlight" LIGHTWEIGHT="true" >
  <PUBLIC:PROPERTY NAME="color" />
  <PUBLIC:ATTACH EVENT="onmouseover" FOR="element" ONEVENT="on()" />
  <PUBLIC:ATTACH EVENT="onmouseout" FOR="element" ONEVENT="off()" />
</PUBLIC:COMPONENT>
<SCRIPT LANGUAGE="JScript">
function on()
{
  element.runtimeStyle.color = color;
}
function off()
{
  element.runtimeStyle.color = "";
}
</SCRIPT>

The following HTML demonstrates how we would use this element behavior on a Web page, assuming that the behavior was implemented in a file named highlight.htc:

<HTML xmlns:ext>
<HEAD>
<?IMPORT NAMESPACE="ext" IMPLEMENTATION="highlight.htc">
</HEAD>
<BODY>
<ext:highlight color="red">Red</ext:highlight>
<ext:highlight color="green">Green</ext:highlight>
<ext:highlight color="blue">Blue</ext:highlight>
</BODY>
</HTML>

The problem that we have is that HTML does not have to conform to the rules of well-formed XML. XSL, however, is written as XML. In fact, writing XSL that outputs HTML means that, maybe for the first time, you'll be closing every HTML element and placing attribute values in quotes. If you look closely at the previous HTML listing, you'll notice there are three items that don't match the rules of well-formed XML.

  1. The namespace attribute on the HTML element does not include a value assignment in quotes.
  2. The import declaration contains an invalid character, '?', and has no closing element.
  3. The custom elements contain an unknown namespace.

Since we want to use XSL and output HTML declarations that are not valid XML, how do we mix the two technologies? The answer is that XSL provides some elements that allow additional control over the output.

For each of the element behavior declarations, we can use an XSL element to produce the desired output.

  1. The namespace attribute on the HTML element can be output by using the XSL <xsl:attribute> element. Internet Explorer ignores the value that we specify.
  2. The IMPORT declaration is a processing instruction, so we can use the XSL <xsl:pi> element to output this element.
  3. The custom elements cannot be output as literal result elements because the namespace, ext, is unknown, but we can use the XSL <xsl:element> element to write these elements to the output.

The following XML contains data that we want to render using our element behavior. Internet Explorer will use the XSL specified by the <xml:stylesheet> element to transform the XML.

<?XML VERSION="1.0"?>
<?xml:stylesheet type="text/xsl" href="presentation.xsl"?>
<ROOT>
<TEXT>Roses are <COLOR>red</COLOR>, violets are <COLOR>blue</COLOR>.</TEXT>
<TEXT>My <COLOR>green</COLOR> and <COLOR>yellow</COLOR> sweater is just the right hue.</TEXT>
</ROOT>

Finally, the following XSL is one way we can produce the desired HTML output. Save this to a file named presentation.xsl to transform the previous XML data.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<xsl:template match="/">
  <HTML>
  <xsl:attribute name="xmlns:ext"/>
  <HEAD>
  <xsl:pi name="IMPORT">NAMESPACE="ext" IMPLEMENTATION="highlight.htc"</xsl:pi>
  </HEAD>
  <BODY>
  <xsl:apply-templates/>
  </BODY>
  </HTML>
</xsl:template>

<xsl:template match="root">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="text">
  <xsl:apply-templates/><BR/>
</xsl:template>

<xsl:template match="color">
  <xsl:element name="ext:highlight">
    <xsl:attribute name="color"><xsl:value-of select="."/></xsl:attribute>
    <xsl:value-of select="."/></xsl:element></xsl:template>

<xsl:template match="text()">
  <xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>

Save the Last GIF for Me

Dear Web Team:

I am using VB6 [Visual Basic® 6.0] on a Windows® 2000 Advanced Server PC to create an application hosting the WebBrowser control to AUTOMATICALLY select and save to the local hard disk certain HTM, JPG, and GIF files in the document window. I can use the DHTML method:

document.execCommand("saveas",false,path&filename) 

To save the main HTML page to disk, but despite using the "False" argument, an unwanted dialog box is always displayed. This of course demands user input, which I don't know how to provide by program, and in any case does not save any embedded images. I would like to use a similar command to save selected JPGs and GIFs embedded in the document to hard disk, without requesting them again from the server, but I can't find the right syntax. Could you advise the best way to save these files efficiently and automatically, without any user intervention?

Many thanks,
Donald Cruttenden

The Web Team Replies:

Boy, security is a double-edged sword, isn't it? Though the execCommand method can be used on images (via the controlRange object or IHTMLControlRange), as well as the document, the SaveAs command will ignore the showUI parameter and always prompt the user. This is, as you may have guessed, designed to prevent evil pages from saving 500,000 pictures of, let's say, our smiling faces to your hard drive without prompting you. However, it's going to prompt the user in this case too, even though you have only the best intentions (right?). As it turns out, the execCommand method is actually a wrapper of sorts for IOleCommandTarget::Exec() which also prompts the user when it's called on to save an item programmatically (via either OLECMDID_SAVE or OLECMDID_SAVEAS) as does another wrapper for this method IWebBrowser2::ExecWB. Now that we've eliminated practically every function on the site with 'save' in the description, is all hope lost?

Far from it—we just need to call on our friend URLMON to save us. If we can't ask the WebBrowser control to save these items silently, we'll just have do it ourselves, and we're going to use an URLMON API, UrlDownloadToFile, to help. As described in Q244757 - HOWTO: Download a File Without Prompting, we can use this function to silently download a file to a specific location on a disk given only a URL. So to illustrate how this can be done, here's a quick Visual Basic sample that shows how to programmatically save all the images on the page without prompting the user. To test out this code, just start a new VB EXE project, add a WebBrowser control, name it wbMain, and then paste the following code into your form:

Private Declare Function URLDownloadToFile Lib "urlmon" Alias _
    "URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, ByVal _
    szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long

Const BASE_DIR = "C:\images\"

Private Sub Form_Load()
    wbMain.Navigate "http://msdn.microsoft.com/ie"
End Sub

Private Sub wbMain_DocumentComplete(ByVal pDisp As Object, URL As Variant)
    
    Dim collImages As IHTMLElementCollection
    Dim img As IHTMLImgElement
    Dim strFileName As String
    Dim strExtension As String
    Dim lResult As Long
    Dim i As Long
    Dim nAnomalies As Long
    
    Set collImages = pDisp.Document.getElementsByTagName("IMG")
    
    For i = 0 To collImages.length - 1
        Set img = collImages.Item(i)
        strFileName = Right(img.src, Len(img.src) - InStrRev(img.src, "/"))
        strExtension = LCase(Right(strFileName, Len(strFileName) - InStrRev(strFileName, ".")))
        If strExtension = "gif" Or strExtension = "jpg" Or strExtension = "jpeg" Then
            lResult = URLDownloadToFile(0, img.src, BASE_DIR & strFileName, 0, 0)
        Else
            ' The file may be dynamically generated or may have
            ' a file name without an extension.  We'll inspect
            ' the mimeType property as a last ditch effort and
            ' invent our own filename.
            If InStr(1, img.mimeType, "GIF", vbTextCompare) Then
                lResult = URLDownloadToFile(0, img.src, BASE_DIR & "anomaly" & nAnomalies & ".gif", 0, 0)
            ElseIf InStr(1, img.mimeType, "JPG", vbTextCompare) Then
                lResult = URLDownloadToFile(0, img.src, BASE_DIR & "anomaly" & nAnomalies & ".jpg", 0, 0)
            End If
        End If
        Set img = Nothing
    Next i
    Set collImages = Nothing
End Sub

As indicated by the last else clause, images generated dynamically by server-side code (ASP pages or ISAPI DLLs) could pose a problem from the filename perspective, but the mimeType property will rescue us in most cases.

[The implementation of code to fetch INPUT TYPE=IMAGE elements is left as an exercise to the reader.]

Caching In on Storage

Dear Web Team:

I am developing a software product designed to run specifically on Internet Explorer 5.0 (and higher). I have been looking for Internet Explorer specification documentation. For instance, one issue is the amount of data that can be cached by the browser. What is the maximum data that can be cached? Do multiple windows increase the size of the data I can store? Is there a good online source that will cover this information?

Thanks,
Brian Simon
Cupertino, CA

Web Team Replies:

Persistence is rewarding! All the information you ever wanted to know about the userData and other persistence behaviors of Internet Explorer 5.0 and greater can be found at the following URLs. There are also some good samples that demonstrate how these default persistence behaviors can be used.

http://msdn.microsoft.com/workshop/author/persistence/overview.asp

http://msdn.microsoft.com/workshop/author/behaviors/reference/behaviors/userData.asp

http://msdn.microsoft.com/workshop/Author/persistence/howto/sessiondata.asp

http://www.microsoft.com/mind/1299/inside/inside1299.asp

There are limits to the amount of data you can persist per document and domain, and it depends on the security zone of the domain. Typically, the limit for the Internet zone is 128 KB per document and 1024 KB per domain. If the zone of the URL cannot be determined, the limits are 64 KB per document and 640 KB per domain. You can use hidden frames in your document to increase the amount of data you want to store but still seamlessly show only one page to the user.

Using the userData persistence feature is far better than using cookies for two reasons:

  1. You will avoid sending the information back and forth between the client and server, thus decreasing your navigation and page load time. This will also help improve the performance of the server.
  2. You can store structured data since the userData persistence behavior uses XML for its storage.

Get In the Zone

Dear Web Team:

I started to use the function CoGetClassObjectFromURL() in my application, but I can't deal with the interface IInternetHostSecurityManager. When the process of download begins, this function performs a QueryInterface in my IBindStatusCallback implementation to obtain the IInternetHostSecurityManager interface. Then, I return the pointer to my implementation of the interface. When the function calls the ProcessUrlAction method, my implementation always returns URLPOLICY_ALLOW in pPolicy argument, but this seems to be ignored, and the function honors the Internet Explorer settings. What happened?

Thanks in advance,
Marcos Cesar

The Web Team replies:

From your problem description, it sounds like you're running into the limitation mentioned in Q246227 - SAMPLE: SECUMGR Overrides Security Manager for WebBrowser Host. You can't programmatically loosen security settings for the URLACTION_DOWNLOAD_UNSIGNED_ACTIVEX action. The only way to get around this is to temporarily add your download site to a security zone and then to modify that zone's policy to allow this action to occur without prompting. Before getting into the subject of how to do all this programmatically, it is important to be aware of the potential security risks involved (you are going to be loosening the Internet Explorer global security settings even if only temporarily). Caveat coder—no program should ever manipulate a user's security settings without making the user aware of what the program is doing and give the user a choice as to whether or not he or she will accept these risks (if not, he or she will have to live with some prompting). Now that we've underscored the scary security topic, let's jump into the fun and write some code.

Adding a site to a security zone is a relatively straightforward procedure best carried out using IInternetSecurityManager and IInternetZoneManager interfaces. These interfaces indirectly modify the security zone information stored in the registry, so you will not be able to use them in situations where the user does not have permissions to modify the underlying registry keys. While it's true that you could achieve the same effect by changing the registry keys directly, keep in mind that these registry structures could change with the next version of Internet Explorer. The security and zone manager abstractions prevent you from having to completely rewrite your code in case that happens.

The code to carry out our task can be broken up into three sections:

  • The global variables (in which we'll persist the current security settings so we can reset them when we're through).
  • The code we'll execute before calling CoGetClassObjectFromURL.
  • The code we'll execute after calling CoGetClassObjectFromURL.

Here's the global variables section (if the guilt of using global variables keeps you up at night, feel free to use class members instead):

#define DOWNLOAD_SITE TEXT("http://myserver")
DWORD g_dwOldTemplate = URLTEMPLATE_LOW;
DWORD g_dwOldFlags = 0;
DWORD g_dwOldPolicy = URLPOLICY_QUERY;

Here's the pre-CoGetClassObjectFromURL code:

IInternetSecurityManager *pSecurityMgr = NULL;
IInternetZoneManager *pZoneMgr = NULL;
DWORD dwNewPolicy = URLPOLICY_ALLOW;
HRESULT hr = S_OK;
ZONEATTRIBUTES za;
za.cbSize = sizeof(za);

hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER, 
      IID_IInternetSecurityManager, (void**)&pSecurityMgr);

hr = CoCreateInstance(CLSID_InternetZoneManager, NULL, CLSCTX_INPROC_SERVER, 
      IID_IInternetZoneManager, (void**)&pZoneMgr);

hr = pZoneMgr->GetZoneAttributes(URLZONE_TRUSTED, &za);

g_dwOldTemplate = za.dwTemplateCurrentLevel;
g_dwOldFlags = za.dwFlags;

za.dwFlags &= ~ZAFLAGS_REQUIRE_VERIFICATION;
za.dwTemplateCurrentLevel = URLTEMPLATE_CUSTOM;
hr = pZoneMgr->SetZoneAttributes(URLZONE_TRUSTED, &za);

hr = pSecurityMgr->SetZoneMapping(URLZONE_TRUSTED, DOWNLOAD_SITE, SZM_CREATE); 

hr = pZoneMgr->GetZoneActionPolicy(URLZONE_TRUSTED, URLACTION_DOWNLOAD_UNSIGNED_ACTIVEX, 
      (BYTE*)&g_dwOldPolicy, sizeof(DWORD), URLZONEREG_DEFAULT);

hr = pZoneMgr->SetZoneActionPolicy(URLZONE_TRUSTED, URLACTION_DOWNLOAD_UNSIGNED_ACTIVEX, 
      (BYTE*)&dwNewPolicy, sizeof(DWORD), URLZONEREG_DEFAULT);

pZoneMgr->Release();
pSecurityMgr->Release();

And finally the post-CoGetClassObjectFromURL code:

IInternetSecurityManager *pSecurityMgr = NULL;
IInternetZoneManager *pZoneMgr = NULL;
HRESULT hr = S_OK;
ZONEATTRIBUTES za;
za.cbSize = sizeof(za);

hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER, 
      IID_IInternetSecurityManager, (void**)&pSecurityMgr);

hr = CoCreateInstance(CLSID_InternetZoneManager, NULL, CLSCTX_INPROC_SERVER, 
      IID_IInternetZoneManager, (void**)&pZoneMgr);

hr = pZoneMgr->SetZoneActionPolicy(URLZONE_TRUSTED, URLACTION_DOWNLOAD_UNSIGNED_ACTIVEX, 
      (BYTE*)&g_dwOldPolicy, sizeof(DWORD), URLZONEREG_DEFAULT);

hr = pSecurityMgr->SetZoneMapping(URLZONE_TRUSTED, DOWNLOAD_SITE, SZM_DELETE); 

hr = pZoneMgr->GetZoneAttributes(URLZONE_TRUSTED, &za);
za.dwTemplateCurrentLevel = g_dwOldTemplate;
za.dwFlags = g_dwOldFlags;
hr = pZoneMgr->SetZoneAttributes(URLZONE_TRUSTED, &za);

pZoneMgr->Release();
pSecurityMgr->Release();

Note that the clearing of the ZAFLAGS_REQUIRE_VERIFICATION flag is necessary as it prevents the addition of Trusted Sites that do not use HTTPS. Note also that we replace all the original settings after we're through downloading the components; however, there is a finite amount of time during the asynchronous download in which the modified security settings will be used by all other instances of Internet Explorer. It's in this window of time when you could see ill effects from this procedure. Another potential hazard is the case in which something goes awry in the download process. It's imperative that all code paths go through the cleanup/restoration code in order to prevent the security from staying modified.

For more information about Internet Explorer's security zone registry settings check out Q182569 - Description of Internet Explorer Security Zones Registry Entries or consult your local librarian.

The Web Team in Short

CSS and a padded cell

Q: Rolando in Peru wanted to know how to set cellpadding and cellspacing on tables with CSS.

A: You can set cellpadding using the CSS padding property for the TD. CSS2 specifies a table cell-spacing property, but most browsers with CSS support only fully support CSS1, so you won't find it in Internet Explorer or Netscape yet. CSS2 also allows for the border-collapse attribute which is supported in Internet Explorer 5 and later. It removes the space between the table and cell borders.

Word up or browser up?

Q: Jimmy asked how to ensure a Microsoft Word document opens in Word instead of in Internet Explorer.

A: You should find what you need in the KB article Q259970 - PRB: In-Place Activating Document Servers in Internet Explorer.

Streaming and the IHTMLDocument2 Object

Q: Ajit asked about difficulties using IPersistStreamInit and IStream with Internet Explorer 5.5.

A: The KB article Q271868 - BUG: IPersistStreamInit Not Available for a FRAME in a FRAMESET should help you get around this problem.

On last month's synchronous scrolling sample

Q: Erik had trouble getting the sample code to work that illustrated how to scroll to iframes synchronously.

A: The text for that sample states that there is a security issue with accessing frames that point to a different domain than the parent window. If you use the code directly from the article, without changing the source attribute of the iframes, then you need to save the file as an .hta file, not an .htm file.


  
Show:
© 2014 Microsoft