From 37298d2efd41bf524d9d201497a0c91e95928ea8 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 17 Jul 2013 12:53:24 +0100 Subject: [PATCH 01/31] bug 879963 - part 1 - preserve in-progress font loaders when updating the user font set. r=dbaron --- layout/style/nsFontFaceLoader.cpp | 45 ++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp index eecd0518245..d561b04cca3 100644 --- a/layout/style/nsFontFaceLoader.cpp +++ b/layout/style/nsFontFaceLoader.cpp @@ -413,39 +413,54 @@ static PLDHashOperator DetachFontEntries(const nsAString& aKey, return PL_DHASH_NEXT; } +static PLDHashOperator RemoveIfEmpty(const nsAString& aKey, + nsRefPtr& aFamily, + void* aUserArg) +{ + return aFamily->GetFontList().Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE; +} + bool nsUserFontSet::UpdateRules(const nsTArray& aRules) { bool modified = false; - // destroy any current loaders, as the entries they refer to - // may be about to get replaced - if (mLoaders.Count() > 0) { - modified = true; // trigger reflow so that any necessary downloads - // will be reinitiated - } - mLoaders.EnumerateEntries(DestroyIterator, nullptr); + // The @font-face rules that make up the user font set have changed, + // so we need to update the set. However, we want to preserve existing + // font entries wherever possible, so that we don't discard and then + // re-download resources in the (common) case where at least some of the + // same rules are still present. nsTArray oldRules; mRules.SwapElements(oldRules); - // destroy the font family records; we need to re-create them - // because we might end up with faces in a different order, - // even if they're the same font entries as before + // Remove faces from the font family records; we need to re-insert them + // because we might end up with faces in a different order even if they're + // the same font entries as before. (The order can affect font selection + // where multiple faces match the requested style, perhaps with overlapping + // unicode-range coverage.) mFontFamilies.Enumerate(DetachFontEntries, nullptr); - mFontFamilies.Clear(); for (uint32_t i = 0, i_end = aRules.Length(); i < i_end; ++i) { - // insert each rule into our list, migrating old font entries if possible + // Insert each rule into our list, migrating old font entries if possible // rather than creating new ones; set modified to true if we detect - // that rule ordering has changed, or if a new entry is created + // that rule ordering has changed, or if a new entry is created. InsertRule(aRules[i].mRule, aRules[i].mSheetType, oldRules, modified); } - // if any rules are left in the old list, note that the set has changed + // Remove any residual families that have no font entries (i.e., they were + // not defined at all by the updated set of @font-face rules). + mFontFamilies.Enumerate(RemoveIfEmpty, nullptr); + + // If any rules are left in the old list, note that the set has changed + // (even if the new set was built entirely by migrating old font entries). if (oldRules.Length() > 0) { modified = true; - // any in-progress loaders for obsolete rules should be cancelled + // Any in-progress loaders for obsolete rules should be cancelled, + // as the resource being downloaded will no longer be required. + // We need to explicitly remove any loaders here, otherwise the loaders + // will keep their "orphaned" font entries alive until they complete, + // even after the oldRules array is deleted. size_t count = oldRules.Length(); for (size_t i = 0; i < count; ++i) { gfxFontEntry *fe = oldRules[i].mFontEntry; From f6223eadc2accc298c75c06ae6ded14d597afe0e Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 17 Jul 2013 12:53:31 +0100 Subject: [PATCH 02/31] bug 879963 - part 2 - avoid adding duplicate face entries to a user font family. r=dbaron --- gfx/thebes/gfxUserFontSet.cpp | 67 ++++++++++++++++++++++++++++--- gfx/thebes/gfxUserFontSet.h | 30 ++++++++++++-- layout/style/nsFontFaceLoader.cpp | 2 +- 3 files changed, 89 insertions(+), 10 deletions(-) diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp index ec29d467f1f..bbce9966702 100644 --- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -45,7 +45,7 @@ static uint64_t sFontSetGeneration = 0; gfxProxyFontEntry::gfxProxyFontEntry(const nsTArray& aFontFaceSrcList, uint32_t aWeight, - uint32_t aStretch, + int32_t aStretch, uint32_t aItalicStyle, const nsTArray& aFeatureSettings, uint32_t aLanguageOverride, @@ -60,6 +60,8 @@ gfxProxyFontEntry::gfxProxyFontEntry(const nsTArray& aFontFaceSr mSrcIndex = 0; mWeight = aWeight; mStretch = aStretch; + // XXX Currently, we don't distinguish 'italic' and 'oblique' styles; + // we need to fix this. (Bug 543715) mItalic = (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0; mFeatureSettings.AppendElements(aFeatureSettings); mLanguageOverride = aLanguageOverride; @@ -70,6 +72,29 @@ gfxProxyFontEntry::~gfxProxyFontEntry() { } +bool +gfxProxyFontEntry::Matches(const nsTArray& aFontFaceSrcList, + uint32_t aWeight, + int32_t aStretch, + uint32_t aItalicStyle, + const nsTArray& aFeatureSettings, + uint32_t aLanguageOverride, + gfxSparseBitSet *aUnicodeRanges) +{ + // XXX font entries don't distinguish italic from oblique (bug 543715) + bool isItalic = + (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0; + + return mWeight == aWeight && + mStretch == aStretch && + mItalic == isItalic && + mFeatureSettings == aFeatureSettings && + mLanguageOverride == aLanguageOverride && + mSrcList == aFontFaceSrcList; + // XXX once we support unicode-range (bug 475891), + // we'll need to compare that here as well +} + gfxFont* gfxProxyFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) { @@ -91,14 +116,12 @@ gfxFontEntry* gfxUserFontSet::AddFontFace(const nsAString& aFamilyName, const nsTArray& aFontFaceSrcList, uint32_t aWeight, - uint32_t aStretch, + int32_t aStretch, uint32_t aItalicStyle, const nsTArray& aFeatureSettings, const nsString& aLanguageOverride, gfxSparseBitSet *aUnicodeRanges) { - gfxProxyFontEntry *proxyEntry = nullptr; - nsAutoString key(aFamilyName); ToLowerCase(key); @@ -115,10 +138,42 @@ gfxUserFontSet::AddFontFace(const nsAString& aFamilyName, mFontFamilies.Put(key, family); } - // construct a new face and add it into the family uint32_t languageOverride = gfxFontStyle::ParseFontLanguageOverride(aLanguageOverride); - proxyEntry = + + // If there's already a proxy in the family whose descriptors all match, + // we can just move it to the end of the list instead of adding a new + // face that will always "shadow" the old one. + // Note that we can't do this for "real" (non-proxy) entries, even if the + // style descriptors match, as they might have had a different source list, + // but we no longer have the old source list available to check. + nsTArray >& fontList = family->GetFontList(); + for (uint32_t i = 0, count = fontList.Length(); i < count; i++) { + if (!fontList[i]->mIsProxy) { + continue; + } + + gfxProxyFontEntry *existingProxyEntry = + static_cast(fontList[i].get()); + if (!existingProxyEntry->Matches(aFontFaceSrcList, + aWeight, aStretch, aItalicStyle, + aFeatureSettings, languageOverride, + aUnicodeRanges)) { + continue; + } + + // We've found an entry that matches the new face exactly, so advance + // it to the end of the list. + // (Hold a strong reference while doing this, in case the only thing + // keeping the proxyFontEntry alive is the reference from family!) + nsRefPtr ref(existingProxyEntry); + family->RemoveFontEntry(existingProxyEntry); + family->AddFontEntry(existingProxyEntry); + return existingProxyEntry; + } + + // construct a new face and add it into the family + gfxProxyFontEntry *proxyEntry = new gfxProxyFontEntry(aFontFaceSrcList, aWeight, aStretch, aItalicStyle, aFeatureSettings, diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h index f801c9ec075..7a9fb609e14 100644 --- a/gfx/thebes/gfxUserFontSet.h +++ b/gfx/thebes/gfxUserFontSet.h @@ -41,6 +41,20 @@ struct gfxFontFaceSrc { nsCOMPtr mOriginPrincipal; // principal if url }; +inline bool +operator==(const gfxFontFaceSrc& a, const gfxFontFaceSrc& b) +{ + bool equals; + return (a.mIsLocal && b.mIsLocal && + a.mLocalName == b.mLocalName) || + (!a.mIsLocal && !b.mIsLocal && + a.mUseOriginPrincipal == b.mUseOriginPrincipal && + a.mFormatFlags == b.mFormatFlags && + NS_SUCCEEDED(a.mURI->Equals(b.mURI, &equals)) && equals && + NS_SUCCEEDED(a.mReferrer->Equals(b.mReferrer, &equals)) && equals && + a.mOriginPrincipal->Equals(b.mOriginPrincipal)); +} + // Subclassed to store platform-specific code cleaned out when font entry is // deleted. // Lifetime: from when platform font is created until it is deactivated. @@ -168,13 +182,14 @@ public: // add in a font face - // weight, stretch - 0 == unknown, [1, 9] otherwise + // weight - 0 == unknown, [100, 900] otherwise (multiples of 100) + // stretch = [NS_FONT_STRETCH_ULTRA_CONDENSED, NS_FONT_STRETCH_ULTRA_EXPANDED] // italic style = constants in gfxFontConstants.h, e.g. NS_FONT_STYLE_NORMAL // TODO: support for unicode ranges not yet implemented gfxFontEntry *AddFontFace(const nsAString& aFamilyName, const nsTArray& aFontFaceSrcList, uint32_t aWeight, - uint32_t aStretch, + int32_t aStretch, uint32_t aItalicStyle, const nsTArray& aFeatureSettings, const nsString& aLanguageOverride, @@ -428,7 +443,7 @@ class gfxProxyFontEntry : public gfxFontEntry { public: gfxProxyFontEntry(const nsTArray& aFontFaceSrcList, uint32_t aWeight, - uint32_t aStretch, + int32_t aStretch, uint32_t aItalicStyle, const nsTArray& aFeatureSettings, uint32_t aLanguageOverride, @@ -436,6 +451,15 @@ public: virtual ~gfxProxyFontEntry(); + // Return whether the entry matches the given list of attributes + bool Matches(const nsTArray& aFontFaceSrcList, + uint32_t aWeight, + int32_t aStretch, + uint32_t aItalicStyle, + const nsTArray& aFeatureSettings, + uint32_t aLanguageOverride, + gfxSparseBitSet *aUnicodeRanges); + virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold); // note that code depends on the ordering of these values! diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp index d561b04cca3..10dc41fb3a4 100644 --- a/layout/style/nsFontFaceLoader.cpp +++ b/layout/style/nsFontFaceLoader.cpp @@ -530,7 +530,7 @@ nsUserFontSet::InsertRule(nsCSSFontFaceRule *aRule, uint8_t aSheetType, // this is a new rule: uint32_t weight = NS_STYLE_FONT_WEIGHT_NORMAL; - uint32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL; + int32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL; uint32_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL; nsString languageOverride; From cf3683fe3f546616bcd5254ee2f10d10efcc11c9 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 17 Jul 2013 12:53:38 +0100 Subject: [PATCH 03/31] bug 879963 - test for redundant downloading of a repeated @font-face resource. r=dbaron --- layout/style/test/Makefile.in | 2 + layout/style/test/redundant_font_download.sjs | 60 ++++++++ .../test/test_redundant_font_download.html | 130 ++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 layout/style/test/redundant_font_download.sjs create mode 100644 layout/style/test/test_redundant_font_download.html diff --git a/layout/style/test/Makefile.in b/layout/style/test/Makefile.in index 76ba4defd91..f5f783b748f 100644 --- a/layout/style/test/Makefile.in +++ b/layout/style/test/Makefile.in @@ -145,6 +145,8 @@ MOCHITEST_FILES = test_acid3_test46.html \ test_property_database.html \ test_priority_preservation.html \ test_property_syntax_errors.html \ + test_redundant_font_download.html \ + redundant_font_download.sjs \ test_rem_unit.html \ test_rule_insertion.html \ test_rule_serialization.html \ diff --git a/layout/style/test/redundant_font_download.sjs b/layout/style/test/redundant_font_download.sjs new file mode 100644 index 00000000000..7eb7d8f8df5 --- /dev/null +++ b/layout/style/test/redundant_font_download.sjs @@ -0,0 +1,60 @@ +const BinaryOutputStream = + Components.Constructor("@mozilla.org/binaryoutputstream;1", + "nsIBinaryOutputStream", + "setOutputStream"); + +// this is simply a hex dump of a red square .PNG image +const RED_SQUARE = + [ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, + 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x20, 0x08, 0x02, 0x00, 0x00, 0x00, 0xFC, + 0x18, 0xED, 0xA3, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, + 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, 0x00, 0x28, + 0x49, 0x44, 0x41, 0x54, 0x48, 0xC7, 0xED, 0xCD, 0x41, 0x0D, + 0x00, 0x00, 0x08, 0x04, 0xA0, 0xD3, 0xFE, 0x9D, 0x35, 0x85, + 0x0F, 0x37, 0x28, 0x40, 0x4D, 0x6E, 0x75, 0x04, 0x02, 0x81, + 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0xC1, 0x93, 0x60, 0x01, + 0xA3, 0xC4, 0x01, 0x3F, 0x58, 0x1D, 0xEF, 0x27, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 + ]; + +function handleRequest(request, response) +{ + var query = {}; + request.queryString.split('&').forEach(function (val) { + var [name, value] = val.split('='); + query[name] = unescape(value); + }); + + response.setHeader("Cache-Control", "no-cache"); + + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/plain", false); + + var log = getState("bug-879963-request-log") || ""; + + var stream = new BinaryOutputStream(response.bodyOutputStream); + + if (query["q"] == "init") { + log = "init"; // initialize the log, and return a PNG image + response.setHeader("Content-Type", "image/png", false); + stream.writeByteArray(RED_SQUARE, RED_SQUARE.length); + } else if (query["q"] == "image") { + log = log + ";" + query["q"]; + response.setHeader("Content-Type", "image/png", false); + stream.writeByteArray(RED_SQUARE, RED_SQUARE.length); + } else if (query["q"] == "font") { + log = log + ";" + query["q"]; + // we don't provide a real font; that's ok, OTS will just reject it + response.write("Junk"); + } else if (query["q"] == "report") { + // don't include the actual "report" request in the log we return + response.write(log); + } else { + log = log + ";" + query["q"]; + response.setStatusLine(request.httpVersion, 404, "Not Found"); + } + + setState("bug-879963-request-log", log); +} diff --git a/layout/style/test/test_redundant_font_download.html b/layout/style/test/test_redundant_font_download.html new file mode 100644 index 00000000000..3cd2df448b0 --- /dev/null +++ b/layout/style/test/test_redundant_font_download.html @@ -0,0 +1,130 @@ + + + + + + Test for bug 879963 + + + + + + + + + + + Mozilla Bug 879963 + +
+ + +
+ +
+ Test +
+ +
+ +
+ + + + + + From 1ab8695848394d2f35b0c28e35bbe549a6b2cb61 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 17 Jul 2013 14:12:05 +0200 Subject: [PATCH 04/31] Bug 883004 - Part 2: Move Direct2D 1.1 detection to configure.in r=glandium --- configure.in | 7 +++++++ gfx/2d/moz.build | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/configure.in b/configure.in index fad2fef4fd5..baca359b82d 100644 --- a/configure.in +++ b/configure.in @@ -8124,6 +8124,13 @@ dnl ======================================================== dnl Graphics checks. dnl ======================================================== +if test "${OS_TARGET}" = "WINNT"; then + if $PERL -e "exit($MOZ_WINSDK_MAXVER < 0x06020000)"; then + MOZ_ENABLE_DIRECT2D1_1=1 + AC_SUBST(MOZ_ENABLE_DIRECT2D1_1) + fi +fi + if test "${OS_TARGET}" = "WINNT" -o "${OS_ARCH}" = "Darwin" -o "${MOZ_WIDGET_TOOLKIT}" = "android" -o "${MOZ_WIDGET_TOOLKIT}" = "gtk2"; then case "${target_cpu}" in i*86*|x86_64|arm) diff --git a/gfx/2d/moz.build b/gfx/2d/moz.build index f49078abda2..fa9cdd0b169 100644 --- a/gfx/2d/moz.build +++ b/gfx/2d/moz.build @@ -49,7 +49,7 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': 'PathD2D.cpp', 'ScaledFontDWrite.cpp', ] - if CONFIG['MOZ_WINSDK_MAXVER'] >= '0x06020000': + if CONFIG['MOZ_ENABLE_DIRECT2D1_1']: CPP_SOURCES += [ 'RadialGradientEffectD2D1.cpp' ] From 7c1ee02372dbe774faad4b1aa8f0c6bbda2cef25 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 17 Jul 2013 14:12:22 +0200 Subject: [PATCH 05/31] Bug 883004 - Part 3: Add Direct2D 1.1 backend to Moz2D. r=jrmuizel --- gfx/2d/2D.h | 11 + gfx/2d/DrawTargetD2D1.cpp | 916 +++++++++++++++++++++++++++++++++++ gfx/2d/DrawTargetD2D1.h | 214 ++++++++ gfx/2d/Factory.cpp | 45 ++ gfx/2d/GradientStopsD2D.h | 1 + gfx/2d/HelpersD2D.h | 52 ++ gfx/2d/Makefile.in | 5 + gfx/2d/PathD2D.h | 4 +- gfx/2d/ScaledFontDWrite.h | 1 + gfx/2d/SourceSurfaceD2D1.cpp | 163 +++++++ gfx/2d/SourceSurfaceD2D1.h | 85 ++++ gfx/2d/Types.h | 4 +- gfx/2d/moz.build | 4 +- gfx/thebes/gfxPlatform.h | 2 + 14 files changed, 1504 insertions(+), 3 deletions(-) create mode 100644 gfx/2d/DrawTargetD2D1.cpp create mode 100644 gfx/2d/DrawTargetD2D1.h create mode 100644 gfx/2d/SourceSurfaceD2D1.cpp create mode 100644 gfx/2d/SourceSurfaceD2D1.h diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index 79c8ec6f791..4b406716a18 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -35,6 +35,8 @@ typedef _cairo_scaled_font cairo_scaled_font_t; struct ID3D10Device1; struct ID3D10Texture2D; +struct ID3D11Device; +struct ID2D1Device; struct IDWriteRenderingParams; class GrContext; @@ -989,6 +991,11 @@ public: static void SetDirect3D10Device(ID3D10Device1 *aDevice); static ID3D10Device1 *GetDirect3D10Device(); +#ifdef USE_D2D1_1 + static void SetDirect3D11Device(ID3D11Device *aDevice); + static ID3D11Device *GetDirect3D11Device(); + static ID2D1Device *GetD2D1Device(); +#endif static TemporaryRef CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams); @@ -999,6 +1006,10 @@ public: private: static ID3D10Device1 *mD3D10Device; +#ifdef USE_D2D1_1 + static ID3D11Device *mD3D11Device; + static ID2D1Device *mD2D1Device; +#endif #endif static DrawEventRecorder *mRecorder; diff --git a/gfx/2d/DrawTargetD2D1.cpp b/gfx/2d/DrawTargetD2D1.cpp new file mode 100644 index 00000000000..4679ebe1066 --- /dev/null +++ b/gfx/2d/DrawTargetD2D1.cpp @@ -0,0 +1,916 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DrawTargetD2D1.h" +#include "DrawTargetD2D.h" +#include "GradientStopsD2D.h" +#include "SourceSurfaceD2D1.h" +#include "SourceSurfaceD2D.h" +#include "RadialGradientEffectD2D1.h" + +#include "HelpersD2D.h" +#include "Tools.h" + +using namespace std; + +namespace mozilla { +namespace gfx { + +uint64_t DrawTargetD2D1::mVRAMUsageDT; +uint64_t DrawTargetD2D1::mVRAMUsageSS; +ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr; + +ID2D1Factory1 *D2DFactory1() +{ + return DrawTargetD2D1::factory(); +} + +DrawTargetD2D1::DrawTargetD2D1() + : mClipsArePushed(false) +{ +} + +DrawTargetD2D1::~DrawTargetD2D1() +{ + PopAllClips(); + + mDC->EndDraw(); +} + +TemporaryRef +DrawTargetD2D1::Snapshot() +{ + if (mSnapshot) { + return mSnapshot; + } + PopAllClips(); + + mDC->Flush(); + + mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this); + + return mSnapshot; +} + +void +DrawTargetD2D1::Flush() +{ + mDC->Flush(); +} + +void +DrawTargetD2D1::DrawSurface(SourceSurface *aSurface, + const Rect &aDest, + const Rect &aSource, + const DrawSurfaceOptions &aSurfOptions, + const DrawOptions &aOptions) +{ + Matrix mat; + + RefPtr image = GetImageForSurface(aSurface, mat, EXTEND_CLAMP); + + if (!image) { + gfxWarning() << *this << ": Unable to get D2D image for surface."; + return; + } + + PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); + + D2D1_RECT_F samplingBounds; + + if (aSurfOptions.mSamplingBounds == SAMPLING_BOUNDED) { + samplingBounds = D2DRect(aSource); + } else { + samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width), Float(aSurface->GetSize().height)); + } + + Float xScale = aDest.width / aSource.width; + Float yScale = aDest.height / aSource.height; + + RefPtr brush; + + // Here we scale the source pattern up to the size and position where we want + // it to be. + Matrix transform; + transform.Translate(aDest.x, aDest.y); + transform.Scale(xScale, yScale); + + mDC->CreateImageBrush(image, D2D1::ImageBrushProperties(samplingBounds), + D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)), + byRef(brush)); + mDC->FillRectangle(D2DRect(aDest), brush); + + FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color())); +} + +void +DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface *aSurface, + const Point &aDest, + const Color &aColor, + const Point &aOffset, + Float aSigma, + CompositionOp aOperator) +{ + MarkChanged(); + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + Matrix mat; + RefPtr image = GetImageForSurface(aSurface, mat, EXTEND_CLAMP); + + if (!mat.IsIdentity()) { + gfxDebug() << *this << ": At this point complex partial uploads are not supported for Shadow surfaces."; + return; + } + + // Step 1, create the shadow effect. + RefPtr shadowEffect; + mDC->CreateEffect(CLSID_D2D1Shadow, byRef(shadowEffect)); + shadowEffect->SetInput(0, image); + shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma); + D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a }; + shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color); + + // Step 2, move the shadow effect into place. + RefPtr affineTransformEffect; + mDC->CreateEffect(CLSID_D2D12DAffineTransform, byRef(affineTransformEffect)); + affineTransformEffect->SetInputEffect(0, shadowEffect); + D2D1_MATRIX_3X2_F matrix = D2D1::Matrix3x2F::Translation(aOffset.x, aOffset.y); + affineTransformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, matrix); + + // Step 3, create an effect that combines shadow and bitmap in one image. + RefPtr compositeEffect; + mDC->CreateEffect(CLSID_D2D1Composite, byRef(compositeEffect)); + compositeEffect->SetInputEffect(0, affineTransformEffect); + compositeEffect->SetInput(1, image); + compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2DCompositionMode(aOperator)); + + D2D1_POINT_2F surfPoint = D2DPoint(aDest); + mDC->DrawImage(compositeEffect, &surfPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator)); +} + +void +DrawTargetD2D1::ClearRect(const Rect &aRect) +{ + MarkChanged(); + + mDC->PushAxisAlignedClip(D2DRect(aRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + mDC->Clear(); + mDC->PopAxisAlignedClip(); +} + +void +DrawTargetD2D1::MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions) +{ + RefPtr bitmap; + + RefPtr image = GetImageForSurface(aMask, Matrix(), EXTEND_CLAMP); + + PrepareForDrawing(aOptions.mCompositionOp, aSource); + + // FillOpacityMask only works if the antialias mode is MODE_ALIASED + mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + + IntSize size = aMask->GetSize(); + Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height)); + image->QueryInterface((ID2D1Bitmap**)&bitmap); + if (!bitmap) { + gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces."; + return; + } + + Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height)); + RefPtr brush = CreateBrushForPattern(aSource, aOptions.mAlpha); + mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect)); + + mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + + FinalizeDrawing(aOptions.mCompositionOp, aSource); +} + +void +DrawTargetD2D1::CopySurface(SourceSurface *aSurface, + const IntRect &aSourceRect, + const IntPoint &aDestination) +{ + MarkChanged(); + + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + Matrix mat; + RefPtr image = GetImageForSurface(aSurface, mat, EXTEND_CLAMP); + + if (!mat.IsIdentity()) { + gfxDebug() << *this << ": At this point complex partial uploads are not supported for CopySurface."; + return; + } + + mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)), + D2D1::RectF(Float(aSourceRect.x), Float(aSourceRect.y), + Float(aSourceRect.XMost()), Float(aSourceRect.YMost())), + D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); +} + +void +DrawTargetD2D1::FillRect(const Rect &aRect, + const Pattern &aPattern, + const DrawOptions &aOptions) +{ + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + mDC->FillRectangle(D2DRect(aRect), brush); + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::StrokeRect(const Rect &aRect, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); + + mDC->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle); + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::StrokeLine(const Point &aStart, + const Point &aEnd, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); + + mDC->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle); + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::Stroke(const Path *aPath, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions, + const DrawOptions &aOptions) +{ + if (aPath->GetBackendType() != BACKEND_DIRECT2D) { + gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; + return; + } + const PathD2D *d2dPath = static_cast(aPath); + + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); + + mDC->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle); + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::Fill(const Path *aPath, + const Pattern &aPattern, + const DrawOptions &aOptions) +{ + if (aPath->GetBackendType() != BACKEND_DIRECT2D) { + gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; + return; + } + const PathD2D *d2dPath = static_cast(aPath); + + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + + mDC->FillGeometry(d2dPath->mGeometry, brush); + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, + const GlyphBuffer &aBuffer, + const Pattern &aPattern, + const DrawOptions &aOptions, + const GlyphRenderingOptions *aRenderingOptions) +{ + if (aFont->GetType() != FONT_DWRITE) { + gfxDebug() << *this << ": Ignoring drawing call for incompatible font."; + return; + } + + ScaledFontDWrite *font = static_cast(aFont); + + IDWriteRenderingParams *params = nullptr; + if (aRenderingOptions) { + if (aRenderingOptions->GetType() != FONT_DWRITE) { + gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions."; + // This should never happen. + MOZ_ASSERT(false); + } else { + params = static_cast(aRenderingOptions)->mParams; + } + } + + AntialiasMode aaMode = font->GetDefaultAAMode(); + + if (aOptions.mAntialiasMode != AA_DEFAULT) { + aaMode = aOptions.mAntialiasMode; + } + + PrepareForDrawing(aOptions.mCompositionOp, aPattern); + + bool forceClearType = false; + if (mFormat == FORMAT_B8G8R8A8 && mPermitSubpixelAA && + aOptions.mCompositionOp == OP_OVER && aaMode == AA_SUBPIXEL) { + forceClearType = true; + } + + + D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; + + switch (aaMode) { + case AA_NONE: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; + break; + case AA_GRAY: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + break; + case AA_SUBPIXEL: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + break; + default: + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; + } + + if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && + mFormat != FORMAT_B8G8R8X8 && !forceClearType) { + d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + } + + mDC->SetTextAntialiasMode(d2dAAMode); + + if (params != mTextRenderingParams) { + mDC->SetTextRenderingParams(params); + mTextRenderingParams = params; + } + + RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); + + AutoDWriteGlyphRun autoRun; + DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun); + + if (brush) { + mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush); + } + + FinalizeDrawing(aOptions.mCompositionOp, aPattern); +} + +void +DrawTargetD2D1::Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions) +{ + PrepareForDrawing(aOptions.mCompositionOp, aSource); + + RefPtr source = CreateBrushForPattern(aSource, aOptions.mAlpha); + RefPtr mask = CreateBrushForPattern(aMask, 1.0f); + mDC->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), + 1.0f, mask), + nullptr); + + Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height); + Matrix mat = mTransform; + mat.Invert(); + + mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source); + + mDC->PopLayer(); + + FinalizeDrawing(aOptions.mCompositionOp, aSource); +} + +void +DrawTargetD2D1::PushClip(const Path *aPath) +{ + if (aPath->GetBackendType() != BACKEND_DIRECT2D) { + gfxDebug() << *this << ": Ignoring clipping call for incompatible path."; + return; + } + + RefPtr pathD2D = static_cast(const_cast(aPath)); + + PushedClip clip; + clip.mTransform = D2DMatrix(mTransform); + clip.mPath = pathD2D; + + pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds); + + mPushedClips.push_back(clip); + + // The transform of clips is relative to the world matrix, since we use the total + // transform for the clips, make the world matrix identity. + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + if (mClipsArePushed) { + PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform); + } +} + +void +DrawTargetD2D1::PushClipRect(const Rect &aRect) +{ + if (!mTransform.IsRectilinear()) { + // Whoops, this isn't a rectangle in device space, Direct2D will not deal + // with this transform the way we want it to. + // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx + + RefPtr pathBuilder = CreatePathBuilder(); + pathBuilder->MoveTo(aRect.TopLeft()); + pathBuilder->LineTo(aRect.TopRight()); + pathBuilder->LineTo(aRect.BottomRight()); + pathBuilder->LineTo(aRect.BottomLeft()); + pathBuilder->Close(); + RefPtr path = pathBuilder->Finish(); + return PushClip(path); + } + + PushedClip clip; + Rect rect = mTransform.TransformBounds(aRect); + IntRect intRect; + clip.mIsPixelAligned = rect.ToIntRect(&intRect); + + // Do not store the transform, just store the device space rectangle directly. + clip.mBounds = D2DRect(rect); + + mPushedClips.push_back(clip); + + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + if (mClipsArePushed) { + mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } +} + +void +DrawTargetD2D1::PopClip() +{ + if (mClipsArePushed) { + if (mPushedClips.back().mPath) { + mDC->PopLayer(); + } else { + mDC->PopAxisAlignedClip(); + } + } + mPushedClips.pop_back(); +} + +TemporaryRef +DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) const +{ + RefPtr bitmap; + + HRESULT hr = mDC->CreateBitmap(D2DIntSize(aSize), aData, aStride, + D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(aFormat)), + byRef(bitmap)); + + if (!bitmap) { + return nullptr; + } + + return new SourceSurfaceD2D1(bitmap.get(), mDC, aFormat, aSize); +} + +TemporaryRef +DrawTargetD2D1::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const +{ + RefPtr dt = new DrawTargetD2D1(); + + if (!dt->Init(aSize, aFormat)) { + return nullptr; + } + + return dt; +} + +TemporaryRef +DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const +{ + RefPtr path; + HRESULT hr = factory()->CreatePathGeometry(byRef(path)); + + if (FAILED(hr)) { + gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: " << hr; + return nullptr; + } + + RefPtr sink; + hr = path->Open(byRef(sink)); + if (FAILED(hr)) { + gfxWarning() << *this << ": Failed to access Direct2D Path Geometry. Code: " << hr; + return nullptr; + } + + if (aFillRule == FILL_WINDING) { + sink->SetFillMode(D2D1_FILL_MODE_WINDING); + } + + return new PathBuilderD2D(sink, path, aFillRule); +} + +TemporaryRef +DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const +{ + D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops]; + + for (uint32_t i = 0; i < aNumStops; i++) { + stops[i].position = rawStops[i].offset; + stops[i].color = D2DColor(rawStops[i].color); + } + + RefPtr stopCollection; + + HRESULT hr = + mDC->CreateGradientStopCollection(stops, aNumStops, + D2D1_GAMMA_2_2, D2DExtend(aExtendMode), + byRef(stopCollection)); + delete [] stops; + + if (FAILED(hr)) { + gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: " << hr; + return nullptr; + } + + return new GradientStopsD2D(stopCollection); +} + +bool +DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat) +{ + HRESULT hr; + + hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC)); + + if (FAILED(hr)) { + gfxWarning() << *this << ": Error " << hr << " failed to initialize new DeviceContext."; + return false; + } + + D2D1_BITMAP_PROPERTIES1 props; + props.dpiX = 96; + props.dpiY = 96; + props.pixelFormat = D2DPixelFormat(aFormat); + props.colorContext = nullptr; + props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET; + mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mBitmap)); + + if (FAILED(hr)) { + gfxWarning() << *this << ": Error " << hr << " failed to create new CommandList."; + return false; + } + + mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap)); + + mDC->SetTarget(mBitmap); + + mDC->BeginDraw(); + + mFormat = aFormat; + mSize = aSize; + + return true; +} + +/** + * Private helpers. + */ +uint32_t +DrawTargetD2D1::GetByteSize() const +{ + return mSize.width * mSize.height * BytesPerPixel(mFormat); +} + +ID2D1Factory1* +DrawTargetD2D1::factory() +{ + if (mFactory) { + return mFactory; + } + + HRESULT hr = D2DFactory()->QueryInterface((ID2D1Factory1**)&mFactory); + + if (FAILED(hr)) { + return nullptr; + } + + RadialGradientEffectD2D1::Register(mFactory); + + return mFactory; +} + +void +DrawTargetD2D1::MarkChanged() +{ + if (mSnapshot) { + if (mSnapshot->hasOneRef()) { + // Just destroy it, since no-one else knows about it. + mSnapshot = nullptr; + } else { + mSnapshot->DrawTargetWillChange(); + // The snapshot will no longer depend on this target. + MOZ_ASSERT(!mSnapshot); + } + } + if (mDependentTargets.size()) { + // Copy mDependentTargets since the Flush()es below will modify it. + TargetSet tmpTargets = mDependentTargets; + for (TargetSet::iterator iter = tmpTargets.begin(); + iter != tmpTargets.end(); iter++) { + (*iter)->Flush(); + } + // The Flush() should have broken all dependencies on this target. + MOZ_ASSERT(!mDependentTargets.size()); + } +} + +void +DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern) +{ + MarkChanged(); + + // It's important to do this before FlushTransformToDC! As this will cause + // the transform to become dirty. + if (!mClipsArePushed) { + mClipsArePushed = true; + PushClipsToDC(mDC); + } + + FlushTransformToDC(); + + if (aOp == OP_OVER && IsPatternSupportedByD2D(aPattern)) { + return; + } + + mDC->SetTarget(mTempBitmap); + mDC->Clear(D2D1::ColorF(0, 0)); +} + +void +DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern) +{ + bool patternSupported = IsPatternSupportedByD2D(aPattern); + + if (aOp == OP_OVER && patternSupported) { + return; + } + + RefPtr image; + mDC->GetTarget(byRef(image)); + + mDC->SetTarget(mBitmap); + + if (patternSupported) { + mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); + return; + } + + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + RefPtr radialGradientEffect; + + mDC->CreateEffect(CLSID_RadialGradientEffect, byRef(radialGradientEffect)); + const RadialGradientPattern *pat = static_cast(&aPattern); + + radialGradientEffect->SetValue(RADIAL_PROP_STOP_COLLECTION, + static_cast(pat->mStops.get())->mStopCollection); + radialGradientEffect->SetValue(RADIAL_PROP_CENTER_1, D2D1::Vector2F(pat->mCenter1.x, pat->mCenter1.y)); + radialGradientEffect->SetValue(RADIAL_PROP_CENTER_2, D2D1::Vector2F(pat->mCenter2.x, pat->mCenter2.y)); + radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_1, pat->mRadius1); + radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2); + radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2); + radialGradientEffect->SetValue(RADIAL_PROP_TRANSFORM, D2DMatrix(pat->mMatrix * mTransform)); + radialGradientEffect->SetInput(0, image); + + mDC->DrawImage(radialGradientEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); +} + +void +DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource) +{ + if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) { + aSource->mDrawTarget->mDependentTargets.insert(this); + mDependingOnTargets.insert(aSource->mDrawTarget); + } +} + +void +DrawTargetD2D1::PopAllClips() +{ + if (mClipsArePushed) { + PopClipsFromDC(mDC); + + mClipsArePushed = false; + } +} + +void +DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC) +{ + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + for (std::vector::iterator iter = mPushedClips.begin(); + iter != mPushedClips.end(); iter++) { + if (iter->mPath) { + PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform); + } else { + mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } + } +} + +void +DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC) +{ + for (int i = mPushedClips.size() - 1; i >= 0; i--) { + if (mPushedClips[i].mPath) { + aDC->PopLayer(); + } else { + aDC->PopAxisAlignedClip(); + } + } +} + +TemporaryRef +DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) +{ + if (!IsPatternSupportedByD2D(aPattern)) { + RefPtr colBrush; + mDC->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush)); + return colBrush; + } + + if (aPattern.GetType() == PATTERN_COLOR) { + RefPtr colBrush; + Color color = static_cast(&aPattern)->mColor; + mDC->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g, + color.b, color.a), + D2D1::BrushProperties(aAlpha), + byRef(colBrush)); + return colBrush; + } else if (aPattern.GetType() == PATTERN_LINEAR_GRADIENT) { + RefPtr gradBrush; + const LinearGradientPattern *pat = + static_cast(&aPattern); + + GradientStopsD2D *stops = static_cast(pat->mStops.get()); + + if (!stops) { + gfxDebug() << "No stops specified for gradient pattern."; + return nullptr; + } + + if (pat->mBegin == pat->mEnd) { + RefPtr colBrush; + uint32_t stopCount = stops->mStopCollection->GetGradientStopCount(); + vector d2dStops(stopCount); + stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount); + mDC->CreateSolidColorBrush(d2dStops.back().color, + D2D1::BrushProperties(aAlpha), + byRef(colBrush)); + return colBrush; + } + + mDC->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin), + D2DPoint(pat->mEnd)), + D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), + stops->mStopCollection, + byRef(gradBrush)); + return gradBrush; + } else if (aPattern.GetType() == PATTERN_RADIAL_GRADIENT) { + RefPtr gradBrush; + const RadialGradientPattern *pat = + static_cast(&aPattern); + + GradientStopsD2D *stops = static_cast(pat->mStops.get()); + + if (!stops) { + gfxDebug() << "No stops specified for gradient pattern."; + return nullptr; + } + + // This will not be a complex radial gradient brush. + mDC->CreateRadialGradientBrush( + D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2), + D2DPoint(pat->mCenter1 - pat->mCenter2), + pat->mRadius2, pat->mRadius2), + D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), + stops->mStopCollection, + byRef(gradBrush)); + + return gradBrush; + } else if (aPattern.GetType() == PATTERN_SURFACE) { + const SurfacePattern *pat = + static_cast(&aPattern); + + if (!pat->mSurface) { + gfxDebug() << "No source surface specified for surface pattern"; + return nullptr; + } + + + Matrix mat = pat->mMatrix; + + RefPtr imageBrush; + RefPtr image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode); + mDC->CreateImageBrush(image, + D2D1::ImageBrushProperties(D2D1::RectF(0, 0, + Float(pat->mSurface->GetSize().width), + Float(pat->mSurface->GetSize().height)), + D2DExtend(pat->mExtendMode), D2DExtend(pat->mExtendMode), + D2DInterpolationMode(pat->mFilter)), + D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), + byRef(imageBrush)); + return imageBrush; + } + + gfxWarning() << "Invalid pattern type detected."; + return nullptr; +} + +TemporaryRef +DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform, + ExtendMode aExtendMode) +{ + RefPtr image; + + switch (aSurface->GetType()) { + case SURFACE_D2D1_1_IMAGE: + { + SourceSurfaceD2D1 *surf = static_cast(aSurface); + image = surf->GetImage(); + AddDependencyOnSource(surf); + } + break; + default: + { + RefPtr dataSurf = aSurface->GetDataSurface(); + if (!dataSurf) { + gfxWarning() << "Invalid surface type."; + return nullptr; + } + + image = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode, + aSourceTransform, mDC); + + return image; + } + break; + } + + return image; +} + +void +DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform) +{ + D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE; + + if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) { + options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; + } + + mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, + 1.0, nullptr, options), nullptr); +} + +} +} diff --git a/gfx/2d/DrawTargetD2D1.h b/gfx/2d/DrawTargetD2D1.h new file mode 100644 index 00000000000..86b9eb9acf0 --- /dev/null +++ b/gfx/2d/DrawTargetD2D1.h @@ -0,0 +1,214 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_DRAWTARGETD2D1_H_ +#define MOZILLA_GFX_DRAWTARGETD2D1_H_ + +#include "2D.h" +#include +#include +#include "PathD2D.h" +#include "HelpersD2D.h" + +#include +#include + +#ifdef _MSC_VER +#include +#else +#include +#endif + +struct IDWriteFactory; + +namespace mozilla { +namespace gfx { + +class SourceSurfaceD2D1; +class GradientStopsD2D; +class ScaledFontDWrite; + +const int32_t kLayerCacheSize1 = 5; + +class DrawTargetD2D1 : public DrawTarget +{ +public: + DrawTargetD2D1(); + virtual ~DrawTargetD2D1(); + + virtual BackendType GetType() const { return BACKEND_DIRECT2D1_1; } + virtual TemporaryRef Snapshot(); + virtual IntSize GetSize() { return mSize; } + + virtual void Flush(); + virtual void DrawSurface(SourceSurface *aSurface, + const Rect &aDest, + const Rect &aSource, + const DrawSurfaceOptions &aSurfOptions, + const DrawOptions &aOptions); + virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, + const Point &aDest, + const Color &aColor, + const Point &aOffset, + Float aSigma, + CompositionOp aOperator); + virtual void ClearRect(const Rect &aRect); + virtual void MaskSurface(const Pattern &aSource, + SourceSurface *aMask, + Point aOffset, + const DrawOptions &aOptions = DrawOptions()); + + virtual void CopySurface(SourceSurface *aSurface, + const IntRect &aSourceRect, + const IntPoint &aDestination); + + virtual void FillRect(const Rect &aRect, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions()); + virtual void StrokeRect(const Rect &aRect, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()); + virtual void StrokeLine(const Point &aStart, + const Point &aEnd, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()); + virtual void Stroke(const Path *aPath, + const Pattern &aPattern, + const StrokeOptions &aStrokeOptions = StrokeOptions(), + const DrawOptions &aOptions = DrawOptions()); + virtual void Fill(const Path *aPath, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions()); + virtual void FillGlyphs(ScaledFont *aFont, + const GlyphBuffer &aBuffer, + const Pattern &aPattern, + const DrawOptions &aOptions = DrawOptions(), + const GlyphRenderingOptions *aRenderingOptions = nullptr); + virtual void Mask(const Pattern &aSource, + const Pattern &aMask, + const DrawOptions &aOptions = DrawOptions()); + virtual void PushClip(const Path *aPath); + virtual void PushClipRect(const Rect &aRect); + virtual void PopClip(); + + virtual TemporaryRef CreateSourceSurfaceFromData(unsigned char *aData, + const IntSize &aSize, + int32_t aStride, + SurfaceFormat aFormat) const; + virtual TemporaryRef OptimizeSourceSurface(SourceSurface *aSurface) const { return nullptr; } + + virtual TemporaryRef + CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { return nullptr; } + + virtual TemporaryRef + CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const; + + virtual TemporaryRef CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const; + + virtual TemporaryRef + CreateGradientStops(GradientStop *aStops, + uint32_t aNumStops, + ExtendMode aExtendMode = EXTEND_CLAMP) const; + + virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; } + + bool Init(const IntSize &aSize, SurfaceFormat aFormat); + uint32_t GetByteSize() const; + + static ID2D1Factory1 *factory(); + static void CleanupD2D(); + static IDWriteFactory *GetDWriteFactory(); + + operator std::string() const { + std::stringstream stream; + stream << "DrawTargetD2D 1.1 (" << this << ")"; + return stream.str(); + } + + static uint64_t mVRAMUsageDT; + static uint64_t mVRAMUsageSS; + +private: + friend class SourceSurfaceD2D1; + +#ifdef _MSC_VER + typedef stdext::hash_set TargetSet; +#else + typedef std::unordered_set TargetSet; +#endif + + // This function will mark the surface as changing, and make sure any + // copy-on-write snapshots are notified. + void MarkChanged(); + void PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern); + void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern); + void FlushTransformToDC() { + if (mTransformDirty) { + mDC->SetTransform(D2DMatrix(mTransform)); + mTransformDirty = false; + } + } + void AddDependencyOnSource(SourceSurfaceD2D1* aSource); + + void PopAllClips(); + void PushClipsToDC(ID2D1DeviceContext *aDC); + void PopClipsFromDC(ID2D1DeviceContext *aDC); + + TemporaryRef CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f); + + TemporaryRef GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform, + ExtendMode aExtendMode); + + void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform); + + IntSize mSize; + + RefPtr mDevice; + RefPtr mTexture; + // This is only valid if mCurrentClippedGeometry is non-null. And will + // only be the intersection of all pixel-aligned retangular clips. This is in + // device space. + IntRect mCurrentClipBounds; + mutable RefPtr mDC; + RefPtr mBitmap; + RefPtr mTempBitmap; + + // We store this to prevent excessive SetTextRenderingParams calls. + RefPtr mTextRenderingParams; + + // List of pushed clips. + struct PushedClip + { + D2D1_RECT_F mBounds; + union { + // If mPath is non-NULL, the mTransform member will be used, otherwise + // the mIsPixelAligned member is valid. + D2D1_MATRIX_3X2_F mTransform; + bool mIsPixelAligned; + }; + RefPtr mPath; + }; + std::vector mPushedClips; + + // The latest snapshot of this surface. This needs to be told when this + // target is modified. We keep it alive as a cache. + RefPtr mSnapshot; + // A list of targets we need to flush when we're modified. + TargetSet mDependentTargets; + // A list of targets which have this object in their mDependentTargets set + TargetSet mDependingOnTargets; + + // True of the current clip stack is pushed to the main RT. + bool mClipsArePushed; + static ID2D1Factory1 *mFactory; + static IDWriteFactory *mDWriteFactory; +}; + +} +} + +#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */ diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index 5efae036dee..9547c8c833e 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -34,8 +34,12 @@ #ifdef WIN32 #include "DrawTargetD2D.h" +#ifdef USE_D2D1_1 +#include "DrawTargetD2D1.h" +#endif #include "ScaledFontDWrite.h" #include +#include "HelpersD2D.h" #endif #include "DrawTargetDual.h" @@ -151,6 +155,10 @@ int sGfxLogLevel = LOG_DEBUG; #ifdef WIN32 ID3D10Device1 *Factory::mD3D10Device; +#ifdef USE_D2D1_1 +ID3D11Device *Factory::mD3D11Device; +ID2D1Device *Factory::mD2D1Device; +#endif #endif DrawEventRecorder *Factory::mRecorder; @@ -185,6 +193,17 @@ Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFor } break; } +#ifdef USE_D2D1_1 + case BACKEND_DIRECT2D1_1: + { + RefPtr newTarget; + newTarget = new DrawTargetD2D1(); + if (newTarget->Init(aSize, aFormat)) { + retVal = newTarget; + } + break; + } +#endif #elif defined XP_MACOSX case BACKEND_COREGRAPHICS: case BACKEND_COREGRAPHICS_ACCELERATED: @@ -427,6 +446,32 @@ Factory::GetDirect3D10Device() return mD3D10Device; } +#ifdef USE_D2D1_1 +void +Factory::SetDirect3D11Device(ID3D11Device *aDevice) +{ + mD3D11Device = aDevice; + + RefPtr factory = D2DFactory1(); + + RefPtr device; + aDevice->QueryInterface((IDXGIDevice**)byRef(device)); + factory->CreateDevice(device, &mD2D1Device); +} + +ID3D11Device* +Factory::GetDirect3D11Device() +{ + return mD3D11Device; +} + +ID2D1Device* +Factory::GetD2D1Device() +{ + return mD2D1Device; +} +#endif + TemporaryRef Factory::CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams) { diff --git a/gfx/2d/GradientStopsD2D.h b/gfx/2d/GradientStopsD2D.h index fe5b4e25c63..40bdabcf2d2 100644 --- a/gfx/2d/GradientStopsD2D.h +++ b/gfx/2d/GradientStopsD2D.h @@ -24,6 +24,7 @@ public: private: friend class DrawTargetD2D; + friend class DrawTargetD2D1; mutable RefPtr mStopCollection; }; diff --git a/gfx/2d/HelpersD2D.h b/gfx/2d/HelpersD2D.h index cc836009d6e..720b4072e9a 100644 --- a/gfx/2d/HelpersD2D.h +++ b/gfx/2d/HelpersD2D.h @@ -6,7 +6,11 @@ #ifndef MOZILLA_GFX_HELPERSD2D_H_ #define MOZILLA_GFX_HELPERSD2D_H_ +#ifndef USE_D2D1_1 #include "moz-d2d1-1.h" +#else +#include +#endif #include @@ -26,6 +30,10 @@ namespace gfx { ID2D1Factory* D2DFactory(); +#ifdef USE_D2D1_1 +ID2D1Factory1* D2DFactory1(); +#endif + static inline D2D1_POINT_2F D2DPoint(const Point &aPoint) { return D2D1::Point2F(aPoint.x, aPoint.y); @@ -68,6 +76,18 @@ static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const Filter &aFilter) } } +#ifdef USE_D2D1_1 +static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const Filter &aFilter) +{ + switch (aFilter) { + case FILTER_POINT: + return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + default: + return D2D1_INTERPOLATION_MODE_LINEAR; + } +} +#endif + static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode) { switch (aMode) { @@ -152,6 +172,38 @@ static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat) return D2D1::PixelFormat(DXGIFormat(aFormat), AlphaMode(aFormat)); } +#ifdef USE_D2D1_1 +static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp) +{ + switch(aOp) { + case OP_OVER: + return D2D1_COMPOSITE_MODE_SOURCE_OVER; + case OP_ADD: + return D2D1_COMPOSITE_MODE_PLUS; + case OP_ATOP: + return D2D1_COMPOSITE_MODE_SOURCE_ATOP; + case OP_OUT: + return D2D1_COMPOSITE_MODE_SOURCE_OUT; + case OP_IN: + return D2D1_COMPOSITE_MODE_SOURCE_IN; + case OP_SOURCE: + return D2D1_COMPOSITE_MODE_SOURCE_COPY; + case OP_DEST_IN: + return D2D1_COMPOSITE_MODE_DESTINATION_IN; + case OP_DEST_OUT: + return D2D1_COMPOSITE_MODE_DESTINATION_OUT; + case OP_DEST_OVER: + return D2D1_COMPOSITE_MODE_DESTINATION_OVER; + case OP_DEST_ATOP: + return D2D1_COMPOSITE_MODE_DESTINATION_ATOP; + case OP_XOR: + return D2D1_COMPOSITE_MODE_XOR; + default: + return D2D1_COMPOSITE_MODE_SOURCE_OVER; + } +} +#endif + static inline bool IsPatternSupportedByD2D(const Pattern &aPattern) { if (aPattern.GetType() != PATTERN_RADIAL_GRADIENT) { diff --git a/gfx/2d/Makefile.in b/gfx/2d/Makefile.in index 46678d1a267..9f9b39c63af 100644 --- a/gfx/2d/Makefile.in +++ b/gfx/2d/Makefile.in @@ -42,6 +42,11 @@ DEFINES += -DWIN32 -DINITGUID ifdef MOZ_ENABLE_SKIA DEFINES += -DSKIA_IMPLEMENTATION=1 -DGR_IMPLEMENTATION=1 endif + +# For Direct2D 1.1 we require WINSDK_MAXVER 0x06020000 or higher. +ifdef MOZ_ENABLE_DIRECT2D1_1 +DEFINES += -DUSE_D2D1_1 +endif endif include $(topsrcdir)/config/rules.mk diff --git a/gfx/2d/PathD2D.h b/gfx/2d/PathD2D.h index 9b72b6cdb5c..84171efdf9b 100644 --- a/gfx/2d/PathD2D.h +++ b/gfx/2d/PathD2D.h @@ -6,8 +6,9 @@ #ifndef MOZILLA_GFX_PATHD2D_H_ #define MOZILLA_GFX_PATHD2D_H_ +#include + #include "2D.h" -#include "moz-d2d1-1.h" namespace mozilla { namespace gfx { @@ -90,6 +91,7 @@ public: private: friend class DrawTargetD2D; + friend class DrawTargetD2D1; mutable RefPtr mGeometry; bool mEndedActive; diff --git a/gfx/2d/ScaledFontDWrite.h b/gfx/2d/ScaledFontDWrite.h index 2f5cc94f5fc..5de1b4f7c41 100644 --- a/gfx/2d/ScaledFontDWrite.h +++ b/gfx/2d/ScaledFontDWrite.h @@ -57,6 +57,7 @@ public: private: friend class DrawTargetD2D; + friend class DrawTargetD2D1; RefPtr mParams; }; diff --git a/gfx/2d/SourceSurfaceD2D1.cpp b/gfx/2d/SourceSurfaceD2D1.cpp new file mode 100644 index 00000000000..a3054048a1c --- /dev/null +++ b/gfx/2d/SourceSurfaceD2D1.cpp @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SourceSurfaceD2D1.h" +#include "DrawTargetD2D1.h" +#include "Tools.h" + +namespace mozilla { +namespace gfx { + +SourceSurfaceD2D1::SourceSurfaceD2D1(ID2D1Image *aImage, ID2D1DeviceContext *aDC, + SurfaceFormat aFormat, const IntSize &aSize, + DrawTargetD2D1 *aDT) + : mImage(aImage) + , mDC(aDC) + , mDrawTarget(aDT) +{ + aImage->QueryInterface((ID2D1Bitmap1**)byRef(mRealizedBitmap)); + + mFormat = aFormat; + mSize = aSize; +} + +SourceSurfaceD2D1::~SourceSurfaceD2D1() +{ +} + +TemporaryRef +SourceSurfaceD2D1::GetDataSurface() +{ + EnsureRealizedBitmap(); + + RefPtr softwareBitmap; + D2D1_BITMAP_PROPERTIES1 props; + props.dpiX = 96; + props.dpiY = 96; + props.pixelFormat = D2DPixelFormat(mFormat); + props.colorContext = nullptr; + props.bitmapOptions = D2D1_BITMAP_OPTIONS_CANNOT_DRAW | + D2D1_BITMAP_OPTIONS_CPU_READ; + mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(softwareBitmap)); + + D2D1_POINT_2U point = D2D1::Point2U(0, 0); + D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height); + softwareBitmap->CopyFromBitmap(&point, mRealizedBitmap, &rect); + return new DataSourceSurfaceD2D1(softwareBitmap, mFormat); +} + +void +SourceSurfaceD2D1::EnsureRealizedBitmap() +{ + if (mRealizedBitmap) { + return; + } + + RefPtr dc; + Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, byRef(dc)); + + D2D1_BITMAP_PROPERTIES1 props; + props.dpiX = 96; + props.dpiY = 96; + props.pixelFormat = D2DPixelFormat(mFormat); + props.colorContext = nullptr; + props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET; + dc->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mRealizedBitmap)); + + dc->SetTarget(mRealizedBitmap); + + dc->BeginDraw(); + dc->DrawImage(mImage); + dc->EndDraw(); +} + +void +SourceSurfaceD2D1::DrawTargetWillChange() +{ + // At this point in time this should always be true here. + MOZ_ASSERT(mRealizedBitmap); + + RefPtr oldBitmap = mRealizedBitmap; + + D2D1_BITMAP_PROPERTIES1 props; + props.dpiX = 96; + props.dpiY = 96; + props.pixelFormat = D2DPixelFormat(mFormat); + props.colorContext = nullptr; + props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET; + mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mRealizedBitmap)); + + D2D1_POINT_2U point = D2D1::Point2U(0, 0); + D2D1_RECT_U rect = D2D1::RectU(0, 0, mSize.width, mSize.height); + mRealizedBitmap->CopyFromBitmap(&point, oldBitmap, &rect); + mImage = mRealizedBitmap; + + DrawTargetD2D1::mVRAMUsageSS += mSize.width * mSize.height * BytesPerPixel(mFormat); + mDrawTarget = nullptr; + + // We now no longer depend on the source surface content remaining the same. + MarkIndependent(); +} + +void +SourceSurfaceD2D1::MarkIndependent() +{ + if (mDrawTarget) { + MOZ_ASSERT(mDrawTarget->mSnapshot == this); + mDrawTarget->mSnapshot = nullptr; + mDrawTarget = nullptr; + } +} + +DataSourceSurfaceD2D1::DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat) + : mBitmap(aMappableBitmap) + , mFormat(aFormat) + , mMapped(false) +{ +} + +DataSourceSurfaceD2D1::~DataSourceSurfaceD2D1() +{ + if (mMapped) { + mBitmap->Unmap(); + } +} + +IntSize +DataSourceSurfaceD2D1::GetSize() const +{ + D2D1_SIZE_F size = mBitmap->GetSize(); + + return IntSize(int32_t(size.width), int32_t(size.height)); +} + +uint8_t* +DataSourceSurfaceD2D1::GetData() +{ + EnsureMapped(); + + return mMap.bits; +} + +int32_t +DataSourceSurfaceD2D1::Stride() +{ + EnsureMapped(); + + return mMap.pitch; +} + +void +DataSourceSurfaceD2D1::EnsureMapped() +{ + if (mMapped) { + return; + } + mBitmap->Map(D2D1_MAP_OPTIONS_READ, &mMap); + mMapped = true; +} + +} +} diff --git a/gfx/2d/SourceSurfaceD2D1.h b/gfx/2d/SourceSurfaceD2D1.h new file mode 100644 index 00000000000..9426c2123f9 --- /dev/null +++ b/gfx/2d/SourceSurfaceD2D1.h @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_SOURCESURFACED2DTARGET_H_ +#define MOZILLA_GFX_SOURCESURFACED2DTARGET_H_ + +#include "2D.h" +#include "HelpersD2D.h" +#include +#include +#include + +namespace mozilla { +namespace gfx { + +class DrawTargetD2D1; + +class SourceSurfaceD2D1 : public SourceSurface +{ +public: + SourceSurfaceD2D1(ID2D1Image* aImage, ID2D1DeviceContext *aDC, + SurfaceFormat aFormat, const IntSize &aSize, + DrawTargetD2D1 *aDT = nullptr); + ~SourceSurfaceD2D1(); + + virtual SurfaceType GetType() const { return SURFACE_D2D1_1_IMAGE; } + virtual IntSize GetSize() const { return mSize; } + virtual SurfaceFormat GetFormat() const { return mFormat; } + virtual TemporaryRef GetDataSurface(); + + ID2D1Image *GetImage() { return mImage; } + +private: + friend class DrawTargetD2D1; + + void EnsureRealizedBitmap(); + + // This function is called by the draw target this texture belongs to when + // it is about to be changed. The texture will be required to make a copy + // of itself when this happens. + void DrawTargetWillChange(); + + // This will mark the surface as no longer depending on its drawtarget, + // this may happen on destruction or copying. + void MarkIndependent(); + + RefPtr mImage; + // This may be null if we were created for a non-bitmap image and have not + // had a reason yet to realize ourselves. + RefPtr mRealizedBitmap; + RefPtr mDC; + + SurfaceFormat mFormat; + IntSize mSize; + RefPtr mDrawTarget; +}; + +class DataSourceSurfaceD2D1 : public DataSourceSurface +{ +public: + DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat); + ~DataSourceSurfaceD2D1(); + + virtual SurfaceType GetType() const { return SURFACE_DATA; } + virtual IntSize GetSize() const; + virtual SurfaceFormat GetFormat() const { return mFormat; } + virtual uint8_t *GetData(); + virtual int32_t Stride(); + +private: + friend class SourceSurfaceD2DTarget; + void EnsureMapped(); + + mutable RefPtr mBitmap; + SurfaceFormat mFormat; + D2D1_MAPPED_RECT mMap; + bool mMapped; +}; + +} +} + +#endif /* MOZILLA_GFX_SOURCESURFACED2D2TARGET_H_ */ diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index 5259dc7e8ab..7010e7c5bb7 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -27,6 +27,7 @@ enum SurfaceType SURFACE_COREGRAPHICS_CGCONTEXT, /* Surface wrapping a CG context */ SURFACE_SKIA, /* Surface wrapping a Skia bitmap */ SURFACE_DUAL_DT, /* Snapshot of a dual drawtarget */ + SURFACE_D2D1_1_IMAGE, /* A D2D 1.1 ID2D1Image SourceSurface */ SURFACE_RECORDING /* Surface used for recording */ }; @@ -50,7 +51,8 @@ enum BackendType BACKEND_COREGRAPHICS_ACCELERATED, BACKEND_CAIRO, BACKEND_SKIA, - BACKEND_RECORDING + BACKEND_RECORDING, + BACKEND_DIRECT2D1_1 }; enum FontType diff --git a/gfx/2d/moz.build b/gfx/2d/moz.build index fa9cdd0b169..4f5e45e8462 100644 --- a/gfx/2d/moz.build +++ b/gfx/2d/moz.build @@ -51,7 +51,9 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': ] if CONFIG['MOZ_ENABLE_DIRECT2D1_1']: CPP_SOURCES += [ - 'RadialGradientEffectD2D1.cpp' + 'RadialGradientEffectD2D1.cpp', + 'DrawTargetD2D1.cpp', + 'SourceSurfaceD2D1.cpp' ] if CONFIG['MOZ_ENABLE_SKIA']: CPP_SOURCES += [ diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 11d7711c796..9ca128894af 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -133,6 +133,8 @@ GetBackendName(mozilla::gfx::BackendType aBackend) return "skia"; case mozilla::gfx::BACKEND_RECORDING: return "recording"; + case mozilla::gfx::BACKEND_DIRECT2D1_1: + return "direct2d 1.1"; case mozilla::gfx::BACKEND_NONE: return "none"; } From 48676fdbcbfbcdc3bcb45ca753094ee2c0352683 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Wed, 17 Jul 2013 14:16:17 +0200 Subject: [PATCH 06/31] Relanding after backout Bug 891695 - IonMonkey: Avoid passing pointers by const reference. r=rpearl --- js/src/ion/MIR.cpp | 8 ++-- js/src/ion/MIR.h | 104 ++++++++++++++++++++++----------------------- js/src/jsapi.h | 2 +- js/src/vm/Shape.h | 2 +- 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/js/src/ion/MIR.cpp b/js/src/ion/MIR.cpp index ebcb5870fd0..5171f13f1c3 100644 --- a/js/src/ion/MIR.cpp +++ b/js/src/ion/MIR.cpp @@ -153,7 +153,7 @@ MDefinition::valueHash() const } bool -MDefinition::congruentIfOperandsEqual(MDefinition * const &ins) const +MDefinition::congruentIfOperandsEqual(MDefinition *ins) const { if (numOperands() != ins->numOperands()) return false; @@ -387,7 +387,7 @@ MConstant::valueHash() const return (HashNumber)JSVAL_TO_IMPL(value_).asBits; } bool -MConstant::congruentTo(MDefinition * const &ins) const +MConstant::congruentTo(MDefinition *ins) const { if (!ins->isConstant()) return false; @@ -488,7 +488,7 @@ MParameter::valueHash() const } bool -MParameter::congruentTo(MDefinition * const &ins) const +MParameter::congruentTo(MDefinition *ins) const { if (!ins->isParameter()) return false; @@ -641,7 +641,7 @@ MPhi::foldsTo(bool useValueNumbers) } bool -MPhi::congruentTo(MDefinition *const &ins) const +MPhi::congruentTo(MDefinition *ins) const { if (!ins->isPhi()) return false; diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 0b96632b3d4..8ff56b963b1 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -357,10 +357,10 @@ class MDefinition : public MNode } virtual HashNumber valueHash() const; - virtual bool congruentTo(MDefinition* const &ins) const { + virtual bool congruentTo(MDefinition *ins) const { return false; } - bool congruentIfOperandsEqual(MDefinition * const &ins) const; + bool congruentIfOperandsEqual(MDefinition *ins) const; virtual MDefinition *foldsTo(bool useValueNumbers); virtual void analyzeEdgeCasesForward(); virtual void analyzeEdgeCasesBackward(); @@ -753,7 +753,7 @@ class MConstant : public MNullaryInstruction void printOpcode(FILE *fp) const; HashNumber valueHash() const; - bool congruentTo(MDefinition * const &ins) const; + bool congruentTo(MDefinition *ins) const; AliasSet getAliasSet() const { return AliasSet::None(); @@ -787,7 +787,7 @@ class MParameter : public MNullaryInstruction void printOpcode(FILE *fp) const; HashNumber valueHash() const; - bool congruentTo(MDefinition * const &ins) const; + bool congruentTo(MDefinition *ins) const; }; class MCallee : public MNullaryInstruction @@ -802,7 +802,7 @@ class MCallee : public MNullaryInstruction public: INSTRUCTION_HEADER(Callee) - bool congruentTo(MDefinition * const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } @@ -1677,7 +1677,7 @@ class MBinaryInstruction : public MAryInstruction<2> replaceOperand(1, temp); } - bool congruentTo(MDefinition *const &ins) const + bool congruentTo(MDefinition *ins) const { if (op() != ins->op()) return false; @@ -1737,7 +1737,7 @@ class MTernaryInstruction : public MAryInstruction<3> return op() ^ first->valueNumber() ^ second->valueNumber() ^ third->valueNumber(); } - bool congruentTo(MDefinition *const &ins) const + bool congruentTo(MDefinition *ins) const { if (op() != ins->op()) return false; @@ -1786,7 +1786,7 @@ class MQuaternaryInstruction : public MAryInstruction<4> third->valueNumber() ^ fourth->valueNumber(); } - bool congruentTo(MDefinition *const &ins) const + bool congruentTo(MDefinition *ins) const { if (op() != ins->op()) return false; @@ -1933,7 +1933,7 @@ class MCompare void printOpcode(FILE *fp) const; protected: - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!MBinaryInstruction::congruentTo(ins)) return false; return compareType() == ins->toCompare()->compareType() && @@ -1969,7 +1969,7 @@ class MBox : public MUnaryInstruction return new MBox(ins); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2048,7 +2048,7 @@ class MUnbox : public MUnaryInstruction, public BoxInputsPolicy bool fallible() const { return mode() != Infallible; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isUnbox() || ins->toUnbox()->mode() != mode()) return false; return congruentIfOperandsEqual(ins); @@ -2454,7 +2454,7 @@ class MToDouble } MDefinition *foldsTo(bool useValueNumbers); - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isToDouble() || ins->toToDouble()->conversion() != conversion()) return false; return congruentIfOperandsEqual(ins); @@ -2486,7 +2486,7 @@ class MAsmJSUnsignedToDouble } MDefinition *foldsTo(bool useValueNumbers); - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2528,7 +2528,7 @@ class MToInt32 : public MUnaryInstruction canBeNegativeZero_ = negativeZero; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } @@ -2560,7 +2560,7 @@ class MTruncateToInt32 : public MUnaryInstruction MDefinition *foldsTo(bool useValueNumbers); - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2590,7 +2590,7 @@ class MToString : public MUnaryInstruction MDefinition *foldsTo(bool useValueNumbers); - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2623,7 +2623,7 @@ class MBitNot MDefinition *foldsTo(bool useValueNumbers); void infer(); - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2719,7 +2719,7 @@ class MBinaryBitwiseInstruction virtual MDefinition *foldIfEqual() = 0; virtual void infer(BaselineInspector *inspector, jsbytecode *pc); - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2933,7 +2933,7 @@ class MBinaryArithInstruction setResultType(MIRType_Int32); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return MBinaryInstruction::congruentTo(ins); } AliasSet getAliasSet() const { @@ -2982,7 +2982,7 @@ class MMinMax TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isMinMax()) return false; if (isMax() != ins->toMinMax()->isMax()) @@ -3028,7 +3028,7 @@ class MAbs TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } bool fallible() const; @@ -3066,7 +3066,7 @@ class MSqrt TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } @@ -3105,7 +3105,7 @@ class MAtan2 return this; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } @@ -3139,7 +3139,7 @@ class MPow MDefinition *power() const { return rhs(); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } TypePolicy *typePolicy() { @@ -3167,7 +3167,7 @@ class MPowHalf static MPowHalf *New(MDefinition *input) { return new MPowHalf(input); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } TypePolicy *typePolicy() { @@ -3251,7 +3251,7 @@ class MMathFunction TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isMathFunction()) return false; if (ins->toMathFunction()->function() != function()) @@ -3535,7 +3535,7 @@ class MConcat TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -3578,7 +3578,7 @@ class MParConcat TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -3728,7 +3728,7 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode MDefinition *foldsTo(bool useValueNumbers); - bool congruentTo(MDefinition * const &ins) const; + bool congruentTo(MDefinition *ins) const; bool isIterator() const { return isIterator_; @@ -4157,7 +4157,7 @@ class MSlots MDefinition *object() const { return getOperand(0); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4190,7 +4190,7 @@ class MElements MDefinition *object() const { return getOperand(0); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4227,7 +4227,7 @@ class MConstantElements : public MNullaryInstruction return (HashNumber)(size_t) value_; } - bool congruentTo(MDefinition * const &ins) const { + bool congruentTo(MDefinition *ins) const { return ins->isConstantElements() && ins->toConstantElements()->value() == value(); } @@ -4258,7 +4258,7 @@ class MConvertElementsToDoubles MDefinition *elements() const { return getOperand(0); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4304,7 +4304,7 @@ class MMaybeToDoubleElement MDefinition *value() const { return getOperand(1); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4333,7 +4333,7 @@ class MInitializedLength MDefinition *elements() const { return getOperand(0); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4386,7 +4386,7 @@ class MArrayLength MDefinition *elements() const { return getOperand(0); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4419,7 +4419,7 @@ class MTypedArrayLength MDefinition *object() const { return getOperand(0); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4454,7 +4454,7 @@ class MTypedArrayElements MDefinition *object() const { return getOperand(0); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4557,7 +4557,7 @@ class MBoundsCheck void setMaximum(int32_t n) { maximum_ = n; } - bool congruentTo(MDefinition * const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isBoundsCheck()) return false; MBoundsCheck *other = ins->toBoundsCheck(); @@ -5303,7 +5303,7 @@ class MClampToUint8 TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -5343,7 +5343,7 @@ class MLoadFixedSlot size_t slot() const { return slot_; } - bool congruentTo(MDefinition * const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isLoadFixedSlot()) return false; if (slot() != ins->toLoadFixedSlot()->slot()) @@ -5534,7 +5534,7 @@ class MGetPropertyCache } TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition * const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!idempotent_) return false; if (!ins->isGetPropertyCache()) @@ -5591,7 +5591,7 @@ class MGetPropertyPolymorphic return new MGetPropertyPolymorphic(obj, name); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isGetPropertyPolymorphic()) return false; if (name() != ins->toGetPropertyPolymorphic()->name()) @@ -6107,7 +6107,7 @@ class MGuardShape BailoutKind bailoutKind() const { return bailoutKind_; } - bool congruentTo(MDefinition * const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isGuardShape()) return false; if (shape() != ins->toGuardShape()->shape()) @@ -6159,7 +6159,7 @@ class MGuardObjectType bool bailOnEquality() const { return bailOnEquality_; } - bool congruentTo(MDefinition * const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isGuardObjectType()) return false; if (typeObject() != ins->toGuardObjectType()->typeObject()) @@ -6204,7 +6204,7 @@ class MGuardClass const Class *getClass() const { return class_; } - bool congruentTo(MDefinition * const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isGuardClass()) return false; if (getClass() != ins->toGuardClass()->getClass()) @@ -6249,7 +6249,7 @@ class MLoadSlot return slot_; } - bool congruentTo(MDefinition * const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!ins->isLoadSlot()) return false; if (slot() != ins->toLoadSlot()->slot()) @@ -6856,7 +6856,7 @@ class MGetDOMProperty return this; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { if (!isDomPure()) return false; @@ -6910,7 +6910,7 @@ class MStringLength MDefinition *string() const { return getOperand(0); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -7209,7 +7209,7 @@ class MArgumentsLength : public MNullaryInstruction return new MArgumentsLength(); } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -7244,7 +7244,7 @@ class MGetArgument TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *const &ins) const { + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -7436,7 +7436,7 @@ class MTypeBarrier return this; } - bool congruentTo(MDefinition * const &def) const { + bool congruentTo(MDefinition *def) const { return false; } BailoutKind bailoutKind() const { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index e1c0b39990a..9004cc8e1bd 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2576,7 +2576,7 @@ JS_CallGenericTracer(JSTracer *trc, void *gcthing, const char *name); template inline void -JS_CallHashSetObjectTracer(JSTracer *trc, HashSetEnum &e, JSObject *const &key, const char *name) +JS_CallHashSetObjectTracer(JSTracer *trc, HashSetEnum &e, JSObject *key, const char *name) { JSObject *updated = key; JS_SET_TRACING_LOCATION(trc, reinterpret_cast(&const_cast(key))); diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index d506c60470d..b0ac2cc701f 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -994,7 +994,7 @@ struct StackShape JS_ASSERT(slot <= SHAPE_INVALID_SLOT); } - StackShape(Shape *const &shape) + StackShape(Shape *shape) : base(shape->base()->unowned()), propid(shape->propidRef()), slot_(shape->slotInfo & Shape::SLOT_MASK), From abd55eab3977c4cebd703fc307895d27a9766f87 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Wed, 17 Jul 2013 08:54:01 -0400 Subject: [PATCH 07/31] Backed out changeset 56423bbe9a13 (bug 891695) because it didn't un-bust itself automagically. --- js/src/ion/MIR.cpp | 8 ++-- js/src/ion/MIR.h | 104 ++++++++++++++++++++++----------------------- js/src/jsapi.h | 2 +- js/src/vm/Shape.h | 2 +- 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/js/src/ion/MIR.cpp b/js/src/ion/MIR.cpp index 5171f13f1c3..ebcb5870fd0 100644 --- a/js/src/ion/MIR.cpp +++ b/js/src/ion/MIR.cpp @@ -153,7 +153,7 @@ MDefinition::valueHash() const } bool -MDefinition::congruentIfOperandsEqual(MDefinition *ins) const +MDefinition::congruentIfOperandsEqual(MDefinition * const &ins) const { if (numOperands() != ins->numOperands()) return false; @@ -387,7 +387,7 @@ MConstant::valueHash() const return (HashNumber)JSVAL_TO_IMPL(value_).asBits; } bool -MConstant::congruentTo(MDefinition *ins) const +MConstant::congruentTo(MDefinition * const &ins) const { if (!ins->isConstant()) return false; @@ -488,7 +488,7 @@ MParameter::valueHash() const } bool -MParameter::congruentTo(MDefinition *ins) const +MParameter::congruentTo(MDefinition * const &ins) const { if (!ins->isParameter()) return false; @@ -641,7 +641,7 @@ MPhi::foldsTo(bool useValueNumbers) } bool -MPhi::congruentTo(MDefinition *ins) const +MPhi::congruentTo(MDefinition *const &ins) const { if (!ins->isPhi()) return false; diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 8ff56b963b1..0b96632b3d4 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -357,10 +357,10 @@ class MDefinition : public MNode } virtual HashNumber valueHash() const; - virtual bool congruentTo(MDefinition *ins) const { + virtual bool congruentTo(MDefinition* const &ins) const { return false; } - bool congruentIfOperandsEqual(MDefinition *ins) const; + bool congruentIfOperandsEqual(MDefinition * const &ins) const; virtual MDefinition *foldsTo(bool useValueNumbers); virtual void analyzeEdgeCasesForward(); virtual void analyzeEdgeCasesBackward(); @@ -753,7 +753,7 @@ class MConstant : public MNullaryInstruction void printOpcode(FILE *fp) const; HashNumber valueHash() const; - bool congruentTo(MDefinition *ins) const; + bool congruentTo(MDefinition * const &ins) const; AliasSet getAliasSet() const { return AliasSet::None(); @@ -787,7 +787,7 @@ class MParameter : public MNullaryInstruction void printOpcode(FILE *fp) const; HashNumber valueHash() const; - bool congruentTo(MDefinition *ins) const; + bool congruentTo(MDefinition * const &ins) const; }; class MCallee : public MNullaryInstruction @@ -802,7 +802,7 @@ class MCallee : public MNullaryInstruction public: INSTRUCTION_HEADER(Callee) - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition * const &ins) const { return congruentIfOperandsEqual(ins); } @@ -1677,7 +1677,7 @@ class MBinaryInstruction : public MAryInstruction<2> replaceOperand(1, temp); } - bool congruentTo(MDefinition *ins) const + bool congruentTo(MDefinition *const &ins) const { if (op() != ins->op()) return false; @@ -1737,7 +1737,7 @@ class MTernaryInstruction : public MAryInstruction<3> return op() ^ first->valueNumber() ^ second->valueNumber() ^ third->valueNumber(); } - bool congruentTo(MDefinition *ins) const + bool congruentTo(MDefinition *const &ins) const { if (op() != ins->op()) return false; @@ -1786,7 +1786,7 @@ class MQuaternaryInstruction : public MAryInstruction<4> third->valueNumber() ^ fourth->valueNumber(); } - bool congruentTo(MDefinition *ins) const + bool congruentTo(MDefinition *const &ins) const { if (op() != ins->op()) return false; @@ -1933,7 +1933,7 @@ class MCompare void printOpcode(FILE *fp) const; protected: - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { if (!MBinaryInstruction::congruentTo(ins)) return false; return compareType() == ins->toCompare()->compareType() && @@ -1969,7 +1969,7 @@ class MBox : public MUnaryInstruction return new MBox(ins); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2048,7 +2048,7 @@ class MUnbox : public MUnaryInstruction, public BoxInputsPolicy bool fallible() const { return mode() != Infallible; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { if (!ins->isUnbox() || ins->toUnbox()->mode() != mode()) return false; return congruentIfOperandsEqual(ins); @@ -2454,7 +2454,7 @@ class MToDouble } MDefinition *foldsTo(bool useValueNumbers); - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { if (!ins->isToDouble() || ins->toToDouble()->conversion() != conversion()) return false; return congruentIfOperandsEqual(ins); @@ -2486,7 +2486,7 @@ class MAsmJSUnsignedToDouble } MDefinition *foldsTo(bool useValueNumbers); - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2528,7 +2528,7 @@ class MToInt32 : public MUnaryInstruction canBeNegativeZero_ = negativeZero; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } @@ -2560,7 +2560,7 @@ class MTruncateToInt32 : public MUnaryInstruction MDefinition *foldsTo(bool useValueNumbers); - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2590,7 +2590,7 @@ class MToString : public MUnaryInstruction MDefinition *foldsTo(bool useValueNumbers); - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2623,7 +2623,7 @@ class MBitNot MDefinition *foldsTo(bool useValueNumbers); void infer(); - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2719,7 +2719,7 @@ class MBinaryBitwiseInstruction virtual MDefinition *foldIfEqual() = 0; virtual void infer(BaselineInspector *inspector, jsbytecode *pc); - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -2933,7 +2933,7 @@ class MBinaryArithInstruction setResultType(MIRType_Int32); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return MBinaryInstruction::congruentTo(ins); } AliasSet getAliasSet() const { @@ -2982,7 +2982,7 @@ class MMinMax TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { if (!ins->isMinMax()) return false; if (isMax() != ins->toMinMax()->isMax()) @@ -3028,7 +3028,7 @@ class MAbs TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } bool fallible() const; @@ -3066,7 +3066,7 @@ class MSqrt TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } @@ -3105,7 +3105,7 @@ class MAtan2 return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } @@ -3139,7 +3139,7 @@ class MPow MDefinition *power() const { return rhs(); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } TypePolicy *typePolicy() { @@ -3167,7 +3167,7 @@ class MPowHalf static MPowHalf *New(MDefinition *input) { return new MPowHalf(input); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } TypePolicy *typePolicy() { @@ -3251,7 +3251,7 @@ class MMathFunction TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { if (!ins->isMathFunction()) return false; if (ins->toMathFunction()->function() != function()) @@ -3535,7 +3535,7 @@ class MConcat TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -3578,7 +3578,7 @@ class MParConcat TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -3728,7 +3728,7 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode MDefinition *foldsTo(bool useValueNumbers); - bool congruentTo(MDefinition *ins) const; + bool congruentTo(MDefinition * const &ins) const; bool isIterator() const { return isIterator_; @@ -4157,7 +4157,7 @@ class MSlots MDefinition *object() const { return getOperand(0); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4190,7 +4190,7 @@ class MElements MDefinition *object() const { return getOperand(0); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4227,7 +4227,7 @@ class MConstantElements : public MNullaryInstruction return (HashNumber)(size_t) value_; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition * const &ins) const { return ins->isConstantElements() && ins->toConstantElements()->value() == value(); } @@ -4258,7 +4258,7 @@ class MConvertElementsToDoubles MDefinition *elements() const { return getOperand(0); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4304,7 +4304,7 @@ class MMaybeToDoubleElement MDefinition *value() const { return getOperand(1); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4333,7 +4333,7 @@ class MInitializedLength MDefinition *elements() const { return getOperand(0); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4386,7 +4386,7 @@ class MArrayLength MDefinition *elements() const { return getOperand(0); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4419,7 +4419,7 @@ class MTypedArrayLength MDefinition *object() const { return getOperand(0); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4454,7 +4454,7 @@ class MTypedArrayElements MDefinition *object() const { return getOperand(0); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -4557,7 +4557,7 @@ class MBoundsCheck void setMaximum(int32_t n) { maximum_ = n; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition * const &ins) const { if (!ins->isBoundsCheck()) return false; MBoundsCheck *other = ins->toBoundsCheck(); @@ -5303,7 +5303,7 @@ class MClampToUint8 TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -5343,7 +5343,7 @@ class MLoadFixedSlot size_t slot() const { return slot_; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition * const &ins) const { if (!ins->isLoadFixedSlot()) return false; if (slot() != ins->toLoadFixedSlot()->slot()) @@ -5534,7 +5534,7 @@ class MGetPropertyCache } TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition * const &ins) const { if (!idempotent_) return false; if (!ins->isGetPropertyCache()) @@ -5591,7 +5591,7 @@ class MGetPropertyPolymorphic return new MGetPropertyPolymorphic(obj, name); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { if (!ins->isGetPropertyPolymorphic()) return false; if (name() != ins->toGetPropertyPolymorphic()->name()) @@ -6107,7 +6107,7 @@ class MGuardShape BailoutKind bailoutKind() const { return bailoutKind_; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition * const &ins) const { if (!ins->isGuardShape()) return false; if (shape() != ins->toGuardShape()->shape()) @@ -6159,7 +6159,7 @@ class MGuardObjectType bool bailOnEquality() const { return bailOnEquality_; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition * const &ins) const { if (!ins->isGuardObjectType()) return false; if (typeObject() != ins->toGuardObjectType()->typeObject()) @@ -6204,7 +6204,7 @@ class MGuardClass const Class *getClass() const { return class_; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition * const &ins) const { if (!ins->isGuardClass()) return false; if (getClass() != ins->toGuardClass()->getClass()) @@ -6249,7 +6249,7 @@ class MLoadSlot return slot_; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition * const &ins) const { if (!ins->isLoadSlot()) return false; if (slot() != ins->toLoadSlot()->slot()) @@ -6856,7 +6856,7 @@ class MGetDOMProperty return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { if (!isDomPure()) return false; @@ -6910,7 +6910,7 @@ class MStringLength MDefinition *string() const { return getOperand(0); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -7209,7 +7209,7 @@ class MArgumentsLength : public MNullaryInstruction return new MArgumentsLength(); } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -7244,7 +7244,7 @@ class MGetArgument TypePolicy *typePolicy() { return this; } - bool congruentTo(MDefinition *ins) const { + bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { @@ -7436,7 +7436,7 @@ class MTypeBarrier return this; } - bool congruentTo(MDefinition *def) const { + bool congruentTo(MDefinition * const &def) const { return false; } BailoutKind bailoutKind() const { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 9004cc8e1bd..e1c0b39990a 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2576,7 +2576,7 @@ JS_CallGenericTracer(JSTracer *trc, void *gcthing, const char *name); template inline void -JS_CallHashSetObjectTracer(JSTracer *trc, HashSetEnum &e, JSObject *key, const char *name) +JS_CallHashSetObjectTracer(JSTracer *trc, HashSetEnum &e, JSObject *const &key, const char *name) { JSObject *updated = key; JS_SET_TRACING_LOCATION(trc, reinterpret_cast(&const_cast(key))); diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index b0ac2cc701f..d506c60470d 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -994,7 +994,7 @@ struct StackShape JS_ASSERT(slot <= SHAPE_INVALID_SLOT); } - StackShape(Shape *shape) + StackShape(Shape *const &shape) : base(shape->base()->unowned()), propid(shape->propidRef()), slot_(shape->slotInfo & Shape::SLOT_MASK), From 74efe04844fc212f546a634106e4b7c14022134f Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Tue, 16 Jul 2013 18:54:47 -0600 Subject: [PATCH 08/31] Bug 894669 - Add analysis for finding variables unnecessarily entrained by inner functions, r=luke. --- js/src/shell/js.cpp | 16 ++++ js/src/vm/ScopeObject.cpp | 172 ++++++++++++++++++++++++++++++++++++++ js/src/vm/ScopeObject.h | 5 ++ 3 files changed, 193 insertions(+) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 3018137125b..73ec489d74e 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -172,6 +172,7 @@ static bool compileOnly = false; static bool fuzzingSafe = false; #ifdef DEBUG +static bool dumpEntrainedVariables = false; static bool OOM_printAllocationCount = false; #endif @@ -428,6 +429,12 @@ RunFile(JSContext *cx, Handle obj, const char *filename, FILE *file, RootedScript script(cx); script = JS::Compile(cx, obj, options, file); + +#ifdef DEBUG + if (dumpEntrainedVariables) + AnalyzeEntrainedVariables(cx, script); +#endif + JS_SetOptions(cx, oldopts); JS_ASSERT_IF(!script, gGotError); if (script && !compileOnly) { @@ -5069,6 +5076,11 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op) #endif /* JS_ION */ +#ifdef DEBUG + if (op->getBoolOption("dump-entrained-variables")) + dumpEntrainedVariables = true; +#endif + /* |scriptArgs| gets bound on the global before any code is run. */ if (!BindScriptArgs(cx, obj, op)) return EXIT_FAILURE; @@ -5302,6 +5314,10 @@ main(int argc, char **argv, char **envp) "to test JIT codegen (no-op on platforms other than x86).") || !op.addBoolOption('\0', "fuzzing-safe", "Don't expose functions that aren't safe for " "fuzzers to call") +#ifdef DEBUG + || !op.addBoolOption('\0', "dump-entrained-variables", "Print variables which are " + "unnecessarily entrained by inner functions") +#endif #ifdef JSGC_GENERATIONAL || !op.addBoolOption('\0', "no-ggc", "Disable Generational GC") #endif diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index a292fd38e29..25deafbcf98 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -2192,3 +2192,175 @@ js::GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame) ScopeIter si(frame, cx); return GetDebugScope(cx, si); } + +#ifdef DEBUG + +typedef HashSet PropertyNameSet; + +static bool +RemoveReferencedNames(JSContext *cx, HandleScript script, PropertyNameSet &remainingNames) +{ + // Remove from remainingNames --- the closure variables in some outer + // script --- any free variables in this script. This analysis isn't perfect: + // + // - It will not account for free variables in an inner script which are + // actually accessing some name in an intermediate script between the + // inner and outer scripts. This can cause remainingNames to be an + // underapproximation. + // + // - It will not account for new names introduced via eval. This can cause + // remainingNames to be an overapproximation. This would be easy to fix + // but is nice to have as the eval will probably not access these + // these names and putting eval in an inner script is bad news if you + // care about entraining variables unnecessarily. + + for (jsbytecode *pc = script->code; + pc != script->code + script->length; + pc += GetBytecodeLength(pc)) + { + PropertyName *name; + + switch (JSOp(*pc)) { + case JSOP_NAME: + case JSOP_CALLNAME: + case JSOP_SETNAME: + name = script->getName(pc); + break; + + case JSOP_GETALIASEDVAR: + case JSOP_CALLALIASEDVAR: + case JSOP_SETALIASEDVAR: + name = ScopeCoordinateName(cx, script, pc); + break; + + default: + name = NULL; + break; + } + + if (name) + remainingNames.remove(name); + } + + if (script->hasObjects()) { + ObjectArray *objects = script->objects(); + for (size_t i = 0; i < objects->length; i++) { + JSObject *obj = objects->vector[i]; + if (obj->is() && obj->as().isInterpreted()) { + JSFunction *fun = &obj->as(); + RootedScript innerScript(cx, fun->getOrCreateScript(cx)); + if (!innerScript) + return false; + + if (!RemoveReferencedNames(cx, innerScript, remainingNames)) + return false; + } + } + } + + return true; +} + +static bool +AnalyzeEntrainedVariablesInScript(JSContext *cx, HandleScript script, HandleScript innerScript) +{ + PropertyNameSet remainingNames(cx); + if (!remainingNames.init()) + return false; + + for (BindingIter bi(script); bi; bi++) { + if (bi->aliased()) { + PropertyNameSet::AddPtr p = remainingNames.lookupForAdd(bi->name()); + if (!p && !remainingNames.add(p, bi->name())) + return false; + } + } + + if (!RemoveReferencedNames(cx, innerScript, remainingNames)) + return false; + + if (!remainingNames.empty()) { + Sprinter buf(cx); + if (!buf.init()) + return false; + + buf.printf("Script "); + + if (JSAtom *name = script->function()->displayAtom()) { + buf.putString(name); + buf.printf(" "); + } + + buf.printf("(%s:%d) has variables entrained by ", script->filename(), script->lineno); + + if (JSAtom *name = innerScript->function()->displayAtom()) { + buf.putString(name); + buf.printf(" "); + } + + buf.printf("(%s:%d) ::", innerScript->filename(), innerScript->lineno); + + for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); r.popFront()) { + buf.printf(" "); + buf.putString(r.front()); + } + + printf("%s\n", buf.string()); + } + + if (innerScript->hasObjects()) { + ObjectArray *objects = innerScript->objects(); + for (size_t i = 0; i < objects->length; i++) { + JSObject *obj = objects->vector[i]; + if (obj->is() && obj->as().isInterpreted()) { + JSFunction *fun = &obj->as(); + RootedScript innerInnerScript(cx, fun->nonLazyScript()); + if (!AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript)) + return false; + } + } + } + + return true; +} + +// Look for local variables in script or any other script inner to it, which are +// part of the script's call object and are unnecessarily entrained by their own +// inner scripts which do not refer to those variables. An example is: +// +// function foo() { +// var a, b; +// function bar() { return a; } +// function baz() { return b; } +// } +// +// |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|. +bool +js::AnalyzeEntrainedVariables(JSContext *cx, HandleScript script) +{ + if (!script->hasObjects()) + return true; + + ObjectArray *objects = script->objects(); + for (size_t i = 0; i < objects->length; i++) { + JSObject *obj = objects->vector[i]; + if (obj->is() && obj->as().isInterpreted()) { + JSFunction *fun = &obj->as(); + RootedScript innerScript(cx, fun->getOrCreateScript(cx)); + if (!innerScript) + return false; + + if (script->function() && script->function()->isHeavyweight()) { + if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript)) + return false; + } + + if (!AnalyzeEntrainedVariables(cx, innerScript)) + return false; + } + } + + return true; +} + +#endif diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 5a395de1d59..aa75d5f4501 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -747,6 +747,11 @@ StaticBlockObject::enclosingBlock() const return obj && obj->is() ? &obj->as() : NULL; } +#ifdef DEBUG +bool +AnalyzeEntrainedVariables(JSContext *cx, HandleScript script); +#endif + } // namespace js #endif /* vm_ScopeObject_h */ From 67669b264e576abfbcd7cd3ab5f789ee0e31aaf0 Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Wed, 17 Jul 2013 09:11:19 -0400 Subject: [PATCH 09/31] Bug 887706 - Use a dynamic port in httpserver xpcshell tests so they can be run in parallel. r=Waldo --- .../test/test_basic_functionality.js | 37 ++- .../test/httpserver/test/test_body_length.js | 14 +- .../test/httpserver/test/test_byte_range.js | 14 +- .../test/httpserver/test/test_cern_meta.js | 34 ++- .../test/test_default_index_handler.js | 32 +- .../test/httpserver/test/test_empty_body.js | 19 +- .../test/test_errorhandler_exception.js | 15 +- .../test/httpserver/test/test_header_array.js | 15 +- .../test/httpserver/test/test_name_scheme.js | 41 +-- .../test/httpserver/test/test_processasync.js | 44 ++- netwerk/test/httpserver/test/test_qi.js | 19 +- .../httpserver/test/test_registerdirectory.js | 289 ++++++++---------- .../test/httpserver/test/test_registerfile.js | 16 +- .../httpserver/test/test_registerprefix.js | 40 ++- .../test_request_line_split_in_two_packets.js | 16 +- .../httpserver/test/test_response_write.js | 15 +- .../test/httpserver/test/test_seizepower.js | 37 ++- .../httpserver/test/test_setindexhandler.js | 21 +- .../httpserver/test/test_setstatusline.js | 45 ++- netwerk/test/httpserver/test/test_sjs.js | 21 +- .../httpserver/test/test_sjs_object_state.js | 7 +- .../test/httpserver/test/test_sjs_state.js | 89 ++---- .../test/test_sjs_throwing_exceptions.js | 29 +- .../test/httpserver/test/test_start_stop.js | 11 +- netwerk/test/httpserver/test/xpcshell.ini | 1 + 25 files changed, 440 insertions(+), 481 deletions(-) diff --git a/netwerk/test/httpserver/test/test_basic_functionality.js b/netwerk/test/httpserver/test/test_basic_functionality.js index f29d34630dc..00b47ea8ae7 100644 --- a/netwerk/test/httpserver/test/test_basic_functionality.js +++ b/netwerk/test/httpserver/test/test_basic_functionality.js @@ -8,21 +8,28 @@ * Basic functionality test, from the client programmer's POV. */ -var tests = - [ - new Test("http://localhost:4444/objHandler", - null, start_objHandler, null), - new Test("http://localhost:4444/functionHandler", - null, start_functionHandler, null), - new Test("http://localhost:4444/nonexistent-path", - null, start_non_existent_path, null), - new Test("http://localhost:4444/lotsOfHeaders", - null, start_lots_of_headers, null), +XPCOMUtils.defineLazyGetter(this, "port", function() { + return srv.identity.primaryPort; +}); + +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test("http://localhost:" + port + "/objHandler", + null, start_objHandler, null), + new Test("http://localhost:" + port + "/functionHandler", + null, start_functionHandler, null), + new Test("http://localhost:" + port + "/nonexistent-path", + null, start_non_existent_path, null), + new Test("http://localhost:" + port + "/lotsOfHeaders", + null, start_lots_of_headers, null), ]; +}); + +var srv; function run_test() { - var srv = createServer(); + srv = createServer(); // base path // XXX should actually test this works with a file by comparing streams! @@ -36,7 +43,7 @@ function run_test() srv.registerPathHandler("/functionHandler", functionHandler); srv.registerPathHandler("/lotsOfHeaders", lotsOfHeadersHandler); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } @@ -118,13 +125,13 @@ var objHandler = var body = "Request (slightly reformatted):\n\n"; body += metadata.method + " " + metadata.path; - do_check_eq(metadata.port, 4444); + do_check_eq(metadata.port, port); if (metadata.queryString) body += "?" + metadata.queryString; body += " HTTP/" + metadata.httpVersion + "\n"; - + var headEnum = metadata.headers; while (headEnum.hasMoreElements()) { @@ -150,7 +157,7 @@ function functionHandler(metadata, response) response.setStatusLine("1.1", 404, "Page Not Found"); response.setHeader("foopy", "quux-baz", false); - do_check_eq(metadata.port, 4444); + do_check_eq(metadata.port, port); do_check_eq(metadata.host, "localhost"); do_check_eq(metadata.path.charAt(0), "/"); diff --git a/netwerk/test/httpserver/test/test_body_length.js b/netwerk/test/httpserver/test/test_body_length.js index 9f3beb833df..c2502d26daf 100644 --- a/netwerk/test/httpserver/test/test_body_length.js +++ b/netwerk/test/httpserver/test/test_body_length.js @@ -10,15 +10,13 @@ * octal number. */ -const PORT = 4444; - var srv; function run_test() { srv = createServer(); srv.registerPathHandler("/content-length", contentLength); - srv.start(PORT); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } @@ -44,10 +42,12 @@ function contentLength(request, response) * BEGIN TESTS * ***************/ -var tests = [ - new Test("http://localhost:4444/content-length", - init_content_length), - ]; +XPCOMUtils.defineLazyGetter(this, 'tests', function() { + return [ + new Test("http://localhost:" + srv.identity.primaryPort + "/content-length", + init_content_length), + ]; +}); function init_content_length(ch) { diff --git a/netwerk/test/httpserver/test/test_byte_range.js b/netwerk/test/httpserver/test/test_byte_range.js index 9e6f5927a1c..9b3b2e1abeb 100644 --- a/netwerk/test/httpserver/test/test_byte_range.js +++ b/netwerk/test/httpserver/test/test_byte_range.js @@ -7,10 +7,13 @@ // checks if a byte range request and non-byte range request retrieve the // correct data. -const PREFIX = "http://localhost:4444"; +var srv; +XPCOMUtils.defineLazyGetter(this, "PREFIX", function() { + return "http://localhost:" + srv.identity.primaryPort; +}); -var tests = - [ +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ new Test(PREFIX + "/range.txt", init_byterange, start_byterange, stop_byterange), new Test(PREFIX + "/range.txt", @@ -40,14 +43,15 @@ var tests = new Test(PREFIX + "/range.txt", null, start_normal, stop_normal) ]; +}); function run_test() { - var srv = createServer(); + srv = createServer(); var dir = do_get_file("data/ranges/"); srv.registerDirectory("/", dir); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_cern_meta.js b/netwerk/test/httpserver/test/test_cern_meta.js index a451e887d07..b178f07fc0b 100644 --- a/netwerk/test/httpserver/test/test_cern_meta.js +++ b/netwerk/test/httpserver/test/test_cern_meta.js @@ -5,31 +5,35 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // exercises support for mod_cern_meta-style header/status line modification +var srv; -const PREFIX = "http://localhost:4444"; +XPCOMUtils.defineLazyGetter(this, 'PREFIX', function() { + return "http://localhost:" + srv.identity.primaryPort; +}); -var tests = - [ - new Test(PREFIX + "/test_both.html", - null, start_testBoth, null), - new Test(PREFIX + "/test_ctype_override.txt", - null, start_test_ctype_override_txt, null), - new Test(PREFIX + "/test_status_override.html", - null, start_test_status_override_html, null), - new Test(PREFIX + "/test_status_override_nodesc.txt", - null, start_test_status_override_nodesc_txt, null), - new Test(PREFIX + "/caret_test.txt^", - null, start_caret_test_txt_, null) +XPCOMUtils.defineLazyGetter(this, 'tests', function() { + return [ + new Test(PREFIX + "/test_both.html", + null, start_testBoth, null), + new Test(PREFIX + "/test_ctype_override.txt", + null, start_test_ctype_override_txt, null), + new Test(PREFIX + "/test_status_override.html", + null, start_test_status_override_html, null), + new Test(PREFIX + "/test_status_override_nodesc.txt", + null, start_test_status_override_nodesc_txt, null), + new Test(PREFIX + "/caret_test.txt^", + null, start_caret_test_txt_, null) ]; +}); function run_test() { - var srv = createServer(); + srv = createServer(); var cernDir = do_get_file("data/cern_meta/"); srv.registerDirectory("/", cernDir); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_default_index_handler.js b/netwerk/test/httpserver/test/test_default_index_handler.js index 032410d23c4..be5280a9c4a 100644 --- a/netwerk/test/httpserver/test/test_default_index_handler.js +++ b/netwerk/test/httpserver/test/test_default_index_handler.js @@ -10,6 +10,10 @@ var srv, dir, dirEntries; +XPCOMUtils.defineLazyGetter(this, 'BASE_URL', function() { + return "http://localhost:" + srv.identity.primaryPort + "/"; +}); + function run_test() { createTestDirectory(); @@ -20,7 +24,7 @@ function run_test() var nameDir = do_get_file("data/name-scheme/"); srv.registerDirectory("/bar/", nameDir); - srv.start(4444); + srv.start(-1); function done() { @@ -255,38 +259,32 @@ function makeFile(name, isDirectory, parentDir, lst) * TESTS * *********/ -var tests = []; -var test; +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test(BASE_URL, null, start, stopRootDirectory), + new Test(BASE_URL + "foo/", null, start, stopFooDirectory), + new Test(BASE_URL + "bar/folder^/", null, start, stopTrailingCaretDirectory), + ]; +}); // check top-level directory listing -test = new Test("http://localhost:4444/", - null, start, stopRootDirectory), -tests.push(test); function start(ch) { do_check_eq(ch.getResponseHeader("Content-Type"), "text/html;charset=utf-8"); } function stopRootDirectory(ch, cx, status, data) { - dataCheck(data, "http://localhost:4444/", "/", dirEntries[0]); + dataCheck(data, BASE_URL, "/", dirEntries[0]); } - // check non-top-level, too -test = new Test("http://localhost:4444/foo/", - null, start, stopFooDirectory), -tests.push(test); function stopFooDirectory(ch, cx, status, data) { - dataCheck(data, "http://localhost:4444/foo/", "/foo/", dirEntries[1]); + dataCheck(data, BASE_URL + "foo/", "/foo/", dirEntries[1]); } - // trailing-caret leaf with hidden files -test = new Test("http://localhost:4444/bar/folder^/", - null, start, stopTrailingCaretDirectory), -tests.push(test); function stopTrailingCaretDirectory(ch, cx, status, data) { - hiddenDataCheck(data, "http://localhost:4444/bar/folder^/", "/bar/folder^/"); + hiddenDataCheck(data, BASE_URL + "bar/folder^/", "/bar/folder^/"); } diff --git a/netwerk/test/httpserver/test/test_empty_body.js b/netwerk/test/httpserver/test/test_empty_body.js index 0ded52f1534..d7c68ac62ea 100644 --- a/netwerk/test/httpserver/test/test_empty_body.js +++ b/netwerk/test/httpserver/test/test_empty_body.js @@ -7,23 +7,26 @@ // in its original incarnation, the server didn't like empty response-bodies; // see the comment in _end for details -var tests = - [ - new Test("http://localhost:4444/empty-body-unwritten", - null, ensureEmpty, null), - new Test("http://localhost:4444/empty-body-written", - null, ensureEmpty, null), +var srv; + +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test("http://localhost:" + srv.identity.primaryPort + "/empty-body-unwritten", + null, ensureEmpty, null), + new Test("http://localhost:" + srv.identity.primaryPort + "/empty-body-written", + null, ensureEmpty, null), ]; +}); function run_test() { - var srv = createServer(); + srv = createServer(); // register a few test paths srv.registerPathHandler("/empty-body-unwritten", emptyBodyUnwritten); srv.registerPathHandler("/empty-body-written", emptyBodyWritten); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_errorhandler_exception.js b/netwerk/test/httpserver/test/test_errorhandler_exception.js index 849d90fd73b..783e02f7118 100644 --- a/netwerk/test/httpserver/test/test_errorhandler_exception.js +++ b/netwerk/test/httpserver/test/test_errorhandler_exception.js @@ -7,15 +7,18 @@ // Request handlers may throw exceptions, and those exception should be caught // by the server and converted into the proper error codes. -var tests = - [ - new Test("http://localhost:4444/throws/exception", +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test("http://localhost:" + srv.identity.primaryPort + "/throws/exception", null, start_throws_exception, succeeded), - new Test("http://localhost:4444/this/file/does/not/exist/and/404s", + new Test("http://localhost:" + srv.identity.primaryPort + + "/this/file/does/not/exist/and/404s", null, start_nonexistent_404_fails_so_400, succeeded), - new Test("http://localhost:4444/attempts/404/fails/so/400/fails/so/500s", + new Test("http://localhost:" + srv.identity.primaryPort + + "/attempts/404/fails/so/400/fails/so/500s", register400Handler, start_multiple_exceptions_500, succeeded), ]; +}); var srv; @@ -26,7 +29,7 @@ function run_test() srv.registerErrorHandler(404, throwsException); srv.registerPathHandler("/throws/exception", throwsException); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_header_array.js b/netwerk/test/httpserver/test/test_header_array.js index c34f9dc00b9..26803d29abc 100644 --- a/netwerk/test/httpserver/test/test_header_array.js +++ b/netwerk/test/httpserver/test/test_header_array.js @@ -6,15 +6,15 @@ // test that special headers are sent as an array of headers with the same name -const PORT = 4444; +var srv; function run_test() { - var srv; + srv; srv = createServer(); srv.registerPathHandler("/path-handler", pathHandler); - srv.start(PORT); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } @@ -47,9 +47,12 @@ function pathHandler(request, response) * BEGIN TESTS * ***************/ -var tests = [ - new Test("http://localhost:4444/path-handler", - null, check)]; +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test("http://localhost:" + srv.identity.primaryPort + "/path-handler", + null, check) + ]; +}); function check(ch, cx) { diff --git a/netwerk/test/httpserver/test/test_name_scheme.js b/netwerk/test/httpserver/test/test_name_scheme.js index 59e928c5f70..43ce20aa9b6 100644 --- a/netwerk/test/httpserver/test/test_name_scheme.js +++ b/netwerk/test/httpserver/test/test_name_scheme.js @@ -8,42 +8,47 @@ // htaccess-like functionality without the need to explicitly disable display // of such files -const PREFIX = "http://localhost:4444"; +var srv; -var tests = - [ - new Test(PREFIX + "/bar.html^", +XPCOMUtils.defineLazyGetter(this, "PREFIX", function() { + return "http://localhost:" + srv.identity.primaryPort; +}); + +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test(PREFIX + "/bar.html^", null, start_bar_html_, null), - new Test(PREFIX + "/foo.html^", + new Test(PREFIX + "/foo.html^", null, start_foo_html_, null), - new Test(PREFIX + "/normal-file.txt", + new Test(PREFIX + "/normal-file.txt", null, start_normal_file_txt, null), - new Test(PREFIX + "/folder^/file.txt", + new Test(PREFIX + "/folder^/file.txt", null, start_folder__file_txt, null), - new Test(PREFIX + "/foo/bar.html^", + new Test(PREFIX + "/foo/bar.html^", null, start_bar_html_, null), - new Test(PREFIX + "/foo/foo.html^", + new Test(PREFIX + "/foo/foo.html^", null, start_foo_html_, null), - new Test(PREFIX + "/foo/normal-file.txt", + new Test(PREFIX + "/foo/normal-file.txt", null, start_normal_file_txt, null), - new Test(PREFIX + "/foo/folder^/file.txt", + new Test(PREFIX + "/foo/folder^/file.txt", null, start_folder__file_txt, null), - new Test(PREFIX + "/end-caret^/bar.html^", + new Test(PREFIX + "/end-caret^/bar.html^", null, start_bar_html_, null), - new Test(PREFIX + "/end-caret^/foo.html^", + new Test(PREFIX + "/end-caret^/foo.html^", null, start_foo_html_, null), - new Test(PREFIX + "/end-caret^/normal-file.txt", + new Test(PREFIX + "/end-caret^/normal-file.txt", null, start_normal_file_txt, null), - new Test(PREFIX + "/end-caret^/folder^/file.txt", + new Test(PREFIX + "/end-caret^/folder^/file.txt", null, start_folder__file_txt, null) - ]; + ]; +}); function run_test() { - var srv = createServer(); + srv = createServer(); // make sure underscores work in directories "mounted" in directories with // folders starting with _ @@ -52,7 +57,7 @@ function run_test() srv.registerDirectory("/foo/", nameDir); srv.registerDirectory("/end-caret^/", nameDir); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_processasync.js b/netwerk/test/httpserver/test/test_processasync.js index bdcd6d95398..6b0f2e868ce 100644 --- a/netwerk/test/httpserver/test/test_processasync.js +++ b/netwerk/test/httpserver/test/test_processasync.js @@ -8,15 +8,18 @@ * Tests for correct behavior of asynchronous responses. */ -const PORT = 4444; -const PREPATH = "http://localhost:" + PORT; +XPCOMUtils.defineLazyGetter(this, "PREPATH", function() { + return "http://localhost:" + srv.identity.primaryPort; +}); + +var srv; function run_test() { - var srv = createServer(); + srv = createServer(); for (var path in handlers) srv.registerPathHandler(path, handlers[path]); - srv.start(PORT); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } @@ -26,8 +29,18 @@ function run_test() * BEGIN TESTS * ***************/ -var test; -var tests = []; +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test(PREPATH + "/handleSync", null, start_handleSync, null), + new Test(PREPATH + "/handleAsync1", null, start_handleAsync1, + stop_handleAsync1), + new Test(PREPATH + "/handleAsync2", init_handleAsync2, start_handleAsync2, + stop_handleAsync2), + new Test(PREPATH + "/handleAsyncOrdering", null, null, + stop_handleAsyncOrdering) + ]; +}); + var handlers = {}; function handleSync(request, response) @@ -54,11 +67,6 @@ function start_handleSync(ch, cx) do_check_eq(ch.responseStatusText, "handleSync pass"); } -test = new Test(PREPATH + "/handleSync", - null, start_handleSync, null), -tests.push(test); - - function handleAsync1(request, response) { response.setStatusLine(request.httpVersion, 500, "Old status line!"); @@ -125,11 +133,6 @@ function stop_handleAsync1(ch, cx, status, data) do_check_eq(data.length, 0); } -test = new Test(PREPATH + "/handleAsync1", - null, start_handleAsync1, stop_handleAsync1), -tests.push(test); - - const startToHeaderDelay = 500; const startToFinishedDelay = 750; @@ -233,11 +236,6 @@ function stop_handleAsync2(ch, cx, status, data) do_check_eq(String.fromCharCode.apply(null, data), "BODY"); } -test = new Test(PREPATH + "/handleAsync2", - init_handleAsync2, start_handleAsync2, stop_handleAsync2); -tests.push(test); - - /* * Tests that accessing output stream *before* calling processAsync() works * correctly, sending written data immediately as it is written, not buffering @@ -304,7 +302,3 @@ function stop_handleAsyncOrdering(ch, cx, status, data) do_throw("value " + v + " at index " + index + " should be zero"); }); } - -test = new Test(PREPATH + "/handleAsyncOrdering", - null, null, stop_handleAsyncOrdering); -tests.push(test); diff --git a/netwerk/test/httpserver/test/test_qi.js b/netwerk/test/httpserver/test/test_qi.js index 24e1affcb0d..3642b9744a9 100644 --- a/netwerk/test/httpserver/test/test_qi.js +++ b/netwerk/test/httpserver/test/test_qi.js @@ -10,17 +10,20 @@ * created by XPConnect. */ -var tests = - [ - new Test("http://localhost:4444/test", - null, start_test, null), - new Test("http://localhost:4444/sjs/qi.sjs", - null, start_sjs_qi, null), +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test("http://localhost:" + srv.identity.primaryPort + "/test", + null, start_test, null), + new Test("http://localhost:" + srv.identity.primaryPort + "/sjs/qi.sjs", + null, start_sjs_qi, null), ]; +}); + +var srv; function run_test() { - var srv = createServer(); + srv = createServer(); var qi; try @@ -37,7 +40,7 @@ function run_test() srv.registerPathHandler("/test", testHandler); srv.registerDirectory("/", do_get_file("data/")); srv.registerContentType("sjs", "sjs"); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_registerdirectory.js b/netwerk/test/httpserver/test/test_registerdirectory.js index 97e71570c29..6d986fab745 100644 --- a/netwerk/test/httpserver/test/test_registerdirectory.js +++ b/netwerk/test/httpserver/test/test_registerdirectory.js @@ -6,10 +6,9 @@ // tests the registerDirectory API -const BASE = "http://localhost:4444"; - -var tests = []; -var test; +XPCOMUtils.defineLazyGetter(this, "BASE", function() { + return "http://localhost:" + srv.identity.primaryPort; +}); function nocache(ch) @@ -50,234 +49,190 @@ function checkFile(ch, cx, status, data) fileContents(actualFile)); } +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ /*********************** * without a base path * ***********************/ - -test = new Test(BASE + "/test_registerdirectory.js", + new Test(BASE + "/test_registerdirectory.js", nocache, notFound, null), -tests.push(test); - /******************** * with a base path * ********************/ - -test = new Test(BASE + "/test_registerdirectory.js", - function(ch) - { - nocache(ch); - serverBasePath = testsDirectory.clone(); - srv.registerDirectory("/", serverBasePath); - }, - null, - checkFile); -tests.push(test); - + new Test(BASE + "/test_registerdirectory.js", + function(ch) + { + nocache(ch); + serverBasePath = testsDirectory.clone(); + srv.registerDirectory("/", serverBasePath); + }, + null, + checkFile), /***************************** * without a base path again * *****************************/ - -test = new Test(BASE + "/test_registerdirectory.js", - function(ch) - { - nocache(ch); - serverBasePath = null; - srv.registerDirectory("/", serverBasePath); - }, - notFound, - null); -tests.push(test); - + new Test(BASE + "/test_registerdirectory.js", + function(ch) + { + nocache(ch); + serverBasePath = null; + srv.registerDirectory("/", serverBasePath); + }, + notFound, + null), /*************************** * registered path handler * ***************************/ - -test = new Test(BASE + "/test_registerdirectory.js", - function(ch) - { - nocache(ch); - srv.registerPathHandler("/test_registerdirectory.js", - override_test_registerdirectory); - }, - checkOverride, - null); -tests.push(test); - + new Test(BASE + "/test_registerdirectory.js", + function(ch) + { + nocache(ch); + srv.registerPathHandler("/test_registerdirectory.js", + override_test_registerdirectory); + }, + checkOverride, + null), /************************ * removed path handler * ************************/ - -test = new Test(BASE + "/test_registerdirectory.js", - function init_registerDirectory6(ch) - { - nocache(ch); - srv.registerPathHandler("/test_registerdirectory.js", null); - }, - notFound, - null); -tests.push(test); - + new Test(BASE + "/test_registerdirectory.js", + function init_registerDirectory6(ch) + { + nocache(ch); + srv.registerPathHandler("/test_registerdirectory.js", null); + }, + notFound, + null), /******************** * with a base path * ********************/ + new Test(BASE + "/test_registerdirectory.js", + function(ch) + { + nocache(ch); -test = new Test(BASE + "/test_registerdirectory.js", - function(ch) - { - nocache(ch); - - // set the base path again - serverBasePath = testsDirectory.clone(); - srv.registerDirectory("/", serverBasePath); - }, - null, - checkFile); -tests.push(test); - + // set the base path again + serverBasePath = testsDirectory.clone(); + srv.registerDirectory("/", serverBasePath); + }, + null, + checkFile), /************************* * ...and a path handler * *************************/ - -test = new Test(BASE + "/test_registerdirectory.js", - function(ch) - { - nocache(ch); - srv.registerPathHandler("/test_registerdirectory.js", - override_test_registerdirectory); - }, - checkOverride, - null); -tests.push(test); - + new Test(BASE + "/test_registerdirectory.js", + function(ch) + { + nocache(ch); + srv.registerPathHandler("/test_registerdirectory.js", + override_test_registerdirectory); + }, + checkOverride, + null), /************************ * removed base handler * ************************/ - -test = new Test(BASE + "/test_registerdirectory.js", - function(ch) - { - nocache(ch); - serverBasePath = null; - srv.registerDirectory("/", serverBasePath); - }, - checkOverride, - null); -tests.push(test); - + new Test(BASE + "/test_registerdirectory.js", + function(ch) + { + nocache(ch); + serverBasePath = null; + srv.registerDirectory("/", serverBasePath); + }, + checkOverride, + null), /************************ * removed path handler * ************************/ - -test = new Test(BASE + "/test_registerdirectory.js", - function(ch) - { - nocache(ch); - srv.registerPathHandler("/test_registerdirectory.js", null); - }, - notFound, - null); -tests.push(test); - + new Test(BASE + "/test_registerdirectory.js", + function(ch) + { + nocache(ch); + srv.registerPathHandler("/test_registerdirectory.js", null); + }, + notFound, + null), /************************* * mapping set up, works * *************************/ - -test = new Test(BASE + "/foo/test_registerdirectory.js", - function(ch) - { - nocache(ch); - serverBasePath = testsDirectory.clone(); - srv.registerDirectory("/foo/", serverBasePath); - }, - check200, - null); -tests.push(test); - + new Test(BASE + "/foo/test_registerdirectory.js", + function(ch) + { + nocache(ch); + serverBasePath = testsDirectory.clone(); + srv.registerDirectory("/foo/", serverBasePath); + }, + check200, + null), /********************* * no mapping, fails * *********************/ - -test = new Test(BASE + "/foo/test_registerdirectory.js/test_registerdirectory.js", - nocache, - notFound, - null); -tests.push(test); - + new Test(BASE + "/foo/test_registerdirectory.js/test_registerdirectory.js", + nocache, + notFound, + null), /****************** * mapping, works * ******************/ - -test = new Test(BASE + "/foo/test_registerdirectory.js/test_registerdirectory.js", - function(ch) - { - nocache(ch); - srv.registerDirectory("/foo/test_registerdirectory.js/", - serverBasePath); - }, - null, - checkFile); -tests.push(test); - + new Test(BASE + "/foo/test_registerdirectory.js/test_registerdirectory.js", + function(ch) + { + nocache(ch); + srv.registerDirectory("/foo/test_registerdirectory.js/", + serverBasePath); + }, + null, + checkFile), /************************************ * two mappings set up, still works * ************************************/ - -test = new Test(BASE + "/foo/test_registerdirectory.js", - nocache, null, checkFile); -tests.push(test); - + new Test(BASE + "/foo/test_registerdirectory.js", + nocache, null, checkFile), /************************** * remove topmost mapping * **************************/ - -test = new Test(BASE + "/foo/test_registerdirectory.js", - function(ch) - { - nocache(ch); - srv.registerDirectory("/foo/", null); - }, - notFound, - null); -tests.push(test); - + new Test(BASE + "/foo/test_registerdirectory.js", + function(ch) + { + nocache(ch); + srv.registerDirectory("/foo/", null); + }, + notFound, + null), /************************************** * lower mapping still present, works * **************************************/ - -test = new Test(BASE + "/foo/test_registerdirectory.js/test_registerdirectory.js", - nocache, null, checkFile); -tests.push(test); - + new Test(BASE + "/foo/test_registerdirectory.js/test_registerdirectory.js", + nocache, null, checkFile), /******************* * mapping removed * *******************/ - -test = new Test(BASE + "/foo/test_registerdirectory.js/test_registerdirectory.js", - function(ch) - { - nocache(ch); - srv.registerDirectory("/foo/test_registerdirectory.js/", null); - }, - notFound, - null); -tests.push(test); - + new Test(BASE + "/foo/test_registerdirectory.js/test_registerdirectory.js", + function(ch) + { + nocache(ch); + srv.registerDirectory("/foo/test_registerdirectory.js/", null); + }, + notFound, + null) + ]; +}); var srv; @@ -289,7 +244,7 @@ function run_test() testsDirectory = do_get_cwd(); srv = createServer(); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_registerfile.js b/netwerk/test/httpserver/test/test_registerfile.js index 69128447ae5..9794688875e 100644 --- a/netwerk/test/httpserver/test/test_registerfile.js +++ b/netwerk/test/httpserver/test/test_registerfile.js @@ -6,7 +6,9 @@ // tests the registerFile API -const BASE = "http://localhost:4444"; +XPCOMUtils.defineLazyGetter(this, "BASE", function() { + return "http://localhost:" + srv.identity.primaryPort; +}); var file = do_get_file("test_registerfile.js"); @@ -21,15 +23,19 @@ function onStop(ch, cx, status, data) do_check_eq(data.length, file.fileSize); } -var test = new Test(BASE + "/foo", null, onStart, onStop); +XPCOMUtils.defineLazyGetter(this, "test", function() { + return new Test(BASE + "/foo", null, onStart, onStop); +}); + +var srv; function run_test() { - var srv = createServer(); + srv = createServer(); try { - srv.registerFile("/foo", do_get_cwd()); + srv.registerFile("/foo", do_get_profile()); throw "registerFile succeeded!"; } catch (e) @@ -38,7 +44,7 @@ function run_test() } srv.registerFile("/foo", file); - srv.start(4444); + srv.start(-1); runHttpTests([test], testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_registerprefix.js b/netwerk/test/httpserver/test/test_registerprefix.js index 2d3d19a01d7..2473a56a327 100644 --- a/netwerk/test/httpserver/test/test_registerprefix.js +++ b/netwerk/test/httpserver/test/test_registerprefix.js @@ -6,10 +6,9 @@ // tests the registerPrefixHandler API -const BASE = "http://localhost:4444"; - -var tests = []; -var test; +XPCOMUtils.defineLazyGetter(this, "BASE", function() { + return "http://localhost:" + srv.identity.primaryPort; +}); function nocache(ch) { @@ -33,6 +32,20 @@ function makeCheckOverride(magic) }); } +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test(BASE + "/prefix/dummy", prefixHandler, null, + makeCheckOverride("prefix")), + new Test(BASE + "/prefix/dummy", pathHandler, null, + makeCheckOverride("path")), + new Test(BASE + "/prefix/subpath/dummy", longerPrefixHandler, null, + makeCheckOverride("subpath")), + new Test(BASE + "/prefix/dummy", removeHandlers, null, notFound), + new Test(BASE + "/prefix/subpath/dummy", newPrefixHandler, null, + makeCheckOverride("subpath")) + ]; +}); + /*************************** * registered prefix handler * ***************************/ @@ -43,10 +56,6 @@ function prefixHandler(channel) srv.registerPrefixHandler("/prefix/", makeOverride("prefix")); } -test = new Test(BASE + "/prefix/dummy", prefixHandler, null, - makeCheckOverride("prefix")); -tests.push(test); - /******************************** * registered path handler on top * ********************************/ @@ -56,9 +65,6 @@ function pathHandler(channel) nocache(channel); srv.registerPathHandler("/prefix/dummy", makeOverride("path")); } -test = new Test(BASE + "/prefix/dummy", pathHandler, null, - makeCheckOverride("path")); -tests.push(test); /********************************** * registered longer prefix handler * @@ -69,9 +75,6 @@ function longerPrefixHandler(channel) nocache(channel); srv.registerPrefixHandler("/prefix/subpath/", makeOverride("subpath")); } -test = new Test(BASE + "/prefix/subpath/dummy", longerPrefixHandler, null, - makeCheckOverride("subpath")); -tests.push(test); /************************ * removed prefix handler * @@ -83,8 +86,6 @@ function removeHandlers(channel) srv.registerPrefixHandler("/prefix/", null); srv.registerPathHandler("/prefix/dummy", null); } -test = new Test(BASE + "/prefix/dummy", removeHandlers, null, notFound); -tests.push(test); /***************************** * re-register shorter handler * @@ -95,9 +96,6 @@ function newPrefixHandler(channel) nocache(channel); srv.registerPrefixHandler("/prefix/", makeOverride("prefix")); } -test = new Test(BASE + "/prefix/subpath/dummy", newPrefixHandler, null, - makeCheckOverride("subpath")); -tests.push(test); var srv; var serverBasePath; @@ -105,10 +103,10 @@ var testsDirectory; function run_test() { - testsDirectory = do_get_cwd(); + testsDirectory = do_get_profile(); srv = createServer(); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_request_line_split_in_two_packets.js b/netwerk/test/httpserver/test/test_request_line_split_in_two_packets.js index 05c083e6a5c..405d9745835 100644 --- a/netwerk/test/httpserver/test/test_request_line_split_in_two_packets.js +++ b/netwerk/test/httpserver/test/test_request_line_split_in_two_packets.js @@ -10,18 +10,16 @@ * properly. */ -const PORT = 4444; - -var srv; +var srv = createServer(); +srv.start(-1); +const PORT = srv.identity.primaryPort; function run_test() { - srv = createServer(); srv.registerPathHandler("/lots-of-leading-blank-lines", lotsOfLeadingBlankLines); srv.registerPathHandler("/very-long-request-line", veryLongRequestLine); - srv.start(PORT); runRawTests(tests, testComplete(srv)); } @@ -53,7 +51,7 @@ reallyLong = reallyLong + reallyLong + reallyLong + reallyLong; // 524288 if (reallyLong.length !== 524288) throw new TypeError("generated length not as long as expected"); str = "GET /very-long-request-line?" + reallyLong + " HTTP/1.1\r\n" + - "Host: localhost:4444\r\n" + + "Host: localhost:" + PORT + "\r\n" + "\r\n"; data = []; for (var i = 0; i < str.length; i += 16384) @@ -80,7 +78,7 @@ function checkVeryLongRequestLine(data) "Version: 1.1", "Scheme: http", "Host: localhost", - "Port: 4444", + "Port: " + PORT, ]; expectLines(iter, body); @@ -100,7 +98,7 @@ for (var i = 0; i < 14; i++) blankLines += blankLines; str = blankLines + "GET /lots-of-leading-blank-lines HTTP/1.1\r\n" + - "Host: localhost:4444\r\n" + + "Host: localhost:" + PORT + "\r\n" + "\r\n"; data = []; for (var i = 0; i < str.length; i += 100) @@ -127,7 +125,7 @@ function checkLotsOfLeadingBlankLines(data) "Version: 1.1", "Scheme: http", "Host: localhost", - "Port: 4444", + "Port: " + PORT, ]; expectLines(iter, body); diff --git a/netwerk/test/httpserver/test/test_response_write.js b/netwerk/test/httpserver/test/test_response_write.js index 84d4476f5cd..6810139a437 100644 --- a/netwerk/test/httpserver/test/test_response_write.js +++ b/netwerk/test/httpserver/test/test_response_write.js @@ -6,21 +6,24 @@ // make sure response.write works for strings, and coerces other args to strings -var tests = - [ - new Test("http://localhost:4444/writeString", +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test("http://localhost:" + srv.identity.primaryPort + "/writeString", null, check_1234, succeeded), - new Test("http://localhost:4444/writeInt", + new Test("http://localhost:" + srv.identity.primaryPort + "/writeInt", null, check_1234, succeeded), ]; +}); + +var srv; function run_test() { - var srv = createServer(); + srv = createServer(); srv.registerPathHandler("/writeString", writeString); srv.registerPathHandler("/writeInt", writeInt); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_seizepower.js b/netwerk/test/httpserver/test/test_seizepower.js index d41fc2a921f..b11258be6c2 100644 --- a/netwerk/test/httpserver/test/test_seizepower.js +++ b/netwerk/test/httpserver/test/test_seizepower.js @@ -8,7 +8,9 @@ * Tests that the seizePower API works correctly. */ -const PORT = 4444; +XPCOMUtils.defineLazyGetter(this, "PORT", function() { + return srv.identity.primaryPort; +}); var srv; @@ -22,7 +24,7 @@ function run_test() srv.registerPathHandler("/async-seizure", handleAsyncSeizure); srv.registerPathHandler("/seize-after-async", handleSeizeAfterAsync); - srv.start(PORT); + srv.start(-1); runRawTests(tests, testComplete(srv)); } @@ -134,50 +136,47 @@ function handleSeizeAfterAsync(request, response) * BEGIN TESTS * ***************/ -var test, data; -var tests = []; +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new RawTest("localhost", PORT, data0, checkRawData), + new RawTest("localhost", PORT, data1, checkTooLate), + new RawTest("localhost", PORT, data2, checkExceptions), + new RawTest("localhost", PORT, data3, checkAsyncSeizure), + new RawTest("localhost", PORT, data4, checkSeizeAfterAsync), + ]; +}); -data = "GET /raw-data HTTP/1.0\r\n" + +var data0 = "GET /raw-data HTTP/1.0\r\n" + "\r\n"; function checkRawData(data) { do_check_eq(data, "Raw data!"); } -test = new RawTest("localhost", PORT, data, checkRawData), -tests.push(test); -data = "GET /called-too-late HTTP/1.0\r\n" + +var data1 = "GET /called-too-late HTTP/1.0\r\n" + "\r\n"; function checkTooLate(data) { do_check_eq(LineIterator(data).next(), "too-late passed"); } -test = new RawTest("localhost", PORT, data, checkTooLate), -tests.push(test); -data = "GET /exceptions HTTP/1.0\r\n" + +var data2 = "GET /exceptions HTTP/1.0\r\n" + "\r\n"; function checkExceptions(data) { do_check_eq("exceptions test passed", data); } -test = new RawTest("localhost", PORT, data, checkExceptions), -tests.push(test); -data = "GET /async-seizure HTTP/1.0\r\n" + +var data3 = "GET /async-seizure HTTP/1.0\r\n" + "\r\n"; function checkAsyncSeizure(data) { do_check_eq(data, "async seizure passed"); } -test = new RawTest("localhost", PORT, data, checkAsyncSeizure), -tests.push(test); -data = "GET /seize-after-async HTTP/1.0\r\n" + +var data4 = "GET /seize-after-async HTTP/1.0\r\n" + "\r\n"; function checkSeizeAfterAsync(data) { do_check_eq(LineIterator(data).next(), "HTTP/1.0 200 async seizure pass"); } -test = new RawTest("localhost", PORT, data, checkSeizeAfterAsync), -tests.push(test); diff --git a/netwerk/test/httpserver/test/test_setindexhandler.js b/netwerk/test/httpserver/test/test_setindexhandler.js index 5b35d277b99..152c4b0b74f 100644 --- a/netwerk/test/httpserver/test/test_setindexhandler.js +++ b/netwerk/test/httpserver/test/test_setindexhandler.js @@ -11,21 +11,25 @@ var srv, serverBasePath; function run_test() { srv = createServer(); - serverBasePath = do_get_cwd(); + serverBasePath = do_get_profile(); srv.registerDirectory("/", serverBasePath); srv.setIndexHandler(myIndexHandler); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } +XPCOMUtils.defineLazyGetter(this, "URL", function() { + return "http://localhost:" + srv.identity.primaryPort + "/"; +}); -var tests = []; -var test; +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test(URL, init, startCustomIndexHandler, stopCustomIndexHandler), + new Test(URL, init, startDefaultIndexHandler, stopDefaultIndexHandler) + ]; +}); -test = new Test("http://localhost:4444/", - init, startCustomIndexHandler, stopCustomIndexHandler); -tests.push(test); function init(ch) { ch.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; // important! @@ -41,9 +45,6 @@ function stopCustomIndexHandler(ch, cx, status, data) do_check_eq(String.fromCharCode.apply(null, data), "directory!"); } -test = new Test("http://localhost:4444/", - init, startDefaultIndexHandler, stopDefaultIndexHandler); -tests.push(test); function startDefaultIndexHandler(ch, cx) { do_check_eq(ch.responseStatus, 200); diff --git a/netwerk/test/httpserver/test/test_setstatusline.js b/netwerk/test/httpserver/test/test_setstatusline.js index 8876a38d6d6..6015fc972e9 100644 --- a/netwerk/test/httpserver/test/test_setstatusline.js +++ b/netwerk/test/httpserver/test/test_setstatusline.js @@ -7,6 +7,10 @@ // exercise nsIHttpResponse.setStatusLine, ensure its atomicity, and ensure the // specified behavior occurs if it's not called +XPCOMUtils.defineLazyGetter(this, "URL", function() { + return "http://localhost:" + srv.identity.primaryPort; +}); + var srv; function run_test() @@ -22,7 +26,7 @@ function run_test() srv.registerPathHandler("/crazyCode", crazyCode); srv.registerPathHandler("/nullVersion", nullVersion); - srv.start(4444); + srv.start(-1); runHttpTests(tests, testComplete(srv)); } @@ -48,16 +52,24 @@ function checkStatusLine(channel, httpMaxVer, httpMinVer, httpCode, statusText) * TESTS * *********/ -var tests = []; -var test; +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test(URL + "/no/setstatusline", null, startNoSetStatusLine, stop), + new Test(URL + "/http1_0", null, startHttp1_0, stop), + new Test(URL + "/http1_1", null, startHttp1_1, stop), + new Test(URL + "/invalidVersion", null, startPassedTrue, stop), + new Test(URL + "/invalidStatus", null, startPassedTrue, stop), + new Test(URL + "/invalidDescription", null, startPassedTrue, stop), + new Test(URL + "/crazyCode", null, startCrazy, stop), + new Test(URL + "/nullVersion", null, startNullVersion, stop) + ]; +}); + // /no/setstatusline function noSetstatusline(metadata, response) { } -test = new Test("http://localhost:4444/no/setstatusline", - null, startNoSetStatusLine, stop); -tests.push(test); function startNoSetStatusLine(ch, cx) { checkStatusLine(ch, 1, 1, 200, "OK"); @@ -73,9 +85,6 @@ function http1_0(metadata, response) { response.setStatusLine("1.0", 200, "OK"); } -test = new Test("http://localhost:4444/http1_0", - null, startHttp1_0, stop); -tests.push(test); function startHttp1_0(ch, cx) { checkStatusLine(ch, 1, 0, 200, "OK"); @@ -87,9 +96,6 @@ function http1_1(metadata, response) { response.setStatusLine("1.1", 200, "OK"); } -test = new Test("http://localhost:4444/http1_1", - null, startHttp1_1, stop); -tests.push(test); function startHttp1_1(ch, cx) { checkStatusLine(ch, 1, 1, 200, "OK"); @@ -108,9 +114,6 @@ function invalidVersion(metadata, response) response.setHeader("Passed", "true", false); } } -test = new Test("http://localhost:4444/invalidVersion", - null, startPassedTrue, stop); -tests.push(test); function startPassedTrue(ch, cx) { checkStatusLine(ch, 1, 1, 200, "OK"); @@ -130,9 +133,6 @@ function invalidStatus(metadata, response) response.setHeader("Passed", "true", false); } } -test = new Test("http://localhost:4444/invalidStatus", - null, startPassedTrue, stop); -tests.push(test); // /invalidDescription @@ -147,9 +147,6 @@ function invalidDescription(metadata, response) response.setHeader("Passed", "true", false); } } -test = new Test("http://localhost:4444/invalidDescription", - null, startPassedTrue, stop); -tests.push(test); // /crazyCode @@ -157,9 +154,6 @@ function crazyCode(metadata, response) { response.setStatusLine("1.1", 617, "Crazy"); } -test = new Test("http://localhost:4444/crazyCode", - null, startCrazy, stop); -tests.push(test); function startCrazy(ch, cx) { checkStatusLine(ch, 1, 1, 617, "Crazy"); @@ -171,9 +165,6 @@ function nullVersion(metadata, response) { response.setStatusLine(null, 255, "NULL"); } -test = new Test("http://localhost:4444/nullVersion", - null, startNullVersion, stop); -tests.push(test); function startNullVersion(ch, cx) { // currently, this server implementation defaults to 1.1 diff --git a/netwerk/test/httpserver/test/test_sjs.js b/netwerk/test/httpserver/test/test_sjs.js index 3d23eda9f84..1ae498c5795 100644 --- a/netwerk/test/httpserver/test/test_sjs.js +++ b/netwerk/test/httpserver/test/test_sjs.js @@ -6,10 +6,17 @@ // tests support for server JS-generated pages -const BASE = "http://localhost:4444"; +var srv = createServer(); var sjs = do_get_file("data/sjs/cgi.sjs"); -var srv; +// NB: The server has no state at this point -- all state is set up and torn +// down in the tests, because we run the same tests twice with only a +// different query string on the requests, followed by the oddball +// test that doesn't care about throwing or not. +srv.start(-1); +const PORT = srv.identity.primaryPort; + +const BASE = "http://localhost:" + PORT; var test; var tests = []; @@ -230,8 +237,6 @@ tests.push(test); function run_test() { - srv = createServer(); - // Test for a content-type which isn't a field-value try { @@ -242,13 +247,5 @@ function run_test() { isException(e, Cr.NS_ERROR_INVALID_ARG); } - - - // NB: The server has no state at this point -- all state is set up and torn - // down in the tests, because we run the same tests twice with only a - // different query string on the requests, followed by the oddball - // test that doesn't care about throwing or not. - - srv.start(4444); runHttpTests(tests, testComplete(srv)); } diff --git a/netwerk/test/httpserver/test/test_sjs_object_state.js b/netwerk/test/httpserver/test/test_sjs_object_state.js index 94a8fc1111c..af9874b3b24 100644 --- a/netwerk/test/httpserver/test/test_sjs_object_state.js +++ b/netwerk/test/httpserver/test/test_sjs_object_state.js @@ -8,9 +8,10 @@ * Tests that the object-state-preservation mechanism works correctly. */ -const PORT = 4444; -const PATH = "http://localhost:" + PORT + "/object-state.sjs"; +XPCOMUtils.defineLazyGetter(this, "PATH", function() { + return "http://localhost:" + srv.identity.primaryPort + "/object-state.sjs"; +}); var srv; @@ -20,7 +21,7 @@ function run_test() var sjsDir = do_get_file("data/sjs/"); srv.registerDirectory("/", sjsDir); srv.registerContentType("sjs", "sjs"); - srv.start(PORT); + srv.start(-1); do_test_pending(); diff --git a/netwerk/test/httpserver/test/test_sjs_state.js b/netwerk/test/httpserver/test/test_sjs_state.js index 476c45cd52f..584f72001c0 100644 --- a/netwerk/test/httpserver/test/test_sjs_state.js +++ b/netwerk/test/httpserver/test/test_sjs_state.js @@ -6,7 +6,9 @@ // exercises the server's state-preservation API -const PORT = 4444; +XPCOMUtils.defineLazyGetter(this, "URL", function() { + return "http://localhost:" + srv.identity.primaryPort; +}); var srv; @@ -17,7 +19,7 @@ function run_test() srv.registerDirectory("/", sjsDir); srv.registerContentType("sjs", "sjs"); srv.registerPathHandler("/path-handler", pathHandler); - srv.start(PORT); + srv.start(-1); function done() { @@ -76,8 +78,35 @@ function pathHandler(request, response) * BEGIN TESTS * ***************/ -var test; -var tests = []; +XPCOMUtils.defineLazyGetter(this, "tests", function() { + return [ + new Test(URL + "/state1.sjs?" + + "newShared=newShared&newPrivate=newPrivate", + null, start_initial, null), + new Test(URL + "/state1.sjs?" + + "newShared=newShared2&newPrivate=newPrivate2", + null, start_overwrite, null), + new Test(URL + "/state1.sjs?" + + "newShared=&newPrivate=newPrivate3", + null, start_remove, null), + new Test(URL + "/path-handler", + null, start_handler, null), + new Test(URL + "/path-handler", + null, start_handler_again, null), + new Test(URL + "/state2.sjs?" + + "newShared=newShared4&newPrivate=newPrivate4", + null, start_other_initial, null), + new Test(URL + "/state2.sjs?" + + "newShared=", + null, start_other_remove_ignore, null), + new Test(URL + "/state2.sjs?" + + "newShared=newShared5&newPrivate=newPrivate5", + null, start_other_set_new, null), + new Test(URL + "/state1.sjs?" + + "newShared=done!&newPrivate=", + null, start_set_remove_original, null) + ]; +}); /* Hack around bug 474845 for now. */ function getHeaderFunction(ch) @@ -109,100 +138,48 @@ function expectValues(ch, oldShared, newShared, oldPrivate, newPrivate) do_check_eq(getHeader("X-New-Private-Value"), newPrivate); } - -test = new Test("http://localhost:4444/state1.sjs?" + - "newShared=newShared&newPrivate=newPrivate", - null, start_initial, null); -tests.push(test); - function start_initial(ch, cx) { dumpn("XXX start_initial"); expectValues(ch, "", "newShared", "", "newPrivate"); } - -test = new Test("http://localhost:4444/state1.sjs?" + - "newShared=newShared2&newPrivate=newPrivate2", - null, start_overwrite, null); -tests.push(test); - function start_overwrite(ch, cx) { expectValues(ch, "newShared", "newShared2", "newPrivate", "newPrivate2"); } - -test = new Test("http://localhost:4444/state1.sjs?" + - "newShared=&newPrivate=newPrivate3", - null, start_remove, null); -tests.push(test); - function start_remove(ch, cx) { expectValues(ch, "newShared2", "", "newPrivate2", "newPrivate3"); } - -test = new Test("http://localhost:4444/path-handler", - null, start_handler, null); -tests.push(test); - function start_handler(ch, cx) { expectValues(ch, "", "pathHandlerShared", "", "pathHandlerPrivate"); } - -test = new Test("http://localhost:4444/path-handler", - null, start_handler_again, null); -tests.push(test); - function start_handler_again(ch, cx) { expectValues(ch, "pathHandlerShared", "", "pathHandlerPrivate", "pathHandlerPrivate2"); } - -test = new Test("http://localhost:4444/state2.sjs?" + - "newShared=newShared4&newPrivate=newPrivate4", - null, start_other_initial, null); -tests.push(test); - function start_other_initial(ch, cx) { expectValues(ch, "", "newShared4", "", "newPrivate4"); } - -test = new Test("http://localhost:4444/state2.sjs?" + - "newShared=", - null, start_other_remove_ignore, null); -tests.push(test); - function start_other_remove_ignore(ch, cx) { expectValues(ch, "newShared4", "", "newPrivate4", ""); } - -test = new Test("http://localhost:4444/state2.sjs?" + - "newShared=newShared5&newPrivate=newPrivate5", - null, start_other_set_new, null); -tests.push(test); - function start_other_set_new(ch, cx) { expectValues(ch, "", "newShared5", "newPrivate4", "newPrivate5"); } - -test = new Test("http://localhost:4444/state1.sjs?" + - "newShared=done!&newPrivate=", - null, start_set_remove_original, null); -tests.push(test); - function start_set_remove_original(ch, cx) { expectValues(ch, "newShared5", "done!", "newPrivate3", ""); diff --git a/netwerk/test/httpserver/test/test_sjs_throwing_exceptions.js b/netwerk/test/httpserver/test/test_sjs_throwing_exceptions.js index d1eb7526dc5..1d6db344af4 100644 --- a/netwerk/test/httpserver/test/test_sjs_throwing_exceptions.js +++ b/netwerk/test/httpserver/test/test_sjs_throwing_exceptions.js @@ -10,15 +10,19 @@ * then preventing any file from being opened). */ -const PORT = 4444; +XPCOMUtils.defineLazyGetter(this, "URL", function() { + return "http://localhost:" + srv.identity.primaryPort; +}); + +var srv; function run_test() { - var srv = createServer(); + srv = createServer(); var sjsDir = do_get_file("data/sjs/"); srv.registerDirectory("/", sjsDir); srv.registerContentType("sjs", "sjs"); - srv.start(PORT); + srv.start(-1); function done() { @@ -41,16 +45,15 @@ var lastPassed = false; // This hits the open-file limit for me on OS X; your mileage may vary. const TEST_RUNS = 250; -var test = new Test("http://localhost:4444/thrower.sjs?throw", - null, start_thrower); - -var tests = new Array(TEST_RUNS + 1); -for (var i = 0; i < TEST_RUNS; i++) - tests[i] = test; - -// ...and don't forget to stop! -tests[TEST_RUNS] = new Test("http://localhost:4444/thrower.sjs", - null, start_last); +XPCOMUtils.defineLazyGetter(this, "tests", function() { + var _tests = new Array(TEST_RUNS + 1); + var _test = new Test(URL + "/thrower.sjs?throw", null, start_thrower); + for (var i = 0; i < TEST_RUNS; i++) + _tests[i] = _test; + // ...and don't forget to stop! + _tests[TEST_RUNS] = new Test(URL + "/thrower.sjs", null, start_last); + return _tests; +}); function start_thrower(ch, cx) { diff --git a/netwerk/test/httpserver/test/test_start_stop.js b/netwerk/test/httpserver/test/test_start_stop.js index c4111f36356..39d946cb69a 100644 --- a/netwerk/test/httpserver/test/test_start_stop.js +++ b/netwerk/test/httpserver/test/test_start_stop.js @@ -8,8 +8,13 @@ * Tests for correct behavior of the server start() and stop() methods. */ -const PORT = 4444; -const PREPATH = "http://localhost:" + PORT; +XPCOMUtils.defineLazyGetter(this, "PORT", function() { + return srv.identity.primaryPort; +}); + +XPCOMUtils.defineLazyGetter(this, "PREPATH", function() { + return "http://localhost:" + PORT; +}); var srv, srv2; @@ -25,7 +30,7 @@ function run_test() dumpn("*** run_test"); srv = createServer(); - srv.start(PORT); + srv.start(-1); try { diff --git a/netwerk/test/httpserver/test/xpcshell.ini b/netwerk/test/httpserver/test/xpcshell.ini index 10bc80bb2ca..25219d3e955 100644 --- a/netwerk/test/httpserver/test/xpcshell.ini +++ b/netwerk/test/httpserver/test/xpcshell.ini @@ -13,6 +13,7 @@ tail = [test_header_array.js] [test_headers.js] [test_host.js] +run-sequentially = Reusing same server on different specific ports. [test_linedata.js] [test_load_module.js] [test_name_scheme.js] From 46b96e564364a2c557d9fc722bc270298fd4264f Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Thu, 11 Jul 2013 11:31:24 -0700 Subject: [PATCH 10/31] Bug 888537 - Use a dynamic port in downloads/ xpcshell tests so they can be run in parallel. r=mak --- .../test/unit/head_download_manager.js | 8 ++++++-- .../downloads/test/unit/test_bug_382825.js | 4 ++-- .../downloads/test/unit/test_bug_395092.js | 4 ++-- .../downloads/test/unit/test_bug_401430.js | 7 ++++--- .../downloads/test/unit/test_bug_406857.js | 7 +++---- .../test_cancel_download_files_removed.js | 6 ++++-- .../test/unit/test_download_manager.js | 6 +++--- .../test/unit/test_download_samename.js | 6 ++++-- .../downloads/test/unit/test_guid.js | 6 ++++-- .../test/unit/test_offline_support.js | 5 +++-- .../test/unit/test_private_resume.js | 16 ++++++++------- .../test/unit/test_privatebrowsing.js | 16 +++++++-------- .../test/unit/test_privatebrowsing_cancel.js | 20 ++++++++++--------- .../downloads/test/unit/test_resume.js | 5 +++-- .../downloads/test/unit/test_sleep_wake.js | 5 +++-- 15 files changed, 69 insertions(+), 52 deletions(-) diff --git a/toolkit/components/downloads/test/unit/head_download_manager.js b/toolkit/components/downloads/test/unit/head_download_manager.js index acfedb7fde8..23bac57827f 100644 --- a/toolkit/components/downloads/test/unit/head_download_manager.js +++ b/toolkit/components/downloads/test/unit/head_download_manager.js @@ -78,6 +78,7 @@ function importDownloadsFile(aFName) var gDownloadCount = 0; /** * Adds a download to the DM, and starts it. + * @param server: a HttpServer used to serve the sourceURI * @param aParams (optional): an optional object which contains the function * parameters: * resultFileName: leaf node for the target file @@ -87,8 +88,11 @@ var gDownloadCount = 0; * runBeforeStart: a function to run before starting the download * isPrivate: whether the download is private or not */ -function addDownload(aParams) +function addDownload(server, aParams) { + if (!server) + do_throw("Must provide a valid server."); + const PORT = server.identity.primaryPort; if (!aParams) aParams = {}; if (!("resultFileName" in aParams)) @@ -98,7 +102,7 @@ function addDownload(aParams) aParams.targetFile.append(aParams.resultFileName); } if (!("sourceURI" in aParams)) - aParams.sourceURI = "http://localhost:4444/head_download_manager.js"; + aParams.sourceURI = "http://localhost:" + PORT + "/head_download_manager.js"; if (!("downloadName" in aParams)) aParams.downloadName = null; if (!("runBeforeStart" in aParams)) diff --git a/toolkit/components/downloads/test/unit/test_bug_382825.js b/toolkit/components/downloads/test/unit/test_bug_382825.js index b0ad8e45f87..7c344f5e030 100644 --- a/toolkit/components/downloads/test/unit/test_bug_382825.js +++ b/toolkit/components/downloads/test/unit/test_bug_382825.js @@ -10,7 +10,7 @@ const dm = Cc["@mozilla.org/download-manager;1"].getService(nsIDownloadManager); function test_retry_canceled() { - var dl = addDownload(); + var dl = addDownload(httpserv); // since we are going to be retrying a failed download, we need to inflate // this so it doesn't stop our server @@ -41,7 +41,7 @@ function run_test() { httpserv = new HttpServer(); httpserv.registerDirectory("/", do_get_cwd()); - httpserv.start(4444); + httpserv.start(-1); dm.addListener(getDownloadListener()); diff --git a/toolkit/components/downloads/test/unit/test_bug_395092.js b/toolkit/components/downloads/test/unit/test_bug_395092.js index 04b1d8c224d..0d491beada8 100644 --- a/toolkit/components/downloads/test/unit/test_bug_395092.js +++ b/toolkit/components/downloads/test/unit/test_bug_395092.js @@ -32,7 +32,7 @@ function run_test() { httpserv = new HttpServer(); httpserv.registerDirectory("/", do_get_cwd()); - httpserv.start(4444); + httpserv.start(-1); // our download listener var listener = { @@ -59,6 +59,6 @@ function run_test() getService(Ci.nsIObserverService); os.addObserver(observer, "dl-start", false); - addDownload(); + addDownload(httpserv); do_test_pending(); } diff --git a/toolkit/components/downloads/test/unit/test_bug_401430.js b/toolkit/components/downloads/test/unit/test_bug_401430.js index 7849d24b1a6..524a858dc5d 100644 --- a/toolkit/components/downloads/test/unit/test_bug_401430.js +++ b/toolkit/components/downloads/test/unit/test_bug_401430.js @@ -88,7 +88,7 @@ function run_test() httpserv = new HttpServer(); httpserv.registerDirectory("/", do_get_cwd()); - httpserv.start(4444); + httpserv.start(-1); var listener = { onDownloadStateChange: function test_401430_odsc(aState, aDownload) { @@ -107,6 +107,7 @@ function run_test() // need to save the file to the CWD, because the profile dir is in $TEMP, // and Windows apparently doesn't like putting things from $TEMP into // the recent files list. - var dl = addDownload({resultFileName: resultFileName, - targetFile: do_get_file(resultFileName, true)}); + var dl = addDownload(httpserv, + {resultFileName: resultFileName, + targetFile: do_get_file(resultFileName, true)}); } diff --git a/toolkit/components/downloads/test/unit/test_bug_406857.js b/toolkit/components/downloads/test/unit/test_bug_406857.js index bdd069cabf2..3f2677a2690 100644 --- a/toolkit/components/downloads/test/unit/test_bug_406857.js +++ b/toolkit/components/downloads/test/unit/test_bug_406857.js @@ -7,22 +7,21 @@ * retrying the download. */ -const HTTP_SERVER_PORT = 4444; - function run_test() { let dm = Cc["@mozilla.org/download-manager;1"]. getService(Ci.nsIDownloadManager); let db = dm.DBConnection; var httpserv = new HttpServer(); - httpserv.start(HTTP_SERVER_PORT); + httpserv.start(-1); let stmt = db.createStatement( "INSERT INTO moz_downloads (source, target, state, referrer) " + "VALUES (?1, ?2, ?3, ?4)"); // Download from the test http server - stmt.bindByIndex(0, "http://localhost:"+HTTP_SERVER_PORT+"/httpd.js"); + stmt.bindByIndex(0, "http://localhost:"+ httpserv.identity.primaryPort + + "/httpd.js"); // Download to a temp local file let file = Cc["@mozilla.org/file/directory_service;1"]. diff --git a/toolkit/components/downloads/test/unit/test_cancel_download_files_removed.js b/toolkit/components/downloads/test/unit/test_cancel_download_files_removed.js index ed7930590cb..a496e634a1e 100644 --- a/toolkit/components/downloads/test/unit/test_cancel_download_files_removed.js +++ b/toolkit/components/downloads/test/unit/test_cancel_download_files_removed.js @@ -88,7 +88,9 @@ function runNextTest() let set = DownloadListener.set = tests[currentTest]; currentTest++; - let channel = NetUtil.newChannel("http://localhost:4444" + set.serverPath); + let channel = NetUtil.newChannel("http://localhost:" + + httpserver.identity.primaryPort + + set.serverPath); let uriloader = Cc["@mozilla.org/uriloader;1"].getService(Ci.nsIURILoader); uriloader.openURI(channel, true, new WindowContext()); } @@ -126,7 +128,7 @@ function run_test() { Services.prefs.setBoolPref("browser.download.manager.showWhenStarting", false); httpserver = new HttpServer(); - httpserver.start(4444); + httpserver.start(-1); do_test_pending(); // setup files to be download, each with the same suggested filename diff --git a/toolkit/components/downloads/test/unit/test_download_manager.js b/toolkit/components/downloads/test/unit/test_download_manager.js index 7d03c471283..635216f594d 100644 --- a/toolkit/components/downloads/test/unit/test_download_manager.js +++ b/toolkit/components/downloads/test/unit/test_download_manager.js @@ -66,13 +66,13 @@ function test_resumeDownload_empty_queue() function test_addDownload_normal() { print("*** DOWNLOAD MANAGER TEST - Testing normal download adding"); - addDownload(); + addDownload(httpserv); } function test_addDownload_cancel() { print("*** DOWNLOAD MANAGER TEST - Testing download cancel"); - var dl = addDownload(); + var dl = addDownload(httpserv); dm.cancelDownload(dl.id); @@ -98,7 +98,7 @@ function run_test() { httpserv = new HttpServer(); httpserv.registerDirectory("/", do_get_cwd()); - httpserv.start(4444); + httpserv.start(-1); // our download listener var listener = { diff --git a/toolkit/components/downloads/test/unit/test_download_samename.js b/toolkit/components/downloads/test/unit/test_download_samename.js index 3dabbc62833..08e7a36bf12 100644 --- a/toolkit/components/downloads/test/unit/test_download_samename.js +++ b/toolkit/components/downloads/test/unit/test_download_samename.js @@ -111,7 +111,9 @@ function runNextTest() let set = DownloadListener.set = tests[currentTest]; currentTest++; - let channel = NetUtil.newChannel("http://localhost:4444" + set.serverURL); + let channel = NetUtil.newChannel("http://localhost:" + + httpserver.identity.primaryPort + + set.serverURL); let uriloader = Cc["@mozilla.org/uriloader;1"].getService(Ci.nsIURILoader); uriloader.openURI(channel, true, new WindowContext()); } @@ -148,7 +150,7 @@ function run_test() { Services.prefs.setBoolPref("browser.download.manager.showWhenStarting", false); httpserver = new HttpServer(); - httpserver.start(4444); + httpserver.start(-1); do_test_pending(); // setup files to be download, each with the same suggested filename diff --git a/toolkit/components/downloads/test/unit/test_guid.js b/toolkit/components/downloads/test/unit/test_guid.js index e5f1bbdb281..17a2a50f1af 100644 --- a/toolkit/components/downloads/test/unit/test_guid.js +++ b/toolkit/components/downloads/test/unit/test_guid.js @@ -6,7 +6,9 @@ const dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManage function run_test() { - let dl = addDownload(); + let server = new HttpServer(); + server.start(-1); + let dl = addDownload(server); do_test_pending(); do_print(dl.guid); @@ -23,4 +25,4 @@ function run_test() do_test_finished(); }); }); -} \ No newline at end of file +} diff --git a/toolkit/components/downloads/test/unit/test_offline_support.js b/toolkit/components/downloads/test/unit/test_offline_support.js index 1b32b47a38f..c040089fa1d 100644 --- a/toolkit/components/downloads/test/unit/test_offline_support.js +++ b/toolkit/components/downloads/test/unit/test_offline_support.js @@ -74,7 +74,7 @@ function run_test() } resp.bodyOutputStream.write(body, body.length); }); - httpserv.start(4444); + httpserv.start(-1); /** * 3. Perform various actions for certain download states @@ -141,7 +141,8 @@ function run_test() nsIWBP.PERSIST_FLAGS_BYPASS_CACHE | nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION; let dl = dm.addDownload(nsIDM.DOWNLOAD_TYPE_DOWNLOAD, - createURI("http://localhost:4444/resume"), + createURI("http://localhost:" + + httpserv.identity.primaryPort + "/resume"), createURI(destFile), null, null, Math.round(Date.now() * 1000), null, persist, false); persist.progressListener = dl.QueryInterface(nsIWPL); diff --git a/toolkit/components/downloads/test/unit/test_private_resume.js b/toolkit/components/downloads/test/unit/test_private_resume.js index 2717434bb45..4571250d06c 100644 --- a/toolkit/components/downloads/test/unit/test_private_resume.js +++ b/toolkit/components/downloads/test/unit/test_private_resume.js @@ -31,11 +31,11 @@ function run_test() { let full = ""; let body = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; //60 for (var i = 0; i < 1000; i++) { - full += body; + full += body; } response.write(full); }); - httpserv.start(4444); + httpserv.start(-1); let state = 0; @@ -66,8 +66,8 @@ function run_test() { // public request. state++; - - addDownload({ + + addDownload(httpserv, { isPrivate: true, sourceURI: downloadCSource, downloadName: downloadCName + "!!!", @@ -96,11 +96,13 @@ function run_test() { downloadUtils.downloadManager.addPrivacyAwareListener(listener); - const downloadCSource = "http://localhost:4444/head_download_manager.js"; + const downloadCSource = "http://localhost:" + + httpserv.identity.primaryPort + + "/head_download_manager.js"; const downloadCName = "download-C"; // First a public download that completes without interruption. - let dl = addDownload({ + let dl = addDownload(httpserv, { isPrivate: false, sourceURI: downloadCSource, downloadName: downloadCName, @@ -109,4 +111,4 @@ function run_test() { do_check_eq(downloadUtils.downloadManager.activeDownloadCount, 1); } }); -} \ No newline at end of file +} diff --git a/toolkit/components/downloads/test/unit/test_privatebrowsing.js b/toolkit/components/downloads/test/unit/test_privatebrowsing.js index 4b1600ab5c1..9be7b0ea9ce 100644 --- a/toolkit/components/downloads/test/unit/test_privatebrowsing.js +++ b/toolkit/components/downloads/test/unit/test_privatebrowsing.js @@ -85,10 +85,9 @@ function run_test() { do_test_pending(); let httpserv = new HttpServer(); httpserv.registerDirectory("/", do_get_cwd()); + httpserv.start(-1); - let tmpDir = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties). - get("TmpD", Ci.nsIFile); + let tmpDir = do_get_tempdir(); const nsIWBP = Ci.nsIWebBrowserPersist; // make sure we're starting with an empty DB @@ -153,7 +152,7 @@ function run_test() { do_check_eq(dm.activeDownloadCount, 0); // Create Download-B - let dlB = addDownload({ + let dlB = addDownload(httpserv, { isPrivate: true, targetFile: fileB, sourceURI: downloadBSource, @@ -182,8 +181,7 @@ function run_test() { Services.obs.notifyObservers(null, "last-pb-context-exited", null); // Create Download-C - httpserv.start(4444); - dlC = addDownload({ + dlC = addDownload(httpserv, { isPrivate: false, targetFile: fileC, sourceURI: downloadCSource, @@ -257,7 +255,9 @@ function run_test() { // properties of Download-C let downloadC = -1; - const downloadCSource = "http://localhost:4444/head_download_manager.js"; + const downloadCSource = "http://localhost:" + + httpserv.identity.primaryPort + + "/head_download_manager.js"; const downloadCDest = "download-file-C"; const downloadCName = "download-C"; @@ -276,7 +276,7 @@ function run_test() { let dlC; // Create Download-A - let dlA = addDownload({ + let dlA = addDownload(httpserv, { isPrivate: false, targetFile: fileA, sourceURI: downloadASource, diff --git a/toolkit/components/downloads/test/unit/test_privatebrowsing_cancel.js b/toolkit/components/downloads/test/unit/test_privatebrowsing_cancel.js index 04fc55f2f89..fea05cf2994 100644 --- a/toolkit/components/downloads/test/unit/test_privatebrowsing_cancel.js +++ b/toolkit/components/downloads/test/unit/test_privatebrowsing_cancel.js @@ -108,7 +108,7 @@ function run_test() { response.setHeader("Accept-Ranges", "none", false); response.write("foo"); }); - httpserv.start(4444); + httpserv.start(-1); let tmpDir = Cc["@mozilla.org/file/directory_service;1"]. getService(Ci.nsIProperties). @@ -140,7 +140,7 @@ function run_test() { do_check_eq(dlD.state, dm.DOWNLOAD_CANCELED); // Create Download-E - dlE = addDownload({ + dlE = addDownload(httpserv, { isPrivate: true, targetFile: fileE, sourceURI: downloadESource, @@ -164,7 +164,7 @@ function run_test() { do_check_eq(dlE.state, dm.DOWNLOAD_CANCELED); // Create Download-F - dlF = addDownload({ + dlF = addDownload(httpserv, { isPrivate: true, targetFile: fileF, sourceURI: downloadFSource, @@ -199,7 +199,7 @@ function run_test() { do_check_eq(dlF.state, dm.DOWNLOAD_CANCELED); // Create Download-G - dlG = addDownload({ + dlG = addDownload(httpserv, { isPrivate: false, targetFile: fileG, sourceURI: downloadGSource, @@ -218,23 +218,25 @@ function run_test() { dm.addPrivacyAwareListener(listener); + const PORT = httpserv.identity.primaryPort; + // properties of Download-D - const downloadDSource = "http://localhost:4444/noresume"; + const downloadDSource = "http://localhost:" + PORT + "/noresume"; const downloadDDest = "download-file-D"; const downloadDName = "download-D"; // properties of Download-E - const downloadESource = "http://localhost:4444/file/head_download_manager.js"; + const downloadESource = "http://localhost:" + PORT + "/file/head_download_manager.js"; const downloadEDest = "download-file-E"; const downloadEName = "download-E"; // properties of Download-F - const downloadFSource = "http://localhost:4444/file/head_download_manager.js"; + const downloadFSource = "http://localhost:" + PORT + "/file/head_download_manager.js"; const downloadFDest = "download-file-F"; const downloadFName = "download-F"; // properties of Download-G - const downloadGSource = "http://localhost:4444/noresume"; + const downloadGSource = "http://localhost:" + PORT + "/noresume"; const downloadGDest = "download-file-G"; const downloadGName = "download-G"; @@ -253,7 +255,7 @@ function run_test() { fileG.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); // Create Download-D - let dlD = addDownload({ + let dlD = addDownload(httpserv, { isPrivate: true, targetFile: fileD, sourceURI: downloadDSource, diff --git a/toolkit/components/downloads/test/unit/test_resume.js b/toolkit/components/downloads/test/unit/test_resume.js index 619711486b1..52b42e2fbd7 100644 --- a/toolkit/components/downloads/test/unit/test_resume.js +++ b/toolkit/components/downloads/test/unit/test_resume.js @@ -54,7 +54,7 @@ function run_test() } resp.bodyOutputStream.write(body, body.length); }); - httpserv.start(4444); + httpserv.start(-1); /** * 3. Perform various actions for certain download states @@ -122,7 +122,8 @@ function run_test() nsIWBP.PERSIST_FLAGS_BYPASS_CACHE | nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION; var dl = dm.addDownload(nsIDM.DOWNLOAD_TYPE_DOWNLOAD, - createURI("http://localhost:4444/resume"), + createURI("http://localhost:" + + httpserv.identity.primaryPort + "/resume"), createURI(destFile), null, null, Math.round(Date.now() * 1000), null, persist, false); persist.progressListener = dl.QueryInterface(nsIWPL); diff --git a/toolkit/components/downloads/test/unit/test_sleep_wake.js b/toolkit/components/downloads/test/unit/test_sleep_wake.js index 0ff764fb965..73df0a435b4 100644 --- a/toolkit/components/downloads/test/unit/test_sleep_wake.js +++ b/toolkit/components/downloads/test/unit/test_sleep_wake.js @@ -74,7 +74,7 @@ function run_test() } resp.bodyOutputStream.write(body, body.length); }); - httpserv.start(4444); + httpserv.start(-1); /** * 3. Perform various actions for certain download states @@ -141,7 +141,8 @@ function run_test() nsIWBP.PERSIST_FLAGS_BYPASS_CACHE | nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION; let dl = dm.addDownload(nsIDM.DOWNLOAD_TYPE_DOWNLOAD, - createURI("http://localhost:4444/resume"), + createURI("http://localhost:" + + httpserv.identity.primaryPort + "/resume"), createURI(destFile), null, null, Math.round(Date.now() * 1000), null, persist, false); persist.progressListener = dl.QueryInterface(nsIWPL); From 01876f031be48d52c2c620cab04c24f0abcb2964 Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Wed, 10 Jul 2013 14:10:04 -0700 Subject: [PATCH 11/31] Bug 889076 - Use the profile dir to store minidumps in the crashreporter xpcshell tests. r=ted --- testing/xpcshell/head.js | 2 +- .../crashreporter/test/unit/crasher_subprocess_head.js | 7 ++++++- toolkit/crashreporter/test/unit/head_crashreporter.js | 8 ++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js index d0f4afd06a0..fc2acdf2715 100644 --- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -87,7 +87,7 @@ try { // nsIXULRuntime is not available in some configurations. Components.classes["@mozilla.org/toolkit/crash-reporter;1"] .getService(Components.interfaces.nsICrashReporter)) { crashReporter.enabled = true; - crashReporter.minidumpPath = do_get_cwd(); + crashReporter.minidumpPath = do_get_tempdir(); } } } diff --git a/toolkit/crashreporter/test/unit/crasher_subprocess_head.js b/toolkit/crashreporter/test/unit/crasher_subprocess_head.js index 161c6031f5b..e801804fb84 100644 --- a/toolkit/crashreporter/test/unit/crasher_subprocess_head.js +++ b/toolkit/crashreporter/test/unit/crasher_subprocess_head.js @@ -3,6 +3,11 @@ let cwd = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("CurWorkD", Components.interfaces.nsILocalFile); +// get the temp dir +let env = Components.classes["@mozilla.org/process/environment;1"].getService(Components.interfaces.nsIEnvironment); +let _tmpd = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); +_tmpd.initWithPath(env.get("XPCSHELL_TEST_TEMP_DIR")); + let crashReporter = Components.classes["@mozilla.org/toolkit/crash-reporter;1"] .getService(Components.interfaces.nsICrashReporter); @@ -13,7 +18,7 @@ let processType = Components.classes["@mozilla.org/xre/runtime;1"]. getService(Components.interfaces.nsIXULRuntime).processType; if (processType == Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT) { crashReporter.enabled = true; - crashReporter.minidumpPath = cwd; + crashReporter.minidumpPath = _tmpd; } let ios = Components.classes["@mozilla.org/network/io-service;1"] diff --git a/toolkit/crashreporter/test/unit/head_crashreporter.js b/toolkit/crashreporter/test/unit/head_crashreporter.js index e4b7d1ac690..8153b65c244 100644 --- a/toolkit/crashreporter/test/unit/head_crashreporter.js +++ b/toolkit/crashreporter/test/unit/head_crashreporter.js @@ -72,7 +72,7 @@ function handleMinidump(callback) { // find minidump let minidump = null; - let en = do_get_cwd().directoryEntries; + let en = do_get_tempdir().directoryEntries; while (en.hasMoreElements()) { let f = en.getNext().QueryInterface(Components.interfaces.nsILocalFile); if (f.leafName.substr(-4) == ".dmp") { @@ -116,7 +116,7 @@ function do_content_crash(setup, callback) let crashReporter = Components.classes["@mozilla.org/toolkit/crash-reporter;1"] .getService(Components.interfaces.nsICrashReporter); - crashReporter.minidumpPath = do_get_cwd(); + crashReporter.minidumpPath = do_get_tempdir(); let headfile = do_get_file("../unit/crasher_subprocess_head.js"); let tailfile = do_get_file("../unit/crasher_subprocess_tail.js"); @@ -127,14 +127,14 @@ function do_content_crash(setup, callback) } let handleCrash = function() { - try { + try { handleMinidump(callback); } catch (x) { do_report_unexpected_exception(x); } do_test_finished(); }; - + sendCommand("load(\"" + headfile.path.replace(/\\/g, "/") + "\");", function() sendCommand(setup, function() sendCommand("load(\"" + tailfile.path.replace(/\\/g, "/") + "\");", From 0fd512ba48abd0d90b0406d9c6c8e91007e13bdd Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Wed, 17 Jul 2013 06:11:18 -0700 Subject: [PATCH 12/31] Bug 894782 - Fix callsite cloning interaction with inline dispatch in Ion. (r=jandem) --- js/src/ion/IonBuilder.cpp | 14 +++++++++++--- js/src/ion/MIR.cpp | 11 +++-------- js/src/ion/MIR.h | 2 +- js/src/jit-test/tests/parallelarray/bug894782.js | 4 ++++ 4 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 js/src/jit-test/tests/parallelarray/bug894782.js diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index a68a18ef8c9..2fa4ca79d5c 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -4098,12 +4098,16 @@ IonBuilder::inlineCalls(CallInfo &callInfo, AutoObjectVector &targets, callInfo.pushFormals(dispatchBlock); // Patch any InlinePropertyTable to only contain functions that are inlineable. - // Also guarantee that the table uses functions from |targets| instead of |originals|. + // + // Note that we trim using originals, as callsite clones are not user + // visible. We don't patch the entries inside the table with the cloned + // targets, as the entries should only be used for comparison. + // // The InlinePropertyTable will also be patched at the end to exclude native functions // that vetoed inlining. if (maybeCache) { InlinePropertyTable *propTable = maybeCache->propTable(); - propTable->trimToAndMaybePatchTargets(targets, originals); + propTable->trimToTargets(originals); if (propTable->numEntries() == 0) maybeCache = NULL; } @@ -4153,6 +4157,10 @@ IonBuilder::inlineCalls(CallInfo &callInfo, AutoObjectVector &targets, // Inline each of the inlineable targets. JS_ASSERT(targets.length() == originals.length()); for (uint32_t i = 0; i < targets.length(); i++) { + // When original != target, the target is a callsite clone. The + // original should be used for guards, and the target should be the + // actual function inlined. + JSFunction *original = &originals[i]->as(); JSFunction *target = &targets[i]->as(); // Target must be inlineable. @@ -4160,7 +4168,7 @@ IonBuilder::inlineCalls(CallInfo &callInfo, AutoObjectVector &targets, continue; // Target must be reachable by the MDispatchInstruction. - if (maybeCache && !maybeCache->propTable()->hasFunction(target)) { + if (maybeCache && !maybeCache->propTable()->hasFunction(original)) { choiceSet[i] = false; continue; } diff --git a/js/src/ion/MIR.cpp b/js/src/ion/MIR.cpp index ebcb5870fd0..64258579739 100644 --- a/js/src/ion/MIR.cpp +++ b/js/src/ion/MIR.cpp @@ -2293,8 +2293,7 @@ InlinePropertyTable::trimTo(AutoObjectVector &targets, Vector &choiceSet) } void -InlinePropertyTable::trimToAndMaybePatchTargets(AutoObjectVector &targets, - AutoObjectVector &originals) +InlinePropertyTable::trimToTargets(AutoObjectVector &targets) { IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases", (int)numEntries()); @@ -2302,12 +2301,8 @@ InlinePropertyTable::trimToAndMaybePatchTargets(AutoObjectVector &targets, size_t i = 0; while (i < numEntries()) { bool foundFunc = false; - // Compare using originals, but if we find a matching function, - // patch it to the target, which might be a clone. - for (size_t j = 0; j < originals.length(); j++) { - if (entries_[i]->func == originals[j]) { - if (entries_[i]->func != targets[j]) - entries_[i] = new Entry(entries_[i]->typeObj, &targets[j]->as()); + for (size_t j = 0; j < targets.length(); j++) { + if (entries_[i]->func == targets[j]) { foundFunc = true; break; } diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 0b96632b3d4..6911735ef86 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -5464,7 +5464,7 @@ class InlinePropertyTable : public TempObject void trimTo(AutoObjectVector &targets, Vector &choiceSet); // Ensure that the InlinePropertyTable's domain is a subset of |targets|. - void trimToAndMaybePatchTargets(AutoObjectVector &targets, AutoObjectVector &originals); + void trimToTargets(AutoObjectVector &targets); }; class MGetPropertyCache diff --git a/js/src/jit-test/tests/parallelarray/bug894782.js b/js/src/jit-test/tests/parallelarray/bug894782.js new file mode 100644 index 00000000000..d1aee892ab1 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/bug894782.js @@ -0,0 +1,4 @@ +// Don't crash + +print(ParallelArray()) +String(Object.create(ParallelArray(8077, function() {}))) From f3b49b50e2e3f71b58ecda069f86f3cab8ad2b15 Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Wed, 17 Jul 2013 09:19:19 -0400 Subject: [PATCH 13/31] Bug 894882 - Make sure the correct host is displayed when prompting for WebRTC from iframes r=wesj --- mobile/android/chrome/content/WebrtcUI.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mobile/android/chrome/content/WebrtcUI.js b/mobile/android/chrome/content/WebrtcUI.js index 2333559fa0a..079021455b7 100644 --- a/mobile/android/chrome/content/WebrtcUI.js +++ b/mobile/android/chrome/content/WebrtcUI.js @@ -82,7 +82,7 @@ var WebrtcUI = { browser.ownerDocument.defaultView.navigator.mozGetUserMediaDevices( function (devices) { - WebrtcUI.prompt(browser, callID, params.audio, params.video, devices); + WebrtcUI.prompt(windowID, callID, params.audio, params.video, devices); }, function (error) { Cu.reportError(error); @@ -156,7 +156,7 @@ var WebrtcUI = { } }, - prompt: function prompt(aBrowser, aCallID, aAudioRequested, aVideoRequested, aDevices) { + prompt: function prompt(aWindowID, aCallID, aAudioRequested, aVideoRequested, aDevices) { let audioDevices = []; let videoDevices = []; for (let device of aDevices) { @@ -183,7 +183,8 @@ var WebrtcUI = { else return; - let host = aBrowser.contentDocument.documentURIObject.asciiHost; + let contentWindow = Services.wm.getOuterWindowWithId(aWindowID); + let host = contentWindow.document.documentURIObject.host; let requestor = BrowserApp.manifest ? "'" + BrowserApp.manifest.name + "'" : host; let message = Strings.browser.formatStringFromName("getUserMedia.share" + requestType + ".message", [ requestor ], 1); From 02c59c8c531b344ffb7875ac3c095b215ce09500 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 17 Jul 2013 09:21:02 -0400 Subject: [PATCH 14/31] Bug 889480 - When NP_Initialize fails, we should not try to call NP_Shutdown later, r=gfritzsche --HG-- extra : rebase_source : f55c38a81af16badbc27f3eaa82e7f8dbedd4d0c --- dom/plugins/ipc/PluginModuleParent.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index 075679cb7a2..91548a8ddc1 100755 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -1192,9 +1192,11 @@ PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs uint32_t flags = 0; if (!CallNP_Initialize(flags, error)) { + mShutdown = true; return NS_ERROR_FAILURE; } else if (*error != NPERR_NO_ERROR) { + mShutdown = true; return NS_OK; } @@ -1220,8 +1222,14 @@ PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) flags |= kAllowAsyncDrawing; #endif - if (!CallNP_Initialize(flags, error)) + if (!CallNP_Initialize(flags, error)) { + mShutdown = true; return NS_ERROR_FAILURE; + } + if (*error != NPERR_NO_ERROR) { + mShutdown = true; + return NS_OK; + } #if defined XP_WIN // Send the info needed to join the chrome process's audio session to the From 5d426fcffacff06bd09a86929a1360d5f3242aa0 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 17 Jul 2013 09:21:02 -0400 Subject: [PATCH 15/31] Bug 888908 - When the location bar is missing, don't auto-popup the plugin notification (ever). It will still pop up when clicking the in-page UI. And Australis will make this code moot anyway, r=jaws --HG-- extra : rebase_source : 69b7a078b7202a9806eaa33829d6107062e5a589 --- browser/base/content/browser-plugins.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/browser/base/content/browser-plugins.js b/browser/base/content/browser-plugins.js index 44933eca9f5..056d67900a2 100644 --- a/browser/base/content/browser-plugins.js +++ b/browser/base/content/browser-plugins.js @@ -766,8 +766,7 @@ var gPluginHandler = { fallbackType == plugin.PLUGIN_BLOCKLISTED; }); let dismissed = notification ? notification.dismissed : true; - // Always show the doorhanger if the anchor is not available. - if (!isElementVisible(gURLBar) || aPrimaryPlugin) + if (aPrimaryPlugin) dismissed = false; let primaryPluginPermission = null; From 1de4d3c8fe7551ba766fe6eec43af48797842459 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 17 Jul 2013 09:21:02 -0400 Subject: [PATCH 16/31] Bug 889788 - Plugin doorhanger breaks when plugins are dynamically removed from a page. This implements the short-term solution of hiding the plugin doorhanger when no more plugins are on the page (the better long-term solution will keep showing the UI and track all the plugins a page ever uses). This also implements a short-term solution when a user enables a plugin. Also bug 887088 - Short-term UI solution: when a user loads multiple tabs from a site and enables a plugin on one of them, the plugins are not enabled on other tabs but the "continue allowing" button does nothing. This patch makes the "Continue Allowing" button enable existing plugins of that type. r=jaws r=jschoenick --HG-- extra : rebase_source : 4b24a6d5bf3b5c33b1e19702eca676b340b5d8b1 --- browser/base/content/browser-plugins.js | 43 ++++++++++++--------- browser/base/content/browser.js | 1 + browser/base/content/urlbarBindings.xml | 29 ++++++++++---- content/base/src/nsObjectLoadingContent.cpp | 28 ++++++++++---- 4 files changed, 67 insertions(+), 34 deletions(-) diff --git a/browser/base/content/browser-plugins.js b/browser/base/content/browser-plugins.js index 056d67900a2..3ce2e5d877c 100644 --- a/browser/base/content/browser-plugins.js +++ b/browser/base/content/browser-plugins.js @@ -217,14 +217,21 @@ var gPluginHandler = { }, handleEvent : function(event) { - let plugin = event.target; - let doc = plugin.ownerDocument; - - // We're expecting the target to be a plugin. - if (!(plugin instanceof Ci.nsIObjectLoadingContent)) - return; + let plugin; + let doc; let eventType = event.type; + if (eventType === "PluginRemoved") { + doc = event.target; + } + else { + plugin = event.target; + doc = plugin.ownerDocument; + + if (!(plugin instanceof Ci.nsIObjectLoadingContent)) + return; + } + if (eventType == "PluginBindingAttached") { // The plugin binding fires this event when it is created. // As an untrusted event, ensure that this object actually has a binding @@ -304,6 +311,7 @@ var gPluginHandler = { break; case "PluginInstantiated": + case "PluginRemoved": this._showClickToPlayNotification(browser); break; } @@ -686,18 +694,12 @@ var gPluginHandler = { switch (aNewState) { case "allownow": - if (aPluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) { - return; - } permission = Ci.nsIPermissionManager.ALLOW_ACTION; expireType = Ci.nsIPermissionManager.EXPIRE_SESSION; expireTime = Date.now() + Services.prefs.getIntPref(this.PREF_SESSION_PERSIST_MINUTES) * 60 * 1000; break; case "allowalways": - if (aPluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) { - return; - } permission = Ci.nsIPermissionManager.ALLOW_ACTION; expireType = Ci.nsIPermissionManager.EXPIRE_TIME; expireTime = Date.now() + @@ -705,25 +707,28 @@ var gPluginHandler = { break; case "block": - if (aPluginInfo.fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) { - return; - } permission = Ci.nsIPermissionManager.PROMPT_ACTION; expireType = Ci.nsIPermissionManager.EXPIRE_NEVER; expireTime = 0; break; + // In case a plugin has already been allowed in another tab, the "continue allowing" button + // shouldn't change any permissions but should run the plugin-enablement code below. + case "continue": + break; default: Cu.reportError(Error("Unexpected plugin state: " + aNewState)); return; } let browser = aNotification.browser; - Services.perms.add(browser.currentURI, aPluginInfo.permissionString, - permission, expireType, expireTime); + if (aNewState != "continue") { + Services.perms.add(browser.currentURI, aPluginInfo.permissionString, + permission, expireType, expireTime); - if (aNewState == "block") { - return; + if (aNewState == "block") { + return; + } } // Manually activate the plugins that would have been automatically diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 59789cd0231..a07435d9189 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -755,6 +755,7 @@ var gBrowserInit = { gBrowser.addEventListener("PluginCrashed", gPluginHandler, true); gBrowser.addEventListener("PluginOutdated", gPluginHandler, true); gBrowser.addEventListener("PluginInstantiated", gPluginHandler, true); + gBrowser.addEventListener("PluginRemoved", gPluginHandler, true); gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true); diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index 060a9aaabd2..8414b3ac0db 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -1574,12 +1574,19 @@ this.appendChild(item); this._items.push(item); } - if (this.notification.options.centerActions.length == 1) { - this._setState(this._states.SINGLE); - } else if (this.notification.options.primaryPlugin) { - this._setState(this._states.MULTI_COLLAPSED); - } else { - this._setState(this._states.MULTI_EXPANDED); + switch (this.notification.options.centerActions.length) { + case 0: + PopupNotifications._dismiss(); + break; + case 1: + this._setState(this._states.SINGLE); + break; + default: + if (this.notification.options.primaryPlugin) { + this._setState(this._states.MULTI_COLLAPSED); + } else { + this._setState(this._states.MULTI_EXPANDED); + } } ]]> @@ -1645,7 +1652,7 @@ button2 = { label: "pluginContinue.label", accesskey: "pluginContinue.accesskey", - action: "_cancel", + action: "_singleContinue", default: true }; switch (action.blocklistState) { @@ -1819,6 +1826,14 @@ this._cancel(); ]]> + + + GetCurrentDoc()) + , mEvent(aEvent) + { + } + + nsSimplePluginEvent(nsIDocument* aTarget, const nsAString& aEvent) + : mTarget(aTarget) + , mDocument(aTarget) + , mEvent(aEvent) + { + } ~nsSimplePluginEvent() {} NS_IMETHOD Run(); private: - nsCOMPtr mContent; + nsCOMPtr mTarget; + nsCOMPtr mDocument; nsString mEvent; }; NS_IMETHODIMP nsSimplePluginEvent::Run() { - LOG(("OBJLC [%p]: nsSimplePluginEvent firing event \"%s\"", mContent.get(), + LOG(("OBJLC [%p]: nsSimplePluginEvent firing event \"%s\"", mTarget.get(), mEvent.get())); - nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent, + nsContentUtils::DispatchTrustedEvent(mDocument, mTarget, mEvent, true, true); return NS_OK; } @@ -674,7 +684,9 @@ nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent) /// would keep the docshell around, but trash the frameloader UnloadObject(); } - + nsCOMPtr ev = new nsSimplePluginEvent(thisContent->GetCurrentDoc(), + NS_LITERAL_STRING("PluginRemoved")); + NS_DispatchToCurrentThread(ev); } nsObjectLoadingContent::nsObjectLoadingContent() From 57fd2695a34fea26c7b15701e64c92fca316af6e Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Wed, 17 Jul 2013 09:27:49 -0400 Subject: [PATCH 17/31] Bug 868859 - Use CPOWs to pass browser.contentWindow and webProgress.DOMWindow in e10s. r=felipe --- toolkit/content/browser-child.js | 26 ++++++++++++++++++---- toolkit/content/widgets/remote-browser.xml | 10 +++++++++ toolkit/modules/RemoteWebProgress.jsm | 5 ++++- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/toolkit/content/browser-child.js b/toolkit/content/browser-child.js index 25176aaa128..0ddc8379071 100644 --- a/toolkit/content/browser-child.js +++ b/toolkit/content/browser-child.js @@ -29,12 +29,24 @@ let WebProgressListener = { }; }, + _setupObjects: function setupObjects(aWebProgress) { + let win = docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + return { + contentWindow: win, + // DOMWindow is not necessarily the content-window with subframes. + DOMWindow: aWebProgress.DOMWindow + }; + }, + onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { let json = this._setupJSON(aWebProgress, aRequest); + let objects = this._setupObjects(aWebProgress); + json.stateFlags = aStateFlags; json.status = aStatus; - sendAsyncMessage("Content:StateChange", json); + sendAsyncMessage("Content:StateChange", json, objects); }, onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) { @@ -45,29 +57,35 @@ let WebProgressListener = { let charset = content.document.characterSet; let json = this._setupJSON(aWebProgress, aRequest); + let objects = this._setupObjects(aWebProgress); + json.documentURI = aWebProgress.DOMWindow.document.documentURIObject.spec; json.location = spec; json.canGoBack = docShell.canGoBack; json.canGoForward = docShell.canGoForward; json.charset = charset.toString(); - sendAsyncMessage("Content:LocationChange", json); + sendAsyncMessage("Content:LocationChange", json, objects); }, onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) { let json = this._setupJSON(aWebProgress, aRequest); + let objects = this._setupObjects(aWebProgress); + json.status = aStatus; json.message = aMessage; - sendAsyncMessage("Content:StatusChange", json); + sendAsyncMessage("Content:StatusChange", json, objects); }, onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) { let json = this._setupJSON(aWebProgress, aRequest); + let objects = this._setupObjects(aWebProgress); + json.state = aState; json.status = SecurityUI.getSSLStatusAsString(); - sendAsyncMessage("Content:SecurityChange", json); + sendAsyncMessage("Content:SecurityChange", json, objects); }, QueryInterface: function QueryInterface(aIID) { diff --git a/toolkit/content/widgets/remote-browser.xml b/toolkit/content/widgets/remote-browser.xml index 34611763d83..478d55e6818 100644 --- a/toolkit/content/widgets/remote-browser.xml +++ b/toolkit/content/widgets/remote-browser.xml @@ -62,6 +62,16 @@ onget="return this._characterSet" readonly="true"/> + null + + + + + Date: Wed, 8 May 2013 23:16:46 +0200 Subject: [PATCH 18/31] Bug 868859 - Make chromeWindow.content work in e10s. r=bz --- browser/base/content/browser.js | 4 ++++ browser/metro/base/content/browser.js | 4 ++++ docshell/base/nsIDocShellTreeOwner.idl | 5 ++++- dom/base/nsGlobalWindow.cpp | 22 +++++++++++++++++++ dom/interfaces/base/nsIBrowserDOMWindow.idl | 9 +++++++- dom/interfaces/base/nsIDOMJSWindow.idl | 5 ++++- dom/interfaces/base/nsIDOMWindow.idl | 4 ++-- dom/ipc/test.xul | 3 +++ .../webBrowser/nsDocShellTreeOwner.cpp | 11 +++++++++- mobile/android/chrome/content/browser.js | 4 ++++ xpfe/appshell/src/nsChromeTreeOwner.cpp | 21 ++++++++++++++++++ xpfe/appshell/src/nsContentTreeOwner.cpp | 10 ++++++++- 12 files changed, 95 insertions(+), 7 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index a07435d9189..3aa3771711c 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -4433,6 +4433,10 @@ nsBrowserAccess.prototype = { isTabContentWindow: function (aWindow) { return gBrowser.browsers.some(function (browser) browser.contentWindow == aWindow); + }, + + get contentWindow() { + return gBrowser.contentWindow; } } diff --git a/browser/metro/base/content/browser.js b/browser/metro/base/content/browser.js index d44ec31db76..1e414a90736 100644 --- a/browser/metro/base/content/browser.js +++ b/browser/metro/base/content/browser.js @@ -1205,6 +1205,10 @@ nsBrowserAccess.prototype = { isTabContentWindow: function(aWindow) { return Browser.browsers.some(function (browser) browser.contentWindow == aWindow); + }, + + get contentWindow() { + return Browser.selectedBrowser.contentWindow; } }; diff --git a/docshell/base/nsIDocShellTreeOwner.idl b/docshell/base/nsIDocShellTreeOwner.idl index 74071acc988..6bcd1f9b501 100644 --- a/docshell/base/nsIDocShellTreeOwner.idl +++ b/docshell/base/nsIDocShellTreeOwner.idl @@ -12,7 +12,7 @@ interface nsIDocShellTreeItem; -[scriptable, uuid(932f9f93-8e21-4728-a527-cafc64b4d831)] +[scriptable, uuid(6cd89e60-1060-491e-8c31-ce969435ec56)] interface nsIDocShellTreeOwner : nsISupports { /* @@ -64,6 +64,9 @@ interface nsIDocShellTreeOwner : nsISupports */ readonly attribute nsIDocShellTreeItem primaryContentShell; + [implicit_jscontext] + readonly attribute jsval contentWindow; + /* Tells the tree owner to size its window or parent window in such a way that the shell passed along will be the size specified. diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 72433184c67..9d8203ec77c 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3618,6 +3618,28 @@ nsGlobalWindow::GetContent(nsIDOMWindow** aContent) return NS_OK; } +NS_IMETHODIMP +nsGlobalWindow::GetScriptableContent(JSContext* aCx, JS::Value* aVal) +{ + nsCOMPtr content; + nsresult rv = GetContent(getter_AddRefs(content)); + NS_ENSURE_SUCCESS(rv, rv); + + if (content || !nsContentUtils::IsCallerChrome() || !IsChromeWindow()) { + JS::Rooted global(aCx, JS_GetGlobalForScopeChain(aCx)); + if (content && global) { + nsCOMPtr wrapper; + return nsContentUtils::WrapNative(aCx, global, content, aVal, + getter_AddRefs(wrapper)); + } + return NS_ERROR_FAILURE; + } + + // Something tries to get .content on a ChromeWindow, try to fetch the CPOW. + nsCOMPtr treeOwner = GetTreeOwner(); + NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE); + return treeOwner->GetContentWindow(aCx, aVal); +} NS_IMETHODIMP nsGlobalWindow::GetPrompter(nsIPrompt** aPrompt) diff --git a/dom/interfaces/base/nsIBrowserDOMWindow.idl b/dom/interfaces/base/nsIBrowserDOMWindow.idl index 1eed3350380..8f009159a06 100644 --- a/dom/interfaces/base/nsIBrowserDOMWindow.idl +++ b/dom/interfaces/base/nsIBrowserDOMWindow.idl @@ -9,7 +9,7 @@ interface nsIDOMWindow; interface nsIURI; interface nsIFrameLoaderOwner; -[scriptable, uuid(3ab89888-eb41-4dc8-b347-115555f47c80)] +[scriptable, uuid(e420bd32-b8c4-4b47-8cca-09e0bddbb0c3)] /** * The C++ source has access to the browser script source through @@ -92,5 +92,12 @@ interface nsIBrowserDOMWindow : nsISupports * currently open tab in this toplevel browser window. */ boolean isTabContentWindow(in nsIDOMWindow aWindow); + + /** + * The contentWindow property of the currently selected browser. + * This is used to implement .content in remote-Firefox. + */ + + readonly attribute jsval contentWindow; }; diff --git a/dom/interfaces/base/nsIDOMJSWindow.idl b/dom/interfaces/base/nsIDOMJSWindow.idl index faef7e13e35..343a071a6ed 100644 --- a/dom/interfaces/base/nsIDOMJSWindow.idl +++ b/dom/interfaces/base/nsIDOMJSWindow.idl @@ -5,7 +5,7 @@ #include "domstubs.idl" -[scriptable, uuid(35b653f4-e679-4843-8391-89cb2f5a9ba4)] +[scriptable, uuid(f28c92a2-302a-4448-b589-46af599de352)] interface nsIDOMJSWindow : nsISupports { void dump(in DOMString str); @@ -66,4 +66,7 @@ interface nsIDOMJSWindow : nsISupports * This property is "replaceable" in JavaScript. */ readonly attribute nsIDOMWindow frames; + + [implicit_jscontext, binaryname(ScriptableContent)] + readonly attribute jsval content; }; diff --git a/dom/interfaces/base/nsIDOMWindow.idl b/dom/interfaces/base/nsIDOMWindow.idl index 8fdb79cb89e..e769ed6d8b0 100644 --- a/dom/interfaces/base/nsIDOMWindow.idl +++ b/dom/interfaces/base/nsIDOMWindow.idl @@ -25,7 +25,7 @@ interface nsIVariant; * @see */ -[scriptable, uuid(be62660a-e3f6-409c-a4a9-378364a9526f)] +[scriptable, uuid(db8ea3c8-6997-460a-8715-0a1cbf20f15d)] interface nsIDOMWindow : nsISupports { // the current browsing context @@ -360,7 +360,7 @@ interface nsIDOMWindow : nsISupports void sizeToContent(); /* [replaceable] content */ - readonly attribute nsIDOMWindow content; + [noscript] readonly attribute nsIDOMWindow content; /* [replaceable] prompter */ [noscript] readonly attribute nsIPrompt prompter; diff --git a/dom/ipc/test.xul b/dom/ipc/test.xul index 25efdb6f6ce..3eede172db9 100644 --- a/dom/ipc/test.xul +++ b/dom/ipc/test.xul @@ -253,6 +253,9 @@ }, openDialog: function(aType, aName, aFeatures, aArguments, aFrameElement) { alert(aType + ", " + aName + ", " + aFeatures + ", " + aArguments + ", " + aFrameElement); + }, + get contentWindow() { + return null; } } diff --git a/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp b/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp index 6fe02ef98ff..64fcf164df2 100644 --- a/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp +++ b/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp @@ -353,7 +353,7 @@ nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) { NS_ENSURE_ARG_POINTER(aShell); - if(mTreeOwner) + if (mTreeOwner) return mTreeOwner->GetPrimaryContentShell(aShell); *aShell = (mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell); @@ -362,6 +362,15 @@ nsDocShellTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) return NS_OK; } +NS_IMETHODIMP +nsDocShellTreeOwner::GetContentWindow(JSContext* aCx, JS::Value* aVal) +{ + if (mTreeOwner) + return mTreeOwner->GetContentWindow(aCx, aVal); + + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX, int32_t aCY) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 3014ca00817..99780a6da36 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2492,6 +2492,10 @@ nsBrowserAccess.prototype = { isTabContentWindow: function(aWindow) { return BrowserApp.getBrowserForWindow(aWindow) != null; + }, + + get contentWindow() { + return BrowserApp.selectedBrowser.contentWindow; } }; diff --git a/xpfe/appshell/src/nsChromeTreeOwner.cpp b/xpfe/appshell/src/nsChromeTreeOwner.cpp index b5cdf2c0858..9240472bf96 100644 --- a/xpfe/appshell/src/nsChromeTreeOwner.cpp +++ b/xpfe/appshell/src/nsChromeTreeOwner.cpp @@ -24,6 +24,8 @@ #include "nsIDOMNodeList.h" #include "nsIDOMXULElement.h" #include "nsIXULBrowserWindow.h" +#include "nsIDOMChromeWindow.h" +#include "nsIBrowserDOMWindow.h" // CIDs static NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID); @@ -245,6 +247,25 @@ NS_IMETHODIMP nsChromeTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aS return mXULWindow->GetPrimaryContentShell(aShell); } +NS_IMETHODIMP +nsChromeTreeOwner::GetContentWindow(JSContext* aCx, JS::Value* aVal) +{ + NS_ENSURE_STATE(mXULWindow); + + nsCOMPtr domWin; + mXULWindow->GetWindowDOMWindow(getter_AddRefs(domWin)); + nsCOMPtr chromeWin = do_QueryInterface(domWin); + if (!chromeWin) + return NS_OK; + + nsCOMPtr browserDOMWin; + chromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin)); + if (!browserDOMWin) + return NS_OK; + + return browserDOMWin->GetContentWindow(aVal); +} + NS_IMETHODIMP nsChromeTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX, int32_t aCY) { diff --git a/xpfe/appshell/src/nsContentTreeOwner.cpp b/xpfe/appshell/src/nsContentTreeOwner.cpp index ba985ddcd76..2a2e0dff8d1 100644 --- a/xpfe/appshell/src/nsContentTreeOwner.cpp +++ b/xpfe/appshell/src/nsContentTreeOwner.cpp @@ -292,12 +292,20 @@ nsContentTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) return mXULWindow->ContentShellRemoved(aContentShell); } -NS_IMETHODIMP nsContentTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) +NS_IMETHODIMP +nsContentTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) { NS_ENSURE_STATE(mXULWindow); return mXULWindow->GetPrimaryContentShell(aShell); } +NS_IMETHODIMP +nsContentTreeOwner::GetContentWindow(JSContext* aCx, JS::Value* aVal) +{ + NS_ENSURE_STATE(mXULWindow); + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP nsContentTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX, int32_t aCY) { From 40feac8ae562c7e963f6255343e282f320914661 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Wed, 17 Jul 2013 09:27:49 -0400 Subject: [PATCH 19/31] Bug 893858 - More testing for CPOWs and two small bug fixes. r=dvander --- content/base/test/chrome/cpows_child.js | 9 +++++- content/base/test/chrome/cpows_parent.xul | 35 ++++++++++++++++++++++- js/ipc/JavaScriptChild.cpp | 2 +- js/src/jsapi.cpp | 11 ++++--- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/content/base/test/chrome/cpows_child.js b/content/base/test/chrome/cpows_child.js index edfbf5f311d..a53c7c1940d 100644 --- a/content/base/test/chrome/cpows_child.js +++ b/content/base/test/chrome/cpows_child.js @@ -27,8 +27,15 @@ function make_object() o.b = true; o.s = "hello"; o.x = { i: 10 }; - o.f = function () { return 99; } + o.f = function () { return 99; }; + + // Doing anything with this Proxy will throw. + var throwing = new Proxy({}, new Proxy({}, { + get: function (trap) { throw trap; } + })); + return { "data": o, + "throwing": throwing, "document": content.document }; } diff --git a/content/base/test/chrome/cpows_parent.xul b/content/base/test/chrome/cpows_parent.xul index 52461b42bcf..7d9592a8f96 100644 --- a/content/base/test/chrome/cpows_parent.xul +++ b/content/base/test/chrome/cpows_parent.xul @@ -37,6 +37,39 @@ ok(data.b === false, "boolean property"); ok(data.s === "bye", "string property"); ok(data.x === null, "nested property"); + + let throwing = message.objects.throwing; + // Based on the table on: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy + let tests = [ + () => Object.getOwnPropertyDescriptor(throwing, 'test'), + () => Object.getOwnPropertyNames(throwing), + () => Object.defineProperty(throwing, 'test', {value: 1}), + () => delete throwing.test, + () => "test" in throwing, + () => Object.prototype.hasOwnProperty.call(throwing, 'test'), + () => throwing.test, + () => { throwing.test = 1 }, + // () => { for (let prop in throwing) {} }, Bug 783829 + () => { for (let prop of throwing) {} }, + () => Object.keys(throwing), + () => Function.prototype.call.call(throwing), + () => new throwing, + () => Object.preventExtensions(throwing), + () => Object.freeze(throwing), + () => Object.seal(throwing), + ] + + for (let test of tests) { + let threw = false; + try { + test() + } catch (e) { + threw = true; + } + ok(threw, "proxy operation threw exception"); + } + } function recvAsyncMessage(message) { @@ -57,7 +90,7 @@ run_tests("inprocess"); return; } - + finish(); } diff --git a/js/ipc/JavaScriptChild.cpp b/js/ipc/JavaScriptChild.cpp index 9ee02c5eeba..52bcfc90472 100644 --- a/js/ipc/JavaScriptChild.cpp +++ b/js/ipc/JavaScriptChild.cpp @@ -434,7 +434,7 @@ JavaScriptChild::AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs, boo return fail(cx, rs); *result = !!extensible; - return true; + return ok(rs); } bool diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 57711331d61..76dc052a843 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -7206,12 +7206,11 @@ JS_GetScriptedGlobal(JSContext *cx) JS_PUBLIC_API(JSBool) JS_PreventExtensions(JSContext *cx, JS::HandleObject obj) { - JSBool extensible; - if (!JS_IsExtensible(cx, obj, &extensible)) - return JS_TRUE; - if (extensible) - return JS_TRUE; - + bool extensible; + if (!JSObject::isExtensible(cx, obj, &extensible)) + return false; + if (!extensible) + return true; return JSObject::preventExtensions(cx, obj); } From b42a1584eaa83f913a15598f9be9a33d4a3a8b59 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Mon, 22 Apr 2013 18:41:59 +0200 Subject: [PATCH 20/31] Bug 886903 - Send correct UserAgent data for content process. r=jdm --- dom/ipc/ContentChild.cpp | 5 ++++- dom/ipc/ContentChild.h | 5 ++++- dom/ipc/ContentParent.cpp | 4 +++- dom/ipc/PContent.ipdl | 2 +- toolkit/xre/nsAppRunner.cpp | 8 ++++++-- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 08062664fd1..9ad914432d9 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1181,10 +1181,13 @@ PreloadSlowThings() } bool -ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID) +ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID, + const nsCString& name, const nsCString& UAName) { mAppInfo.version.Assign(version); mAppInfo.buildID.Assign(buildID); + mAppInfo.name.Assign(name); + mAppInfo.UAName.Assign(UAName); // If we're part of the mozbrowser machinery, go ahead and start // preloading things. We can only do this for mozbrowser because // PreloadSlowThings() may set the docshell of the first TabChild diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index bdae4f9f479..127fad3de4f 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -60,6 +60,8 @@ public: { nsCString version; nsCString buildID; + nsCString name; + nsCString UAName; }; bool Init(MessageLoop* aIOLoop, @@ -194,7 +196,8 @@ public: virtual bool RecvGarbageCollect(); virtual bool RecvCycleCollect(); - virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID); + virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID, + const nsCString& name, const nsCString& UAName); virtual bool RecvLastPrivateDocShellDestroyed(); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index dad5ca32461..85bdebd8c88 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1133,9 +1133,11 @@ ContentParent::ContentParent(mozIApplication* aApp, if (gAppData) { nsCString version(gAppData->version); nsCString buildID(gAppData->buildID); + nsCString name(gAppData->name); + nsCString UAName(gAppData->UAName); //Sending all information to content process - unused << SendAppInfo(version, buildID); + unused << SendAppInfo(version, buildID, name, UAName); } } diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index cc7bdb12bb8..6acb5278e6d 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -258,7 +258,7 @@ child: */ ActivateA11y(); - AppInfo(nsCString version, nsCString buildID); + AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName); // Notify child that last-pb-context-exited notification was observed LastPrivateDocShellDestroyed(); diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index eeb8e6ea9b1..9b4934fa5d1 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -632,7 +632,9 @@ NS_IMETHODIMP nsXULAppInfo::GetName(nsACString& aResult) { if (XRE_GetProcessType() == GeckoProcessType_Content) { - return NS_ERROR_NOT_AVAILABLE; + ContentChild* cc = ContentChild::GetSingleton(); + aResult = cc->GetAppInfo().name; + return NS_OK; } aResult.Assign(gAppData->name); @@ -696,7 +698,9 @@ NS_IMETHODIMP nsXULAppInfo::GetUAName(nsACString& aResult) { if (XRE_GetProcessType() == GeckoProcessType_Content) { - return NS_ERROR_NOT_AVAILABLE; + ContentChild* cc = ContentChild::GetSingleton(); + aResult = cc->GetAppInfo().UAName; + return NS_OK; } aResult.Assign(gAppData->UAName); From 35bb6135daa6f74695eadf9dc5626fc098472bd4 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Wed, 17 Jul 2013 09:57:28 -0400 Subject: [PATCH 21/31] Bug 803480 - Enable NeckoWifi by default only on platforms where it's actually supported. r=ted --- configure.in | 38 ++++++++++++++++++++------------------ netwerk/wifi/Makefile.in | 2 +- netwerk/wifi/moz.build | 2 +- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/configure.in b/configure.in index baca359b82d..1a16ae2e577 100644 --- a/configure.in +++ b/configure.in @@ -4293,7 +4293,6 @@ NS_PRINTING=1 MOZ_PDF_PRINTING= MOZ_DISABLE_CRYPTOLEGACY= NSS_DISABLE_DBM= -NECKO_WIFI=1 NECKO_COOKIES=1 NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource websocket wyciwyg device" USE_ARM_KUSER= @@ -4323,11 +4322,6 @@ case "${target}" in fi NSS_DISABLE_DBM=1 - if test -z "$gonkdir"; then - NECKO_WIFI= - else - NECKO_WIFI=1 - fi MOZ_THEME_FASTSTRIPE=1 MOZ_TREE_FREETYPE=1 MOZ_MEMORY=1 @@ -8478,28 +8472,36 @@ done dnl dnl option to disable necko's wifi scanner dnl + +case "$OS_TARGET" in + Android) + if test -n "$gonkdir"; then + NECKO_WIFI=1 + fi + ;; + Darwin|SunOS|WINNT) + NECKO_WIFI=1 + ;; + Linux) + if test -z "$MOZ_ENABLE_DBUS"; then + AC_MSG_ERROR([Necko WiFi scanning needs DBus on your platform, remove --disable-dbus or use --disable-necko-wifi]) + fi + NECKO_WIFI=1 + NECKO_WIFI_DBUS=1 + ;; +esac + MOZ_ARG_DISABLE_BOOL(necko-wifi, [ --disable-necko-wifi Disable necko wifi scanner], NECKO_WIFI=, NECKO_WIFI=1) -if test "$OS_ARCH" = "OS2"; then - dnl OS/2 implementation of Necko-WiFi support will be added in bug 506566 - NECKO_WIFI= -fi -if test "$NECKO_WIFI" -a \ - "$OS_ARCH" != "Linux" -a \ - "$OS_ARCH" != "Darwin" -a \ - "$OS_ARCH" != "SunOS" -a \ - "$OS_ARCH" != "WINNT"; then - AC_MSG_ERROR([Necko WiFi scanning not supported on your platform, use --disable-necko-wifi]) -fi - if test "$NECKO_WIFI"; then AC_DEFINE(NECKO_WIFI) _NON_GLOBAL_ACDEFINES="$_NON_GLOBAL_ACDEFINES NECKO_WIFI" fi AC_SUBST(NECKO_WIFI) +AC_SUBST(NECKO_WIFI_DBUS) dnl dnl option to disable cookies diff --git a/netwerk/wifi/Makefile.in b/netwerk/wifi/Makefile.in index 26dbd79fab2..4cbf912d87b 100644 --- a/netwerk/wifi/Makefile.in +++ b/netwerk/wifi/Makefile.in @@ -20,7 +20,7 @@ ifeq ($(OS_ARCH),SunOS) OS_INCLUDES += $(GLIB_CFLAGS) endif -ifdef MOZ_ENABLE_DBUS +ifdef NECKO_WIFI_DBUS OS_INCLUDES += $(MOZ_DBUS_GLIB_CFLAGS) endif diff --git a/netwerk/wifi/moz.build b/netwerk/wifi/moz.build index 5e70211353e..07b01de7577 100644 --- a/netwerk/wifi/moz.build +++ b/netwerk/wifi/moz.build @@ -43,7 +43,7 @@ elif CONFIG['OS_ARCH'] == 'SunOS': 'nsWifiScannerSolaris.cpp', ] -if CONFIG['MOZ_ENABLE_DBUS']: +if CONFIG['NECKO_WIFI_DBUS']: CPP_SOURCES += [ 'nsWifiScannerDBus.cpp', ] From c9c6847ad2dc58a003e020a66296f7be4ab29bb7 Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Mon, 15 Jul 2013 17:13:37 -0700 Subject: [PATCH 22/31] Bug 887480 - Make the content/base xpcshell tests use dynamic httpd ports so they can be run concurrently. r=bz --- content/base/test/unit/head_utilities.js | 2 ++ content/base/test/unit/test_bug553888.js | 7 +++---- content/base/test/unit/test_bug558431.js | 1 - content/base/test/unit/test_cspreports.js | 13 ++++--------- content/base/test/unit/test_csputils.js | 7 ++----- content/base/test/unit/xpcshell.ini | 1 + 6 files changed, 12 insertions(+), 19 deletions(-) diff --git a/content/base/test/unit/head_utilities.js b/content/base/test/unit/head_utilities.js index f28b67978d7..607d557839e 100644 --- a/content/base/test/unit/head_utilities.js +++ b/content/base/test/unit/head_utilities.js @@ -3,6 +3,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +Components.utils.import("resource://testing-common/httpd.js"); + const nsIDocumentEncoder = Components.interfaces.nsIDocumentEncoder; const replacementChar = Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER; diff --git a/content/base/test/unit/test_bug553888.js b/content/base/test/unit/test_bug553888.js index fcf770471ca..036d51211df 100644 --- a/content/base/test/unit/test_bug553888.js +++ b/content/base/test/unit/test_bug553888.js @@ -4,9 +4,10 @@ Components.utils.import("resource://testing-common/httpd.js"); -var server = null; +var server = new HttpServer(); +server.start(-1); -const SERVER_PORT = 4444; +const SERVER_PORT = server.identity.primaryPort; const HTTP_BASE = "http://localhost:" + SERVER_PORT; const redirectPath = "/redirect"; const headerCheckPath = "/headerCheck"; @@ -36,10 +37,8 @@ function headerCheckHandler(metadata, response) { } function run_test() { - var server = new HttpServer(); server.registerPathHandler(redirectPath, redirectHandler); server.registerPathHandler(headerCheckPath, headerCheckHandler); - server.start(SERVER_PORT); do_test_pending(); var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] diff --git a/content/base/test/unit/test_bug558431.js b/content/base/test/unit/test_bug558431.js index 3386b9f4dd4..fd66f6acf04 100644 --- a/content/base/test/unit/test_bug558431.js +++ b/content/base/test/unit/test_bug558431.js @@ -5,7 +5,6 @@ const Cu = Components.utils; const Cr = Components.results; Cu.import('resource://gre/modules/CSPUtils.jsm'); -Cu.import("resource://testing-common/httpd.js"); var httpserv = null; diff --git a/content/base/test/unit/test_cspreports.js b/content/base/test/unit/test_cspreports.js index bd4a9f82ac0..e7c5f0d89b6 100644 --- a/content/base/test/unit/test_cspreports.js +++ b/content/base/test/unit/test_cspreports.js @@ -10,16 +10,14 @@ const Cr = Components.results; Cu.import('resource://gre/modules/CSPUtils.jsm'); Cu.import('resource://gre/modules/NetUtil.jsm'); -// load the HTTP server -Cu.import("resource://testing-common/httpd.js"); +var httpServer = new HttpServer(); +httpServer.start(-1); +var testsToFinish = 0; -const REPORT_SERVER_PORT = 9000; +const REPORT_SERVER_PORT = httpServer.identity.primaryPort; const REPORT_SERVER_URI = "http://localhost"; const REPORT_SERVER_PATH = "/report"; -var httpServer = null; -var testsToFinish = 0; - /** * Construct a callback that listens to a report submission and either passes * or fails a test based on what it gets. @@ -99,9 +97,6 @@ function run_test() { ":" + REPORT_SERVER_PORT + "/foo/self"); - httpServer = new HttpServer(); - httpServer.start(REPORT_SERVER_PORT); - // test that inline script violations cause a report. makeTest(0, {"blocked-uri": "self"}, false, function(csp) { diff --git a/content/base/test/unit/test_csputils.js b/content/base/test/unit/test_csputils.js index b7d76c1c249..a9bccf12579 100644 --- a/content/base/test/unit/test_csputils.js +++ b/content/base/test/unit/test_csputils.js @@ -11,13 +11,11 @@ const Cr = Components.results; Cu.import('resource://gre/modules/CSPUtils.jsm'); Cu.import('resource://gre/modules/NetUtil.jsm'); -// load the HTTP server -Cu.import("resource://testing-common/httpd.js"); - var httpServer = new HttpServer(); +httpServer.start(-1); const POLICY_FROM_URI = "default-src 'self'; img-src *"; -const POLICY_PORT = 9000; +const POLICY_PORT = httpServer.identity.primaryPort; const POLICY_URI = "http://localhost:" + POLICY_PORT + "/policy"; const POLICY_URI_RELATIVE = "/policy"; @@ -1048,7 +1046,6 @@ function run_test() { } //server.registerDirectory("/", nsILocalFileForBasePath); httpServer.registerPathHandler("/policy", policyresponder); - httpServer.start(POLICY_PORT); for(let i in tests) { tests[i](); diff --git a/content/base/test/unit/xpcshell.ini b/content/base/test/unit/xpcshell.ini index 9d7c35dd399..1ccf85794d3 100644 --- a/content/base/test/unit/xpcshell.ini +++ b/content/base/test/unit/xpcshell.ini @@ -8,6 +8,7 @@ tail = [test_csputils.js] [test_cspreports.js] [test_error_codes.js] +run-sequentially = Hardcoded 4444 port. [test_thirdpartyutil.js] [test_xhr_standalone.js] [test_xmlserializer.js] From 0c8ed8b0edc7fb53884231b217e497fc2f43d4c7 Mon Sep 17 00:00:00 2001 From: Guillaume Abadie Date: Wed, 17 Jul 2013 09:58:09 -0400 Subject: [PATCH 23/31] Bug 890311 - [WebGL 2.0] Add WebGL2Context C++ class inheriting WebGLContext. r=jgilbert --- content/canvas/src/WebGL1Context.h | 9 +++ content/canvas/src/WebGL2Context.cpp | 61 ++++++++++++++++++ content/canvas/src/WebGL2Context.h | 64 +++++++++++++++++++ content/canvas/src/WebGLContext.h | 2 + content/canvas/src/WebGLContextGL.cpp | 13 +++- .../canvas/src/WebGLContextNotSupported.cpp | 6 ++ content/canvas/src/moz.build | 1 + .../html/content/src/HTMLCanvasElement.cpp | 16 +++++ dom/bindings/Bindings.conf | 12 +++- dom/webidl/WebGL2RenderingContext.webidl | 12 ++++ dom/webidl/WebIDL.mk | 1 + 11 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 content/canvas/src/WebGL2Context.cpp create mode 100644 content/canvas/src/WebGL2Context.h create mode 100644 dom/webidl/WebGL2RenderingContext.webidl diff --git a/content/canvas/src/WebGL1Context.h b/content/canvas/src/WebGL1Context.h index eb0bbfec557..5b3c2472747 100644 --- a/content/canvas/src/WebGL1Context.h +++ b/content/canvas/src/WebGL1Context.h @@ -24,6 +24,15 @@ public: virtual ~WebGL1Context(); + // ------------------------------------------------------------------------- + // IMPLEMENT WebGLContext + + virtual bool IsWebGL2() const MOZ_OVERRIDE + { + return false; + } + + // ------------------------------------------------------------------------- // IMPLEMENT nsWrapperCache diff --git a/content/canvas/src/WebGL2Context.cpp b/content/canvas/src/WebGL2Context.cpp new file mode 100644 index 00000000000..0c57ca61afe --- /dev/null +++ b/content/canvas/src/WebGL2Context.cpp @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WebGL2Context.h" +#include "mozilla/dom/WebGL2RenderingContextBinding.h" + +#include "mozilla/Telemetry.h" + +using namespace mozilla; + +// ----------------------------------------------------------------------------- +// CONSTRUCTOR & DESTRUCTOR + +WebGL2Context::WebGL2Context() + : WebGLContext() +{ + MOZ_ASSERT(IsSupported(), "not supposed to create a WebGL2Context" + "context when not supported"); +} + +WebGL2Context::~WebGL2Context() +{ + +} + + +// ----------------------------------------------------------------------------- +// STATIC FUNCTIONS + +bool +WebGL2Context::IsSupported() +{ +#ifdef RELEASE_BUILD + return false; +#else + return Preferences::GetBool("webgl.enable-prototype-webgl2", false); +#endif +} + +WebGL2Context* +WebGL2Context::Create() +{ +#ifdef RELEASE_BUILD + return nullptr; +#else + return new WebGL2Context(); +#endif +} + + +// ----------------------------------------------------------------------------- +// IMPLEMENT nsWrapperCache + +JSObject* +WebGL2Context::WrapObject(JSContext *cx, JS::Handle scope) +{ + return dom::WebGL2RenderingContextBinding::Wrap(cx, scope, this); +} + diff --git a/content/canvas/src/WebGL2Context.h b/content/canvas/src/WebGL2Context.h new file mode 100644 index 00000000000..5b3ee932ee0 --- /dev/null +++ b/content/canvas/src/WebGL2Context.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WEBGL2CONTEXT_H_ +#define WEBGL2CONTEXT_H_ + +#include "WebGLContext.h" + +namespace mozilla { + +class WebGL2Context + : public WebGLContext +{ +// ----------------------------------------------------------------------------- +// PUBLIC +public: + + // ------------------------------------------------------------------------- + // DESTRUCTOR + + virtual ~WebGL2Context(); + + + // ------------------------------------------------------------------------- + // STATIC FUNCTIONS + + static bool IsSupported(); + + static WebGL2Context* Create(); + + + // ------------------------------------------------------------------------- + // IMPLEMENT WebGLContext + + virtual bool IsWebGL2() const MOZ_OVERRIDE + { + return true; + } + + + // ------------------------------------------------------------------------- + // IMPLEMENT nsWrapperCache + + virtual JSObject* WrapObject(JSContext *cx, + JS::Handle scope) MOZ_OVERRIDE; + + +// ----------------------------------------------------------------------------- +// PRIVATE +private: + + // ------------------------------------------------------------------------- + // CONSTRUCTOR + + WebGL2Context(); + + +}; + +} // namespace mozilla + +#endif diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h index c1151167313..8ee6f9bc057 100644 --- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -153,6 +153,8 @@ public: virtual JSObject* WrapObject(JSContext *cx, JS::Handle scope) = 0; + virtual bool IsWebGL2() const = 0; + NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT // nsICanvasRenderingContextInternal diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index 1085cd92e2e..b3aaa4e1bb6 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -2113,7 +2113,18 @@ WebGLContext::GetParameter(JSContext* cx, WebGLenum pname, ErrorResult& rv) case LOCAL_GL_RENDERER: return StringValue(cx, "Mozilla", rv); case LOCAL_GL_VERSION: - return StringValue(cx, "WebGL 1.0", rv); + { + const char* version = 0; + + if (IsWebGL2()) { + version = "WebGL 2.0"; + } else { + version = "WebGL 1.0"; + } + + MOZ_ASSERT(version != 0); + return StringValue(cx, version, rv); + } case LOCAL_GL_SHADING_LANGUAGE_VERSION: return StringValue(cx, "WebGL GLSL ES 1.0", rv); diff --git a/content/canvas/src/WebGLContextNotSupported.cpp b/content/canvas/src/WebGLContextNotSupported.cpp index 94eb14eaf2a..7bad27281f0 100644 --- a/content/canvas/src/WebGLContextNotSupported.cpp +++ b/content/canvas/src/WebGLContextNotSupported.cpp @@ -4,7 +4,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsIDOMWebGLRenderingContext.h" +#include "WebGL2Context.h" #define DUMMY(func,rtype) nsresult func (rtype ** aResult) { return NS_ERROR_FAILURE; } DUMMY(NS_NewCanvasRenderingContextWebGL, nsIDOMWebGLRenderingContext) + +WebGL2Context * WebGL2Context::Create() +{ + return nullptr; +} diff --git a/content/canvas/src/moz.build b/content/canvas/src/moz.build index 26c5cd848d8..b2f3fc5f605 100644 --- a/content/canvas/src/moz.build +++ b/content/canvas/src/moz.build @@ -29,6 +29,7 @@ if CONFIG['MOZ_WEBGL']: 'WebGLActiveInfo.cpp', 'WebGLBuffer.cpp', 'WebGL1Context.cpp', + 'WebGL2Context.cpp', 'WebGLContext.cpp', 'WebGLContextGL.cpp', 'WebGLContextUtils.cpp', diff --git a/content/html/content/src/HTMLCanvasElement.cpp b/content/html/content/src/HTMLCanvasElement.cpp index 25b5208d555..565cc458850 100644 --- a/content/html/content/src/HTMLCanvasElement.cpp +++ b/content/html/content/src/HTMLCanvasElement.cpp @@ -31,6 +31,8 @@ #include "nsNetUtil.h" #include "nsStreamUtils.h" +#include "../canvas/src/WebGL2Context.h" + using namespace mozilla::layers; NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas) @@ -682,6 +684,20 @@ HTMLCanvasElement::GetContextHelper(const nsAString& aContextId, ctx.forget(aContext); return NS_OK; } + else if (WebGL2Context::IsSupported() && + aContextId.EqualsLiteral("experimental-webgl2")) + { + Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1); + nsRefPtr ctx = WebGL2Context::Create(); + + if (ctx == nullptr) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + ctx->SetCanvasElement(this); + ctx.forget(aContext); + return NS_OK; + } NS_ConvertUTF16toUTF8 ctxId(aContextId); diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index fa1ed0009d6..68e7fb18953 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1287,8 +1287,16 @@ DOMInterfaces = { }, 'WebGLRenderingContext': { - 'nativeType': 'mozilla::WebGL1Context', - 'headerFile': 'WebGL1Context.h', + 'nativeType': 'mozilla::WebGLContext', + 'headerFile': 'WebGLContext.h', + 'resultNotAddRefed': [ 'canvas', 'getContextAttributes', 'getExtension', + 'getAttachedShaders' ], + 'implicitJSContext': [ 'getSupportedExtensions' ], +}, + +'WebGL2RenderingContext': { + 'nativeType': 'mozilla::WebGLContext', + 'headerFile': 'WebGLContext.h', 'resultNotAddRefed': [ 'canvas', 'getContextAttributes', 'getExtension', 'getAttachedShaders' ], 'implicitJSContext': [ 'getSupportedExtensions' ], diff --git a/dom/webidl/WebGL2RenderingContext.webidl b/dom/webidl/WebGL2RenderingContext.webidl new file mode 100644 index 00000000000..c4287dba54c --- /dev/null +++ b/dom/webidl/WebGL2RenderingContext.webidl @@ -0,0 +1,12 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This IDL depend on WebGLRenderingContext.webidl + */ + +interface WebGL2RenderingContext : WebGLRenderingContext { + +}; + diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index dacd989bc11..91c81b655a1 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -389,6 +389,7 @@ endif ifdef MOZ_WEBGL webidl_files += \ WebGLRenderingContext.webidl \ + WebGL2RenderingContext.webidl \ $(NULL) endif From 2107f8a4cc1c21b5366abbc685bdb7ba24f96f0d Mon Sep 17 00:00:00 2001 From: Drew Willcoxon Date: Wed, 17 Jul 2013 09:59:08 -0400 Subject: [PATCH 24/31] Bug 894234 - Guard against audioContext->IsOffline(). r=ehsan --- dom/base/nsGlobalWindow.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 9d8203ec77c..e4e8b9d13e5 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3284,7 +3284,7 @@ nsPIDOMWindow::AddAudioContext(AudioContext* aAudioContext) mAudioContexts.AppendElement(aAudioContext); nsIDocShell* docShell = GetDocShell(); - if (docShell && !docShell->GetAllowMedia()) { + if (docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline()) { aAudioContext->Mute(); } } @@ -3293,7 +3293,9 @@ void nsPIDOMWindow::MuteAudioContexts() { for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { - mAudioContexts[i]->Mute(); + if (!mAudioContexts[i]->IsOffline()) { + mAudioContexts[i]->Mute(); + } } } @@ -3301,7 +3303,9 @@ void nsPIDOMWindow::UnmuteAudioContexts() { for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { - mAudioContexts[i]->Unmute(); + if (!mAudioContexts[i]->IsOffline()) { + mAudioContexts[i]->Unmute(); + } } } From bec3d32a7f3ce1aa63e205b508ba1a47969827cb Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Wed, 17 Jul 2013 10:00:15 -0400 Subject: [PATCH 25/31] Bug 827346 - indexedDB.open() silently fails in an Open Web App (OWA/mozApp). r=myk --- dom/apps/src/Webapps.js | 2 +- webapprt/Startup.jsm | 14 +------ webapprt/prefs.js | 9 +++++ webapprt/test/content/Makefile.in | 1 + webapprt/test/content/webapprt_indexeddb.html | 38 +++++++++++++++++++ 5 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 webapprt/test/content/webapprt_indexeddb.html diff --git a/dom/apps/src/Webapps.js b/dom/apps/src/Webapps.js index 3c1854660e5..4d9c68a1b74 100644 --- a/dom/apps/src/Webapps.js +++ b/dom/apps/src/Webapps.js @@ -84,7 +84,7 @@ WebappsRegistry.prototype = { } } catch(e) { throw new Components.Exception( - "INVALID_URL: '" + aURL, Cr.NS_ERROR_FAILURE + "INVALID_URL: '" + aURL + "'", Cr.NS_ERROR_FAILURE ); } diff --git a/webapprt/Startup.jsm b/webapprt/Startup.jsm index 0402debe1ed..8f4f3bd2c67 100644 --- a/webapprt/Startup.jsm +++ b/webapprt/Startup.jsm @@ -24,19 +24,7 @@ WebappsHandler.init(); // On firstrun, set permissions to their default values. if (!Services.prefs.getBoolPref("webapprt.firstrun")) { - Cu.import("resource://webapprt/modules/WebappRT.jsm"); - let uri = Services.io.newURI(WebappRT.config.app.origin, null, null); - - // Set AppCache-related permissions. - Services.perms.add(uri, "pin-app", - Ci.nsIPermissionManager.ALLOW_ACTION); - Services.perms.add(uri, "offline-app", - Ci.nsIPermissionManager.ALLOW_ACTION); - - Services.perms.add(uri, "indexedDB", - Ci.nsIPermissionManager.ALLOW_ACTION); - Services.perms.add(uri, "indexedDB-unlimited", - Ci.nsIPermissionManager.ALLOW_ACTION); + // Once we support packaged apps, set their permissions here on firstrun. // Now that we've set the appropriate permissions, twiddle the firstrun // flag so we don't try to do so again. diff --git a/webapprt/prefs.js b/webapprt/prefs.js index 4e2243b8d77..4e6eb1d8b81 100644 --- a/webapprt/prefs.js +++ b/webapprt/prefs.js @@ -31,6 +31,15 @@ pref("extensions.blocklist.itemURL", "https://addons.mozilla.org/%LOCALE%/%APP%/ pref("full-screen-api.enabled", true); +// IndexedDB +pref("dom.indexedDB.enabled", true); +pref("indexedDB.feature.enabled", true); +pref("dom.indexedDB.warningQuota", 50); + +// Offline cache prefs +pref("browser.offline-apps.notify", false); +pref("browser.cache.offline.enable", true); +pref("offline-apps.allow_by_default", true); // Enable smooth scrolling pref("general.smoothScroll", true); diff --git a/webapprt/test/content/Makefile.in b/webapprt/test/content/Makefile.in index a6b78f6c628..e8870e3d9f4 100644 --- a/webapprt/test/content/Makefile.in +++ b/webapprt/test/content/Makefile.in @@ -13,6 +13,7 @@ include $(DEPTH)/config/autoconf.mk MOCHITEST_FILES = \ test.webapp \ webapprt_sample.html \ + webapprt_indexeddb.html \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/webapprt/test/content/webapprt_indexeddb.html b/webapprt/test/content/webapprt_indexeddb.html new file mode 100644 index 00000000000..4852ee7e6ff --- /dev/null +++ b/webapprt/test/content/webapprt_indexeddb.html @@ -0,0 +1,38 @@ + + + + + + + + + + + +

+ This is the IndexedDB WebappRT content mochitest. +

+ +
+      
+    
+ + From a5ea257881465f90762ffb03a12f672fc418bbd3 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Wed, 17 Jul 2013 10:01:04 -0400 Subject: [PATCH 26/31] Bug 894162 - webapprt-test-chrome|content tests hang after opening WebappRT Test Shim. r=myk --- webapprt/test/chrome/Makefile.in | 2 ++ webapprt/test/chrome/sample.webapp^headers^ | 1 + webapprt/test/chrome/window-title.webapp^headers^ | 1 + webapprt/test/content/Makefile.in | 1 + webapprt/test/content/test.webapp^headers^ | 1 + 5 files changed, 6 insertions(+) create mode 100644 webapprt/test/chrome/sample.webapp^headers^ create mode 100644 webapprt/test/chrome/window-title.webapp^headers^ create mode 100644 webapprt/test/content/test.webapp^headers^ diff --git a/webapprt/test/chrome/Makefile.in b/webapprt/test/chrome/Makefile.in index cb1e79f2e3b..a9898b3ae07 100644 --- a/webapprt/test/chrome/Makefile.in +++ b/webapprt/test/chrome/Makefile.in @@ -14,9 +14,11 @@ MOCHITEST_WEBAPPRT_CHROME_FILES = \ head.js \ browser_sample.js \ sample.webapp \ + sample.webapp^headers^ \ sample.html \ browser_window-title.js \ window-title.webapp \ + window-title.webapp^headers^ \ window-title.html \ $(NULL) diff --git a/webapprt/test/chrome/sample.webapp^headers^ b/webapprt/test/chrome/sample.webapp^headers^ new file mode 100644 index 00000000000..a2367b11c78 --- /dev/null +++ b/webapprt/test/chrome/sample.webapp^headers^ @@ -0,0 +1 @@ +Content-Type: application/x-web-app-manifest+json diff --git a/webapprt/test/chrome/window-title.webapp^headers^ b/webapprt/test/chrome/window-title.webapp^headers^ new file mode 100644 index 00000000000..a2367b11c78 --- /dev/null +++ b/webapprt/test/chrome/window-title.webapp^headers^ @@ -0,0 +1 @@ +Content-Type: application/x-web-app-manifest+json diff --git a/webapprt/test/content/Makefile.in b/webapprt/test/content/Makefile.in index e8870e3d9f4..2ea38598bfd 100644 --- a/webapprt/test/content/Makefile.in +++ b/webapprt/test/content/Makefile.in @@ -12,6 +12,7 @@ include $(DEPTH)/config/autoconf.mk MOCHITEST_FILES = \ test.webapp \ + test.webapp^headers^ \ webapprt_sample.html \ webapprt_indexeddb.html \ $(NULL) diff --git a/webapprt/test/content/test.webapp^headers^ b/webapprt/test/content/test.webapp^headers^ new file mode 100644 index 00000000000..a2367b11c78 --- /dev/null +++ b/webapprt/test/content/test.webapp^headers^ @@ -0,0 +1 @@ +Content-Type: application/x-web-app-manifest+json From d3ee8222e18105ed7e8516b8e02232aa09fc9763 Mon Sep 17 00:00:00 2001 From: Mina Almasry Date: Wed, 17 Jul 2013 10:09:04 -0400 Subject: [PATCH 27/31] Bug 888864 - Changing a stylesheet ProcessingInstruction's data no longer applies immediately. r=bz --- content/base/src/Comment.h | 1 + content/base/src/nsGenericDOMDataNode.h | 2 +- content/base/src/nsTextNode.h | 1 + content/base/test/Makefile.in | 1 + ...essing_instruction_update_stylesheet.xhtml | 46 +++++++++++++++++++ content/xml/content/src/CDATASection.h | 1 + .../xml/content/src/ProcessingInstruction.h | 1 + .../src/XMLStylesheetProcessingInstruction.h | 10 ++++ 8 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 content/base/test/test_processing_instruction_update_stylesheet.xhtml diff --git a/content/base/src/Comment.h b/content/base/src/Comment.h index 530f2ab89c0..c2968e0909d 100644 --- a/content/base/src/Comment.h +++ b/content/base/src/Comment.h @@ -46,6 +46,7 @@ public: // nsIDOMCharacterData NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::) + using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function. // nsIDOMComment // Empty interface diff --git a/content/base/src/nsGenericDOMDataNode.h b/content/base/src/nsGenericDOMDataNode.h index 51b7c7aa3bd..0f51254e00f 100644 --- a/content/base/src/nsGenericDOMDataNode.h +++ b/content/base/src/nsGenericDOMDataNode.h @@ -169,7 +169,7 @@ public: // WebIDL API // Our XPCOM GetData is just fine for WebIDL - void SetData(const nsAString& aData, mozilla::ErrorResult& rv) + virtual void SetData(const nsAString& aData, mozilla::ErrorResult& rv) { rv = SetData(aData); } diff --git a/content/base/src/nsTextNode.h b/content/base/src/nsTextNode.h index cb762c2a7cc..e0e3e0f6285 100644 --- a/content/base/src/nsTextNode.h +++ b/content/base/src/nsTextNode.h @@ -53,6 +53,7 @@ public: // nsIDOMCharacterData NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::) + using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function. // nsIDOMText NS_FORWARD_NSIDOMTEXT(nsGenericDOMDataNode::) diff --git a/content/base/test/Makefile.in b/content/base/test/Makefile.in index 638d49a18f6..68ed1b91c24 100644 --- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -638,6 +638,7 @@ MOCHITEST_FILES_C= \ test_bug890580.html \ test_declare_stylesheet_obsolete.html \ variable_style_sheet.sjs \ + test_processing_instruction_update_stylesheet.xhtml \ $(NULL) # OOP tests don't work on Windows (bug 763081) or native-fennec diff --git a/content/base/test/test_processing_instruction_update_stylesheet.xhtml b/content/base/test/test_processing_instruction_update_stylesheet.xhtml new file mode 100644 index 00000000000..c921bcc1c25 --- /dev/null +++ b/content/base/test/test_processing_instruction_update_stylesheet.xhtml @@ -0,0 +1,46 @@ + + + + + + + Test for Bug 888864 + + + + + +

This changes color

+Mozilla Bug 888864 +
+
+ + diff --git a/content/xml/content/src/CDATASection.h b/content/xml/content/src/CDATASection.h index 6caabd0591e..03537c6506e 100644 --- a/content/xml/content/src/CDATASection.h +++ b/content/xml/content/src/CDATASection.h @@ -48,6 +48,7 @@ public: // nsIDOMCharacterData NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::) + using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function. // nsIDOMText NS_FORWARD_NSIDOMTEXT(nsGenericDOMDataNode::) diff --git a/content/xml/content/src/ProcessingInstruction.h b/content/xml/content/src/ProcessingInstruction.h index 04fc8d383df..461cfff33a0 100644 --- a/content/xml/content/src/ProcessingInstruction.h +++ b/content/xml/content/src/ProcessingInstruction.h @@ -30,6 +30,7 @@ public: // nsIDOMCharacterData NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::) + using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function. // nsIDOMProcessingInstruction NS_DECL_NSIDOMPROCESSINGINSTRUCTION diff --git a/content/xml/content/src/XMLStylesheetProcessingInstruction.h b/content/xml/content/src/XMLStylesheetProcessingInstruction.h index e65f9227162..4b23986449c 100644 --- a/content/xml/content/src/XMLStylesheetProcessingInstruction.h +++ b/content/xml/content/src/XMLStylesheetProcessingInstruction.h @@ -63,6 +63,16 @@ public: // nsStyleLinkElement NS_IMETHOD GetCharset(nsAString& aCharset) MOZ_OVERRIDE; + virtual void SetData(const nsAString& aData, mozilla::ErrorResult& rv) MOZ_OVERRIDE + { + nsGenericDOMDataNode::SetData(aData, rv); + if (rv.Failed()) { + return; + } + UpdateStyleSheetInternal(nullptr, true); + } + using ProcessingInstruction::SetData; // Prevent hiding overloaded virtual function. + protected: nsCOMPtr mOverriddenBaseURI; From f704f16388b41fa4465a26353f82a24e210fc253 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Wed, 17 Jul 2013 16:21:22 +0200 Subject: [PATCH 28/31] Bug 894447 - Avoid unnecessary monitor instructions and bailouts with JSOP_SETELEM accessing named properties and backout bug 894463. r=bhackett --- js/src/ion/IonBuilder.cpp | 19 +++++++++++++------ js/src/jsinferinlines.h | 7 +------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index 2fa4ca79d5c..1ba1826d8a9 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -6735,18 +6735,25 @@ IonBuilder::jsop_setelem() return jsop_setelem_typed(arrayType, SetElem_Normal, object, index, value); - if (!PropertyWriteNeedsTypeBarrier(cx, current, &object, NULL, &value)) { - if (ElementAccessIsDenseNative(object, index)) { + if (ElementAccessIsDenseNative(object, index)) { + do { + if (PropertyWriteNeedsTypeBarrier(cx, current, &object, NULL, &value)) + break; + if (!object->resultTypeSet()) + break; + types::StackTypeSet::DoubleConversion conversion = object->resultTypeSet()->convertDoubleElements(cx); // If AmbiguousDoubleConversion, only handle int32 values for now. - if (conversion != types::StackTypeSet::AmbiguousDoubleConversion || - value->type() == MIRType_Int32) + if (conversion == types::StackTypeSet::AmbiguousDoubleConversion && + value->type() != MIRType_Int32) { - return jsop_setelem_dense(conversion, SetElem_Normal, object, index, value); + break; } - } + + return jsop_setelem_dense(conversion, SetElem_Normal, object, index, value); + } while(false); } if (object->type() == MIRType_Magic) diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index 350410c1899..93eadd3186a 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -970,14 +970,9 @@ TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id) // But if we don't have too many properties yet, don't do anything. The // idea here is that normal object initialization should not trigger // deoptimization in most cases, while actual usage as a hashmap should. - // Except for vanilla objects and arrays work around bug 894447 for now - // by deoptimizing more eagerly. Since in those cases we often have a - // pc-keyed TypeObject, this is ok. TypeObject* type = obj->type(); - if (!obj->is() && !obj->is() && - type->getPropertyCount() < 8) { + if (type->getPropertyCount() < 8) return; - } MarkTypeObjectUnknownProperties(cx, type); } } From 2f2d0463541ac7b51ed86f0f3cd4031bbcc78699 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Wed, 17 Jul 2013 10:23:10 -0400 Subject: [PATCH 29/31] Bug 882647 - get rid of BOUNDARY_ATTRIBUTE_RANGE, r=tbsaunde --- accessible/public/nsIAccessibleText.idl | 3 +- .../src/generic/HyperTextAccessible.cpp | 192 ------------------ accessible/src/generic/HyperTextAccessible.h | 16 +- accessible/tests/mochitest/text.js | 3 - 4 files changed, 2 insertions(+), 212 deletions(-) diff --git a/accessible/public/nsIAccessibleText.idl b/accessible/public/nsIAccessibleText.idl index 3daf98c8ddc..bd0f28bbda5 100644 --- a/accessible/public/nsIAccessibleText.idl +++ b/accessible/public/nsIAccessibleText.idl @@ -11,7 +11,7 @@ typedef long AccessibleTextBoundary; interface nsIAccessible; interface nsIPersistentProperties; -[scriptable, uuid(0f4633b1-550c-4b50-8c04-0eb1005eef2f)] +[scriptable, uuid(43d81eb0-1215-4dc4-9226-a4355bd2d20d)] interface nsIAccessibleText : nsISupports { // In parameters for character offsets: @@ -27,7 +27,6 @@ interface nsIAccessibleText : nsISupports const AccessibleTextBoundary BOUNDARY_SENTENCE_END = 4; // don't use, deprecated const AccessibleTextBoundary BOUNDARY_LINE_START = 5; const AccessibleTextBoundary BOUNDARY_LINE_END = 6; - const AccessibleTextBoundary BOUNDARY_ATTRIBUTE_RANGE = 7; /** * The current current caret offset. diff --git a/accessible/src/generic/HyperTextAccessible.cpp b/accessible/src/generic/HyperTextAccessible.cpp index f929eddf4de..6b8665a89e2 100644 --- a/accessible/src/generic/HyperTextAccessible.cpp +++ b/accessible/src/generic/HyperTextAccessible.cpp @@ -878,186 +878,6 @@ HyperTextAccessible::FindLineBoundary(int32_t aOffset, return -1; } -/* -Gets the specified text relative to aBoundaryType, which means: -BOUNDARY_CHAR The character before/at/after the offset is returned. -BOUNDARY_WORD_START From the word start before/at/after the offset to the next word start. -BOUNDARY_WORD_END From the word end before/at/after the offset to the next work end. -BOUNDARY_LINE_START From the line start before/at/after the offset to the next line start. -BOUNDARY_LINE_END From the line end before/at/after the offset to the next line start. -*/ - -nsresult -HyperTextAccessible::GetTextHelper(EGetTextType aType, - AccessibleTextBoundary aBoundaryType, - int32_t aOffset, - int32_t* aStartOffset, int32_t* aEndOffset, - nsAString& aText) -{ - aText.Truncate(); - - NS_ENSURE_ARG_POINTER(aStartOffset); - NS_ENSURE_ARG_POINTER(aEndOffset); - *aStartOffset = *aEndOffset = 0; - - int32_t offset = ConvertMagicOffset(aOffset); - if (offset < 0) - return NS_ERROR_INVALID_ARG; - - if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET && offset > 0 && - (aBoundaryType == BOUNDARY_LINE_START || - aBoundaryType == BOUNDARY_LINE_END)) { - // It is the same character offset when the caret is visually at - // the very end of a line or the start of a new line. Getting text at - // the line should provide the line with the visual caret, - // otherwise screen readers will announce the wrong line as the user - // presses up or down arrow and land at the end of a line. - nsRefPtr frameSelection = FrameSelection(); - if (frameSelection && - frameSelection->GetHint() == nsFrameSelection::HINTLEFT) { - -- offset; // We are at the start of a line - } - } - - nsSelectionAmount amount; - bool needsStart = false; - switch (aBoundaryType) { - case BOUNDARY_WORD_START: - needsStart = true; - amount = eSelectWord; - break; - - case BOUNDARY_WORD_END: - amount = eSelectWord; - break; - - case BOUNDARY_LINE_START: - // Newlines are considered at the end of a line. Since getting - // the BOUNDARY_LINE_START gets the text from the line-start to the next - // line-start, the newline is included at the end of the string. - needsStart = true; - amount = eSelectLine; - break; - - case BOUNDARY_LINE_END: - // Newlines are considered at the end of a line. Since getting - // the BOUNDARY_END_START gets the text from the line-end to the next - //line-end, the newline is included at the beginning of the string. - amount = eSelectLine; - break; - - case BOUNDARY_ATTRIBUTE_RANGE: - { - nsresult rv = GetTextAttributes(false, offset, - aStartOffset, aEndOffset, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - - return GetText(*aStartOffset, *aEndOffset, aText); - } - - default: // Note, sentence support is deprecated and falls through to here - return NS_ERROR_INVALID_ARG; - } - - int32_t startOffset = offset + (aBoundaryType == BOUNDARY_LINE_END); // Avoid getting the previous line - int32_t endOffset = startOffset; - - // Convert offsets to frame-relative - nsRefPtr startAcc; - nsIFrame *startFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr, - nullptr, getter_AddRefs(startAcc)); - - if (!startFrame) { - int32_t textLength = CharacterCount(); - if (aBoundaryType == BOUNDARY_LINE_START && offset > 0 && offset == textLength) { - // Asking for start of line, while on last character - if (startAcc) - startFrame = startAcc->GetFrame(); - } - if (!startFrame) { - return offset > textLength ? NS_ERROR_FAILURE : NS_OK; - } - else { - // We're on the last continuation since we're on the last character - startFrame = startFrame->GetLastContinuation(); - } - } - - int32_t finalStartOffset = 0, finalEndOffset = 0; - EWordMovementType wordMovementType = needsStart ? eStartWord : eEndWord; - - nsIPresShell* presShell = mDoc->PresShell(); - // If aType == eGetAt we'll change both the start and end offset from - // the original offset - if (aType == eGetAfter) { - finalStartOffset = offset; - } - else { - finalStartOffset = GetRelativeOffset(presShell, startFrame, startOffset, - startAcc, - (amount == eSelectLine ? eSelectBeginLine : amount), - eDirPrevious, needsStart, - wordMovementType); - NS_ENSURE_TRUE(finalStartOffset >= 0, NS_ERROR_FAILURE); - } - - if (aType == eGetBefore) { - finalEndOffset = offset; - } - else { - // Start moving forward from the start so that we don't get - // 2 words/lines if the offset occurred on whitespace boundary - // Careful, startOffset and endOffset are passed by reference to GetPosAndText() and changed - // For BOUNDARY_LINE_END, make sure we start of this line - startOffset = endOffset = finalStartOffset + (aBoundaryType == BOUNDARY_LINE_END); - nsRefPtr endAcc; - nsIFrame *endFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr, - nullptr, getter_AddRefs(endAcc)); - if (endAcc && endAcc->Role() == roles::STATICTEXT) { - // Static text like list bullets will ruin our forward calculation, - // since the caret cannot be in the static text. Start just after the static text. - startOffset = endOffset = finalStartOffset + - (aBoundaryType == BOUNDARY_LINE_END) + - nsAccUtils::TextLength(endAcc); - - endFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr, - nullptr, getter_AddRefs(endAcc)); - } - if (!endFrame) { - return NS_ERROR_FAILURE; - } - finalEndOffset = GetRelativeOffset(presShell, endFrame, endOffset, endAcc, - (amount == eSelectLine ? eSelectEndLine : amount), - eDirNext, needsStart, wordMovementType); - NS_ENSURE_TRUE(endOffset >= 0, NS_ERROR_FAILURE); - if (finalEndOffset == offset) { - if (aType == eGetAt && amount == eSelectWord) { - // Fix word error for the first character in word: PeekOffset() will return the previous word when - // offset points to the first character of the word, but accessibility APIs want the current word - // that the first character is in - return GetTextHelper(eGetAfter, aBoundaryType, offset, - aStartOffset, aEndOffset, aText); - } - int32_t textLength = CharacterCount(); - if (finalEndOffset < textLength) { - // This happens sometimes when current character at finalStartOffset - // is an embedded object character representing another hypertext, that - // the AT really needs to dig into separately - ++ finalEndOffset; - } - } - } - - *aStartOffset = finalStartOffset; - *aEndOffset = finalEndOffset; - - NS_ASSERTION((finalStartOffset < offset && finalEndOffset >= offset) || aType != eGetBefore, "Incorrect results for GetTextHelper"); - NS_ASSERTION((finalStartOffset <= offset && finalEndOffset > offset) || aType == eGetBefore, "Incorrect results for GetTextHelper"); - - GetPosAndText(finalStartOffset, finalEndOffset, &aText); - return NS_OK; -} - /** * nsIAccessibleText impl. */ @@ -1120,10 +940,6 @@ HyperTextAccessible::GetTextBeforeOffset(int32_t aOffset, *aStartOffset = FindLineBoundary(*aEndOffset, ePrevLineEnd); return GetText(*aStartOffset, *aEndOffset, aText); - case BOUNDARY_ATTRIBUTE_RANGE: - return GetTextHelper(eGetBefore, aBoundaryType, aOffset, - aStartOffset, aEndOffset, aText); - default: return NS_ERROR_INVALID_ARG; } @@ -1177,10 +993,6 @@ HyperTextAccessible::GetTextAtOffset(int32_t aOffset, *aEndOffset = FindLineBoundary(offset, eThisLineEnd); return GetText(*aStartOffset, *aEndOffset, aText); - case BOUNDARY_ATTRIBUTE_RANGE: - return GetTextHelper(eGetAt, aBoundaryType, aOffset, - aStartOffset, aEndOffset, aText); - default: return NS_ERROR_INVALID_ARG; } @@ -1243,10 +1055,6 @@ HyperTextAccessible::GetTextAfterOffset(int32_t aOffset, *aEndOffset = FindLineBoundary(offset, eNextLineEnd); return GetText(*aStartOffset, *aEndOffset, aText); - case BOUNDARY_ATTRIBUTE_RANGE: - return GetTextHelper(eGetAfter, aBoundaryType, aOffset, - aStartOffset, aEndOffset, aText); - default: return NS_ERROR_INVALID_ARG; } diff --git a/accessible/src/generic/HyperTextAccessible.h b/accessible/src/generic/HyperTextAccessible.h index ffa7fd1140c..ba941429429 100644 --- a/accessible/src/generic/HyperTextAccessible.h +++ b/accessible/src/generic/HyperTextAccessible.h @@ -334,22 +334,8 @@ protected: nsSelectionAmount aAmount, EWordMovementType aWordMovementType = eDefaultBehavior); - /* - * This does the work for nsIAccessibleText::GetText[At|Before|After]Offset - * @param aType, eGetBefore, eGetAt, eGetAfter - * @param aBoundaryType, char/word-start/word-end/line-start/line-end/paragraph/attribute - * @param aOffset, offset into the hypertext to start from - * @param *aStartOffset, the resulting start offset for the returned substring - * @param *aEndOffset, the resulting end offset for the returned substring - * @param aText, the resulting substring - * @return success/failure code - */ - nsresult GetTextHelper(EGetTextType aType, AccessibleTextBoundary aBoundaryType, - int32_t aOffset, int32_t *aStartOffset, int32_t *aEndOffset, - nsAString & aText); - /** - * Used by GetTextHelper() to move backward/forward from a given point + * Used by FindOffset() to move backward/forward from a given point * by word/line/etc. * * @param aPresShell the current presshell we're moving in diff --git a/accessible/tests/mochitest/text.js b/accessible/tests/mochitest/text.js index 0681b31221a..09d34ca2cca 100644 --- a/accessible/tests/mochitest/text.js +++ b/accessible/tests/mochitest/text.js @@ -6,7 +6,6 @@ const BOUNDARY_WORD_START = nsIAccessibleText.BOUNDARY_WORD_START; const BOUNDARY_WORD_END = nsIAccessibleText.BOUNDARY_WORD_END; const BOUNDARY_LINE_START = nsIAccessibleText.BOUNDARY_LINE_START; const BOUNDARY_LINE_END = nsIAccessibleText.BOUNDARY_LINE_END; -const BOUNDARY_ATTRIBUTE_RANGE = nsIAccessibleText.BOUNDARY_ATTRIBUTE_RANGE; const kTextEndOffset = nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT; const kCaretOffset = nsIAccessibleText.TEXT_OFFSET_CARET; @@ -585,7 +584,5 @@ function boundaryToString(aBoundaryType) return "line start"; case BOUNDARY_LINE_END: return "line end"; - case BOUNDARY_ATTRIBUTE_RANGE: - return "attr range"; } } From d8fa3b15c05788952fcceb33f11db678c4331e92 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 16 Jul 2013 14:45:41 +0200 Subject: [PATCH 30/31] Bug 894150 - Unregister the PannerNode in the AudioListener when destroying the stream. r=ehsan --- content/media/webaudio/AudioContext.cpp | 3 +++ content/media/webaudio/AudioListener.cpp | 5 +++++ content/media/webaudio/AudioListener.h | 1 + content/media/webaudio/PannerNode.cpp | 8 +++++++ content/media/webaudio/PannerNode.h | 3 +++ content/media/webaudio/test/Makefile.in | 1 + .../media/webaudio/test/test_bug894150.html | 21 +++++++++++++++++++ 7 files changed, 42 insertions(+) create mode 100644 content/media/webaudio/test/test_bug894150.html diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp index 44abf6d05de..de380dedb12 100644 --- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -401,6 +401,9 @@ void AudioContext::UnregisterPannerNode(PannerNode* aNode) { mPannerNodes.RemoveEntry(aNode); + if (mListener) { + mListener->UnregisterPannerNode(aNode); + } } void diff --git a/content/media/webaudio/AudioListener.cpp b/content/media/webaudio/AudioListener.cpp index e20bbdc13c2..8c3c9163852 100644 --- a/content/media/webaudio/AudioListener.cpp +++ b/content/media/webaudio/AudioListener.cpp @@ -52,6 +52,11 @@ AudioListener::RegisterPannerNode(PannerNode* aPannerNode) UpdatePannersVelocity(); } +void AudioListener::UnregisterPannerNode(PannerNode* aPannerNode) +{ + mPanners.RemoveElement(aPannerNode); +} + void AudioListener::SendDoubleParameterToStream(uint32_t aIndex, double aValue) { diff --git a/content/media/webaudio/AudioListener.h b/content/media/webaudio/AudioListener.h index e2e83d852e8..bb38108cca5 100644 --- a/content/media/webaudio/AudioListener.h +++ b/content/media/webaudio/AudioListener.h @@ -127,6 +127,7 @@ public: } void RegisterPannerNode(PannerNode* aPannerNode); + void UnregisterPannerNode(PannerNode* aPannerNode); private: void SendDoubleParameterToStream(uint32_t aIndex, double aValue); diff --git a/content/media/webaudio/PannerNode.cpp b/content/media/webaudio/PannerNode.cpp index 89036112024..9ad2a1422e7 100644 --- a/content/media/webaudio/PannerNode.cpp +++ b/content/media/webaudio/PannerNode.cpp @@ -212,6 +212,14 @@ PannerNode::WrapObject(JSContext* aCx, JS::Handle aScope) return PannerNodeBinding::Wrap(aCx, aScope, this); } +void PannerNode::DestroyMediaStream() +{ + if (Context()) { + Context()->UnregisterPannerNode(this); + } + AudioNode::DestroyMediaStream(); +} + // Those three functions are described in the spec. float PannerNodeEngine::LinearGainFunction(float aDistance) diff --git a/content/media/webaudio/PannerNode.h b/content/media/webaudio/PannerNode.h index d0b0839a319..065d1d2d1d9 100644 --- a/content/media/webaudio/PannerNode.h +++ b/content/media/webaudio/PannerNode.h @@ -31,9 +31,12 @@ public: explicit PannerNode(AudioContext* aContext); virtual ~PannerNode(); + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; + virtual void DestroyMediaStream() MOZ_OVERRIDE; + NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PannerNode, AudioNode) diff --git a/content/media/webaudio/test/Makefile.in b/content/media/webaudio/test/Makefile.in index c4cfe976c7c..7a45ccdf0dc 100644 --- a/content/media/webaudio/test/Makefile.in +++ b/content/media/webaudio/test/Makefile.in @@ -27,6 +27,7 @@ MOCHITEST_FILES := \ test_bug867203.html \ test_bug875221.html \ test_bug875402.html \ + test_bug894150.html \ test_analyserNode.html \ test_AudioBuffer.html \ test_AudioContext.html \ diff --git a/content/media/webaudio/test/test_bug894150.html b/content/media/webaudio/test/test_bug894150.html new file mode 100644 index 00000000000..676826d0d00 --- /dev/null +++ b/content/media/webaudio/test/test_bug894150.html @@ -0,0 +1,21 @@ + + + + Test whether we can create an AudioContext interface + + + + + + From 023cccd3392eaab4c6e53d145f53d00069fa1c2b Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Wed, 17 Jul 2013 15:34:52 +0100 Subject: [PATCH 31/31] Bug 869696 - Disable gralloc surfaces on the Geeksphone Peak. r=jrmuizel Geeksphone distribute a known bad driver that has issues when using gralloc-backed surfaces. Performance is much better just falling back to shared memory. --- gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp | 27 +++++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp index 6890c1604ff..d782650c663 100644 --- a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp +++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp @@ -346,13 +346,28 @@ ISurfaceAllocator::PlatformAllocSurfaceDescriptor(const gfxIntSize& aSize, SurfaceDescriptor* aBuffer) { - // Check for Nexus S to disable gralloc. We only check for this on ICS or - // earlier, in hopes that JB will work. + // Check for devices that have problems with gralloc. We only check for + // this on ICS or earlier, in hopes that JB will work. #if ANDROID_VERSION <= 15 - char propValue[PROPERTY_VALUE_MAX]; - property_get("ro.product.device", propValue, "None"); - if (strcmp("crespo",propValue) == 0) { - NS_WARNING("Nexus S has issues with gralloc, falling back to shmem"); + static bool checkedDevice = false; + static bool disableGralloc = false; + + if (!checkedDevice) { + char propValue[PROPERTY_VALUE_MAX]; + property_get("ro.product.device", propValue, "None"); + + if (strcmp("crespo",propValue) == 0) { + NS_WARNING("Nexus S has issues with gralloc, falling back to shmem"); + disableGralloc = true; + } else if (strcmp("peak", propValue) == 0) { + NS_WARNING("Geeksphone Peak has issues with gralloc, falling back to shmem"); + disableGralloc = true; + } + + checkedDevice = true; + } + + if (disableGralloc) { return false; } #endif