Title: [101059] trunk/Source/WebCore
Revision
101059
Author
[email protected]
Date
2011-11-23 02:10:29 -0800 (Wed, 23 Nov 2011)

Log Message

[chromium] HDC leak in Uniscribe Helper
https://bugs.webkit.org/show_bug.cgi?id=68598

Patch by Marc-Andre Decoste <[email protected]> on 2011-11-23
Reviewed by Darin Fisher.

For some reason the Script functions on Windows sometimes return
E_PENDING even with a non-NULL DC, so we must handle that case.
Also, we should not use the screen DC to select font since this
refreshes the whole desktop, so I added a cached compatible DC.

Note that this doesn't reproduce with WebKit alone, it only reproduces
within Chrome, so we can't write a WebKit test for it. A chromium
browser test will be added once this change gets rolled in the
chromium DEPS file.

* platform/graphics/chromium/UniscribeHelper.cpp:
(WebCore::UniscribeHelper::shape):
(WebCore::UniscribeHelper::EnsureCachedDCCreated):
(WebCore::UniscribeHelper::fillShapes):
* platform/graphics/chromium/UniscribeHelper.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (101058 => 101059)


--- trunk/Source/WebCore/ChangeLog	2011-11-23 10:02:27 UTC (rev 101058)
+++ trunk/Source/WebCore/ChangeLog	2011-11-23 10:10:29 UTC (rev 101059)
@@ -1,3 +1,26 @@
+2011-11-23  Marc-Andre Decoste  <[email protected]>
+
+        [chromium] HDC leak in Uniscribe Helper
+        https://bugs.webkit.org/show_bug.cgi?id=68598
+
+        Reviewed by Darin Fisher.
+
+        For some reason the Script functions on Windows sometimes return
+        E_PENDING even with a non-NULL DC, so we must handle that case.
+        Also, we should not use the screen DC to select font since this
+        refreshes the whole desktop, so I added a cached compatible DC.
+
+        Note that this doesn't reproduce with WebKit alone, it only reproduces
+        within Chrome, so we can't write a WebKit test for it. A chromium
+        browser test will be added once this change gets rolled in the
+        chromium DEPS file.
+
+        * platform/graphics/chromium/UniscribeHelper.cpp:
+        (WebCore::UniscribeHelper::shape):
+        (WebCore::UniscribeHelper::EnsureCachedDCCreated):
+        (WebCore::UniscribeHelper::fillShapes):
+        * platform/graphics/chromium/UniscribeHelper.h:
+
 2011-11-23  Anna Cavender  <[email protected]>
 
         Move readyState from TextTrack to HTMLTrackElement

Modified: trunk/Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp (101058 => 101059)


--- trunk/Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp	2011-11-23 10:02:27 UTC (rev 101058)
+++ trunk/Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp	2011-11-23 10:10:29 UTC (rev 101059)
@@ -106,6 +106,10 @@
         *style = getStyleFromLogfont(logfont);
 }
 
+// This memory DC will NOT be released but it's OK
+// since we want to keep it for the whole life span of the process.
+HDC UniscribeHelper::m_cachedDC = 0;
+
 static bool canUseGlyphIndex(const SCRIPT_ITEM& run)
 {
     // On early version of Uniscribe, ScriptShape() sets run.a.fNoGlyphIndex
@@ -622,8 +626,6 @@
     Vector<SCRIPT_GLYPHPROP, cUniscribeHelperStackChars> glyphProps;
     int ascent = m_ascent;
     WORD spaceGlyph = m_spaceGlyph;
-    HDC tempDC = 0;
-    HGDIOBJ oldFont = 0;
     HRESULT hr;
     // When used to fill up glyph pages for simple scripts in non-BMP,
     // we don't want any font fallback in this class. The simple script
@@ -665,11 +667,16 @@
         ZeroMemory(&shaping.m_glyphs[0],
                    sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size());
 #endif
+        // If our DC is already created, select the font in it so we can use it now.
+        // Otherwise, we'll create it as needed afterward...
+        if (m_cachedDC)
+            SelectObject(m_cachedDC, hfont);
+
         // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
         // here. Is that what we want? It will display control characters.
         if (gScriptShapeOpenTypeFunc) {
             TEXTRANGE_PROPERTIES* rangeProps = m_featureRecords.size() ? &m_rangeProperties : 0;
-            hr = gScriptShapeOpenTypeFunc(tempDC, scriptCache, &run.a,
+            hr = gScriptShapeOpenTypeFunc(m_cachedDC, scriptCache, &run.a,
                                           scriptTag, 0, &itemLength,
                                           &rangeProps, rangeProps ? 1 : 0,
                                           input, itemLength, numGlyphs,
@@ -677,30 +684,25 @@
                                           &shaping.m_glyphs[0], &glyphProps[0],
                                           &generatedGlyphs);
         } else {
-            hr = ScriptShape(tempDC, scriptCache, input, itemLength,
+            hr = ScriptShape(m_cachedDC, scriptCache, input, itemLength,
                              numGlyphs, &run.a,
                              &shaping.m_glyphs[0], &shaping.m_logs[0],
                              &shaping.m_visualAttributes[0], &generatedGlyphs);
         }
-        if (hr == E_PENDING) {
-            // Allocate the DC.
-            tempDC = GetDC(0);
-            oldFont = SelectObject(tempDC, hfont);
+        // We receive E_PENDING when we need to try again with a Drawing Context,
+        // but we don't want to retry again if we already tried with non-zero DC.
+        if (hr == E_PENDING && !m_cachedDC) {
+            EnsureCachedDCCreated();
             continue;
-        } else if (hr == E_OUTOFMEMORY) {
+        }
+        if (hr == E_OUTOFMEMORY) {
             numGlyphs *= 2;
             continue;
-        } else if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(shaping, run, fontProperties) && canUseGlyphIndex(run)))
+        }
+        if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(shaping, run, fontProperties) && canUseGlyphIndex(run)))
             break;
 
-        // The current font can't render this run. clear DC and try
-        // next font.
-        if (tempDC) {
-            SelectObject(tempDC, oldFont);
-            ReleaseDC(0, tempDC);
-            tempDC = 0;
-        }
-
+        // The current font can't render this run, try next font.
         if (!m_disableFontFallback &&
             nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) {
             // The primary font does not support this run. Try next font.
@@ -786,10 +788,7 @@
     }
     shaping.m_advance.resize(generatedGlyphs);
     shaping.m_offsets.resize(generatedGlyphs);
-    if (tempDC) {
-        SelectObject(tempDC, oldFont);
-        ReleaseDC(0, tempDC);
-    }
+
     // On failure, our logs don't mean anything, so zero those out.
     if (!result)
         shaping.m_logs.clear();
@@ -797,6 +796,20 @@
     return result;
 }
 
+void UniscribeHelper::EnsureCachedDCCreated()
+{
+    if (m_cachedDC)
+        return;
+    // Allocate a memory DC that is compatible with the Desktop DC since we don't have any window,
+    // and we don't want to use the Desktop DC directly since it can have nasty side effects
+    // as identified in Chrome Issue http://crbug.com/59315.
+    HDC screenDC = ::GetDC(0);
+    m_cachedDC = ::CreateCompatibleDC(screenDC);
+    ASSERT(m_cachedDC);
+
+    int result = ::ReleaseDC(0, screenDC);
+    ASSERT(result == 1);
+}
 void UniscribeHelper::fillShapes()
 {
     m_shapes.resize(m_runs.size());
@@ -836,30 +849,15 @@
 
         // Compute placements. Note that offsets is documented incorrectly
         // and is actually an array.
-
-        // DC that we lazily create if Uniscribe commands us to.
-        // (this does not happen often because scriptCache is already
-        //  updated when calling ScriptShape).
-        HDC tempDC = 0;
-        HGDIOBJ oldFont = 0;
-        HRESULT hr;
-        while (true) {
-            shaping.m_prePadding = 0;
-            hr = ScriptPlace(tempDC, shaping.m_scriptCache,
-                             &shaping.m_glyphs[0],
-                             static_cast<int>(shaping.m_glyphs.size()),
-                             &shaping.m_visualAttributes[0], &m_runs[i].a,
-                             &shaping.m_advance[0], &shaping.m_offsets[0],
-                             &shaping.m_abc);
-            if (hr != E_PENDING)
-                break;
-
-            // Allocate the DC and run the loop again.
-            tempDC = GetDC(0);
-            oldFont = SelectObject(tempDC, shaping.m_hfont);
-        }
-
-        if (FAILED(hr)) {
+        EnsureCachedDCCreated();
+        SelectObject(m_cachedDC, shaping.m_hfont);
+        shaping.m_prePadding = 0;
+        if (FAILED(ScriptPlace(m_cachedDC, shaping.m_scriptCache,
+                               &shaping.m_glyphs[0],
+                               static_cast<int>(shaping.m_glyphs.size()),
+                               &shaping.m_visualAttributes[0], &m_runs[i].a,
+                               &shaping.m_advance[0], &shaping.m_offsets[0],
+                               &shaping.m_abc))) {
             // Some error we don't know how to handle. Nuke all of our data
             // since we can't deal with partially valid data later.
             m_runs.clear();
@@ -867,11 +865,6 @@
             m_shapes.clear();
             m_screenOrder.clear();
         }
-
-        if (tempDC) {
-            SelectObject(tempDC, oldFont);
-            ReleaseDC(0, tempDC);
-        }
     }
 
     adjustSpaceAdvances();

Modified: trunk/Source/WebCore/platform/graphics/chromium/UniscribeHelper.h (101058 => 101059)


--- trunk/Source/WebCore/platform/graphics/chromium/UniscribeHelper.h	2011-11-23 10:02:27 UTC (rev 101058)
+++ trunk/Source/WebCore/platform/graphics/chromium/UniscribeHelper.h	2011-11-23 10:10:29 UTC (rev 101059)
@@ -40,7 +40,7 @@
 #include <unicode/uchar.h>
 #include <wtf/Vector.h>
 
-class UniscribeTest_TooBig_Test;  // A gunit test for UniscribeHelper.
+class UniscribeTest_TooBig_Test; // A gunit test for UniscribeHelper.
 
 namespace WebCore {
 
@@ -380,6 +380,9 @@
     // NextWinFontData scans fallback fonts from the beginning.
     virtual void resetFontIndex() {}
 
+    // If m_cachedDC is 0, creates one that is compatible with the screen DC.
+    void EnsureCachedDCCreated();
+
     // The input data for this run of Uniscribe. See the constructor.
     const UChar* m_input;
     const int m_inputLength;
@@ -394,6 +397,8 @@
     // m_height and m_style if they're known. Getters for them would have to
     // 'infer' their values from m_hfont ONLY when they're not set.
     HFONT m_hfont;
+    // We cache the DC to use with ScriptShape/ScriptPlace.
+    static HDC m_cachedDC;
     SCRIPT_CACHE* m_scriptCache;
     SCRIPT_FONTPROPERTIES* m_fontProperties;
     int m_ascent;
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to