summaryrefslogtreecommitdiffstats
path: root/widget/IconLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/IconLoader.cpp')
-rw-r--r--widget/IconLoader.cpp181
1 files changed, 181 insertions, 0 deletions
diff --git a/widget/IconLoader.cpp b/widget/IconLoader.cpp
new file mode 100644
index 0000000000..6da82caa70
--- /dev/null
+++ b/widget/IconLoader.cpp
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/widget/IconLoader.h"
+#include "gfxPlatform.h"
+#include "imgIContainer.h"
+#include "imgLoader.h"
+#include "mozilla/dom/Document.h"
+#include "nsContentUtils.h"
+#include "nsIContent.h"
+
+using namespace mozilla;
+
+using mozilla::gfx::SourceSurface;
+
+namespace mozilla::widget {
+
+NS_IMPL_CYCLE_COLLECTION(mozilla::widget::IconLoader, mContent, mHelper)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(mozilla::widget::IconLoader)
+ NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::widget::IconLoader)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::widget::IconLoader)
+
+IconLoader::IconLoader(Helper* aHelper, nsINode* aContent,
+ const nsIntRect& aImageRegionRect)
+ : mContent(aContent),
+ mContentType(nsIContentPolicy::TYPE_INTERNAL_IMAGE),
+ mImageRegionRect(aImageRegionRect),
+ mLoadedIcon(false),
+ mHelper(aHelper) {}
+
+IconLoader::~IconLoader() { Destroy(); }
+
+void IconLoader::Destroy() {
+ if (mIconRequest) {
+ mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
+ mIconRequest = nullptr;
+ }
+ if (mHelper) {
+ mHelper = nullptr;
+ }
+}
+
+nsresult IconLoader::LoadIcon(nsIURI* aIconURI, bool aIsInternalIcon) {
+ if (mIconRequest) {
+ // Another icon request is already in flight. Kill it.
+ mIconRequest->Cancel(NS_BINDING_ABORTED);
+ mIconRequest = nullptr;
+ }
+
+ mLoadedIcon = false;
+
+ if (!mContent) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<mozilla::dom::Document> document = mContent->OwnerDoc();
+
+ nsCOMPtr<nsILoadGroup> loadGroup = document->GetDocumentLoadGroup();
+ if (!loadGroup) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<imgLoader> loader = nsContentUtils::GetImgLoaderForDocument(document);
+ if (!loader) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ if (aIsInternalIcon) {
+ rv = loader->LoadImage(
+ aIconURI, nullptr, nullptr, nullptr, 0, loadGroup, this, nullptr,
+ nullptr, nsIRequest::LOAD_NORMAL, nullptr, mContentType, u""_ns,
+ /* aUseUrgentStartForChannel */ false, /* aLinkPreload */ false,
+ getter_AddRefs(mIconRequest));
+ } else {
+ rv = loader->LoadImage(
+ aIconURI, nullptr, nullptr, mContent->NodePrincipal(), 0, loadGroup,
+ this, mContent, document, nsIRequest::LOAD_NORMAL, nullptr,
+ mContentType, u""_ns, /* aUseUrgentStartForChannel */ false,
+ /* aLinkPreload */ false, getter_AddRefs(mIconRequest));
+ }
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+//
+// imgINotificationObserver
+//
+
+void IconLoader::Notify(imgIRequest* aRequest, int32_t aType,
+ const nsIntRect* aData) {
+ if (aType == imgINotificationObserver::LOAD_COMPLETE) {
+ // Make sure the image loaded successfully.
+ uint32_t status = imgIRequest::STATUS_ERROR;
+ if (NS_FAILED(aRequest->GetImageStatus(&status)) ||
+ (status & imgIRequest::STATUS_ERROR)) {
+ mIconRequest->Cancel(NS_BINDING_ABORTED);
+ mIconRequest = nullptr;
+ return;
+ }
+
+ nsCOMPtr<imgIContainer> image;
+ aRequest->GetImage(getter_AddRefs(image));
+ MOZ_ASSERT(image);
+
+ // Ask the image to decode at its intrinsic size.
+ int32_t width = 0, height = 0;
+ image->GetWidth(&width);
+ image->GetHeight(&height);
+ image->RequestDecodeForSize(nsIntSize(width, height),
+ imgIContainer::FLAG_HIGH_QUALITY_SCALING);
+ }
+
+ if (aType == imgINotificationObserver::FRAME_COMPLETE) {
+ nsresult rv = OnFrameComplete(aRequest);
+
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<imgIContainer> image;
+ aRequest->GetImage(getter_AddRefs(image));
+ MOZ_ASSERT(image);
+
+ mHelper->OnComplete(image, mImageRegionRect);
+ return;
+ }
+
+ if (aType == imgINotificationObserver::DECODE_COMPLETE) {
+ if (mIconRequest && mIconRequest == aRequest) {
+ mIconRequest->Cancel(NS_BINDING_ABORTED);
+ mIconRequest = nullptr;
+ }
+ }
+}
+
+nsresult IconLoader::OnFrameComplete(imgIRequest* aRequest) {
+ if (aRequest != mIconRequest) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Only support one frame.
+ if (mLoadedIcon) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<imgIContainer> imageContainer;
+ aRequest->GetImage(getter_AddRefs(imageContainer));
+ if (!imageContainer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t origWidth = 0, origHeight = 0;
+ imageContainer->GetWidth(&origWidth);
+ imageContainer->GetHeight(&origHeight);
+
+ // If the image region is invalid, don't draw the image to almost match
+ // the behavior of other platforms.
+ if (!mImageRegionRect.IsEmpty() && (mImageRegionRect.XMost() > origWidth ||
+ mImageRegionRect.YMost() > origHeight)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mImageRegionRect.IsEmpty()) {
+ mImageRegionRect.SetRect(0, 0, origWidth, origHeight);
+ }
+
+ mLoadedIcon = true;
+
+ return NS_OK;
+}
+
+} // namespace mozilla::widget