|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Lei Zhang <thestig@chromium.org> |
| 3 | +Date: Tue, 10 Aug 2021 21:38:36 +0000 |
| 4 | +Subject: Do more class validity checks in PrintViewManagerBase. |
| 5 | + |
| 6 | +PrintViewManagerBase runs a nested loop. In some situations, |
| 7 | +PrintViewManagerBase and related classes like PrintViewManager and |
| 8 | +PrintPreviewHandler can get deleted while the nested loop is running. |
| 9 | +When this happens, the nested loop exists to a PrintViewManagerBase |
| 10 | +that is no longer valid. |
| 11 | + |
| 12 | +Use base::WeakPtrs liberally to check for this condition and exit |
| 13 | +safely. |
| 14 | + |
| 15 | +(cherry picked from commit a2cb1fb333d2faacb2fe1380f8d2621b5ee6af7e) |
| 16 | + |
| 17 | +Bug: 1231134 |
| 18 | +Change-Id: I21ec131574331ce973d22594c11e70088147e149 |
| 19 | +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3057880 |
| 20 | +Reviewed-by: Alan Screen <awscreen@chromium.org> |
| 21 | +Commit-Queue: Lei Zhang <thestig@chromium.org> |
| 22 | +Cr-Original-Commit-Position: refs/heads/master@{#906269} |
| 23 | +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3086110 |
| 24 | +Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> |
| 25 | +Cr-Commit-Position: refs/branch-heads/4515@{#2024} |
| 26 | +Cr-Branched-From: 488fc70865ddaa05324ac00a54a6eb783b4bc41c-refs/heads/master@{#885287} |
| 27 | + |
| 28 | +diff --git a/chrome/browser/printing/print_view_manager.cc b/chrome/browser/printing/print_view_manager.cc |
| 29 | +index b7a2fa5a10b2461e83d78fca63e3165d5eb1350f..9f11d5053ff19e4ccfdbe54e7f59e311da3c6e0a 100644 |
| 30 | +--- a/chrome/browser/printing/print_view_manager.cc |
| 31 | ++++ b/chrome/browser/printing/print_view_manager.cc |
| 32 | +@@ -91,7 +91,11 @@ bool PrintViewManager::PrintForSystemDialogNow( |
| 33 | + DCHECK(!on_print_dialog_shown_callback_); |
| 34 | + on_print_dialog_shown_callback_ = std::move(dialog_shown_callback); |
| 35 | + is_switching_to_system_dialog_ = true; |
| 36 | ++ |
| 37 | ++ auto weak_this = weak_factory_.GetWeakPtr(); |
| 38 | + DisconnectFromCurrentPrintJob(); |
| 39 | ++ if (!weak_this) |
| 40 | ++ return false; |
| 41 | + |
| 42 | + // Don't print / print preview crashed tabs. |
| 43 | + if (IsCrashed()) |
| 44 | +diff --git a/chrome/browser/printing/print_view_manager.h b/chrome/browser/printing/print_view_manager.h |
| 45 | +index 8770b7bc60cb28d70cde3af8303939bf7ac27892..16c13724c397fade0eb64f9b5ec26c7bf51570ac 100644 |
| 46 | +--- a/chrome/browser/printing/print_view_manager.h |
| 47 | ++++ b/chrome/browser/printing/print_view_manager.h |
| 48 | +@@ -130,6 +130,11 @@ class PrintViewManager : public PrintViewManagerBase, |
| 49 | + |
| 50 | + WEB_CONTENTS_USER_DATA_KEY_DECL(); |
| 51 | + |
| 52 | ++ // Keep this last so that all weak pointers will be invalidated at the |
| 53 | ++ // beginning of destruction. Note that PrintViewManagerBase has its own |
| 54 | ++ // base::WeakPtrFactory as well, but PrintViewManager should use this one. |
| 55 | ++ base::WeakPtrFactory<PrintViewManager> weak_factory_{this}; |
| 56 | ++ |
| 57 | + DISALLOW_COPY_AND_ASSIGN(PrintViewManager); |
| 58 | + }; |
| 59 | + |
| 60 | +diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc |
| 61 | +index 75908fc9978db020d752e7fc7211d1a075c11ffb..b896f69f3ec272001c5c6c0071bc23a3c9134d70 100644 |
| 62 | +--- a/chrome/browser/printing/print_view_manager_base.cc |
| 63 | ++++ b/chrome/browser/printing/print_view_manager_base.cc |
| 64 | +@@ -192,7 +192,10 @@ bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh, |
| 65 | + bool silent, |
| 66 | + base::Value settings, |
| 67 | + CompletionCallback callback) { |
| 68 | ++ auto weak_this = weak_ptr_factory_.GetWeakPtr(); |
| 69 | + DisconnectFromCurrentPrintJob(); |
| 70 | ++ if (!weak_this) |
| 71 | ++ return false; |
| 72 | + |
| 73 | + // Don't print / print preview crashed tabs. |
| 74 | + if (IsCrashed()) |
| 75 | +@@ -625,6 +628,8 @@ bool PrintViewManagerBase::RenderAllMissingPagesNow() { |
| 76 | + // or in DidPrintDocument(). The check is done in |
| 77 | + // ShouldQuitFromInnerMessageLoop(). |
| 78 | + // BLOCKS until all the pages are received. (Need to enable recursive task) |
| 79 | ++ // WARNING: Do not do any work after RunInnerMessageLoop() returns, as `this` |
| 80 | ++ // may have gone away. |
| 81 | + if (!RunInnerMessageLoop()) { |
| 82 | + // This function is always called from DisconnectFromCurrentPrintJob() so we |
| 83 | + // know that the job will be stopped/canceled in any case. |
| 84 | +@@ -651,8 +656,11 @@ bool PrintViewManagerBase::CreateNewPrintJob( |
| 85 | + DCHECK(query); |
| 86 | + |
| 87 | + if (callback_.is_null()) { |
| 88 | ++ auto weak_this = weak_ptr_factory_.GetWeakPtr(); |
| 89 | + // Disconnect the current |print_job_| only when calling window.print() |
| 90 | + DisconnectFromCurrentPrintJob(); |
| 91 | ++ if (!weak_this) |
| 92 | ++ return false; |
| 93 | + } |
| 94 | + |
| 95 | + // We can't print if there is no renderer. |
| 96 | +@@ -681,7 +689,10 @@ bool PrintViewManagerBase::CreateNewPrintJob( |
| 97 | + void PrintViewManagerBase::DisconnectFromCurrentPrintJob() { |
| 98 | + // Make sure all the necessary rendered page are done. Don't bother with the |
| 99 | + // return value. |
| 100 | ++ auto weak_this = weak_ptr_factory_.GetWeakPtr(); |
| 101 | + bool result = RenderAllMissingPagesNow(); |
| 102 | ++ if (!weak_this) |
| 103 | ++ return; |
| 104 | + |
| 105 | + // Verify that assertion. |
| 106 | + if (print_job_ && print_job_->document() && |
| 107 | +@@ -763,7 +774,10 @@ bool PrintViewManagerBase::RunInnerMessageLoop() { |
| 108 | + |
| 109 | + quit_inner_loop_ = run_loop.QuitClosure(); |
| 110 | + |
| 111 | ++ auto weak_this = weak_ptr_factory_.GetWeakPtr(); |
| 112 | + run_loop.Run(); |
| 113 | ++ if (!weak_this) |
| 114 | ++ return false; |
| 115 | + |
| 116 | + // If the inner-loop quit closure is still set then we timed out. |
| 117 | + bool success = !quit_inner_loop_; |
| 118 | +diff --git a/chrome/browser/printing/print_view_manager_base.h b/chrome/browser/printing/print_view_manager_base.h |
| 119 | +index 095d9dfcc3ee14b646b63c29e406434c1623704a..ed4c0ec98bb84753828d0649799077edc9796317 100644 |
| 120 | +--- a/chrome/browser/printing/print_view_manager_base.h |
| 121 | ++++ b/chrome/browser/printing/print_view_manager_base.h |
| 122 | +@@ -115,6 +115,8 @@ class PrintViewManagerBase : public content::NotificationObserver, |
| 123 | + |
| 124 | + // Makes sure the current print_job_ has all its data before continuing, and |
| 125 | + // disconnect from it. |
| 126 | ++ // WARNING: `this` may not be alive after DisconnectFromCurrentPrintJob() |
| 127 | ++ // returns. |
| 128 | + void DisconnectFromCurrentPrintJob(); |
| 129 | + |
| 130 | + // Manages the low-level talk to the printer. |
| 131 | +@@ -168,6 +170,7 @@ class PrintViewManagerBase : public content::NotificationObserver, |
| 132 | + // Requests the RenderView to render all the missing pages for the print job. |
| 133 | + // No-op if no print job is pending. Returns true if at least one page has |
| 134 | + // been requested to the renderer. |
| 135 | ++ // WARNING: `this` may not be alive after RenderAllMissingPagesNow() returns. |
| 136 | + bool RenderAllMissingPagesNow(); |
| 137 | + |
| 138 | + // Checks that synchronization is correct with |print_job_| based on |cookie|. |
| 139 | +@@ -201,6 +204,7 @@ class PrintViewManagerBase : public content::NotificationObserver, |
| 140 | + // while the blocking inner message loop is running. This is useful in cases |
| 141 | + // where the RenderView is about to be destroyed while a printing job isn't |
| 142 | + // finished. |
| 143 | ++ // WARNING: `this` may not be alive after RunInnerMessageLoop() returns. |
| 144 | + bool RunInnerMessageLoop(); |
| 145 | + |
| 146 | + // In the case of Scripted Printing, where the renderer is controlling the |
| 147 | +diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc |
| 148 | +index 2cea700c82790f696775ece3080662232326aabc..ab89b180e6f9586d6280d884f126fb46b278be04 100644 |
| 149 | +--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc |
| 150 | ++++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc |
| 151 | +@@ -864,9 +864,12 @@ void PrintPreviewHandler::HandleShowSystemDialog( |
| 152 | + if (!initiator) |
| 153 | + return; |
| 154 | + |
| 155 | ++ auto weak_this = weak_factory_.GetWeakPtr(); |
| 156 | + auto* print_view_manager = PrintViewManager::FromWebContents(initiator); |
| 157 | + print_view_manager->PrintForSystemDialogNow(base::BindOnce( |
| 158 | + &PrintPreviewHandler::ClosePreviewDialog, weak_factory_.GetWeakPtr())); |
| 159 | ++ if (!weak_this) |
| 160 | ++ return; |
| 161 | + |
| 162 | + // Cancel the pending preview request if exists. |
| 163 | + print_preview_ui()->OnCancelPendingPreviewRequest(); |