http://bugs.webkit.org/show_bug.cgi?id=9673
authorap <ap@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Dec 2006 06:47:43 +0000 (06:47 +0000)
committerap <ap@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Dec 2006 06:47:43 +0000 (06:47 +0000)
        Add support for window.atob() and window.btoa()

        Reviewed by Darin.

JavaScriptCore:
        * JavaScriptCore.exp: Export UString::is8Bit().
        * JavaScriptCore.xcodeproj/project.pbxproj: Added StringExtras.h as
        a private header.

WebCore:
        * WebCore.xcodeproj/project.pbxproj: Added Base64.{h,cpp}
        * bindings/js/kjs_window.cpp:
        (KJS::WindowFunc::callAsFunction):
        * bindings/js/kjs_window.h:
        (KJS::Window::):
        * platform/Base64.cpp: Added.
        (base64Encode):
        (base64Decode):
        * platform/Base64.h: Added.
        * ForwardingHeaders/wtf/StringExtras.h: Added.
        * platform/DeprecatedString.cpp: Use strncasecmp from StringExtras.

LayoutTests:
        * fast/dom/Window/atob-btoa-expected.txt: Added.
        * fast/dom/Window/atob-btoa.html: Added.
        * fast/dom/Window/btoa-pnglet-expected.checksum: Added.
        * fast/dom/Window/btoa-pnglet-expected.png: Added.
        * fast/dom/Window/btoa-pnglet-expected.txt: Added.
        * fast/dom/Window/btoa-pnglet.html: Added.
        * fast/dom/Window/window-properties-expected.txt:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@18170 268f45cc-cd09-0410-ab3c-d52691b4dbfc

19 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.exp
JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
LayoutTests/ChangeLog
LayoutTests/fast/dom/Window/atob-btoa-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Window/atob-btoa.html [new file with mode: 0644]
LayoutTests/fast/dom/Window/btoa-pnglet-expected.checksum [new file with mode: 0644]
LayoutTests/fast/dom/Window/btoa-pnglet-expected.png [new file with mode: 0644]
LayoutTests/fast/dom/Window/btoa-pnglet-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Window/btoa-pnglet.html [new file with mode: 0644]
LayoutTests/fast/dom/Window/window-properties-expected.txt
WebCore/ChangeLog
WebCore/ForwardingHeaders/wtf/StringExtras.h [new file with mode: 0644]
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/bindings/js/kjs_window.cpp
WebCore/bindings/js/kjs_window.h
WebCore/platform/Base64.cpp [new file with mode: 0644]
WebCore/platform/Base64.h [new file with mode: 0644]
WebCore/platform/DeprecatedString.cpp

index d966791e3ad750d701c312d9205761b2c93606cb..770554d7c5b9be763f7f52bb9de3779b7b204244 100644 (file)
@@ -1,3 +1,14 @@
+2006-12-11  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        http://bugs.webkit.org/show_bug.cgi?id=9673
+        Add support for window.atob() and window.btoa()
+
+        * JavaScriptCore.exp: Export UString::is8Bit().
+        * JavaScriptCore.xcodeproj/project.pbxproj: Added StringExtras.h as 
+        a private header.
+
 2006-12-11  Darin Adler  <darin@apple.com>
 
         Reviewed by Brady.
index e03dc0420184a238867d0cb533fb8e22c577bfc1..7903db5dc9d182bf80fd5720c5ab54dc46bf40e1 100644 (file)
@@ -252,6 +252,7 @@ __ZNK3KJS7JSValue9toIntegerEPNS_9ExecStateE
 __ZNK3KJS7UString10UTF8StringEv
 __ZNK3KJS7UString14toStrictUInt32EPb
 __ZNK3KJS7UString5asciiEv
+__ZNK3KJS7UString6is8BitEv
 __ZNK3KJS7UString8toUInt32EPb
 __ZNK3KJS8JSObject11hasPropertyEPNS_9ExecStateERKNS_10IdentifierE
 __ZNK3KJS8JSObject12defaultValueEPNS_9ExecStateENS_6JSTypeE
index 2bed72700b8d5e5a75c328b76a5bbdc1d7f3d68e..cbed41776886b0f22e1232988672bd1ff7ac7957 100644 (file)
                BCF655590A2049710038A194 /* MathExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = BCF6553B0A2048DE0038A194 /* MathExtras.h */; settings = {ATTRIBUTES = (Private, ); }; };
                D212022A0AD4310D00ED79B6 /* DateMath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D21202280AD4310C00ED79B6 /* DateMath.cpp */; };
                D212022B0AD4310D00ED79B6 /* DateMath.h in Headers */ = {isa = PBXBuildFile; fileRef = D21202290AD4310C00ED79B6 /* DateMath.h */; };
+               E11D51760B2E798D0056C188 /* StringExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = E11D51750B2E798D0056C188 /* StringExtras.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E195679609E7CF1200B89D13 /* UnicodeIcu.h in Headers */ = {isa = PBXBuildFile; fileRef = E195678F09E7CF1200B89D13 /* UnicodeIcu.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E195679809E7CF1200B89D13 /* Unicode.h in Headers */ = {isa = PBXBuildFile; fileRef = E195679409E7CF1200B89D13 /* Unicode.h */; settings = {ATTRIBUTES = (Private, ); }; };
 /* End PBXBuildFile section */
                };
 /* End PBXBuildRule section */
 
-/* Begin PBXBuildStyle section */
-               BC4C649B0B2A359400E57352 /* Development */ = {
-                       isa = PBXBuildStyle;
-                       buildSettings = {
-                               COPY_PHASE_STRIP = NO;
-                       };
-                       name = Development;
-               };
-               BC4C649C0B2A359400E57352 /* Deployment */ = {
-                       isa = PBXBuildStyle;
-                       buildSettings = {
-                               COPY_PHASE_STRIP = YES;
-                       };
-                       name = Deployment;
-               };
-/* End PBXBuildStyle section */
-
 /* Begin PBXContainerItemProxy section */
                141211350A48796100480255 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                BCF6553B0A2048DE0038A194 /* MathExtras.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MathExtras.h; sourceTree = "<group>"; };
                D21202280AD4310C00ED79B6 /* DateMath.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DateMath.cpp; sourceTree = "<group>"; };
                D21202290AD4310C00ED79B6 /* DateMath.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DateMath.h; sourceTree = "<group>"; };
+               E11D51750B2E798D0056C188 /* StringExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringExtras.h; sourceTree = "<group>"; };
                E195678F09E7CF1200B89D13 /* UnicodeIcu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnicodeIcu.h; sourceTree = "<group>"; };
                E195679409E7CF1200B89D13 /* Unicode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Unicode.h; sourceTree = "<group>"; };
                F5BB2BC5030F772101FCFE1D /* completion.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = completion.h; sourceTree = "<group>"; tabWidth = 8; };
                                6580F795094070560082C219 /* PassRefPtr.h */,
                                65D6D87E09B5A32E0002E4D7 /* Platform.h */,
                                65C647B3093EF8D60022C380 /* RefPtr.h */,
+                               E11D51750B2E798D0056C188 /* StringExtras.h */,
                                6541BD6E08E80A17002CBEE7 /* TCPageMap.h */,
                                6541BD6F08E80A17002CBEE7 /* TCSpinLock.h */,
                                6541BD7008E80A17002CBEE7 /* TCSystemAlloc.cpp */,
                                65C7A1740A8EAACB00FA37EA /* JSWrapperObject.h in Headers */,
                                93B6A0DF0AA64DA40076DE27 /* GetPtr.h in Headers */,
                                D212022B0AD4310D00ED79B6 /* DateMath.h in Headers */,
+                               E11D51760B2E798D0056C188 /* StringExtras.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 171599a094aca4165a71e88ab9ae74d03bb2e53e..742398277060d23c16d77239beef1efed5f286ca 100644 (file)
@@ -1,3 +1,18 @@
+2006-12-11  Alexey Proskuryakov  <ap@nypop.com>
+
+        http://bugs.webkit.org/show_bug.cgi?id=9673
+        Add support for window.atob() and window.btoa()
+
+        Reviewed by Darin.
+
+        * fast/dom/Window/atob-btoa-expected.txt: Added.
+        * fast/dom/Window/atob-btoa.html: Added.
+        * fast/dom/Window/btoa-pnglet-expected.checksum: Added.
+        * fast/dom/Window/btoa-pnglet-expected.png: Added.
+        * fast/dom/Window/btoa-pnglet-expected.txt: Added.
+        * fast/dom/Window/btoa-pnglet.html: Added.
+        * fast/dom/Window/window-properties-expected.txt:
+
 2006-12-11  Darin Adler  <darin@apple.com>
 
         * fast/frames/iframe-option-crash-expected.txt: Updated results for this test.
diff --git a/LayoutTests/fast/dom/Window/atob-btoa-expected.txt b/LayoutTests/fast/dom/Window/atob-btoa-expected.txt
new file mode 100644 (file)
index 0000000..cec87fb
--- /dev/null
@@ -0,0 +1,48 @@
+PASS window.atob("YQ==") is "a"
+PASS window.atob("YWI=") is "ab"
+PASS window.atob("YWJj") is "abc"
+PASS window.atob("YWJjZA==") is "abcd"
+PASS window.atob("YWJjZGU=") is "abcde"
+PASS window.atob("YWJjZGVm") is "abcdef"
+PASS window.btoa("a") is "YQ=="
+PASS window.btoa("ab") is "YWI="
+PASS window.btoa("abc") is "YWJj"
+PASS window.btoa("abcd") is "YWJjZA=="
+PASS window.btoa("abcde") is "YWJjZGU="
+PASS window.btoa("abcdef") is "YWJjZGVm"
+PASS typeof window.btoa is "function"
+PASS window.btoa() threw exception SyntaxError: Not enough arguments.
+PASS window.btoa("") is ""
+PASS window.btoa(null) is ""
+PASS window.btoa(window) is "W29iamVjdCBXaW5kb3dd"
+PASS window.btoa("éé") is "6ek="
+PASS window.btoa("\u0080\u0081") is "gIE="
+PASS window.btoa("тест") threw exception Error: INVALID_CHARACTER_ERR: DOM Exception 5.
+PASS window.btoa is 0
+PASS typeof window.btoa is "number"
+PASS typeof window.atob is "function"
+PASS window.atob() threw exception SyntaxError: Not enough arguments.
+PASS window.atob("") is ""
+PASS window.atob(null) is ""
+PASS window.atob(" YQ==") threw exception Error: Cannot decode base64.
+PASS window.atob("YQ==\u000a") threw exception Error: Cannot decode base64.
+PASS window.atob("6ek=") is "éé"
+PASS window.atob("6ek") is "éé"
+PASS window.atob("gIE=") is "\80\81"
+PASS window.atob("тест") threw exception Error: INVALID_CHARACTER_ERR: DOM Exception 5.
+PASS window.atob("z") threw exception Error: Cannot decode base64.
+PASS window.atob("zz") is "Ï"
+PASS window.atob("zzz") is "Ï<"
+PASS window.atob("zzzz") is "Ï<ó"
+PASS window.atob("zzzzz") threw exception Error: Cannot decode base64.
+PASS window.atob("=") threw exception Error: Cannot decode base64.
+PASS window.atob("==") threw exception Error: Cannot decode base64.
+PASS window.atob("===") threw exception Error: Cannot decode base64.
+PASS window.atob("====") threw exception Error: Cannot decode base64.
+PASS window.atob("=====") threw exception Error: Cannot decode base64.
+PASS window.atob is 0
+PASS typeof window.atob is "number"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/Window/atob-btoa.html b/LayoutTests/fast/dom/Window/atob-btoa.html
new file mode 100644 (file)
index 0000000..4a7ae6a
--- /dev/null
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css">
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+shouldBe('window.atob("YQ==")', '"a"');
+shouldBe('window.atob("YWI=")', '"ab"');
+shouldBe('window.atob("YWJj")', '"abc"');
+shouldBe('window.atob("YWJjZA==")', '"abcd"');
+shouldBe('window.atob("YWJjZGU=")', '"abcde"');
+shouldBe('window.atob("YWJjZGVm")', '"abcdef"');
+shouldBe('window.btoa("a")', '"YQ=="');
+shouldBe('window.btoa("ab")', '"YWI="');
+shouldBe('window.btoa("abc")', '"YWJj"');
+shouldBe('window.btoa("abcd")', '"YWJjZA=="');
+shouldBe('window.btoa("abcde")', '"YWJjZGU="');
+shouldBe('window.btoa("abcdef")', '"YWJjZGVm"');
+
+shouldBe('typeof window.btoa', '"function"');
+shouldThrow('window.btoa()');
+shouldBe('window.btoa("")', '""');
+shouldBe('window.btoa(null)', '""');
+shouldBe('window.btoa(window)', '"W29iamVjdCBXaW5kb3dd"'); // "[object Window]"
+shouldBe('window.btoa("éé")', '"6ek="');
+shouldBe('window.btoa("\\u0080\\u0081")', '"gIE="');
+shouldThrow('window.btoa("тест")');
+window.btoa = 0;
+shouldBe('window.btoa', '0');
+shouldBe('typeof window.btoa', '"number"');
+
+shouldBe('typeof window.atob', '"function"');
+shouldThrow('window.atob()');
+shouldBe('window.atob("")', '""');
+shouldBe('window.atob(null)', '""');
+shouldThrow('window.atob(" YQ==")');
+shouldThrow('window.atob("YQ==\\u000a")');
+shouldBe('window.atob("6ek=")', '"éé"');
+shouldBe('window.atob("6ek")', '"éé"');
+shouldBe('window.atob("gIE=")', '"\u0080\u0081"');
+shouldThrow('window.atob("тест")');
+shouldThrow('window.atob("z")');
+shouldBe('window.atob("zz")', '"Ï"');
+shouldBe('window.atob("zzz")', '"Ï\u003C"');
+shouldBe('window.atob("zzzz")', '"Ï\u003Có"');
+shouldThrow('window.atob("zzzzz")');
+shouldThrow('window.atob("=")');
+shouldThrow('window.atob("==")');
+shouldThrow('window.atob("===")');
+shouldThrow('window.atob("====")');
+shouldThrow('window.atob("=====")');
+window.atob = 0;
+shouldBe('window.atob', '0');
+shouldBe('typeof window.atob', '"number"');
+
+successfullyParsed = true;
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/Window/btoa-pnglet-expected.checksum b/LayoutTests/fast/dom/Window/btoa-pnglet-expected.checksum
new file mode 100644 (file)
index 0000000..f57033f
--- /dev/null
@@ -0,0 +1 @@
+40e68cf2b802e976b46b19f94ff19a55
\ No newline at end of file
diff --git a/LayoutTests/fast/dom/Window/btoa-pnglet-expected.png b/LayoutTests/fast/dom/Window/btoa-pnglet-expected.png
new file mode 100644 (file)
index 0000000..31d1d51
Binary files /dev/null and b/LayoutTests/fast/dom/Window/btoa-pnglet-expected.png differ
diff --git a/LayoutTests/fast/dom/Window/btoa-pnglet-expected.txt b/LayoutTests/fast/dom/Window/btoa-pnglet-expected.txt
new file mode 100644 (file)
index 0000000..d9e0946
--- /dev/null
@@ -0,0 +1,10 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
+      RenderBlock {P} at (0,0) size 784x18
+        RenderText {#text} at (0,0) size 216x18
+          text run at (0,0) width 216: "Should see a light green rectangle:"
+      RenderBlock {DIV} at (0,34) size 784x100
+        RenderImage {IMG} at (0,0) size 100x100
diff --git a/LayoutTests/fast/dom/Window/btoa-pnglet.html b/LayoutTests/fast/dom/Window/btoa-pnglet.html
new file mode 100644 (file)
index 0000000..a3c3c32
--- /dev/null
@@ -0,0 +1,798 @@
+<head>
+<script>
+//
+// PNG drawing library for JavaScript.
+// Copyright (C) 1999 by Roger E Critchlow Jr,
+// Santa Fe, New Mexico, USA.
+//
+// Licensed under the Academic Free License version 2.1
+//
+// The home page for Pnglets is http://www.elf.org/pnglets,
+// a copy of the AFL may be found at http://www.opensource.org/licenses/afl-2.1.php,
+// Pnglets were inspired by and copied from gd1.3, http://www.boutell.com/gd,
+// other parts were inspired by or copied from Tcl/Tk, http://www.scriptics.com,
+// and some algorithms were taken from Foley & van Dam 2nd Edition.
+//
+// Thanks to Alex Vincent for pointing out the advantages of eliminating strict
+// javascript warnings.
+//
+
+// create a new Pnglet of specified width, height, and depth
+// width and height are specified in pixels
+// depth is really the number of palette entries
+function Pnglet(width,height,depth) {
+  this.width = width || 16;
+  this.height = height || 16;
+  this.depth = Math.min(256, depth || 16);
+
+  // pixel data and row filter identifier size
+  this.pix_size = height*(width+1);
+
+  // deflate header, pix_size, block headers, adler32 checksum
+  this.data_size = 2 + this.pix_size + 5*Math.floor((this.pix_size+0xffff-1)/0xffff) + 4;
+
+  // offsets and sizes of Png chunks
+  this.ihdr_offs = 0;                    // IHDR offset and size
+  this.ihdr_size = 4+4+13+4;
+  this.plte_offs = this.ihdr_offs+this.ihdr_size;    // PLTE offset and size
+  this.plte_size = 4+4+3*depth+4;
+  this.trns_offs = this.plte_offs+this.plte_size;    // tRNS offset and size
+  this.trns_size = 4+4+depth+4;
+  this.idat_offs = this.trns_offs+this.trns_size;    // IDAT offset and size
+  this.idat_size = 4+4+this.data_size+4;
+  this.iend_offs = this.idat_offs+this.idat_size;    // IEND offset and size
+  this.iend_size = 4+4+4;
+  this.png_size = this.iend_offs+this.iend_size;    // total PNG size
+
+  // array of one byte strings
+  this.png = new Array(this.png_size);
+
+  // functions for initializing data
+  function initialize(png, offs, str) {
+    for (var i = 1; i < arguments.length; i += 1)
+      if (typeof arguments[i].length != "undefined")
+        for (var j = 0; j < arguments[i].length; j += 1)
+          png[offs++] = arguments[i].charAt(j);
+  };
+  function byte2(w) { return String.fromCharCode((w>>8)&255, w&255); };
+  function byte4(w) { return String.fromCharCode((w>>24)&255, (w>>16)&255, (w>>8)&255, w&255); };
+  function byte2lsb(w) { return String.fromCharCode(w&255, (w>>8)&255); };
+
+  // initialize everything to zero byte
+  for (var i = 0; i < this.png_size; i += 1)
+    this.png[i] = String.fromCharCode(0);
+
+  // initialize non-zero elements
+  initialize(this.png, this.ihdr_offs, byte4(this.ihdr_size-12), 'IHDR',
+             byte4(width), byte4(height), String.fromCharCode(8, 3));
+  initialize(this.png, this.plte_offs, byte4(this.plte_size-12), 'PLTE');
+  initialize(this.png, this.trns_offs, byte4(this.trns_size-12), 'tRNS');
+  initialize(this.png, this.idat_offs, byte4(this.idat_size-12), 'IDAT');
+  initialize(this.png, this.iend_offs, byte4(this.iend_size-12), 'IEND');
+
+  // initialize deflate header
+  var header = ((8 + (7<<4)) << 8) | (3 << 6);
+  header += 31 - (header % 31);
+  initialize(this.png, this.idat_offs+8, byte2(header));
+
+  // initialize deflate block headers
+  for (i = 0; i*0xffff < this.pix_size; i += 1) {
+    var size, bits;
+    if (i + 0xffff < this.pix_size) {
+      size = 0xffff;
+      bits = String.fromCharCode(0);
+    } else {
+      size = this.pix_size - i*0xffff;
+      bits = String.fromCharCode(1);
+    }
+    initialize(this.png, this.idat_offs+8+2+i*(5+0xffff), bits, byte2lsb(size), byte2lsb(~size));
+  }
+
+  // initialize palette hash
+  this.palette = new Object();
+  this.pindex = 0;
+}
+
+// version string/number
+Pnglet.version = "19990427.0";
+
+// test if coordinates are within bounds
+Pnglet.prototype.inBounds = function(x,y) { return x >= 0 && x < this.width && y >= 0 && y < this.height; }
+
+// clip an x value to the window width
+Pnglet.prototype.clipX = function(x) { return (x < 0) ? 0 : (x >= this.width) ? this.width-1 : x ; }
+
+// clip a y value to the window height
+Pnglet.prototype.clipY = function(y) { return (y < 0) ? 0 : (y >= this.height) ? this.height-1 : y ; }
+
+// compute the index into a png for a given pixel
+Pnglet.prototype.index = function(x,y) {
+  var i = y*(this.width+1)+x+1;
+  var j = this.idat_offs+8+2+Math.floor((i/0xffff)+1)*5+i;
+  return j;
+}
+
+// make a color in a Pnglet
+Pnglet.prototype.color = function(red, green, blue, alpha) {
+  alpha = alpha >= 0 ? alpha : 255;
+  var rgba = (((((alpha<<8)+red)<<8)+green)<<8)+blue;
+  if ( typeof this.palette[rgba] == "undefined") {
+    if (this.pindex == this.depth) return String.fromCharCode(0);
+    this.palette[rgba] = String.fromCharCode(this.pindex);
+    this.png[this.plte_offs+8+this.pindex*3+0] = String.fromCharCode(red);
+    this.png[this.plte_offs+8+this.pindex*3+1] = String.fromCharCode(green);
+    this.png[this.plte_offs+8+this.pindex*3+2] = String.fromCharCode(blue);
+    this.png[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha);
+    this.pindex += 1;
+  }
+  return this.palette[rgba];
+}
+
+// return true if this is a color
+Pnglet.prototype.isColor = function(color) {
+  return typeof(color) == 'string' &&
+    color.length == 1 &&
+    color.charCodeAt(0) >= 0 &&
+    color.charCodeAt(0) < this.depth;
+}
+
+// find the red, green, blue, or alpha value of a Pnglet color
+Pnglet.prototype.red = function(color) { return this.png[this.plte_offs+8+color.charCodeAt(0)*3+0].charCodeAt(0); }
+Pnglet.prototype.green = function(color) { return this.png[this.plte_offs+8+color.charCodeAt(0)*3+1].charCodeAt(0); }
+Pnglet.prototype.blue = function(color) { return this.png[this.plte_offs+8+color.charCodeAt(0)*3+2].charCodeAt(0); }
+Pnglet.prototype.alpha = function(color) { return this.png[this.trns_offs+8+color.charCodeAt(0)].charCodeAt(0); }
+
+// draw a point or points
+Pnglet.prototype.point = function(pointColor, x0, y0) {
+  var a = arguments;
+  this.pointNXY(pointColor, (a.length-1)/2, function(i) { return a[2*i+1]; }, function(i) { return a[2*i+2]; });
+}
+
+Pnglet.prototype.pointNXY = function(pointColor, n, x, y) {
+  if ( ! this.isColor(pointColor))
+    return;
+  for (var i = 0; i < n; i += 1) {
+    var x1 = x(i), y1 = y(i);
+    if (this.inBounds(x1,y1))
+      this.png[this.index(x1,y1)] = pointColor;
+  }
+}
+
+// read a pixel 
+Pnglet.prototype.getPoint = function(x,y) { return this.inBounds(x,y) ? this.png[this.index(x,y)] : String.fromCharCode(0); }
+
+// draw a horizontal line
+Pnglet.prototype.horizontalLine = function(lineColor, x1, x2, y) {
+  if ( ! this.isColor(lineColor))
+    return;
+  x1 = this.clipX(x1);
+  x2 = this.clipX(x2);
+  var x;
+  if (x1 < x2)
+    for (x = x1; x <= x2; x += 1)
+      this.png[this.index(x,y)] = lineColor;
+  else
+    for (x = x2; x <= x1; x += 1)
+      this.png[this.index(x,y)] = lineColor;
+}
+
+// draw a vertical line
+Pnglet.prototype.verticalLine = function(lineColor, x, y1, y2) {
+  if ( ! this.isColor(lineColor))
+    return;
+  y1 = this.clipY(y1);
+  y2 = this.clipY(y2);
+  var y;
+  if (y1 < y2)
+    for (y = y1; y <= y2; y += 1)
+      this.png[this.index(x,y)] = lineColor;
+  else
+    for (y = y2; y <= y1; y += 1)
+      this.png[this.index(x,y)] = lineColor;
+}
+
+// draw a general line
+Pnglet.prototype.generalLine = function(lineColor, x1, y1, x2, y2) {
+  if ( ! this.isColor(lineColor))
+    return;
+  var dx = Math.abs(x2-x1), dy = Math.abs(y2-y1);
+  var incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag, xinc, yinc;
+  if (dy <= dx) {
+    d = 2*dy - dx;
+    incr1 = 2*dy;
+    incr2 = 2 * (dy - dx);
+    if (x1 > x2) {
+      x = x2;
+      y = y2;
+      ydirflag = -1;
+      xend = x1;
+    } else {
+      x = x1;
+      y = y1;
+      ydirflag = 1;
+      xend = x2;
+    }
+    yinc = (((y2 - y1) * ydirflag) > 0) ? 1 : -1;
+    this.point(lineColor, x, y);
+    while (x++ < xend) {
+      if (d < 0) {
+        d += incr1;
+      } else {
+        y += yinc;
+        d += incr2;
+      }
+      this.point(lineColor, x, y);
+    }
+  } else {            /* dy > dx */
+    d = 2*dx - dy;
+    incr1 = 2*dx;
+    incr2 = 2 * (dx - dy);
+    if (y1 > y2) {
+      y = y2;
+      x = x2;
+      yend = y1;
+      xdirflag = -1;
+    } else {
+      y = y1;
+      x = x1;
+      yend = y2;
+      xdirflag = 1;
+    }
+    xinc = (((x2 - x1) * xdirflag) > 0) ? 1 : -1;
+    this.point(lineColor, x, y);
+    while (y++ < yend) {
+      if (d < 0) {
+        d += incr1;
+      } else {
+        x += xinc;
+        d += incr2;
+      }
+      this.point(lineColor, x, y);
+    }
+  }
+}
+
+// draw a line
+Pnglet.prototype.line = function(lineColor, x0, y0) {
+  var a = arguments;
+  this.lineNXY(lineColor, (a.length-1)/2, function(i) { return a[2*i+1]; }, function(i) { return a[2*i+2]; });
+}
+
+Pnglet.prototype.lineNXY = function(lineColor, n, x, y) {
+  if ( ! this.isColor(lineColor))
+    return;
+  var x1 = x(0), y1 = y(0);
+  for (var i = 1; i < n; i += 1) {
+    var x2 = x(i), y2 = y(i);
+    if (x1 == x2)
+      this.verticalLine(lineColor, x1, y1, y2);
+    else if (y1 == y2)
+      this.horizontalLine(lineColor, x1, x2, y1);
+    else
+      this.generalLine(lineColor, x1, y1, x2, y2);
+    x1 = x2;
+    y1 = y2;
+  }
+}
+
+// draw a polygon
+Pnglet.prototype.polygon = function(outlineColor, fillColor, x1, y1) {
+  var a = arguments;
+  this.polygonNXY(outlineColor, fillColor, (a.length-2)/2, function(i) {return a[2*i+2];}, function(i) {return a[2*i+3];});
+}
+
+Pnglet.prototype.polygonNXY = function(outlineColor, fillColor, n, x, y) {
+  if (n <= 0)
+    return;
+  if (this.isColor(fillColor))
+    this.concaveNXY(fillColor, n, x, y);
+  if (this.isColor(outlineColor))
+    this.lineNXY(outlineColor, n+1, function(i) { return x(i%n); }, function(i) { return y(i%n); });
+}
+
+/*
+ * Concave Polygon Scan Conversion
+ * by Paul Heckbert
+ * from "Graphics Gems", Academic Press, 1990
+ */
+Pnglet.prototype.concaveNXY = function(fillColor, n, ptx, pty) {
+  function Edge(ex, edx, ei) {    /* a polygon edge */
+    this.x = ex;    /* x coordinate of edge's intersection with current scanline */
+    this.dx = edx;    /* change in x with respect to y */
+    this.i = ei;    /* edge number: edge i goes from pt[i] to pt[i+1] */
+  };
+  function cdelete(di) {    /* remove edge i from active list */
+    for (var j = 0; j < active.length; j += 1)
+      if (active[j].i == di)
+        active.splice(j, 1);
+  };
+  function cinsert(ii, iy) {    /* append edge i to end of active list */
+    var ij = ii<n-1 ? ii+1 : 0;
+    var px, py, qx, qy;
+    if (pty(ii) < pty(ij)) {
+      px = ptx(ii); py = pty(ii);
+      qx = ptx(ij); qy = pty(ij);
+    } else {
+      px = ptx(ij); py = pty(ij);
+      qx = ptx(ii); qy = pty(ii);
+    }
+    /* initialize x position at intersection of edge with scanline y */
+    var dx = (qx-px)/(qy-py);
+    active.push(new Edge(dx*(iy+.5-py)+px, dx, ii));
+  };
+
+  var ind = new Array(n);        /* list of vertex indices, sorted by pt[ind[j]].y */
+  var active = new Array(0);        /* start with an empty active list */
+
+  /* create y-sorted array of indices ind[k] into vertex list */
+  for (var k = 0; k < n; k += 1) ind[k] = k;
+  ind.sort(function(i1, i2) { return pty(i1) <= pty(i2) ? -1 : 1; });
+  k = 0;                        /* ind[k] is next vertex to process */
+  var y0 = Math.max(0, Math.ceil(pty(ind[0])+.5));            /* ymin of polygon */
+  var y1 = Math.min(this.height, Math.floor(pty(ind[n-1])-.5));    /* ymax of polygon */
+
+  for (var y = y0; y <= y1; y += 1) {        /* step through scanlines */
+    /* scanline y is at y+.5 in continuous coordinates */
+
+    /* check vertices between previous scanline and current one, if any */
+    for (; k<n && pty(ind[k]) <= y+.5; k += 1) {
+      /* to simplify, if pt.y=y+.5, pretend it's above */
+      /* invariant: y-.5 < pt[i].y <= y+.5 */
+      var i = ind[k];    
+
+      /*
+       * insert or delete edges before and after vertex i (i-1 to i,
+       * and i to i+1) from active list if they cross scanline y
+       */
+      var j = (i-1+n)%n;        /* vertex previous to i */
+      if (pty(j) <= y-.5)    {    /* old edge, remove from active list */
+        cdelete(j);
+      } else if (pty(j) > y+.5) {    /* new edge, add to active list */
+        cinsert(j, y);
+      }
+      if (i != ind[k]) {
+        alert("Your browser's implementation of JavaScript is seriously broken,\n"+
+              "as in variables are changing value of their own volition.\n"+
+              "You should upgrade to a newer version browser.");
+        return;
+      }
+      j = (i+1)%n;        /* vertex next after i */
+      if (pty(j) <= y-.5)    {    /* old edge, remove from active list */
+        cdelete(i);
+      } else if (pty(j) > y+.5) {    /* new edge, add to active list */
+        cinsert(i, y);
+      }
+    }
+
+    /* sort active edge list by active[j].x */
+    active.sort(function(u,v) { return u.x <= v.x ? -1 : 1; });
+
+    /* draw horizontal segments for scanline y */
+    for (j = 0; j < active.length; j += 2) {    /* draw horizontal segments */
+      /* span 'tween j & j+1 is inside, span tween j+1 & j+2 is outside */
+      var xl = Math.ceil(active[j].x+.5);        /* left end of span */
+      if (xl<0) xl = 0;
+      var xr = Math.floor(active[j+1].x-.5);    /* right end of span */
+      if (xr>this.width-1) xr = this.width-1;
+      if (xl<=xr)
+        this.horizontalLine(fillColor, xl, xr, y);    /* draw pixels in span */
+      active[j].x += active[j].dx;    /* increment edge coords */
+      active[j+1].x += active[j+1].dx;
+    }
+  }
+}
+
+// draw a rectangle
+Pnglet.prototype.rectangle = function(outlineColor, fillColor, x0,y0,x1,y1) {
+  if (this.isColor(fillColor))
+    for (var y = y0; y < y1; y += 1)
+      this.horizontalLine(fillColor, x0+1, x1-2, y);
+  if (this.isColor(outlineColor)) {
+    this.horizontalLine(outlineColor, x0, x1-1, y0);
+    this.horizontalLine(outlineColor, x0, x1-1, y1-1);
+    this.verticalLine(outlineColor, x0, y0, y1-1);
+    this.verticalLine(outlineColor, x1-1, y0, y1-1);
+  }
+}
+    
+// draw an arc
+Pnglet.prototype.arc = function(outlineColor, cx,cy,w,h, s,e) {
+  var p = this.midpointEllipse(cx,cy, w,h, s,e);
+  function x(i) { return p[i*2]; };
+  function y(i) { return p[i*2+1]; };
+  this.lineNXY(outlineColor, p.length/2, x, y);
+}
+
+// draw an oval
+Pnglet.prototype.oval = function(outlineColor, fillColor, cx,cy,w,h) {
+  var p = this.midpointEllipse(cx,cy, w,h, 0,359);
+  function x(i) { return p[i*2]; };
+  function y(i) { return p[i*2+1]; };
+  this.polygonNXY(outlineColor, fillColor, p.length/2, x, y);
+}
+
+// draw an arc with chord
+Pnglet.prototype.chord = function(outlineColor, fillColor, cx,cy,w,h, s,e) {
+  var p = this.midpointEllipse(cx,cy, w,h, s,e);
+  function x(i) { return p[i*2]; };
+  function y(i) { return p[i*2+1]; };
+  this.polygonNXY(outlineColor, fillColor, p.length/2, x, y);
+}
+
+// draw an arc with pieslice
+Pnglet.prototype.pieslice = function(outlineColor, fillColor, cx,cy,w,h, s,e) {
+  var p = this.midpointEllipse(cx,cy, w,h, s,e);
+  p[p.length] = cx;
+  p[p.length] = cy;
+  function x(i) { return p[i*2]; };
+  function y(i) { return p[i*2+1]; };
+  this.polygonNXY(outlineColor, fillColor, p.length/2, x, y);
+}
+
+// oval arcs
+// generate points of oval circumference
+// midpoint ellipse, Foley & van Dam, 2nd Edition, p. 90, 1990
+Pnglet.prototype.midpointEllipse = function(cx,cy, w,h, s,e) {
+  var a = Math.floor(w/2), b = Math.floor(h/2);
+  var a2 = a*a, b2 = b*b, x = 0, y = b;
+  var d1 = b2 - a2*b + a2/4;
+  cx = Math.floor(cx);
+  cy = Math.floor(cy);
+  var p = new Array();
+
+  // quadrant I, anticlockwise
+  p.push(x,-y);
+  while (a2*(y-1/2) > b2*(x+1)) {
+    if (d1 < 0) {
+      d1 += b2*(2*x+3);
+    } else {
+      d1 += b2*(2*x+3) + a2*(-2*y + 2);
+      y -= 1;
+    }
+    x += 1;
+    p.unshift(x,-y);
+  }
+  var d2 = b2*(x+1/2)*(x+1/2) + a2*(y-1)*(y-1) - a2*b2;
+  while (y > 0) {
+    if (d2 < 0) {
+      d2 += b2*(2*x+2) + a2*(-2*y+3);
+      x += 1;
+    } else {
+      d2 += a2*(-2*y+3);
+    }
+    y -= 1;
+    p.unshift(x,-y);
+  }
+  // quadrant II, anticlockwise
+  var n4 = p.length;
+  for (var i = n4-4; i >= 0; i -= 2)
+    p.push(-p[i], p[i+1]);
+  // quadrants III and IV, anticlockwise
+  var n2 = p.length;
+  for (i = n2-4; i > 0; i -= 2)
+    p.push(p[i], -p[i+1]);
+
+  // compute start and end indexes from start and extent
+  e %= 360;
+  if (e < 0) {
+    s += e;
+    e = -e;
+  }
+  s %= 360;
+  if (s < 0)
+    s += 360;
+  var is = Math.floor(s/359 * p.length/2);
+  var ie = Math.floor(e/359 * p.length/2)+1;
+  p = p.slice(is*2).concat(p.slice(0, is*2)).slice(0, ie*2);
+
+  // displace to center
+  for (i = 0; i < p.length; i += 2) {
+    p[i] += cx;
+    p[i+1] += cy;
+  }
+  return p;
+}
+
+// fill a region 
+// from gd1.3 with modifications
+Pnglet.prototype.fill = function(outlineColor,fillColor,x,y) {
+  if (outlineColor) {            // fill to outline color
+    /* Seek left */
+    var leftLimit = -1;
+    for (var i = x; i >= 0 && this.getPoint(i, y) != outlineColor; i -= 1)
+      leftLimit = i;
+    
+    if (leftLimit == -1)
+      return;
+
+    /* Seek right */
+    var rightLimit = x;
+    for (i = (x+1); i < this.width && this.getPoint(i, y) != outlineColor; i += 1)
+      rightLimit = i;
+
+    /* fill extent found */
+    this.horizontalLine(fillColor, leftLimit, rightLimit, y);
+
+    /* Seek above and below */
+    for (var dy = -1; dy <= 1; dy += 2) {
+      if (this.inBounds(x,y+dy)) {
+        var lastBorder = 1;
+        for (i = leftLimit; i <= rightLimit; i++) {
+          var c = this.getPoint(i, y+dy);
+          if (lastBorder) {
+            if ((c != outlineColor) && (c != fillColor)) {    
+              this.fill(outlineColor, fillColor, i, y+dy);
+              lastBorder = 0;
+            }
+          } else if ((c == outlineColor) || (c == fillColor)) {
+            lastBorder = 1;
+          }
+        }
+      }
+    }
+    
+  } else {            // flood fill color at x, y 
+    /* Test for completion */
+    var oldColor = this.getPoint(x, y);
+    if (oldColor == fillColor)
+      return;
+
+    /* Seek left */
+    leftLimit = (-1);
+    for (i = x; i >= 0 && this.getPoint(i, y) == oldColor; i--)
+      leftLimit = i;
+
+    if (leftLimit == -1)
+      return;
+
+    /* Seek right */
+    rightLimit = x;
+    for (i = (x+1); i < this.width && this.getPoint(i, y) == oldColor; i++)
+      rightLimit = i;
+
+    /* Fill extent found */
+    this.horizontalLine(fillColor, leftLimit, rightLimit, y);
+
+    /* Seek above and below */
+    for (dy = -1; dy <= 1; dy += 2) {
+      if (this.inBounds(x,y+dy)) {
+        lastBorder = 1;
+        for (i = leftLimit; i <= rightLimit; i++) {
+          c = this.getPoint(i, y+dy);
+          if (lastBorder) {
+            if (c == oldColor) {    
+              this.fill(null, fillColor, i, y+dy);
+              lastBorder = 0;
+            }
+          } else if (c != oldColor) {
+            lastBorder = 1;
+          }
+        }
+      }
+    }
+  }
+}
+
+// smoothed points
+Pnglet.prototype.smoothPoint = function(smoothSteps, pointColor, x0, y0) {
+  var a = arguments, self = this, n = (a.length-2)/2;
+  this.smooth(smoothSteps,
+              function(n, x, y) { self.pointNXY(pointColor, n, x, y); },
+              n,
+              function(i) { return a[2*i+2]; },
+              function(i) { return a[2*i+3]; });
+}
+
+// smoothed polyline
+Pnglet.prototype.smoothLine = function(smoothSteps, lineColor, x0, y0) {
+  var a = arguments, self = this, n = (a.length-2)/2;
+  this.smooth(smoothSteps,
+              function(n, x, y) { self.lineNXY(lineColor, n, x, y); },
+              n,
+              function(i) { return a[2*i+2]; },
+              function(i) { return a[2*i+3]; });
+}
+
+// smoothed polygon
+Pnglet.prototype.smoothPolygon = function(smoothSteps, outlineColor, fillColor, x0, y0) {
+  var a = arguments, self = this, n = (a.length-3)/2 + 1;
+  this.smooth(smoothSteps,
+              function(n, x, y) { self.polygonNXY(outlineColor, fillColor, n, x, y); },
+              n,
+              function(i) { return a[2*(i%(n-1))+3]; },
+              function(i) { return a[2*(i%(n-1))+4]; });
+}
+    
+// generate smoothSteps points for the line segment connecting
+// each consecutive pair of points in x(i), y(i).
+// adapted from the source for tk8.1b3, http://www.scriptics.com
+Pnglet.prototype.smooth = function(smoothSteps, fNXY, n, x, y) {
+  var control = new Array(8);
+  var outputPoints = 0;
+  var dblPoints = new Array();
+
+  // compute numSteps of smoothed points
+  // according to the basis in control[]
+  // placing points into coordPtr[coordOff]
+  function smoothPoints(control, numSteps, coordPtr, coordOff) {
+    for (var i = 1; i <= numSteps; i++, coordOff += 2) {
+      var t = i/numSteps, t2 = t*t, t3 = t2*t,
+        u = 1.0 - t, u2 = u*u, u3 = u2*u;
+      coordPtr[coordOff+0] = control[0]*u3 + 3.0 * (control[2]*t*u2 + control[4]*t2*u) + control[6]*t3;
+      coordPtr[coordOff+1] = control[1]*u3 + 3.0 * (control[3]*t*u2 + control[5]*t2*u) + control[7]*t3;
+    }
+  };
+
+  /*
+   * If the curve is a closed one then generate a special spline
+   * that spans the last points and the first ones.  Otherwise
+   * just put the first point into the output.
+   */
+
+  var closed = (x(0) == x(n-1)) && (y(0) == y(n-1));
+  if (closed) {
+    control[0] = 0.5*x(n-2) + 0.5*x(0);
+    control[1] = 0.5*y(n-2) + 0.5*y(0);
+    control[2] = 0.167*x(n-2) + 0.833*x(0);
+    control[3] = 0.167*y(n-2) + 0.833*y(0);
+    control[4] = 0.833*x(0) + 0.167*x(1);
+    control[5] = 0.833*y(0) + 0.167*y(1);
+    control[6] = 0.5*x(0) + 0.5*x(1);
+    control[7] = 0.5*y(0) + 0.5*y(1);
+    dblPoints[2*outputPoints+0] = control[0];
+    dblPoints[2*outputPoints+1] = control[1];
+    outputPoints += 1;
+    smoothPoints(control, smoothSteps, dblPoints, 2*outputPoints);
+    outputPoints += smoothSteps;
+  } else {
+    dblPoints[2*outputPoints+0] = x(0);
+    dblPoints[2*outputPoints+1] = y(0);
+    outputPoints += 1;
+  }
+
+  for (var i = 2; i < n; i += 1) {
+    var j = i - 2;
+    /*
+     * Set up the first two control points.  This is done
+     * differently for the first spline of an open curve
+     * than for other cases.
+     */
+    if ((i == 2) && !closed) {
+      control[0] = x(j);
+      control[1] = y(j);
+      control[2] = 0.333*x(j) + 0.667*x(j+1);
+      control[3] = 0.333*y(j) + 0.667*y(j+1);
+    } else {
+      control[0] = 0.5*x(j) + 0.5*x(j+1);
+      control[1] = 0.5*y(j) + 0.5*y(j+1);
+      control[2] = 0.167*x(j) + 0.833*x(j+1);
+      control[3] = 0.167*y(j) + 0.833*y(j+1);
+    }
+
+    /*
+     * Set up the last two control points.  This is done
+     * differently for the last spline of an open curve
+     * than for other cases.
+     */
+
+    if ((i == (n-1)) && !closed) {
+      control[4] = .667*x(j+1) + .333*x(j+2);
+      control[5] = .667*y(j+1) + .333*y(j+2);
+      control[6] = x(j+2);
+      control[7] = y(j+2);
+    } else {
+      control[4] = .833*x(j+1) + .167*x(j+2);
+      control[5] = .833*y(j+1) + .167*y(j+2);
+      control[6] = 0.5*x(j+1) + 0.5*x(j+2);
+      control[7] = 0.5*y(j+1) + 0.5*y(j+2);
+    }
+
+    /*
+     * If the first two points coincide, or if the last
+     * two points coincide, then generate a single
+     * straight-line segment by outputting the last control
+     * point.
+     */
+
+    if (((x(j) == x(j+1)) && (y(j) == y(j+1)))
+        || ((x(j+1) == x(j+2)) && (y(j+1) == y(j+2)))) {
+      dblPoints[2*outputPoints+0] = control[6];
+      dblPoints[2*outputPoints+1] = control[7];
+      outputPoints += 1;
+      continue;
+    }
+
+    /*
+     * Generate a Bezier spline using the control points.
+     */
+    smoothPoints(control, smoothSteps, dblPoints, 2*outputPoints);
+    outputPoints += smoothSteps;
+  }
+
+  // draw the points
+  // anonymous functions don't work here
+  // they result in "undefined" point values
+  function myx(i) { return Math.round(dblPoints[2*i]); }
+    function myy(i) { return Math.round(dblPoints[2*i+1]); }
+    fNXY(outputPoints, myx, myy);
+}
+
+// output a PNG string
+Pnglet.prototype.output = function() {
+  // output translations
+  function initialize(png, offs, str) {
+    for (var i = 1; i < arguments.length; i += 1)
+      if (typeof arguments[i].length != "undefined")
+        for (var j = 0; j < arguments[i].length; j += 1)
+          png[offs++] = arguments[i].charAt(j);
+  }
+    function byte4(w) { return String.fromCharCode((w>>24)&255, (w>>16)&255, (w>>8)&255, w&255); }
+
+    // compute adler32 of output pixels + row filter bytes
+    var BASE = 65521; /* largest prime smaller than 65536 */
+  var NMAX = 5552;  /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+  var s1 = 1;
+  var s2 = 0;
+  var n = NMAX;
+  for (var y = 0; y < this.height; y += 1)
+    for (var x = -1; x < this.width; x += 1) {
+      s1 += this.png[this.index(x,y)].charCodeAt(0);
+      s2 += s1;
+      if ((n -= 1) == 0) {
+        s1 %= BASE;
+        s2 %= BASE;
+        n = NMAX;
+      }
+    }
+  s1 %= BASE;
+  s2 %= BASE;
+  initialize(this.png, this.idat_offs+this.idat_size-8, byte4((s2 << 16) | s1));
+
+  // compute crc32 of the PNG chunks
+  function crc32(png, offs, size) {
+    var crc = -1;        // initialize crc
+    for (var i = 4; i < size-4; i += 1)
+      crc = Pnglet.crc32_table[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff);
+    initialize(png, offs+size-4, byte4(crc ^ -1));
+  }
+
+    crc32(this.png, this.ihdr_offs, this.ihdr_size);
+  crc32(this.png, this.plte_offs, this.plte_size);
+  crc32(this.png, this.trns_offs, this.trns_size);
+  crc32(this.png, this.idat_offs, this.idat_size);
+  crc32(this.png, this.iend_offs, this.iend_size);
+
+  // convert PNG to string
+  return "\211PNG\r\n\032\n"+this.png.join('');
+}
+
+/* Table of CRCs of all 8-bit messages. */
+Pnglet.crc32_table = new Array(256);
+for (var n = 0; n < 256; n++) {
+  var c = n;
+  for (var k = 0; k < 8; k++) {
+    if (c & 1)
+      c = -306674912 ^ ((c >> 1) & 0x7fffffff);
+    else
+      c = (c >> 1) & 0x7fffffff;
+  }
+  Pnglet.crc32_table[n] = c;
+}
+</script>
+</head>
+
+<body>
+<p>Should see a light green rectangle:</p>
+<div id=result></div>
+<script>
+    pngdata = new Pnglet(1,1,1);
+    pngdata.point(pngdata.color(0,255,0,127),1,1);
+
+    png = document.createElement("img");
+    png.style.height = "100px";
+    png.style.width = "100px";
+    
+    png.src = "data:image/png;base64," + btoa(pngdata.output());
+    document.getElementById('result').appendChild(png);
+</script>
+</body>
\ No newline at end of file
index 1f4ce3aa280c2fa1d99d07b61e0d8cfff9960205..6825899c0e62e72629fa65f784af12ed567a6d56 100644 (file)
@@ -535,28 +535,13 @@ window.XSLTProcessor.prototype.transformToFragment [function]
 window.addEventListener [function]
 window.alert [function]
 window.appleScriptController [object RuntimeObject]
+window.atob [function]
 window.blur [function]
+window.btoa [function]
 window.captureEvents [function]
 window.clearInterval [function]
 window.clearTimeout [function]
-window.clientInformation [object Navigator]
-window.clientInformation.appCodeName [string]
-window.clientInformation.appName [string]
-window.clientInformation.appVersion [string]
-window.clientInformation.cookieEnabled [boolean]
-window.clientInformation.javaEnabled [function]
-window.clientInformation.language [string]
-window.clientInformation.mimeTypes [object MimeTypeArray]
-window.clientInformation.mimeTypes.length [number]
-window.clientInformation.platform [string]
-window.clientInformation.plugins [object PluginArray]
-window.clientInformation.plugins.length [number]
-window.clientInformation.plugins.refresh [function]
-window.clientInformation.product [string]
-window.clientInformation.productSub [string]
-window.clientInformation.userAgent [string]
-window.clientInformation.vendor [string]
-window.clientInformation.vendorSub [string]
+window.clientInformation [printed above as window.navigator]
 window.close [function]
 window.closed [boolean]
 window.confirm [function]
@@ -1617,7 +1602,24 @@ window.moveBy [function]
 window.moveTo [function]
 window.name [string]
 window.navigationController [object RuntimeObject]
-window.navigator [printed above as window.clientInformation]
+window.navigator [object Navigator]
+window.navigator.appCodeName [string]
+window.navigator.appName [string]
+window.navigator.appVersion [string]
+window.navigator.cookieEnabled [boolean]
+window.navigator.javaEnabled [function]
+window.navigator.language [string]
+window.navigator.mimeTypes [object MimeTypeArray]
+window.navigator.mimeTypes.length [number]
+window.navigator.platform [string]
+window.navigator.plugins [object PluginArray]
+window.navigator.plugins.length [number]
+window.navigator.plugins.refresh [function]
+window.navigator.product [string]
+window.navigator.productSub [string]
+window.navigator.userAgent [string]
+window.navigator.vendor [string]
+window.navigator.vendorSub [string]
 window.objCPlugin [object RuntimeObject]
 window.objCPluginFunction [function]
 window.offscreenBuffering [boolean]
index b671fb6d94c7a846a1e40c49b6e3ea90f49654d3..301509fb26754b991439181a9f403a21151835f1 100644 (file)
@@ -1,3 +1,22 @@
+2006-12-11  Alexey Proskuryakov  <ap@nypop.com>
+
+        Reviewed by Darin.
+
+        http://bugs.webkit.org/show_bug.cgi?id=9673
+        Add support for window.atob() and window.btoa()
+
+        * WebCore.xcodeproj/project.pbxproj: Added Base64.{h,cpp}
+        * bindings/js/kjs_window.cpp:
+        (KJS::WindowFunc::callAsFunction):
+        * bindings/js/kjs_window.h:
+        (KJS::Window::):
+        * platform/Base64.cpp: Added.
+        (base64Encode):
+        (base64Decode):
+        * platform/Base64.h: Added.
+        * ForwardingHeaders/wtf/StringExtras.h: Added.
+        * platform/DeprecatedString.cpp: Use strncasecmp from StringExtras.
+
 2006-12-11  Darin Adler  <darin@apple.com>
 
         Reviewed by Brady.
diff --git a/WebCore/ForwardingHeaders/wtf/StringExtras.h b/WebCore/ForwardingHeaders/wtf/StringExtras.h
new file mode 100644 (file)
index 0000000..e2af18c
--- /dev/null
@@ -0,0 +1 @@
+#import <JavaScriptCore/StringExtras.h>
index ea6edb7d8d2c110e4246d056a362b4dca2e1d485..effce27a8e1818da390de01e8f85fc0be3b5865a 100644 (file)
                DB23C2CC0A508D29002489EB /* IndentOutdentCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = DB23C2CA0A508D29002489EB /* IndentOutdentCommand.h */; };
                DD763BB20992C2C900740B8E /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD763BB10992C2C900740B8E /* libxml2.dylib */; };
                DD7CDF250A23CF9800069928 /* CSSUnknownRule.h in Headers */ = {isa = PBXBuildFile; fileRef = A80E6CCE0A1989CA007FB8C5 /* CSSUnknownRule.h */; };
+               E11D51930B2E7A5F0056C188 /* Base64.h in Headers */ = {isa = PBXBuildFile; fileRef = E11D51910B2E7A5F0056C188 /* Base64.h */; };
+               E11D51940B2E7A5F0056C188 /* Base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11D51920B2E7A5F0056C188 /* Base64.cpp */; };
                E14842DE0A674934007E4D39 /* TextCodecICU.h in Headers */ = {isa = PBXBuildFile; fileRef = E14842DD0A674934007E4D39 /* TextCodecICU.h */; };
                E14842FF0A674A31007E4D39 /* TextCodecICU.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E14842FE0A674A31007E4D39 /* TextCodecICU.cpp */; };
                E148432F0A674FC2007E4D39 /* TextCodecMac.h in Headers */ = {isa = PBXBuildFile; fileRef = E148432E0A674FC2007E4D39 /* TextCodecMac.h */; };
                DB23C2C90A508D29002489EB /* IndentOutdentCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = IndentOutdentCommand.cpp; sourceTree = "<group>"; };
                DB23C2CA0A508D29002489EB /* IndentOutdentCommand.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = IndentOutdentCommand.h; sourceTree = "<group>"; };
                DD763BB10992C2C900740B8E /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = /usr/lib/libxml2.dylib; sourceTree = "<absolute>"; };
+               E11D51910B2E7A5F0056C188 /* Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Base64.h; sourceTree = "<group>"; };
+               E11D51920B2E7A5F0056C188 /* Base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Base64.cpp; sourceTree = "<group>"; };
                E14842DD0A674934007E4D39 /* TextCodecICU.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextCodecICU.h; sourceTree = "<group>"; };
                E14842FE0A674A31007E4D39 /* TextCodecICU.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextCodecICU.cpp; sourceTree = "<group>"; };
                E148432E0A674FC2007E4D39 /* TextCodecMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = TextCodecMac.h; path = mac/TextCodecMac.h; sourceTree = "<group>"; };
                                93CD4FD70995F9EA007ECC97 /* AtomicString.cpp */,
                                93CD4FD80995F9EA007ECC97 /* AtomicString.h */,
                                93CD4FD90995F9EA007ECC97 /* AtomicStringImpl.h */,
+                               E11D51910B2E7A5F0056C188 /* Base64.h */,
+                               E11D51920B2E7A5F0056C188 /* Base64.cpp */,
                                65F5382009B2B55700F3DC4A /* character-sets.txt */,
                                9326DC0A09DAD5BE00AFC847 /* CharsetData.h */,
                                BCC8CFCA0986CD2400140BF2 /* ColorData.gperf */,
                                BC18C5D00B2A886F0018461D /* TextBreakIterator.h in Headers */,
                                AA4C3A770B2B1679002334A2 /* StyleElement.h in Headers */,
                                93E241FF0B2B4E4000C732A1 /* HTMLFrameOwnerElement.h in Headers */,
+                               E11D51930B2E7A5F0056C188 /* Base64.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                BC18C5D10B2A886F0018461D /* TextBreakIteratorICU.cpp in Sources */,
                                AA4C3A760B2B1679002334A2 /* StyleElement.cpp in Sources */,
                                93E2425F0B2B509500C732A1 /* HTMLFrameOwnerElement.cpp in Sources */,
+                               E11D51940B2E7A5F0056C188 /* Base64.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 8556f7b660822d18cb23c08e3724ea383ae6346a..238267e9ff0aa1de9c37fc9f43c473edf40d9d78 100644 (file)
@@ -4,6 +4,7 @@
  *  Copyright (C) 2000 Harri Porten (porten@kde.org)
  *  Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
  *  Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
+ *  Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
 #include "config.h"
 #include "kjs_window.h"
 
+#include "Base64.h"
 #include "Chrome.h"
 #include "CString.h"
 #include "DOMWindow.h"
 #include "Element.h"
 #include "EventListener.h"
 #include "EventNames.h"
+#include "ExceptionCode.h"
 #include "FloatRect.h"
 #include "Frame.h"
 #include "FrameLoadRequest.h"
@@ -220,6 +223,8 @@ const ClassInfo Window::info = { "Window", 0, &WindowTable, 0 };
 
 /*
 @begin WindowTable 103
+  atob          Window::AToB            DontDelete|Function 1
+  btoa          Window::BToA            DontDelete|Function 1
   closed        Window::Closed          DontDelete|ReadOnly
   crypto        Window::Crypto          DontDelete|ReadOnly
   defaultStatus Window::DefaultStatus   DontDelete
@@ -1535,6 +1540,30 @@ JSValue *WindowFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const Li
     frame->runJavaScriptAlert(str);
     exec->dynamicInterpreter()->resumeTimeoutCheck();
     return jsUndefined();
+  case Window::AToB:
+  case Window::BToA: {
+    if (args.size() < 1)
+        return throwError(exec, SyntaxError, "Not enough arguments");
+    if (v->isNull())
+        return jsString();
+    if (!s.is8Bit()) {
+        setDOMException(exec, INVALID_CHARACTER_ERR);
+        return jsUndefined();
+    }
+    
+    Vector<char> in(s.size());
+    for (int i = 0; i < s.size(); ++i)
+        in[i] = static_cast<char>(s.data()[i].unicode());
+    Vector<char> out;
+
+    if (id == Window::AToB) {
+        if (!base64Decode(in, out))
+            return throwError(exec, GeneralError, "Cannot decode base64");
+    } else
+        base64Encode(in, out);
+    
+    return jsString(String(out.data(), out.size()));
+  }
   case Window::Confirm: {
     if (frame && frame->document())
       frame->document()->updateRendering();
index 4f0c802a9ccffb10e19f15e556386c90e9506445..c3201843718047cd3b081c8e4fd59efbcfab6bb5 100644 (file)
@@ -155,7 +155,7 @@ namespace KJS {
     UnprotectedListenersMap jsUnprotectedHTMLEventListeners;
     virtual const ClassInfo* classInfo() const { return &info; }
     static const ClassInfo info;
-    enum { Closed, Crypto, DefaultStatus, Status, DOMException, Frames, History_, Event_, InnerHeight,
+    enum { AToB, BToA, Closed, Crypto, DefaultStatus, Status, DOMException, Frames, History_, Event_, InnerHeight,
            InnerWidth, Length, Location_, Locationbar, Name, Navigator_, ClientInformation,
            Menubar, OffscreenBuffering, Opener, OuterHeight, OuterWidth, PageXOffset, PageYOffset,
            Parent, Personalbar, ScreenX, ScreenY, Scrollbars, Scroll, ScrollBy,
diff --git a/WebCore/platform/Base64.cpp b/WebCore/platform/Base64.cpp
new file mode 100644 (file)
index 0000000..6378f9a
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+   Copyright (C) 2000-2001 Dawit Alemayehu <adawit@kde.org>
+   Copyright (C) 2006 Alexey Proskuryakov <ap@webkit.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License (LGPL)
+   version 2 as published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+   This code is based on the java implementation in HTTPClient
+   package by Ronald Tschalär Copyright (C) 1996-1999.
+*/
+
+#include "config.h"
+#include "Base64.h"
+
+#include <wtf/platform.h>
+#include <wtf/StringExtras.h>
+
+static const char base64EncMap[64] =
+{
+  0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+  0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
+  0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+  0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+  0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
+  0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+  0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33,
+  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F
+};
+
+static const char base64DecMap[128] =
+{
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3F,
+  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
+  0x3C, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+  0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
+  0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+  0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
+  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+  0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+  0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+void base64Encode(const Vector<char>& in, Vector<char>& out, bool insertLFs)
+{
+    // clear out the output buffer
+    out.clear();
+    if (in.isEmpty())
+        return;
+
+    unsigned sidx = 0;
+    unsigned didx = 0;
+    const char* data = in.data();
+    const unsigned len = in.size();
+
+    unsigned out_len = ((len + 2) / 3) * 4;
+
+    // Deal with the 76 characters or less per
+    // line limit specified in RFC 2045 on a
+    // pre request basis.
+    insertLFs = (insertLFs && out_len > 76);
+    if (insertLFs)
+        out_len += ((out_len - 1) / 76);
+
+    int count = 0;
+    out.resize(out_len);
+
+    // 3-byte to 4-byte conversion + 0-63 to ascii printable conversion
+    if (len > 1) {
+        while (sidx < len - 2) {
+            if (insertLFs) {
+                if (count && (count % 76) == 0)
+                    out[didx++] = '\n';
+                count += 4;
+            }
+            out[didx++] = base64EncMap[(data[sidx] >> 2) & 077];
+            out[didx++] = base64EncMap[(data[sidx + 1] >> 4) & 017 | (data[sidx] << 4) & 077];
+            out[didx++] = base64EncMap[(data[sidx + 2] >> 6) & 003 | (data[sidx + 1] << 2) & 077];
+            out[didx++] = base64EncMap[data[sidx + 2] & 077];
+            sidx += 3;
+        }
+    }
+
+    if (sidx < len) {
+        if (insertLFs && (count > 0) && (count % 76) == 0)
+           out[didx++] = '\n';
+
+        out[didx++] = base64EncMap[(data[sidx] >> 2) & 077];
+        if (sidx < len - 1) {
+            out[didx++] = base64EncMap[(data[sidx + 1] >> 4) & 017 | (data[sidx] << 4) & 077];
+            out[didx++] = base64EncMap[(data[sidx + 1] << 2) & 077];
+        } else
+            out[didx++] = base64EncMap[(data[sidx] << 4) & 077];
+    }
+
+    // Add padding
+    while (didx < out.size()) {
+        out[didx] = '=';
+        didx++;
+    }
+}
+
+bool base64Decode(const Vector<char>& in, Vector<char>& out)
+{
+    out.clear();
+    if (in.isEmpty())
+        return true;
+
+    unsigned len = in.size();
+    const char* data = in.data();
+
+    while (len && data[len-1] == '=')
+        --len;
+
+    out.resize(len);
+    for (unsigned idx = 0; idx < len; idx++) {
+        unsigned char ch = data[idx];
+        if ((ch > 47 && ch < 58) || (ch > 64 && ch < 91) || (ch > 96 && ch < 123) || ch == '+' || ch == '/' || ch == '=')
+            out[idx] = base64DecMap[ch];
+        else
+            return false;
+    }
+
+    // 4-byte to 3-byte conversion
+    unsigned outLen = len - ((len + 3) / 4);
+    if (!outLen || ((outLen + 2) / 3) * 4 < len)
+        return false;
+
+    unsigned sidx = 0;
+    unsigned didx = 0;
+    if (outLen > 1) {
+        while (didx < outLen - 2) {
+            out[didx] = (((out[sidx] << 2) & 255) | ((out[sidx + 1] >> 4) & 003));
+            out[didx + 1] = (((out[sidx + 1] << 4) & 255) | ((out[sidx + 2] >> 2) & 017));
+            out[didx + 2] = (((out[sidx + 2] << 6) & 255) | (out[sidx + 3] & 077));
+            sidx += 4;
+            didx += 3;
+        }
+    }
+
+    if (didx < outLen)
+        out[didx] = (((out[sidx] << 2) & 255) | ((out[sidx + 1] >> 4) & 003));
+
+    if (++didx < outLen)
+        out[didx] = (((out[sidx + 1] << 4) & 255) | ((out[sidx + 2] >> 2) & 017));
+
+    if (outLen < out.size())
+        out.resize(outLen);
+
+    return true;
+}
diff --git a/WebCore/platform/Base64.h b/WebCore/platform/Base64.h
new file mode 100644 (file)
index 0000000..54e6469
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef Base64_h
+#define Base64_h
+
+#include <wtf/Vector.h>
+
+void base64Encode(const Vector<char>&, Vector<char>&, bool insertLFs = false);
+
+// this decoder is not general purpose - it returns an error if it encounters a linefeed, as needed for window.atob
+bool base64Decode(const Vector<char>&, Vector<char>&);
+
+#endif // Base64_h
index 8a472c01dfc834fe0b7506d55a02167e4749fcb8..0d488d1b426d39e76174f73bc831b6632dd0d780 100644 (file)
@@ -36,6 +36,7 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <wtf/Platform.h>
+#include <wtf/StringExtras.h>
 
 #if PLATFORM(WIN_OS)
 #include <windows.h>
@@ -828,13 +829,6 @@ bool DeprecatedString::startsWith(const char *prefix) const
     }
 }
 
-#if PLATFORM(WIN_OS)
-inline int strncasecmp(const char *first, const char *second, size_t maxLength)
-{
-    return _strnicmp(first, second, maxLength);
-}
-#endif
-
 bool DeprecatedString::startsWith(const char *prefix, bool caseSensitive) const
 {
     if (caseSensitive) {