<commit_before>#include "../cshared/shared.h"
#include "../cshared/disasm.h"
#include "../cshared/stream.h"

FILE *output = stdout;

struct DisasmContext
{
    std::vector<std::string> section_labels;
    std::map<int, std::string> labels;
};

struct StreamReader : public BaseReader
{
    Stream &stream;
    StreamReader(Stream &stream, uint64_t &position) : BaseReader(position), stream(stream)
    {
    }

    virtual uint64_t raw_read(void *buffer, uint64_t size) override
    {
        auto raw_data   = stream.raw_base_data();
        auto length     = stream.raw_base_length();
        auto offset     = position();
        auto max_length = std::min(size, length - offset);

        memcpy(buffer, raw_data + offset, (size_t)max_length);

        return max_length;
    }

    bool feof()
    {
        return stream.eof();
    }
};

void parse_substream(Stream &stream, uint64_t start, bool no_numbers)
{
    uint64_t                      position = 0;
    StreamReader                  reader(stream, position);
    DisasmProcessor<StreamReader> processor(reader);

    fprintf(stderr, "cdisasm: parse substream %d bytes\n", (int)stream.length());
    processor.go([start, no_numbers](uint64_t pos, std::string info) {
        if (no_numbers)
        {
            fprintf(output, "    ");
        }
        else
        {
            fprintf(output, "%8" PRIu64 ": ", start + pos);
        }
        fprintf(output, "%s\n", info.c_str());
    });
}

void parse_data_substream(Stream &input_stream, uint64_t start, bool no_numbers)
{
    uint64_t     position = 0;
    StreamReader reader(input_stream, position);

    AsmStream<StreamReader> stream(reader);

    std::string string;
    bool        use_string = false;
    uint64_t    string_pos = 0;

    while (true)
    {
        try
        {
            auto          pos   = stream.position();
            unsigned char byte  = stream.read_uint8();
            bool          ascii = byte >= 32 && byte <= 127;

            if (ascii)
            {
                if (!use_string)
                {
                    string_pos = pos;
                }
                use_string = true;
                string += byte;
            }
            else if (use_string)
            {
                use_string = false;
                if (byte == 0)
                {
                    if (!no_numbers)
                    {
                        fprintf(output, "%8" PRIu64 ": ", start + string_pos);
                    }
                    else
                    {
                        fprintf(output, "    ");
                    }
                    fprintf(output, "W_STRING \"%s\"\n", string.c_str());
                }
                else
                {
                    size_t i = 0;
                    for (auto ch : string)
                    {
                        if (!no_numbers)
                        {
                            fprintf(output, "%8" PRIu64 ": ", start + string_pos + i);
                        }
                        else
                        {
                            fprintf(output, "    ");
                        }
                        fprintf(output, "W_BYTE %d\n", (int)ch);
                        i++;
                    }

                    if (!no_numbers)
                    {
                        fprintf(output, "%8" PRIu64 ": ", start + string_pos);
                    }
                    else
                    {
                        fprintf(output, "    ");
                    }
                    fprintf(output, "W_BYTE %d\n", (int)byte);
                }
                string = "";
            }
            else
            {
                if (!no_numbers)
                {
                    fprintf(output, "%8" PRIu64 ": ", start + pos);
                }
                else
                {
                    fprintf(output, "    ");
                }
                fprintf(output, "W_BYTE %d\n", (int)byte);
            }
        }
        catch (EOFError)
        {
            assert(!use_string);
            break;
        }
    }
}

void parse_symbol_substream(Stream &input_stream, uint64_t start, bool no_numbers)
{
    uint64_t     position = 0;
    StreamReader reader(input_stream, position);

    AsmStream<StreamReader> stream(reader);

    while (true)
    {
        try
        {
            auto pos    = stream.position();
            auto symbol = stream.read_uint64();
            if (!no_numbers)
            {
                fprintf(output, "%8" PRIu64 ": ", start + pos);
            }
            else
            {
                fprintf(output, "    ");
            }
            fprintf(output, "W_SYMBOL %" PRIu64 "\n", symbol);
        }
        catch (EOFError)
        {
            break;
        }
    }
}

void parse_func_substream(Stream &input_stream, uint64_t start, bool no_numbers)
{
    uint64_t     position = 0;
    StreamReader reader(input_stream, position);

    AsmStream<StreamReader> stream(reader);

    while (true)
    {
        try
        {
            auto pos         = stream.position();
            auto symbol      = stream.read_uint16();
            auto class_index = stream.read_int16();
            auto address     = stream.read_uint64();
            if (!no_numbers)
            {
                fprintf(output, "%8" PRIu64 ": ", start + pos);
            }
            else
            {
                fprintf(output, "    ");
            }
            fprintf(output, "W_METHOD %" PRIu16 ", %" PRId16 ", %" PRIu64 "\n", symbol, class_index,
                    address);
        }
        catch (EOFError)
        {
            break;
        }
    }
}

// TODO: move to Stream
void read_stream(Stream &stream, FILE *input = stdin, bool close_input = false)
{
#ifdef DAB_PLATFORM_WINDOWS
    if (input == stdin)
    {
        freopen(NULL, "rb", input);
    }
#endif
    byte buffer[1024];
    while (!feof(input))
    {
        size_t bytes = fread(buffer, 1, 1024, input);
        if (bytes)
        {
            stream.append(buffer, bytes);
        }
    }
    if (close_input)
    {
        fclose(input);
    }
}

bool parse_bool_arg(int argc, char **argv, const std::string &arg)
{
    for (int i = 1; i < argc; i++)
    {
        if (arg == argv[i])
            return true;
    }
    return false;
}

void parse_headers(DisasmContext &context, BinHeader *base_header)
{
    auto *header   = &base_header->header;
    auto *sections = base_header->sections;

    fprintf(output, "/* disasm */\n");
    fprintf(output, "    W_HEADER %d\n", (int)header->version);
    fprintf(output, "    W_OFFSET %" PRIu64 "\n", (uint64_t)header->offset);
    for (size_t i = 0; i < header->section_count; i++)
    {
        auto section = sections[i];

        std::string label_name = std::string("_") + section.name;
        std::transform(label_name.begin(), label_name.end(), label_name.begin(), ::toupperc);

        auto base_label_name = label_name;
        int  label_counter   = 2;

        while (true)
        {
            if (!std::count(context.section_labels.begin(), context.section_labels.end(),
                            label_name))
            {
                break;
            }
            char number[8];
            snprintf(number, sizeof(number), "%d", label_counter++);
            label_name = base_label_name + number;
        }

        context.section_labels.push_back(label_name);

        fprintf(output, "    W_SECTION %s, \"%s\"\n", label_name.c_str(), section.name);
    }
    fprintf(output, "    W_END_HEADER\n\n");
}

int main(int argc, char **argv)
{
    DisasmContext context;

    bool raw          = parse_bool_arg(argc, argv, "--raw");
    bool with_headers = parse_bool_arg(argc, argv, "--with-headers");
    bool no_numbers   = parse_bool_arg(argc, argv, "--no-numbers");

    FILE *input       = stdin;
    bool  close_input = false;

    for (int argn = 1; argn < argc; argn++)
    {
        auto arg = argv[argn];
        if (strstr(arg, "--") == NULL)
        {
            input       = fopen(arg, "rb");
            close_input = true;

            if (!input)
            {
                fprintf(stderr, "disasm: error: cannot open file <%s> for reading.\n", arg);
                exit(1);
            }
        }
    }

    Stream stream;
    read_stream(stream, input, close_input);

    if (raw)
    {
        parse_substream(stream, 0, no_numbers);
    }
    else
    {
        auto base_header = stream.peek_header();
        auto header      = &base_header->header;
        auto sections    = base_header->sections;

        fprintf(stderr, "cdisasm: %d sections\n", (int)header->section_count);
        if (with_headers)
        {
            parse_headers(context, base_header);
        }

        auto offset = header->offset;

        for (size_t i = 0; i < header->section_count; i++)
        {
            auto section = sections[i];
            fprintf(stderr, "cdisasm: section[%d] '%s' [address %" PRIu64 " length %" PRIu64 "]\n",
                    (int)i, section.name, section.pos, section.length);

            if (with_headers)
            {
                fprintf(output, "%s:\n", context.section_labels[i].c_str());
            }

            std::string section_name = section.name;
            auto        substream    = stream.section_stream(i);

            auto start_pos = section.pos + offset;

            if (section_name == "code")
            {
                parse_substream(substream, start_pos, no_numbers);
            }
            else if (with_headers && (section_name == "data" || section_name == "symd"))
            {
                parse_data_substream(substream, start_pos, no_numbers);
            }
            else if (with_headers && section_name == "symb")
            {
                parse_symbol_substream(substream, start_pos, no_numbers);
            }
            else if (with_headers && section_name == "func")
            {
                parse_func_substream(substream, start_pos, no_numbers);
            }

            if (with_headers)
            {
                fprintf(output, "\n");
            }
        }
    }

    return 0;
}
<commit_msg>disasm: allow to dump partial string<commit_after>#include "../cshared/shared.h"
#include "../cshared/disasm.h"
#include "../cshared/stream.h"

FILE *output = stdout;

struct DisasmContext
{
    std::vector<std::string> section_labels;
    std::map<int, std::string> labels;
};

struct StreamReader : public BaseReader
{
    Stream &stream;
    StreamReader(Stream &stream, uint64_t &position) : BaseReader(position), stream(stream)
    {
    }

    virtual uint64_t raw_read(void *buffer, uint64_t size) override
    {
        auto raw_data   = stream.raw_base_data();
        auto length     = stream.raw_base_length();
        auto offset     = position();
        auto max_length = std::min(size, length - offset);

        memcpy(buffer, raw_data + offset, (size_t)max_length);

        return max_length;
    }

    bool feof()
    {
        return stream.eof();
    }
};

void parse_substream(Stream &stream, uint64_t start, bool no_numbers)
{
    uint64_t                      position = 0;
    StreamReader                  reader(stream, position);
    DisasmProcessor<StreamReader> processor(reader);

    fprintf(stderr, "cdisasm: parse substream %d bytes\n", (int)stream.length());
    processor.go([start, no_numbers](uint64_t pos, std::string info) {
        if (no_numbers)
        {
            fprintf(output, "    ");
        }
        else
        {
            fprintf(output, "%8" PRIu64 ": ", start + pos);
        }
        fprintf(output, "%s\n", info.c_str());
    });
}

void parse_data_substream(Stream &input_stream, uint64_t start, bool no_numbers)
{
    uint64_t     position = 0;
    StreamReader reader(input_stream, position);

    AsmStream<StreamReader> stream(reader);

    std::string string;
    bool        use_string = false;
    uint64_t    string_pos = 0;

    while (true)
    {
        try
        {
            auto          pos   = stream.position();
            unsigned char byte  = stream.read_uint8();
            bool          ascii = byte >= 32 && byte <= 127;

            if (ascii)
            {
                if (!use_string)
                {
                    string_pos = pos;
                }
                use_string = true;
                string += byte;
            }
            else if (use_string)
            {
                use_string = false;
                if (byte == 0)
                {
                    if (!no_numbers)
                    {
                        fprintf(output, "%8" PRIu64 ": ", start + string_pos);
                    }
                    else
                    {
                        fprintf(output, "    ");
                    }
                    fprintf(output, "W_STRING \"%s\"\n", string.c_str());
                }
                else
                {
                    size_t i = 0;
                    for (auto ch : string)
                    {
                        if (!no_numbers)
                        {
                            fprintf(output, "%8" PRIu64 ": ", start + string_pos + i);
                        }
                        else
                        {
                            fprintf(output, "    ");
                        }
                        fprintf(output, "W_BYTE %d\n", (int)ch);
                        i++;
                    }

                    if (!no_numbers)
                    {
                        fprintf(output, "%8" PRIu64 ": ", start + string_pos);
                    }
                    else
                    {
                        fprintf(output, "    ");
                    }
                    fprintf(output, "W_BYTE %d\n", (int)byte);
                }
                string = "";
            }
            else
            {
                if (!no_numbers)
                {
                    fprintf(output, "%8" PRIu64 ": ", start + pos);
                }
                else
                {
                    fprintf(output, "    ");
                }
                fprintf(output, "W_BYTE %d\n", (int)byte);
            }
        }
        catch (EOFError)
        {
            if (use_string)
            {
                size_t i = 0;
                for (auto ch : string)
                {
                    if (!no_numbers)
                    {
                        fprintf(output, "%8" PRIu64 ": ", start + string_pos + i);
                    }
                    else
                    {
                        fprintf(output, "    ");
                    }
                    fprintf(output, "W_BYTE %d\n", (int)ch);
                    i++;
                }
            }
            break;
        }
    }
}

void parse_symbol_substream(Stream &input_stream, uint64_t start, bool no_numbers)
{
    uint64_t     position = 0;
    StreamReader reader(input_stream, position);

    AsmStream<StreamReader> stream(reader);

    while (true)
    {
        try
        {
            auto pos    = stream.position();
            auto symbol = stream.read_uint64();
            if (!no_numbers)
            {
                fprintf(output, "%8" PRIu64 ": ", start + pos);
            }
            else
            {
                fprintf(output, "    ");
            }
            fprintf(output, "W_SYMBOL %" PRIu64 "\n", symbol);
        }
        catch (EOFError)
        {
            break;
        }
    }
}

void parse_func_substream(Stream &input_stream, uint64_t start, bool no_numbers)
{
    uint64_t     position = 0;
    StreamReader reader(input_stream, position);

    AsmStream<StreamReader> stream(reader);

    while (true)
    {
        try
        {
            auto pos         = stream.position();
            auto symbol      = stream.read_uint16();
            auto class_index = stream.read_int16();
            auto address     = stream.read_uint64();
            if (!no_numbers)
            {
                fprintf(output, "%8" PRIu64 ": ", start + pos);
            }
            else
            {
                fprintf(output, "    ");
            }
            fprintf(output, "W_METHOD %" PRIu16 ", %" PRId16 ", %" PRIu64 "\n", symbol, class_index,
                    address);
        }
        catch (EOFError)
        {
            break;
        }
    }
}

// TODO: move to Stream
void read_stream(Stream &stream, FILE *input = stdin, bool close_input = false)
{
#ifdef DAB_PLATFORM_WINDOWS
    if (input == stdin)
    {
        freopen(NULL, "rb", input);
    }
#endif
    byte buffer[1024];
    while (!feof(input))
    {
        size_t bytes = fread(buffer, 1, 1024, input);
        if (bytes)
        {
            stream.append(buffer, bytes);
        }
    }
    if (close_input)
    {
        fclose(input);
    }
}

bool parse_bool_arg(int argc, char **argv, const std::string &arg)
{
    for (int i = 1; i < argc; i++)
    {
        if (arg == argv[i])
            return true;
    }
    return false;
}

void parse_headers(DisasmContext &context, BinHeader *base_header)
{
    auto *header   = &base_header->header;
    auto *sections = base_header->sections;

    fprintf(output, "/* disasm */\n");
    fprintf(output, "    W_HEADER %d\n", (int)header->version);
    fprintf(output, "    W_OFFSET %" PRIu64 "\n", (uint64_t)header->offset);
    for (size_t i = 0; i < header->section_count; i++)
    {
        auto section = sections[i];

        std::string label_name = std::string("_") + section.name;
        std::transform(label_name.begin(), label_name.end(), label_name.begin(), ::toupperc);

        auto base_label_name = label_name;
        int  label_counter   = 2;

        while (true)
        {
            if (!std::count(context.section_labels.begin(), context.section_labels.end(),
                            label_name))
            {
                break;
            }
            char number[8];
            snprintf(number, sizeof(number), "%d", label_counter++);
            label_name = base_label_name + number;
        }

        context.section_labels.push_back(label_name);

        fprintf(output, "    W_SECTION %s, \"%s\"\n", label_name.c_str(), section.name);
    }
    fprintf(output, "    W_END_HEADER\n\n");
}

int main(int argc, char **argv)
{
    DisasmContext context;

    bool raw          = parse_bool_arg(argc, argv, "--raw");
    bool with_headers = parse_bool_arg(argc, argv, "--with-headers");
    bool no_numbers   = parse_bool_arg(argc, argv, "--no-numbers");

    FILE *input       = stdin;
    bool  close_input = false;

    for (int argn = 1; argn < argc; argn++)
    {
        auto arg = argv[argn];
        if (strstr(arg, "--") == NULL)
        {
            input       = fopen(arg, "rb");
            close_input = true;

            if (!input)
            {
                fprintf(stderr, "disasm: error: cannot open file <%s> for reading.\n", arg);
                exit(1);
            }
        }
    }

    Stream stream;
    read_stream(stream, input, close_input);

    if (raw)
    {
        parse_substream(stream, 0, no_numbers);
    }
    else
    {
        auto base_header = stream.peek_header();
        auto header      = &base_header->header;
        auto sections    = base_header->sections;

        fprintf(stderr, "cdisasm: %d sections\n", (int)header->section_count);
        if (with_headers)
        {
            parse_headers(context, base_header);
        }

        auto offset = header->offset;

        for (size_t i = 0; i < header->section_count; i++)
        {
            auto section = sections[i];
            fprintf(stderr, "cdisasm: section[%d] '%s' [address %" PRIu64 " length %" PRIu64 "]\n",
                    (int)i, section.name, section.pos, section.length);

            if (with_headers)
            {
                fprintf(output, "%s:\n", context.section_labels[i].c_str());
            }

            std::string section_name = section.name;
            auto        substream    = stream.section_stream(i);

            auto start_pos = section.pos + offset;

            if (section_name == "code")
            {
                parse_substream(substream, start_pos, no_numbers);
            }
            else if (with_headers && (section_name == "data" || section_name == "symd"))
            {
                parse_data_substream(substream, start_pos, no_numbers);
            }
            else if (with_headers && section_name == "symb")
            {
                parse_symbol_substream(substream, start_pos, no_numbers);
            }
            else if (with_headers && section_name == "func")
            {
                parse_func_substream(substream, start_pos, no_numbers);
            }

            if (with_headers)
            {
                fprintf(output, "\n");
            }
        }
    }

    return 0;
}
<|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/utf_string_conversions.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/ui_test_utils.h"
#include "net/base/mock_host_resolver.h"

class AppApiTest : public ExtensionApiTest {
};

// Simulates a page calling window.open on an URL, and waits for the navigation.
static void WindowOpenHelper(Browser* browser,
                             RenderViewHost* opener_host,
                             const GURL& url,
                             bool newtab_process_should_equal_opener) {
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(
      opener_host, L"", L"window.open('" + UTF8ToWide(url.spec()) + L"');"));

  // The above window.open call is not user-initiated, it will create
  // a popup window instead of a new tab in current window.
  // Now the active tab in last active window should be the new tab.
  Browser* last_active_browser = BrowserList::GetLastActive();
  EXPECT_TRUE(last_active_browser);
  TabContents* newtab = last_active_browser->GetSelectedTabContents();
  EXPECT_TRUE(newtab);
  if (!newtab->controller().GetLastCommittedEntry() ||
      newtab->controller().GetLastCommittedEntry()->url() != url)
    ui_test_utils::WaitForNavigation(&newtab->controller());
  EXPECT_EQ(url, newtab->controller().GetLastCommittedEntry()->url());
  if (newtab_process_should_equal_opener)
    EXPECT_EQ(opener_host->process(), newtab->render_view_host()->process());
  else
    EXPECT_NE(opener_host->process(), newtab->render_view_host()->process());
}

// Simulates a page navigating itself to an URL, and waits for the navigation.
static void NavigateTabHelper(TabContents* contents, const GURL& url) {
  bool result = false;
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
      contents->render_view_host(), L"",
      L"window.addEventListener('unload', function() {"
      L"    window.domAutomationController.send(true);"
      L"}, false);"
      L"window.location = '" + UTF8ToWide(url.spec()) + L"';",
      &result));
  ASSERT_TRUE(result);

  if (!contents->controller().GetLastCommittedEntry() ||
      contents->controller().GetLastCommittedEntry()->url() != url)
    ui_test_utils::WaitForNavigation(&contents->controller());
  EXPECT_EQ(url, contents->controller().GetLastCommittedEntry()->url());
}

IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcess) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kDisablePopupBlocking);

  host_resolver()->AddRule("*", "127.0.0.1");
  ASSERT_TRUE(test_server()->Start());

  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));

  // Open two tabs in the app, one outside it.
  GURL base_url = test_server()->GetURL(
      "files/extensions/api_test/app_process/");

  // The app under test acts on URLs whose host is "localhost",
  // so the URLs we navigate to must have host "localhost".
  GURL::Replacements replace_host;
  std::string host_str("localhost");  // must stay in scope with replace_host
  replace_host.SetHostStr(host_str);
  base_url = base_url.ReplaceComponents(replace_host);

  browser()->NewTab();
  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
  browser()->NewTab();
  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path2/empty.html"));
  browser()->NewTab();
  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path3/empty.html"));

  // The extension should have opened 3 new tabs. Including the original blank
  // tab, we now have 4 tabs. Two should be part of the extension app, and
  // grouped in the same process.
  ASSERT_EQ(4, browser()->tab_count());
  RenderViewHost* host = browser()->GetTabContentsAt(1)->render_view_host();

  EXPECT_EQ(host->process(),
            browser()->GetTabContentsAt(2)->render_view_host()->process());
  EXPECT_NE(host->process(),
            browser()->GetTabContentsAt(3)->render_view_host()->process());

  // Now let's do the same using window.open. The same should happen.
  ASSERT_EQ(1u, BrowserList::GetBrowserCount(browser()->profile()));
  WindowOpenHelper(browser(), host,
                   base_url.Resolve("path1/empty.html"), true);
  WindowOpenHelper(browser(), host,
                   base_url.Resolve("path2/empty.html"), true);
  // TODO(creis): This should open in a new process (i.e., false for the last
  // argument), but we temporarily avoid swapping processes away from an app
  // until we're able to restore window.opener if the page later returns to an
  // in-app URL.  See crbug.com/65953.
  WindowOpenHelper(browser(), host,
                   base_url.Resolve("path3/empty.html"), true);

  // Now let's have these pages navigate, into or out of the extension web
  // extent. They should switch processes.
  const GURL& app_url(base_url.Resolve("path1/empty.html"));
  const GURL& non_app_url(base_url.Resolve("path3/empty.html"));
  NavigateTabHelper(browser()->GetTabContentsAt(2), non_app_url);
  NavigateTabHelper(browser()->GetTabContentsAt(3), app_url);
  // TODO(creis): This should swap out of the app's process (i.e., EXPECT_NE),
  // but we temporarily avoid swapping away from an app in case it needs to
  // communicate with window.opener later.  See crbug.com/65953.
  EXPECT_EQ(host->process(),
            browser()->GetTabContentsAt(2)->render_view_host()->process());
  EXPECT_EQ(host->process(),
            browser()->GetTabContentsAt(3)->render_view_host()->process());

  // If one of the popup tabs navigates back to the app, window.opener should
  // be valid.
  NavigateTabHelper(browser()->GetTabContentsAt(6), app_url);
  EXPECT_EQ(host->process(),
            browser()->GetTabContentsAt(6)->render_view_host()->process());
  bool windowOpenerValid = false;
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
      browser()->GetTabContentsAt(6)->render_view_host(), L"",
      L"window.domAutomationController.send(window.opener != null)",
      &windowOpenerValid));
  ASSERT_TRUE(windowOpenerValid);
}

// Tests that app process switching works properly in the following scenario:
// 1. navigate to a page1 in the app
// 2. page1 redirects to a page2 outside the app extent (ie, "/server-redirect")
// 3. page2 redirects back to a page in the app
// The final navigation should end up in the app process.
// See http://crbug.com/61757
IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcessRedirectBack) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kDisablePopupBlocking);

  host_resolver()->AddRule("*", "127.0.0.1");
  ASSERT_TRUE(test_server()->Start());

  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));

  // Open two tabs in the app.
  GURL base_url = test_server()->GetURL(
      "files/extensions/api_test/app_process/");

  // The app under test acts on URLs whose host is "localhost",
  // so the URLs we navigate to must have host "localhost".
  GURL::Replacements replace_host;
  std::string host_str("localhost");  // must stay in scope with replace_host
  replace_host.SetHostStr(host_str);
  base_url = base_url.ReplaceComponents(replace_host);

  browser()->NewTab();
  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
  browser()->NewTab();
  ui_test_utils::NavigateToURL(browser(),
                               base_url.Resolve("path1/redirect.html"));

  // Wait until the second tab finishes its redirect train (2 hops).
  NavigationController& controller =
      browser()->GetSelectedTabContents()->controller();
  while (controller.GetActiveEntry()->url().path() !=
         "/files/extensions/api_test/app_process/path1/empty.html") {
    ui_test_utils::WaitForNavigation(&controller);
  }

  // 3 tabs, including the initial about:blank. The last 2 should be the same
  // process.
  ASSERT_EQ(3, browser()->tab_count());
  RenderViewHost* host = browser()->GetTabContentsAt(1)->render_view_host();
  EXPECT_EQ(host->process(),
            browser()->GetTabContentsAt(2)->render_view_host()->process());
}
<commit_msg>Small cleanup for previous CL in AppApiTest.AppProcessRedirectBack.<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/utf_string_conversions.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/ui_test_utils.h"
#include "net/base/mock_host_resolver.h"

class AppApiTest : public ExtensionApiTest {
};

// Simulates a page calling window.open on an URL, and waits for the navigation.
static void WindowOpenHelper(Browser* browser,
                             RenderViewHost* opener_host,
                             const GURL& url,
                             bool newtab_process_should_equal_opener) {
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(
      opener_host, L"", L"window.open('" + UTF8ToWide(url.spec()) + L"');"));

  // The above window.open call is not user-initiated, it will create
  // a popup window instead of a new tab in current window.
  // Now the active tab in last active window should be the new tab.
  Browser* last_active_browser = BrowserList::GetLastActive();
  EXPECT_TRUE(last_active_browser);
  TabContents* newtab = last_active_browser->GetSelectedTabContents();
  EXPECT_TRUE(newtab);
  if (!newtab->controller().GetLastCommittedEntry() ||
      newtab->controller().GetLastCommittedEntry()->url() != url)
    ui_test_utils::WaitForNavigation(&newtab->controller());
  EXPECT_EQ(url, newtab->controller().GetLastCommittedEntry()->url());
  if (newtab_process_should_equal_opener)
    EXPECT_EQ(opener_host->process(), newtab->render_view_host()->process());
  else
    EXPECT_NE(opener_host->process(), newtab->render_view_host()->process());
}

// Simulates a page navigating itself to an URL, and waits for the navigation.
static void NavigateTabHelper(TabContents* contents, const GURL& url) {
  bool result = false;
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
      contents->render_view_host(), L"",
      L"window.addEventListener('unload', function() {"
      L"    window.domAutomationController.send(true);"
      L"}, false);"
      L"window.location = '" + UTF8ToWide(url.spec()) + L"';",
      &result));
  ASSERT_TRUE(result);

  if (!contents->controller().GetLastCommittedEntry() ||
      contents->controller().GetLastCommittedEntry()->url() != url)
    ui_test_utils::WaitForNavigation(&contents->controller());
  EXPECT_EQ(url, contents->controller().GetLastCommittedEntry()->url());
}

IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcess) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kDisablePopupBlocking);

  host_resolver()->AddRule("*", "127.0.0.1");
  ASSERT_TRUE(test_server()->Start());

  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));

  // Open two tabs in the app, one outside it.
  GURL base_url = test_server()->GetURL(
      "files/extensions/api_test/app_process/");

  // The app under test acts on URLs whose host is "localhost",
  // so the URLs we navigate to must have host "localhost".
  GURL::Replacements replace_host;
  std::string host_str("localhost");  // must stay in scope with replace_host
  replace_host.SetHostStr(host_str);
  base_url = base_url.ReplaceComponents(replace_host);

  browser()->NewTab();
  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
  browser()->NewTab();
  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path2/empty.html"));
  browser()->NewTab();
  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path3/empty.html"));

  // The extension should have opened 3 new tabs. Including the original blank
  // tab, we now have 4 tabs. Two should be part of the extension app, and
  // grouped in the same process.
  ASSERT_EQ(4, browser()->tab_count());
  RenderViewHost* host = browser()->GetTabContentsAt(1)->render_view_host();

  EXPECT_EQ(host->process(),
            browser()->GetTabContentsAt(2)->render_view_host()->process());
  EXPECT_NE(host->process(),
            browser()->GetTabContentsAt(3)->render_view_host()->process());

  // Now let's do the same using window.open. The same should happen.
  ASSERT_EQ(1u, BrowserList::GetBrowserCount(browser()->profile()));
  WindowOpenHelper(browser(), host,
                   base_url.Resolve("path1/empty.html"), true);
  WindowOpenHelper(browser(), host,
                   base_url.Resolve("path2/empty.html"), true);
  // TODO(creis): This should open in a new process (i.e., false for the last
  // argument), but we temporarily avoid swapping processes away from an app
  // until we're able to restore window.opener if the page later returns to an
  // in-app URL.  See crbug.com/65953.
  WindowOpenHelper(browser(), host,
                   base_url.Resolve("path3/empty.html"), true);

  // Now let's have these pages navigate, into or out of the extension web
  // extent. They should switch processes.
  const GURL& app_url(base_url.Resolve("path1/empty.html"));
  const GURL& non_app_url(base_url.Resolve("path3/empty.html"));
  NavigateTabHelper(browser()->GetTabContentsAt(2), non_app_url);
  NavigateTabHelper(browser()->GetTabContentsAt(3), app_url);
  // TODO(creis): This should swap out of the app's process (i.e., EXPECT_NE),
  // but we temporarily avoid swapping away from an app in case it needs to
  // communicate with window.opener later.  See crbug.com/65953.
  EXPECT_EQ(host->process(),
            browser()->GetTabContentsAt(2)->render_view_host()->process());
  EXPECT_EQ(host->process(),
            browser()->GetTabContentsAt(3)->render_view_host()->process());

  // If one of the popup tabs navigates back to the app, window.opener should
  // be valid.
  NavigateTabHelper(browser()->GetTabContentsAt(6), app_url);
  EXPECT_EQ(host->process(),
            browser()->GetTabContentsAt(6)->render_view_host()->process());
  bool windowOpenerValid = false;
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
      browser()->GetTabContentsAt(6)->render_view_host(), L"",
      L"window.domAutomationController.send(window.opener != null)",
      &windowOpenerValid));
  ASSERT_TRUE(windowOpenerValid);
}

// Tests that app process switching works properly in the following scenario:
// 1. navigate to a page1 in the app
// 2. page1 redirects to a page2 outside the app extent (ie, "/server-redirect")
// 3. page2 redirects back to a page in the app
// The final navigation should end up in the app process.
// See http://crbug.com/61757
IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcessRedirectBack) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kDisablePopupBlocking);

  host_resolver()->AddRule("*", "127.0.0.1");
  ASSERT_TRUE(test_server()->Start());

  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));

  // Open two tabs in the app.
  GURL base_url = test_server()->GetURL(
      "files/extensions/api_test/app_process/");

  // The app under test acts on URLs whose host is "localhost",
  // so the URLs we navigate to must have host "localhost".
  GURL::Replacements replace_host;
  std::string host_str("localhost");  // must stay in scope with replace_host
  replace_host.SetHostStr(host_str);
  base_url = base_url.ReplaceComponents(replace_host);

  browser()->NewTab();
  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
  browser()->NewTab();
  // Wait until the second tab finishes its redirect train (2 hops).
  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), base_url.Resolve("path1/redirect.html"), 2);

  // 3 tabs, including the initial about:blank. The last 2 should be the same
  // process.
  ASSERT_EQ(3, browser()->tab_count());
  EXPECT_EQ("/files/extensions/api_test/app_process/path1/empty.html",
            browser()->GetTabContentsAt(2)->controller().
                GetLastCommittedEntry()->url().path());
  RenderViewHost* host = browser()->GetTabContentsAt(1)->render_view_host();
  EXPECT_EQ(host->process(),
            browser()->GetTabContentsAt(2)->render_view_host()->process());
}
<|endoftext|>


<commit_before>//
//  DefaultTileTexturizer.cpp
//  G3MiOSSDK
//
//  Created by Diego Gomez Deck on 4/16/14.
//
//

#include "DefaultTileTexturizer.hpp"

#include "LayerSet.hpp"
#include "Tile.hpp"
#include "LeveledTexturedMesh.hpp"
#include "TextureIDReference.hpp"
#include "FrameTasksExecutor.hpp"
#include "LayerTilesRenderParameters.hpp"

class DTT_LTMInitializer : public LazyTextureMappingInitializer {
private:
  const Tile* _tile;
  const Tile* _ancestor;

  float _translationU;
  float _translationV;
  float _scaleU;
  float _scaleV;

  const TileTessellator* _tessellator;
#ifdef C_CODE
  const Vector2I _resolution;
#endif
#ifdef JAVA_CODE
  private final Vector2I _resolution;
#endif

public:
  DTT_LTMInitializer(const Vector2I& resolution,
                     const Tile* tile,
                     const Tile* ancestor,
                     const TileTessellator* tessellator) :
  _resolution(resolution),
  _tile(tile),
  _ancestor(ancestor),
  _tessellator(tessellator),
  _translationU(0),
  _translationV(0),
  _scaleU(1),
  _scaleV(1)
  {

  }

  virtual ~DTT_LTMInitializer() {
#ifdef JAVA_CODE
    super.dispose();
#endif

  }

  void initialize() {
    // The default scale and translation are ok when (tile == _ancestor)
    if (_tile != _ancestor) {
      const Sector tileSector = _tile->_sector;

      const Vector2F lowerTextCoordUV = _tessellator->getTextCoord(_ancestor,
                                                                   tileSector._lower);

      const Vector2F upperTextCoordUV = _tessellator->getTextCoord(_ancestor,
                                                                   tileSector._upper);

      _translationU = lowerTextCoordUV._x;
      _translationV = upperTextCoordUV._y;

      _scaleU = upperTextCoordUV._x - lowerTextCoordUV._x;
      _scaleV = lowerTextCoordUV._y - upperTextCoordUV._y;
    }
  }

  const Vector2F getTranslation() const {
    return Vector2F(_translationU, _translationV);
  }

  const Vector2F getScale() const {
    return Vector2F(_scaleU, _scaleV);
  }

  IFloatBuffer* createTextCoords() const {
    return _tessellator->createTextCoords(_resolution, _tile);
  }

};

class DTT_TileTextureBuilder : public RCObject {
private:
  LeveledTexturedMesh* _mesh;

  DefaultTileTexturizer* _texturizer;
  TileRasterizer*        _tileRasterizer;
  Tile*                  _tile;

//  std::vector<Petition*> _petitions;
//  int                    _petitionsCount;
//  int                    _stepsDone;

  TexturesHandler* _texturesHandler;

#ifdef C_CODE
  const Vector2I   _tileTextureResolution;
  const Vector2I   _tileMeshResolution;
#endif
#ifdef JAVA_CODE
  private final Vector2I _tileTextureResolution;
  private final Vector2I _tileMeshResolution;
#endif

  IDownloader*     _downloader;

  const Mesh* _tessellatorMesh;

  const TileTessellator* _tessellator;

  const bool _logTilesPetitions;

  //  std::vector<TileTextureBuilder_PetitionStatus> _status;
  //  std::vector<long long>                         _requestsIds;

  bool _finalized;
  bool _canceled;
  bool _alreadyStarted;

  long long _texturePriority;

  const TextureIDReference* getTopLevelTextureIdForTile(Tile* tile) const {
    LeveledTexturedMesh* mesh = (LeveledTexturedMesh*) tile->getTexturizedMesh();

    return (mesh == NULL) ? NULL : mesh->getTopLevelTextureId();
  }

  LeveledTexturedMesh* createMesh() const {
    std::vector<LazyTextureMapping*>* mappings = new std::vector<LazyTextureMapping*>();

    Tile* ancestor = _tile;
    bool fallbackSolved = false;
    while (ancestor != NULL && !fallbackSolved) {
      const bool ownedTexCoords = true;
      const bool transparent    = false;
      LazyTextureMapping* mapping = new LazyTextureMapping(new DTT_LTMInitializer(_tileMeshResolution,
                                                                                _tile,
                                                                                ancestor,
                                                                                _tessellator),
                                                           ownedTexCoords,
                                                           transparent);

      if (ancestor != _tile) {
        const TextureIDReference* glTextureId = getTopLevelTextureIdForTile(ancestor);
        if (glTextureId != NULL) {
          TextureIDReference* glTextureIdRetainedCopy = glTextureId->createCopy();

          mapping->setGLTextureId(glTextureIdRetainedCopy);
          fallbackSolved = true;
        }
      }

      mappings->push_back(mapping);

      ancestor = ancestor->getParent();
    }

    return new LeveledTexturedMesh(_tessellatorMesh,
                                   false,
                                   mappings);
  }

public:

  DTT_TileTextureBuilder(DefaultTileTexturizer*            texturizer,
                         TileRasterizer*                   tileRasterizer,
                         const G3MRenderContext*           rc,
                         const LayerTilesRenderParameters* layerTilesRenderParameters,
//                         const std::vector<Petition*>&     petitions,
                         IDownloader*                      downloader,
                         Tile*                             tile,
                         const Mesh*                       tessellatorMesh,
                         const TileTessellator*            tessellator,
                         long long                         texturePriority,
                         bool                              logTilesPetitions) :
  _texturizer(texturizer),
  _tileRasterizer(tileRasterizer),
  _texturesHandler(rc->getTexturesHandler()),
  _tileTextureResolution( layerTilesRenderParameters->_tileTextureResolution ),
  _tileMeshResolution( layerTilesRenderParameters->_tileMeshResolution ),
  _downloader(downloader),
  _tile(tile),
  _tessellatorMesh(tessellatorMesh),
//  _stepsDone(0),
  _mesh( NULL ),
  _tessellator(tessellator),
  _finalized(false),
  _canceled(false),
  _alreadyStarted(false),
  _texturePriority(texturePriority),
  _logTilesPetitions(logTilesPetitions)
  {
    _mesh = createMesh();
  }

  LeveledTexturedMesh* getMesh() {
    return _mesh;
  }

  void cleanMesh() {
    _mesh = NULL;
  }

  void cleanTile() {
    _tile = NULL;
  }

  void start() {
#warning Diego at work!
  }

  void cancel() {
#warning Diego at work!
//    if (!_canceled) {
//      _canceled = true;
//
//      if (!_finalized) {
//        for (int i = 0; i < _requestsIds.size(); i++) {
//          const long long requestId = _requestsIds[i];
//          _downloader->cancelRequest(requestId);
//        }
//      }
//      _requestsIds.clear();
//    }
  }

  bool isCanceled() const {
    return _canceled;
  }

};

class DTT_TileTextureBuilderHolder : public ITexturizerData {
private:
  DTT_TileTextureBuilder* _builder;

public:
  DTT_TileTextureBuilderHolder(DTT_TileTextureBuilder* builder) :
  _builder(builder)
  {
  }

  DTT_TileTextureBuilder* get() const {
    return _builder;
  }

  ~DTT_TileTextureBuilderHolder() {
    if (_builder != NULL) {
      _builder->_release();
    }
  }

  //#ifndef C_CODE
  //  void unusedMethod() { }
  //#endif
};

class DTT_TileTextureBuilderStartTask : public FrameTask {
private:
  DTT_TileTextureBuilder* _builder;

public:
  DTT_TileTextureBuilderStartTask(DTT_TileTextureBuilder* builder) :
  _builder(builder)
  {
    _builder->_retain();
  }

  virtual ~DTT_TileTextureBuilderStartTask() {
    _builder->_release();
#ifdef JAVA_CODE
    super.dispose();
#endif
  }

  void execute(const G3MRenderContext* rc) {
    _builder->start();
  }

  bool isCanceled(const G3MRenderContext* rc) {
    return _builder->isCanceled();
  }
};

LeveledTexturedMesh* DefaultTileTexturizer::getMesh(Tile* tile) const {
  DTT_TileTextureBuilderHolder* tileBuilderHolder = (DTT_TileTextureBuilderHolder*) tile->getTexturizerData();
  return (tileBuilderHolder == NULL) ? NULL : tileBuilderHolder->get()->getMesh();
}

RenderState DefaultTileTexturizer::getRenderState(LayerSet* layerSet) {
  if (layerSet != NULL) {
    return layerSet->getRenderState();
  }
  return RenderState::ready();
}

void DefaultTileTexturizer::initialize(const G3MContext* context,
                                       const TilesRenderParameters* parameters) {
  // do nothing
}

Mesh* DefaultTileTexturizer::texturize(const G3MRenderContext* rc,
                                       const TileTessellator* tessellator,
                                       TileRasterizer* tileRasterizer,
                                       const LayerTilesRenderParameters* layerTilesRenderParameters,
                                       const LayerSet* layerSet,
                                       bool forceFullRender,
                                       long long texturePriority,
                                       Tile* tile,
                                       Mesh* tessellatorMesh,
                                       Mesh* previousMesh,
                                       bool logTilesPetitions) {
  DTT_TileTextureBuilderHolder* builderHolder = (DTT_TileTextureBuilderHolder*) tile->getTexturizerData();

  if (builderHolder == NULL) {
    builderHolder = new DTT_TileTextureBuilderHolder(new DTT_TileTextureBuilder(this,
                                                                                tileRasterizer,
                                                                                rc,
                                                                                layerTilesRenderParameters,
//                                                                                layerSet->createTileMapPetitions(rc,
//                                                                                layerTilesRenderParameters,
//                                                                                tile),
                                                                                rc->getDownloader(),
                                                                                tile,
                                                                                tessellatorMesh,
                                                                                tessellator,
                                                                                texturePriority,
                                                                                logTilesPetitions
                                                                                )
                                                     );
    tile->setTexturizerData(builderHolder);
  }

  DTT_TileTextureBuilder* builder = builderHolder->get();
  if (forceFullRender) {
    builder->start();
  }
  else {
    rc->getFrameTasksExecutor()->addPreRenderTask(new DTT_TileTextureBuilderStartTask(builder));
  }

  tile->setTexturizerDirty(false);
  return builder->getMesh();
}

void DefaultTileTexturizer::tileToBeDeleted(Tile* tile,
                                            Mesh* mesh) {
  DTT_TileTextureBuilderHolder* builderHolder = (DTT_TileTextureBuilderHolder*) tile->getTexturizerData();
  if (builderHolder != NULL) {
    DTT_TileTextureBuilder* builder = builderHolder->get();
    builder->cancel();
    builder->cleanTile();
    builder->cleanMesh();
  }
}

void DefaultTileTexturizer::tileMeshToBeDeleted(Tile* tile,
                                                Mesh* mesh) {
  DTT_TileTextureBuilderHolder* builderHolder = (DTT_TileTextureBuilderHolder*) tile->getTexturizerData();
  if (builderHolder != NULL) {
    DTT_TileTextureBuilder* builder = builderHolder->get();
    builder->cancel();
    builder->cleanMesh();
  }
}

bool DefaultTileTexturizer::tileMeetsRenderCriteria(Tile* tile) {
  return false;
}

void DefaultTileTexturizer::justCreatedTopTile(const G3MRenderContext* rc,
                                               Tile* tile,
                                               LayerSet* layerSet) {
  // do nothing
}

void DefaultTileTexturizer::ancestorTexturedSolvedChanged(Tile* tile,
                                                          Tile* ancestorTile,
                                                          bool textureSolved) {
  if (!textureSolved) {
    return;
  }

  if (tile->isTextureSolved()) {
    return;
  }

  LeveledTexturedMesh* ancestorMesh = getMesh(ancestorTile);
  if (ancestorMesh == NULL) {
    return;
  }

  const TextureIDReference* glTextureId = ancestorMesh->getTopLevelTextureId();
  if (glTextureId == NULL) {
    return;
  }

  LeveledTexturedMesh* tileMesh = getMesh(tile);
  if (tileMesh == NULL) {
    return;
  }

  const TextureIDReference* glTextureIdRetainedCopy = glTextureId->createCopy();

  const int level = tile->_level - ancestorTile->_level;
  if (!tileMesh->setGLTextureIdForLevel(level, glTextureIdRetainedCopy)) {
    delete glTextureIdRetainedCopy;
  }
}

bool DefaultTileTexturizer::onTerrainTouchEvent(const G3MEventContext* ec,
                                                const Geodetic3D& position,
                                                const Tile* tile,
                                                LayerSet* layerSet) {
  if (layerSet == NULL) {
    return false;
  }

  return layerSet->onTerrainTouchEvent(ec, position, tile);
}
<commit_msg>Tile's image creation refactoring - NOT YET USABLE<commit_after>//
//  DefaultTileTexturizer.cpp
//  G3MiOSSDK
//
//  Created by Diego Gomez Deck on 4/16/14.
//
//

#include "DefaultTileTexturizer.hpp"

#include "LayerSet.hpp"
#include "Tile.hpp"
#include "LeveledTexturedMesh.hpp"
#include "TextureIDReference.hpp"
#include "FrameTasksExecutor.hpp"
#include "LayerTilesRenderParameters.hpp"

class DTT_LTMInitializer : public LazyTextureMappingInitializer {
private:
  const Tile* _tile;
  const Tile* _ancestor;

  float _translationU;
  float _translationV;
  float _scaleU;
  float _scaleV;

  const TileTessellator* _tessellator;
#ifdef C_CODE
  const Vector2I _resolution;
#endif
#ifdef JAVA_CODE
  private final Vector2I _resolution;
#endif

public:
  DTT_LTMInitializer(const Vector2I& resolution,
                     const Tile* tile,
                     const Tile* ancestor,
                     const TileTessellator* tessellator) :
  _resolution(resolution),
  _tile(tile),
  _ancestor(ancestor),
  _tessellator(tessellator),
  _translationU(0),
  _translationV(0),
  _scaleU(1),
  _scaleV(1)
  {

  }

  virtual ~DTT_LTMInitializer() {
#ifdef JAVA_CODE
    super.dispose();
#endif

  }

  void initialize() {
    // The default scale and translation are ok when (tile == _ancestor)
    if (_tile != _ancestor) {
      const Sector tileSector = _tile->_sector;

      const Vector2F lowerTextCoordUV = _tessellator->getTextCoord(_ancestor,
                                                                   tileSector._lower);

      const Vector2F upperTextCoordUV = _tessellator->getTextCoord(_ancestor,
                                                                   tileSector._upper);

      _translationU = lowerTextCoordUV._x;
      _translationV = upperTextCoordUV._y;

      _scaleU = upperTextCoordUV._x - lowerTextCoordUV._x;
      _scaleV = lowerTextCoordUV._y - upperTextCoordUV._y;
    }
  }

  const Vector2F getTranslation() const {
    return Vector2F(_translationU, _translationV);
  }

  const Vector2F getScale() const {
    return Vector2F(_scaleU, _scaleV);
  }

  IFloatBuffer* createTextCoords() const {
    return _tessellator->createTextCoords(_resolution, _tile);
  }

};

class DTT_TileTextureBuilder : public RCObject {
private:
  LeveledTexturedMesh* _mesh;

  DefaultTileTexturizer* _texturizer;
  TileRasterizer*        _tileRasterizer;
  Tile*                  _tile;

//  std::vector<Petition*> _petitions;
//  int                    _petitionsCount;
//  int                    _stepsDone;

  TexturesHandler* _texturesHandler;

#ifdef C_CODE
  const Vector2I   _tileTextureResolution;
  const Vector2I   _tileMeshResolution;
#endif
#ifdef JAVA_CODE
  private final Vector2I _tileTextureResolution;
  private final Vector2I _tileMeshResolution;
#endif

  IDownloader*     _downloader;

  const Mesh* _tessellatorMesh;

  const TileTessellator* _tessellator;

  const bool _logTilesPetitions;

  //  std::vector<TileTextureBuilder_PetitionStatus> _status;
  //  std::vector<long long>                         _requestsIds;

  bool _finalized;
  bool _canceled;
  bool _alreadyStarted;

  long long _texturePriority;

  const TextureIDReference* getTopLevelTextureIdForTile(Tile* tile) const {
    LeveledTexturedMesh* mesh = (LeveledTexturedMesh*) tile->getTexturizedMesh();

    return (mesh == NULL) ? NULL : mesh->getTopLevelTextureId();
  }

  LeveledTexturedMesh* createMesh() const {
    std::vector<LazyTextureMapping*>* mappings = new std::vector<LazyTextureMapping*>();

    Tile* ancestor = _tile;
    bool fallbackSolved = false;
    while (ancestor != NULL && !fallbackSolved) {
      const bool ownedTexCoords = true;
      const bool transparent    = false;
      LazyTextureMapping* mapping = new LazyTextureMapping(new DTT_LTMInitializer(_tileMeshResolution,
                                                                                _tile,
                                                                                ancestor,
                                                                                _tessellator),
                                                           ownedTexCoords,
                                                           transparent);

      if (ancestor != _tile) {
        const TextureIDReference* glTextureId = getTopLevelTextureIdForTile(ancestor);
        if (glTextureId != NULL) {
          TextureIDReference* glTextureIdRetainedCopy = glTextureId->createCopy();

          mapping->setGLTextureId(glTextureIdRetainedCopy);
          fallbackSolved = true;
        }
      }

      mappings->push_back(mapping);

      ancestor = ancestor->getParent();
    }

    return new LeveledTexturedMesh(_tessellatorMesh,
                                   false,
                                   mappings);
  }

public:

  DTT_TileTextureBuilder(DefaultTileTexturizer*            texturizer,
                         TileRasterizer*                   tileRasterizer,
                         const G3MRenderContext*           rc,
                         const LayerTilesRenderParameters* layerTilesRenderParameters,
//                         const std::vector<Petition*>&     petitions,
                         IDownloader*                      downloader,
                         Tile*                             tile,
                         const Mesh*                       tessellatorMesh,
                         const TileTessellator*            tessellator,
                         long long                         texturePriority,
                         bool                              logTilesPetitions) :
  _texturizer(texturizer),
  _tileRasterizer(tileRasterizer),
  _texturesHandler(rc->getTexturesHandler()),
  _tileTextureResolution( layerTilesRenderParameters->_tileTextureResolution ),
  _tileMeshResolution( layerTilesRenderParameters->_tileMeshResolution ),
  _downloader(downloader),
  _tile(tile),
  _tessellatorMesh(tessellatorMesh),
//  _stepsDone(0),
  _mesh( NULL ),
  _tessellator(tessellator),
  _finalized(false),
  _canceled(false),
  _alreadyStarted(false),
  _texturePriority(texturePriority),
  _logTilesPetitions(logTilesPetitions)
  {
    _mesh = createMesh();
  }

  LeveledTexturedMesh* getMesh() {
    return _mesh;
  }

  void cleanMesh() {
    _mesh = NULL;
  }

  void cleanTile() {
    _tile = NULL;
  }

  void start() {
#warning Diego at work!
  }

  void cancel() {
#warning Diego at work!
//    if (!_canceled) {
//      _canceled = true;
//
//      if (!_finalized) {
//        for (int i = 0; i < _requestsIds.size(); i++) {
//          const long long requestId = _requestsIds[i];
//          _downloader->cancelRequest(requestId);
//        }
//      }
//      _requestsIds.clear();
//    }
  }

  bool isCanceled() const {
    return _canceled;
  }

};

class DTT_TileTextureBuilderHolder : public ITexturizerData {
private:
  DTT_TileTextureBuilder* _builder;

public:
  DTT_TileTextureBuilderHolder(DTT_TileTextureBuilder* builder) :
  _builder(builder)
  {
  }

  DTT_TileTextureBuilder* get() const {
    return _builder;
  }

  ~DTT_TileTextureBuilderHolder() {
    if (_builder != NULL) {
      _builder->_release();
    }
  }

#ifndef C_CODE
  void unusedMethod() { }
#endif
};

class DTT_TileTextureBuilderStartTask : public FrameTask {
private:
  DTT_TileTextureBuilder* _builder;

public:
  DTT_TileTextureBuilderStartTask(DTT_TileTextureBuilder* builder) :
  _builder(builder)
  {
    _builder->_retain();
  }

  virtual ~DTT_TileTextureBuilderStartTask() {
    _builder->_release();
#ifdef JAVA_CODE
    super.dispose();
#endif
  }

  void execute(const G3MRenderContext* rc) {
    _builder->start();
  }

  bool isCanceled(const G3MRenderContext* rc) {
    return _builder->isCanceled();
  }
};

LeveledTexturedMesh* DefaultTileTexturizer::getMesh(Tile* tile) const {
  DTT_TileTextureBuilderHolder* tileBuilderHolder = (DTT_TileTextureBuilderHolder*) tile->getTexturizerData();
  return (tileBuilderHolder == NULL) ? NULL : tileBuilderHolder->get()->getMesh();
}

RenderState DefaultTileTexturizer::getRenderState(LayerSet* layerSet) {
  if (layerSet != NULL) {
    return layerSet->getRenderState();
  }
  return RenderState::ready();
}

void DefaultTileTexturizer::initialize(const G3MContext* context,
                                       const TilesRenderParameters* parameters) {
  // do nothing
}

Mesh* DefaultTileTexturizer::texturize(const G3MRenderContext* rc,
                                       const TileTessellator* tessellator,
                                       TileRasterizer* tileRasterizer,
                                       const LayerTilesRenderParameters* layerTilesRenderParameters,
                                       const LayerSet* layerSet,
                                       bool forceFullRender,
                                       long long texturePriority,
                                       Tile* tile,
                                       Mesh* tessellatorMesh,
                                       Mesh* previousMesh,
                                       bool logTilesPetitions) {
  DTT_TileTextureBuilderHolder* builderHolder = (DTT_TileTextureBuilderHolder*) tile->getTexturizerData();

  if (builderHolder == NULL) {
    builderHolder = new DTT_TileTextureBuilderHolder(new DTT_TileTextureBuilder(this,
                                                                                tileRasterizer,
                                                                                rc,
                                                                                layerTilesRenderParameters,
//                                                                                layerSet->createTileMapPetitions(rc,
//                                                                                layerTilesRenderParameters,
//                                                                                tile),
                                                                                rc->getDownloader(),
                                                                                tile,
                                                                                tessellatorMesh,
                                                                                tessellator,
                                                                                texturePriority,
                                                                                logTilesPetitions
                                                                                )
                                                     );
    tile->setTexturizerData(builderHolder);
  }

  DTT_TileTextureBuilder* builder = builderHolder->get();
  if (forceFullRender) {
    builder->start();
  }
  else {
    rc->getFrameTasksExecutor()->addPreRenderTask(new DTT_TileTextureBuilderStartTask(builder));
  }

  tile->setTexturizerDirty(false);
  return builder->getMesh();
}

void DefaultTileTexturizer::tileToBeDeleted(Tile* tile,
                                            Mesh* mesh) {
  DTT_TileTextureBuilderHolder* builderHolder = (DTT_TileTextureBuilderHolder*) tile->getTexturizerData();
  if (builderHolder != NULL) {
    DTT_TileTextureBuilder* builder = builderHolder->get();
    builder->cancel();
    builder->cleanTile();
    builder->cleanMesh();
  }
}

void DefaultTileTexturizer::tileMeshToBeDeleted(Tile* tile,
                                                Mesh* mesh) {
  DTT_TileTextureBuilderHolder* builderHolder = (DTT_TileTextureBuilderHolder*) tile->getTexturizerData();
  if (builderHolder != NULL) {
    DTT_TileTextureBuilder* builder = builderHolder->get();
    builder->cancel();
    builder->cleanMesh();
  }
}

bool DefaultTileTexturizer::tileMeetsRenderCriteria(Tile* tile) {
  return false;
}

void DefaultTileTexturizer::justCreatedTopTile(const G3MRenderContext* rc,
                                               Tile* tile,
                                               LayerSet* layerSet) {
  // do nothing
}

void DefaultTileTexturizer::ancestorTexturedSolvedChanged(Tile* tile,
                                                          Tile* ancestorTile,
                                                          bool textureSolved) {
  if (!textureSolved) {
    return;
  }

  if (tile->isTextureSolved()) {
    return;
  }

  LeveledTexturedMesh* ancestorMesh = getMesh(ancestorTile);
  if (ancestorMesh == NULL) {
    return;
  }

  const TextureIDReference* glTextureId = ancestorMesh->getTopLevelTextureId();
  if (glTextureId == NULL) {
    return;
  }

  LeveledTexturedMesh* tileMesh = getMesh(tile);
  if (tileMesh == NULL) {
    return;
  }

  const TextureIDReference* glTextureIdRetainedCopy = glTextureId->createCopy();

  const int level = tile->_level - ancestorTile->_level;
  if (!tileMesh->setGLTextureIdForLevel(level, glTextureIdRetainedCopy)) {
    delete glTextureIdRetainedCopy;
  }
}

bool DefaultTileTexturizer::onTerrainTouchEvent(const G3MEventContext* ec,
                                                const Geodetic3D& position,
                                                const Tile* tile,
                                                LayerSet* layerSet) {
  if (layerSet == NULL) {
    return false;
  }

  return layerSet->onTerrainTouchEvent(ec, position, tile);
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 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/extensions/extension_file_util.h"

#include "base/file_util.h"
#include "base/logging.h"
#include "base/scoped_temp_dir.h"
#include "base/string_util.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/json_value_serializer.h"
#include "net/base/file_stream.h"

namespace extension_file_util {

const char kInstallDirectoryName[] = "Extensions";
const char kCurrentVersionFileName[] = "Current Version";

bool MoveDirSafely(const FilePath& source_dir, const FilePath& dest_dir) {
  if (file_util::PathExists(dest_dir)) {
    if (!file_util::Delete(dest_dir, true))
      return false;
  } else {
    FilePath parent = dest_dir.DirName();
    if (!file_util::DirectoryExists(parent)) {
      if (!file_util::CreateDirectory(parent))
        return false;
    }
  }

  if (!file_util::Move(source_dir, dest_dir))
    return false;

  return true;
}

bool SetCurrentVersion(const FilePath& dest_dir, const std::string& version,
                       std::string* error) {
  // Write out the new CurrentVersion file.
  // <profile>/Extension/<name>/CurrentVersion
  FilePath current_version = dest_dir.AppendASCII(kCurrentVersionFileName);
  FilePath current_version_old =
    current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
  if (file_util::PathExists(current_version_old)) {
    if (!file_util::Delete(current_version_old, false)) {
      *error = "Couldn't remove CurrentVersion_old file.";
      return false;
    }
  }

  if (file_util::PathExists(current_version)) {
    if (!file_util::Move(current_version, current_version_old)) {
      *error = "Couldn't move CurrentVersion file.";
      return false;
    }
  }
  net::FileStream stream;
  int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
  if (stream.Open(current_version, flags) != 0)
    return false;
  if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
    // Restore the old CurrentVersion.
    if (file_util::PathExists(current_version_old)) {
      if (!file_util::Move(current_version_old, current_version)) {
        LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
            " to " << current_version.value();

        // TODO(erikkay): This is an ugly state to be in.  Try harder?
      }
    }
    *error = "Couldn't create CurrentVersion file.";
    return false;
  }
  return true;
}

bool ReadCurrentVersion(const FilePath& dir, std::string* version_string) {
  FilePath current_version = dir.AppendASCII(kCurrentVersionFileName);
  if (file_util::PathExists(current_version)) {
    if (file_util::ReadFileToString(current_version, version_string)) {
      TrimWhitespace(*version_string, TRIM_ALL, version_string);
      return true;
    }
  }
  return false;
}

Extension::InstallType CompareToInstalledVersion(
    const FilePath& install_directory, const std::string& id,
    const std::string& new_version_str, std::string *current_version_str) {
  CHECK(current_version_str);
  FilePath dir(install_directory.AppendASCII(id.c_str()));
  if (!ReadCurrentVersion(dir, current_version_str))
    return Extension::NEW_INSTALL;

  scoped_ptr<Version> current_version(
    Version::GetVersionFromString(*current_version_str));
  scoped_ptr<Version> new_version(
    Version::GetVersionFromString(new_version_str));
  int comp = new_version->CompareTo(*current_version);
  if (comp > 0)
    return Extension::UPGRADE;
  else if (comp == 0)
    return Extension::REINSTALL;
  else
    return Extension::DOWNGRADE;
}

bool SanityCheckExtension(const FilePath& dir) {
  // Verify that the directory actually exists.
  // TODO(erikkay): A further step would be to verify that the extension
  // has actually loaded successfully.
  FilePath manifest_file(dir.AppendASCII(Extension::kManifestFilename));
  return file_util::PathExists(dir) && file_util::PathExists(manifest_file);
}

bool InstallExtension(const FilePath& src_dir,
                      const FilePath& extensions_dir,
                      const std::string& extension_id,
                      const std::string& extension_version,
                      FilePath* version_dir,
                      Extension::InstallType* install_type,
                      std::string* error) {
  FilePath dest_dir = extensions_dir.AppendASCII(extension_id);
  *version_dir = dest_dir.AppendASCII(extension_version);

  std::string current_version;
  *install_type = CompareToInstalledVersion(
      extensions_dir, extension_id, extension_version, &current_version);

  // Do not allow downgrade.
  if (*install_type == Extension::DOWNGRADE)
    return true;

  if (*install_type == Extension::REINSTALL) {
    if (!SanityCheckExtension(*version_dir)) {
      // Treat corrupted existing installation as new install case.
      *install_type = Extension::NEW_INSTALL;
    } else {
      return true;
    }
  }

  // If anything fails after this, we want to delete the extension dir.
  ScopedTempDir scoped_version_dir;
  scoped_version_dir.Set(*version_dir);

  if (!MoveDirSafely(src_dir, *version_dir)) {
    *error = "Could not move extension directory into profile.";
    return false;
  }

  if (!SetCurrentVersion(dest_dir, extension_version, error))
    return false;

  scoped_version_dir.Take();
  return true;
}

Extension* LoadExtension(const FilePath& extension_path, bool require_key,
                         std::string* error) {
  FilePath manifest_path =
      extension_path.AppendASCII(Extension::kManifestFilename);
  if (!file_util::PathExists(manifest_path)) {
    *error = extension_manifest_errors::kInvalidManifest;
    return NULL;
  }

  JSONFileValueSerializer serializer(manifest_path);
  scoped_ptr<Value> root(serializer.Deserialize(error));
  if (!root.get())
    return NULL;

  if (!root->IsType(Value::TYPE_DICTIONARY)) {
    *error = extension_manifest_errors::kInvalidManifest;
    return NULL;
  }

  scoped_ptr<Extension> extension(new Extension(extension_path));
  if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()),
                                require_key, error))
    return NULL;

  // Validate icons exist.
  for (std::map<int, std::string>::const_iterator iter =
       extension->icons().begin(); iter != extension->icons().end(); ++iter) {
    if (!file_util::PathExists(extension->GetResourcePath(iter->second))) {
      *error = StringPrintf("Could not load extension icon '%s'.",
                            iter->second.c_str());
      return NULL;
    }
  }

  // Theme resource validation.
  if (extension->IsTheme()) {
    DictionaryValue* images_value = extension->GetThemeImages();
    DictionaryValue::key_iterator iter = images_value->begin_keys();
    if (images_value) {
      while (iter != images_value->end_keys()) {
        std::string val;
        if (images_value->GetString(*iter , &val)) {
          FilePath image_path = extension->path().AppendASCII(val);
          if (!file_util::PathExists(image_path)) {
            *error = StringPrintf(
                "Could not load '%s' for theme.",
                WideToUTF8(image_path.ToWStringHack()).c_str());
            return NULL;
          }
        }
        ++iter;
      }
    }

    // Themes cannot contain other extension types.
    return extension.release();
  }

  // Validate that claimed script resources actually exist.
  for (size_t i = 0; i < extension->content_scripts().size(); ++i) {
    const UserScript& script = extension->content_scripts()[i];

    for (size_t j = 0; j < script.js_scripts().size(); j++) {
      const FilePath& path = script.js_scripts()[j].path();
      if (!file_util::PathExists(path)) {
        *error = StringPrintf("Could not load '%s' for content script.",
                              WideToUTF8(path.ToWStringHack()).c_str());
        return NULL;
      }
    }

    for (size_t j = 0; j < script.css_scripts().size(); j++) {
      const FilePath& path = script.css_scripts()[j].path();
      if (!file_util::PathExists(path)) {
        *error = StringPrintf("Could not load '%s' for content script.",
                              WideToUTF8(path.ToWStringHack()).c_str());
        return NULL;
      }
    }
  }

  for (size_t i = 0; i < extension->plugins().size(); ++i) {
    const Extension::PluginInfo& plugin = extension->plugins()[i];
    if (!file_util::PathExists(plugin.path)) {
      *error = StringPrintf("Could not load '%s' for plugin.",
                            WideToUTF8(plugin.path.ToWStringHack()).c_str());
      return NULL;
    }
  }

  // Validate icon location for page actions.
  const PageActionMap& page_actions = extension->page_actions();
  for (PageActionMap::const_iterator i(page_actions.begin());
       i != page_actions.end(); ++i) {
    PageAction* page_action = i->second;
    const std::vector<FilePath>& icon_paths = page_action->icon_paths();
    for (std::vector<FilePath>::const_iterator iter = icon_paths.begin();
         iter != icon_paths.end(); ++iter) {
      FilePath path = *iter;
      if (!file_util::PathExists(path)) {
        *error = StringPrintf("Could not load icon '%s' for page action.",
                              WideToUTF8(path.ToWStringHack()).c_str());
        return NULL;
      }
    }
  }

  return extension.release();
}

void UninstallExtension(const std::string& id, const FilePath& extensions_dir) {
  // First, delete the Current Version file. If the directory delete fails, then
  // at least the extension won't be loaded again.
  FilePath extension_root = extensions_dir.AppendASCII(id);

  if (!file_util::PathExists(extension_root)) {
    LOG(WARNING) << "Asked to remove a non-existent extension " << id;
    return;
  }

  FilePath current_version_file = extension_root.AppendASCII(
      kCurrentVersionFileName);
  if (!file_util::PathExists(current_version_file)) {
    LOG(WARNING) << "Extension " << id
                 << " does not have a Current Version file.";
  } else {
    if (!file_util::Delete(current_version_file, false)) {
      LOG(WARNING) << "Could not delete Current Version file for extension "
                   << id;
      return;
    }
  }

  // OK, now try and delete the entire rest of the directory. One major place
  // this can fail is if the extension contains a plugin (stupid plugins). It's
  // not a big deal though, because we'll notice next time we startup that the
  // Current Version file is gone and finish the delete then.
  if (!file_util::Delete(extension_root, true))
    LOG(WARNING) << "Could not delete directory for extension " << id;
}

void GarbageCollectExtensions(const FilePath& install_directory) {
  // Nothing to clean up if it doesn't exist.
  if (!file_util::DirectoryExists(install_directory))
    return;

  LOG(INFO) << "Loading installed extensions...";
  file_util::FileEnumerator enumerator(install_directory,
                                       false,  // Not recursive.
                                       file_util::FileEnumerator::DIRECTORIES);
  FilePath extension_path;
  for (extension_path = enumerator.Next(); !extension_path.value().empty();
       extension_path = enumerator.Next()) {
    std::string extension_id = WideToASCII(
        extension_path.BaseName().ToWStringHack());

    // If there is no Current Version file, just delete the directory and move
    // on. This can legitimately happen when an uninstall does not complete, for
    // example, when a plugin is in use at uninstall time.
    FilePath current_version_path = extension_path.AppendASCII(
        kCurrentVersionFileName);
    if (!file_util::PathExists(current_version_path)) {
      LOG(INFO) << "Deleting incomplete install for directory "
                << WideToASCII(extension_path.ToWStringHack()) << ".";
      file_util::Delete(extension_path, true);  // Recursive.
      continue;
    }

    // Ignore directories that aren't valid IDs.
    if (!Extension::IdIsValid(extension_id)) {
      LOG(WARNING) << "Invalid extension ID encountered in extensions "
                      "directory: " << extension_id;
      LOG(INFO) << "Deleting invalid extension directory "
                << WideToASCII(extension_path.ToWStringHack()) << ".";
      file_util::Delete(extension_path, true);  // Recursive.
      continue;
    }
  }
}

}  // extensionfile_util
<commit_msg>Put the null pointer check for theme images before we use the pointer.<commit_after>// Copyright (c) 2009 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/extensions/extension_file_util.h"

#include "base/file_util.h"
#include "base/logging.h"
#include "base/scoped_temp_dir.h"
#include "base/string_util.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/json_value_serializer.h"
#include "net/base/file_stream.h"

namespace extension_file_util {

const char kInstallDirectoryName[] = "Extensions";
const char kCurrentVersionFileName[] = "Current Version";

bool MoveDirSafely(const FilePath& source_dir, const FilePath& dest_dir) {
  if (file_util::PathExists(dest_dir)) {
    if (!file_util::Delete(dest_dir, true))
      return false;
  } else {
    FilePath parent = dest_dir.DirName();
    if (!file_util::DirectoryExists(parent)) {
      if (!file_util::CreateDirectory(parent))
        return false;
    }
  }

  if (!file_util::Move(source_dir, dest_dir))
    return false;

  return true;
}

bool SetCurrentVersion(const FilePath& dest_dir, const std::string& version,
                       std::string* error) {
  // Write out the new CurrentVersion file.
  // <profile>/Extension/<name>/CurrentVersion
  FilePath current_version = dest_dir.AppendASCII(kCurrentVersionFileName);
  FilePath current_version_old =
    current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
  if (file_util::PathExists(current_version_old)) {
    if (!file_util::Delete(current_version_old, false)) {
      *error = "Couldn't remove CurrentVersion_old file.";
      return false;
    }
  }

  if (file_util::PathExists(current_version)) {
    if (!file_util::Move(current_version, current_version_old)) {
      *error = "Couldn't move CurrentVersion file.";
      return false;
    }
  }
  net::FileStream stream;
  int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
  if (stream.Open(current_version, flags) != 0)
    return false;
  if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
    // Restore the old CurrentVersion.
    if (file_util::PathExists(current_version_old)) {
      if (!file_util::Move(current_version_old, current_version)) {
        LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
            " to " << current_version.value();

        // TODO(erikkay): This is an ugly state to be in.  Try harder?
      }
    }
    *error = "Couldn't create CurrentVersion file.";
    return false;
  }
  return true;
}

bool ReadCurrentVersion(const FilePath& dir, std::string* version_string) {
  FilePath current_version = dir.AppendASCII(kCurrentVersionFileName);
  if (file_util::PathExists(current_version)) {
    if (file_util::ReadFileToString(current_version, version_string)) {
      TrimWhitespace(*version_string, TRIM_ALL, version_string);
      return true;
    }
  }
  return false;
}

Extension::InstallType CompareToInstalledVersion(
    const FilePath& install_directory, const std::string& id,
    const std::string& new_version_str, std::string *current_version_str) {
  CHECK(current_version_str);
  FilePath dir(install_directory.AppendASCII(id.c_str()));
  if (!ReadCurrentVersion(dir, current_version_str))
    return Extension::NEW_INSTALL;

  scoped_ptr<Version> current_version(
    Version::GetVersionFromString(*current_version_str));
  scoped_ptr<Version> new_version(
    Version::GetVersionFromString(new_version_str));
  int comp = new_version->CompareTo(*current_version);
  if (comp > 0)
    return Extension::UPGRADE;
  else if (comp == 0)
    return Extension::REINSTALL;
  else
    return Extension::DOWNGRADE;
}

bool SanityCheckExtension(const FilePath& dir) {
  // Verify that the directory actually exists.
  // TODO(erikkay): A further step would be to verify that the extension
  // has actually loaded successfully.
  FilePath manifest_file(dir.AppendASCII(Extension::kManifestFilename));
  return file_util::PathExists(dir) && file_util::PathExists(manifest_file);
}

bool InstallExtension(const FilePath& src_dir,
                      const FilePath& extensions_dir,
                      const std::string& extension_id,
                      const std::string& extension_version,
                      FilePath* version_dir,
                      Extension::InstallType* install_type,
                      std::string* error) {
  FilePath dest_dir = extensions_dir.AppendASCII(extension_id);
  *version_dir = dest_dir.AppendASCII(extension_version);

  std::string current_version;
  *install_type = CompareToInstalledVersion(
      extensions_dir, extension_id, extension_version, &current_version);

  // Do not allow downgrade.
  if (*install_type == Extension::DOWNGRADE)
    return true;

  if (*install_type == Extension::REINSTALL) {
    if (!SanityCheckExtension(*version_dir)) {
      // Treat corrupted existing installation as new install case.
      *install_type = Extension::NEW_INSTALL;
    } else {
      return true;
    }
  }

  // If anything fails after this, we want to delete the extension dir.
  ScopedTempDir scoped_version_dir;
  scoped_version_dir.Set(*version_dir);

  if (!MoveDirSafely(src_dir, *version_dir)) {
    *error = "Could not move extension directory into profile.";
    return false;
  }

  if (!SetCurrentVersion(dest_dir, extension_version, error))
    return false;

  scoped_version_dir.Take();
  return true;
}

Extension* LoadExtension(const FilePath& extension_path, bool require_key,
                         std::string* error) {
  FilePath manifest_path =
      extension_path.AppendASCII(Extension::kManifestFilename);
  if (!file_util::PathExists(manifest_path)) {
    *error = extension_manifest_errors::kInvalidManifest;
    return NULL;
  }

  JSONFileValueSerializer serializer(manifest_path);
  scoped_ptr<Value> root(serializer.Deserialize(error));
  if (!root.get())
    return NULL;

  if (!root->IsType(Value::TYPE_DICTIONARY)) {
    *error = extension_manifest_errors::kInvalidManifest;
    return NULL;
  }

  scoped_ptr<Extension> extension(new Extension(extension_path));
  if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()),
                                require_key, error))
    return NULL;

  // Validate icons exist.
  for (std::map<int, std::string>::const_iterator iter =
       extension->icons().begin(); iter != extension->icons().end(); ++iter) {
    if (!file_util::PathExists(extension->GetResourcePath(iter->second))) {
      *error = StringPrintf("Could not load extension icon '%s'.",
                            iter->second.c_str());
      return NULL;
    }
  }

  // Theme resource validation.
  if (extension->IsTheme()) {
    DictionaryValue* images_value = extension->GetThemeImages();
    if (images_value) {
      DictionaryValue::key_iterator iter = images_value->begin_keys();
      while (iter != images_value->end_keys()) {
        std::string val;
        if (images_value->GetString(*iter , &val)) {
          FilePath image_path = extension->path().AppendASCII(val);
          if (!file_util::PathExists(image_path)) {
            *error = StringPrintf(
                "Could not load '%s' for theme.",
                WideToUTF8(image_path.ToWStringHack()).c_str());
            return NULL;
          }
        }
        ++iter;
      }
    }

    // Themes cannot contain other extension types.
    return extension.release();
  }

  // Validate that claimed script resources actually exist.
  for (size_t i = 0; i < extension->content_scripts().size(); ++i) {
    const UserScript& script = extension->content_scripts()[i];

    for (size_t j = 0; j < script.js_scripts().size(); j++) {
      const FilePath& path = script.js_scripts()[j].path();
      if (!file_util::PathExists(path)) {
        *error = StringPrintf("Could not load '%s' for content script.",
                              WideToUTF8(path.ToWStringHack()).c_str());
        return NULL;
      }
    }

    for (size_t j = 0; j < script.css_scripts().size(); j++) {
      const FilePath& path = script.css_scripts()[j].path();
      if (!file_util::PathExists(path)) {
        *error = StringPrintf("Could not load '%s' for content script.",
                              WideToUTF8(path.ToWStringHack()).c_str());
        return NULL;
      }
    }
  }

  for (size_t i = 0; i < extension->plugins().size(); ++i) {
    const Extension::PluginInfo& plugin = extension->plugins()[i];
    if (!file_util::PathExists(plugin.path)) {
      *error = StringPrintf("Could not load '%s' for plugin.",
                            WideToUTF8(plugin.path.ToWStringHack()).c_str());
      return NULL;
    }
  }

  // Validate icon location for page actions.
  const PageActionMap& page_actions = extension->page_actions();
  for (PageActionMap::const_iterator i(page_actions.begin());
       i != page_actions.end(); ++i) {
    PageAction* page_action = i->second;
    const std::vector<FilePath>& icon_paths = page_action->icon_paths();
    for (std::vector<FilePath>::const_iterator iter = icon_paths.begin();
         iter != icon_paths.end(); ++iter) {
      FilePath path = *iter;
      if (!file_util::PathExists(path)) {
        *error = StringPrintf("Could not load icon '%s' for page action.",
                              WideToUTF8(path.ToWStringHack()).c_str());
        return NULL;
      }
    }
  }

  return extension.release();
}

void UninstallExtension(const std::string& id, const FilePath& extensions_dir) {
  // First, delete the Current Version file. If the directory delete fails, then
  // at least the extension won't be loaded again.
  FilePath extension_root = extensions_dir.AppendASCII(id);

  if (!file_util::PathExists(extension_root)) {
    LOG(WARNING) << "Asked to remove a non-existent extension " << id;
    return;
  }

  FilePath current_version_file = extension_root.AppendASCII(
      kCurrentVersionFileName);
  if (!file_util::PathExists(current_version_file)) {
    LOG(WARNING) << "Extension " << id
                 << " does not have a Current Version file.";
  } else {
    if (!file_util::Delete(current_version_file, false)) {
      LOG(WARNING) << "Could not delete Current Version file for extension "
                   << id;
      return;
    }
  }

  // OK, now try and delete the entire rest of the directory. One major place
  // this can fail is if the extension contains a plugin (stupid plugins). It's
  // not a big deal though, because we'll notice next time we startup that the
  // Current Version file is gone and finish the delete then.
  if (!file_util::Delete(extension_root, true))
    LOG(WARNING) << "Could not delete directory for extension " << id;
}

void GarbageCollectExtensions(const FilePath& install_directory) {
  // Nothing to clean up if it doesn't exist.
  if (!file_util::DirectoryExists(install_directory))
    return;

  LOG(INFO) << "Loading installed extensions...";
  file_util::FileEnumerator enumerator(install_directory,
                                       false,  // Not recursive.
                                       file_util::FileEnumerator::DIRECTORIES);
  FilePath extension_path;
  for (extension_path = enumerator.Next(); !extension_path.value().empty();
       extension_path = enumerator.Next()) {
    std::string extension_id = WideToASCII(
        extension_path.BaseName().ToWStringHack());

    // If there is no Current Version file, just delete the directory and move
    // on. This can legitimately happen when an uninstall does not complete, for
    // example, when a plugin is in use at uninstall time.
    FilePath current_version_path = extension_path.AppendASCII(
        kCurrentVersionFileName);
    if (!file_util::PathExists(current_version_path)) {
      LOG(INFO) << "Deleting incomplete install for directory "
                << WideToASCII(extension_path.ToWStringHack()) << ".";
      file_util::Delete(extension_path, true);  // Recursive.
      continue;
    }

    // Ignore directories that aren't valid IDs.
    if (!Extension::IdIsValid(extension_id)) {
      LOG(WARNING) << "Invalid extension ID encountered in extensions "
                      "directory: " << extension_id;
      LOG(INFO) << "Deleting invalid extension directory "
                << WideToASCII(extension_path.ToWStringHack()) << ".";
      file_util::Delete(extension_path, true);  // Recursive.
      continue;
    }
  }
}

}  // extensionfile_util
<|endoftext|>