<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/sync_setup_flow.h"

#include "base/callback.h"
#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/metrics/histogram.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/sync_setup_flow_handler.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "ui/base/l10n/l10n_font_util.h"
#include "ui/gfx/font.h"

namespace {

// Helper function to disable password sync.
void DisablePasswordSync(ProfileSyncService* service) {
  syncable::ModelTypeSet types;
  service->GetPreferredDataTypes(&types);
  types.erase(syncable::PASSWORDS);
  service->OnUserChoseDatatypes(false, types);
}

}  // namespace

SyncConfiguration::SyncConfiguration()
    : sync_everything(false),
      use_secondary_passphrase(false) {
}

SyncConfiguration::~SyncConfiguration() {}

SyncSetupFlow::~SyncSetupFlow() {
  flow_handler_->SetFlow(NULL);
}

// static
SyncSetupFlow* SyncSetupFlow::Run(ProfileSyncService* service,
                                  SyncSetupFlowContainer* container,
                                  SyncSetupWizard::State start,
                                  SyncSetupWizard::State end) {
  DictionaryValue args;
  if (start == SyncSetupWizard::GAIA_LOGIN)
    SyncSetupFlow::GetArgsForGaiaLogin(service, &args);
  else if (start == SyncSetupWizard::CONFIGURE)
    SyncSetupFlow::GetArgsForConfigure(service, &args);
  else if (start == SyncSetupWizard::ENTER_PASSPHRASE)
    SyncSetupFlow::GetArgsForEnterPassphrase(false, false, &args);

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);

  return new SyncSetupFlow(start, end, json_args, container, service);
}

// static
void SyncSetupFlow::GetArgsForGaiaLogin(const ProfileSyncService* service,
                                        DictionaryValue* args) {
  const GoogleServiceAuthError& error = service->GetAuthError();
  if (!service->last_attempted_user_email().empty()) {
    args->SetString("user", service->last_attempted_user_email());
    args->SetInteger("error", error.state());
    args->SetBoolean("editable_user", true);
  } else {
    string16 user;
    if (!service->cros_user().empty())
      user = UTF8ToUTF16(service->cros_user());
    else
      user = service->GetAuthenticatedUsername();
    args->SetString("user", user);
    args->SetInteger("error", 0);
    args->SetBoolean("editable_user", user.empty());
  }

  args->SetString("captchaUrl", error.captcha().image_url.spec());
}

// static
void SyncSetupFlow::GetArgsForConfigure(ProfileSyncService* service,
                                        DictionaryValue* args) {
  // The SYNC_EVERYTHING case will set this to true.
  args->SetBoolean("showSyncEverythingPage", false);

  args->SetBoolean("keepEverythingSynced",
      service->profile()->GetPrefs()->GetBoolean(prefs::kKeepEverythingSynced));

  // Bookmarks, Preferences, and Themes are launched for good, there's no
  // going back now.  Check if the other data types are registered though.
  syncable::ModelTypeSet registered_types;
  service->GetRegisteredDataTypes(&registered_types);
  args->SetBoolean("passwordsRegistered",
      registered_types.count(syncable::PASSWORDS) > 0);
  args->SetBoolean("autofillRegistered",
      registered_types.count(syncable::AUTOFILL) > 0);
  args->SetBoolean("extensionsRegistered",
      registered_types.count(syncable::EXTENSIONS) > 0);
  args->SetBoolean("typedUrlsRegistered",
      registered_types.count(syncable::TYPED_URLS) > 0);
  args->SetBoolean("appsRegistered",
      registered_types.count(syncable::APPS) > 0);
  args->SetBoolean("sessionsRegistered",
      registered_types.count(syncable::SESSIONS) > 0);
  args->SetBoolean("syncBookmarks",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncBookmarks));
  args->SetBoolean("syncPreferences",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPreferences));
  args->SetBoolean("syncThemes",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncThemes));
  args->SetBoolean("syncPasswords",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPasswords));
  args->SetBoolean("syncAutofill",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncAutofill));
  args->SetBoolean("syncExtensions",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncExtensions));
  args->SetBoolean("syncSessions",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncSessions));
  args->SetBoolean("syncTypedUrls",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncTypedUrls));
  args->SetBoolean("syncApps",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncApps));
  args->SetBoolean("encryptionEnabled",
      CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableSyncEncryption));

  syncable::ModelTypeSet encrypted_types;
  service->GetEncryptedDataTypes(&encrypted_types);
  bool encrypt_all =
      encrypted_types.upper_bound(syncable::PASSWORDS) != encrypted_types.end();
  args->SetBoolean("encryptAllData", encrypt_all);

  // Load the parameters for the encryption tab.
  args->SetBoolean("usePassphrase", service->IsUsingSecondaryPassphrase());
}

// static
void SyncSetupFlow::GetArgsForEnterPassphrase(
    bool tried_creating_explicit_passphrase,
    bool tried_setting_explicit_passphrase,
    DictionaryValue* args) {
  args->SetBoolean("passphrase_creation_rejected",
                   tried_creating_explicit_passphrase);
  args->SetBoolean("passphrase_setting_rejected",
                   tried_setting_explicit_passphrase);
}

void SyncSetupFlow::AttachSyncSetupHandler(SyncSetupFlowHandler* handler) {
  flow_handler_ = handler;
  ActivateState(current_state_);
}

void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) {
  if (!ShouldAdvance(advance_state)) {
    LOG(WARNING) << "Invalid state change from "
                 << current_state_ << " to " << advance_state;
    return;
  }

  ActivateState(advance_state);
}

void SyncSetupFlow::Focus() {
  // TODO(jhawkins): Implement this.
}

// A callback to notify the delegate that the dialog closed.
void SyncSetupFlow::OnDialogClosed(const std::string& json_retval) {
  DCHECK(json_retval.empty());
  container_->set_flow(NULL);  // Sever ties from the wizard.
  if (current_state_ == SyncSetupWizard::DONE) {
    service_->SetSyncSetupCompleted();
  }

  // Record the state at which the user cancelled the signon dialog.
  switch (current_state_) {
    case SyncSetupWizard::GAIA_LOGIN:
      ProfileSyncService::SyncEvent(
          ProfileSyncService::CANCEL_FROM_SIGNON_WITHOUT_AUTH);
      break;
    case SyncSetupWizard::GAIA_SUCCESS:
      ProfileSyncService::SyncEvent(
          ProfileSyncService::CANCEL_DURING_SIGNON);
      break;
    case SyncSetupWizard::CONFIGURE:
    case SyncSetupWizard::ENTER_PASSPHRASE:
    case SyncSetupWizard::SETTING_UP:
      // TODO(atwilson): Treat a close during ENTER_PASSPHRASE like a
      // Cancel + Skip (i.e. call OnPassphraseCancel()). http://crbug.com/74645
      ProfileSyncService::SyncEvent(
          ProfileSyncService::CANCEL_DURING_CONFIGURE);
      break;
    case SyncSetupWizard::DONE:
      // TODO(sync): rename this histogram; it's tracking authorization AND
      // initial sync download time.
      UMA_HISTOGRAM_MEDIUM_TIMES("Sync.UserPerceivedAuthorizationTime",
                                 base::TimeTicks::Now() - login_start_time_);
      break;
    default:
      break;
  }

  service_->OnUserCancelledDialog();
  delete this;
}

void SyncSetupFlow::OnUserSubmittedAuth(const std::string& username,
                                        const std::string& password,
                                        const std::string& captcha,
                                        const std::string& access_code) {
  service_->OnUserSubmittedAuth(username, password, captcha, access_code);
}

void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) {
  // Go to the "loading..." screen.
  Advance(SyncSetupWizard::SETTING_UP);

  if (configuration.encrypt_all) {
    syncable::ModelTypeSet data_types;
    service_->GetRegisteredDataTypes(&data_types);
    service_->EncryptDataTypes(data_types);
  }

  // If we are activating the passphrase, we need to have one supplied.
  DCHECK(service_->IsUsingSecondaryPassphrase() ||
         !configuration.use_secondary_passphrase ||
         configuration.secondary_passphrase.length() > 0);

  if (configuration.use_secondary_passphrase &&
      !service_->IsUsingSecondaryPassphrase()) {
    service_->SetPassphrase(configuration.secondary_passphrase, true, true);
    tried_creating_explicit_passphrase_ = true;
  }

  service_->OnUserChoseDatatypes(configuration.sync_everything,
                                 configuration.data_types);
}

void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase) {
  Advance(SyncSetupWizard::SETTING_UP);
  service_->SetPassphrase(passphrase, true, false);
  tried_setting_explicit_passphrase_ = true;
}

void SyncSetupFlow::OnPassphraseCancel() {
  // If the user cancels when being asked for the passphrase,
  // just disable encrypted sync and continue setting up.
  if (current_state_ == SyncSetupWizard::ENTER_PASSPHRASE)
    DisablePasswordSync(service_);

  Advance(SyncSetupWizard::SETTING_UP);
}

// TODO(jhawkins): Use this method instead of a direct link in the html.
void SyncSetupFlow::OnGoToDashboard() {
  BrowserList::GetLastActive()->OpenPrivacyDashboardTabAndActivate();
}

// Use static Run method to get an instance.
SyncSetupFlow::SyncSetupFlow(SyncSetupWizard::State start_state,
                             SyncSetupWizard::State end_state,
                             const std::string& args,
                             SyncSetupFlowContainer* container,
                             ProfileSyncService* service)
    : container_(container),
      dialog_start_args_(args),
      current_state_(start_state),
      end_state_(end_state),
      login_start_time_(base::TimeTicks::Now()),
      flow_handler_(NULL),
      service_(service),
      tried_creating_explicit_passphrase_(false),
      tried_setting_explicit_passphrase_(false) {
}

// Returns true if the flow should advance to |state| based on |current_state_|.
bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) {
  switch (state) {
    case SyncSetupWizard::GAIA_LOGIN:
      return current_state_ == SyncSetupWizard::FATAL_ERROR ||
             current_state_ == SyncSetupWizard::GAIA_LOGIN ||
             current_state_ == SyncSetupWizard::SETTING_UP;
    case SyncSetupWizard::GAIA_SUCCESS:
      return current_state_ == SyncSetupWizard::GAIA_LOGIN;
    case SyncSetupWizard::SYNC_EVERYTHING:
    case SyncSetupWizard::CONFIGURE:
      return current_state_ == SyncSetupWizard::GAIA_SUCCESS;
    case SyncSetupWizard::ENTER_PASSPHRASE:
      return current_state_ == SyncSetupWizard::SYNC_EVERYTHING ||
             current_state_ == SyncSetupWizard::CONFIGURE ||
             current_state_ == SyncSetupWizard::SETTING_UP;
    case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR:
      DCHECK(current_state_ != SyncSetupWizard::GAIA_LOGIN &&
             current_state_ != SyncSetupWizard::GAIA_SUCCESS);
      return true;
    case SyncSetupWizard::SETTING_UP:
      return current_state_ == SyncSetupWizard::SYNC_EVERYTHING ||
             current_state_ == SyncSetupWizard::CONFIGURE ||
             current_state_ == SyncSetupWizard::ENTER_PASSPHRASE;
    case SyncSetupWizard::FATAL_ERROR:
      return true;  // You can always hit the panic button.
    case SyncSetupWizard::DONE:
      return current_state_ == SyncSetupWizard::SETTING_UP ||
             current_state_ == SyncSetupWizard::ENTER_PASSPHRASE;
    default:
      NOTREACHED() << "Unhandled State: " << state;
      return false;
  }
}

void SyncSetupFlow::ActivateState(SyncSetupWizard::State state) {
  switch (state) {
    case SyncSetupWizard::GAIA_LOGIN: {
      DictionaryValue args;
      SyncSetupFlow::GetArgsForGaiaLogin(service_, &args);
      flow_handler_->ShowGaiaLogin(args);
      break;
    }
    case SyncSetupWizard::GAIA_SUCCESS:
      if (end_state_ == SyncSetupWizard::GAIA_SUCCESS) {
        flow_handler_->ShowGaiaSuccessAndClose();
        break;
      }
      state = SyncSetupWizard::SYNC_EVERYTHING;
      //  Fall through.
    case SyncSetupWizard::SYNC_EVERYTHING: {
      DictionaryValue args;
      SyncSetupFlow::GetArgsForConfigure(service_, &args);
      args.SetBoolean("showSyncEverythingPage", true);
      flow_handler_->ShowConfigure(args);
      break;
    }
    case SyncSetupWizard::CONFIGURE: {
      DictionaryValue args;
      SyncSetupFlow::GetArgsForConfigure(service_, &args);
      flow_handler_->ShowConfigure(args);
      break;
    }
    case SyncSetupWizard::ENTER_PASSPHRASE: {
      DictionaryValue args;
      SyncSetupFlow::GetArgsForEnterPassphrase(
          tried_creating_explicit_passphrase_,
          tried_setting_explicit_passphrase_,
          &args);
      flow_handler_->ShowPassphraseEntry(args);
      break;
    }
    case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR: {
      DictionaryValue args;
      SyncSetupFlow::GetArgsForConfigure(service_, &args);
      args.SetBoolean("was_aborted", true);
      flow_handler_->ShowConfigure(args);
      break;
    }
    case SyncSetupWizard::SETTING_UP: {
      flow_handler_->ShowSettingUp();
      break;
    }
    case SyncSetupWizard::FATAL_ERROR: {
      // This shows the user the "Could not connect to server" error.
      // TODO(sync): Update this error messaging.
      DictionaryValue args;
      SyncSetupFlow::GetArgsForGaiaLogin(service_, &args);
      args.SetInteger("error", GoogleServiceAuthError::CONNECTION_FAILED);
      flow_handler_->ShowGaiaLogin(args);
      break;
    }
    case SyncSetupWizard::DONE:
      flow_handler_->ShowSetupDone(
          UTF16ToWide(service_->GetAuthenticatedUsername()));
      break;
    default:
      NOTREACHED() << "Invalid advance state: " << state;
  }
  current_state_ = state;
}
<commit_msg>Dependency cleanup.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/sync_setup_flow.h"

#include "base/callback.h"
#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/metrics/histogram.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/sync_setup_flow_handler.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "grit/generated_resources.h"

namespace {

// Helper function to disable password sync.
void DisablePasswordSync(ProfileSyncService* service) {
  syncable::ModelTypeSet types;
  service->GetPreferredDataTypes(&types);
  types.erase(syncable::PASSWORDS);
  service->OnUserChoseDatatypes(false, types);
}

}  // namespace

SyncConfiguration::SyncConfiguration()
    : sync_everything(false),
      use_secondary_passphrase(false) {
}

SyncConfiguration::~SyncConfiguration() {}

SyncSetupFlow::~SyncSetupFlow() {
  flow_handler_->SetFlow(NULL);
}

// static
SyncSetupFlow* SyncSetupFlow::Run(ProfileSyncService* service,
                                  SyncSetupFlowContainer* container,
                                  SyncSetupWizard::State start,
                                  SyncSetupWizard::State end) {
  DictionaryValue args;
  if (start == SyncSetupWizard::GAIA_LOGIN)
    SyncSetupFlow::GetArgsForGaiaLogin(service, &args);
  else if (start == SyncSetupWizard::CONFIGURE)
    SyncSetupFlow::GetArgsForConfigure(service, &args);
  else if (start == SyncSetupWizard::ENTER_PASSPHRASE)
    SyncSetupFlow::GetArgsForEnterPassphrase(false, false, &args);

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);

  return new SyncSetupFlow(start, end, json_args, container, service);
}

// static
void SyncSetupFlow::GetArgsForGaiaLogin(const ProfileSyncService* service,
                                        DictionaryValue* args) {
  const GoogleServiceAuthError& error = service->GetAuthError();
  if (!service->last_attempted_user_email().empty()) {
    args->SetString("user", service->last_attempted_user_email());
    args->SetInteger("error", error.state());
    args->SetBoolean("editable_user", true);
  } else {
    string16 user;
    if (!service->cros_user().empty())
      user = UTF8ToUTF16(service->cros_user());
    else
      user = service->GetAuthenticatedUsername();
    args->SetString("user", user);
    args->SetInteger("error", 0);
    args->SetBoolean("editable_user", user.empty());
  }

  args->SetString("captchaUrl", error.captcha().image_url.spec());
}

// static
void SyncSetupFlow::GetArgsForConfigure(ProfileSyncService* service,
                                        DictionaryValue* args) {
  // The SYNC_EVERYTHING case will set this to true.
  args->SetBoolean("showSyncEverythingPage", false);

  args->SetBoolean("keepEverythingSynced",
      service->profile()->GetPrefs()->GetBoolean(prefs::kKeepEverythingSynced));

  // Bookmarks, Preferences, and Themes are launched for good, there's no
  // going back now.  Check if the other data types are registered though.
  syncable::ModelTypeSet registered_types;
  service->GetRegisteredDataTypes(&registered_types);
  args->SetBoolean("passwordsRegistered",
      registered_types.count(syncable::PASSWORDS) > 0);
  args->SetBoolean("autofillRegistered",
      registered_types.count(syncable::AUTOFILL) > 0);
  args->SetBoolean("extensionsRegistered",
      registered_types.count(syncable::EXTENSIONS) > 0);
  args->SetBoolean("typedUrlsRegistered",
      registered_types.count(syncable::TYPED_URLS) > 0);
  args->SetBoolean("appsRegistered",
      registered_types.count(syncable::APPS) > 0);
  args->SetBoolean("sessionsRegistered",
      registered_types.count(syncable::SESSIONS) > 0);
  args->SetBoolean("syncBookmarks",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncBookmarks));
  args->SetBoolean("syncPreferences",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPreferences));
  args->SetBoolean("syncThemes",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncThemes));
  args->SetBoolean("syncPasswords",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPasswords));
  args->SetBoolean("syncAutofill",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncAutofill));
  args->SetBoolean("syncExtensions",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncExtensions));
  args->SetBoolean("syncSessions",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncSessions));
  args->SetBoolean("syncTypedUrls",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncTypedUrls));
  args->SetBoolean("syncApps",
      service->profile()->GetPrefs()->GetBoolean(prefs::kSyncApps));
  args->SetBoolean("encryptionEnabled",
      CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableSyncEncryption));

  syncable::ModelTypeSet encrypted_types;
  service->GetEncryptedDataTypes(&encrypted_types);
  bool encrypt_all =
      encrypted_types.upper_bound(syncable::PASSWORDS) != encrypted_types.end();
  args->SetBoolean("encryptAllData", encrypt_all);

  // Load the parameters for the encryption tab.
  args->SetBoolean("usePassphrase", service->IsUsingSecondaryPassphrase());
}

// static
void SyncSetupFlow::GetArgsForEnterPassphrase(
    bool tried_creating_explicit_passphrase,
    bool tried_setting_explicit_passphrase,
    DictionaryValue* args) {
  args->SetBoolean("passphrase_creation_rejected",
                   tried_creating_explicit_passphrase);
  args->SetBoolean("passphrase_setting_rejected",
                   tried_setting_explicit_passphrase);
}

void SyncSetupFlow::AttachSyncSetupHandler(SyncSetupFlowHandler* handler) {
  flow_handler_ = handler;
  ActivateState(current_state_);
}

void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) {
  if (!ShouldAdvance(advance_state)) {
    LOG(WARNING) << "Invalid state change from "
                 << current_state_ << " to " << advance_state;
    return;
  }

  ActivateState(advance_state);
}

void SyncSetupFlow::Focus() {
  // TODO(jhawkins): Implement this.
}

// A callback to notify the delegate that the dialog closed.
void SyncSetupFlow::OnDialogClosed(const std::string& json_retval) {
  DCHECK(json_retval.empty());
  container_->set_flow(NULL);  // Sever ties from the wizard.
  if (current_state_ == SyncSetupWizard::DONE) {
    service_->SetSyncSetupCompleted();
  }

  // Record the state at which the user cancelled the signon dialog.
  switch (current_state_) {
    case SyncSetupWizard::GAIA_LOGIN:
      ProfileSyncService::SyncEvent(
          ProfileSyncService::CANCEL_FROM_SIGNON_WITHOUT_AUTH);
      break;
    case SyncSetupWizard::GAIA_SUCCESS:
      ProfileSyncService::SyncEvent(
          ProfileSyncService::CANCEL_DURING_SIGNON);
      break;
    case SyncSetupWizard::CONFIGURE:
    case SyncSetupWizard::ENTER_PASSPHRASE:
    case SyncSetupWizard::SETTING_UP:
      // TODO(atwilson): Treat a close during ENTER_PASSPHRASE like a
      // Cancel + Skip (i.e. call OnPassphraseCancel()). http://crbug.com/74645
      ProfileSyncService::SyncEvent(
          ProfileSyncService::CANCEL_DURING_CONFIGURE);
      break;
    case SyncSetupWizard::DONE:
      // TODO(sync): rename this histogram; it's tracking authorization AND
      // initial sync download time.
      UMA_HISTOGRAM_MEDIUM_TIMES("Sync.UserPerceivedAuthorizationTime",
                                 base::TimeTicks::Now() - login_start_time_);
      break;
    default:
      break;
  }

  service_->OnUserCancelledDialog();
  delete this;
}

void SyncSetupFlow::OnUserSubmittedAuth(const std::string& username,
                                        const std::string& password,
                                        const std::string& captcha,
                                        const std::string& access_code) {
  service_->OnUserSubmittedAuth(username, password, captcha, access_code);
}

void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) {
  // Go to the "loading..." screen.
  Advance(SyncSetupWizard::SETTING_UP);

  if (configuration.encrypt_all) {
    syncable::ModelTypeSet data_types;
    service_->GetRegisteredDataTypes(&data_types);
    service_->EncryptDataTypes(data_types);
  }

  // If we are activating the passphrase, we need to have one supplied.
  DCHECK(service_->IsUsingSecondaryPassphrase() ||
         !configuration.use_secondary_passphrase ||
         configuration.secondary_passphrase.length() > 0);

  if (configuration.use_secondary_passphrase &&
      !service_->IsUsingSecondaryPassphrase()) {
    service_->SetPassphrase(configuration.secondary_passphrase, true, true);
    tried_creating_explicit_passphrase_ = true;
  }

  service_->OnUserChoseDatatypes(configuration.sync_everything,
                                 configuration.data_types);
}

void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase) {
  Advance(SyncSetupWizard::SETTING_UP);
  service_->SetPassphrase(passphrase, true, false);
  tried_setting_explicit_passphrase_ = true;
}

void SyncSetupFlow::OnPassphraseCancel() {
  // If the user cancels when being asked for the passphrase,
  // just disable encrypted sync and continue setting up.
  if (current_state_ == SyncSetupWizard::ENTER_PASSPHRASE)
    DisablePasswordSync(service_);

  Advance(SyncSetupWizard::SETTING_UP);
}

// TODO(jhawkins): Use this method instead of a direct link in the html.
void SyncSetupFlow::OnGoToDashboard() {
  BrowserList::GetLastActive()->OpenPrivacyDashboardTabAndActivate();
}

// Use static Run method to get an instance.
SyncSetupFlow::SyncSetupFlow(SyncSetupWizard::State start_state,
                             SyncSetupWizard::State end_state,
                             const std::string& args,
                             SyncSetupFlowContainer* container,
                             ProfileSyncService* service)
    : container_(container),
      dialog_start_args_(args),
      current_state_(start_state),
      end_state_(end_state),
      login_start_time_(base::TimeTicks::Now()),
      flow_handler_(NULL),
      service_(service),
      tried_creating_explicit_passphrase_(false),
      tried_setting_explicit_passphrase_(false) {
}

// Returns true if the flow should advance to |state| based on |current_state_|.
bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) {
  switch (state) {
    case SyncSetupWizard::GAIA_LOGIN:
      return current_state_ == SyncSetupWizard::FATAL_ERROR ||
             current_state_ == SyncSetupWizard::GAIA_LOGIN ||
             current_state_ == SyncSetupWizard::SETTING_UP;
    case SyncSetupWizard::GAIA_SUCCESS:
      return current_state_ == SyncSetupWizard::GAIA_LOGIN;
    case SyncSetupWizard::SYNC_EVERYTHING:
    case SyncSetupWizard::CONFIGURE:
      return current_state_ == SyncSetupWizard::GAIA_SUCCESS;
    case SyncSetupWizard::ENTER_PASSPHRASE:
      return current_state_ == SyncSetupWizard::SYNC_EVERYTHING ||
             current_state_ == SyncSetupWizard::CONFIGURE ||
             current_state_ == SyncSetupWizard::SETTING_UP;
    case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR:
      DCHECK(current_state_ != SyncSetupWizard::GAIA_LOGIN &&
             current_state_ != SyncSetupWizard::GAIA_SUCCESS);
      return true;
    case SyncSetupWizard::SETTING_UP:
      return current_state_ == SyncSetupWizard::SYNC_EVERYTHING ||
             current_state_ == SyncSetupWizard::CONFIGURE ||
             current_state_ == SyncSetupWizard::ENTER_PASSPHRASE;
    case SyncSetupWizard::FATAL_ERROR:
      return true;  // You can always hit the panic button.
    case SyncSetupWizard::DONE:
      return current_state_ == SyncSetupWizard::SETTING_UP ||
             current_state_ == SyncSetupWizard::ENTER_PASSPHRASE;
    default:
      NOTREACHED() << "Unhandled State: " << state;
      return false;
  }
}

void SyncSetupFlow::ActivateState(SyncSetupWizard::State state) {
  switch (state) {
    case SyncSetupWizard::GAIA_LOGIN: {
      DictionaryValue args;
      SyncSetupFlow::GetArgsForGaiaLogin(service_, &args);
      flow_handler_->ShowGaiaLogin(args);
      break;
    }
    case SyncSetupWizard::GAIA_SUCCESS:
      if (end_state_ == SyncSetupWizard::GAIA_SUCCESS) {
        flow_handler_->ShowGaiaSuccessAndClose();
        break;
      }
      state = SyncSetupWizard::SYNC_EVERYTHING;
      //  Fall through.
    case SyncSetupWizard::SYNC_EVERYTHING: {
      DictionaryValue args;
      SyncSetupFlow::GetArgsForConfigure(service_, &args);
      args.SetBoolean("showSyncEverythingPage", true);
      flow_handler_->ShowConfigure(args);
      break;
    }
    case SyncSetupWizard::CONFIGURE: {
      DictionaryValue args;
      SyncSetupFlow::GetArgsForConfigure(service_, &args);
      flow_handler_->ShowConfigure(args);
      break;
    }
    case SyncSetupWizard::ENTER_PASSPHRASE: {
      DictionaryValue args;
      SyncSetupFlow::GetArgsForEnterPassphrase(
          tried_creating_explicit_passphrase_,
          tried_setting_explicit_passphrase_,
          &args);
      flow_handler_->ShowPassphraseEntry(args);
      break;
    }
    case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR: {
      DictionaryValue args;
      SyncSetupFlow::GetArgsForConfigure(service_, &args);
      args.SetBoolean("was_aborted", true);
      flow_handler_->ShowConfigure(args);
      break;
    }
    case SyncSetupWizard::SETTING_UP: {
      flow_handler_->ShowSettingUp();
      break;
    }
    case SyncSetupWizard::FATAL_ERROR: {
      // This shows the user the "Could not connect to server" error.
      // TODO(sync): Update this error messaging.
      DictionaryValue args;
      SyncSetupFlow::GetArgsForGaiaLogin(service_, &args);
      args.SetInteger("error", GoogleServiceAuthError::CONNECTION_FAILED);
      flow_handler_->ShowGaiaLogin(args);
      break;
    }
    case SyncSetupWizard::DONE:
      flow_handler_->ShowSetupDone(
          UTF16ToWide(service_->GetAuthenticatedUsername()));
      break;
    default:
      NOTREACHED() << "Invalid advance state: " << state;
  }
  current_state_ = state;
}
<|endoftext|>


<commit_before>#include "StdAfx.h"
#include "TiffIFD.h"
#include "TiffParser.h"
/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2014 Klaus Post

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

    http://www.klauspost.com
*/

namespace RawSpeed {

#ifdef CHECKSIZE
#undef CHECKSIZE
#endif

#define CHECKSIZE(A) if (A > size) ThrowTPE("Error reading TIFF structure (invalid size). File Corrupt")

TiffIFD::TiffIFD() {
  nextIFD = 0;
  endian = little;
  mFile = 0;
}

TiffIFD::TiffIFD(FileMap* f) {
  nextIFD = 0;
  endian = little;
  mFile = f;
}

TiffIFD::TiffIFD(FileMap* f, uint32 offset) {
  mFile = f;
  uint32 size = f->getSize();
  uint32 entries;
  endian = little;
  CHECKSIZE(offset);

  entries = *(unsigned short*)f->getData(offset);    // Directory entries in this IFD

  CHECKSIZE(offset + 2 + entries*4);
  for (uint32 i = 0; i < entries; i++) {
    TiffEntry *t = new TiffEntry(f, offset + 2 + i*12);

    switch (t->tag) {
      case DNGPRIVATEDATA:
        {
          try {
            TiffIFD *maker_ifd = parseDngPrivateData(t);
            mSubIFD.push_back(maker_ifd);
            delete(t);
          } catch (TiffParserException) {
            // Unparsable private data are added as entries
            mEntry[t->tag] = t;
          }
        }
        break;
      case MAKERNOTE:
      case MAKERNOTE_ALT:
        {
          try {
            mSubIFD.push_back(parseMakerNote(f, t->getDataOffset(), endian));
            delete(t);
          } catch (TiffParserException) {
            // Unparsable makernotes are added as entries
            mEntry[t->tag] = t;
          }
        }
        break;

      case FUJI_RAW_IFD:
        if (t->type == 0xd) // FUJI - correct type
          t->type = TIFF_LONG;
      case SUBIFDS:
      case EXIFIFDPOINTER:
        try {
          const unsigned int* sub_offsets = t->getIntArray();

          for (uint32 j = 0; j < t->count; j++) {
            mSubIFD.push_back(new TiffIFD(f, sub_offsets[j]));
          }
          delete(t);
        } catch (TiffParserException) {
          // Unparsable subifds are added as entries
          mEntry[t->tag] = t;
        }
        break;
      default:
        mEntry[t->tag] = t;
    }
  }
  nextIFD = *(int*)f->getData(offset + 2 + entries * 12);
}

TiffIFD* TiffIFD::parseDngPrivateData(TiffEntry *t) {
  /*
  1. Six bytes containing the zero-terminated string "Adobe". (The DNG specification calls for the DNGPrivateData tag to start with an ASCII string identifying the creator/format).
  2. 4 bytes: an ASCII string ("MakN" for a Makernote),  indicating what sort of data is being stored here. Note that this is not zero-terminated.
  3. A four-byte count (number of data bytes following); this is the length of the original MakerNote data. (This is always in "most significant byte first" format).
  4. 2 bytes: the byte-order indicator from the original file (the usual 'MM'/4D4D or 'II'/4949).
  5. 4 bytes: the original file offset for the MakerNote tag data (stored according to the byte order given above).
  6. The contents of the MakerNote tag. This is a simple byte-for-byte copy, with no modification.
  */
  uint32 size = t->count;
  const uchar8 *data = t->getData();
  string id((const char*)data);
  if (0 != id.compare("Adobe"))
    ThrowTPE("Not Adobe Private data");

  data+=6;
  if (!(data[0] == 'M' && data[1] == 'a' && data[2] == 'k' &&data[3] == 'N' ))
    ThrowTPE("Not Makernote");

  data+=4;
  uint32 count;
  if (big == getHostEndianness())
    count = *(uint32*)data;
  else
    count = (unsigned int)data[0] << 24 | (unsigned int)data[1] << 16 | (unsigned int)data[2] << 8 | (unsigned int)data[3];

  data+=4;
  CHECKSIZE(count);
  Endianness makernote_endian = unknown;
  if (data[0] == 0x49 && data[1] == 0x49)
    makernote_endian = little;
  else if (data[0] == 0x4D && data[1] == 0x4D)
    makernote_endian = big;
  else
    ThrowTPE("Cannot determine endianess of DNG makernote");

  data+=2;
  uint32 org_offset;

  if (big == getHostEndianness())
    org_offset = *(uint32*)data;
  else
    org_offset = (unsigned int)data[0] << 24 | (unsigned int)data[1] << 16 | (unsigned int)data[2] << 8 | (unsigned int)data[3];

  data+=4;
  /* We don't parse original makernotes that are placed after 300MB mark in the original file */
  if (org_offset+count > 300*1024*1024)
    ThrowTPE("Adobe Private data: original offset of makernote is past 300MB offset");

  /* Create fake tiff with original offsets */
  uchar8* maker_data = new uchar8[org_offset+count];
  memcpy(&maker_data[org_offset],data, count);
  FileMap *maker_map = new FileMap(maker_data, org_offset+count);

  TiffIFD *maker_ifd;
  try {
    maker_ifd = parseMakerNote(maker_map, org_offset, makernote_endian);
  } catch (TiffParserException &e) {
    delete[] maker_data;
    delete maker_map;
    throw e;
  }
  delete[] maker_data;
  delete maker_map;
  return maker_ifd;
}

const uchar8 fuji_signature[] = {
  'F', 'U', 'J', 'I', 'F', 'I', 'L', 'M', 0x0c, 0x00, 0x00, 0x00
};

/* This will attempt to parse makernotes and return it as an IFD */
TiffIFD* TiffIFD::parseMakerNote(FileMap *f, uint32 offset, Endianness parent_end)
{
  FileMap *mFile = f;
  uint32 size = f->getSize();
  CHECKSIZE(offset + 20);
  TiffIFD *maker_ifd = NULL;
  const uchar8* data = f->getData(offset);

  // Pentax makernote starts with AOC\0 - If it's there, skip it
  if (data[0] == 0x41 && data[1] == 0x4f && data[2] == 0x43 && data[3] == 0)
  {
    data +=4;
    offset +=4;
  }

  // Pentax also has "PENTAX" at the start, makernote starts at 8
  if (data[0] == 0x50 && data[1] == 0x45 && data[2] == 0x4e && data[3] == 0x54 && data[4] == 0x41 && data[5] == 0x58)
  {
    mFile = new FileMap(f->getDataWrt(offset), f->getSize()-offset);
    parent_end = getTiffEndianness((const ushort16*)&data[8]);
    if (parent_end == unknown)
      ThrowTPE("Cannot determine Pentax makernote endianness");
    data +=10;
    offset = 10;
  // Check for fuji signature in else block so we don't accidentally leak FileMap
  } else if (0 == memcmp(fuji_signature,&data[0], sizeof(fuji_signature))) {
    mFile = new FileMap(f->getDataWrt(offset), f->getSize()-offset);
    offset = 12;
  }

  // Panasonic has the word Exif at byte 6, a complete Tiff header starts at byte 12
  // This TIFF is 0 offset based
  if (data[6] == 0x45 && data[7] == 0x78 && data[8] == 0x69 && data[9] == 0x66)
  {
    parent_end = getTiffEndianness((const ushort16*)&data[12]);
    if (parent_end == unknown)
      ThrowTPE("Cannot determine Panasonic makernote endianness");
    data +=20;
    offset +=20;
  }

  // Some have MM or II to indicate endianness - read that
  if (data[0] == 0x49 && data[1] == 0x49) {
    offset +=2;
    parent_end = little;
  } else if (data[0] == 0x4D && data[1] == 0x4D) {
    parent_end = big;
    offset +=2;
  }

  // Attempt to parse the rest as an IFD
  try {
    if (parent_end == getHostEndianness())
      maker_ifd = new TiffIFD(mFile, offset);
    else
      maker_ifd = new TiffIFDBE(mFile, offset);
  } catch (...) {
    if (mFile != f)
      delete mFile;
 	  throw;
  }

  if (mFile != f)
    delete mFile;
  // If the structure cannot be read, a TiffParserException will be thrown.
  mFile = f;
  return maker_ifd;
}

TiffIFD::~TiffIFD(void) {
  for (map<TiffTag, TiffEntry*>::iterator i = mEntry.begin(); i != mEntry.end(); ++i) {
    delete((*i).second);
  }
  mEntry.clear();
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    delete(*i);
  }
  mSubIFD.clear();
}

bool TiffIFD::hasEntryRecursive(TiffTag tag) {
  if (mEntry.find(tag) != mEntry.end())
    return TRUE;
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    if ((*i)->hasEntryRecursive(tag))
      return TRUE;
  }
  return false;
}

vector<TiffIFD*> TiffIFD::getIFDsWithTag(TiffTag tag) {
  vector<TiffIFD*> matchingIFDs;
  if (mEntry.find(tag) != mEntry.end()) {
    matchingIFDs.push_back(this);
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    vector<TiffIFD*> t = (*i)->getIFDsWithTag(tag);
    for (uint32 j = 0; j < t.size(); j++) {
      matchingIFDs.push_back(t[j]);
    }
  }
  return matchingIFDs;
}

vector<TiffIFD*> TiffIFD::getIFDsWithTagWhere(TiffTag tag, uint32 isValue) {
  vector<TiffIFD*> matchingIFDs;
  if (mEntry.find(tag) != mEntry.end()) {
    TiffEntry* entry = mEntry[tag];
    if (entry->isInt() && entry->getInt() == isValue)
      matchingIFDs.push_back(this);
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    vector<TiffIFD*> t = (*i)->getIFDsWithTag(tag);
    for (uint32 j = 0; j < t.size(); j++) {
      matchingIFDs.push_back(t[j]);
    }
  }
  return matchingIFDs;
}

vector<TiffIFD*> TiffIFD::getIFDsWithTagWhere(TiffTag tag, string isValue) {
  vector<TiffIFD*> matchingIFDs;
  if (mEntry.find(tag) != mEntry.end()) {
    TiffEntry* entry = mEntry[tag];
    if (entry->isString() && 0 == entry->getString().compare(isValue))
      matchingIFDs.push_back(this);
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    vector<TiffIFD*> t = (*i)->getIFDsWithTag(tag);
    for (uint32 j = 0; j < t.size(); j++) {
      matchingIFDs.push_back(t[j]);
    }
  }
  return matchingIFDs;
}

TiffEntry* TiffIFD::getEntryRecursive(TiffTag tag) {
  if (mEntry.find(tag) != mEntry.end()) {
    return mEntry[tag];
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    TiffEntry* entry = (*i)->getEntryRecursive(tag);
    if (entry)
      return entry;
  }
  return NULL;
}

TiffEntry* TiffIFD::getEntryRecursiveWhere(TiffTag tag, uint32 isValue) {
  if (mEntry.find(tag) != mEntry.end()) {
    TiffEntry* entry = mEntry[tag];
    if (entry->isInt() && entry->getInt() == isValue)
      return entry;
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    TiffEntry* entry = (*i)->getEntryRecursive(tag);
    if (entry)
      return entry;
  }
  return NULL;
}

TiffEntry* TiffIFD::getEntryRecursiveWhere(TiffTag tag, string isValue) {
  if (mEntry.find(tag) != mEntry.end()) {
    TiffEntry* entry = mEntry[tag];
    if (entry->isString() && 0 == entry->getString().compare(isValue))
      return entry;
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    TiffEntry* entry = (*i)->getEntryRecursive(tag);
    if (entry)
      return entry;
  }
  return NULL;
}

TiffEntry* TiffIFD::getEntry(TiffTag tag) {
  if (mEntry.find(tag) != mEntry.end()) {
    return mEntry[tag];
  }
  ThrowTPE("TiffIFD: TIFF Parser entry 0x%x not found.", tag);
  return 0;
}

bool TiffIFD::hasEntry(TiffTag tag) {
  return mEntry.find(tag) != mEntry.end();
}

} // namespace RawSpeed
<commit_msg>Parse Nikon v3 makernotes while parsing TIFF.<commit_after>#include "StdAfx.h"
#include "TiffIFD.h"
#include "TiffParser.h"
/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2014 Klaus Post

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

    http://www.klauspost.com
*/

namespace RawSpeed {

#ifdef CHECKSIZE
#undef CHECKSIZE
#endif

#define CHECKSIZE(A) if (A > size) ThrowTPE("Error reading TIFF structure (invalid size). File Corrupt")

TiffIFD::TiffIFD() {
  nextIFD = 0;
  endian = little;
  mFile = 0;
}

TiffIFD::TiffIFD(FileMap* f) {
  nextIFD = 0;
  endian = little;
  mFile = f;
}

TiffIFD::TiffIFD(FileMap* f, uint32 offset) {
  mFile = f;
  uint32 size = f->getSize();
  uint32 entries;
  endian = little;
  CHECKSIZE(offset);

  entries = *(unsigned short*)f->getData(offset);    // Directory entries in this IFD

  CHECKSIZE(offset + 2 + entries*4);
  for (uint32 i = 0; i < entries; i++) {
    TiffEntry *t = new TiffEntry(f, offset + 2 + i*12);

    switch (t->tag) {
      case DNGPRIVATEDATA:
        {
          try {
            TiffIFD *maker_ifd = parseDngPrivateData(t);
            mSubIFD.push_back(maker_ifd);
            delete(t);
          } catch (TiffParserException) {
            // Unparsable private data are added as entries
            mEntry[t->tag] = t;
          }
        }
        break;
      case MAKERNOTE:
      case MAKERNOTE_ALT:
        {
          try {
            mSubIFD.push_back(parseMakerNote(f, t->getDataOffset(), endian));
            delete(t);
          } catch (TiffParserException) {
            // Unparsable makernotes are added as entries
            mEntry[t->tag] = t;
          }
        }
        break;

      case FUJI_RAW_IFD:
        if (t->type == 0xd) // FUJI - correct type
          t->type = TIFF_LONG;
      case SUBIFDS:
      case EXIFIFDPOINTER:
        try {
          const unsigned int* sub_offsets = t->getIntArray();

          for (uint32 j = 0; j < t->count; j++) {
            mSubIFD.push_back(new TiffIFD(f, sub_offsets[j]));
          }
          delete(t);
        } catch (TiffParserException) {
          // Unparsable subifds are added as entries
          mEntry[t->tag] = t;
        }
        break;
      default:
        mEntry[t->tag] = t;
    }
  }
  nextIFD = *(int*)f->getData(offset + 2 + entries * 12);
}

TiffIFD* TiffIFD::parseDngPrivateData(TiffEntry *t) {
  /*
  1. Six bytes containing the zero-terminated string "Adobe". (The DNG specification calls for the DNGPrivateData tag to start with an ASCII string identifying the creator/format).
  2. 4 bytes: an ASCII string ("MakN" for a Makernote),  indicating what sort of data is being stored here. Note that this is not zero-terminated.
  3. A four-byte count (number of data bytes following); this is the length of the original MakerNote data. (This is always in "most significant byte first" format).
  4. 2 bytes: the byte-order indicator from the original file (the usual 'MM'/4D4D or 'II'/4949).
  5. 4 bytes: the original file offset for the MakerNote tag data (stored according to the byte order given above).
  6. The contents of the MakerNote tag. This is a simple byte-for-byte copy, with no modification.
  */
  uint32 size = t->count;
  const uchar8 *data = t->getData();
  string id((const char*)data);
  if (0 != id.compare("Adobe"))
    ThrowTPE("Not Adobe Private data");

  data+=6;
  if (!(data[0] == 'M' && data[1] == 'a' && data[2] == 'k' &&data[3] == 'N' ))
    ThrowTPE("Not Makernote");

  data+=4;
  uint32 count;
  if (big == getHostEndianness())
    count = *(uint32*)data;
  else
    count = (unsigned int)data[0] << 24 | (unsigned int)data[1] << 16 | (unsigned int)data[2] << 8 | (unsigned int)data[3];

  data+=4;
  CHECKSIZE(count);
  Endianness makernote_endian = unknown;
  if (data[0] == 0x49 && data[1] == 0x49)
    makernote_endian = little;
  else if (data[0] == 0x4D && data[1] == 0x4D)
    makernote_endian = big;
  else
    ThrowTPE("Cannot determine endianess of DNG makernote");

  data+=2;
  uint32 org_offset;

  if (big == getHostEndianness())
    org_offset = *(uint32*)data;
  else
    org_offset = (unsigned int)data[0] << 24 | (unsigned int)data[1] << 16 | (unsigned int)data[2] << 8 | (unsigned int)data[3];

  data+=4;
  /* We don't parse original makernotes that are placed after 300MB mark in the original file */
  if (org_offset+count > 300*1024*1024)
    ThrowTPE("Adobe Private data: original offset of makernote is past 300MB offset");

  /* Create fake tiff with original offsets */
  uchar8* maker_data = new uchar8[org_offset+count];
  memcpy(&maker_data[org_offset],data, count);
  FileMap *maker_map = new FileMap(maker_data, org_offset+count);

  TiffIFD *maker_ifd;
  try {
    maker_ifd = parseMakerNote(maker_map, org_offset, makernote_endian);
  } catch (TiffParserException &e) {
    delete[] maker_data;
    delete maker_map;
    throw e;
  }
  delete[] maker_data;
  delete maker_map;
  return maker_ifd;
}

const uchar8 fuji_signature[] = {
  'F', 'U', 'J', 'I', 'F', 'I', 'L', 'M', 0x0c, 0x00, 0x00, 0x00
};

const uchar8 nikon_v3_signature[] = {
  'N', 'i', 'k', 'o', 'n', 0x0, 0x2
};

/* This will attempt to parse makernotes and return it as an IFD */
TiffIFD* TiffIFD::parseMakerNote(FileMap *f, uint32 offset, Endianness parent_end)
{
  FileMap *mFile = f;
  uint32 size = f->getSize();
  CHECKSIZE(offset + 20);
  TiffIFD *maker_ifd = NULL;
  const uchar8* data = f->getData(offset);

  // Pentax makernote starts with AOC\0 - If it's there, skip it
  if (data[0] == 0x41 && data[1] == 0x4f && data[2] == 0x43 && data[3] == 0)
  {
    data +=4;
    offset +=4;
  }

  // Pentax also has "PENTAX" at the start, makernote starts at 8
  if (data[0] == 0x50 && data[1] == 0x45 && data[2] == 0x4e && data[3] == 0x54 && data[4] == 0x41 && data[5] == 0x58)
  {
    mFile = new FileMap(f->getDataWrt(offset), f->getSize()-offset);
    parent_end = getTiffEndianness((const ushort16*)&data[8]);
    if (parent_end == unknown)
      ThrowTPE("Cannot determine Pentax makernote endianness");
    data +=10;
    offset = 10;
  // Check for fuji signature in else block so we don't accidentally leak FileMap
  } else if (0 == memcmp(fuji_signature,&data[0], sizeof(fuji_signature))) {
    mFile = new FileMap(f->getDataWrt(offset), f->getSize()-offset);
    offset = 12;
  } else if (0 == memcmp(nikon_v3_signature,&data[0], sizeof(nikon_v3_signature))) {
    offset += 10;
    mFile = new FileMap(f->getDataWrt(offset), f->getSize()-offset);
    data +=10;
    offset = 8;
    // Read endianness
    if (data[0] == 0x49 && data[1] == 0x49) {
      parent_end = little;
    } else if (data[0] == 0x4D && data[1] == 0x4D) {
      parent_end = big;
    }
    data += 2;
  }

  // Panasonic has the word Exif at byte 6, a complete Tiff header starts at byte 12
  // This TIFF is 0 offset based
  if (data[6] == 0x45 && data[7] == 0x78 && data[8] == 0x69 && data[9] == 0x66)
  {
    parent_end = getTiffEndianness((const ushort16*)&data[12]);
    if (parent_end == unknown)
      ThrowTPE("Cannot determine Panasonic makernote endianness");
    data +=20;
    offset +=20;
  }

  // Some have MM or II to indicate endianness - read that
  if (data[0] == 0x49 && data[1] == 0x49) {
    offset +=2;
    parent_end = little;
  } else if (data[0] == 0x4D && data[1] == 0x4D) {
    parent_end = big;
    offset +=2;
  }

  // Attempt to parse the rest as an IFD
  try {
    if (parent_end == getHostEndianness())
      maker_ifd = new TiffIFD(mFile, offset);
    else
      maker_ifd = new TiffIFDBE(mFile, offset);
  } catch (...) {
    if (mFile != f)
      delete mFile;
 	  throw;
  }

  if (mFile != f)
    delete mFile;
  // If the structure cannot be read, a TiffParserException will be thrown.
  mFile = f;
  return maker_ifd;
}

TiffIFD::~TiffIFD(void) {
  for (map<TiffTag, TiffEntry*>::iterator i = mEntry.begin(); i != mEntry.end(); ++i) {
    delete((*i).second);
  }
  mEntry.clear();
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    delete(*i);
  }
  mSubIFD.clear();
}

bool TiffIFD::hasEntryRecursive(TiffTag tag) {
  if (mEntry.find(tag) != mEntry.end())
    return TRUE;
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    if ((*i)->hasEntryRecursive(tag))
      return TRUE;
  }
  return false;
}

vector<TiffIFD*> TiffIFD::getIFDsWithTag(TiffTag tag) {
  vector<TiffIFD*> matchingIFDs;
  if (mEntry.find(tag) != mEntry.end()) {
    matchingIFDs.push_back(this);
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    vector<TiffIFD*> t = (*i)->getIFDsWithTag(tag);
    for (uint32 j = 0; j < t.size(); j++) {
      matchingIFDs.push_back(t[j]);
    }
  }
  return matchingIFDs;
}

vector<TiffIFD*> TiffIFD::getIFDsWithTagWhere(TiffTag tag, uint32 isValue) {
  vector<TiffIFD*> matchingIFDs;
  if (mEntry.find(tag) != mEntry.end()) {
    TiffEntry* entry = mEntry[tag];
    if (entry->isInt() && entry->getInt() == isValue)
      matchingIFDs.push_back(this);
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    vector<TiffIFD*> t = (*i)->getIFDsWithTag(tag);
    for (uint32 j = 0; j < t.size(); j++) {
      matchingIFDs.push_back(t[j]);
    }
  }
  return matchingIFDs;
}

vector<TiffIFD*> TiffIFD::getIFDsWithTagWhere(TiffTag tag, string isValue) {
  vector<TiffIFD*> matchingIFDs;
  if (mEntry.find(tag) != mEntry.end()) {
    TiffEntry* entry = mEntry[tag];
    if (entry->isString() && 0 == entry->getString().compare(isValue))
      matchingIFDs.push_back(this);
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    vector<TiffIFD*> t = (*i)->getIFDsWithTag(tag);
    for (uint32 j = 0; j < t.size(); j++) {
      matchingIFDs.push_back(t[j]);
    }
  }
  return matchingIFDs;
}

TiffEntry* TiffIFD::getEntryRecursive(TiffTag tag) {
  if (mEntry.find(tag) != mEntry.end()) {
    return mEntry[tag];
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    TiffEntry* entry = (*i)->getEntryRecursive(tag);
    if (entry)
      return entry;
  }
  return NULL;
}

TiffEntry* TiffIFD::getEntryRecursiveWhere(TiffTag tag, uint32 isValue) {
  if (mEntry.find(tag) != mEntry.end()) {
    TiffEntry* entry = mEntry[tag];
    if (entry->isInt() && entry->getInt() == isValue)
      return entry;
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    TiffEntry* entry = (*i)->getEntryRecursive(tag);
    if (entry)
      return entry;
  }
  return NULL;
}

TiffEntry* TiffIFD::getEntryRecursiveWhere(TiffTag tag, string isValue) {
  if (mEntry.find(tag) != mEntry.end()) {
    TiffEntry* entry = mEntry[tag];
    if (entry->isString() && 0 == entry->getString().compare(isValue))
      return entry;
  }
  for (vector<TiffIFD*>::iterator i = mSubIFD.begin(); i != mSubIFD.end(); ++i) {
    TiffEntry* entry = (*i)->getEntryRecursive(tag);
    if (entry)
      return entry;
  }
  return NULL;
}

TiffEntry* TiffIFD::getEntry(TiffTag tag) {
  if (mEntry.find(tag) != mEntry.end()) {
    return mEntry[tag];
  }
  ThrowTPE("TiffIFD: TIFF Parser entry 0x%x not found.", tag);
  return 0;
}

bool TiffIFD::hasEntry(TiffTag tag) {
  return mEntry.find(tag) != mEntry.end();
}

} // namespace RawSpeed
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/autofill_helper.h"

#include "app/l10n_util.h"
#include "chrome/renderer/form_manager.h"
#include "chrome/renderer/render_view.h"
#include "grit/generated_resources.h"
#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFormControlElement.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
#include "webkit/glue/password_form.h"

using WebKit::WebFormControlElement;
using WebKit::WebFormElement;
using WebKit::WebFrame;
using WebKit::WebInputElement;
using WebKit::WebNode;
using WebKit::WebString;

namespace {

// The size above which we stop triggering autofill for an input text field
// (so to avoid sending long strings through IPC).
const size_t kMaximumTextSizeForAutoFill = 1000;

}  // namespace

AutoFillHelper::AutoFillHelper(RenderView* render_view)
    : render_view_(render_view),
      autofill_query_id_(0),
      autofill_action_(AUTOFILL_NONE),
      suggestions_clear_index_(-1),
      suggestions_options_index_(-1) {
}

void AutoFillHelper::QueryAutoFillSuggestions(const WebNode& node,
                                              const WebString& name,
                                              const WebString& value) {
  static int query_counter = 0;
  autofill_query_id_ = query_counter++;
  autofill_query_node_ = node;

  const WebFormControlElement& element = node.toConst<WebFormControlElement>();
  webkit_glue::FormField field;
  FormManager::WebFormControlElementToFormField(element, true, &field);

  // WebFormControlElementToFormField does not scrape the DOM for the field
  // label, so find the label here.
  // TODO(jhawkins): Add form and field identities so we can use the cached form
  // data in FormManager.
  field.set_label(FormManager::LabelForElement(element));

  bool form_autofilled = form_manager_.FormWithNodeIsAutoFilled(node);
  render_view_->Send(new ViewHostMsg_QueryFormFieldAutoFill(
      render_view_->routing_id(), autofill_query_id_, form_autofilled, field));
}

void AutoFillHelper::RemoveAutocompleteSuggestion(
    const WebKit::WebString& name, const WebKit::WebString& value) {
  // The index of clear & options will have shifted down.
  if (suggestions_clear_index_ != -1)
    suggestions_clear_index_--;
  if (suggestions_options_index_ != -1)
    suggestions_options_index_--;

  render_view_->Send(new ViewHostMsg_RemoveAutocompleteEntry(
      render_view_->routing_id(), name, value));
}

void AutoFillHelper::SuggestionsReceived(int query_id,
                                         const std::vector<string16>& values,
                                         const std::vector<string16>& labels,
                                         const std::vector<string16>& icons,
                                         const std::vector<int>& unique_ids) {
  WebKit::WebView* web_view = render_view_->webview();
  if (!web_view || query_id != autofill_query_id_)
    return;

  // Any popup currently showing is now obsolete.
  web_view->hidePopups();

  // No suggestions: nothing to do.
  if (values.empty())
    return;

  std::vector<string16> v(values);
  std::vector<string16> l(labels);
  std::vector<string16> i(icons);
  std::vector<int> ids(unique_ids);
  int separator_index = -1;

  // The form has been auto-filled, so give the user the chance to clear the
  // form.  Append the 'Clear form' menu item.
  if (form_manager_.FormWithNodeIsAutoFilled(autofill_query_node_)) {
    v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
    l.push_back(string16());
    i.push_back(string16());
    ids.push_back(0);
    suggestions_clear_index_ = v.size() - 1;
    separator_index = values.size();
  }

  // Only include "AutoFill Options" special menu item if we have AutoFill
  // items, identified by |unique_ids| having at least one valid value.
  bool show_options = false;
  for (size_t i = 0; i < ids.size(); ++i) {
    if (ids[i] != 0) {
      show_options = true;
      break;
    }
  }
  if (show_options) {
    // Append the 'AutoFill Options...' menu item.
    v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS));
    l.push_back(string16());
    i.push_back(string16());
    ids.push_back(0);
    suggestions_options_index_ = v.size() - 1;
    separator_index = values.size();
  }

  // Send to WebKit for display.
  if (!v.empty()) {
    web_view->applyAutoFillSuggestions(
        autofill_query_node_, v, l, i, ids, separator_index);
  }
}

void AutoFillHelper::FormDataFilled(int query_id,
                                    const webkit_glue::FormData& form) {
  if (!render_view_->webview() || query_id != autofill_query_id_)
    return;

  switch (autofill_action_) {
    case AUTOFILL_FILL:
      form_manager_.FillForm(form, autofill_query_node_);
      break;
    case AUTOFILL_PREVIEW:
      form_manager_.PreviewForm(form);
      break;
    default:
      NOTREACHED();
  }
  autofill_action_ = AUTOFILL_NONE;
}

void AutoFillHelper::DidSelectAutoFillSuggestion(const WebNode& node,
                                                 const WebString& value,
                                                 const WebString& label,
                                                 int unique_id) {
  DidClearAutoFillSelection(node);
  QueryAutoFillFormData(node, value, label, unique_id, AUTOFILL_PREVIEW);
}

void AutoFillHelper::DidAcceptAutoFillSuggestion(const WebNode& node,
                                                 const WebString& value,
                                                 const WebString& label,
                                                 int unique_id,
                                                 unsigned index) {
  if (suggestions_options_index_ != -1 &&
      index == static_cast<unsigned>(suggestions_options_index_)) {
    // User selected 'AutoFill Options'.
    render_view_->Send(new ViewHostMsg_ShowAutoFillDialog(
        render_view_->routing_id()));
  } else if (suggestions_clear_index_ != -1 &&
             index == static_cast<unsigned>(suggestions_clear_index_)) {
    // User selected 'Clear form'.
    // The form has been auto-filled, so give the user the chance to clear the
    // form.
    form_manager_.ClearFormWithNode(node);
  } else if (form_manager_.FormWithNodeIsAutoFilled(node) || !unique_id) {
    // User selected an Autocomplete entry, so we fill directly.
    WebInputElement element = node.toConst<WebInputElement>();

    // Set the suggested value to update input element value immediately in UI.
    // The |setValue| call has update delayed until element loses focus.
    string16 substring = value;
    substring = substring.substr(0, element.maxLength());
    element.setSuggestedValue(substring);
    element.setValue(substring);

    WebFrame* webframe = node.document().frame();
    if (webframe)
      webframe->notifiyPasswordListenerOfAutocomplete(element);
  } else {
    // Fill the values for the whole form.
    QueryAutoFillFormData(node, value, label, unique_id, AUTOFILL_FILL);
  }

  suggestions_clear_index_ = -1;
  suggestions_options_index_ = -1;
}

void AutoFillHelper::DidClearAutoFillSelection(const WebNode& node) {
  form_manager_.ClearPreviewedFormWithNode(node);
}

void AutoFillHelper::FrameContentsAvailable(WebFrame* frame) {
  form_manager_.ExtractForms(frame);
  SendForms(frame);
}

void AutoFillHelper::FrameWillClose(WebFrame* frame) {
  form_manager_.ResetFrame(frame);
}

void AutoFillHelper::FrameDetached(WebFrame* frame) {
  form_manager_.ResetFrame(frame);
}

void AutoFillHelper::TextDidChangeInTextField(const WebInputElement& element) {
  ShowSuggestions(element, false, true);
}

void AutoFillHelper::InputElementClicked(const WebInputElement& element,
                                         bool already_focused) {
  if (already_focused)
    ShowSuggestions(element, true, false);
}

void AutoFillHelper::ShowSuggestions(
    const WebInputElement& const_element,
    bool autofill_on_empty_values,
    bool requires_caret_at_end) {
  // We need to call non-const methods.
  WebInputElement element(const_element);
  if (!element.isEnabledFormControl() ||
      element.inputType() != WebInputElement::Text ||
      element.inputType() == WebInputElement::Password ||
      !element.autoComplete() || element.isReadOnly()) {
    return;
  }

  WebString name = element.nameForAutofill();
  if (name.isEmpty())  // If the field has no name, then we won't have values.
    return;

  // Don't attempt to autofill with values that are too large.
  WebString value = element.value();
  if (value.length() > kMaximumTextSizeForAutoFill)
    return;

  if (!autofill_on_empty_values && value.isEmpty())
    return;

  if (requires_caret_at_end &&
      (element.selectionStart() != element.selectionEnd() ||
       element.selectionEnd() != static_cast<int>(value.length()))) {
    return;
  }

  QueryAutoFillSuggestions(element, name, value);
}

void AutoFillHelper::QueryAutoFillFormData(const WebNode& node,
                                           const WebString& value,
                                           const WebString& label,
                                           int unique_id,
                                           AutoFillAction action) {
  static int query_counter = 0;
  autofill_query_id_ = query_counter++;

  webkit_glue::FormData form;
  const WebInputElement element = node.toConst<WebInputElement>();
  if (!form_manager_.FindFormWithFormControlElement(
          element, FormManager::REQUIRE_NONE, &form))
    return;

  autofill_action_ = action;
  render_view_->Send(new ViewHostMsg_FillAutoFillFormData(
      render_view_->routing_id(), autofill_query_id_, form, value, label,
      unique_id));
}

void AutoFillHelper::SendForms(WebFrame* frame) {
  // TODO(jhawkins): Use FormManager once we have strict ordering of form
  // control elements in the cache.
  WebKit::WebVector<WebFormElement> web_forms;
  frame->forms(web_forms);

  std::vector<webkit_glue::FormData> forms;
  for (size_t i = 0; i < web_forms.size(); ++i) {
    const WebFormElement& web_form = web_forms[i];

    webkit_glue::FormData form;
    if (FormManager::WebFormElementToFormData(
            web_form, FormManager::REQUIRE_NONE, false, &form)) {
      forms.push_back(form);
    }
  }

  if (!forms.empty()) {
    render_view_->Send(new ViewHostMsg_FormsSeen(render_view_->routing_id(),
                                                 forms));
  }
}
<commit_msg>Every time a new autofill suggestion was received we would hide the autofill popup before showing a new one. This causes it to flash every time the user type a new key (as we get new suggestions for each key stroke). It is safe to hide the autofill only when there are no suggestions. If there are suggestions, the WebKit side takes care of resizing and potentially hiding the popup if necessary.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/autofill_helper.h"

#include "app/l10n_util.h"
#include "chrome/renderer/form_manager.h"
#include "chrome/renderer/render_view.h"
#include "grit/generated_resources.h"
#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFormControlElement.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
#include "webkit/glue/password_form.h"

using WebKit::WebFormControlElement;
using WebKit::WebFormElement;
using WebKit::WebFrame;
using WebKit::WebInputElement;
using WebKit::WebNode;
using WebKit::WebString;

namespace {

// The size above which we stop triggering autofill for an input text field
// (so to avoid sending long strings through IPC).
const size_t kMaximumTextSizeForAutoFill = 1000;

}  // namespace

AutoFillHelper::AutoFillHelper(RenderView* render_view)
    : render_view_(render_view),
      autofill_query_id_(0),
      autofill_action_(AUTOFILL_NONE),
      suggestions_clear_index_(-1),
      suggestions_options_index_(-1) {
}

void AutoFillHelper::QueryAutoFillSuggestions(const WebNode& node,
                                              const WebString& name,
                                              const WebString& value) {
  static int query_counter = 0;
  autofill_query_id_ = query_counter++;
  autofill_query_node_ = node;

  const WebFormControlElement& element = node.toConst<WebFormControlElement>();
  webkit_glue::FormField field;
  FormManager::WebFormControlElementToFormField(element, true, &field);

  // WebFormControlElementToFormField does not scrape the DOM for the field
  // label, so find the label here.
  // TODO(jhawkins): Add form and field identities so we can use the cached form
  // data in FormManager.
  field.set_label(FormManager::LabelForElement(element));

  bool form_autofilled = form_manager_.FormWithNodeIsAutoFilled(node);
  render_view_->Send(new ViewHostMsg_QueryFormFieldAutoFill(
      render_view_->routing_id(), autofill_query_id_, form_autofilled, field));
}

void AutoFillHelper::RemoveAutocompleteSuggestion(
    const WebKit::WebString& name, const WebKit::WebString& value) {
  // The index of clear & options will have shifted down.
  if (suggestions_clear_index_ != -1)
    suggestions_clear_index_--;
  if (suggestions_options_index_ != -1)
    suggestions_options_index_--;

  render_view_->Send(new ViewHostMsg_RemoveAutocompleteEntry(
      render_view_->routing_id(), name, value));
}

void AutoFillHelper::SuggestionsReceived(int query_id,
                                         const std::vector<string16>& values,
                                         const std::vector<string16>& labels,
                                         const std::vector<string16>& icons,
                                         const std::vector<int>& unique_ids) {
  WebKit::WebView* web_view = render_view_->webview();
  if (!web_view || query_id != autofill_query_id_)
    return;

  if (values.empty()) {
    // No suggestions, any popup currently showing is obsolete.
    web_view->hidePopups();
    return;
  }

  std::vector<string16> v(values);
  std::vector<string16> l(labels);
  std::vector<string16> i(icons);
  std::vector<int> ids(unique_ids);
  int separator_index = -1;

  // The form has been auto-filled, so give the user the chance to clear the
  // form.  Append the 'Clear form' menu item.
  if (form_manager_.FormWithNodeIsAutoFilled(autofill_query_node_)) {
    v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
    l.push_back(string16());
    i.push_back(string16());
    ids.push_back(0);
    suggestions_clear_index_ = v.size() - 1;
    separator_index = values.size();
  }

  // Only include "AutoFill Options" special menu item if we have AutoFill
  // items, identified by |unique_ids| having at least one valid value.
  bool show_options = false;
  for (size_t i = 0; i < ids.size(); ++i) {
    if (ids[i] != 0) {
      show_options = true;
      break;
    }
  }
  if (show_options) {
    // Append the 'AutoFill Options...' menu item.
    v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS));
    l.push_back(string16());
    i.push_back(string16());
    ids.push_back(0);
    suggestions_options_index_ = v.size() - 1;
    separator_index = values.size();
  }

  // Send to WebKit for display.
  if (!v.empty()) {
    web_view->applyAutoFillSuggestions(
        autofill_query_node_, v, l, i, ids, separator_index);
  }
}

void AutoFillHelper::FormDataFilled(int query_id,
                                    const webkit_glue::FormData& form) {
  if (!render_view_->webview() || query_id != autofill_query_id_)
    return;

  switch (autofill_action_) {
    case AUTOFILL_FILL:
      form_manager_.FillForm(form, autofill_query_node_);
      break;
    case AUTOFILL_PREVIEW:
      form_manager_.PreviewForm(form);
      break;
    default:
      NOTREACHED();
  }
  autofill_action_ = AUTOFILL_NONE;
}

void AutoFillHelper::DidSelectAutoFillSuggestion(const WebNode& node,
                                                 const WebString& value,
                                                 const WebString& label,
                                                 int unique_id) {
  DidClearAutoFillSelection(node);
  QueryAutoFillFormData(node, value, label, unique_id, AUTOFILL_PREVIEW);
}

void AutoFillHelper::DidAcceptAutoFillSuggestion(const WebNode& node,
                                                 const WebString& value,
                                                 const WebString& label,
                                                 int unique_id,
                                                 unsigned index) {
  if (suggestions_options_index_ != -1 &&
      index == static_cast<unsigned>(suggestions_options_index_)) {
    // User selected 'AutoFill Options'.
    render_view_->Send(new ViewHostMsg_ShowAutoFillDialog(
        render_view_->routing_id()));
  } else if (suggestions_clear_index_ != -1 &&
             index == static_cast<unsigned>(suggestions_clear_index_)) {
    // User selected 'Clear form'.
    // The form has been auto-filled, so give the user the chance to clear the
    // form.
    form_manager_.ClearFormWithNode(node);
  } else if (form_manager_.FormWithNodeIsAutoFilled(node) || !unique_id) {
    // User selected an Autocomplete entry, so we fill directly.
    WebInputElement element = node.toConst<WebInputElement>();

    // Set the suggested value to update input element value immediately in UI.
    // The |setValue| call has update delayed until element loses focus.
    string16 substring = value;
    substring = substring.substr(0, element.maxLength());
    element.setSuggestedValue(substring);
    element.setValue(substring);

    WebFrame* webframe = node.document().frame();
    if (webframe)
      webframe->notifiyPasswordListenerOfAutocomplete(element);
  } else {
    // Fill the values for the whole form.
    QueryAutoFillFormData(node, value, label, unique_id, AUTOFILL_FILL);
  }

  suggestions_clear_index_ = -1;
  suggestions_options_index_ = -1;
}

void AutoFillHelper::DidClearAutoFillSelection(const WebNode& node) {
  form_manager_.ClearPreviewedFormWithNode(node);
}

void AutoFillHelper::FrameContentsAvailable(WebFrame* frame) {
  form_manager_.ExtractForms(frame);
  SendForms(frame);
}

void AutoFillHelper::FrameWillClose(WebFrame* frame) {
  form_manager_.ResetFrame(frame);
}

void AutoFillHelper::FrameDetached(WebFrame* frame) {
  form_manager_.ResetFrame(frame);
}

void AutoFillHelper::TextDidChangeInTextField(const WebInputElement& element) {
  ShowSuggestions(element, false, true);
}

void AutoFillHelper::InputElementClicked(const WebInputElement& element,
                                         bool already_focused) {
  if (already_focused)
    ShowSuggestions(element, true, false);
}

void AutoFillHelper::ShowSuggestions(
    const WebInputElement& const_element,
    bool autofill_on_empty_values,
    bool requires_caret_at_end) {
  // We need to call non-const methods.
  WebInputElement element(const_element);
  if (!element.isEnabledFormControl() ||
      element.inputType() != WebInputElement::Text ||
      element.inputType() == WebInputElement::Password ||
      !element.autoComplete() || element.isReadOnly()) {
    return;
  }

  WebString name = element.nameForAutofill();
  if (name.isEmpty())  // If the field has no name, then we won't have values.
    return;

  // Don't attempt to autofill with values that are too large.
  WebString value = element.value();
  if (value.length() > kMaximumTextSizeForAutoFill)
    return;

  if (!autofill_on_empty_values && value.isEmpty())
    return;

  if (requires_caret_at_end &&
      (element.selectionStart() != element.selectionEnd() ||
       element.selectionEnd() != static_cast<int>(value.length()))) {
    return;
  }

  QueryAutoFillSuggestions(element, name, value);
}

void AutoFillHelper::QueryAutoFillFormData(const WebNode& node,
                                           const WebString& value,
                                           const WebString& label,
                                           int unique_id,
                                           AutoFillAction action) {
  static int query_counter = 0;
  autofill_query_id_ = query_counter++;

  webkit_glue::FormData form;
  const WebInputElement element = node.toConst<WebInputElement>();
  if (!form_manager_.FindFormWithFormControlElement(
          element, FormManager::REQUIRE_NONE, &form))
    return;

  autofill_action_ = action;
  render_view_->Send(new ViewHostMsg_FillAutoFillFormData(
      render_view_->routing_id(), autofill_query_id_, form, value, label,
      unique_id));
}

void AutoFillHelper::SendForms(WebFrame* frame) {
  // TODO(jhawkins): Use FormManager once we have strict ordering of form
  // control elements in the cache.
  WebKit::WebVector<WebFormElement> web_forms;
  frame->forms(web_forms);

  std::vector<webkit_glue::FormData> forms;
  for (size_t i = 0; i < web_forms.size(); ++i) {
    const WebFormElement& web_form = web_forms[i];

    webkit_glue::FormData form;
    if (FormManager::WebFormElementToFormData(
            web_form, FormManager::REQUIRE_NONE, false, &form)) {
      forms.push_back(form);
    }
  }

  if (!forms.empty()) {
    render_view_->Send(new ViewHostMsg_FormsSeen(render_view_->routing_id(),
                                                 forms));
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
#include <objidl.h>
#include <mlang.h>
#endif

#include "chrome/renderer/render_process_impl.h"

#include "app/surface/transport_dib.h"
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/histogram.h"
#include "base/path_service.h"
#include "base/sys_info.h"
// TODO(jar): DNS calls should be renderer specific, not including browser.
#include "chrome/browser/net/dns_global.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/nacl_types.h"
#include "chrome/renderer/render_view.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_message_utils.h"
#include "media/base/media.h"
#include "media/base/media_switches.h"
#include "native_client/src/trusted/plugin/nacl_entry_points.h"
#include "webkit/glue/plugins/plugin_instance.h"
#include "webkit/glue/plugins/plugin_lib.h"
#include "webkit/glue/webkit_glue.h"

#if defined(OS_MACOSX)
#include "base/mac_util.h"
#endif

namespace {

bool LaunchNaClProcess(const char* url,
                       int imc_fd,
                       nacl::Handle* imc_handle,
                       nacl::Handle* nacl_process_handle,
                       int* nacl_process_id) {
  // TODO(gregoryd): nacl::FileDescriptor will be soon merged with
  // base::FileDescriptor
  nacl::FileDescriptor imc_descriptor;
  base::ProcessHandle nacl_process;
  if (!RenderThread::current()->Send(
    new ViewHostMsg_LaunchNaCl(ASCIIToWide(url),
        imc_fd,
        &imc_descriptor,
        &nacl_process,
        reinterpret_cast<base::ProcessId*>(nacl_process_id)))) {
    return false;
  }
  *imc_handle = nacl::ToNativeHandle(imc_descriptor);
  *nacl_process_handle = nacl_process;
  return true;
}

}  // namespace

//-----------------------------------------------------------------------------

RenderProcessImpl::RenderProcessImpl()
    : ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_(
          base::TimeDelta::FromSeconds(5),
          this, &RenderProcessImpl::ClearTransportDIBCache)),
      transport_dib_next_sequence_number_(0) {
  in_process_plugins_ = InProcessPlugins();
  for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i)
    shared_mem_cache_[i] = NULL;

#if defined(OS_WIN)
  // HACK:  See http://b/issue?id=1024307 for rationale.
  if (GetModuleHandle(L"LPK.DLL") == NULL) {
    // Makes sure lpk.dll is loaded by gdi32 to make sure ExtTextOut() works
    // when buffering into a EMF buffer for printing.
    typedef BOOL (__stdcall *GdiInitializeLanguagePack)(int LoadedShapingDLLs);
    GdiInitializeLanguagePack gdi_init_lpk =
        reinterpret_cast<GdiInitializeLanguagePack>(GetProcAddress(
            GetModuleHandle(L"GDI32.DLL"),
            "GdiInitializeLanguagePack"));
    DCHECK(gdi_init_lpk);
    if (gdi_init_lpk) {
      gdi_init_lpk(0);
    }
  }
#endif

  // Out of process dev tools rely upon auto break behavior.
  webkit_glue::SetJavaScriptFlags(
      L"--debugger-auto-break"
      // Enable lazy in-memory profiling.
      L" --prof --prof-lazy --logfile=* --compress-log");

  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
  if (command_line.HasSwitch(switches::kJavaScriptFlags)) {
    webkit_glue::SetJavaScriptFlags(
      command_line.GetSwitchValue(switches::kJavaScriptFlags));
  }

  if (command_line.HasSwitch(switches::kEnableWatchdog)) {
    // TODO(JAR): Need to implement renderer IO msgloop watchdog.
  }

  if (command_line.HasSwitch(switches::kDumpHistogramsOnExit)) {
    StatisticsRecorder::set_dump_on_exit(true);
  }

#ifndef DISABLE_NACL
  if (command_line.HasSwitch(switches::kInternalNaCl))
    RegisterInternalNaClPlugin(LaunchNaClProcess);
#endif

  if (!command_line.HasSwitch(switches::kDisableByteRangeSupport)) {
    webkit_glue::SetMediaCacheEnabled(true);
  }

#if defined(OS_MACOSX)
  FilePath bundle_path = mac_util::MainAppBundlePath();

  initialized_media_library_ =
     media::InitializeMediaLibrary(bundle_path.Append("Libraries"));
#else
  FilePath module_path;
  initialized_media_library_ =
      PathService::Get(base::DIR_MODULE, &module_path) &&
      media::InitializeMediaLibrary(module_path);

  // TODO(hclam): Add more checks here. Currently this is not used.
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableOpenMax)) {
    media::InitializeOpenMaxLibrary(module_path);
  }
#endif

  // Load the pdf plugin before the sandbox is turned on.
  FilePath pdf;
  if (PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf)) {
    static scoped_refptr<NPAPI::PluginLib> pdf_lib =
        NPAPI::PluginLib::CreatePluginLib(pdf);
    // Actually load the plugin.
    pdf_lib->NP_Initialize();
    // Keep an instance around to prevent the plugin unloading after a pdf is
    // closed.
    // Don't use scoped_ptr here because then get asserts on process shut down
    // when running in --single-process.
    static NPAPI::PluginInstance* instance = pdf_lib->CreateInstance("");
    instance->plugin_lib();  // Quiet unused variable warnings in gcc.
  }
}

RenderProcessImpl::~RenderProcessImpl() {
  // TODO(port): Try and limit what we pull in for our non-Win unit test bundle.
#ifndef NDEBUG
  // log important leaked objects
  webkit_glue::CheckForLeaks();
#endif

  GetShutDownEvent()->Signal();
  ClearTransportDIBCache();
}

bool RenderProcessImpl::InProcessPlugins() {
  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
#if defined(OS_LINUX)
  // Plugin processes require a UI message loop, and the Linux message loop
  // implementation only allows one UI loop per process.
  if (command_line.HasSwitch(switches::kInProcessPlugins))
    NOTIMPLEMENTED() << ": in process plugins not supported on Linux";
  return command_line.HasSwitch(switches::kInProcessPlugins);
#else
  return command_line.HasSwitch(switches::kInProcessPlugins) ||
         command_line.HasSwitch(switches::kSingleProcess);
#endif
}

// -----------------------------------------------------------------------------
// Platform specific code for dealing with bitmap transport...

TransportDIB* RenderProcessImpl::CreateTransportDIB(size_t size) {
#if defined(OS_WIN) || defined(OS_LINUX)
  // Windows and Linux create transport DIBs inside the renderer
  return TransportDIB::Create(size, transport_dib_next_sequence_number_++);
#elif defined(OS_MACOSX)  // defined(OS_WIN) || defined(OS_LINUX)
  // Mac creates transport DIBs in the browser, so we need to do a sync IPC to
  // get one.  The TransportDIB is cached in the browser.
  TransportDIB::Handle handle;
  IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, true, &handle);
  if (!main_thread()->Send(msg))
    return NULL;
  if (handle.fd < 0)
    return NULL;
  return TransportDIB::Map(handle);
#endif  // defined(OS_MACOSX)
}

void RenderProcessImpl::FreeTransportDIB(TransportDIB* dib) {
  if (!dib)
    return;

#if defined(OS_MACOSX)
  // On Mac we need to tell the browser that it can drop a reference to the
  // shared memory.
  IPC::Message* msg = new ViewHostMsg_FreeTransportDIB(dib->id());
  main_thread()->Send(msg);
#endif

  delete dib;
}

// -----------------------------------------------------------------------------

skia::PlatformCanvas* RenderProcessImpl::GetDrawingCanvas(
    TransportDIB** memory, const gfx::Rect& rect) {
  int width = rect.width();
  int height = rect.height();
  const size_t stride = skia::PlatformCanvas::StrideForWidth(rect.width());
#if defined(OS_LINUX)
  const size_t max_size = base::SysInfo::MaxSharedMemorySize();
#else
  const size_t max_size = 0;
#endif

  // If the requested size is too big, reduce the height. Ideally we might like
  // to reduce the width as well to make the size reduction more "balanced", but
  // it rarely comes up in practice.
  if ((max_size != 0) && (height * stride > max_size))
    height = max_size / stride;

  const size_t size = height * stride;

  if (!GetTransportDIBFromCache(memory, size)) {
    *memory = CreateTransportDIB(size);
    if (!*memory)
      return false;
  }

  return (*memory)->GetPlatformCanvas(width, height);
}

void RenderProcessImpl::ReleaseTransportDIB(TransportDIB* mem) {
  if (PutSharedMemInCache(mem)) {
    shared_mem_cache_cleaner_.Reset();
    return;
  }

  FreeTransportDIB(mem);
}

bool RenderProcessImpl::UseInProcessPlugins() const {
  return in_process_plugins_;
}

bool RenderProcessImpl::HasInitializedMediaLibrary() const {
  return initialized_media_library_;
}

bool RenderProcessImpl::GetTransportDIBFromCache(TransportDIB** mem,
                                             size_t size) {
  // look for a cached object that is suitable for the requested size.
  for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) {
    if (shared_mem_cache_[i] &&
        size <= shared_mem_cache_[i]->size()) {
      *mem = shared_mem_cache_[i];
      shared_mem_cache_[i] = NULL;
      return true;
    }
  }

  return false;
}

int RenderProcessImpl::FindFreeCacheSlot(size_t size) {
  // simple algorithm:
  //  - look for an empty slot to store mem, or
  //  - if full, then replace smallest entry which is smaller than |size|
  for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) {
    if (shared_mem_cache_[i] == NULL)
      return i;
  }

  size_t smallest_size = size;
  int smallest_index = -1;

  for (size_t i = 1; i < arraysize(shared_mem_cache_); ++i) {
    const size_t entry_size = shared_mem_cache_[i]->size();
    if (entry_size < smallest_size) {
      smallest_size = entry_size;
      smallest_index = i;
    }
  }

  if (smallest_index != -1) {
    FreeTransportDIB(shared_mem_cache_[smallest_index]);
    shared_mem_cache_[smallest_index] = NULL;
  }

  return smallest_index;
}

bool RenderProcessImpl::PutSharedMemInCache(TransportDIB* mem) {
  const int slot = FindFreeCacheSlot(mem->size());
  if (slot == -1)
    return false;

  shared_mem_cache_[slot] = mem;
  return true;
}

void RenderProcessImpl::ClearTransportDIBCache() {
  for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) {
    if (shared_mem_cache_[i]) {
      FreeTransportDIB(shared_mem_cache_[i]);
      shared_mem_cache_[i] = NULL;
    }
  }
}
<commit_msg>Temporary changes for plugin.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
#include <objidl.h>
#include <mlang.h>
#endif

#include "chrome/renderer/render_process_impl.h"

#include "app/surface/transport_dib.h"
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/histogram.h"
#include "base/path_service.h"
#include "base/sys_info.h"
// TODO(jar): DNS calls should be renderer specific, not including browser.
#include "chrome/browser/net/dns_global.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/nacl_types.h"
#include "chrome/renderer/render_view.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_message_utils.h"
#include "media/base/media.h"
#include "media/base/media_switches.h"
#include "native_client/src/trusted/plugin/nacl_entry_points.h"
#include "webkit/glue/plugins/plugin_instance.h"
#include "webkit/glue/plugins/plugin_lib.h"
#include "webkit/glue/webkit_glue.h"

#if defined(OS_MACOSX)
#include "base/mac_util.h"
#elif defined(OS_WIN)
#include "base/iat_patch.h"
#endif

namespace {

bool LaunchNaClProcess(const char* url,
                       int imc_fd,
                       nacl::Handle* imc_handle,
                       nacl::Handle* nacl_process_handle,
                       int* nacl_process_id) {
  // TODO(gregoryd): nacl::FileDescriptor will be soon merged with
  // base::FileDescriptor
  nacl::FileDescriptor imc_descriptor;
  base::ProcessHandle nacl_process;
  if (!RenderThread::current()->Send(
    new ViewHostMsg_LaunchNaCl(ASCIIToWide(url),
        imc_fd,
        &imc_descriptor,
        &nacl_process,
        reinterpret_cast<base::ProcessId*>(nacl_process_id)))) {
    return false;
  }
  *imc_handle = nacl::ToNativeHandle(imc_descriptor);
  *nacl_process_handle = nacl_process;
  return true;
}

}  // namespace

//-----------------------------------------------------------------------------

#if defined(OS_WIN)

static iat_patch::IATPatchFunction g_iat_patch_createdca;
HDC WINAPI CreateDCAPatch(LPCSTR driver_name,
                          LPCSTR device_name,
                          LPCSTR output,
                          const void* init_data) {
  DCHECK(std::string("DISPLAY") == std::string(driver_name));
  DCHECK(!device_name);
  DCHECK(!output);
  DCHECK(!init_data);

  // CreateDC fails behind the sandbox, but not CreateCompatibleDC.
  return CreateCompatibleDC(NULL);
}

#endif

RenderProcessImpl::RenderProcessImpl()
    : ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_(
          base::TimeDelta::FromSeconds(5),
          this, &RenderProcessImpl::ClearTransportDIBCache)),
      transport_dib_next_sequence_number_(0) {
  in_process_plugins_ = InProcessPlugins();
  for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i)
    shared_mem_cache_[i] = NULL;

#if defined(OS_WIN)
  // HACK:  See http://b/issue?id=1024307 for rationale.
  if (GetModuleHandle(L"LPK.DLL") == NULL) {
    // Makes sure lpk.dll is loaded by gdi32 to make sure ExtTextOut() works
    // when buffering into a EMF buffer for printing.
    typedef BOOL (__stdcall *GdiInitializeLanguagePack)(int LoadedShapingDLLs);
    GdiInitializeLanguagePack gdi_init_lpk =
        reinterpret_cast<GdiInitializeLanguagePack>(GetProcAddress(
            GetModuleHandle(L"GDI32.DLL"),
            "GdiInitializeLanguagePack"));
    DCHECK(gdi_init_lpk);
    if (gdi_init_lpk) {
      gdi_init_lpk(0);
    }
  }
#endif

  // Out of process dev tools rely upon auto break behavior.
  webkit_glue::SetJavaScriptFlags(
      L"--debugger-auto-break"
      // Enable lazy in-memory profiling.
      L" --prof --prof-lazy --logfile=* --compress-log");

  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
  if (command_line.HasSwitch(switches::kJavaScriptFlags)) {
    webkit_glue::SetJavaScriptFlags(
      command_line.GetSwitchValue(switches::kJavaScriptFlags));
  }

  if (command_line.HasSwitch(switches::kEnableWatchdog)) {
    // TODO(JAR): Need to implement renderer IO msgloop watchdog.
  }

  if (command_line.HasSwitch(switches::kDumpHistogramsOnExit)) {
    StatisticsRecorder::set_dump_on_exit(true);
  }

#ifndef DISABLE_NACL
  if (command_line.HasSwitch(switches::kInternalNaCl))
    RegisterInternalNaClPlugin(LaunchNaClProcess);
#endif

  if (!command_line.HasSwitch(switches::kDisableByteRangeSupport)) {
    webkit_glue::SetMediaCacheEnabled(true);
  }

#if defined(OS_MACOSX)
  FilePath bundle_path = mac_util::MainAppBundlePath();

  initialized_media_library_ =
     media::InitializeMediaLibrary(bundle_path.Append("Libraries"));
#else
  FilePath module_path;
  initialized_media_library_ =
      PathService::Get(base::DIR_MODULE, &module_path) &&
      media::InitializeMediaLibrary(module_path);

  // TODO(hclam): Add more checks here. Currently this is not used.
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableOpenMax)) {
    media::InitializeOpenMaxLibrary(module_path);
  }
#endif

  // Load the pdf plugin before the sandbox is turned on.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kInternalPDF)) {
    FilePath pdf;
    if (PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf)) {
      static scoped_refptr<NPAPI::PluginLib> pdf_lib =
          NPAPI::PluginLib::CreatePluginLib(pdf);
      // Actually load the plugin.
      pdf_lib->NP_Initialize();
      // Keep an instance around to prevent the plugin unloading after a pdf is
      // closed.
      // Don't use scoped_ptr here because then get asserts on process shut down
      // when running in --single-process.
      static NPAPI::PluginInstance* instance = pdf_lib->CreateInstance("");
      instance->plugin_lib();  // Quiet unused variable warnings in gcc.

#if defined(OS_WIN)
      g_iat_patch_createdca.Patch(
          pdf_lib->plugin_info().path.value().c_str(),
          "gdi32.dll", "CreateDCA", CreateDCAPatch);
#endif
    }
  }
}

RenderProcessImpl::~RenderProcessImpl() {
  // TODO(port): Try and limit what we pull in for our non-Win unit test bundle.
#ifndef NDEBUG
  // log important leaked objects
  webkit_glue::CheckForLeaks();
#endif

  GetShutDownEvent()->Signal();
  ClearTransportDIBCache();
}

bool RenderProcessImpl::InProcessPlugins() {
  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
#if defined(OS_LINUX)
  // Plugin processes require a UI message loop, and the Linux message loop
  // implementation only allows one UI loop per process.
  if (command_line.HasSwitch(switches::kInProcessPlugins))
    NOTIMPLEMENTED() << ": in process plugins not supported on Linux";
  return command_line.HasSwitch(switches::kInProcessPlugins);
#else
  return command_line.HasSwitch(switches::kInProcessPlugins) ||
         command_line.HasSwitch(switches::kSingleProcess);
#endif
}

// -----------------------------------------------------------------------------
// Platform specific code for dealing with bitmap transport...

TransportDIB* RenderProcessImpl::CreateTransportDIB(size_t size) {
#if defined(OS_WIN) || defined(OS_LINUX)
  // Windows and Linux create transport DIBs inside the renderer
  return TransportDIB::Create(size, transport_dib_next_sequence_number_++);
#elif defined(OS_MACOSX)  // defined(OS_WIN) || defined(OS_LINUX)
  // Mac creates transport DIBs in the browser, so we need to do a sync IPC to
  // get one.  The TransportDIB is cached in the browser.
  TransportDIB::Handle handle;
  IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, true, &handle);
  if (!main_thread()->Send(msg))
    return NULL;
  if (handle.fd < 0)
    return NULL;
  return TransportDIB::Map(handle);
#endif  // defined(OS_MACOSX)
}

void RenderProcessImpl::FreeTransportDIB(TransportDIB* dib) {
  if (!dib)
    return;

#if defined(OS_MACOSX)
  // On Mac we need to tell the browser that it can drop a reference to the
  // shared memory.
  IPC::Message* msg = new ViewHostMsg_FreeTransportDIB(dib->id());
  main_thread()->Send(msg);
#endif

  delete dib;
}

// -----------------------------------------------------------------------------

skia::PlatformCanvas* RenderProcessImpl::GetDrawingCanvas(
    TransportDIB** memory, const gfx::Rect& rect) {
  int width = rect.width();
  int height = rect.height();
  const size_t stride = skia::PlatformCanvas::StrideForWidth(rect.width());
#if defined(OS_LINUX)
  const size_t max_size = base::SysInfo::MaxSharedMemorySize();
#else
  const size_t max_size = 0;
#endif

  // If the requested size is too big, reduce the height. Ideally we might like
  // to reduce the width as well to make the size reduction more "balanced", but
  // it rarely comes up in practice.
  if ((max_size != 0) && (height * stride > max_size))
    height = max_size / stride;

  const size_t size = height * stride;

  if (!GetTransportDIBFromCache(memory, size)) {
    *memory = CreateTransportDIB(size);
    if (!*memory)
      return false;
  }

  return (*memory)->GetPlatformCanvas(width, height);
}

void RenderProcessImpl::ReleaseTransportDIB(TransportDIB* mem) {
  if (PutSharedMemInCache(mem)) {
    shared_mem_cache_cleaner_.Reset();
    return;
  }

  FreeTransportDIB(mem);
}

bool RenderProcessImpl::UseInProcessPlugins() const {
  return in_process_plugins_;
}

bool RenderProcessImpl::HasInitializedMediaLibrary() const {
  return initialized_media_library_;
}

bool RenderProcessImpl::GetTransportDIBFromCache(TransportDIB** mem,
                                             size_t size) {
  // look for a cached object that is suitable for the requested size.
  for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) {
    if (shared_mem_cache_[i] &&
        size <= shared_mem_cache_[i]->size()) {
      *mem = shared_mem_cache_[i];
      shared_mem_cache_[i] = NULL;
      return true;
    }
  }

  return false;
}

int RenderProcessImpl::FindFreeCacheSlot(size_t size) {
  // simple algorithm:
  //  - look for an empty slot to store mem, or
  //  - if full, then replace smallest entry which is smaller than |size|
  for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) {
    if (shared_mem_cache_[i] == NULL)
      return i;
  }

  size_t smallest_size = size;
  int smallest_index = -1;

  for (size_t i = 1; i < arraysize(shared_mem_cache_); ++i) {
    const size_t entry_size = shared_mem_cache_[i]->size();
    if (entry_size < smallest_size) {
      smallest_size = entry_size;
      smallest_index = i;
    }
  }

  if (smallest_index != -1) {
    FreeTransportDIB(shared_mem_cache_[smallest_index]);
    shared_mem_cache_[smallest_index] = NULL;
  }

  return smallest_index;
}

bool RenderProcessImpl::PutSharedMemInCache(TransportDIB* mem) {
  const int slot = FindFreeCacheSlot(mem->size());
  if (slot == -1)
    return false;

  shared_mem_cache_[slot] = mem;
  return true;
}

void RenderProcessImpl::ClearTransportDIBCache() {
  for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) {
    if (shared_mem_cache_[i]) {
      FreeTransportDIB(shared_mem_cache_[i]);
      shared_mem_cache_[i] = NULL;
    }
  }
}
<|endoftext|>