void
ZoomConstraintsClient::Destroy()
{
  if (!(mPresShell && mDocument)) {
    return;
  }

  ZCC_LOG("Destroying %p\n", this);

  if (mEventTarget) {
    mEventTarget->RemoveEventListener(DOM_META_ADDED, this, false);
    mEventTarget = nullptr;
  }

  nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
  if (observerService) {
    observerService->RemoveObserver(this, BEFORE_FIRST_PAINT.Data());
  }

  if (mGuid) {
    if (nsIWidget* widget = GetWidget(mPresShell)) {
      ZCC_LOG("Sending null constraints in %p for { %u, %" PRIu64 " }\n",
        this, mGuid->mPresShellId, mGuid->mScrollId);
      widget->UpdateZoomConstraints(mGuid->mPresShellId, mGuid->mScrollId, Nothing());
      mGuid = Nothing();
    }
  }

  mDocument = nullptr;
  mPresShell = nullptr;
}
MobileViewportManager::MobileViewportManager(nsIPresShell* aPresShell,
                                             nsIDocument* aDocument)
  : mDocument(aDocument)
  , mPresShell(aPresShell)
  , mIsFirstPaint(false)
  , mPainted(false)
{
  MOZ_ASSERT(mPresShell);
  MOZ_ASSERT(mDocument);

  MVM_LOG("%p: creating with presShell %p document %p\n", this, mPresShell, aDocument);

  if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow()) {
    mEventTarget = window->GetChromeEventHandler();
  }
  if (mEventTarget) {
    mEventTarget->AddEventListener(DOM_META_ADDED, this, false);
    mEventTarget->AddEventListener(DOM_META_CHANGED, this, false);
    mEventTarget->AddEventListener(FULL_ZOOM_CHANGE, this, false);
    mEventTarget->AddEventListener(LOAD, this, true);
  }

  nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
  if (observerService) {
    observerService->AddObserver(this, BEFORE_FIRST_PAINT.Data(), false);
  }
}
void
ZoomConstraintsClient::Init(nsIPresShell* aPresShell, nsIDocument* aDocument)
{
  if (!(aPresShell && aDocument)) {
    return;
  }

  mPresShell = aPresShell;
  mDocument = aDocument;

  if (nsCOMPtr<nsPIDOMWindow> window = mDocument->GetWindow()) {
    mEventTarget = window->GetChromeEventHandler();
  }
  if (mEventTarget) {
    mEventTarget->AddEventListener(DOM_META_ADDED, this, false);
    mEventTarget->AddEventListener(DOM_META_CHANGED, this, false);
  }

  nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
  if (observerService) {
    observerService->AddObserver(this, BEFORE_FIRST_PAINT.Data(), false);
  }

  Preferences::AddStrongObserver(this, "browser.ui.zoom.force-user-scalable");
}
NS_IMETHODIMP
ZoomConstraintsClient::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
{
  if (SameCOMIdentity(aSubject, mDocument) && BEFORE_FIRST_PAINT.EqualsASCII(aTopic)) {
    ZCC_LOG("Got a before-first-paint event in %p\n", this);
    RefreshZoomConstraints();
  } else if (NS_PREF_CHANGED.EqualsASCII(aTopic)) {
    ZCC_LOG("Got a pref-change event in %p\n", this);
    // We need to run this later because all the pref change listeners need
    // to execute before we can be guaranteed that gfxPrefs::ForceUserScalable()
    // returns the updated value.
    NS_DispatchToMainThread(NS_NewRunnableMethod(
      this, &ZoomConstraintsClient::RefreshZoomConstraints));
  }
  return NS_OK;
}
NS_IMETHODIMP
ZoomConstraintsClient::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
{
  if (SameCOMIdentity(aSubject, mDocument) && BEFORE_FIRST_PAINT.EqualsASCII(aTopic)) {
    ZCC_LOG("Got a before-first-paint event in %p\n", this);
    RefreshZoomConstraints();
  }
  return NS_OK;
}
NS_IMETHODIMP
MobileViewportManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
{
  if (SameCOMIdentity(aSubject, mDocument) && BEFORE_FIRST_PAINT.EqualsASCII(aTopic)) {
    MVM_LOG("%p: got a before-first-paint event\n", this);
    mIsFirstPaint = true;
    RefreshViewportSize(false);
  }
  return NS_OK;
}
NS_IMETHODIMP
MobileViewportManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
{
  if (SameCOMIdentity(aSubject, mDocument) && BEFORE_FIRST_PAINT.EqualsASCII(aTopic)) {
    MVM_LOG("%p: got a before-first-paint event\n", this);
    if (!mPainted) {
      // before-first-paint message arrived before load event
      SetInitialViewport();
    }
  }
  return NS_OK;
}
void
MobileViewportManager::Destroy()
{
  MVM_LOG("%p: destroying\n", this);

  if (mEventTarget) {
    mEventTarget->RemoveEventListener(DOM_META_ADDED, this, false);
    mEventTarget->RemoveEventListener(FULL_ZOOM_CHANGE, this, false);
    mEventTarget = nullptr;
  }

  nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
  if (observerService) {
    observerService->RemoveObserver(this, BEFORE_FIRST_PAINT.Data());
  }

  mDocument = nullptr;
  mPresShell = nullptr;
}