1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "mozilla/widget/IconLoader.h"
7 #include "gfxPlatform.h"
8 #include "imgIContainer.h"
9 #include "imgLoader.h"
10 #include "imgRequestProxy.h"
11 #include "mozilla/dom/Document.h"
12 #include "nsContentUtils.h"
13 #include "nsIContent.h"
14 #include "nsIContentPolicy.h"
15 
16 using namespace mozilla;
17 
18 namespace mozilla::widget {
19 
NS_IMPL_ISUPPORTS(IconLoader,imgINotificationObserver)20 NS_IMPL_ISUPPORTS(IconLoader, imgINotificationObserver)
21 
22 IconLoader::IconLoader(Listener* aListener) : mListener(aListener) {}
23 
~IconLoader()24 IconLoader::~IconLoader() { Destroy(); }
25 
Destroy()26 void IconLoader::Destroy() {
27   if (mIconRequest) {
28     mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
29     mIconRequest = nullptr;
30   }
31   mListener = nullptr;
32 }
33 
LoadIcon(nsIURI * aIconURI,nsINode * aNode,bool aIsInternalIcon)34 nsresult IconLoader::LoadIcon(nsIURI* aIconURI, nsINode* aNode,
35                               bool aIsInternalIcon) {
36   if (mIconRequest) {
37     // Another icon request is already in flight.  Kill it.
38     mIconRequest->Cancel(NS_BINDING_ABORTED);
39     mIconRequest = nullptr;
40   }
41 
42   if (!aNode) {
43     return NS_ERROR_FAILURE;
44   }
45 
46   RefPtr<mozilla::dom::Document> document = aNode->OwnerDoc();
47 
48   nsCOMPtr<nsILoadGroup> loadGroup = document->GetDocumentLoadGroup();
49   if (!loadGroup) {
50     return NS_ERROR_FAILURE;
51   }
52 
53   RefPtr<imgLoader> loader = nsContentUtils::GetImgLoaderForDocument(document);
54   if (!loader) {
55     return NS_ERROR_FAILURE;
56   }
57 
58   nsresult rv;
59   if (aIsInternalIcon) {
60     rv = loader->LoadImage(
61         aIconURI, nullptr, nullptr, nullptr, 0, loadGroup, this, nullptr,
62         nullptr, nsIRequest::LOAD_NORMAL, nullptr,
63         nsIContentPolicy::TYPE_INTERNAL_IMAGE, u""_ns,
64         /* aUseUrgentStartForChannel */ false, /* aLinkPreload */ false,
65         getter_AddRefs(mIconRequest));
66   } else {
67     // TODO: nsIContentPolicy::TYPE_INTERNAL_IMAGE may not be the correct
68     // policy. See bug 1691868 for more details.
69     rv = loader->LoadImage(
70         aIconURI, nullptr, nullptr, aNode->NodePrincipal(), 0, loadGroup, this,
71         aNode, document, nsIRequest::LOAD_NORMAL, nullptr,
72         nsIContentPolicy::TYPE_INTERNAL_IMAGE, u""_ns,
73         /* aUseUrgentStartForChannel */ false,
74         /* aLinkPreload */ false, getter_AddRefs(mIconRequest));
75   }
76   if (NS_FAILED(rv)) {
77     return rv;
78   }
79 
80   return NS_OK;
81 }
82 
83 //
84 // imgINotificationObserver
85 //
86 
Notify(imgIRequest * aRequest,int32_t aType,const nsIntRect * aData)87 void IconLoader::Notify(imgIRequest* aRequest, int32_t aType,
88                         const nsIntRect* aData) {
89   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
90     // Make sure the image loaded successfully.
91     uint32_t status = imgIRequest::STATUS_ERROR;
92     if (NS_FAILED(aRequest->GetImageStatus(&status)) ||
93         (status & imgIRequest::STATUS_ERROR)) {
94       mIconRequest->Cancel(NS_BINDING_ABORTED);
95       mIconRequest = nullptr;
96       return;
97     }
98 
99     nsCOMPtr<imgIContainer> image;
100     aRequest->GetImage(getter_AddRefs(image));
101     MOZ_ASSERT(image);
102 
103     // Ask the image to decode at its intrinsic size.
104     int32_t width = 0, height = 0;
105     image->GetWidth(&width);
106     image->GetHeight(&height);
107     image->RequestDecodeForSize(nsIntSize(width, height),
108                                 imgIContainer::FLAG_HIGH_QUALITY_SCALING);
109   }
110 
111   if (aType == imgINotificationObserver::FRAME_COMPLETE) {
112     nsCOMPtr<imgIContainer> image;
113     aRequest->GetImage(getter_AddRefs(image));
114     MOZ_ASSERT(image);
115 
116     if (mListener) {
117       mListener->OnComplete(image);
118     }
119     return;
120   }
121 
122   if (aType == imgINotificationObserver::DECODE_COMPLETE) {
123     if (mIconRequest && mIconRequest == aRequest) {
124       mIconRequest->Cancel(NS_BINDING_ABORTED);
125       mIconRequest = nullptr;
126     }
127   }
128 }
129 
130 }  // namespace mozilla::widget
131