Add a permissions model to the Chrome NPAPI plugin.
Permissions are stored in localstorage of the background page.
They can be changed by navigating to the extension's options page.
A page action indicates if the plugin permissions are good or bad for the current host.
Review at http://gwt-code-reviews.appspot.com/1084801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9283 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/plugins/npapi/ScriptableInstance.cpp b/plugins/npapi/ScriptableInstance.cpp
index 8b85d75..1e74d4d 100644
--- a/plugins/npapi/ScriptableInstance.cpp
+++ b/plugins/npapi/ScriptableInstance.cpp
@@ -21,10 +21,6 @@
#include "ReturnMessage.h"
#include "ServerMethods.h"
#include "AllowedConnections.h"
-#ifdef _WINDOWS
-#include "Preferences.h"
-#include "AllowDialog.h"
-#endif
#include "mozincludes.h"
#include "scoped_ptr/scoped_ptr.h"
@@ -32,6 +28,10 @@
using std::string;
using std::endl;
+const static string BACKGROUND_PAGE_STR = "chrome-extension://jpjpnpmbddbjkfaccnmhnkdgjideieim/background.html";
+const static string UNKNOWN_STR = "unknown";
+const static string INCLUDE_STR = "include";
+const static string EXCLUDE_STR = "exclude";
static inline string convertToString(const NPString& str) {
return string(GetNPStringUTF8Characters(str), GetNPStringUTF8Length(str));
@@ -62,6 +62,12 @@
_connectId(NPN_GetStringIdentifier("connect")),
initID(NPN_GetStringIdentifier("init")),
toStringID(NPN_GetStringIdentifier("toString")),
+ loadHostEntriesID(NPN_GetStringIdentifier("loadHostEntries")),
+ locationID(NPN_GetStringIdentifier("location")),
+ hrefID(NPN_GetStringIdentifier("href")),
+ urlID(NPN_GetStringIdentifier("url")),
+ includeID(NPN_GetStringIdentifier("include")),
+ getHostPermissionID(NPN_GetStringIdentifier("getHostPermission")),
connectedID(NPN_GetStringIdentifier("connected")),
statsID(NPN_GetStringIdentifier("stats")),
gwtId(NPN_GetStringIdentifier("__gwt_ObjectId")),
@@ -178,7 +184,11 @@
bool ScriptableInstance::hasMethod(NPIdentifier name) {
Debug::log(Debug::Debugging) << "ScriptableInstance::hasMethod(name=" << NPN_UTF8FromIdentifier(name) << ")"
<< Debug::flush;
- if (name == _connectId || name == initID || name == toStringID) {
+ if (name == _connectId ||
+ name == initID ||
+ name == toStringID ||
+ name == loadHostEntriesID ||
+ name == getHostPermissionID) {
return true;
}
return false;
@@ -200,6 +210,10 @@
val += _channel->isConnected() ? 'Y' : 'N';
val += ']';
NPVariantProxy::assignFrom(*result, val);
+ } else if (name == loadHostEntriesID) {
+ loadHostEntries(args, argCount, result);
+ } else if (name == getHostPermissionID) {
+ getHostPermission(args, argCount, result);
}
return true;
}
@@ -248,6 +262,81 @@
result->type = NPVariantType_Bool;
}
+string ScriptableInstance::getLocationHref() {
+ NPVariantWrapper locationVariant(*this);
+ NPVariantWrapper hrefVariant(*this);
+
+ // window.location
+ NPN_GetProperty(getNPP(), window, locationID, locationVariant.addressForReturn());
+ //window.location.href
+ NPN_GetProperty(getNPP(), locationVariant.getAsObject(), hrefID, hrefVariant.addressForReturn());
+
+ const NPString* locationHref = NPVariantProxy::getAsNPString(hrefVariant);
+ return convertToString(*locationHref);
+}
+
+
+void ScriptableInstance::loadHostEntries(const NPVariant* args, unsigned argCount, NPVariant* result) {
+ string locationHref = getLocationHref();
+ if (locationHref.compare(BACKGROUND_PAGE_STR) == 0) {
+ AllowedConnections::clearRules();
+ for (unsigned i = 0; i < argCount; ++i) {
+ //Get the host entry object {url: "somehost.net", include: true/false}
+ NPObject* hostEntry = NPVariantProxy::getAsObject(args[i]);
+ if (!hostEntry) {
+ Debug::log(Debug::Error) << "Got a host entry that is not an object.\n";
+ break;
+ }
+
+ //Get the url
+ NPVariantWrapper urlVariant(*this);
+ if (!NPN_GetProperty(getNPP(), hostEntry, urlID, urlVariant.addressForReturn()) ||
+ !urlVariant.isString()) {
+ Debug::log(Debug::Error) << "Got a host.url entry that is not a string.\n";
+ break;
+ }
+ const NPString* urlNPString = urlVariant.getAsNPString();
+ string urlString = convertToString(*urlNPString);
+
+ //Include/Exclude?
+ NPVariantWrapper includeVariant(*this);
+ if (!NPN_GetProperty(getNPP(), hostEntry, includeID, includeVariant.addressForReturn()) ||
+ !includeVariant.isBoolean()) {
+ Debug::log(Debug::Error) << "Got a host.include entry that is not a boolean.\n";
+ break;
+ }
+ bool include = includeVariant.getAsBoolean();
+ Debug::log(Debug::Info) << "Adding " << urlString << "(" << (include ? "include" : "exclude") << ")\n";
+ AllowedConnections::addRule(urlString, !include);
+ }
+ } else {
+ Debug::log(Debug::Error) << "ScriptableInstance::loadHostEntries called from outside the background page: " <<
+ locationHref << "\n";
+ }
+}
+
+void ScriptableInstance::getHostPermission(const NPVariant* args, unsigned argCount, NPVariant* result) {
+ if (argCount != 1 || !NPVariantProxy::isString(args[0])) {
+ Debug::log(Debug::Error) << "ScriptableInstance::getHostPermission called with incorrect arguments.\n";
+ }
+
+ const NPString url = args[0].value.stringValue;
+ const string urlStr = convertToString(url);
+ bool allowed = false;
+ bool matches = AllowedConnections::matchesRule(urlStr, &allowed);
+ string retStr;
+
+ if (!matches) {
+ retStr = UNKNOWN_STR;
+ } else if (allowed) {
+ retStr = INCLUDE_STR;
+ } else {
+ retStr = EXCLUDE_STR;
+ }
+
+ NPVariantProxy(*this, *result) = retStr;
+}
+
void ScriptableInstance::connect(const NPVariant* args, unsigned argCount, NPVariant* result) {
if (argCount != 5 || !NPVariantProxy::isString(args[0])
|| !NPVariantProxy::isString(args[1])
@@ -263,7 +352,8 @@
result->type = NPVariantType_Void;
return;
}
- const NPString url = args[0].value.stringValue;
+ //ignore args[0]. Get the URL from window.location.href instead.
+ const string urlStr = getLocationHref();
const NPString sessionKey = args[1].value.stringValue;
const NPString hostAddr = args[2].value.stringValue;
const NPString moduleName = args[3].value.stringValue;
@@ -272,27 +362,9 @@
<< ",sessionKey=" << NPVariantProxy::toString(args[1]) << ",host=" << NPVariantProxy::toString(args[2])
<< ",module=" << NPVariantProxy::toString(args[3]) << ",hostedHtmlVers=" << NPVariantProxy::toString(args[4])
<< ")" << Debug::flush;
- const std::string urlStr = convertToString(url);
-#ifdef _WINDOWS
- // TODO: platform-independent preferences storage
- Preferences::loadAccessList();
-#endif
bool allowed = false;
- if (!AllowedConnections::matchesRule(urlStr, &allowed)) {
-#ifdef _WINDOWS
- // TODO: platform-independent allow-connection dialog
- bool remember = false;
- allowed = AllowDialog::askUserToAllow(&remember);
- if (remember) {
- std::string host = AllowedConnections::getHostFromUrl(urlStr);
- Preferences::addNewRule(host, !allowed);
- }
-#elif 0
- // WARNING: BIG SECURITY HOLE IF ENABLED!
- allowed = true;
-#endif
- }
+ AllowedConnections::matchesRule(urlStr, &allowed);
if (!allowed) {
BOOLEAN_TO_NPVARIANT(false, *result);
result->type = NPVariantType_Bool;
@@ -356,7 +428,7 @@
return id;
}
-void ScriptableInstance::fatalError(HostChannel& channel, const std::string& message) {
+void ScriptableInstance::fatalError(HostChannel& channel, const string& message) {
// TODO(jat): better error handling
Debug::log(Debug::Error) << "Fatal error: " << message << Debug::flush;
}
@@ -382,7 +454,7 @@
}
}
-void ScriptableInstance::loadJsni(HostChannel& channel, const std::string& js) {
+void ScriptableInstance::loadJsni(HostChannel& channel, const string& js) {
NPString npScript;
dupString(js.c_str(), npScript);
NPVariantWrapper npResult(*this);
@@ -405,7 +477,7 @@
NPObject* obj = localObjects.get(id);
NPIdentifier propID;
if (args[1].isString()) {
- std::string propName = args[1].getString();
+ string propName = args[1].getString();
propID = NPN_GetStringIdentifier(propName.c_str());
} else {
int propNum = args[1].getInt();
@@ -434,7 +506,7 @@
NPObject* obj = localObjects.get(id);
NPIdentifier propID;
if (args[1].isString()) {
- std::string propName = args[1].getString();
+ string propName = args[1].getString();
propID = NPN_GetStringIdentifier(propName.c_str());
} else {
int propNum = args[1].getInt();
@@ -477,7 +549,7 @@
}
Debug::log(Debug::Error) << Debug::flush;
// TODO(jat): should we create a real exception object?
- std::string buf("unexpected invokeSpecial(");
+ string buf("unexpected invokeSpecial(");
buf += static_cast<int>(dispatchId);
buf += ")";
returnValue->setString(buf);
@@ -485,7 +557,7 @@
}
bool ScriptableInstance::invoke(HostChannel& channel, const Value& thisRef,
- const std::string& methodName, int numArgs, const Value* const args,
+ const string& methodName, int numArgs, const Value* const args,
Value* returnValue) {
Debug::log(Debug::Debugging) << "invokeJS(" << methodName << ", this="
<< thisRef.toString() << ", numArgs=" << numArgs << ")" << Debug::flush;