зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-inbound and mozulla-central
This commit is contained in:
Коммит
e2c04d1fe3
|
@ -510,9 +510,11 @@ protected:
|
|||
{
|
||||
ContextState& state = CurrentState();
|
||||
|
||||
// The spec says we should not draw shadows when the alpha value is 0,
|
||||
// regardless of the operator being used.
|
||||
return state.StyleIsColor(STYLE_SHADOW) &&
|
||||
NS_GET_A(state.colorStyles[STYLE_SHADOW]) > 0 &&
|
||||
mThebes->CurrentOperator() == gfxContext::OPERATOR_OVER;
|
||||
(state.shadowOffset != gfxPoint(0, 0) || state.shadowBlur != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -621,7 +621,18 @@ protected:
|
|||
|
||||
// The spec says we should not draw shadows if the operator is OVER.
|
||||
// If it's over and the alpha value is zero, nothing needs to be drawn.
|
||||
return state.op == OP_OVER && NS_GET_A(state.shadowColor) != 0;
|
||||
return NS_GET_A(state.shadowColor) != 0 &&
|
||||
(state.shadowBlur != 0 || state.shadowOffset.x != 0 || state.shadowOffset.y != 0);
|
||||
}
|
||||
|
||||
CompositionOp UsedOperation()
|
||||
{
|
||||
if (NeedToDrawShadow()) {
|
||||
// In this case the shadow rendering will use the operator.
|
||||
return OP_OVER;
|
||||
}
|
||||
|
||||
return CurrentState().op;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -928,7 +939,8 @@ protected:
|
|||
|
||||
mCtx->mTarget->DrawSurfaceWithShadow(snapshot, mSurfOffset,
|
||||
Color::FromABGR(mCtx->CurrentState().shadowColor),
|
||||
mCtx->CurrentState().shadowOffset, mSigma);
|
||||
mCtx->CurrentState().shadowOffset, mSigma,
|
||||
mCtx->CurrentState().op);
|
||||
}
|
||||
|
||||
DrawTarget* operator->()
|
||||
|
@ -2136,7 +2148,7 @@ nsCanvasRenderingContext2DAzure::FillRect(float x, float y, float w, float h)
|
|||
|
||||
AdjustedTarget(this)->FillRect(mgfx::Rect(x, y, w, h),
|
||||
GeneralPattern().ForStyle(this, STYLE_FILL, mTarget),
|
||||
DrawOptions(state.globalAlpha, state.op));
|
||||
DrawOptions(state.globalAlpha, UsedOperation()));
|
||||
|
||||
return RedrawUser(gfxRect(x, y, w, h));
|
||||
}
|
||||
|
@ -2165,7 +2177,7 @@ nsCanvasRenderingContext2DAzure::StrokeRect(float x, float y, float w, float h)
|
|||
state.dash.Length(),
|
||||
state.dash.Elements(),
|
||||
state.dashOffset),
|
||||
DrawOptions(state.globalAlpha, state.op));
|
||||
DrawOptions(state.globalAlpha, UsedOperation()));
|
||||
return NS_OK;
|
||||
} else if (!w) {
|
||||
CapStyle cap = CAP_BUTT;
|
||||
|
@ -2180,7 +2192,7 @@ nsCanvasRenderingContext2DAzure::StrokeRect(float x, float y, float w, float h)
|
|||
state.dash.Length(),
|
||||
state.dash.Elements(),
|
||||
state.dashOffset),
|
||||
DrawOptions(state.globalAlpha, state.op));
|
||||
DrawOptions(state.globalAlpha, UsedOperation()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2192,7 +2204,7 @@ nsCanvasRenderingContext2DAzure::StrokeRect(float x, float y, float w, float h)
|
|||
state.dash.Length(),
|
||||
state.dash.Elements(),
|
||||
state.dashOffset),
|
||||
DrawOptions(state.globalAlpha, state.op));
|
||||
DrawOptions(state.globalAlpha, UsedOperation()));
|
||||
|
||||
return Redraw();
|
||||
}
|
||||
|
@ -2236,7 +2248,7 @@ nsCanvasRenderingContext2DAzure::Fill()
|
|||
|
||||
AdjustedTarget(this)->
|
||||
Fill(mPath, GeneralPattern().ForStyle(this, STYLE_FILL, mTarget),
|
||||
DrawOptions(CurrentState().globalAlpha, CurrentState().op));
|
||||
DrawOptions(CurrentState().globalAlpha, UsedOperation()));
|
||||
|
||||
return Redraw();
|
||||
}
|
||||
|
@ -2259,7 +2271,7 @@ nsCanvasRenderingContext2DAzure::Stroke()
|
|||
state.dash.Length(),
|
||||
state.dash.Elements(),
|
||||
state.dashOffset),
|
||||
DrawOptions(state.globalAlpha, state.op));
|
||||
DrawOptions(state.globalAlpha, UsedOperation()));
|
||||
|
||||
return Redraw();
|
||||
}
|
||||
|
@ -3133,7 +3145,7 @@ struct NS_STACK_CLASS nsCanvasBidiProcessorAzure : public nsBidiPresUtils::BidiP
|
|||
FillGlyphs(scaledFont, buffer,
|
||||
nsCanvasRenderingContext2DAzure::GeneralPattern().
|
||||
ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_FILL, mCtx->mTarget),
|
||||
DrawOptions(mState->globalAlpha, mState->op));
|
||||
DrawOptions(mState->globalAlpha, mCtx->UsedOperation()));
|
||||
} else if (mOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_STROKE) {
|
||||
RefPtr<Path> path = scaledFont->GetPathForGlyphs(buffer, mCtx->mTarget);
|
||||
|
||||
|
@ -3148,7 +3160,7 @@ struct NS_STACK_CLASS nsCanvasBidiProcessorAzure : public nsBidiPresUtils::BidiP
|
|||
state.dash.Length(),
|
||||
state.dash.Elements(),
|
||||
state.dashOffset),
|
||||
DrawOptions(state.globalAlpha, state.op));
|
||||
DrawOptions(state.globalAlpha, mCtx->UsedOperation()));
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -3784,7 +3796,7 @@ nsCanvasRenderingContext2DAzure::DrawImage(nsIDOMElement *imgElt, float a1,
|
|||
mgfx::Rect(dx, dy, dw, dh),
|
||||
mgfx::Rect(sx, sy, sw, sh),
|
||||
DrawSurfaceOptions(filter),
|
||||
DrawOptions(CurrentState().globalAlpha, CurrentState().op));
|
||||
DrawOptions(CurrentState().globalAlpha, UsedOperation()));
|
||||
|
||||
return RedrawUser(gfxRect(dx, dy, dw, dh));
|
||||
}
|
||||
|
|
|
@ -87,7 +87,6 @@ _TEST_FILES_0 = \
|
|||
test_canvas_strokeStyle_getter.html \
|
||||
test_bug613794.html \
|
||||
test_drawImage_edge_cases.html \
|
||||
test_shadow_operators.html \
|
||||
$(NULL)
|
||||
|
||||
ifneq (1_Linux,$(MOZ_SUITE)_$(OS_ARCH))
|
||||
|
|
|
@ -16742,7 +16742,7 @@ ctx.shadowOffsetX = 100;
|
|||
ctx.fillStyle = '#0f0';
|
||||
ctx.fillRect(-100, 0, 200, 50);
|
||||
|
||||
isPixel(ctx, 50, 25, 255, 255, 0, 255, 2);
|
||||
isPixel(ctx, 50, 25, 0, 255, 0, 255, 2);
|
||||
|
||||
}
|
||||
</script>
|
||||
|
@ -16768,7 +16768,7 @@ ctx.shadowBlur = 1;
|
|||
ctx.fillStyle = '#0f0';
|
||||
ctx.fillRect(-10, -10, 120, 70);
|
||||
|
||||
isPixel(ctx, 50, 25, 255, 255, 0, 255, 2);
|
||||
isPixel(ctx, 50, 25, 0, 255, 0, 255, 2);
|
||||
|
||||
}
|
||||
</script>
|
||||
|
@ -21501,14 +21501,13 @@ function runTests() {
|
|||
//test_2d_composite_uncovered_pattern_source_in();
|
||||
//test_2d_composite_uncovered_pattern_source_out();
|
||||
|
||||
//test_2d_path_rect_zero_6(); // This test is bogus according to the spec; see bug 407107
|
||||
|
||||
// The following tests are disabled due to pending changes in the spec:
|
||||
//
|
||||
//test_2d_shadow_composite_3();
|
||||
//test_2d_path_rect_zero_6(); // This test is bogus according to the spec; see bug 407107
|
||||
|
||||
// These tests are bogus according to the spec: shadows should not be
|
||||
// drawn if shadowBlur, shadowOffsetX, and shadowOffsetY are all zero, whic
|
||||
// they are in these tests
|
||||
//test_2d_shadow_composite_3();
|
||||
//test_2d_shadow_composite_4();
|
||||
//test_2d_shadow_composite_1();
|
||||
//test_2d_shadow_composite_2();
|
||||
try {
|
||||
test_2d_canvas_readonly();
|
||||
} catch (e) {
|
||||
|
@ -23894,6 +23893,16 @@ function runTests() {
|
|||
} catch (e) {
|
||||
ok(false, "unexpected exception thrown in: test_2d_shadow_clip_3");
|
||||
}
|
||||
try {
|
||||
test_2d_shadow_composite_1();
|
||||
} catch (e) {
|
||||
ok(false, "unexpected exception thrown in: test_2d_shadow_composite_1");
|
||||
}
|
||||
try {
|
||||
test_2d_shadow_composite_2();
|
||||
} catch (e) {
|
||||
ok(false, "unexpected exception thrown in: test_2d_shadow_composite_2");
|
||||
}
|
||||
try {
|
||||
test_2d_shadow_gradient_alpha();
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display">
|
||||
<canvas id="c" width="2" height="2"></canvas>
|
||||
<canvas id="c2" width="2" height="2"></canvas>
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
function isPixel(ctx, x,y, r,g,b,a, d) {
|
||||
var pos = x + "," + y;
|
||||
var colour = r + "," + g + "," + b + "," + a;
|
||||
var pixel = ctx.getImageData(x, y, 1, 1);
|
||||
var pr = pixel.data[0],
|
||||
pg = pixel.data[1],
|
||||
pb = pixel.data[2],
|
||||
pa = pixel.data[3];
|
||||
ok(r-d <= pr && pr <= r+d &&
|
||||
g-d <= pg && pg <= g+d &&
|
||||
b-d <= pb && pb <= b+d &&
|
||||
a-d <= pa && pa <= a+d,
|
||||
"pixel "+pos+" of "+ctx.canvas.id+" is "+pr+","+pg+","+pb+","+pa+"; expected "+colour+" +/- "+d);
|
||||
}
|
||||
|
||||
function isSamePixel(ctx,ctx2, x,y, d) {
|
||||
var pos = x + "," + y;
|
||||
var pixel = ctx.getImageData(x, y, 1, 1);
|
||||
var pixel2 = ctx2.getImageData(x, y, 1, 1);
|
||||
var pr = pixel.data[0],
|
||||
pg = pixel.data[1],
|
||||
pb = pixel.data[2],
|
||||
pa = pixel.data[3];
|
||||
var r = pixel2.data[0],
|
||||
g = pixel2.data[1],
|
||||
b = pixel2.data[2],
|
||||
a = pixel2.data[3];
|
||||
var colour = r + "," + g + "," + b + "," + a;
|
||||
ok(r-d <= pr && pr <= r+d &&
|
||||
g-d <= pg && pg <= g+d &&
|
||||
b-d <= pb && pb <= b+d &&
|
||||
a-d <= pa && pa <= a+d,
|
||||
"pixel "+pos+" of "+ctx.canvas.id+" is "+pr+","+pg+","+pb+","+pa+"; expected "+colour+" +/- "+d);
|
||||
}
|
||||
|
||||
var c = document.getElementById("c");
|
||||
var ctx = c.getContext("2d");
|
||||
|
||||
ctx.shadowColor = "rgb(0,255,0)";
|
||||
|
||||
// Test that shadows with zero blur and offset are drawn
|
||||
ctx.fillStyle = "rgba(0,0,255,0.5)";
|
||||
ctx.fillRect(0, 0, 1, 1);
|
||||
isPixel(ctx, 0,0, 0,85,170,192, 1);
|
||||
|
||||
// Test that non-OVER operators do not draw shadows
|
||||
var c2 = document.getElementById("c2");
|
||||
var ctx2 = c2.getContext("2d");
|
||||
var operators = ["source-atop", "source-in", "source-out", "destination-atop",
|
||||
"destination-in", "destination-out", "destination-over", "lighter", "copy",
|
||||
"xor"];
|
||||
|
||||
ctx.shadowOffsetX = ctx.shadowOffsetY = 1;
|
||||
for (var i = 0; i < operators.length; ++i) {
|
||||
ctx.fillStyle = "red";
|
||||
ctx.fillRect(0, 0, 2, 2);
|
||||
ctx.globalCompositeOperation = operators[i];
|
||||
ctx.fillStyle = "blue";
|
||||
ctx.fillRect(0, 0, 1, 1);
|
||||
|
||||
ctx2.fillStyle = "red";
|
||||
ctx2.fillRect(0, 0, 2, 2);
|
||||
ctx2.globalCompositeOperation = operators[i];
|
||||
ctx2.fillStyle = "blue";
|
||||
ctx2.fillRect(0, 0, 1, 1);
|
||||
|
||||
isSamePixel(ctx,ctx2, 1,1, 0);
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -483,12 +483,14 @@ public:
|
|||
* aColor Color of the drawn shadow
|
||||
* aOffset Offset of the shadow
|
||||
* aSigma Sigma used for the guassian filter kernel
|
||||
* aOperator Composition operator used
|
||||
*/
|
||||
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma) = 0;
|
||||
Float aSigma,
|
||||
CompositionOp aOperator) = 0;
|
||||
|
||||
/*
|
||||
* Clear a rectangle on the draw target to transparent black. This will
|
||||
|
|
|
@ -63,7 +63,8 @@ public:
|
|||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma)
|
||||
Float aSigma,
|
||||
CompositionOp aOperator)
|
||||
{ }
|
||||
|
||||
virtual void ClearRect(const Rect &aRect)
|
||||
|
|
|
@ -122,29 +122,7 @@ public:
|
|||
gfxWarning() << "Failed to create shared bitmap for old surface.";
|
||||
}
|
||||
|
||||
factory()->CreatePathGeometry(byRef(mClippedArea));
|
||||
RefPtr<ID2D1GeometrySink> currentSink;
|
||||
mClippedArea->Open(byRef(currentSink));
|
||||
|
||||
std::vector<DrawTargetD2D::PushedClip>::iterator iter = mDT->mPushedClips.begin();
|
||||
iter->mPath->GetGeometry()->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
|
||||
iter->mTransform, currentSink);
|
||||
|
||||
currentSink->Close();
|
||||
|
||||
iter++;
|
||||
for (;iter != mDT->mPushedClips.end(); iter++) {
|
||||
RefPtr<ID2D1PathGeometry> newGeom;
|
||||
factory()->CreatePathGeometry(byRef(newGeom));
|
||||
|
||||
newGeom->Open(byRef(currentSink));
|
||||
mClippedArea->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
|
||||
iter->mTransform, currentSink);
|
||||
|
||||
currentSink->Close();
|
||||
|
||||
mClippedArea = newGeom;
|
||||
}
|
||||
mClippedArea = mDT->GetClippedGeometry();
|
||||
}
|
||||
|
||||
ID2D1Factory *factory() { return mDT->factory(); }
|
||||
|
@ -163,7 +141,9 @@ public:
|
|||
mDT->mTransformDirty = true;
|
||||
|
||||
RefPtr<ID2D1RectangleGeometry> rectGeom;
|
||||
factory()->CreateRectangleGeometry(D2D1::InfiniteRect(), byRef(rectGeom));
|
||||
factory()->CreateRectangleGeometry(
|
||||
D2D1::RectF(0, 0, float(mDT->mSize.width), float(mDT->mSize.height)),
|
||||
byRef(rectGeom));
|
||||
|
||||
RefPtr<ID2D1PathGeometry> invClippedArea;
|
||||
factory()->CreatePathGeometry(byRef(invClippedArea));
|
||||
|
@ -188,7 +168,7 @@ private:
|
|||
// with the old dest surface data.
|
||||
RefPtr<ID2D1Bitmap> mOldSurfBitmap;
|
||||
// This contains the area drawing is clipped to.
|
||||
RefPtr<ID2D1PathGeometry> mClippedArea;
|
||||
RefPtr<ID2D1Geometry> mClippedArea;
|
||||
};
|
||||
|
||||
DrawTargetD2D::DrawTargetD2D()
|
||||
|
@ -307,7 +287,8 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
|||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma)
|
||||
Float aSigma,
|
||||
CompositionOp aOperator)
|
||||
{
|
||||
RefPtr<ID3D10ShaderResourceView> srView = NULL;
|
||||
if (aSurface->GetType() != SURFACE_D2D1_DRAWTARGET) {
|
||||
|
@ -316,6 +297,12 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
|||
|
||||
Flush();
|
||||
|
||||
AutoSaveRestoreClippedOut restoreClippedOut(this);
|
||||
|
||||
if (!IsOperatorBoundByMask(aOperator)) {
|
||||
restoreClippedOut.Save();
|
||||
}
|
||||
|
||||
srView = static_cast<SourceSurfaceD2DTarget*>(aSurface)->GetSRView();
|
||||
|
||||
EnsureViews();
|
||||
|
@ -330,35 +317,36 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
RefPtr<ID3D10RenderTargetView> destRTView = mRTView;
|
||||
RefPtr<ID3D10Texture2D> destTexture;
|
||||
HRESULT hr;
|
||||
|
||||
RefPtr<ID3D10Texture2D> maskTexture;
|
||||
RefPtr<ID3D10ShaderResourceView> maskSRView;
|
||||
if (mPushedClips.size()) {
|
||||
// We need to take clips into account, draw into a temporary surface, which
|
||||
// we then blend back with the proper clips set, using D2D.
|
||||
CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_A8_UNORM,
|
||||
mSize.width, mSize.height,
|
||||
1, 1);
|
||||
desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
|
||||
|
||||
hr = mDevice->CreateTexture2D(&desc, NULL, byRef(destTexture));
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failure to create temporary texture. Size: " << mSize << " Code: " << hr;
|
||||
return;
|
||||
}
|
||||
hr = mDevice->CreateTexture2D(&desc, NULL, byRef(maskTexture));
|
||||
|
||||
hr = mDevice->CreateRenderTargetView(destTexture, NULL, byRef(destRTView));
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failure to create RenderTargetView. Code: " << hr;
|
||||
return;
|
||||
}
|
||||
RefPtr<ID2D1RenderTarget> rt = CreateRTForTexture(maskTexture);
|
||||
|
||||
float color[4] = { 0, 0, 0, 0 };
|
||||
mDevice->ClearRenderTargetView(destRTView, color);
|
||||
RefPtr<ID2D1SolidColorBrush> brush;
|
||||
rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush));
|
||||
|
||||
RefPtr<ID2D1Geometry> geometry = GetClippedGeometry();
|
||||
|
||||
rt->BeginDraw();
|
||||
rt->Clear(D2D1::ColorF(0, 0));
|
||||
rt->FillGeometry(geometry, brush);
|
||||
rt->EndDraw();
|
||||
|
||||
mDevice->CreateShaderResourceView(maskTexture, NULL, byRef(maskSRView));
|
||||
}
|
||||
|
||||
|
||||
IntSize srcSurfSize;
|
||||
ID3D10RenderTargetView *rtViews;
|
||||
D3D10_VIEWPORT viewport;
|
||||
|
@ -526,6 +514,10 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
|||
if (!needBiggerTemp) {
|
||||
tmpRTView = mTempRTView;
|
||||
tmpSRView = mSRView;
|
||||
|
||||
// There could still be content here!
|
||||
float color[4] = { 0, 0, 0, 0 };
|
||||
mDevice->ClearRenderTargetView(tmpRTView, color);
|
||||
} else {
|
||||
CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
srcSurfSize.width,
|
||||
|
@ -591,8 +583,19 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
|||
SetFloatVector(ShaderConstantRectD3D10(-correctedOffset.x / Float(tmpSurfSize.width), -correctedOffset.y / Float(tmpSurfSize.height),
|
||||
mSize.width / Float(tmpSurfSize.width) * dsFactorX,
|
||||
mSize.height / Float(tmpSurfSize.height) * dsFactorY));
|
||||
mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")->
|
||||
GetPassByIndex(1)->Apply(0);
|
||||
|
||||
if (mPushedClips.size()) {
|
||||
mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(maskSRView);
|
||||
mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()->
|
||||
SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
|
||||
mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")->
|
||||
GetPassByIndex(2)->Apply(0);
|
||||
} else {
|
||||
mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")->
|
||||
GetPassByIndex(1)->Apply(0);
|
||||
}
|
||||
|
||||
mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), NULL, 0xffffffff);
|
||||
|
||||
mDevice->Draw(4, 0);
|
||||
|
||||
|
@ -601,39 +604,17 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
|||
SetFloatVector(ShaderConstantRectD3D10(-aDest.x / aSurface->GetSize().width, -aDest.y / aSurface->GetSize().height,
|
||||
Float(mSize.width) / aSurface->GetSize().width,
|
||||
Float(mSize.height) / aSurface->GetSize().height));
|
||||
mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->
|
||||
GetPassByIndex(0)->Apply(0);
|
||||
mDevice->OMSetBlendState(GetBlendStateForOperator(OP_OVER), NULL, 0xffffffff);
|
||||
if (mPushedClips.size()) {
|
||||
mPrivateData->mEffect->GetTechniqueByName("SampleMaskedTexture")->
|
||||
GetPassByIndex(0)->Apply(0);
|
||||
} else {
|
||||
mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->
|
||||
GetPassByIndex(0)->Apply(0);
|
||||
}
|
||||
|
||||
mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), NULL, 0xffffffff);
|
||||
|
||||
mDevice->Draw(4, 0);
|
||||
|
||||
if (mPushedClips.size()) {
|
||||
// Assert destTexture
|
||||
|
||||
// Blend back using the proper clips.
|
||||
PrepareForDrawing(mRT);
|
||||
|
||||
RefPtr<IDXGISurface> surf;
|
||||
hr = destTexture->QueryInterface((IDXGISurface**) byRef(surf));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failure to QI texture to surface. Code: " << hr;
|
||||
return;
|
||||
}
|
||||
|
||||
D2D1_BITMAP_PROPERTIES props =
|
||||
D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(mFormat), AlphaMode(mFormat)));
|
||||
RefPtr<ID2D1Bitmap> bitmap;
|
||||
hr = mRT->CreateSharedBitmap(IID_IDXGISurface, surf,
|
||||
&props, byRef(bitmap));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failure to create shared bitmap for surface. Code: " << hr;
|
||||
return;
|
||||
}
|
||||
|
||||
mRT->DrawBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1409,6 +1390,38 @@ DrawTargetD2D::FinalizeRTForOperator(CompositionOp aOperator, const Rect &aBound
|
|||
mDevice->Draw(4, 0);
|
||||
}
|
||||
|
||||
TemporaryRef<ID2D1Geometry>
|
||||
DrawTargetD2D::GetClippedGeometry()
|
||||
{
|
||||
RefPtr<ID2D1GeometrySink> currentSink;
|
||||
RefPtr<ID2D1PathGeometry> clippedGeometry;
|
||||
|
||||
factory()->CreatePathGeometry(byRef(clippedGeometry));
|
||||
clippedGeometry->Open(byRef(currentSink));
|
||||
|
||||
std::vector<DrawTargetD2D::PushedClip>::iterator iter = mPushedClips.begin();
|
||||
iter->mPath->GetGeometry()->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
|
||||
iter->mTransform, currentSink);
|
||||
|
||||
currentSink->Close();
|
||||
|
||||
iter++;
|
||||
for (;iter != mPushedClips.end(); iter++) {
|
||||
RefPtr<ID2D1PathGeometry> newGeom;
|
||||
factory()->CreatePathGeometry(byRef(newGeom));
|
||||
|
||||
newGeom->Open(byRef(currentSink));
|
||||
clippedGeometry->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
|
||||
iter->mTransform, currentSink);
|
||||
|
||||
currentSink->Close();
|
||||
|
||||
clippedGeometry = newGeom;
|
||||
}
|
||||
|
||||
return clippedGeometry;
|
||||
}
|
||||
|
||||
TemporaryRef<ID2D1RenderTarget>
|
||||
DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture)
|
||||
{
|
||||
|
|
|
@ -79,7 +79,8 @@ public:
|
|||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma);
|
||||
Float aSigma,
|
||||
CompositionOp aOperator);
|
||||
virtual void ClearRect(const Rect &aRect);
|
||||
|
||||
virtual void CopySurface(SourceSurface *aSurface,
|
||||
|
@ -159,6 +160,7 @@ private:
|
|||
void PopAllClips();
|
||||
|
||||
TemporaryRef<ID2D1RenderTarget> CreateRTForTexture(ID3D10Texture2D *aTexture);
|
||||
TemporaryRef<ID2D1Geometry> GetClippedGeometry();
|
||||
|
||||
TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
|
||||
TemporaryRef<ID2D1StrokeStyle> CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions);
|
||||
|
|
|
@ -13,6 +13,7 @@ cbuffer cb0
|
|||
{
|
||||
float4 QuadDesc;
|
||||
float4 TexCoords;
|
||||
float4 MaskTexCoords;
|
||||
}
|
||||
|
||||
cbuffer cb1
|
||||
|
@ -27,9 +28,11 @@ struct VS_OUTPUT
|
|||
{
|
||||
float4 Position : SV_Position;
|
||||
float2 TexCoord : TEXCOORD0;
|
||||
float2 MaskTexCoord : TEXCOORD1;
|
||||
};
|
||||
|
||||
Texture2D tex;
|
||||
Texture2D mask;
|
||||
|
||||
sampler sSampler = sampler_state {
|
||||
Filter = MIN_MAG_MIP_LINEAR;
|
||||
|
@ -38,6 +41,13 @@ sampler sSampler = sampler_state {
|
|||
AddressV = Clamp;
|
||||
};
|
||||
|
||||
sampler sMaskSampler = sampler_state {
|
||||
Filter = MIN_MAG_MIP_LINEAR;
|
||||
Texture = mask;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
};
|
||||
|
||||
sampler sShadowSampler = sampler_state {
|
||||
Filter = MIN_MAG_MIP_LINEAR;
|
||||
Texture = tex;
|
||||
|
@ -79,6 +89,8 @@ VS_OUTPUT SampleTextureVS(float3 pos : POSITION)
|
|||
Output.Position.z = 0;
|
||||
Output.TexCoord.x = pos.x * TexCoords.z + TexCoords.x;
|
||||
Output.TexCoord.y = pos.y * TexCoords.w + TexCoords.y;
|
||||
Output.MaskTexCoord.x = pos.x * MaskTexCoords.z + MaskTexCoords.x;
|
||||
Output.MaskTexCoord.y = pos.y * MaskTexCoords.w + MaskTexCoords.y;
|
||||
return Output;
|
||||
}
|
||||
|
||||
|
@ -87,6 +99,11 @@ float4 SampleTexturePS( VS_OUTPUT In) : SV_Target
|
|||
return tex.Sample(sSampler, In.TexCoord);
|
||||
};
|
||||
|
||||
float4 SampleMaskTexturePS( VS_OUTPUT In) : SV_Target
|
||||
{
|
||||
return tex.Sample(sSampler, In.TexCoord) * mask.Sample(sMaskSampler, In.MaskTexCoord).a;
|
||||
};
|
||||
|
||||
float4 SampleShadowHPS( VS_OUTPUT In) : SV_Target
|
||||
{
|
||||
float outputStrength = 0;
|
||||
|
@ -121,6 +138,23 @@ float4 SampleShadowVPS( VS_OUTPUT In) : SV_Target
|
|||
return outputColor;
|
||||
};
|
||||
|
||||
float4 SampleMaskShadowVPS( VS_OUTPUT In) : SV_Target
|
||||
{
|
||||
float4 outputColor = float4(0, 0, 0, 0);
|
||||
|
||||
outputColor += BlurWeights[0].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].x));
|
||||
outputColor += BlurWeights[0].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].y));
|
||||
outputColor += BlurWeights[0].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].z));
|
||||
outputColor += BlurWeights[0].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[0].w));
|
||||
outputColor += BlurWeights[1].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].x));
|
||||
outputColor += BlurWeights[1].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].y));
|
||||
outputColor += BlurWeights[1].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].z));
|
||||
outputColor += BlurWeights[1].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].w));
|
||||
outputColor += BlurWeights[2].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[2].x));
|
||||
|
||||
return outputColor * mask.Sample(sMaskSampler, In.MaskTexCoord).a;
|
||||
};
|
||||
|
||||
technique10 SampleTexture
|
||||
{
|
||||
pass P0
|
||||
|
@ -132,6 +166,16 @@ technique10 SampleTexture
|
|||
}
|
||||
}
|
||||
|
||||
technique10 SampleMaskedTexture
|
||||
{
|
||||
pass P0
|
||||
{
|
||||
SetRasterizerState(TextureRast);
|
||||
SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
|
||||
SetGeometryShader(NULL);
|
||||
SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleMaskTexturePS()));
|
||||
}
|
||||
}
|
||||
|
||||
technique10 SampleTextureWithShadow
|
||||
{
|
||||
|
@ -153,4 +197,13 @@ technique10 SampleTextureWithShadow
|
|||
SetGeometryShader(NULL);
|
||||
SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleShadowVPS()));
|
||||
}
|
||||
}
|
||||
// Vertical pass - used when using a mask
|
||||
pass P2
|
||||
{
|
||||
SetRasterizerState(TextureRast);
|
||||
SetBlendState(ShadowBlendV, float4(1.0f, 1.0f, 1.0f, 1.0f), 0xffffffff);
|
||||
SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
|
||||
SetGeometryShader(NULL);
|
||||
SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleMaskShadowVPS()));
|
||||
}
|
||||
}
|
||||
|
|
3691
gfx/2d/ShadersD2D.h
3691
gfx/2d/ShadersD2D.h
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -16,12 +16,13 @@ namespace mozilla {
|
|||
namespace gfx {
|
||||
|
||||
# if defined(MOZILLA_MAY_SUPPORT_NEON)
|
||||
void __attribute((noinline)) yuv42x_to_rgb565_row_neon(uint16 *dst,
|
||||
const uint8 *y,
|
||||
const uint8 *u,
|
||||
const uint8 *v,
|
||||
int n,
|
||||
int oddflag)
|
||||
void __attribute((noinline,optimize("-fomit-frame-pointer")))
|
||||
yuv42x_to_rgb565_row_neon(uint16 *dst,
|
||||
const uint8 *y,
|
||||
const uint8 *u,
|
||||
const uint8 *v,
|
||||
int n,
|
||||
int oddflag)
|
||||
{
|
||||
static __attribute__((aligned(16))) uint16 acc_r[8] = {
|
||||
22840, 22840, 22840, 22840, 22840, 22840, 22840, 22840,
|
||||
|
|
|
@ -174,6 +174,7 @@ XPC_MSG_DEF(NS_ERROR_NO_CONTENT , "Channel opened successful
|
|||
XPC_MSG_DEF(NS_ERROR_IN_PROGRESS , "The requested action could not be completed while the object is busy")
|
||||
XPC_MSG_DEF(NS_ERROR_ALREADY_OPENED , "Channel is already open")
|
||||
XPC_MSG_DEF(NS_ERROR_INVALID_CONTENT_ENCODING , "The content encoding of the source document is incorrect")
|
||||
XPC_MSG_DEF(NS_ERROR_CORRUPTED_CONTENT , "Corrupted content was received from server")
|
||||
XPC_MSG_DEF(NS_ERROR_ALREADY_CONNECTED , "The connection is already established")
|
||||
XPC_MSG_DEF(NS_ERROR_NOT_CONNECTED , "The connection does not exist")
|
||||
XPC_MSG_DEF(NS_ERROR_CONNECTION_REFUSED , "The connection was refused")
|
||||
|
@ -187,12 +188,15 @@ XPC_MSG_DEF(NS_ERROR_NOT_RESUMABLE , "This request is not resum
|
|||
XPC_MSG_DEF(NS_ERROR_ENTITY_CHANGED , "It was attempted to resume the request, but the entity has changed in the meantime")
|
||||
XPC_MSG_DEF(NS_ERROR_REDIRECT_LOOP , "The request failed as a result of a detected redirection loop")
|
||||
XPC_MSG_DEF(NS_ERROR_UNSAFE_CONTENT_TYPE , "The request failed because the content type returned by the server was not a type expected by the channel")
|
||||
XPC_MSG_DEF(NS_ERROR_REMOTE_XUL , "Attempt to access remote XUL document that is not in website's whitelist")
|
||||
|
||||
XPC_MSG_DEF(NS_ERROR_FTP_LOGIN , "FTP error while logging in")
|
||||
XPC_MSG_DEF(NS_ERROR_FTP_CWD , "FTP error while changing directory")
|
||||
XPC_MSG_DEF(NS_ERROR_FTP_PASV , "FTP error while changing to passive mode")
|
||||
XPC_MSG_DEF(NS_ERROR_FTP_PWD , "FTP error while retrieving current directory")
|
||||
XPC_MSG_DEF(NS_ERROR_FTP_LIST , "FTP error while retrieving a directory listing")
|
||||
XPC_MSG_DEF(NS_ERROR_UNKNOWN_HOST , "The lookup of the hostname failed")
|
||||
XPC_MSG_DEF(NS_ERROR_DNS_LOOKUP_QUEUE_FULL , "The DNS lookup queue is full")
|
||||
XPC_MSG_DEF(NS_ERROR_UNKNOWN_PROXY_HOST , "The lookup of the proxy hostname failed")
|
||||
XPC_MSG_DEF(NS_ERROR_UNKNOWN_SOCKET_TYPE , "The specified socket type does not exist")
|
||||
XPC_MSG_DEF(NS_ERROR_SOCKET_CREATE_FAILED , "The specified socket type could not be created")
|
||||
|
|
|
@ -66,6 +66,7 @@ HttpChannelParent::HttpChannelParent(PBrowserParent* iframeEmbedding)
|
|||
, mStoredStatus(0)
|
||||
, mStoredProgress(0)
|
||||
, mStoredProgressMax(0)
|
||||
, mHeadersToSyncToChild(nsnull)
|
||||
{
|
||||
// Ensure gHttpHandler is initialized: we need the atom table up and running.
|
||||
nsIHttpProtocolHandler* handler;
|
||||
|
@ -93,13 +94,14 @@ HttpChannelParent::ActorDestroy(ActorDestroyReason why)
|
|||
// HttpChannelParent::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS6(HttpChannelParent,
|
||||
NS_IMPL_ISUPPORTS7(HttpChannelParent,
|
||||
nsIInterfaceRequestor,
|
||||
nsIProgressEventSink,
|
||||
nsIRequestObserver,
|
||||
nsIStreamListener,
|
||||
nsIParentChannel,
|
||||
nsIParentRedirectingChannel)
|
||||
nsIParentRedirectingChannel,
|
||||
nsIHttpHeaderVisitor)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParent::nsIInterfaceRequestor
|
||||
|
@ -425,15 +427,11 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
|
|||
NS_SerializeToString(secInfoSer, secInfoSerialization);
|
||||
}
|
||||
|
||||
// sync request headers to child, in case they've changed
|
||||
RequestHeaderTuples headers;
|
||||
nsHttpHeaderArray harray = requestHead->Headers();
|
||||
|
||||
for (PRUint32 i = 0; i < harray.Count(); i++) {
|
||||
RequestHeaderTuple* tuple = headers.AppendElement();
|
||||
tuple->mHeader = harray.Headers()[i].header;
|
||||
tuple->mValue = harray.Headers()[i].value;
|
||||
tuple->mMerge = false;
|
||||
}
|
||||
mHeadersToSyncToChild = &headers;
|
||||
requestHead->Headers().VisitHeaders(this);
|
||||
mHeadersToSyncToChild = 0;
|
||||
|
||||
nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
|
||||
if (mIPCClosed ||
|
||||
|
@ -598,4 +596,21 @@ HttpChannelParent::CompleteRedirect(PRBool succeeded)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParent::nsIHttpHeaderVisitor
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
HttpChannelParent::VisitHeader(const nsACString &header, const nsACString &value)
|
||||
{
|
||||
// Will be set unless some random code QI's us to nsIHttpHeaderVisitor
|
||||
NS_ENSURE_STATE(mHeadersToSyncToChild);
|
||||
|
||||
RequestHeaderTuple* tuple = mHeadersToSyncToChild->AppendElement();
|
||||
tuple->mHeader = header;
|
||||
tuple->mValue = value;
|
||||
tuple->mMerge = false; // headers already merged:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}} // mozilla::net
|
||||
|
|
|
@ -63,6 +63,7 @@ class HttpChannelParent : public PHttpChannelParent
|
|||
, public nsIParentRedirectingChannel
|
||||
, public nsIProgressEventSink
|
||||
, public nsIInterfaceRequestor
|
||||
, public nsIHttpHeaderVisitor
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -72,6 +73,7 @@ public:
|
|||
NS_DECL_NSIPARENTREDIRECTINGCHANNEL
|
||||
NS_DECL_NSIPROGRESSEVENTSINK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSIHTTPHEADERVISITOR
|
||||
|
||||
HttpChannelParent(PBrowserParent* iframeEmbedding);
|
||||
virtual ~HttpChannelParent();
|
||||
|
@ -130,6 +132,9 @@ private:
|
|||
nsresult mStoredStatus;
|
||||
PRUint64 mStoredProgress;
|
||||
PRUint64 mStoredProgressMax;
|
||||
|
||||
// used while visiting headers, to send them to child: else null
|
||||
RequestHeaderTuples *mHeadersToSyncToChild;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -83,9 +83,9 @@ struct ParamTraits<mozilla::net::RequestHeaderTuple>
|
|||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
{
|
||||
if (!ReadParam(aMsg, aIter, &(aResult->mHeader)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mValue)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mMerge)))
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mHeader) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mValue) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mMerge))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -130,8 +130,8 @@ struct ParamTraits<nsHttpHeaderArray::nsEntry>
|
|||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
{
|
||||
if (!ReadParam(aMsg, aIter, &(aResult->header)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->value)))
|
||||
if (!ReadParam(aMsg, aIter, &aResult->header) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->value))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -147,12 +147,12 @@ struct ParamTraits<nsHttpHeaderArray>
|
|||
{
|
||||
paramType& p = const_cast<paramType&>(aParam);
|
||||
|
||||
WriteParam(aMsg, p.Headers());
|
||||
WriteParam(aMsg, p.mHeaders);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
{
|
||||
if (!ReadParam(aMsg, aIter, &(aResult->Headers())))
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mHeaders))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -180,16 +180,16 @@ struct ParamTraits<nsHttpResponseHead>
|
|||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
{
|
||||
if (!ReadParam(aMsg, aIter, &(aResult->mHeaders)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mVersion)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mStatus)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mStatusText)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mContentLength)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mContentType)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mContentCharset)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mCacheControlNoStore)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mCacheControlNoCache)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mPragmaNoCache)))
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mHeaders) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mVersion) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mStatus) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mStatusText) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mContentLength) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mContentType) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mContentCharset) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mCacheControlNoStore) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mCacheControlNoCache) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mPragmaNoCache))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -137,10 +137,7 @@ nsHttpChunkedDecoder::ParseChunkRemaining(char *buf,
|
|||
LOG(("got trailer: %s\n", buf));
|
||||
// allocate a header array for the trailers on demand
|
||||
if (!mTrailers) {
|
||||
mTrailers = new nsHttpHeaderArray
|
||||
(nsHttpHeaderArray::HTTP_RESPONSE_HEADERS);
|
||||
if (!mTrailers)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
mTrailers = new nsHttpHeaderArray();
|
||||
}
|
||||
mTrailers->ParseHeaderLine(buf);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com> (original author)
|
||||
* Patrick McManus <mcmanus@ducksong.com>
|
||||
* Jason Duell <jduell.mcbugs@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -43,7 +45,6 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// nsHttpHeaderArray <public>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetHeader(nsHttpAtom header,
|
||||
const nsACString &value,
|
||||
|
@ -62,33 +63,50 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Create a new entry, or...
|
||||
if (!entry) {
|
||||
entry = mHeaders.AppendElement(); // new nsEntry()
|
||||
if (!entry)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
entry->header = header;
|
||||
entry->value = value;
|
||||
} else if (merge && !IsSingletonHeader(header)) {
|
||||
MergeHeader(header, entry, value);
|
||||
} else {
|
||||
// Replace the existing string with the new value
|
||||
entry->value = value;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header, const nsACString &value)
|
||||
{
|
||||
nsEntry *entry = nsnull;
|
||||
PRInt32 index;
|
||||
|
||||
index = LookupEntry(header, &entry);
|
||||
|
||||
if (!entry) {
|
||||
if (value.IsEmpty())
|
||||
return NS_OK; // ignore empty headers
|
||||
entry = mHeaders.AppendElement(); //new nsEntry(header, value);
|
||||
if (!entry)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
entry->header = header;
|
||||
entry->value = value;
|
||||
} else if (!IsSingletonHeader(header)) {
|
||||
MergeHeader(header, entry, value);
|
||||
} else {
|
||||
// Multiple instances of non-mergeable header received from network
|
||||
// - ignore if same value
|
||||
if (!entry->value.Equals(value)) {
|
||||
if (IsSuspectDuplicateHeader(header)) {
|
||||
// reply may be corrupt/hacked (ex: CLRF injection attacks)
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
} // else silently drop value: keep value from 1st header seen
|
||||
}
|
||||
}
|
||||
// Append the new value to the existing value iff...
|
||||
else if (merge && CanAppendToHeader(header)) {
|
||||
if (header == nsHttp::Set_Cookie ||
|
||||
header == nsHttp::WWW_Authenticate ||
|
||||
header == nsHttp::Proxy_Authenticate)
|
||||
// Special case these headers and use a newline delimiter to
|
||||
// delimit the values from one another as commas may appear
|
||||
// in the values of these headers contrary to what the spec says.
|
||||
entry->value.Append('\n');
|
||||
else
|
||||
// Delimit each value from the others using a comma (per HTTP spec)
|
||||
entry->value.AppendLiteral(", ");
|
||||
entry->value.Append(value);
|
||||
}
|
||||
// Replace the existing string with the new value
|
||||
else if (CanOverwriteHeader(header))
|
||||
entry->value = value;
|
||||
else if (!entry->value.Equals(value))
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -186,7 +204,7 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
|
|||
if (val) *val = p;
|
||||
|
||||
// assign response header
|
||||
return SetHeader(atom, nsDependentCString(p, p2 - p), PR_TRUE);
|
||||
return SetHeaderFromNet(atom, nsDependentCString(p, p2 - p));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -220,41 +238,3 @@ nsHttpHeaderArray::Clear()
|
|||
{
|
||||
mHeaders.Clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpHeaderArray <private>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
PRInt32
|
||||
nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry)
|
||||
{
|
||||
PRUint32 index = mHeaders.IndexOf(header, 0, nsEntry::MatchHeader());
|
||||
if (index != PR_UINT32_MAX)
|
||||
*entry = &mHeaders[index];
|
||||
return index;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHttpHeaderArray::CanAppendToHeader(nsHttpAtom header)
|
||||
{
|
||||
return header != nsHttp::Content_Type &&
|
||||
header != nsHttp::Content_Length &&
|
||||
header != nsHttp::User_Agent &&
|
||||
header != nsHttp::Referer &&
|
||||
header != nsHttp::Host &&
|
||||
header != nsHttp::Authorization &&
|
||||
header != nsHttp::Proxy_Authorization &&
|
||||
header != nsHttp::If_Modified_Since &&
|
||||
header != nsHttp::If_Unmodified_Since &&
|
||||
header != nsHttp::From &&
|
||||
header != nsHttp::Location &&
|
||||
header != nsHttp::Max_Forwards;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHttpHeaderArray::CanOverwriteHeader(nsHttpAtom header)
|
||||
{
|
||||
if (mType != HTTP_RESPONSE_HEADERS)
|
||||
return PR_TRUE;
|
||||
return header != nsHttp::Content_Length;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
|
@ -49,17 +50,19 @@
|
|||
class nsHttpHeaderArray
|
||||
{
|
||||
public:
|
||||
enum nsHttpHeaderType {
|
||||
HTTP_REQUEST_HEADERS,
|
||||
HTTP_RESPONSE_HEADERS
|
||||
};
|
||||
|
||||
nsHttpHeaderArray(nsHttpHeaderType headerType) : mType(headerType) {}
|
||||
nsHttpHeaderArray() {}
|
||||
~nsHttpHeaderArray() { Clear(); }
|
||||
|
||||
const char *PeekHeader(nsHttpAtom header);
|
||||
|
||||
nsresult SetHeader(nsHttpAtom header, const nsACString &value, PRBool merge = PR_FALSE);
|
||||
// Used by internal setters: to set header from network use SetHeaderFromNet
|
||||
nsresult SetHeader(nsHttpAtom header, const nsACString &value,
|
||||
PRBool merge = PR_FALSE);
|
||||
|
||||
// Merges supported headers. For other duplicate values, determines if error
|
||||
// needs to be thrown or 1st value kept.
|
||||
nsresult SetHeaderFromNet(nsHttpAtom header, const nsACString &value);
|
||||
|
||||
nsresult GetHeader(nsHttpAtom header, nsACString &value);
|
||||
void ClearHeader(nsHttpAtom h);
|
||||
|
||||
|
@ -104,15 +107,90 @@ public:
|
|||
};
|
||||
};
|
||||
|
||||
nsTArray<nsEntry> &Headers() { return mHeaders; }
|
||||
|
||||
private:
|
||||
PRInt32 LookupEntry(nsHttpAtom header, nsEntry **);
|
||||
PRBool CanAppendToHeader(nsHttpAtom header);
|
||||
PRBool CanOverwriteHeader(nsHttpAtom header);
|
||||
void MergeHeader(nsHttpAtom header, nsEntry *entry, const nsACString &value);
|
||||
|
||||
// Header cannot be merged: only one value possible
|
||||
PRBool IsSingletonHeader(nsHttpAtom header);
|
||||
|
||||
// Subset of singleton headers: should never see multiple, different
|
||||
// instances of these, else something fishy may be going on (like CLRF
|
||||
// injection)
|
||||
PRBool IsSuspectDuplicateHeader(nsHttpAtom header);
|
||||
|
||||
nsTArray<nsEntry> mHeaders;
|
||||
nsHttpHeaderType mType;
|
||||
|
||||
friend struct IPC::ParamTraits<nsHttpHeaderArray>;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpHeaderArray <private>: inline functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline PRInt32
|
||||
nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry)
|
||||
{
|
||||
PRUint32 index = mHeaders.IndexOf(header, 0, nsEntry::MatchHeader());
|
||||
if (index != PR_UINT32_MAX)
|
||||
*entry = &mHeaders[index];
|
||||
return index;
|
||||
}
|
||||
|
||||
inline PRBool
|
||||
nsHttpHeaderArray::IsSingletonHeader(nsHttpAtom header)
|
||||
{
|
||||
return header == nsHttp::Content_Type ||
|
||||
header == nsHttp::Content_Disposition ||
|
||||
header == nsHttp::Content_Length ||
|
||||
header == nsHttp::User_Agent ||
|
||||
header == nsHttp::Referer ||
|
||||
header == nsHttp::Host ||
|
||||
header == nsHttp::Authorization ||
|
||||
header == nsHttp::Proxy_Authorization ||
|
||||
header == nsHttp::If_Modified_Since ||
|
||||
header == nsHttp::If_Unmodified_Since ||
|
||||
header == nsHttp::From ||
|
||||
header == nsHttp::Location ||
|
||||
header == nsHttp::Max_Forwards;
|
||||
}
|
||||
|
||||
inline void
|
||||
nsHttpHeaderArray::MergeHeader(nsHttpAtom header,
|
||||
nsEntry *entry,
|
||||
const nsACString &value)
|
||||
{
|
||||
if (value.IsEmpty())
|
||||
return; // merge of empty header = no-op
|
||||
|
||||
// Append the new value to the existing value
|
||||
if (header == nsHttp::Set_Cookie ||
|
||||
header == nsHttp::WWW_Authenticate ||
|
||||
header == nsHttp::Proxy_Authenticate)
|
||||
{
|
||||
// Special case these headers and use a newline delimiter to
|
||||
// delimit the values from one another as commas may appear
|
||||
// in the values of these headers contrary to what the spec says.
|
||||
entry->value.Append('\n');
|
||||
} else {
|
||||
// Delimit each value from the others using a comma (per HTTP spec)
|
||||
entry->value.AppendLiteral(", ");
|
||||
}
|
||||
entry->value.Append(value);
|
||||
}
|
||||
|
||||
inline PRBool
|
||||
nsHttpHeaderArray::IsSuspectDuplicateHeader(nsHttpAtom header)
|
||||
{
|
||||
PRBool retval = header == nsHttp::Content_Length ||
|
||||
header == nsHttp::Content_Disposition ||
|
||||
header == nsHttp::Location;
|
||||
|
||||
NS_ASSERTION(!retval || IsSingletonHeader(header),
|
||||
"Only non-mergeable headers should be in this list\n");
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -52,9 +52,7 @@
|
|||
class nsHttpRequestHead
|
||||
{
|
||||
public:
|
||||
nsHttpRequestHead() : mHeaders(nsHttpHeaderArray::HTTP_REQUEST_HEADERS)
|
||||
, mMethod(nsHttp::Get)
|
||||
, mVersion(NS_HTTP_VERSION_1_1) {}
|
||||
nsHttpRequestHead() : mMethod(nsHttp::Get), mVersion(NS_HTTP_VERSION_1_1) {}
|
||||
~nsHttpRequestHead() {}
|
||||
|
||||
void SetMethod(nsHttpAtom method) { mMethod = method; }
|
||||
|
|
|
@ -445,7 +445,6 @@ nsHttpResponseHead::UpdateHeaders(nsHttpHeaderArray &headers)
|
|||
const char *val = headers.PeekHeaderAt(i, header);
|
||||
|
||||
if (!val) {
|
||||
NS_NOTREACHED("null header value");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,8 +51,7 @@
|
|||
class nsHttpResponseHead
|
||||
{
|
||||
public:
|
||||
nsHttpResponseHead() : mHeaders(nsHttpHeaderArray::HTTP_RESPONSE_HEADERS)
|
||||
, mVersion(NS_HTTP_VERSION_1_1)
|
||||
nsHttpResponseHead() : mVersion(NS_HTTP_VERSION_1_1)
|
||||
, mStatus(200)
|
||||
, mContentLength(LL_MAXUINT)
|
||||
, mCacheControlNoStore(PR_FALSE)
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
do_load_httpd_js();
|
||||
|
||||
var httpserver = new nsHttpServer();
|
||||
var index = 0;
|
||||
|
||||
function setupChannel(url)
|
||||
{
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
var chan = ios.newChannel("http://localhost:4444" + url, "", null);
|
||||
var httpChan = chan.QueryInterface(Components.interfaces.nsIHttpChannel);
|
||||
return httpChan;
|
||||
}
|
||||
|
||||
function completeTest1(request, data, ctx)
|
||||
{
|
||||
httpserver.stop(do_test_finished);
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
httpserver.registerPathHandler("/2xcl", handler);
|
||||
httpserver.start(4444);
|
||||
|
||||
var channel = setupChannel("/2xcl");
|
||||
channel.asyncOpen(new ChannelListener(completeTest1,
|
||||
channel, CL_EXPECT_FAILURE), null);
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function handler(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 200 OK\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 20\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* Tests bugs 597706, 655389: prevent duplicate headers with differing values
|
||||
* for some headers like Content-Length, Location, etc.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test infrastructure
|
||||
|
||||
do_load_httpd_js();
|
||||
|
||||
var httpserver = new nsHttpServer();
|
||||
var index = 0;
|
||||
var test_flags = new Array();
|
||||
var testPathBase = "/dupe_hdrs";
|
||||
|
||||
function run_test()
|
||||
{
|
||||
httpserver.start(4444);
|
||||
|
||||
do_test_pending();
|
||||
run_test_number(1);
|
||||
}
|
||||
|
||||
function run_test_number(num)
|
||||
{
|
||||
testPath = testPathBase + num;
|
||||
httpserver.registerPathHandler(testPath, eval("handler" + num));
|
||||
|
||||
var channel = setupChannel(testPath);
|
||||
flags = test_flags[num]; // OK if flags undefined for test
|
||||
channel.asyncOpen(new ChannelListener(eval("completeTest" + num),
|
||||
channel, flags), null);
|
||||
}
|
||||
|
||||
function setupChannel(url)
|
||||
{
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
var chan = ios.newChannel("http://localhost:4444" + url, "", null);
|
||||
var httpChan = chan.QueryInterface(Components.interfaces.nsIHttpChannel);
|
||||
return httpChan;
|
||||
}
|
||||
|
||||
function endTests()
|
||||
{
|
||||
httpserver.stop(do_test_finished);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 1: FAIL because of conflicting Content-Length headers
|
||||
test_flags[1] = CL_EXPECT_FAILURE;
|
||||
|
||||
function handler1(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
// Comrades! We must seize power from the petty-bourgeois running dogs of
|
||||
// httpd.js in order to reply with multiple instances of the same header!
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 200 OK\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
response.write("Content-Length: 20\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
|
||||
function completeTest1(request, data, ctx)
|
||||
{
|
||||
do_check_eq(request.status, Components.results.NS_ERROR_CORRUPTED_CONTENT);
|
||||
|
||||
run_test_number(2);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 2: OK to have duplicate same Content-Length headers
|
||||
|
||||
function handler2(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 200 OK\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function completeTest2(request, data, ctx)
|
||||
{
|
||||
do_check_eq(request.status, 0);
|
||||
run_test_number(3);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 3: FAIL: 2nd Content-length is blank
|
||||
test_flags[3] = CL_EXPECT_FAILURE;
|
||||
|
||||
function handler3(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 200 OK\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
response.write("Content-Length:\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function completeTest3(request, data, ctx)
|
||||
{
|
||||
do_check_eq(request.status, Components.results.NS_ERROR_CORRUPTED_CONTENT);
|
||||
|
||||
run_test_number(4);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 4: ensure that blank C-len header doesn't allow attacker to reset Clen,
|
||||
// then insert CRLF attack
|
||||
test_flags[4] = CL_EXPECT_FAILURE;
|
||||
|
||||
function handler4(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 200 OK\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
|
||||
// Bad Mr Hacker! Bad!
|
||||
var evilBody = "We are the Evil bytes, Evil bytes, Evil bytes!";
|
||||
response.write("Content-Length:\r\n");
|
||||
response.write("Content-Length: %s\r\n\r\n%s" % (evilBody.length, evilBody));
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function completeTest4(request, data, ctx)
|
||||
{
|
||||
do_check_eq(request.status, Components.results.NS_ERROR_CORRUPTED_CONTENT);
|
||||
|
||||
run_test_number(5);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 5: ensure that we take 1st instance of duplicate, nonmerged headers that
|
||||
// are permitted : (ex: Referrer)
|
||||
|
||||
function handler5(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 200 OK\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
response.write("Referer: naive.org\r\n");
|
||||
response.write("Referer: evil.net\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function completeTest5(request, data, ctx)
|
||||
{
|
||||
try {
|
||||
referer = request.getResponseHeader("Referer");
|
||||
do_check_eq(referer, "naive.org");
|
||||
} catch (ex) {
|
||||
do_throw("Referer header should be present");
|
||||
}
|
||||
|
||||
run_test_number(6);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 5: FAIL if multiple, different Location: headers present
|
||||
// - needed to prevent CRLF injection attacks
|
||||
test_flags[6] = CL_EXPECT_FAILURE;
|
||||
|
||||
function handler6(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 301 Moved\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
response.write("Location: http://localhost:4444/content\r\n");
|
||||
response.write("Location: http://www.microsoft.com/\r\n");
|
||||
response.write("Connection: close/\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function completeTest6(request, data, ctx)
|
||||
{
|
||||
do_check_eq(request.status, Components.results.NS_ERROR_CORRUPTED_CONTENT);
|
||||
|
||||
// run_test_number(7); // Test 7 leaking under e10s: unrelated bug?
|
||||
run_test_number(8);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 7: OK to have multiple Location: headers with same value
|
||||
|
||||
function handler7(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 301 Moved\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
// redirect to previous test handler that completes OK: test 5
|
||||
response.write("Location: http://localhost:4444" + testPathBase + "5\r\n");
|
||||
response.write("Location: http://localhost:4444" + testPathBase + "5\r\n");
|
||||
response.write("Connection: close/\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function completeTest7(request, data, ctx)
|
||||
{
|
||||
// for some reason need this here
|
||||
request.QueryInterface(Components.interfaces.nsIHttpChannel);
|
||||
|
||||
try {
|
||||
referer = request.getResponseHeader("Referer");
|
||||
do_check_eq(referer, "naive.org");
|
||||
} catch (ex) {
|
||||
do_throw("Referer header should be present");
|
||||
}
|
||||
|
||||
run_test_number(8);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FAIL if 2nd Location: headers blank
|
||||
test_flags[8] = CL_EXPECT_FAILURE;
|
||||
|
||||
function handler8(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 301 Moved\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
// redirect to previous test handler that completes OK: test 4
|
||||
response.write("Location: http://localhost:4444" + testPathBase + "4\r\n");
|
||||
response.write("Location:\r\n");
|
||||
response.write("Connection: close/\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function completeTest8(request, data, ctx)
|
||||
{
|
||||
do_check_eq(request.status, Components.results.NS_ERROR_CORRUPTED_CONTENT);
|
||||
|
||||
run_test_number(9);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 9: ensure that blank Location header doesn't allow attacker to reset,
|
||||
// then insert an evil one
|
||||
test_flags[9] = CL_EXPECT_FAILURE;
|
||||
|
||||
function handler9(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 301 Moved\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
// redirect to previous test handler that completes OK: test 2
|
||||
response.write("Location: http://localhost:4444" + testPathBase + "2\r\n");
|
||||
response.write("Location:\r\n");
|
||||
// redirect to previous test handler that completes OK: test 4
|
||||
response.write("Location: http://localhost:4444" + testPathBase + "4\r\n");
|
||||
response.write("Connection: close/\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function completeTest9(request, data, ctx)
|
||||
{
|
||||
// All redirection should fail:
|
||||
do_check_eq(request.status, Components.results.NS_ERROR_CORRUPTED_CONTENT);
|
||||
|
||||
run_test_number(10);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 10: FAIL: if conflicting values for Content-Dispo
|
||||
test_flags[10] = CL_EXPECT_FAILURE;
|
||||
|
||||
function handler10(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 200 OK\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
response.write("Content-Disposition: attachment; filename=foo\r\n");
|
||||
response.write("Content-Disposition: attachment; filename=bar\r\n");
|
||||
response.write("Content-Disposition: attachment; filename=baz\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
|
||||
function completeTest10(request, data, ctx)
|
||||
{
|
||||
do_check_eq(request.status, Components.results.NS_ERROR_CORRUPTED_CONTENT);
|
||||
|
||||
run_test_number(11);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 11: OK to have duplicate same Content-Disposition headers
|
||||
|
||||
function handler11(metadata, response)
|
||||
{
|
||||
var body = "012345678901234567890123456789";
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.0 200 OK\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write("Content-Length: 30\r\n");
|
||||
response.write("Content-Disposition: attachment; filename=foo\r\n");
|
||||
response.write("Content-Disposition: attachment; filename=foo\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function completeTest11(request, data, ctx)
|
||||
{
|
||||
do_check_eq(request.status, 0);
|
||||
|
||||
try {
|
||||
// TODO when bug XXX lands, also get channel C-D properties and make sure
|
||||
// they're blank
|
||||
dispo = request.getResponseHeader("Content-Disposition");
|
||||
do_check_eq(dispo, "attachment; filename=foo");
|
||||
} catch (ex) {
|
||||
do_throw("Content-Disposition should be present");
|
||||
}
|
||||
|
||||
endTests();
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ tail =
|
|||
[test_cookie_header.js]
|
||||
[test_data_protocol.js]
|
||||
[test_dns_service.js]
|
||||
[test_double_content_length.js]
|
||||
[test_duplicate_headers.js]
|
||||
[test_event_sink.js]
|
||||
[test_extract_charset_from_content_type.js]
|
||||
[test_fallback_no-cache-entry_canceled.js]
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
//
|
||||
// Run test script in content process instead of chrome (xpcshell's default)
|
||||
//
|
||||
|
||||
function run_test() {
|
||||
run_test_in_child("../unit/test_duplicate_headers.js");
|
||||
}
|
|
@ -4,6 +4,7 @@ tail =
|
|||
|
||||
[test_channel_close_wrap.js]
|
||||
[test_cookie_wrap.js]
|
||||
[test_duplicate_headers_wrap.js]
|
||||
[test_event_sink_wrap.js]
|
||||
[test_head_wrap.js]
|
||||
[test_httpcancel_wrap.js]
|
||||
|
|
|
@ -52,6 +52,7 @@ ifdef MOZ_THUMB2 #{
|
|||
# work around it by telling gcc that the THUMB frame pointer is a
|
||||
# vanilla callee-save register.
|
||||
OS_CXXFLAGS += -fomit-frame-pointer
|
||||
MOZ_OPTIMIZE_FLAGS := $(filter-out -fno-omit-frame-pointer,$(MOZ_OPTIMIZE_FLAGS))
|
||||
endif #}
|
||||
|
||||
MODULE = handler
|
||||
|
|
Загрузка…
Ссылка в новой задаче