1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/image_decoder.h"
7 #include "chrome/grit/generated_resources.h"
8 #include "chrome/test/base/in_process_browser_test.h"
9 #include "content/public/browser/browser_child_process_observer.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/child_process_data.h"
12 #include "content/public/test/test_utils.h"
13 #include "ui/base/l10n/l10n_util.h"
15 using content::BrowserThread
;
19 std::string
GetValidPngString() {
20 // 1x1 PNG. Does not get much smaller than this.
21 static const char kPngData
[] =
22 "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
23 "\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53"
24 "\xde\x00\x00\x00\x0c\x49\x44\x41\x54\x08\xd7\x63\xf8\xff\xff\x3f"
25 "\x00\x05\xfe\x02\xfe\xdc\xcc\x59\xe7\x00\x00\x00\x00\x49\x45\x4e"
26 "\x44\xae\x42\x60\x82";
27 // Need to specify the buffer size because it contains NULs.
28 return std::string(kPngData
, sizeof(kPngData
) - 1);
31 class TestImageRequest
: public ImageDecoder::ImageRequest
{
33 explicit TestImageRequest(const base::Closure
& quit_closure
)
34 : decode_succeeded_(false),
35 quit_closure_(quit_closure
),
39 ~TestImageRequest() override
{
45 bool decode_succeeded() const { return decode_succeeded_
; }
48 void OnImageDecoded(const SkBitmap
& decoded_image
) override
{
49 decode_succeeded_
= true;
53 void OnDecodeImageFailed() override
{
58 EXPECT_FALSE(quit_called_
);
63 bool decode_succeeded_
;
65 base::Closure quit_closure_
;
68 DISALLOW_COPY_AND_ASSIGN(TestImageRequest
);
71 class KillProcessObserver
: public content::BrowserChildProcessObserver
{
75 utility_process_name_(
76 l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME
)) {
80 ~KillProcessObserver() override
{
84 bool did_kill() const { return did_kill_
; }
87 void BrowserChildProcessHostConnected(
88 const content::ChildProcessData
& data
) override
{
89 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
90 if (data
.handle
== base::kNullProcessHandle
||
91 data
.name
!= utility_process_name_
) {
95 ASSERT_FALSE(did_kill_
);
96 base::ProcessHandle handle
= data
.handle
;
99 // On windows, duplicate the process handle since base::Process closes it on
101 base::ProcessHandle out_handle
;
102 if (!::DuplicateHandle(GetCurrentProcess(), handle
,
103 GetCurrentProcess(), &out_handle
,
104 0, FALSE
, DUPLICATE_SAME_ACCESS
)) {
110 // Use a non-zero exit code so it counts as a crash.
111 // Don't wait for the process after sending the termination signal
112 // (SIGTERM). According to POSIX, doing so causes the resulting zombie to be
113 // removed from the process table. However, Chromium treats an error on
114 // |waitpid| (in this case, ECHILD) as a "normal" termination and doesn't
115 // invoke the process host delegate's OnProcessCrashed().
116 EXPECT_TRUE(base::Process(handle
).Terminate(1, false));
121 const base::string16 utility_process_name_
;
123 DISALLOW_COPY_AND_ASSIGN(KillProcessObserver
);
128 class ImageDecoderBrowserTest
: public InProcessBrowserTest
{
131 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest
, Basic
) {
132 scoped_refptr
<content::MessageLoopRunner
> runner
=
133 new content::MessageLoopRunner
;
134 TestImageRequest
test_request(runner
->QuitClosure());
135 ImageDecoder::Start(&test_request
, std::string());
137 EXPECT_FALSE(test_request
.decode_succeeded());
140 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest
, BasicDecode
) {
141 scoped_refptr
<content::MessageLoopRunner
> runner
=
142 new content::MessageLoopRunner
;
143 TestImageRequest
test_request(runner
->QuitClosure());
144 ImageDecoder::Start(&test_request
, GetValidPngString());
146 EXPECT_TRUE(test_request
.decode_succeeded());
149 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest
, StartAndDestroy
) {
150 scoped_refptr
<content::MessageLoopRunner
> runner
=
151 new content::MessageLoopRunner
;
152 scoped_ptr
<TestImageRequest
> test_request(
153 new TestImageRequest(runner
->QuitClosure()));
154 ImageDecoder::Start(test_request
.get(), std::string());
155 test_request
.reset();
159 // Killing the utility process counts as a crash. Thus the request fails.
160 // If ImageDecoder did not handle the crash properly, the request never finishes
161 // and this test would hang.
162 // Note: This test is inherently racy because KillProcessObserver lives on the
163 // UI thread but ImageDecoder does its work mainly on the IO thread. So the test
164 // checks for both possible valid outcomes.
165 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest
, StartAndKillProcess
) {
166 KillProcessObserver observer
;
167 scoped_refptr
<content::MessageLoopRunner
> runner
=
168 new content::MessageLoopRunner
;
169 TestImageRequest
test_request(runner
->QuitClosure());
170 ImageDecoder::Start(&test_request
, GetValidPngString());
172 if (!test_request
.decode_succeeded()) {
173 // The UI thread won the race. Make sure the utility process did get killed.
174 EXPECT_TRUE(observer
.did_kill());
176 // Else the IO thread won the race and the image got decoded. Oh well.