Serenity Operating System
at master 165 lines 4.8 kB view raw
1/* 2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Debug.h> 8#include <LibGfx/Bitmap.h> 9#include <LibWeb/DOM/Document.h> 10#include <LibWeb/DOM/Element.h> 11#include <LibWeb/Loader/ImageLoader.h> 12#include <LibWeb/Loader/ResourceLoader.h> 13#include <LibWeb/Platform/Timer.h> 14 15namespace Web { 16 17ImageLoader::ImageLoader(DOM::Element& owner_element) 18 : m_owner_element(owner_element) 19 , m_timer(Platform::Timer::create()) 20{ 21} 22 23void ImageLoader::adopt_object_resource(Badge<HTML::HTMLObjectElement>, Resource& resource) 24{ 25 auto image_resource = ImageResource::convert_from_resource(resource); 26 set_resource(image_resource); 27} 28 29void ImageLoader::load(const AK::URL& url) 30{ 31 m_redirects_count = 0; 32 load_without_resetting_redirect_counter(url); 33} 34 35void ImageLoader::load_without_resetting_redirect_counter(AK::URL const& url) 36{ 37 m_loading_state = LoadingState::Loading; 38 39 auto request = LoadRequest::create_for_url_on_page(url, m_owner_element.document().page()); 40 set_resource(ResourceLoader::the().load_resource(Resource::Type::Image, request)); 41} 42 43void ImageLoader::set_visible_in_viewport(bool visible_in_viewport) const 44{ 45 if (m_visible_in_viewport == visible_in_viewport) 46 return; 47 m_visible_in_viewport = visible_in_viewport; 48 49 // FIXME: Don't update volatility every time. If we're here, we're probably scanning through 50 // the whole document, updating "is visible in viewport" flags, and this could lead 51 // to the same bitmap being marked volatile back and forth unnecessarily. 52 if (resource()) 53 const_cast<ImageResource*>(resource())->update_volatility(); 54} 55 56void ImageLoader::resource_did_load() 57{ 58 VERIFY(resource()); 59 60 // For 3xx (Redirection) responses, the Location value refers to the preferred target resource for automatically redirecting the request. 61 auto status_code = resource()->status_code(); 62 if (status_code.has_value() && *status_code >= 300 && *status_code <= 399) { 63 auto location = resource()->response_headers().get("Location"); 64 if (location.has_value()) { 65 if (m_redirects_count > maximum_redirects_allowed) { 66 m_redirects_count = 0; 67 m_loading_state = LoadingState::Failed; 68 if (on_fail) 69 on_fail(); 70 return; 71 } 72 m_redirects_count++; 73 load_without_resetting_redirect_counter(resource()->url().complete_url(location.value())); 74 return; 75 } 76 } 77 m_redirects_count = 0; 78 79 if (!resource()->mime_type().starts_with("image/"sv)) { 80 m_loading_state = LoadingState::Failed; 81 if (on_fail) 82 on_fail(); 83 return; 84 } 85 86 m_loading_state = LoadingState::Loaded; 87 88 if constexpr (IMAGE_LOADER_DEBUG) { 89 if (!resource()->has_encoded_data()) { 90 dbgln("ImageLoader: Resource did load, no encoded data. URL: {}", resource()->url()); 91 } else { 92 dbgln("ImageLoader: Resource did load, has encoded data. URL: {}", resource()->url()); 93 } 94 } 95 96 if (resource()->is_animated() && resource()->frame_count() > 1) { 97 m_timer->set_interval(resource()->frame_duration(0)); 98 m_timer->on_timeout = [this] { animate(); }; 99 m_timer->start(); 100 } 101 102 if (on_load) 103 on_load(); 104} 105 106void ImageLoader::animate() 107{ 108 if (!m_visible_in_viewport) 109 return; 110 111 m_current_frame_index = (m_current_frame_index + 1) % resource()->frame_count(); 112 auto current_frame_duration = resource()->frame_duration(m_current_frame_index); 113 114 if (current_frame_duration != m_timer->interval()) { 115 m_timer->restart(current_frame_duration); 116 } 117 118 if (m_current_frame_index == resource()->frame_count() - 1) { 119 ++m_loops_completed; 120 if (m_loops_completed > 0 && m_loops_completed == resource()->loop_count()) { 121 m_timer->stop(); 122 } 123 } 124 125 if (on_animate) 126 on_animate(); 127} 128 129void ImageLoader::resource_did_fail() 130{ 131 dbgln("ImageLoader: Resource did fail. URL: {}", resource()->url()); 132 m_loading_state = LoadingState::Failed; 133 if (on_fail) 134 on_fail(); 135} 136 137bool ImageLoader::has_image() const 138{ 139 if (!resource()) 140 return false; 141 return bitmap(0); 142} 143 144unsigned ImageLoader::width() const 145{ 146 if (!resource()) 147 return 0; 148 return bitmap(0) ? bitmap(0)->width() : 0; 149} 150 151unsigned ImageLoader::height() const 152{ 153 if (!resource()) 154 return 0; 155 return bitmap(0) ? bitmap(0)->height() : 0; 156} 157 158Gfx::Bitmap const* ImageLoader::bitmap(size_t frame_index) const 159{ 160 if (!resource()) 161 return nullptr; 162 return resource()->bitmap(frame_index); 163} 164 165}