OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/image_decoder.h" | 5 #include "chrome/browser/image_decoder.h" |
6 | 6 |
7 #if defined(OS_POSIX) | 7 #include "chrome/grit/generated_resources.h" |
8 #include <sys/types.h> | |
9 #include <signal.h> | |
10 #endif | |
11 | |
12 #include "chrome/test/base/in_process_browser_test.h" | 8 #include "chrome/test/base/in_process_browser_test.h" |
13 #include "content/public/browser/browser_child_process_observer.h" | 9 #include "content/public/browser/browser_child_process_observer.h" |
14 #include "content/public/browser/browser_thread.h" | 10 #include "content/public/browser/browser_thread.h" |
15 #include "content/public/browser/child_process_data.h" | 11 #include "content/public/browser/child_process_data.h" |
16 #include "content/public/test/test_utils.h" | 12 #include "content/public/test/test_utils.h" |
| 13 #include "ui/base/l10n/l10n_util.h" |
17 | 14 |
18 using content::BrowserThread; | 15 using content::BrowserThread; |
19 | 16 |
20 namespace { | 17 namespace { |
21 | 18 |
| 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); |
| 29 } |
| 30 |
22 class TestImageRequest : public ImageDecoder::ImageRequest { | 31 class TestImageRequest : public ImageDecoder::ImageRequest { |
23 public: | 32 public: |
24 explicit TestImageRequest(const base::Closure& quit_closure) | 33 explicit TestImageRequest(const base::Closure& quit_closure) |
25 : quit_closure_(quit_closure), | 34 : decode_succeeded_(false), |
| 35 quit_closure_(quit_closure), |
26 quit_called_(false) { | 36 quit_called_(false) { |
27 } | 37 } |
28 | 38 |
29 ~TestImageRequest() override { | 39 ~TestImageRequest() override { |
30 if (!quit_called_) { | 40 if (!quit_called_) { |
31 quit_closure_.Run(); | 41 quit_closure_.Run(); |
32 } | 42 } |
33 } | 43 } |
34 | 44 |
| 45 bool decode_succeeded() const { return decode_succeeded_; } |
| 46 |
35 private: | 47 private: |
36 void OnImageDecoded(const SkBitmap& decoded_image) override { | 48 void OnImageDecoded(const SkBitmap& decoded_image) override { |
| 49 decode_succeeded_ = true; |
37 Quit(); | 50 Quit(); |
38 } | 51 } |
39 | 52 |
40 void OnDecodeImageFailed() override { | 53 void OnDecodeImageFailed() override { |
41 Quit(); | 54 Quit(); |
42 } | 55 } |
43 | 56 |
44 void Quit() { | 57 void Quit() { |
45 EXPECT_FALSE(quit_called_); | 58 EXPECT_FALSE(quit_called_); |
46 quit_called_ = true; | 59 quit_called_ = true; |
47 quit_closure_.Run(); | 60 quit_closure_.Run(); |
48 } | 61 } |
49 | 62 |
| 63 bool decode_succeeded_; |
| 64 |
50 base::Closure quit_closure_; | 65 base::Closure quit_closure_; |
51 bool quit_called_; | 66 bool quit_called_; |
52 | 67 |
53 DISALLOW_COPY_AND_ASSIGN(TestImageRequest); | 68 DISALLOW_COPY_AND_ASSIGN(TestImageRequest); |
54 }; | 69 }; |
55 | 70 |
56 class KillProcessObserver : public content::BrowserChildProcessObserver { | 71 class KillProcessObserver : public content::BrowserChildProcessObserver { |
57 public: | 72 public: |
58 KillProcessObserver() { | 73 KillProcessObserver() |
| 74 : did_kill_(false), |
| 75 utility_process_name_( |
| 76 l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME)) { |
59 Add(this); | 77 Add(this); |
60 } | 78 } |
61 | 79 |
62 ~KillProcessObserver() override { | 80 ~KillProcessObserver() override { |
63 Remove(this); | 81 Remove(this); |
64 } | 82 } |
65 | 83 |
| 84 bool did_kill() const { return did_kill_; } |
| 85 |
66 private: | 86 private: |
67 void BrowserChildProcessHostConnected( | 87 void BrowserChildProcessHostConnected( |
68 const content::ChildProcessData& data) override { | 88 const content::ChildProcessData& data) override { |
| 89 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 90 if (data.handle == base::kNullProcessHandle || |
| 91 data.name != utility_process_name_) { |
| 92 return; |
| 93 } |
| 94 |
| 95 ASSERT_FALSE(did_kill_); |
69 base::ProcessHandle handle = data.handle; | 96 base::ProcessHandle handle = data.handle; |
70 | 97 |
71 if (handle == base::kNullProcessHandle) | |
72 return; | |
73 | |
74 #if defined(OS_WIN) | 98 #if defined(OS_WIN) |
75 // On windows, duplicate the process handle since base::Process closes it on | 99 // On windows, duplicate the process handle since base::Process closes it on |
76 // destruction. | 100 // destruction. |
77 base::ProcessHandle out_handle; | 101 base::ProcessHandle out_handle; |
78 if (!::DuplicateHandle(GetCurrentProcess(), handle, | 102 if (!::DuplicateHandle(GetCurrentProcess(), handle, |
79 GetCurrentProcess(), &out_handle, | 103 GetCurrentProcess(), &out_handle, |
80 0, FALSE, DUPLICATE_SAME_ACCESS)) | 104 0, FALSE, DUPLICATE_SAME_ACCESS)) { |
81 return; | 105 return; |
| 106 } |
82 handle = out_handle; | 107 handle = out_handle; |
83 #endif | 108 #endif |
84 | 109 |
85 EXPECT_TRUE(base::Process(handle).Terminate(0, true)); | 110 // Use a non-zero exit code so it counts as a crash. |
86 } | 111 EXPECT_TRUE(base::Process(handle).Terminate(1, true)); |
87 }; | 112 did_kill_ = true; |
88 | |
89 #if defined(OS_POSIX) | |
90 class FreezeProcessObserver : public content::BrowserChildProcessObserver { | |
91 public: | |
92 FreezeProcessObserver() { | |
93 Add(this); | |
94 } | 113 } |
95 | 114 |
96 ~FreezeProcessObserver() override { | 115 bool did_kill_; |
97 Remove(this); | 116 const base::string16 utility_process_name_; |
98 } | |
99 | 117 |
100 private: | 118 DISALLOW_COPY_AND_ASSIGN(KillProcessObserver); |
101 void BrowserChildProcessHostConnected( | |
102 const content::ChildProcessData& data) override { | |
103 if (data.handle != base::kNullProcessHandle) | |
104 EXPECT_EQ(0, kill(data.handle, SIGSTOP)); | |
105 } | |
106 }; | 119 }; |
107 #endif // defined(OS_POSIX) | |
108 | 120 |
109 } // namespace | 121 } // namespace |
110 | 122 |
111 class ImageDecoderBrowserTest : public InProcessBrowserTest { | 123 class ImageDecoderBrowserTest : public InProcessBrowserTest { |
112 }; | 124 }; |
113 | 125 |
114 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, Basic) { | 126 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, Basic) { |
115 scoped_refptr<content::MessageLoopRunner> runner = | 127 scoped_refptr<content::MessageLoopRunner> runner = |
116 new content::MessageLoopRunner; | 128 new content::MessageLoopRunner; |
117 TestImageRequest test_request(runner->QuitClosure()); | 129 TestImageRequest test_request(runner->QuitClosure()); |
118 ImageDecoder::Start(&test_request, std::string()); | 130 ImageDecoder::Start(&test_request, std::string()); |
119 runner->Run(); | 131 runner->Run(); |
| 132 EXPECT_FALSE(test_request.decode_succeeded()); |
| 133 } |
| 134 |
| 135 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, BasicDecode) { |
| 136 scoped_refptr<content::MessageLoopRunner> runner = |
| 137 new content::MessageLoopRunner; |
| 138 TestImageRequest test_request(runner->QuitClosure()); |
| 139 ImageDecoder::Start(&test_request, GetValidPngString()); |
| 140 runner->Run(); |
| 141 EXPECT_TRUE(test_request.decode_succeeded()); |
120 } | 142 } |
121 | 143 |
122 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndDestroy) { | 144 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndDestroy) { |
123 scoped_refptr<content::MessageLoopRunner> runner = | 145 scoped_refptr<content::MessageLoopRunner> runner = |
124 new content::MessageLoopRunner; | 146 new content::MessageLoopRunner; |
125 scoped_ptr<TestImageRequest> test_request( | 147 scoped_ptr<TestImageRequest> test_request( |
126 new TestImageRequest(runner->QuitClosure())); | 148 new TestImageRequest(runner->QuitClosure())); |
127 ImageDecoder::Start(test_request.get(), std::string()); | 149 ImageDecoder::Start(test_request.get(), std::string()); |
128 test_request.reset(); | 150 test_request.reset(); |
129 runner->Run(); | 151 runner->Run(); |
130 } | 152 } |
131 | 153 |
| 154 // Killing the utility process counts as a crash. Thus the request fails. |
| 155 // If ImageDecoder did not handle the crash properly, the request never finishes |
| 156 // and this test would hang. |
| 157 // Note: This test is inherently racy because KillProcessObserver lives on the |
| 158 // UI thread but ImageDecoder does its work mainly on the IO thread. So the test |
| 159 // checks for both possible valid outcomes. |
132 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndKillProcess) { | 160 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndKillProcess) { |
133 KillProcessObserver observer; | 161 KillProcessObserver observer; |
134 scoped_refptr<content::MessageLoopRunner> runner = | 162 scoped_refptr<content::MessageLoopRunner> runner = |
135 new content::MessageLoopRunner; | 163 new content::MessageLoopRunner; |
136 scoped_ptr<TestImageRequest> test_request( | 164 TestImageRequest test_request(runner->QuitClosure()); |
137 new TestImageRequest(runner->QuitClosure())); | 165 ImageDecoder::Start(&test_request, GetValidPngString()); |
138 ImageDecoder::Start(test_request.get(), std::string()); | |
139 runner->Run(); | 166 runner->Run(); |
| 167 if (!test_request.decode_succeeded()) { |
| 168 // The UI thread won the race. Make sure the utility process did get killed. |
| 169 EXPECT_TRUE(observer.did_kill()); |
| 170 } |
| 171 // Else the IO thread won the race and the image got decoded. Oh well. |
140 } | 172 } |
141 | |
142 #if defined(OS_POSIX) | |
143 IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndFreezeProcess) { | |
144 FreezeProcessObserver observer; | |
145 scoped_refptr<content::MessageLoopRunner> runner = | |
146 new content::MessageLoopRunner; | |
147 scoped_ptr<TestImageRequest> test_request( | |
148 new TestImageRequest(runner->QuitClosure())); | |
149 ImageDecoder::Start(test_request.get(), std::string()); | |
150 runner->Run(); | |
151 } | |
152 #endif // defined(OS_POSIX) | |
OLD | NEW |