Blog ⋅ Peter Nelson

Summer of WebKit, Part 2: Printing

by peterdn 29. August 2010 15:57

My main aim for WebKit .NET over the past month or two has been to implement printing.  WebKit Cairo has had printing support since January, and I figured it would be a fairly simple task to hook onto that functionality in the .NET world.  As it turned out, there were one or two issues that had to be worked out first.

Handling _RemotableHandle

Several of the printing methods in the IWebFramePrivate interface (in the WebKit COM API), take a parameter of type HDC, which represents a handle to a graphics device context — in this case, a printer device.  Like all Windows handle types, HDC is defined as (void *), however the handle itself is only 32-bits long and so is sign extended to fit into 64-bits on Win64 (according to this white paper).  Unfortunately, due either to some bug or by design, the MIDL compiler marshals this type as a pointer to some barely documented _RemotableHandle structure which looks like the following:

public struct _RemotableHandle {
    public int fContext;
    public struct __MIDL_IWinTypes_0009 u;
}

public struct __MIDL_IWinTypes_0009 {
    public int hInProc;
    public int hRemote;
}

When run through tlbimp.exe and imported into C#, the type signature for one of these printing functions turns out to be:

public uint getPrintedPageCount(ref _RemotableHandle printDC);

Immediately skeptical, I tried various combinations of setting hInProc and hRemote to the value of the printer device handle, and setting fContext to one of the arcane-looking constants WDT_INPROC_CALL and WDT_REMOTE_CALL, but nothing seemed to work.  My first solution was to modify the relevant IDL files in WebKit itself and change the HDC types to OLE_HANDLE.  This worked well, as OLE_HANDLE is defined as a 32-bit value and MIDL marshals it as an Int32.  After inserting a few casts here and there, my printing code suddenly started to work!

This solution was not ideal, however, for a couple of reasons.  Firstly, it required me to make changes to WebKit each time, including inserting casts to OLE_HANDLE in the all functions I needed.  Secondly, as OLE_HANDLE is always 32-bits long, it was likely to cause headaches when the time came to port WebKit to Win64.  I tried getting around this by using (void *) or a different type of handle, but MIDL either didn’t like this or just marshalled the type as a _RemotableHandle again.  Damn.

The solution I eventually settled on is a bit inelegant, but seems to work as intended.  Leaving WebKit well alone, I ran the generated interop assembly through ildasm.exe, replaced all occurrences of the string “valuetype WebKit.Interop._RemotableHandle&” with “int32”, ilasm.exe’d it back together, and voila, it worked perfectly!  So I wrote a small tool that does this find and replace automatically (UNIX afficiendos should scoff at this point), and lumped it all into the solution’s pre-build event.

Functional Printing

With that sorted, and once I discovered that WebKit measures margins in 1000ths of an inch (a key bit of knowledge), printing functionality began to fall into place.  There are still some minor issues with regards to margin sizing, elements which span multiple pages, and print preview, but for the most part it works.  This functionality is included in the 0.5 release of WebKit .NET.

For the next few weeks I’m planning on focussing on JavaScript-C# interop.  I’ve already started playing around with JavaScriptCore to get a feel for how it works (see my JavaScriptCore Shell on GitHub).  Looks like I’m going to have to dust off my C++/CLI skills for the next installment…

In other random news, I’ve been distracted recently by a invite to the MonoDroid preview.  As would be expected of a pre-beta software release, it’s not perfect yet, but I’ve been very impressed by it so far.  The substantial interest generated in it, the huge amount of activity on the mailing list, and the speed at which the developers respond to and fix issues certainly bodes well for the finished product.  For me, the ability to code for Android from within Visual Studio alone may make it worthwhile.  We’ll see.  I may or may not get around to blogging about it.

Tags: , , ,

WebKit | WebKit .NET

Summer of WebKit, Part 1: Compiling WebKit

by peterdn 19. July 2010 14:52

(Updated December 18 2010.  The build process has changed subtly – updated info is marked in bold.)

With summer upon us, I’m finally getting a bit more time to work on some WebKit and WebKit .NET stuff.  My intention is to make my way through the TODOs in the project roadmap and blog about any interesting bits as I go.  As can be seen, there is a fair amount to be done, hence the ‘Part 1!’

I started, as I usually do, with the mammoth task of compiling WebKit, which inevitably takes much longer than I anticipate.  The exact process seems to change very subtly each time.  Here I’ll describe the exact steps I went though this time (with WebKit revision 63600 or so).  Although mainly for my own reference, it may save someone else a couple of hours if they run into the same problems that I did.  So, to get it building (assuming a build environment is configured as described here, Visual Studio 2008 is installed, and the WebKit source and support libraries have been downloaded):

  1. Grab the dependencies required to build the Cairo port (updated October 2 2010; thanks Brent Fulgham) and extract them somewhere.
  2. Fire up Visual Studio 2008 and open WebKit/win/WebKit.vcproj/WebKit.sln.  Run through the conversion process and finish.  Add the ‘include’ and ‘include/cairo’ directories from the dependencies in the previous step to the Visual C++ include directory list.  Similarly, add the ‘lib’ directory to the Visual C++ libraries directory list.  Close Visual Studio.
  3. Open a cygwin prompt, navigate to the WebKit root directory and run the script (Updated December 18 2010: The WebKitTools directory has been renamed to Tools):
    Tools/Scripts/build-webkit
    This downloads another set of libraries and installs them, along with the support libraries.  As we’ve converted the project to VS 2008, and the script is configured to build with VS 2005, the build will fail, but we can ignore that.  There is probably a more elegant way to install these libraries, but this method suffices for the moment.  No worries there.
  4. Added December 18 2010: It's now also necessary to run the following script to install another set of libraries:
    Tools/Scripts/update-webkit
    This will also update to the latest SVN revision.
  5. Open up WebKit.sln in VS 2008 again and set the build configuration to Release_Cairo (or Debug_Cairo as the case may be).  VC++ 2008 is much more strict than VC++ 2005 so issues more warnings, and by default, the WebKit projects are configured to treat warnings as errors.  In the C++ configuration properties for every project (specifically, JavaScriptCore, WTF, jsc, testapi, WebCore, QTMovieWin, WebKit, and WebKitLib), disable the ‘Treat Warnings as Errors’ option.  Optionally, turn off warnings completely for a smoother build experience (I got over 60,000 on warning level 4 before my machine seized up).
  6. Optionally, disable optimization for JavaScriptCore, WebCore, WebKit, and WebKitLib, but remember to enable again before a release!  This drastically decreased build time for me.
  7. In the WebCore librarian configuration properties, add the following switch to the command line options:
    /expectedoutputsize:3000000000
    This is a workaround for a problem where the linker runs out of memory and issues a “LNK1106: invalid file or disk full: cannot seek to 0x…” error message.  More information can be found in this Microsoft KB article.
  8. Optionally, but required for the DOM access functionality in WebKit .NET 0.4+, replace WebKit/win/DOMCoreClasses.cpp and WebKit/win/DOMCoreClasses.h with these modified versions.  Hopefully I’ll get these changes committed to the actual WebKit repository at some point, but until then you can grab them from here.
  9. We’re pretty much done.  Hit build and go make a cup of tea or do something else constructive for a while.  In my experience, it takes about 45-60 minutes to build from scratch with optimizations disabled, and 2-3 hours with them enabled.

And voila, we (should) have a fully-functioning WebKit Cairo build to use.

As it turns out, I did get enough time to implement basic printing functionality in WebKit .NET (no page setup or print preview yet) as well, but I’ll leave that for part 2 when it’s more complete.  The changes should be up on SVN very soon, and a 0.5 release is well on its way.

Tags: , , ,

WebKit | WebKit .NET

(First!) Using WebKit nightly builds with WebKit .NET

by peterdn 2. May 2010 00:53

Having finally built up the motivation to sort this site out, I now need to start thinking about what to actually blog about.  I’ve decided that the plan is to write about my experiences with hacking WebKit and developing WebKit .NET, and whatever else interesting comes to mind.  So, welcome to what will hopefully be the first post of many!  

I thought I’d start off simple with a guide to using the nightly builds of WebKit with WebKit .NET, which is something that a surprising number of people have asked me about.  These are builds of the ‘official’ Apple WebKit port for Windows.  Before continuing, let me reiterate the differences between this and the Cairo-based WebKit port.

The Apple port unfortunately requires certain libraries that are non-redistributable, in particular, CoreGraphics.dll and CFNetwork.dll, though luckily these come bundled with the Safari browser.  The Cairo port, developed by Brent Fulgham, uses the open source Cairo and cURL libraries instead, and is therefore fully redistributable.  It is this port that is distributed with WebKit .NET releases.  It lags a bit behind in terms of feature support, so it is sometimes necessary to fall back to the Apple port in order to try out new features.  To get started:

Download the latest build of WebKit .NET from http://webkitdotnet.sourceforge.net/downloads.php.  Extract the following files to a directory somewhere:

  • WebKitBrowser.dll
  • WebKit.Interop.dll
  • WebKitBrowser.dll.manifest
  • WebKitBrowserTest.exe (optional)

Download the latest nightly build of WebKit from http://nightly.webkit.org/.  Extract the following to the same directory as above:

  • WebKit.dll
  • JavaScriptCore.dll
  • QTMovieWin.dll
  • JavaScriptCore.resources/
  • WebKit.resources/

Download and install Safari from http://www.apple.com/safari/download/, if you haven’t already.

Navigate to %ProgramFiles%/Common Files/Apple/Apple Application Support (or %ProgramFiles(x86)%/Common Files/Apple/Apple Application Support if you’re running Win64).  Copy everything except the files listed above into your WebKit directory.  Note that depending on the version of Safari, and other Apple software you have installed, some of these files may not be required by WebKit.  I’ll publish a precise list of dependencies at some later point. 

And, you’re done.  Run WebKitBrowserTest.exe to make sure that everything is working as intended.

Tags: , , , ,

WebKit | WebKit .NET