summaryrefslogtreecommitdiffstats
path: root/chrome/renderer/user_script_slave.cc
blob: 10d4e439cd2602ffc13d3edae3cbd22684a77de1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// 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/renderer/user_script_slave.h"

#include "base/histogram.h"
#include "base/logging.h"
#include "base/perftimer.h"
#include "base/pickle.h"
#include "base/shared_memory.h"
#include "chrome/common/resource_bundle.h"
#include "googleurl/src/gurl.h"
#include "webkit/glue/webframe.h"

#include "grit/renderer_resources.h" 

// These two strings are injected before and after the Greasemonkey API and
// user script to wrap it in an anonymous scope.
static const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
static const char kUserScriptTail[] = "\n})(window);";

UserScriptSlave::UserScriptSlave()
    : shared_memory_(NULL),
      script_deleter_(&scripts_),
      user_script_start_line_(0) {
  // TODO: Only windows supports resources and only windows supports user
  // scrips, so only load the Greasemonkey API on windows.  Fix this when
  // better cross platofrm support is available.
#if defined(OS_WIN)
  api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
                IDR_GREASEMONKEY_API_JS);
#endif

  // Count the number of lines that will be injected before the user script.
  StringPiece::size_type pos = 0;
  while ((pos = api_js_.find('\n', pos)) != StringPiece::npos) {
    user_script_start_line_++;
    pos++;
  }

  // NOTE: There is actually one extra line in the injected script because the
  // function header includes a newline as well. But WebKit expects the
  // numbering to be one-based, not zero-based, so actually *not* accounting for
  // this extra line ends us up with the right offset.
}

bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
  scripts_.clear();
  script_contents_.clear();

  // Create the shared memory object (read only).
  shared_memory_.reset(new base::SharedMemory(shared_memory, true));
  if (!shared_memory_.get())
    return false;

  // First get the size of the memory block.
  if (!shared_memory_->Map(sizeof(Pickle::Header)))
    return false;
  Pickle::Header* pickle_header =
      reinterpret_cast<Pickle::Header*>(shared_memory_->memory());

  // Now map in the rest of the block.
  int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size;
  shared_memory_->Unmap();
  if (!shared_memory_->Map(pickle_size))
    return false;

  // Unpickle scripts.
  void* iter = NULL;
  size_t num_scripts = 0;
  Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()),
                pickle_size);
  pickle.ReadSize(&iter, &num_scripts);

  for (size_t i = 0; i < num_scripts; ++i) {
    UserScript* script = new UserScript();
    script->Unpickle(pickle, &iter);

    // Note that this is a pointer into shared memory. We don't own it. It gets
    // cleared up when the last renderer or browser process drops their
    // reference to the shared memory.
    const char* body = NULL;
    int body_length = 0;
    CHECK(pickle.ReadData(&iter, &body, &body_length));

    scripts_.push_back(script);
    script_contents_[script] = StringPiece(body, body_length);
  }

  return true;
}

bool UserScriptSlave::InjectScripts(WebFrame* frame,
                                    UserScript::RunLocation location) {
  PerfTimer timer;
  int num_matched = 0;

  for (std::vector<UserScript*>::iterator script = scripts_.begin();
       script != scripts_.end(); ++script) {
    if ((*script)->MatchesUrl(frame->GetURL()) &&
        (*script)->run_location() == location) {
      std::string inject(kUserScriptHead);
      inject.append(api_js_.as_string());
      inject.append(script_contents_[*script].as_string());
      inject.append(kUserScriptTail);
      frame->ExecuteJavaScript(inject,
                               GURL((*script)->url().spec()),
                               -user_script_start_line_);
      ++num_matched;
    }
  }

  if (location == UserScript::DOCUMENT_START) {
    HISTOGRAM_COUNTS_100("UserScripts:DocStart:Count", num_matched);
    HISTOGRAM_TIMES("UserScripts:DocStart:Time", timer.Elapsed());
  } else {
    HISTOGRAM_COUNTS_100("UserScripts:DocEnd:Count", num_matched);
    HISTOGRAM_TIMES("UserScripts:DocEnd:Time", timer.Elapsed());
  }

  LOG(INFO) << "Injected " << num_matched << " scripts into " <<
      frame->GetURL().spec();

  return true;
}