<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/browser/chromeos/login/login_utils.h"

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/lock.h"
#include "base/nss_util.h"
#include "base/path_service.h"
#include "base/scoped_ptr.h"
#include "base/singleton.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_init.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/chromeos/cros/login_library.h"
#include "chrome/browser/chromeos/cros/network_library.h"
#include "chrome/browser/chromeos/external_cookie_handler.h"
#include "chrome/browser/chromeos/input_method/input_method_util.h"
#include "chrome/browser/chromeos/login/cookie_fetcher.h"
#include "chrome/browser/chromeos/login/google_authenticator.h"
#include "chrome/browser/chromeos/login/ownership_service.h"
#include "chrome/browser/chromeos/login/user_image_downloader.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/net/gaia/token_service.h"
#include "chrome/browser/prefs/pref_member.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/common/net/url_request_context_getter.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "googleurl/src/gurl.h"
#include "net/base/cookie_store.h"
#include "net/url_request/url_request_context.h"
#include "views/widget/widget_gtk.h"

namespace chromeos {

namespace {

// Prefix for Auth token received from ClientLogin request.
const char kAuthPrefix[] = "Auth=";
// Suffix for Auth token received from ClientLogin request.
const char kAuthSuffix[] = "\n";

}  // namespace

class LoginUtilsImpl : public LoginUtils,
                       public NotificationObserver {
 public:
  LoginUtilsImpl()
      : browser_launch_enabled_(true) {
    registrar_.Add(
        this,
        NotificationType::LOGIN_USER_CHANGED,
        NotificationService::AllSources());
  }

  // Invoked after the user has successfully logged in. This launches a browser
  // and does other bookkeeping after logging in.
  virtual void CompleteLogin(const std::string& username,
      const GaiaAuthConsumer::ClientLoginResult& credentials);

  // Invoked after the tmpfs is successfully mounted.
  // Launches a browser in the off the record (incognito) mode.
  virtual void CompleteOffTheRecordLogin(const GURL& start_url);

  // Creates and returns the authenticator to use. The caller owns the returned
  // Authenticator and must delete it when done.
  virtual Authenticator* CreateAuthenticator(LoginStatusConsumer* consumer);

  // Used to postpone browser launch via DoBrowserLaunch() if some post
  // login screen is to be shown.
  virtual void EnableBrowserLaunch(bool enable);

  // Returns if browser launch enabled now or not.
  virtual bool IsBrowserLaunchEnabled() const;

  // Returns auth token for 'cp' Contacts service.
  virtual const std::string& GetAuthToken() const { return auth_token_; }

  // NotificationObserver implementation.
  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details);

 private:
  // Attempt to connect to the preferred network if available.
  void ConnectToPreferredNetwork();

  NotificationRegistrar registrar_;

  // Indicates if DoBrowserLaunch will actually launch the browser or not.
  bool browser_launch_enabled_;

  // Auth token for Contacts service. Received by GoogleAuthenticator as
  // part of ClientLogin response.
  std::string auth_token_;

  DISALLOW_COPY_AND_ASSIGN(LoginUtilsImpl);
};

class LoginUtilsWrapper {
 public:
  LoginUtilsWrapper() {}

  LoginUtils* get() {
    AutoLock create(create_lock_);
    if (!ptr_.get())
      reset(new LoginUtilsImpl);
    return ptr_.get();
  }

  void reset(LoginUtils* ptr) {
    ptr_.reset(ptr);
  }

 private:
  Lock create_lock_;
  scoped_ptr<LoginUtils> ptr_;

  DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper);
};

void LoginUtilsImpl::CompleteLogin(const std::string& username,
    const GaiaAuthConsumer::ClientLoginResult& credentials) {

  LOG(INFO) << "Completing login for " << username;

  if (CrosLibrary::Get()->EnsureLoaded())
    CrosLibrary::Get()->GetLoginLibrary()->StartSession(username, "");

  const std::vector<UserManager::User>& users = UserManager::Get()->GetUsers();

  bool first_login = true;
  for (std::vector<UserManager::User>::const_iterator it = users.begin();
       it < users.end(); ++it) {
    std::string user_email = it->email();
    if (username == user_email) {
      first_login = false;
      break;
    }
  }

  UserManager::Get()->UserLoggedIn(username);
  ConnectToPreferredNetwork();

  // Now launch the initial browser window.
  FilePath user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  ProfileManager* profile_manager = g_browser_process->profile_manager();
  // The default profile will have been changed because the ProfileManager
  // will process the notification that the UserManager sends out.
  Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);

  logging::RedirectChromeLogging(
      user_data_dir.Append(profile_manager->GetCurrentProfileDir()),
      *(CommandLine::ForCurrentProcess()),
      logging::DELETE_OLD_LOG_FILE);

  // Supply credentials for sync and others to use. Load tokens from disk.
  TokenService* token_service = profile->GetTokenService();
  token_service->Initialize(GaiaConstants::kChromeOSSource,
                            profile);
  token_service->LoadTokensFromDB();
  token_service->UpdateCredentials(credentials);
  if (token_service->AreCredentialsValid()) {
    token_service->StartFetchingTokens();
  }

  // Attempt to take ownership; this will fail if device is already owned.
  OwnershipService::GetSharedInstance()->StartTakeOwnershipAttempt();

  // Take the credentials passed in and try to exchange them for
  // full-fledged Google authentication cookies.  This is
  // best-effort; it's possible that we'll fail due to network
  // troubles or some such.  Either way, |cf| will call
  // DoBrowserLaunch on the UI thread when it's done, and then
  // delete itself.
  CookieFetcher* cf = new CookieFetcher(profile);
  cf->AttemptFetch(credentials.data);
  auth_token_ = credentials.token;

  static const char kFallbackInputMethodLocale[] = "en-US";
  if (first_login) {
    std::string locale(g_browser_process->GetApplicationLocale());
    // Add input methods based on the application locale when the user first
    // logs in. For instance, if the user chooses Japanese as the UI
    // language at the first login, we'll add input methods associated with
    // Japanese, such as mozc.
    if (locale != kFallbackInputMethodLocale) {
      StringPrefMember language_preload_engines;
      language_preload_engines.Init(prefs::kLanguagePreloadEngines,
                                    profile->GetPrefs(), this);
      StringPrefMember language_preferred_languages;
      language_preferred_languages.Init(prefs::kLanguagePreferredLanguages,
                                        profile->GetPrefs(), this);

      std::string preload_engines(language_preload_engines.GetValue());
      std::vector<std::string> input_method_ids;
      input_method::GetInputMethodIdsFromLanguageCode(
          locale, input_method::kAllInputMethods, &input_method_ids);
      if (!input_method_ids.empty()) {
        if (!preload_engines.empty())
          preload_engines += ',';
        preload_engines += input_method_ids[0];
      }
      language_preload_engines.SetValue(preload_engines);

      // Add the UI language to the preferred languages the user first logs in.
      std::string preferred_languages(locale);
      preferred_languages += ",";
      preferred_languages += kFallbackInputMethodLocale;
      language_preferred_languages.SetValue(preferred_languages);
    }
  }
}

void LoginUtilsImpl::CompleteOffTheRecordLogin(const GURL& start_url) {
  LOG(INFO) << "Completing off the record login";

  UserManager::Get()->OffTheRecordUserLoggedIn();
  ConnectToPreferredNetwork();

  if (CrosLibrary::Get()->EnsureLoaded()) {
    // For BWSI we ask session manager to restart Chrome with --bwsi flag.
    // We keep only some of the arguments of this process.
    static const char* kForwardSwitches[] = {
        switches::kLoggingLevel,
        switches::kEnableLogging,
        switches::kUserDataDir,
        switches::kScrollPixels,
        switches::kEnableGView,
        switches::kNoFirstRun,
        switches::kLoginProfile
    };
    const CommandLine& browser_command_line =
        *CommandLine::ForCurrentProcess();
    CommandLine command_line(browser_command_line.GetProgram());
    command_line.CopySwitchesFrom(browser_command_line,
                                  kForwardSwitches,
                                  arraysize(kForwardSwitches));
    command_line.AppendSwitch(switches::kBWSI);
    command_line.AppendSwitchASCII(
        switches::kLoginUser,
        UserManager::Get()->logged_in_user().email());
    if (start_url.is_valid())
      command_line.AppendArg(start_url.spec());
    CrosLibrary::Get()->GetLoginLibrary()->RestartJob(
        getpid(),
        command_line.command_line_string());
  }
}

Authenticator* LoginUtilsImpl::CreateAuthenticator(
    LoginStatusConsumer* consumer) {
  return new GoogleAuthenticator(consumer);
}

void LoginUtilsImpl::EnableBrowserLaunch(bool enable) {
  browser_launch_enabled_ = enable;
}

bool LoginUtilsImpl::IsBrowserLaunchEnabled() const {
  return browser_launch_enabled_;
}

void LoginUtilsImpl::Observe(NotificationType type,
                             const NotificationSource& source,
                             const NotificationDetails& details) {
  if (type == NotificationType::LOGIN_USER_CHANGED)
    base::OpenPersistentNSSDB();
}

void LoginUtilsImpl::ConnectToPreferredNetwork() {
  CrosLibrary::Get()->GetNetworkLibrary()->
      ConnectToPreferredNetworkIfAvailable();
}

LoginUtils* LoginUtils::Get() {
  return Singleton<LoginUtilsWrapper>::get()->get();
}

void LoginUtils::Set(LoginUtils* mock) {
  Singleton<LoginUtilsWrapper>::get()->reset(mock);
}

void LoginUtils::DoBrowserLaunch(Profile* profile) {
  // Browser launch was disabled due to some post login screen.
  if (!LoginUtils::Get()->IsBrowserLaunchEnabled())
    return;

  // Update command line in case loose values were added.
  CommandLine::ForCurrentProcess()->InitFromArgv(
      CommandLine::ForCurrentProcess()->argv());

  LOG(INFO) << "Launching browser...";
  BrowserInit browser_init;
  int return_code;
  browser_init.LaunchBrowser(*CommandLine::ForCurrentProcess(),
                             profile,
                             FilePath(),
                             true,
                             &return_code);
}

}  // namespace chromeos
<commit_msg>Passes switch to BWSI session that enables tabbed options.<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/browser/chromeos/login/login_utils.h"

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/lock.h"
#include "base/nss_util.h"
#include "base/path_service.h"
#include "base/scoped_ptr.h"
#include "base/singleton.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_init.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/chromeos/cros/login_library.h"
#include "chrome/browser/chromeos/cros/network_library.h"
#include "chrome/browser/chromeos/external_cookie_handler.h"
#include "chrome/browser/chromeos/input_method/input_method_util.h"
#include "chrome/browser/chromeos/login/cookie_fetcher.h"
#include "chrome/browser/chromeos/login/google_authenticator.h"
#include "chrome/browser/chromeos/login/ownership_service.h"
#include "chrome/browser/chromeos/login/user_image_downloader.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/net/gaia/token_service.h"
#include "chrome/browser/prefs/pref_member.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/common/net/url_request_context_getter.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "googleurl/src/gurl.h"
#include "net/base/cookie_store.h"
#include "net/url_request/url_request_context.h"
#include "views/widget/widget_gtk.h"

namespace chromeos {

namespace {

// Prefix for Auth token received from ClientLogin request.
const char kAuthPrefix[] = "Auth=";
// Suffix for Auth token received from ClientLogin request.
const char kAuthSuffix[] = "\n";

}  // namespace

class LoginUtilsImpl : public LoginUtils,
                       public NotificationObserver {
 public:
  LoginUtilsImpl()
      : browser_launch_enabled_(true) {
    registrar_.Add(
        this,
        NotificationType::LOGIN_USER_CHANGED,
        NotificationService::AllSources());
  }

  // Invoked after the user has successfully logged in. This launches a browser
  // and does other bookkeeping after logging in.
  virtual void CompleteLogin(const std::string& username,
      const GaiaAuthConsumer::ClientLoginResult& credentials);

  // Invoked after the tmpfs is successfully mounted.
  // Launches a browser in the off the record (incognito) mode.
  virtual void CompleteOffTheRecordLogin(const GURL& start_url);

  // Creates and returns the authenticator to use. The caller owns the returned
  // Authenticator and must delete it when done.
  virtual Authenticator* CreateAuthenticator(LoginStatusConsumer* consumer);

  // Used to postpone browser launch via DoBrowserLaunch() if some post
  // login screen is to be shown.
  virtual void EnableBrowserLaunch(bool enable);

  // Returns if browser launch enabled now or not.
  virtual bool IsBrowserLaunchEnabled() const;

  // Returns auth token for 'cp' Contacts service.
  virtual const std::string& GetAuthToken() const { return auth_token_; }

  // NotificationObserver implementation.
  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details);

 private:
  // Attempt to connect to the preferred network if available.
  void ConnectToPreferredNetwork();

  NotificationRegistrar registrar_;

  // Indicates if DoBrowserLaunch will actually launch the browser or not.
  bool browser_launch_enabled_;

  // Auth token for Contacts service. Received by GoogleAuthenticator as
  // part of ClientLogin response.
  std::string auth_token_;

  DISALLOW_COPY_AND_ASSIGN(LoginUtilsImpl);
};

class LoginUtilsWrapper {
 public:
  LoginUtilsWrapper() {}

  LoginUtils* get() {
    AutoLock create(create_lock_);
    if (!ptr_.get())
      reset(new LoginUtilsImpl);
    return ptr_.get();
  }

  void reset(LoginUtils* ptr) {
    ptr_.reset(ptr);
  }

 private:
  Lock create_lock_;
  scoped_ptr<LoginUtils> ptr_;

  DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper);
};

void LoginUtilsImpl::CompleteLogin(const std::string& username,
    const GaiaAuthConsumer::ClientLoginResult& credentials) {

  LOG(INFO) << "Completing login for " << username;

  if (CrosLibrary::Get()->EnsureLoaded())
    CrosLibrary::Get()->GetLoginLibrary()->StartSession(username, "");

  const std::vector<UserManager::User>& users = UserManager::Get()->GetUsers();

  bool first_login = true;
  for (std::vector<UserManager::User>::const_iterator it = users.begin();
       it < users.end(); ++it) {
    std::string user_email = it->email();
    if (username == user_email) {
      first_login = false;
      break;
    }
  }

  UserManager::Get()->UserLoggedIn(username);
  ConnectToPreferredNetwork();

  // Now launch the initial browser window.
  FilePath user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  ProfileManager* profile_manager = g_browser_process->profile_manager();
  // The default profile will have been changed because the ProfileManager
  // will process the notification that the UserManager sends out.
  Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);

  logging::RedirectChromeLogging(
      user_data_dir.Append(profile_manager->GetCurrentProfileDir()),
      *(CommandLine::ForCurrentProcess()),
      logging::DELETE_OLD_LOG_FILE);

  // Supply credentials for sync and others to use. Load tokens from disk.
  TokenService* token_service = profile->GetTokenService();
  token_service->Initialize(GaiaConstants::kChromeOSSource,
                            profile);
  token_service->LoadTokensFromDB();
  token_service->UpdateCredentials(credentials);
  if (token_service->AreCredentialsValid()) {
    token_service->StartFetchingTokens();
  }

  // Attempt to take ownership; this will fail if device is already owned.
  OwnershipService::GetSharedInstance()->StartTakeOwnershipAttempt();

  // Take the credentials passed in and try to exchange them for
  // full-fledged Google authentication cookies.  This is
  // best-effort; it's possible that we'll fail due to network
  // troubles or some such.  Either way, |cf| will call
  // DoBrowserLaunch on the UI thread when it's done, and then
  // delete itself.
  CookieFetcher* cf = new CookieFetcher(profile);
  cf->AttemptFetch(credentials.data);
  auth_token_ = credentials.token;

  static const char kFallbackInputMethodLocale[] = "en-US";
  if (first_login) {
    std::string locale(g_browser_process->GetApplicationLocale());
    // Add input methods based on the application locale when the user first
    // logs in. For instance, if the user chooses Japanese as the UI
    // language at the first login, we'll add input methods associated with
    // Japanese, such as mozc.
    if (locale != kFallbackInputMethodLocale) {
      StringPrefMember language_preload_engines;
      language_preload_engines.Init(prefs::kLanguagePreloadEngines,
                                    profile->GetPrefs(), this);
      StringPrefMember language_preferred_languages;
      language_preferred_languages.Init(prefs::kLanguagePreferredLanguages,
                                        profile->GetPrefs(), this);

      std::string preload_engines(language_preload_engines.GetValue());
      std::vector<std::string> input_method_ids;
      input_method::GetInputMethodIdsFromLanguageCode(
          locale, input_method::kAllInputMethods, &input_method_ids);
      if (!input_method_ids.empty()) {
        if (!preload_engines.empty())
          preload_engines += ',';
        preload_engines += input_method_ids[0];
      }
      language_preload_engines.SetValue(preload_engines);

      // Add the UI language to the preferred languages the user first logs in.
      std::string preferred_languages(locale);
      preferred_languages += ",";
      preferred_languages += kFallbackInputMethodLocale;
      language_preferred_languages.SetValue(preferred_languages);
    }
  }
}

void LoginUtilsImpl::CompleteOffTheRecordLogin(const GURL& start_url) {
  LOG(INFO) << "Completing off the record login";

  UserManager::Get()->OffTheRecordUserLoggedIn();
  ConnectToPreferredNetwork();

  if (CrosLibrary::Get()->EnsureLoaded()) {
    // For BWSI we ask session manager to restart Chrome with --bwsi flag.
    // We keep only some of the arguments of this process.
    static const char* kForwardSwitches[] = {
        switches::kLoggingLevel,
        switches::kEnableLogging,
        switches::kUserDataDir,
        switches::kScrollPixels,
        switches::kEnableGView,
        switches::kNoFirstRun,
        switches::kLoginProfile
    };
    const CommandLine& browser_command_line =
        *CommandLine::ForCurrentProcess();
    CommandLine command_line(browser_command_line.GetProgram());
    command_line.CopySwitchesFrom(browser_command_line,
                                  kForwardSwitches,
                                  arraysize(kForwardSwitches));
    command_line.AppendSwitch(switches::kBWSI);
    command_line.AppendSwitch(switches::kEnableTabbedOptions);
    command_line.AppendSwitchASCII(
        switches::kLoginUser,
        UserManager::Get()->logged_in_user().email());
    if (start_url.is_valid())
      command_line.AppendArg(start_url.spec());
    CrosLibrary::Get()->GetLoginLibrary()->RestartJob(
        getpid(),
        command_line.command_line_string());
  }
}

Authenticator* LoginUtilsImpl::CreateAuthenticator(
    LoginStatusConsumer* consumer) {
  return new GoogleAuthenticator(consumer);
}

void LoginUtilsImpl::EnableBrowserLaunch(bool enable) {
  browser_launch_enabled_ = enable;
}

bool LoginUtilsImpl::IsBrowserLaunchEnabled() const {
  return browser_launch_enabled_;
}

void LoginUtilsImpl::Observe(NotificationType type,
                             const NotificationSource& source,
                             const NotificationDetails& details) {
  if (type == NotificationType::LOGIN_USER_CHANGED)
    base::OpenPersistentNSSDB();
}

void LoginUtilsImpl::ConnectToPreferredNetwork() {
  CrosLibrary::Get()->GetNetworkLibrary()->
      ConnectToPreferredNetworkIfAvailable();
}

LoginUtils* LoginUtils::Get() {
  return Singleton<LoginUtilsWrapper>::get()->get();
}

void LoginUtils::Set(LoginUtils* mock) {
  Singleton<LoginUtilsWrapper>::get()->reset(mock);
}

void LoginUtils::DoBrowserLaunch(Profile* profile) {
  // Browser launch was disabled due to some post login screen.
  if (!LoginUtils::Get()->IsBrowserLaunchEnabled())
    return;

  // Update command line in case loose values were added.
  CommandLine::ForCurrentProcess()->InitFromArgv(
      CommandLine::ForCurrentProcess()->argv());

  LOG(INFO) << "Launching browser...";
  BrowserInit browser_init;
  int return_code;
  browser_init.LaunchBrowser(*CommandLine::ForCurrentProcess(),
                             profile,
                             FilePath(),
                             true,
                             &return_code);
}

}  // namespace chromeos
<|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 "base/file_path.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/page_transition_types.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

void SimulateRendererCrash(Browser* browser) {
  browser->OpenURL(GURL(chrome::kAboutCrashURL), GURL(), CURRENT_TAB,
                   PageTransition::TYPED);
  ui_test_utils::WaitForNotification(
      NotificationType::TAB_CONTENTS_DISCONNECTED);
}

}  // namespace

class CrashRecoveryBrowserTest : public InProcessBrowserTest {
};

// http://crbug.com/29331 - Causes an OS crash dialog in release mode, needs to
// be fixed before it can be enabled to not cause the bots issues.
#if defined(OS_MACOSX)
#define MAYBE_Reload DISABLED_Reload
#define MAYBE_LoadInNewTab DISABLED_LoadInNewTab
#elif defined(OS_WIN)
// http://crbug.com/57158 - Times out sometimes on windows.
#define MAYBE_LoadInNewTab DISABLED_LoadInNewTab
#else
#define MAYBE_Reload Reload
#define MAYBE_LoadInNewTab LoadInNewTab
#endif

// Test that reload works after a crash.
IN_PROC_BROWSER_TEST_F(CrashRecoveryBrowserTest, MAYBE_Reload) {
  // The title of the active tab should change each time this URL is loaded.
  GURL url(
      "data:text/html,<script>document.title=new Date().valueOf()</script>");
  ui_test_utils::NavigateToURL(browser(), url);

  string16 title_before_crash;
  string16 title_after_crash;

  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                &title_before_crash));
  SimulateRendererCrash(browser());
  browser()->Reload(CURRENT_TAB);
  ASSERT_TRUE(ui_test_utils::WaitForNavigationInCurrentTab(browser()));
  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                &title_after_crash));
  EXPECT_NE(title_before_crash, title_after_crash);
}

// Tests that loading a crashed page in a new tab correctly updates the title.
// There was an earlier bug (1270510) in process-per-site in which the max page
// ID of the RenderProcessHost was stale, so the NavigationEntry in the new tab
// was not committed.  This prevents regression of that bug.
IN_PROC_BROWSER_TEST_F(CrashRecoveryBrowserTest, MAYBE_LoadInNewTab) {
  const FilePath::CharType* kTitle2File = FILE_PATH_LITERAL("title2.html");

  ui_test_utils::NavigateToURL(browser(),
      ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
                                FilePath(kTitle2File)));

  string16 title_before_crash;
  string16 title_after_crash;

  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                &title_before_crash));
  SimulateRendererCrash(browser());
  browser()->Reload(CURRENT_TAB);
  ASSERT_TRUE(ui_test_utils::WaitForNavigationInCurrentTab(browser()));
  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                &title_after_crash));
  EXPECT_EQ(title_before_crash, title_after_crash);
}
<commit_msg>Mark CrashRecoveryBrowserTest.MAYBE_Reload as FAILS on windows. TBR=kinuko,gavinp BUG=57183 TEST=none<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 "base/file_path.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/page_transition_types.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

void SimulateRendererCrash(Browser* browser) {
  browser->OpenURL(GURL(chrome::kAboutCrashURL), GURL(), CURRENT_TAB,
                   PageTransition::TYPED);
  ui_test_utils::WaitForNotification(
      NotificationType::TAB_CONTENTS_DISCONNECTED);
}

}  // namespace

class CrashRecoveryBrowserTest : public InProcessBrowserTest {
};

// http://crbug.com/29331 - Causes an OS crash dialog in release mode, needs to
// be fixed before it can be enabled to not cause the bots issues.
#if defined(OS_MACOSX)
#define MAYBE_Reload DISABLED_Reload
#define MAYBE_LoadInNewTab DISABLED_LoadInNewTab
#elif defined(OS_WIN)
// http://crbug.com/57158 - Times out sometimes on windows.
#define MAYBE_LoadInNewTab DISABLED_LoadInNewTab
// http://crbug.com/57183 - Failing on windows.
#define MAYBE_Reload FAILS_Reload
#else
#define MAYBE_Reload Reload
#define MAYBE_LoadInNewTab LoadInNewTab
#endif

// Test that reload works after a crash.
IN_PROC_BROWSER_TEST_F(CrashRecoveryBrowserTest, MAYBE_Reload) {
  // The title of the active tab should change each time this URL is loaded.
  GURL url(
      "data:text/html,<script>document.title=new Date().valueOf()</script>");
  ui_test_utils::NavigateToURL(browser(), url);

  string16 title_before_crash;
  string16 title_after_crash;

  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                &title_before_crash));
  SimulateRendererCrash(browser());
  browser()->Reload(CURRENT_TAB);
  ASSERT_TRUE(ui_test_utils::WaitForNavigationInCurrentTab(browser()));
  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                &title_after_crash));
  EXPECT_NE(title_before_crash, title_after_crash);
}

// Tests that loading a crashed page in a new tab correctly updates the title.
// There was an earlier bug (1270510) in process-per-site in which the max page
// ID of the RenderProcessHost was stale, so the NavigationEntry in the new tab
// was not committed.  This prevents regression of that bug.
IN_PROC_BROWSER_TEST_F(CrashRecoveryBrowserTest, MAYBE_LoadInNewTab) {
  const FilePath::CharType* kTitle2File = FILE_PATH_LITERAL("title2.html");

  ui_test_utils::NavigateToURL(browser(),
      ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
                                FilePath(kTitle2File)));

  string16 title_before_crash;
  string16 title_after_crash;

  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                &title_before_crash));
  SimulateRendererCrash(browser());
  browser()->Reload(CURRENT_TAB);
  ASSERT_TRUE(ui_test_utils::WaitForNavigationInCurrentTab(browser()));
  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                &title_after_crash));
  EXPECT_EQ(title_before_crash, title_after_crash);
}
<|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/browser/safe_browsing/bloom_filter.h"

#include "base/metrics/histogram.h"
#include "base/rand_util.h"
#include "net/base/file_stream.h"
#include "net/base/net_errors.h"

namespace {

// The Jenkins 96 bit mix function:
// http://www.concentric.net/~Ttwang/tech/inthash.htm
uint32 HashMix(BloomFilter::HashKey hash_key, uint32 c) {
  uint32 a = static_cast<uint32>(hash_key)       & 0xFFFFFFFF;
  uint32 b = static_cast<uint32>(hash_key >> 32) & 0xFFFFFFFF;

  a -= (b + c);  a ^= (c >> 13);
  b -= (c + a);  b ^= (a << 8);
  c -= (a + b);  c ^= (b >> 13);
  a -= (b + c);  a ^= (c >> 12);
  b -= (c + a);  b ^= (a << 16);
  c -= (a + b);  c ^= (b >> 5);
  a -= (b + c);  a ^= (c >> 3);
  b -= (c + a);  b ^= (a << 10);
  c -= (a + b);  c ^= (b >> 15);

  return c;
}

}  // namespace

// static
int BloomFilter::FilterSizeForKeyCount(int key_count) {
  const int default_min = BloomFilter::kBloomFilterMinSize;
  const int number_of_keys = std::max(key_count, default_min);
  return std::min(number_of_keys * BloomFilter::kBloomFilterSizeRatio,
                  BloomFilter::kBloomFilterMaxSize * 8);
}

// static
void BloomFilter::RecordFailure(FailureType failure_type) {
  UMA_HISTOGRAM_ENUMERATION("SB2.BloomFailure", failure_type,
                            FAILURE_FILTER_MAX);
}

BloomFilter::BloomFilter(int bit_size) {
  for (int i = 0; i < kNumHashKeys; ++i)
    hash_keys_.push_back(base::RandUint64());
  byte_size_ = bit_size / 8 + 1;
  bit_size_ = byte_size_ * 8;
  data_.reset(new char[byte_size_]);
  memset(data_.get(), 0, byte_size_);
}

BloomFilter::BloomFilter(char* data, int size, const HashKeys& keys)
    : hash_keys_(keys) {
  byte_size_ = size;
  bit_size_ = byte_size_ * 8;
  data_.reset(data);
}

BloomFilter::~BloomFilter() {
}

void BloomFilter::Insert(SBPrefix hash) {
  uint32 hash_uint32 = static_cast<uint32>(hash);
  for (size_t i = 0; i < hash_keys_.size(); ++i) {
    uint32 index = HashMix(hash_keys_[i], hash_uint32) % bit_size_;
    data_[index / 8] |= 1 << (index % 8);
  }
}

bool BloomFilter::Exists(SBPrefix hash) const {
  uint32 hash_uint32 = static_cast<uint32>(hash);
  for (size_t i = 0; i < hash_keys_.size(); ++i) {
    uint32 index = HashMix(hash_keys_[i], hash_uint32) % bit_size_;
    if (!(data_[index / 8] & (1 << (index % 8))))
      return false;
  }
  return true;
}

// static.
BloomFilter* BloomFilter::LoadFile(const FilePath& filter_name) {
  net::FileStream filter;

  if (filter.Open(filter_name,
                  base::PLATFORM_FILE_OPEN |
                  base::PLATFORM_FILE_READ) != net::OK) {
    RecordFailure(FAILURE_FILTER_READ_OPEN);
    return NULL;
  }

  // Make sure we have a file version that we can understand.
  int file_version;
  int bytes_read = filter.Read(reinterpret_cast<char*>(&file_version),
                               sizeof(file_version), NULL);
  if (bytes_read != sizeof(file_version) || file_version != kFileVersion) {
    RecordFailure(FAILURE_FILTER_READ_VERSION);
    return NULL;
  }

  // Get all the random hash keys.
  int num_keys;
  bytes_read = filter.Read(reinterpret_cast<char*>(&num_keys),
                           sizeof(num_keys), NULL);
  if (bytes_read != sizeof(num_keys) ||
      num_keys < 1 || num_keys > kNumHashKeys) {
    RecordFailure(FAILURE_FILTER_READ_NUM_KEYS);
    return NULL;
  }

  HashKeys hash_keys;
  for (int i = 0; i < num_keys; ++i) {
    HashKey key;
    bytes_read = filter.Read(reinterpret_cast<char*>(&key), sizeof(key), NULL);
    if (bytes_read != sizeof(key)) {
      RecordFailure(FAILURE_FILTER_READ_KEY);
      return NULL;
    }
    hash_keys.push_back(key);
  }

  // Read in the filter data, with sanity checks on min and max sizes.
  int64 remaining64 = filter.Available();
  if (remaining64 < kBloomFilterMinSize) {
    RecordFailure(FAILURE_FILTER_READ_DATA_MINSIZE);
    return NULL;
  } else if (remaining64 > kBloomFilterMaxSize) {
    RecordFailure(FAILURE_FILTER_READ_DATA_MAXSIZE);
    return NULL;
  }

  int byte_size = static_cast<int>(remaining64);
  scoped_array<char> data(new char[byte_size]);
  bytes_read = filter.Read(data.get(), byte_size, NULL);
  if (bytes_read < byte_size) {
    RecordFailure(FAILURE_FILTER_READ_DATA_SHORT);
    return NULL;
  } else if (bytes_read != byte_size) {
    RecordFailure(FAILURE_FILTER_READ_DATA);
    return NULL;
  }

  // We've read everything okay, commit the data.
  return new BloomFilter(data.release(), byte_size, hash_keys);
}

bool BloomFilter::WriteFile(const FilePath& filter_name) const {
  net::FileStream filter;

  if (filter.Open(filter_name,
                  base::PLATFORM_FILE_WRITE |
                  base::PLATFORM_FILE_CREATE_ALWAYS) != net::OK)
    return false;

  // Write the version information.
  int version = kFileVersion;
  int bytes_written = filter.Write(reinterpret_cast<char*>(&version),
                                   sizeof(version), NULL);
  if (bytes_written != sizeof(version))
    return false;

  // Write the number of random hash keys.
  int num_keys = static_cast<int>(hash_keys_.size());
  bytes_written = filter.Write(reinterpret_cast<char*>(&num_keys),
                               sizeof(num_keys), NULL);
  if (bytes_written != sizeof(num_keys))
    return false;

  for (int i = 0; i < num_keys; ++i) {
    bytes_written = filter.Write(reinterpret_cast<const char*>(&hash_keys_[i]),
                                 sizeof(hash_keys_[i]), NULL);
    if (bytes_written != sizeof(hash_keys_[i]))
      return false;
  }

  // Write the filter data.
  bytes_written = filter.Write(data_.get(), byte_size_, NULL);
  if (bytes_written != byte_size_)
    return false;

  return true;
}
<commit_msg>Fix bloom filter to not exceed the max size.<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/browser/safe_browsing/bloom_filter.h"

#include "base/metrics/histogram.h"
#include "base/rand_util.h"
#include "net/base/file_stream.h"
#include "net/base/net_errors.h"

namespace {

// The Jenkins 96 bit mix function:
// http://www.concentric.net/~Ttwang/tech/inthash.htm
uint32 HashMix(BloomFilter::HashKey hash_key, uint32 c) {
  uint32 a = static_cast<uint32>(hash_key)       & 0xFFFFFFFF;
  uint32 b = static_cast<uint32>(hash_key >> 32) & 0xFFFFFFFF;

  a -= (b + c);  a ^= (c >> 13);
  b -= (c + a);  b ^= (a << 8);
  c -= (a + b);  c ^= (b >> 13);
  a -= (b + c);  a ^= (c >> 12);
  b -= (c + a);  b ^= (a << 16);
  c -= (a + b);  c ^= (b >> 5);
  a -= (b + c);  a ^= (c >> 3);
  b -= (c + a);  b ^= (a << 10);
  c -= (a + b);  c ^= (b >> 15);

  return c;
}

}  // namespace

// static
int BloomFilter::FilterSizeForKeyCount(int key_count) {
  const int default_min = BloomFilter::kBloomFilterMinSize;
  const int number_of_keys = std::max(key_count, default_min);
  return std::min(number_of_keys * BloomFilter::kBloomFilterSizeRatio,
                  BloomFilter::kBloomFilterMaxSize * 8);
}

// static
void BloomFilter::RecordFailure(FailureType failure_type) {
  UMA_HISTOGRAM_ENUMERATION("SB2.BloomFailure", failure_type,
                            FAILURE_FILTER_MAX);
}

BloomFilter::BloomFilter(int bit_size) {
  for (int i = 0; i < kNumHashKeys; ++i)
    hash_keys_.push_back(base::RandUint64());

  // Round up to the next boundary which fits bit_size.
  byte_size_ = (bit_size + 7) / 8;
  bit_size_ = byte_size_ * 8;
  DCHECK_LE(bit_size, bit_size_);  // strictly more bits.
  data_.reset(new char[byte_size_]);
  memset(data_.get(), 0, byte_size_);
}

BloomFilter::BloomFilter(char* data, int size, const HashKeys& keys)
    : hash_keys_(keys) {
  byte_size_ = size;
  bit_size_ = byte_size_ * 8;
  data_.reset(data);
}

BloomFilter::~BloomFilter() {
}

void BloomFilter::Insert(SBPrefix hash) {
  uint32 hash_uint32 = static_cast<uint32>(hash);
  for (size_t i = 0; i < hash_keys_.size(); ++i) {
    uint32 index = HashMix(hash_keys_[i], hash_uint32) % bit_size_;
    data_[index / 8] |= 1 << (index % 8);
  }
}

bool BloomFilter::Exists(SBPrefix hash) const {
  uint32 hash_uint32 = static_cast<uint32>(hash);
  for (size_t i = 0; i < hash_keys_.size(); ++i) {
    uint32 index = HashMix(hash_keys_[i], hash_uint32) % bit_size_;
    if (!(data_[index / 8] & (1 << (index % 8))))
      return false;
  }
  return true;
}

// static.
BloomFilter* BloomFilter::LoadFile(const FilePath& filter_name) {
  net::FileStream filter;

  if (filter.Open(filter_name,
                  base::PLATFORM_FILE_OPEN |
                  base::PLATFORM_FILE_READ) != net::OK) {
    RecordFailure(FAILURE_FILTER_READ_OPEN);
    return NULL;
  }

  // Make sure we have a file version that we can understand.
  int file_version;
  int bytes_read = filter.Read(reinterpret_cast<char*>(&file_version),
                               sizeof(file_version), NULL);
  if (bytes_read != sizeof(file_version) || file_version != kFileVersion) {
    RecordFailure(FAILURE_FILTER_READ_VERSION);
    return NULL;
  }

  // Get all the random hash keys.
  int num_keys;
  bytes_read = filter.Read(reinterpret_cast<char*>(&num_keys),
                           sizeof(num_keys), NULL);
  if (bytes_read != sizeof(num_keys) ||
      num_keys < 1 || num_keys > kNumHashKeys) {
    RecordFailure(FAILURE_FILTER_READ_NUM_KEYS);
    return NULL;
  }

  HashKeys hash_keys;
  for (int i = 0; i < num_keys; ++i) {
    HashKey key;
    bytes_read = filter.Read(reinterpret_cast<char*>(&key), sizeof(key), NULL);
    if (bytes_read != sizeof(key)) {
      RecordFailure(FAILURE_FILTER_READ_KEY);
      return NULL;
    }
    hash_keys.push_back(key);
  }

  // Read in the filter data, with sanity checks on min and max sizes.
  int64 remaining64 = filter.Available();
  if (remaining64 < kBloomFilterMinSize) {
    RecordFailure(FAILURE_FILTER_READ_DATA_MINSIZE);
    return NULL;
  } else if (remaining64 > kBloomFilterMaxSize) {
    RecordFailure(FAILURE_FILTER_READ_DATA_MAXSIZE);
    return NULL;
  }

  int byte_size = static_cast<int>(remaining64);
  scoped_array<char> data(new char[byte_size]);
  bytes_read = filter.Read(data.get(), byte_size, NULL);
  if (bytes_read < byte_size) {
    RecordFailure(FAILURE_FILTER_READ_DATA_SHORT);
    return NULL;
  } else if (bytes_read != byte_size) {
    RecordFailure(FAILURE_FILTER_READ_DATA);
    return NULL;
  }

  // We've read everything okay, commit the data.
  return new BloomFilter(data.release(), byte_size, hash_keys);
}

bool BloomFilter::WriteFile(const FilePath& filter_name) const {
  net::FileStream filter;

  if (filter.Open(filter_name,
                  base::PLATFORM_FILE_WRITE |
                  base::PLATFORM_FILE_CREATE_ALWAYS) != net::OK)
    return false;

  // Write the version information.
  int version = kFileVersion;
  int bytes_written = filter.Write(reinterpret_cast<char*>(&version),
                                   sizeof(version), NULL);
  if (bytes_written != sizeof(version))
    return false;

  // Write the number of random hash keys.
  int num_keys = static_cast<int>(hash_keys_.size());
  bytes_written = filter.Write(reinterpret_cast<char*>(&num_keys),
                               sizeof(num_keys), NULL);
  if (bytes_written != sizeof(num_keys))
    return false;

  for (int i = 0; i < num_keys; ++i) {
    bytes_written = filter.Write(reinterpret_cast<const char*>(&hash_keys_[i]),
                                 sizeof(hash_keys_[i]), NULL);
    if (bytes_written != sizeof(hash_keys_[i]))
      return false;
  }

  // Write the filter data.
  bytes_written = filter.Write(data_.get(), byte_size_, NULL);
  if (bytes_written != byte_size_)
    return false;

  return true;
}
<|endoftext|>


<commit_before>#include "PageBuilder.h"

PageBuilder::PageBuilder(const std::set<PageInfo> &Pages)
{
    pages = Pages;
}

int PageBuilder::build(const PageInfo &PageToBuild)
{
    pageToBuild = PageToBuild;

    std::cout << std::endl;

    //ensures content and template files exist
    if(!std::ifstream(pageToBuild.contentPath.str()))
    {
        std::cout << "error: cannot build " << pageToBuild.pagePath << " as content file " << pageToBuild.contentPath << " does not exist" << std::endl;
        return 1;
    }
    if(!std::ifstream(pageToBuild.templatePath.str()))
    {
        std::cout << "error: cannot build " << pageToBuild.pagePath << " as template file " << pageToBuild.templatePath << " does not exist." << std::endl;
        return 1;
    }

    std::cout << "building page " << pageToBuild.pagePath << std::endl;

    //makes sure variables are at default values
    codeBlockDepth = htmlCommentDepth = 0;
    indentAmount = "";
    contentAdded = 0;
    processedPage.clear();
    processedPage.str(std::string());
    pageDeps.clear();
    contentAdded = 0;

    //adds content and template paths to dependencies
    pageDeps.insert(pageToBuild.contentPath);
    pageDeps.insert(pageToBuild.templatePath);

    //creates anti-deps of readPath set
    std::set<Path> antiDepsOfReadPath;

    //starts read_and_process from templatePath
    if(read_and_process(pageToBuild.templatePath, antiDepsOfReadPath) > 0)
        return 1;

    //ensures @inputcontent was found inside template tree
    if(!contentAdded)
    {
        std::cout << "error: @inputcontent not found within template file " << pageToBuild.templatePath << " or any of its dependencies, content from " << pageToBuild.contentPath << " has not been inserted" << std::endl;
        return 1;
    }

    //makes sure page file exists
    pageToBuild.pagePath.ensurePathExists();

    //makes sure we can write to page file
    chmod(pageToBuild.pagePath.str().c_str(), 0644);

    //writes processed page to page file
    std::ofstream pageStream(pageToBuild.pagePath.str());
    pageStream << processedPage.str();
    pageStream.close();

    //makes sure user can't accidentally write to page file
    chmod(pageToBuild.pagePath.str().c_str(), 0444);

    //gets path for storing page information
    Path pageInfoPath = pageToBuild.pagePath.getInfoPath();

    //makes sure page info file exists
    pageInfoPath.ensurePathExists();

    //makes sure we can write to info file_
    chmod(pageInfoPath.str().c_str(), 0644);

    //writes page info file
    std::ofstream infoStream(pageInfoPath.str());
    infoStream << dateTimeInfo.currentTime() << " " << dateTimeInfo.currentDate() << std::endl;
    infoStream << this->pageToBuild << std::endl << std::endl;
    for(auto pageDep=pageDeps.begin(); pageDep != pageDeps.end(); pageDep++)
        infoStream << *pageDep << std::endl;
    infoStream.close();

    //makes sure user can't accidentally write to info file
    chmod(pageInfoPath.str().c_str(), 0444);

    std::cout << "page build successful" << std::endl;

    return 0;
}

//reads file whilst writing processed version to ofs
int PageBuilder::read_and_process(const Path &readPath, std::set<Path> antiDepsOfReadPath)
{
    //adds read path to anti dependencies of read path
    antiDepsOfReadPath.insert(readPath);

    std::ifstream ifs(readPath.str());
    std::string baseIndentAmount = indentAmount;
    int baseCodeBlockDepth = codeBlockDepth;

    int lineNo = 0,
        openCodeLineNo = 0;
    std::string inLine;
    while(getline(ifs, inLine))
    {
        lineNo++;

        if(lineNo > 1)
        {
            indentAmount = baseIndentAmount;
            processedPage << std::endl << baseIndentAmount;
        }

        for(size_t linePos=0; linePos<inLine.length();)
        {
            if(inLine[linePos] == '\\') //checks whether to escape
            {
                linePos++;
                /*
                    see http://dev.w3.org/html5/html-author/charref for
                    more character references than you could ever need!
                */
                switch(inLine[linePos])
                {
                    case '~':
                        processedPage << "˜";
                        linePos++;
                        indentAmount += std::string(7, ' ');
                        break;
                    case '!':
                        processedPage << "!";
                        linePos++;
                        indentAmount += std::string(6, ' ');
                        break;
                    case '@':
                        processedPage << "@";
                        linePos++;
                        indentAmount += std::string(8, ' ');
                        break;
                    case '#':
                        processedPage << "#";
                        linePos++;
                        indentAmount += std::string(5, ' ');
                        break;
                    /*case '$': //MUST HAVE MATHJAX HANDLE THIS
                        processedPage << "$";
                        linePos++;
                        indentAmount += string(8, ' ');
                        break;*/
                    case '%':
                        processedPage << "%";
                        linePos++;
                        indentAmount += std::string(8, ' ');
                        break;
                    case '^':
                        processedPage << "^";
                        linePos++;
                        indentAmount += std::string(5, ' ');
                        break;
                    case '&':
                        processedPage << "&";
                        linePos++;
                        indentAmount += std::string(5, ' ');
                        break;
                    case '*':
                        processedPage << "*";
                        linePos++;
                        indentAmount += std::string(5, ' ');
                        break;
                    case '?':
                        processedPage << "?";
                        linePos++;
                        indentAmount += std::string(7, ' ');
                        break;
                    case '<':
                        processedPage << "<";
                        linePos++;
                        indentAmount += std::string(4, ' ');
                        break;
                    case '>':
                        processedPage << ">";
                        linePos++;
                        indentAmount += std::string(4, ' ');
                        break;
                    default:
                        processedPage << "\\";
                        indentAmount += " ";
                }
            }
            else if(inLine[linePos] == '<') //checks about code blocks and html comments opening
            {
                //checks whether we're going down code block depth
                if(htmlCommentDepth == 0 && inLine.substr(linePos+1, 4) == "/pre")
                {
                    codeBlockDepth--;
                    if(codeBlockDepth < baseCodeBlockDepth)
                    {
                        std::cout << "error: " << readPath << ": line " << lineNo << ": </pre> close tag has no preceding <pre*> open tag." << std::endl << std::endl;
                        return 1;
                    }
                }

                //checks whether to escape <
                if(codeBlockDepth > 0)
                {
                    processedPage << "<";
                    indentAmount += std::string("<").length();
                }
                else
                {
                    processedPage << '<';
                    indentAmount += " ";
                }

                //checks whether we're going up code block depth
                if(htmlCommentDepth == 0 && inLine.substr(linePos+1, 3) == "pre")
                {
                    if(codeBlockDepth == baseCodeBlockDepth)
                        openCodeLineNo = lineNo;
                    codeBlockDepth++;
                }

                //checks whether we're going up html comment depth
                if(inLine.substr(linePos+1, 3) == "!--")
                    htmlCommentDepth++;

                linePos++;
            }
            else if(inLine[linePos] == '-') //checks about html comments closing
            {
                //checks whether we're going down html depth
                if(inLine.substr(linePos+1, 2) == "->")
                    htmlCommentDepth--;

                processedPage << '-';
                indentAmount += " ";
                linePos++;
            }
            else if(inLine[linePos] == '@') //checks for commands
            {
                if(linePos > 0 && inLine[linePos-1] == '\\')
                {
                    processedPage << '@';
                    indentAmount += " ";
                    linePos++;
                }
                else if(inLine.substr(linePos, 13) == "@inputcontent")
                {
                    contentAdded = 1;
                    std::string replaceText = "@input(" + pageToBuild.contentPath.str() + ")";
                    inLine.replace(linePos, 13, replaceText);
                }
                else if(inLine.substr(linePos, 7) == "@input(")
                {
                    linePos+=std::string("@input(").length();
                    std::string inputPathStr="";

                    for(; inLine[linePos] != ')'; linePos++)
                        if(inLine[linePos] != '"' && inLine[linePos] != '\'')
                            inputPathStr += inLine[linePos];
                    linePos++;

                    Path inputPath;
                    inputPath.set_file_path_from(inputPathStr);
                    pageDeps.insert(inputPath);

                    //ensures insert file exists
                    if(!std::ifstream(inputPathStr))
                    {
                        std::cout << "error: " << readPath << ": line " << lineNo << ": inputting file " << inputPath << " failed as path does not exist" << std::endl;
                        return 1;
                    }
                    //ensures insert file isn't an anti dep of read path
                    if(antiDepsOfReadPath.count(inputPath))
                    {
                        std::cout << "error: " << readPath << ": line " << lineNo << ": inputting file " << inputPath << " would result in an input loop" << std::endl;
                        return 1;
                    }

                    //adds insert file
                    if(read_and_process(inputPath, antiDepsOfReadPath) > 0)
                        return 1;
                    //indent amount updated inside read_and_process
                }
                else if(inLine.substr(linePos, 8) == "@pathto(")
                {
                    linePos+=std::string("@pathto(").length();
                    Name targetPageName="";

                    for(; inLine[linePos] != ')'; linePos++)
                        if(inLine[linePos] != '"' && inLine[linePos] != '\'')
                            targetPageName += inLine[linePos];
                    linePos++;

                    //warns user if target targetPageName isn't being tracked by nsm
                    PageInfo targetPageInfo;
                    targetPageInfo.pageName = targetPageName;
                    if(!pages.count(targetPageInfo))
                    {
                        std::cout << "error: " << readPath << ": line " << lineNo << ": @pathto(" << targetPageName << ") failed, nsm not tracking " << targetPageName << std::endl;
                        return 1;
                    }

                    Path targetPath = pages.find(targetPageInfo)->pagePath;
                    //targetPath.set_file_path_from(targetPathStr);

                    Path pathToTarget(pathBetween(pageToBuild.pagePath.dir, targetPath.dir), targetPath.file);

                    //adds path to target
                    processedPage << pathToTarget.str();
                    indentAmount += pathToTarget.str().length();
                }
                else if(inLine.substr(linePos, 10) == "@pagetitle")
                {
                    processedPage << pageToBuild.pageTitle.str;
                    indentAmount+=pageToBuild.pageTitle.str.length();
                    linePos+=std::string("@pagetitle").length();
                }
                else if(inLine.substr(linePos, 12) == "@currenttime")
                {
                    processedPage << dateTimeInfo.cTime;
                    indentAmount+=dateTimeInfo.cTime.length();
                    linePos+=std::string("@currenttime").length();
                }
                else if(inLine.substr(linePos, 15) == "@currentUTCtime")
                {
                    processedPage << dateTimeInfo.currentUTCTime();
                    indentAmount+=dateTimeInfo.currentUTCTime().length();
                    linePos+=std::string("@currentUTCtime").length();
                }
                else if(inLine.substr(linePos, 12) == "@currentdate")
                {
                    processedPage << dateTimeInfo.cDate;
                    indentAmount+=dateTimeInfo.cDate.length();
                    linePos+=std::string("@currentdate").length();
                }
                else if(inLine.substr(linePos, 15) == "@currentUTCdate")
                {
                    processedPage << dateTimeInfo.currentUTCDate();
                    indentAmount+=dateTimeInfo.currentUTCDate().length();
                    linePos+=std::string("@currentUTCdate").length();
                }
                else if(inLine.substr(linePos, 9) == "@timezone")
                {
                    processedPage << dateTimeInfo.cTimezone;
                    indentAmount+=dateTimeInfo.cTimezone.length();
                    linePos+=std::string("@timezone").length();
                }
                else if(inLine.substr(linePos, 15) == "@faviconinclude") //checks for favicon include
                {
                    linePos+=std::string("@faviconinclude(").length();
                    std::string faviconPathStr="";

                    for(; inLine[linePos] != ')'; linePos++)
                        if(inLine[linePos] != '"' && inLine[linePos] != '\'')
                            faviconPathStr += inLine[linePos];
                    linePos++;

                    //warns user if favicon file doesn't exist
                    if(!std::ifstream(faviconPathStr.c_str()))
                    {
                        std::cout << "warning: " << readPath << ": line " << lineNo << ": favicon file " << faviconPathStr << " does not exist" << std::endl;
                    }

                    Path faviconPath;
                    faviconPath.set_file_path_from(faviconPathStr);

                    Path pathToFavicon(pathBetween(pageToBuild.pagePath.dir, faviconPath.dir), faviconPath.file);

                    std::string faviconInclude="<link rel='icon' type='image/png' href='";
                    faviconInclude += pathToFavicon.str();
                    faviconInclude+="'>";

                    processedPage << faviconInclude;
                    indentAmount += faviconInclude.length();
                }
                else if(inLine.substr(linePos, 12) == "@cssinclude(") //checks for css includes
                {
                    linePos+=std::string("@cssinclude(").length();
                    std::string cssPathStr="";

                    for(; inLine[linePos] != ')'; linePos++)
                        if(inLine[linePos] != '"' && inLine[linePos] != '\'')
                            cssPathStr += inLine[linePos];
                    linePos++;

                    //warns user if css file doesn't exist
                    if(!std::ifstream(cssPathStr.c_str()))
                    {
                        std::cout << "warning: " << readPath << ": line " << lineNo << ": css file " << cssPathStr << "does not exist" << std::endl;
                    }

                    Path cssPath;
                    cssPath.set_file_path_from(cssPathStr);

                    Path pathToCSSFile(pathBetween(pageToBuild.pagePath.dir, cssPath.dir), cssPath.file);

                    std::string cssInclude="<link rel='stylesheet' type='text/css' href='";
                    cssInclude += pathToCSSFile.str();
                    cssInclude+="'>";

                    processedPage << cssInclude;
                    indentAmount += cssInclude.length();
                }
                else if(inLine.substr(linePos, 11) == "@jsinclude(") //checks for js includes
                {
                    linePos+=std::string("@jsinclude(").length();
                    std::string jsPathStr="";

                    for(; inLine[linePos] != ')'; linePos++)
                        if(inLine[linePos] != '"' && inLine[linePos] != '\'')
                            jsPathStr += inLine[linePos];
                    linePos++;

                    //warns user if js file doesn't exist
                    if(!std::ifstream(jsPathStr.c_str()))
                        std::cout << "warning: " << readPath << ": line " << lineNo << ": js file " << jsPathStr << "does not exist" << std::endl;

                    Path jsPath;
                    jsPath.set_file_path_from(jsPathStr);

                    Path pathToJSFile(pathBetween(pageToBuild.pagePath.dir, jsPath.dir), jsPath.file);

                    std::string jsInclude="<script type='text/javascript' src='";
                    jsInclude += pathToJSFile.str();
                    jsInclude+="'></script>";

                    processedPage << jsInclude;
                    indentAmount += jsInclude.length();
                }
                else
                {
                    processedPage << '@';
                    indentAmount += " ";
                    linePos++;
                }
            }
            else //regular character
            {
                if(inLine[linePos] == '\t')
                    indentAmount += '\t';
                else
                    indentAmount += ' ';
                processedPage << inLine[linePos++];
            }
        }
    }

    if(codeBlockDepth > baseCodeBlockDepth)
    {
        std::cout << "error: " << readPath << ": line " << openCodeLineNo << ": <pre*> open tag has no following </pre> close tag." << std::endl << std::endl;
        return 1;
    }

    ifs.close();

    return 0;
}
<commit_msg>stopped escaping &<commit_after>#include "PageBuilder.h"

PageBuilder::PageBuilder(const std::set<PageInfo> &Pages)
{
    pages = Pages;
}

int PageBuilder::build(const PageInfo &PageToBuild)
{
    pageToBuild = PageToBuild;

    std::cout << std::endl;

    //ensures content and template files exist
    if(!std::ifstream(pageToBuild.contentPath.str()))
    {
        std::cout << "error: cannot build " << pageToBuild.pagePath << " as content file " << pageToBuild.contentPath << " does not exist" << std::endl;
        return 1;
    }
    if(!std::ifstream(pageToBuild.templatePath.str()))
    {
        std::cout << "error: cannot build " << pageToBuild.pagePath << " as template file " << pageToBuild.templatePath << " does not exist." << std::endl;
        return 1;
    }

    std::cout << "building page " << pageToBuild.pagePath << std::endl;

    //makes sure variables are at default values
    codeBlockDepth = htmlCommentDepth = 0;
    indentAmount = "";
    contentAdded = 0;
    processedPage.clear();
    processedPage.str(std::string());
    pageDeps.clear();
    contentAdded = 0;

    //adds content and template paths to dependencies
    pageDeps.insert(pageToBuild.contentPath);
    pageDeps.insert(pageToBuild.templatePath);

    //creates anti-deps of readPath set
    std::set<Path> antiDepsOfReadPath;

    //starts read_and_process from templatePath
    if(read_and_process(pageToBuild.templatePath, antiDepsOfReadPath) > 0)
        return 1;

    //ensures @inputcontent was found inside template tree
    if(!contentAdded)
    {
        std::cout << "error: @inputcontent not found within template file " << pageToBuild.templatePath << " or any of its dependencies, content from " << pageToBuild.contentPath << " has not been inserted" << std::endl;
        return 1;
    }

    //makes sure page file exists
    pageToBuild.pagePath.ensurePathExists();

    //makes sure we can write to page file
    chmod(pageToBuild.pagePath.str().c_str(), 0644);

    //writes processed page to page file
    std::ofstream pageStream(pageToBuild.pagePath.str());
    pageStream << processedPage.str();
    pageStream.close();

    //makes sure user can't accidentally write to page file
    chmod(pageToBuild.pagePath.str().c_str(), 0444);

    //gets path for storing page information
    Path pageInfoPath = pageToBuild.pagePath.getInfoPath();

    //makes sure page info file exists
    pageInfoPath.ensurePathExists();

    //makes sure we can write to info file_
    chmod(pageInfoPath.str().c_str(), 0644);

    //writes page info file
    std::ofstream infoStream(pageInfoPath.str());
    infoStream << dateTimeInfo.currentTime() << " " << dateTimeInfo.currentDate() << std::endl;
    infoStream << this->pageToBuild << std::endl << std::endl;
    for(auto pageDep=pageDeps.begin(); pageDep != pageDeps.end(); pageDep++)
        infoStream << *pageDep << std::endl;
    infoStream.close();

    //makes sure user can't accidentally write to info file
    chmod(pageInfoPath.str().c_str(), 0444);

    std::cout << "page build successful" << std::endl;

    return 0;
}

//reads file whilst writing processed version to ofs
int PageBuilder::read_and_process(const Path &readPath, std::set<Path> antiDepsOfReadPath)
{
    //adds read path to anti dependencies of read path
    antiDepsOfReadPath.insert(readPath);

    std::ifstream ifs(readPath.str());
    std::string baseIndentAmount = indentAmount;
    int baseCodeBlockDepth = codeBlockDepth;

    int lineNo = 0,
        openCodeLineNo = 0;
    std::string inLine;
    while(getline(ifs, inLine))
    {
        lineNo++;

        if(lineNo > 1)
        {
            indentAmount = baseIndentAmount;
            processedPage << std::endl << baseIndentAmount;
        }

        for(size_t linePos=0; linePos<inLine.length();)
        {
            if(inLine[linePos] == '\\') //checks whether to escape
            {
                linePos++;
                /*
                    see http://dev.w3.org/html5/html-author/charref for
                    more character references than you could ever need!
                */
                switch(inLine[linePos])
                {
                    case '~':
                        processedPage << "˜";
                        linePos++;
                        indentAmount += std::string(7, ' ');
                        break;
                    case '!':
                        processedPage << "!";
                        linePos++;
                        indentAmount += std::string(6, ' ');
                        break;
                    case '@':
                        processedPage << "@";
                        linePos++;
                        indentAmount += std::string(8, ' ');
                        break;
                    case '#':
                        processedPage << "#";
                        linePos++;
                        indentAmount += std::string(5, ' ');
                        break;
                    /*case '$': //MUST HAVE MATHJAX HANDLE THIS
                        processedPage << "$";
                        linePos++;
                        indentAmount += string(8, ' ');
                        break;*/
                    case '%':
                        processedPage << "%";
                        linePos++;
                        indentAmount += std::string(8, ' ');
                        break;
                    case '^':
                        processedPage << "^";
                        linePos++;
                        indentAmount += std::string(5, ' ');
                        break;
                    /*case '&':
                        processedPage << "&";
                        linePos++;
                        indentAmount += std::string(5, ' ');
                        break;*/
                    case '*':
                        processedPage << "*";
                        linePos++;
                        indentAmount += std::string(5, ' ');
                        break;
                    case '?':
                        processedPage << "?";
                        linePos++;
                        indentAmount += std::string(7, ' ');
                        break;
                    case '<':
                        processedPage << "<";
                        linePos++;
                        indentAmount += std::string(4, ' ');
                        break;
                    case '>':
                        processedPage << ">";
                        linePos++;
                        indentAmount += std::string(4, ' ');
                        break;
                    default:
                        processedPage << "\\";
                        indentAmount += " ";
                }
            }
            else if(inLine[linePos] == '<') //checks about code blocks and html comments opening
            {
                //checks whether we're going down code block depth
                if(htmlCommentDepth == 0 && inLine.substr(linePos+1, 4) == "/pre")
                {
                    codeBlockDepth--;
                    if(codeBlockDepth < baseCodeBlockDepth)
                    {
                        std::cout << "error: " << readPath << ": line " << lineNo << ": </pre> close tag has no preceding <pre*> open tag." << std::endl << std::endl;
                        return 1;
                    }
                }

                //checks whether to escape <
                if(codeBlockDepth > 0)
                {
                    processedPage << "<";
                    indentAmount += std::string("<").length();
                }
                else
                {
                    processedPage << '<';
                    indentAmount += " ";
                }

                //checks whether we're going up code block depth
                if(htmlCommentDepth == 0 && inLine.substr(linePos+1, 3) == "pre")
                {
                    if(codeBlockDepth == baseCodeBlockDepth)
                        openCodeLineNo = lineNo;
                    codeBlockDepth++;
                }

                //checks whether we're going up html comment depth
                if(inLine.substr(linePos+1, 3) == "!--")
                    htmlCommentDepth++;

                linePos++;
            }
            else if(inLine[linePos] == '-') //checks about html comments closing
            {
                //checks whether we're going down html depth
                if(inLine.substr(linePos+1, 2) == "->")
                    htmlCommentDepth--;

                processedPage << '-';
                indentAmount += " ";
                linePos++;
            }
            else if(inLine[linePos] == '@') //checks for commands
            {
                if(linePos > 0 && inLine[linePos-1] == '\\')
                {
                    processedPage << '@';
                    indentAmount += " ";
                    linePos++;
                }
                else if(inLine.substr(linePos, 13) == "@inputcontent")
                {
                    contentAdded = 1;
                    std::string replaceText = "@input(" + pageToBuild.contentPath.str() + ")";
                    inLine.replace(linePos, 13, replaceText);
                }
                else if(inLine.substr(linePos, 7) == "@input(")
                {
                    linePos+=std::string("@input(").length();
                    std::string inputPathStr="";

                    for(; inLine[linePos] != ')'; linePos++)
                        if(inLine[linePos] != '"' && inLine[linePos] != '\'')
                            inputPathStr += inLine[linePos];
                    linePos++;

                    Path inputPath;
                    inputPath.set_file_path_from(inputPathStr);
                    pageDeps.insert(inputPath);

                    //ensures insert file exists
                    if(!std::ifstream(inputPathStr))
                    {
                        std::cout << "error: " << readPath << ": line " << lineNo << ": inputting file " << inputPath << " failed as path does not exist" << std::endl;
                        return 1;
                    }
                    //ensures insert file isn't an anti dep of read path
                    if(antiDepsOfReadPath.count(inputPath))
                    {
                        std::cout << "error: " << readPath << ": line " << lineNo << ": inputting file " << inputPath << " would result in an input loop" << std::endl;
                        return 1;
                    }

                    //adds insert file
                    if(read_and_process(inputPath, antiDepsOfReadPath) > 0)
                        return 1;
                    //indent amount updated inside read_and_process
                }
                else if(inLine.substr(linePos, 8) == "@pathto(")
                {
                    linePos+=std::string("@pathto(").length();
                    Name targetPageName="";

                    for(; inLine[linePos] != ')'; linePos++)
                        if(inLine[linePos] != '"' && inLine[linePos] != '\'')
                            targetPageName += inLine[linePos];
                    linePos++;

                    //warns user if target targetPageName isn't being tracked by nsm
                    PageInfo targetPageInfo;
                    targetPageInfo.pageName = targetPageName;
                    if(!pages.count(targetPageInfo))
                    {
                        std::cout << "error: " << readPath << ": line " << lineNo << ": @pathto(" << targetPageName << ") failed, nsm not tracking " << targetPageName << std::endl;
                        return 1;
                    }

                    Path targetPath = pages.find(targetPageInfo)->pagePath;
                    //targetPath.set_file_path_from(targetPathStr);

                    Path pathToTarget(pathBetween(pageToBuild.pagePath.dir, targetPath.dir), targetPath.file);

                    //adds path to target
                    processedPage << pathToTarget.str();
                    indentAmount += pathToTarget.str().length();
                }
                else if(inLine.substr(linePos, 10) == "@pagetitle")
                {
                    processedPage << pageToBuild.pageTitle.str;
                    indentAmount+=pageToBuild.pageTitle.str.length();
                    linePos+=std::string("@pagetitle").length();
                }
                else if(inLine.substr(linePos, 12) == "@currenttime")
                {
                    processedPage << dateTimeInfo.cTime;
                    indentAmount+=dateTimeInfo.cTime.length();
                    linePos+=std::string("@currenttime").length();
                }
                else if(inLine.substr(linePos, 15) == "@currentUTCtime")
                {
                    processedPage << dateTimeInfo.currentUTCTime();
                    indentAmount+=dateTimeInfo.currentUTCTime().length();
                    linePos+=std::string("@currentUTCtime").length();
                }
                else if(inLine.substr(linePos, 12) == "@currentdate")
                {
                    processedPage << dateTimeInfo.cDate;
                    indentAmount+=dateTimeInfo.cDate.length();
                    linePos+=std::string("@currentdate").length();
                }
                else if(inLine.substr(linePos, 15) == "@currentUTCdate")
                {
                    processedPage << dateTimeInfo.currentUTCDate();
                    indentAmount+=dateTimeInfo.currentUTCDate().length();
                    linePos+=std::string("@currentUTCdate").length();
                }
                else if(inLine.substr(linePos, 9) == "@timezone")
                {
                    processedPage << dateTimeInfo.cTimezone;
                    indentAmount+=dateTimeInfo.cTimezone.length();
                    linePos+=std::string("@timezone").length();
                }
                else if(inLine.substr(linePos, 15) == "@faviconinclude") //checks for favicon include
                {
                    linePos+=std::string("@faviconinclude(").length();
                    std::string faviconPathStr="";

                    for(; inLine[linePos] != ')'; linePos++)
                        if(inLine[linePos] != '"' && inLine[linePos] != '\'')
                            faviconPathStr += inLine[linePos];
                    linePos++;

                    //warns user if favicon file doesn't exist
                    if(!std::ifstream(faviconPathStr.c_str()))
                    {
                        std::cout << "warning: " << readPath << ": line " << lineNo << ": favicon file " << faviconPathStr << " does not exist" << std::endl;
                    }

                    Path faviconPath;
                    faviconPath.set_file_path_from(faviconPathStr);

                    Path pathToFavicon(pathBetween(pageToBuild.pagePath.dir, faviconPath.dir), faviconPath.file);

                    std::string faviconInclude="<link rel='icon' type='image/png' href='";
                    faviconInclude += pathToFavicon.str();
                    faviconInclude+="'>";

                    processedPage << faviconInclude;
                    indentAmount += faviconInclude.length();
                }
                else if(inLine.substr(linePos, 12) == "@cssinclude(") //checks for css includes
                {
                    linePos+=std::string("@cssinclude(").length();
                    std::string cssPathStr="";

                    for(; inLine[linePos] != ')'; linePos++)
                        if(inLine[linePos] != '"' && inLine[linePos] != '\'')
                            cssPathStr += inLine[linePos];
                    linePos++;

                    //warns user if css file doesn't exist
                    if(!std::ifstream(cssPathStr.c_str()))
                    {
                        std::cout << "warning: " << readPath << ": line " << lineNo << ": css file " << cssPathStr << "does not exist" << std::endl;
                    }

                    Path cssPath;
                    cssPath.set_file_path_from(cssPathStr);

                    Path pathToCSSFile(pathBetween(pageToBuild.pagePath.dir, cssPath.dir), cssPath.file);

                    std::string cssInclude="<link rel='stylesheet' type='text/css' href='";
                    cssInclude += pathToCSSFile.str();
                    cssInclude+="'>";

                    processedPage << cssInclude;
                    indentAmount += cssInclude.length();
                }
                else if(inLine.substr(linePos, 11) == "@jsinclude(") //checks for js includes
                {
                    linePos+=std::string("@jsinclude(").length();
                    std::string jsPathStr="";

                    for(; inLine[linePos] != ')'; linePos++)
                        if(inLine[linePos] != '"' && inLine[linePos] != '\'')
                            jsPathStr += inLine[linePos];
                    linePos++;

                    //warns user if js file doesn't exist
                    if(!std::ifstream(jsPathStr.c_str()))
                        std::cout << "warning: " << readPath << ": line " << lineNo << ": js file " << jsPathStr << "does not exist" << std::endl;

                    Path jsPath;
                    jsPath.set_file_path_from(jsPathStr);

                    Path pathToJSFile(pathBetween(pageToBuild.pagePath.dir, jsPath.dir), jsPath.file);

                    std::string jsInclude="<script type='text/javascript' src='";
                    jsInclude += pathToJSFile.str();
                    jsInclude+="'></script>";

                    processedPage << jsInclude;
                    indentAmount += jsInclude.length();
                }
                else
                {
                    processedPage << '@';
                    indentAmount += " ";
                    linePos++;
                }
            }
            else //regular character
            {
                if(inLine[linePos] == '\t')
                    indentAmount += '\t';
                else
                    indentAmount += ' ';
                processedPage << inLine[linePos++];
            }
        }
    }

    if(codeBlockDepth > baseCodeBlockDepth)
    {
        std::cout << "error: " << readPath << ": line " << openCodeLineNo << ": <pre*> open tag has no following </pre> close tag." << std::endl << std::endl;
        return 1;
    }

    ifs.close();

    return 0;
}
<|endoftext|>


<commit_before>/*******************************************************************************
 This file links the Java side of Android with libsdl
*******************************************************************************/
#include <jni.h>
#include <sys/time.h>
#include <time.h>
#include <android/log.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h>

#define DEBUG

/*******************************************************************************
                               Globals
*******************************************************************************/
static long _getTime(void){
	struct timeval  now;
	gettimeofday(&now, NULL);
	return (long)(now.tv_sec*1000 + now.tv_usec/1000);
}

JNIEnv* mEnv = NULL;
JNIEnv* mAudioThreadEnv = NULL; //See the note below for why this is necessary
JavaVM* mVM = NULL;

//Main activity
jclass mActivityInstance;

//method signatures
jmethodID midCreateGLContext;
jmethodID midFlipBuffers;
jmethodID midEnableFeature;
jmethodID midUpdateAudio;

extern "C" int SDL_main();
extern "C" int Android_OnKeyDown(int keycode);
extern "C" int Android_OnKeyUp(int keycode);
extern "C" void Android_SetScreenResolution(int width, int height);
extern "C" void Android_OnResize(int width, int height, int format);
extern "C" int SDL_SendQuit();
extern "C" void Android_EnableFeature(int featureid, bool enabled);

//If we're not the active app, don't try to render
bool bRenderingEnabled = false;

//Feature IDs
static const int FEATURE_AUDIO = 1;
static const int FEATURE_ACCEL = 2;

//Accelerometer data storage
float fLastAccelerometer[3];

/*******************************************************************************
                 Functions called by JNI
*******************************************************************************/

//Library init
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved){

    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return result;
    }

    mEnv = env;

    __android_log_print(ANDROID_LOG_INFO, "SDL", "JNI: OnLoad");

    jclass cls = mEnv->FindClass ("org/libsdl/app/SDLActivity");
    mActivityInstance = cls;
    midCreateGLContext = mEnv->GetStaticMethodID(cls,"createGLContext","()V");
    midFlipBuffers = mEnv->GetStaticMethodID(cls,"flipBuffers","()V");
    midEnableFeature = mEnv->GetStaticMethodID(cls,"enableFeature","(II)V");
    midUpdateAudio = mEnv->GetStaticMethodID(cls,"updateAudio","([B)V");

    if(!midCreateGLContext || !midFlipBuffers || !midEnableFeature ||
        !midUpdateAudio){
        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Bad mids\n");
    }else{
#ifdef DEBUG
        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Good mids\n");
#endif
    }

    return JNI_VERSION_1_4;
}

//Start up the SDL app
extern "C" void Java_org_libsdl_app_SDLActivity_nativeInit( JNIEnv* env,
                                                                jobject obj ){

	__android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Native Init");

	mEnv = env;
	bRenderingEnabled = true;

	Android_EnableFeature(FEATURE_ACCEL, true);

    SDL_main();
}

//Keydown
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyDown(JNIEnv* env,
               jobject obj, jint keycode){

    int r = Android_OnKeyDown(keycode);
#ifdef DEBUG
    __android_log_print(ANDROID_LOG_INFO, "SDL",
                        "SDL: native key down %d, %d\n", keycode, r);
#endif

}

//Keyup
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyUp(JNIEnv* env,
               jobject obj, jint keycode){

    int r = Android_OnKeyUp(keycode);
#ifdef DEBUG
    __android_log_print(ANDROID_LOG_INFO, "SDL",
                        "SDL: native key up %d, %d\n", keycode, r);
#endif

}

//Touch
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeTouch(JNIEnv* env,
               jobject obj, jint action, jfloat x, jfloat y, jfloat p){

#ifdef DEBUG
    __android_log_print(ANDROID_LOG_INFO, "SDL",
                        "SDL: native touch event %d @ %f/%f, pressure %f\n",
                        action, x, y, p);
#endif

    //TODO: Pass this off to the SDL multitouch stuff

}

//Quit
extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit( JNIEnv*  env,
                                                                jobject obj ){

    //Stop rendering as we're no longer in the foreground
	bRenderingEnabled = false;

    //Inject a SDL_QUIT event
    int r = SDL_SendQuit();

    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Native quit %d", r);
}

//Screen size
extern "C" void Java_org_libsdl_app_SDLActivity_nativeSetScreenSize(
                JNIEnv*  env, jobject obj, jint width, jint height){

    __android_log_print(ANDROID_LOG_INFO, "SDL",
                        "SDL: Set screen size on init: %d/%d\n", width, height);
    Android_SetScreenResolution(width, height);

}

//Resize
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeResize(
                                        JNIEnv*  env, jobject obj, jint width,
                                        jint height, jint format){
    Android_OnResize(width, height, format);
}

extern "C" void Java_org_libsdl_app_SDLActivity_onNativeAccel(
                                        JNIEnv*  env, jobject obj,
                                        jfloat x, jfloat y, jfloat z){
    fLastAccelerometer[0] = x;
    fLastAccelerometer[1] = y;
    fLastAccelerometer[2] = z;
}

/*******************************************************************************
             Functions called by SDL into Java
*******************************************************************************/
extern "C" void Android_CreateContext(){
	__android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: sdl_create_context()\n");

	bRenderingEnabled = true;

    mEnv->CallStaticVoidMethod(mActivityInstance, midCreateGLContext );
}

extern "C" void Android_Render(){

    if(!bRenderingEnabled){
        return;
    }

    //When we get here, we've accumulated a full frame
    mEnv->CallStaticVoidMethod(mActivityInstance, midFlipBuffers );
}

extern "C" void Android_EnableFeature(int featureid, bool enabled){

    mEnv->CallStaticVoidMethod(mActivityInstance, midEnableFeature,
                                featureid, (int)enabled);
}

extern "C" void Android_UpdateAudioBuffer(unsigned char *buf, int len){

    //Annoyingly we can't just call into Java from any thread. Because the audio
    //callback is dispatched from the SDL audio thread (that wasn't made from
    //java, we have to do some magic here to let the JVM know about the thread.
    //Because everything it touches on the Java side is static anyway, it's
    //not a big deal, just annoying.
    if(!mAudioThreadEnv){
        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Need to set up audio thread env\n");

        mVM->AttachCurrentThread(&mAudioThreadEnv, NULL);

        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: ok\n");
    }

    jbyteArray arr = mAudioThreadEnv->NewByteArray(len);

    //blah. We probably should rework this so we avoid the copy.
    mAudioThreadEnv->SetByteArrayRegion(arr, 0, len, (jbyte *)buf);

    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: copied\n");

    mAudioThreadEnv->CallStaticVoidMethod(  mActivityInstance,
                                            midUpdateAudio, arr );

    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: invoked\n");

}

<commit_msg>Fixed prototype for SDL_main() FIXME: Still need to pass some arguments - can we get them from the Java class?<commit_after>/*******************************************************************************
 This file links the Java side of Android with libsdl
*******************************************************************************/
#include <jni.h>
#include <sys/time.h>
#include <time.h>
#include <android/log.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h>

#define DEBUG

/*******************************************************************************
                               Globals
*******************************************************************************/
static long _getTime(void){
	struct timeval  now;
	gettimeofday(&now, NULL);
	return (long)(now.tv_sec*1000 + now.tv_usec/1000);
}

JNIEnv* mEnv = NULL;
JNIEnv* mAudioThreadEnv = NULL; //See the note below for why this is necessary
JavaVM* mVM = NULL;

//Main activity
jclass mActivityInstance;

//method signatures
jmethodID midCreateGLContext;
jmethodID midFlipBuffers;
jmethodID midEnableFeature;
jmethodID midUpdateAudio;

extern "C" int SDL_main(int argc, char *argv[]);
extern "C" int Android_OnKeyDown(int keycode);
extern "C" int Android_OnKeyUp(int keycode);
extern "C" void Android_SetScreenResolution(int width, int height);
extern "C" void Android_OnResize(int width, int height, int format);
extern "C" int SDL_SendQuit();
extern "C" void Android_EnableFeature(int featureid, bool enabled);

//If we're not the active app, don't try to render
bool bRenderingEnabled = false;

//Feature IDs
static const int FEATURE_AUDIO = 1;
static const int FEATURE_ACCEL = 2;

//Accelerometer data storage
float fLastAccelerometer[3];

/*******************************************************************************
                 Functions called by JNI
*******************************************************************************/

//Library init
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved){

    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return result;
    }

    mEnv = env;

    __android_log_print(ANDROID_LOG_INFO, "SDL", "JNI: OnLoad");

    jclass cls = mEnv->FindClass ("org/libsdl/app/SDLActivity");
    mActivityInstance = cls;
    midCreateGLContext = mEnv->GetStaticMethodID(cls,"createGLContext","()V");
    midFlipBuffers = mEnv->GetStaticMethodID(cls,"flipBuffers","()V");
    midEnableFeature = mEnv->GetStaticMethodID(cls,"enableFeature","(II)V");
    midUpdateAudio = mEnv->GetStaticMethodID(cls,"updateAudio","([B)V");

    if(!midCreateGLContext || !midFlipBuffers || !midEnableFeature ||
        !midUpdateAudio){
        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Bad mids\n");
    }else{
#ifdef DEBUG
        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Good mids\n");
#endif
    }

    return JNI_VERSION_1_4;
}

//Start up the SDL app
extern "C" void Java_org_libsdl_app_SDLActivity_nativeInit( JNIEnv* env,
                                                                jobject obj ){

	__android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Native Init");

	mEnv = env;
	bRenderingEnabled = true;

	Android_EnableFeature(FEATURE_ACCEL, true);

    SDL_main(0, 0);
}

//Keydown
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyDown(JNIEnv* env,
               jobject obj, jint keycode){

    int r = Android_OnKeyDown(keycode);
#ifdef DEBUG
    __android_log_print(ANDROID_LOG_INFO, "SDL",
                        "SDL: native key down %d, %d\n", keycode, r);
#endif

}

//Keyup
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyUp(JNIEnv* env,
               jobject obj, jint keycode){

    int r = Android_OnKeyUp(keycode);
#ifdef DEBUG
    __android_log_print(ANDROID_LOG_INFO, "SDL",
                        "SDL: native key up %d, %d\n", keycode, r);
#endif

}

//Touch
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeTouch(JNIEnv* env,
               jobject obj, jint action, jfloat x, jfloat y, jfloat p){

#ifdef DEBUG
    __android_log_print(ANDROID_LOG_INFO, "SDL",
                        "SDL: native touch event %d @ %f/%f, pressure %f\n",
                        action, x, y, p);
#endif

    //TODO: Pass this off to the SDL multitouch stuff

}

//Quit
extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit( JNIEnv*  env,
                                                                jobject obj ){

    //Stop rendering as we're no longer in the foreground
	bRenderingEnabled = false;

    //Inject a SDL_QUIT event
    int r = SDL_SendQuit();

    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Native quit %d", r);
}

//Screen size
extern "C" void Java_org_libsdl_app_SDLActivity_nativeSetScreenSize(
                JNIEnv*  env, jobject obj, jint width, jint height){

    __android_log_print(ANDROID_LOG_INFO, "SDL",
                        "SDL: Set screen size on init: %d/%d\n", width, height);
    Android_SetScreenResolution(width, height);

}

//Resize
extern "C" void Java_org_libsdl_app_SDLActivity_onNativeResize(
                                        JNIEnv*  env, jobject obj, jint width,
                                        jint height, jint format){
    Android_OnResize(width, height, format);
}

extern "C" void Java_org_libsdl_app_SDLActivity_onNativeAccel(
                                        JNIEnv*  env, jobject obj,
                                        jfloat x, jfloat y, jfloat z){
    fLastAccelerometer[0] = x;
    fLastAccelerometer[1] = y;
    fLastAccelerometer[2] = z;
}

/*******************************************************************************
             Functions called by SDL into Java
*******************************************************************************/
extern "C" void Android_CreateContext(){
	__android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: sdl_create_context()\n");

	bRenderingEnabled = true;

    mEnv->CallStaticVoidMethod(mActivityInstance, midCreateGLContext );
}

extern "C" void Android_Render(){

    if(!bRenderingEnabled){
        return;
    }

    //When we get here, we've accumulated a full frame
    mEnv->CallStaticVoidMethod(mActivityInstance, midFlipBuffers );
}

extern "C" void Android_EnableFeature(int featureid, bool enabled){

    mEnv->CallStaticVoidMethod(mActivityInstance, midEnableFeature,
                                featureid, (int)enabled);
}

extern "C" void Android_UpdateAudioBuffer(unsigned char *buf, int len){

    //Annoyingly we can't just call into Java from any thread. Because the audio
    //callback is dispatched from the SDL audio thread (that wasn't made from
    //java, we have to do some magic here to let the JVM know about the thread.
    //Because everything it touches on the Java side is static anyway, it's
    //not a big deal, just annoying.
    if(!mAudioThreadEnv){
        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Need to set up audio thread env\n");

        mVM->AttachCurrentThread(&mAudioThreadEnv, NULL);

        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: ok\n");
    }

    jbyteArray arr = mAudioThreadEnv->NewByteArray(len);

    //blah. We probably should rework this so we avoid the copy.
    mAudioThreadEnv->SetByteArrayRegion(arr, 0, len, (jbyte *)buf);

    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: copied\n");

    mAudioThreadEnv->CallStaticVoidMethod(  mActivityInstance,
                                            midUpdateAudio, arr );

    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: invoked\n");

}

<|endoftext|>