Commit message: None
CVE: CVE-2015-1265
Vulnerability description: Multiple unspecified vulnerabilities in Google Chrome before 43.0.2357.65 allow attackers to cause a denial of service or possibly have other impact via unknown vectors.

Project: Chrome

DEFINE_TRACE(ImageLoader) {
  visitor->trace(m_image);
  visitor->trace(m_imageResourceForImageDocument);
  visitor->trace(m_element);
}

Project: Chrome

  Task(ImageLoader* loader,
       UpdateFromElementBehavior updateBehavior,
       ReferrerPolicy referrerPolicy)
      : m_loader(loader),
        m_shouldBypassMainWorldCSP(shouldBypassMainWorldCSP(loader)),
        m_updateBehavior(updateBehavior),
        m_weakFactory(this),
        m_referrerPolicy(referrerPolicy) {
    ExecutionContext& context = m_loader->element()->document();
    InspectorInstrumentation::asyncTaskScheduled(&context, "Image", this);
    v8::Isolate* isolate = V8PerIsolateData::mainThreadIsolate();
    v8::HandleScope scope(isolate);
    if (!isolate->GetCurrentContext().IsEmpty()) {
      m_scriptState = ScriptState::current(isolate);
    } else {
      m_scriptState =
          ScriptState::forMainWorld(loader->element()->document().frame());
      DCHECK(m_scriptState);
    }
    m_requestURL =
        loader->imageSourceToKURL(loader->element()->imageSourceURL());
  }

Project: Chrome

  void clearLoader() {
    m_loader = nullptr;
    m_scriptState.clear();
  }

Project: Chrome

  static std::unique_ptr<Task> create(ImageLoader* loader,
                                      UpdateFromElementBehavior updateBehavior,
                                      ReferrerPolicy referrerPolicy) {
    return WTF::makeUnique<Task>(loader, updateBehavior, referrerPolicy);
  }

Project: Chrome

  WeakPtr<Task> createWeakPtr() { return m_weakFactory.createWeakPtr(); }

Project: Chrome

inline void ImageLoader::crossSiteOrCSPViolationOccurred(
    AtomicString imageSourceURL) {
  m_failedLoadURL = imageSourceURL;
}

Project: Chrome

void ImageLoader::dispatchPendingErrorEvent() {
  if (!m_hasPendingErrorEvent)
    return;
  m_hasPendingErrorEvent = false;

  if (element()->document().frame())
    element()->dispatchEvent(Event::create(EventTypeNames::error));

  updatedHasPendingEvent();
}

Project: Chrome

void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender) {
  RESOURCE_LOADING_DVLOG(1) << "ImageLoader::dispatchPendingEvent " << this;
  DCHECK(eventSender == &loadEventSender() ||
         eventSender == &errorEventSender());
  const AtomicString& eventType = eventSender->eventType();
  if (eventType == EventTypeNames::load)
    dispatchPendingLoadEvent();
  if (eventType == EventTypeNames::error)
    dispatchPendingErrorEvent();
}

Project: Chrome

void ImageLoader::dispatchPendingLoadEvent() {
  if (!m_hasPendingLoadEvent)
    return;
  if (!m_image)
    return;
  m_hasPendingLoadEvent = false;
  if (element()->document().frame())
    dispatchLoadEvent();

  updatedHasPendingEvent();
}

Project: Chrome

void ImageLoader::dispose() {
  RESOURCE_LOADING_DVLOG(1)
      << "~ImageLoader " << this
      << "; m_hasPendingLoadEvent=" << m_hasPendingLoadEvent
      << ", m_hasPendingErrorEvent=" << m_hasPendingErrorEvent;

  if (m_image) {
    m_image->removeObserver(this);
    m_image = nullptr;
  }
}

Project: Chrome

void ImageLoader::doUpdateFromElement(BypassMainWorldBehavior bypassBehavior,
                                      UpdateFromElementBehavior updateBehavior,
                                      const KURL& url,
                                      ReferrerPolicy referrerPolicy) {
  m_pendingTask.reset();
  std::unique_ptr<IncrementLoadEventDelayCount> loadDelayCounter;
  loadDelayCounter.swap(m_loadDelayCounter);

  Document& document = m_element->document();
  if (!document.isActive())
    return;

  AtomicString imageSourceURL = m_element->imageSourceURL();
  ImageResourceContent* newImage = nullptr;
  if (!url.isNull()) {
    ResourceLoaderOptions resourceLoaderOptions =
        ResourceFetcher::defaultResourceOptions();
    ResourceRequest resourceRequest(url);
    if (updateBehavior == UpdateForcedReload) {
      resourceRequest.setCachePolicy(WebCachePolicy::BypassingCache);
      resourceRequest.setPreviewsState(WebURLRequest::PreviewsNoTransform);
    }

    if (referrerPolicy != ReferrerPolicyDefault) {
      resourceRequest.setHTTPReferrer(SecurityPolicy::generateReferrer(
          referrerPolicy, url, document.outgoingReferrer()));
    }

    if (isHTMLPictureElement(element()->parentNode()) ||
        !element()->fastGetAttribute(HTMLNames::srcsetAttr).isNull())
      resourceRequest.setRequestContext(WebURLRequest::RequestContextImageSet);
    FetchRequest request(resourceRequest, element()->localName(),
                         resourceLoaderOptions);
    configureRequest(request, bypassBehavior, *m_element,
                     document.clientHintsPreferences());

    if (updateBehavior != UpdateForcedReload && document.settings() &&
        document.settings()->getFetchImagePlaceholders()) {
      request.setAllowImagePlaceholder();
    }

    newImage = ImageResourceContent::fetch(request, document.fetcher());

    if (!newImage && !pageIsBeingDismissed(&document)) {
      crossSiteOrCSPViolationOccurred(imageSourceURL);
      dispatchErrorEvent();
    } else {
      clearFailedLoadURL();
    }
  } else {
    if (!imageSourceURL.isNull()) {
      dispatchErrorEvent();
    }
    noImageResourceToLoad();
  }

  ImageResourceContent* oldImage = m_image.get();
  if (updateBehavior == UpdateSizeChanged && m_element->layoutObject() &&
      m_element->layoutObject()->isImage() && newImage == oldImage) {
    toLayoutImage(m_element->layoutObject())->intrinsicSizeChanged();
  } else {
    if (m_hasPendingLoadEvent) {
      loadEventSender().cancelEvent(this);
      m_hasPendingLoadEvent = false;
    }

    if (m_hasPendingErrorEvent && newImage) {
      errorEventSender().cancelEvent(this);
      m_hasPendingErrorEvent = false;
    }

    m_image = newImage;
    m_hasPendingLoadEvent = newImage;
    m_imageComplete = !newImage;

    updateLayoutObject();
    if (newImage) {
      newImage->addObserver(this);
    }
    if (oldImage) {
      oldImage->removeObserver(this);
    }
  }

  if (LayoutImageResource* imageResource = layoutImageResource())
    imageResource->resetAnimation();

  updatedHasPendingEvent();
}

Project: Chrome

void ImageLoader::elementDidMoveToNewDocument() {
  if (m_loadDelayCounter)
    m_loadDelayCounter->documentChanged(m_element->document());
  clearFailedLoadURL();
  setImage(0);
}

Project: Chrome

inline void ImageLoader::enqueueImageLoadingMicroTask(
    UpdateFromElementBehavior updateBehavior,
    ReferrerPolicy referrerPolicy) {
  std::unique_ptr<Task> task =
      Task::create(this, updateBehavior, referrerPolicy);
  m_pendingTask = task->createWeakPtr();
  Microtask::enqueueMicrotask(
      WTF::bind(&Task::run, WTF::passed(std::move(task))));
  m_loadDelayCounter =
      IncrementLoadEventDelayCount::create(m_element->document());
}

Project: Chrome

static ImageEventSender& errorEventSender() {
  DEFINE_STATIC_LOCAL(ImageEventSender, sender,
                      (ImageEventSender::create(EventTypeNames::error)));
  return sender;
}

Project: Chrome

bool ImageLoader::getImageAnimationPolicy(ImageAnimationPolicy& policy) {
  if (!element()->document().settings())
    return false;

  policy = element()->document().settings()->getImageAnimationPolicy();
  return true;
}

Project: Chrome

void ImageLoader::imageNotifyFinished(ImageResourceContent* resource) {
  RESOURCE_LOADING_DVLOG(1)
      << "ImageLoader::imageNotifyFinished " << this
      << "; m_hasPendingLoadEvent=" << m_hasPendingLoadEvent;

  DCHECK(m_failedLoadURL.isEmpty());
  DCHECK_EQ(resource, m_image.get());

  m_imageComplete = true;

  if (m_image)
    m_image->updateImageAnimationPolicy();

  updateLayoutObject();

  if (m_image && m_image->getImage() && m_image->getImage()->isSVGImage())
    toSVGImage(m_image->getImage())->updateUseCounters(element()->document());

  if (!m_hasPendingLoadEvent)
    return;

  if (resource->errorOccurred()) {
    loadEventSender().cancelEvent(this);
    m_hasPendingLoadEvent = false;

    if (resource->resourceError().isAccessCheck()) {
      crossSiteOrCSPViolationOccurred(
          AtomicString(resource->resourceError().failingURL()));
    }

    if (!m_suppressErrorEvents)
      dispatchErrorEvent();

    updatedHasPendingEvent();
    return;
  }
  loadEventSender().dispatchEventSoon(this);
}

Project: Chrome

KURL ImageLoader::imageSourceToKURL(AtomicString imageSourceURL) const {
  KURL url;

  Document& document = m_element->document();
  if (!document.isActive())
    return url;

  if (!imageSourceURL.isNull()) {
    String strippedImageSourceURL =
        stripLeadingAndTrailingHTMLSpaces(imageSourceURL);
    if (!strippedImageSourceURL.isEmpty())
      url = document.completeURL(strippedImageSourceURL);
  }
  return url;
}

Project: Chrome

LayoutImageResource* ImageLoader::layoutImageResource() {
  LayoutObject* layoutObject = m_element->layoutObject();

  if (!layoutObject)
    return 0;

  if (layoutObject->isImage() &&
      !static_cast<LayoutImage*>(layoutObject)->isGeneratedContent())
    return toLayoutImage(layoutObject)->imageResource();

  if (layoutObject->isSVGImage())
    return toLayoutSVGImage(layoutObject)->imageResource();

  if (layoutObject->isVideo())
    return toLayoutVideo(layoutObject)->imageResource();

  return 0;
}

Project: Chrome

static ImageEventSender& loadEventSender() {
  DEFINE_STATIC_LOCAL(ImageEventSender, sender,
                      (ImageEventSender::create(EventTypeNames::load)));
  return sender;
}

Project: Chrome

static inline bool pageIsBeingDismissed(Document* document) {
  return document->pageDismissalEventBeingDispatched() != Document::NoDismissal;
}

Project: Chrome

  void run() {
    if (!m_loader)
      return;
    ExecutionContext& context = m_loader->element()->document();
    InspectorInstrumentation::AsyncTask asyncTask(&context, this);
    if (m_scriptState->contextIsValid()) {
      ScriptState::Scope scope(m_scriptState.get());
      m_loader->doUpdateFromElement(m_shouldBypassMainWorldCSP,
                                    m_updateBehavior, m_requestURL,
                                    m_referrerPolicy);
    } else {
      m_loader->doUpdateFromElement(m_shouldBypassMainWorldCSP,
                                    m_updateBehavior, m_requestURL,
                                    m_referrerPolicy);
    }
  }

Project: Chrome

void ImageLoader::setImage(ImageResourceContent* newImage) {
  setImageWithoutConsideringPendingLoadEvent(newImage);

  updatedHasPendingEvent();
}

Project: Chrome

static ImageLoader::BypassMainWorldBehavior shouldBypassMainWorldCSP(
    ImageLoader* loader) {
  DCHECK(loader);
  DCHECK(loader->element());
  if (loader->element()->document().frame() &&
      loader->element()
          ->document()
          .frame()
          ->script()
          .shouldBypassMainWorldCSP())
    return ImageLoader::BypassMainWorldCSP;
  return ImageLoader::DoNotBypassMainWorldCSP;
}

Project: Chrome

bool ImageLoader::shouldLoadImmediately(const KURL& url) const {
  if (!url.isNull()) {
    Resource* resource = memoryCache()->resourceForURL(
        url, m_element->document().fetcher()->getCacheIdentifier());
    if (resource && !resource->errorOccurred())
      return true;
  }
  return (isHTMLObjectElement(m_element) || isHTMLEmbedElement(m_element));
}

Project: Chrome

void ImageLoader::updateFromElement(UpdateFromElementBehavior updateBehavior,
                                    ReferrerPolicy referrerPolicy) {
  AtomicString imageSourceURL = m_element->imageSourceURL();
  m_suppressErrorEvents = (updateBehavior == UpdateSizeChanged);

  if (updateBehavior == UpdateIgnorePreviousError)
    clearFailedLoadURL();

  if (!m_failedLoadURL.isEmpty() && imageSourceURL == m_failedLoadURL)
    return;

  if (m_loadingImageDocument && updateBehavior != UpdateForcedReload) {
    ImageResource* imageResource =
        ImageResource::create(imageSourceToKURL(m_element->imageSourceURL()));
    imageResource->setStatus(Resource::Pending);
    m_imageResourceForImageDocument = imageResource;
    setImage(imageResource->getContent());
    return;
  }

  if (m_pendingTask) {
    m_pendingTask->clearLoader();
    m_pendingTask.reset();
  }

  KURL url = imageSourceToKURL(imageSourceURL);
  if (shouldLoadImmediately(url)) {
    doUpdateFromElement(DoNotBypassMainWorldCSP, updateBehavior, url,
                        referrerPolicy);
    return;
  }
  if (imageSourceURL.isEmpty()) {
    ImageResourceContent* image = m_image.get();
    if (image) {
      image->removeObserver(this);
    }
    m_image = nullptr;
  }

  Document& document = m_element->document();
  if (document.isActive())
    enqueueImageLoadingMicroTask(updateBehavior, referrerPolicy);
}

Project: Chrome

void ImageLoader::updatedHasPendingEvent() {
  bool wasProtected = m_elementIsProtected;
  m_elementIsProtected = m_hasPendingLoadEvent || m_hasPendingErrorEvent;
  if (wasProtected == m_elementIsProtected)
    return;

  if (m_elementIsProtected) {
    if (m_derefElementTimer.isActive())
      m_derefElementTimer.stop();
    else
      m_keepAlive = m_element;
  } else {
    DCHECK(!m_derefElementTimer.isActive());
    m_derefElementTimer.startOneShot(0, BLINK_FROM_HERE);
  }
}