mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
chore: cherry-pick 56de3b2004, bbca3704f9 and f2b0985ac3 from chromium (#31920)
Co-authored-by: Electron Bot <electron@github.com>
This commit is contained in:
@@ -120,4 +120,7 @@ move_networkstateobserver_from_document_to_window.patch
|
||||
cherry-pick-8af66de55aad.patch
|
||||
cherry-pick-91dd4f79ab5b.patch
|
||||
cherry-pick-45f9dcf5021d.patch
|
||||
introduce_crossthreadcopier_skbitmap.patch
|
||||
allow_null_skbitmap_to_be_transferred_across_threads.patch
|
||||
use_weakptrs_for_the_threadediconloader_s_background_tasks.patch
|
||||
cherry-pick-a5f54612590d.patch
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yutaka Hirano <yhirano@chromium.org>
|
||||
Date: Tue, 9 Nov 2021 09:03:17 +0000
|
||||
Subject: Allow null SkBitmap to be transferred across threads
|
||||
|
||||
(cherry picked from commit dad0c0e5162bcc49b8f60354d3bca92224d8381b)
|
||||
|
||||
Bug: 1241091
|
||||
Change-Id: Ie96932c14c8884d6d3eafa76dab5043e7aa31888
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3251815
|
||||
Reviewed-by: Florin Malita <fmalita@chromium.org>
|
||||
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#936861}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3268018
|
||||
Auto-Submit: Yutaka Hirano <yhirano@chromium.org>
|
||||
Reviewed-by: Kentaro Hara <haraken@chromium.org>
|
||||
Commit-Queue: Kentaro Hara <haraken@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4664@{#893}
|
||||
Cr-Branched-From: 24dc4ee75e01a29d390d43c9c264372a169273a7-refs/heads/main@{#929512}
|
||||
|
||||
diff --git a/third_party/blink/renderer/platform/graphics/skia/skia_utils.h b/third_party/blink/renderer/platform/graphics/skia/skia_utils.h
|
||||
index 3bd49cac3f5dfcad0fcc1140fcf876fe37558930..c037b85210bf2dedeb8478cf918633ad94885048 100644
|
||||
--- a/third_party/blink/renderer/platform/graphics/skia/skia_utils.h
|
||||
+++ b/third_party/blink/renderer/platform/graphics/skia/skia_utils.h
|
||||
@@ -209,11 +209,13 @@ struct CrossThreadCopier<SkBitmap> {
|
||||
|
||||
using Type = SkBitmap;
|
||||
static SkBitmap Copy(const SkBitmap& bitmap) {
|
||||
- CHECK(bitmap.isImmutable()) << "Only immutable bitmaps can be transferred.";
|
||||
+ CHECK(bitmap.isImmutable() || bitmap.isNull())
|
||||
+ << "Only immutable bitmaps can be transferred.";
|
||||
return bitmap;
|
||||
}
|
||||
static SkBitmap Copy(SkBitmap&& bitmap) {
|
||||
- CHECK(bitmap.isImmutable()) << "Only immutable bitmaps can be transferred.";
|
||||
+ CHECK(bitmap.isImmutable() || bitmap.isNull())
|
||||
+ << "Only immutable bitmaps can be transferred.";
|
||||
return std::move(bitmap);
|
||||
}
|
||||
};
|
||||
62
patches/chromium/introduce_crossthreadcopier_skbitmap.patch
Normal file
62
patches/chromium/introduce_crossthreadcopier_skbitmap.patch
Normal file
@@ -0,0 +1,62 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yutaka Hirano <yhirano@chromium.org>
|
||||
Date: Tue, 9 Nov 2021 05:02:50 +0000
|
||||
Subject: Introduce CrossThreadCopier<SkBitmap>
|
||||
|
||||
Allow immutable SkBitmap to be transferred across threads.
|
||||
|
||||
(cherry picked from commit fd794ad56432870e462aab9d62e4f059169c1a5f)
|
||||
|
||||
Bug: 1241091
|
||||
Change-Id: Ia771893245ef2fc1acb05cd4906d64bf2dd406fe
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3244824
|
||||
Reviewed-by: danakj <danakj@chromium.org>
|
||||
Reviewed-by: Florin Malita <fmalita@chromium.org>
|
||||
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#936435}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3267808
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Reviewed-by: Kentaro Hara <haraken@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4664@{#887}
|
||||
Cr-Branched-From: 24dc4ee75e01a29d390d43c9c264372a169273a7-refs/heads/main@{#929512}
|
||||
|
||||
diff --git a/third_party/blink/renderer/platform/graphics/skia/skia_utils.h b/third_party/blink/renderer/platform/graphics/skia/skia_utils.h
|
||||
index 997893fef2caee37b65e8ca6dfe9ca7b81302f98..3bd49cac3f5dfcad0fcc1140fcf876fe37558930 100644
|
||||
--- a/third_party/blink/renderer/platform/graphics/skia/skia_utils.h
|
||||
+++ b/third_party/blink/renderer/platform/graphics/skia/skia_utils.h
|
||||
@@ -39,7 +39,9 @@
|
||||
#include "third_party/blink/renderer/platform/graphics/image.h"
|
||||
#include "third_party/blink/renderer/platform/platform_export.h"
|
||||
#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
|
||||
+#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
|
||||
+#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "third_party/skia/include/core/SkCanvas.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
#include "third_party/skia/include/core/SkData.h"
|
||||
@@ -197,4 +199,25 @@ PLATFORM_EXPORT sk_sp<SkData> TryAllocateSkData(size_t size);
|
||||
|
||||
} // namespace blink
|
||||
|
||||
+namespace WTF {
|
||||
+
|
||||
+// We define CrossThreadCopier<SKBitMap> here because we cannot include skia
|
||||
+// headers in platform/wtf.
|
||||
+template <>
|
||||
+struct CrossThreadCopier<SkBitmap> {
|
||||
+ STATIC_ONLY(CrossThreadCopier);
|
||||
+
|
||||
+ using Type = SkBitmap;
|
||||
+ static SkBitmap Copy(const SkBitmap& bitmap) {
|
||||
+ CHECK(bitmap.isImmutable()) << "Only immutable bitmaps can be transferred.";
|
||||
+ return bitmap;
|
||||
+ }
|
||||
+ static SkBitmap Copy(SkBitmap&& bitmap) {
|
||||
+ CHECK(bitmap.isImmutable()) << "Only immutable bitmaps can be transferred.";
|
||||
+ return std::move(bitmap);
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+} // namespace WTF
|
||||
+
|
||||
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_SKIA_UTILS_H_
|
||||
@@ -0,0 +1,291 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Rayan Kanso <rayankans@google.com>
|
||||
Date: Tue, 9 Nov 2021 14:10:59 +0000
|
||||
Subject: Use WeakPtrs for the ThreadedIconLoader's background tasks.
|
||||
|
||||
(cherry picked from commit f0375e38d259b3651fd9f305ca2e723ca9c02c64)
|
||||
|
||||
Bug: 1241091
|
||||
Change-Id: I35b9cf705f1c5ffa2a719e47aec7b0f7d98ddc6b
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3222803
|
||||
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
|
||||
Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
|
||||
Reviewed-by: Kentaro Hara <haraken@chromium.org>
|
||||
Commit-Queue: Kentaro Hara <haraken@chromium.org>
|
||||
Auto-Submit: Rayan Kanso <rayankans@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#937427}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3269570
|
||||
Auto-Submit: Yutaka Hirano <yhirano@chromium.org>
|
||||
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4664@{#897}
|
||||
Cr-Branched-From: 24dc4ee75e01a29d390d43c9c264372a169273a7-refs/heads/main@{#929512}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/loader/threaded_icon_loader.cc b/third_party/blink/renderer/core/loader/threaded_icon_loader.cc
|
||||
index aba93c594fc85d6f82ff5f2ebf339e46d03a7111..3c15a85a2788c7faca3f39273fe3e77e456a1bad 100644
|
||||
--- a/third_party/blink/renderer/core/loader/threaded_icon_loader.cc
|
||||
+++ b/third_party/blink/renderer/core/loader/threaded_icon_loader.cc
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "skia/ext/image_operations.h"
|
||||
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
|
||||
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
|
||||
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
|
||||
#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
|
||||
#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
|
||||
#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
|
||||
@@ -24,6 +25,80 @@
|
||||
|
||||
namespace blink {
|
||||
|
||||
+namespace {
|
||||
+
|
||||
+void DecodeAndResizeImage(
|
||||
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
|
||||
+ scoped_refptr<SegmentReader> data,
|
||||
+ gfx::Size resize_dimensions,
|
||||
+ CrossThreadOnceFunction<void(SkBitmap, double)> done_callback) {
|
||||
+ auto notify_complete = [&](SkBitmap icon, double resize_scale) {
|
||||
+ // This is needed so it can be moved cross-thread.
|
||||
+ icon.setImmutable();
|
||||
+ PostCrossThreadTask(*task_runner, FROM_HERE,
|
||||
+ CrossThreadBindOnce(std::move(done_callback),
|
||||
+ std::move(icon), resize_scale));
|
||||
+ };
|
||||
+
|
||||
+ std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
|
||||
+ std::move(data), /* data_complete= */ true,
|
||||
+ ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth,
|
||||
+ ColorBehavior::TransformToSRGB());
|
||||
+
|
||||
+ if (!decoder) {
|
||||
+ notify_complete(SkBitmap(), -1.0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ImageFrame* image_frame = decoder->DecodeFrameBufferAtIndex(0);
|
||||
+
|
||||
+ if (!image_frame) {
|
||||
+ notify_complete(SkBitmap(), -1.0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ SkBitmap decoded_icon = image_frame->Bitmap();
|
||||
+ if (resize_dimensions.IsEmpty()) {
|
||||
+ notify_complete(std::move(decoded_icon), 1.0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // If the icon is larger than |resize_dimensions| permits, we need to
|
||||
+ // resize it as well. This can be done synchronously given that we're on a
|
||||
+ // background thread already.
|
||||
+ double scale = std::min(
|
||||
+ static_cast<double>(resize_dimensions.width()) / decoded_icon.width(),
|
||||
+ static_cast<double>(resize_dimensions.height()) / decoded_icon.height());
|
||||
+
|
||||
+ if (scale >= 1.0) {
|
||||
+ notify_complete(std::move(decoded_icon), 1.0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ int resized_width =
|
||||
+ base::ClampToRange(static_cast<int>(scale * decoded_icon.width()), 1,
|
||||
+ resize_dimensions.width());
|
||||
+ int resized_height =
|
||||
+ base::ClampToRange(static_cast<int>(scale * decoded_icon.height()), 1,
|
||||
+ resize_dimensions.height());
|
||||
+
|
||||
+ // Use the RESIZE_GOOD quality allowing the implementation to pick an
|
||||
+ // appropriate method for the resize. Can be increased to RESIZE_BETTER
|
||||
+ // or RESIZE_BEST if the quality looks poor.
|
||||
+ SkBitmap resized_icon = skia::ImageOperations::Resize(
|
||||
+ decoded_icon, skia::ImageOperations::RESIZE_GOOD, resized_width,
|
||||
+ resized_height);
|
||||
+
|
||||
+ if (resized_icon.isNull()) {
|
||||
+ notify_complete(std::move(decoded_icon), 1.0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ notify_complete(std::move(resized_icon), scale);
|
||||
+}
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
void ThreadedIconLoader::Start(
|
||||
ExecutionContext* execution_context,
|
||||
const ResourceRequestHead& resource_request,
|
||||
@@ -83,87 +158,18 @@ void ThreadedIconLoader::DidFinishLoading(uint64_t resource_identifier) {
|
||||
worker_pool::PostTask(
|
||||
FROM_HERE,
|
||||
CrossThreadBindOnce(
|
||||
- &ThreadedIconLoader::DecodeAndResizeImageOnBackgroundThread,
|
||||
- WrapCrossThreadPersistent(this), std::move(task_runner),
|
||||
- SegmentReader::CreateFromSharedBuffer(std::move(data_))));
|
||||
-}
|
||||
-
|
||||
-void ThreadedIconLoader::DecodeAndResizeImageOnBackgroundThread(
|
||||
- scoped_refptr<base::SingleThreadTaskRunner> task_runner,
|
||||
- scoped_refptr<SegmentReader> data) {
|
||||
- DCHECK(task_runner);
|
||||
- DCHECK(data);
|
||||
-
|
||||
- auto notify_complete = [&](double refactor_scale) {
|
||||
- PostCrossThreadTask(
|
||||
- *task_runner, FROM_HERE,
|
||||
- CrossThreadBindOnce(&ThreadedIconLoader::OnBackgroundTaskComplete,
|
||||
- WrapCrossThreadPersistent(this), refactor_scale));
|
||||
- };
|
||||
-
|
||||
- std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
|
||||
- std::move(data), /* data_complete= */ true,
|
||||
- ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth,
|
||||
- ColorBehavior::TransformToSRGB());
|
||||
-
|
||||
- if (!decoder) {
|
||||
- notify_complete(-1.0);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- ImageFrame* image_frame = decoder->DecodeFrameBufferAtIndex(0);
|
||||
-
|
||||
- if (!image_frame) {
|
||||
- notify_complete(-1.0);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- decoded_icon_ = image_frame->Bitmap();
|
||||
- if (!resize_dimensions_) {
|
||||
- notify_complete(1.0);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- // If the icon is larger than |resize_dimensions_| permits, we need to resize
|
||||
- // it as well. This can be done synchronously given that we're on a
|
||||
- // background thread already.
|
||||
- double scale = std::min(
|
||||
- static_cast<double>(resize_dimensions_->width()) / decoded_icon_.width(),
|
||||
- static_cast<double>(resize_dimensions_->height()) /
|
||||
- decoded_icon_.height());
|
||||
-
|
||||
- if (scale >= 1.0) {
|
||||
- notify_complete(1.0);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- int resized_width =
|
||||
- base::ClampToRange(static_cast<int>(scale * decoded_icon_.width()), 1,
|
||||
- resize_dimensions_->width());
|
||||
- int resized_height =
|
||||
- base::ClampToRange(static_cast<int>(scale * decoded_icon_.height()), 1,
|
||||
- resize_dimensions_->height());
|
||||
-
|
||||
- // Use the RESIZE_GOOD quality allowing the implementation to pick an
|
||||
- // appropriate method for the resize. Can be increased to RESIZE_BETTER
|
||||
- // or RESIZE_BEST if the quality looks poor.
|
||||
- SkBitmap resized_icon = skia::ImageOperations::Resize(
|
||||
- decoded_icon_, skia::ImageOperations::RESIZE_GOOD, resized_width,
|
||||
- resized_height);
|
||||
-
|
||||
- if (resized_icon.isNull()) {
|
||||
- notify_complete(1.0);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- decoded_icon_ = std::move(resized_icon);
|
||||
- notify_complete(scale);
|
||||
+ &DecodeAndResizeImage, std::move(task_runner),
|
||||
+ SegmentReader::CreateFromSharedBuffer(std::move(data_)),
|
||||
+ resize_dimensions_ ? *resize_dimensions_ : gfx::Size(),
|
||||
+ CrossThreadBindOnce(&ThreadedIconLoader::OnBackgroundTaskComplete,
|
||||
+ WrapCrossThreadWeakPersistent(this))));
|
||||
}
|
||||
|
||||
-void ThreadedIconLoader::OnBackgroundTaskComplete(double resize_scale) {
|
||||
+void ThreadedIconLoader::OnBackgroundTaskComplete(SkBitmap icon,
|
||||
+ double resize_scale) {
|
||||
if (stopped_)
|
||||
return;
|
||||
- std::move(icon_callback_).Run(std::move(decoded_icon_), resize_scale);
|
||||
+ std::move(icon_callback_).Run(std::move(icon), resize_scale);
|
||||
}
|
||||
|
||||
void ThreadedIconLoader::DidFail(uint64_t, const ResourceError& error) {
|
||||
diff --git a/third_party/blink/renderer/core/loader/threaded_icon_loader.h b/third_party/blink/renderer/core/loader/threaded_icon_loader.h
|
||||
index c1a9bb22b21ef1623c0aefc83ceeafe8e1e0a791..63a293b280678b4f0cc480616474b8cb99469eb7 100644
|
||||
--- a/third_party/blink/renderer/core/loader/threaded_icon_loader.h
|
||||
+++ b/third_party/blink/renderer/core/loader/threaded_icon_loader.h
|
||||
@@ -18,7 +18,6 @@
|
||||
namespace blink {
|
||||
|
||||
class ResourceRequestHead;
|
||||
-class SegmentReader;
|
||||
|
||||
// Utility class for loading, decoding, and potentially rescaling an icon on a
|
||||
// background thread. Note that icons are only downscaled and never upscaled.
|
||||
@@ -52,11 +51,7 @@ class CORE_EXPORT ThreadedIconLoader final
|
||||
void Trace(Visitor* visitor) const override;
|
||||
|
||||
private:
|
||||
- void DecodeAndResizeImageOnBackgroundThread(
|
||||
- scoped_refptr<base::SingleThreadTaskRunner> task_runner,
|
||||
- scoped_refptr<SegmentReader> data);
|
||||
-
|
||||
- void OnBackgroundTaskComplete(double resize_scale);
|
||||
+ void OnBackgroundTaskComplete(SkBitmap icon, double resize_scale);
|
||||
|
||||
Member<ThreadableLoader> threadable_loader_;
|
||||
|
||||
@@ -64,9 +59,7 @@ class CORE_EXPORT ThreadedIconLoader final
|
||||
// of the image data starts.
|
||||
scoped_refptr<SharedBuffer> data_;
|
||||
|
||||
- // Accessed from main thread and background thread.
|
||||
absl::optional<gfx::Size> resize_dimensions_;
|
||||
- SkBitmap decoded_icon_;
|
||||
|
||||
IconCallback icon_callback_;
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/content_index/content_index_icon_loader.cc b/third_party/blink/renderer/modules/content_index/content_index_icon_loader.cc
|
||||
index 79f4224fd96c46d7a6e1caabb44a509443b4bbff..3b5f52a9beb9c86d712332a4605f1b46c40f7682 100644
|
||||
--- a/third_party/blink/renderer/modules/content_index/content_index_icon_loader.cc
|
||||
+++ b/third_party/blink/renderer/modules/content_index/content_index_icon_loader.cc
|
||||
@@ -26,6 +26,7 @@ constexpr base::TimeDelta kIconFetchTimeout = base::TimeDelta::FromSeconds(30);
|
||||
void FetchIcon(ExecutionContext* execution_context,
|
||||
const KURL& icon_url,
|
||||
const gfx::Size& icon_size,
|
||||
+ ThreadedIconLoader* threaded_icon_loader,
|
||||
ThreadedIconLoader::IconCallback callback) {
|
||||
ResourceRequest resource_request(icon_url);
|
||||
resource_request.SetRequestContext(mojom::blink::RequestContextType::IMAGE);
|
||||
@@ -34,7 +35,6 @@ void FetchIcon(ExecutionContext* execution_context,
|
||||
resource_request.SetPriority(ResourceLoadPriority::kMedium);
|
||||
resource_request.SetTimeoutInterval(kIconFetchTimeout);
|
||||
|
||||
- auto* threaded_icon_loader = MakeGarbageCollected<ThreadedIconLoader>();
|
||||
threaded_icon_loader->Start(execution_context, resource_request, icon_size,
|
||||
std::move(callback));
|
||||
}
|
||||
@@ -100,16 +100,21 @@ void ContentIndexIconLoader::Start(
|
||||
if (icon_url.IsEmpty())
|
||||
icon_url = KURL(image_resources[0].src);
|
||||
|
||||
+ auto* threaded_icon_loader = MakeGarbageCollected<ThreadedIconLoader>();
|
||||
// |icons_ptr| is safe to use since it is owned by |barrier_closure|.
|
||||
FetchIcon(
|
||||
- execution_context, icon_url, icon_size,
|
||||
+ execution_context, icon_url, icon_size, threaded_icon_loader,
|
||||
WTF::Bind(
|
||||
[](base::OnceClosure done_closure, Vector<SkBitmap>* icons_ptr,
|
||||
- SkBitmap icon, double resize_scale) {
|
||||
+ ThreadedIconLoader* icon_loader, SkBitmap icon,
|
||||
+ double resize_scale) {
|
||||
icons_ptr->push_back(std::move(icon));
|
||||
std::move(done_closure).Run();
|
||||
},
|
||||
- barrier_closure, WTF::Unretained(icons_ptr)));
|
||||
+ barrier_closure, WTF::Unretained(icons_ptr),
|
||||
+ // Pass |threaded_icon_loader| to the callback to make sure it
|
||||
+ // doesn't get destroyed.
|
||||
+ WrapPersistent(threaded_icon_loader)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user