| 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 |