What XPCOM interfaces should I use to detect opening, closing and switching of tabs and also get their associated URL from a firefox extension?
I have seen instances of code that manage tabs in JS, but how about from C++ ?
You can write small JS component that will reroute tab events to your C++ component using nsIObserverService.
In C++ code you can use this snippet to register your component as observer to user defined events that is used for rerouting tab events.
NS_IMETHODIMP MyCppComponent::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *aData)
{
if( !strcmp( aTopic, "xpcom-startup" ) )
{
nsCOMPtr<nsIObserverService> observerService =
do_GetService( "#mozilla.org/observer-service;1" );
observerService->AddObserver( this, "my-tab-open", false );
observerService->AddObserver( this, "my-tab-close", false );
observerService->AddObserver( this, "my-tab-select", false );
}
else if( !strcmp( aTopic, "my-tab-open" ) )
{
/* . . . */
}
else if( !strcmp( aTopic, "my-tab-close" ) )
{
/* . . . */
}
else if( !strcmp( aTopic, "my-tab-select" ) )
{
/* . . . */
}
/* . . . */
}
And in helper JS component you should to subscribe to tab events and in event handlers you can extract desired data and raise user defined events to execute C++ code.
function tabOpened(event) {
var obsSvc = CC["#mozilla.org/observer-service;1"].
getService(CI.nsIObserverService);
obsSvc.notifyObservers(event.target.linkedBrowser.contentWindow,
"my-tab-open", "some data");
}
function tabClosed(event) {
var obsSvc = CC["#mozilla.org/observer-service;1"].
getService(CI.nsIObserverService);
obsSvc.notifyObservers(event.target.linkedBrowser.contentWindow,
"my-tab-close", "some data");
}
function tabSelected(event) {
var obsSvc = CC["#mozilla.org/observer-service;1"].
getService(CI.nsIObserverService);
obsSvc.notifyObservers(event.target.linkedBrowser.contentWindow,
"my-tab-select", "some data");
}
function contentWndLoad(event) {
var obsSvc = CC["#mozilla.org/observer-service;1"].
getService(CI.nsIObserverService);
var browser = getMostRecentBrowserWindow().getBrowser();
browser.tabContainer.addEventListener("TabOpen", tabOpened, false);
browser.tabContainer.addEventListener("TabClose", tabClosed, false);
browser.tabContainer.addEventListener("TabSelect", tabSelected, false);
}
MyJsComponent.prototype = {
/* . . . */
observe: function(aSubject, aTopic, aData) {
switch(aTopic) {
case "xpcom-startup":
var obsSvc = CC["#mozilla.org/observer-service;1"].
getService(CI.nsIObserverService);
obsSvc.addObserver(this, "toplevel-window-ready", false);
break;
case "toplevel-window-ready":
aSubject.addEventListener("load", contentWndLoad, false);
break;
}
}
/* . . . */
}
Also there are some additional code that you should add to handle specific cases. For instance when user close browser window you won't receive TabClose events for opened tabs in that window... And don’t forget to unregister your observers when you longer need them.
Related
I'm implementing a speech synthesizer using Audio Unit, based on the Core Audio examples. Everything works as expected, except that StopSpeech and StopSpeechAt appear to do nothing.
Here are the speak and stop methods:
void
Synthesizer::speak( const string &text )
{
mIsSpeaking = true;
mLastText = text;
NSString *ns_text = [NSString stringWithCString:text.c_str()
encoding:[NSString defaultCStringEncoding]];
CFStringRef cf_text = (__bridge CFStringRef)ns_text;
CheckError( SpeakCFString( mAuChannel, cf_text, NULL ), "SpeakCFString failed" );
}
void
Synthesizer::stop()
{
if ( isSpeaking() ) {
mStopRequested = true;
CheckError( StopSpeech( mAuChannel ), "StopSpeech failed" );
}
else {
mIsSpeaking = false;
}
}
I've verified that StopSpeech is called on the same thread as SpeakCFString. Oddly, when I try to step into the StopSpeech call with the debugger, it skips right over it. Really weirdly, the Speech Done callback is fired when StopSpeech is called, but the speech continues regardless.
Any idea what might be going on, or things I can do to debug? Is there some workaround I could use to disable the Audio Unit node temporarily?
This is on MacOS 10.12.5.
Meaning, they don't have to be distributed. I'm thinking about using memcached or redis for that. Probably the latter one. What I'm concerned about is "we've got to free some memory, so we'll delete this key/value before it expired" thing. But I'm open to other suggestions as well.
tl;dr Use ready-made solution, suggested by developers.
So, I decided not to use memcached for the purpose. Since it's a caching server. I don't see a way to ensure that it doesn't delete my keys because it's out of memory. With, redis that's not an issue as long as maxmemory-policy = noeviction.
There are 3 links I want to share with you. They are basically 3 ways, that I now know, to solve the issue. As long as you have redis >= 2.6.0 that is.
redis >= 2.6.12
If you've got redis >= 2.6.12, you're lucky and can simply use setnx command with its new options ex and nx:
$redis->set($name, <whatever>, array('nx', 'ex' => $ttl));
But we can't just delete the lock in the end, if we are to allow for critical section taking longer then we expected (>= ttl). Consider the following situation:
C1 acquires the lock
lock expires
C2 acquires the lock
C1 deletes C2's lock
For that not to happen we are going to store current timestamp as a value of the lock. Then, knowing that Lua scripts are atomic (see Atomicity of scripts):
$redis->eval('
if redis.call("get", KEYS[1]) == KEYS[2] then
redis.call("del", KEYS[1])
end
', array($name, $now));
However, is it possible for two clients to have equal now values? For that all the above actions should happen within one second and ttl must be equal to 0.
Resulting code:
function get_redis() {
static $redis;
if ( ! $redis) {
$redis = new Redis;
$redis->connect('127.0.0.1');
}
return $redis;
}
function acquire_lock($name, $ttl) {
if ( ! $ttl)
return FALSE;
$redis = get_redis();
$now = time();
$r = $redis->set($name, $now, array('nx', 'ex' => $ttl));
if ( ! $r)
return FALSE;
$lock = new RedisLock($redis, $name, $now);
register_shutdown_function(function() use ($lock) {
$r = $lock->release();
# if ( ! $r) {
# Here we can log the fact that lock has expired too early
# }
});
return $lock;
}
class RedisLock {
var $redis;
var $name;
var $now;
var $released;
function __construct($redis, $name, $now) {
$this->redis = get_redis();
$this->name = $name;
$this->now = $now;
}
function release() {
if ($this->released)
return TRUE;
$r = $this->redis->eval('
if redis.call("get", KEYS[1]) == KEYS[2] then
redis.call("del", KEYS[1])
return 1
else
return 0
end
', array($this->name, $this->now));
if ($r)
$this->released = TRUE;
return $r;
}
}
$l1 = acquire_lock('l1', 4);
var_dump($l1 ? date('H:i:s', $l1->expires_at) : FALSE);
sleep(2);
$l2 = acquire_lock('l1', 4);
var_dump($l2 ? date('H:i:s', $l2->expires_at) : FALSE); # FALSE
sleep(4);
$l3 = acquire_lock('l1', 4);
var_dump($l3 ? date('H:i:s', $l3->expires_at) : FALSE);
expire
The other solution I found here. You simply make the value expire with expire command:
$redis->eval('
local r = redis.call("setnx", ARGV[1], ARGV[2])
if r == 1 then
redis.call("expire", ARGV[1], ARGV[3])
end
', array($name, $now, $ttl));
So, only acquire_lock function changes:
function acquire_lock($name, $ttl) {
if ( ! $ttl)
return FALSE;
$redis = get_redis();
$now = time();
$r = $redis->eval('
local r = redis.call("setnx", ARGV[1], ARGV[2])
if r == 1 then
redis.call("expire", ARGV[1], ARGV[3])
end
return r
', array($name, $now, $ttl));
if ( ! $r)
return FALSE;
$lock = new RedisLock($redis, $name, $now);
register_shutdown_function(function() use ($lock) {
$r = $lock->release();
# if ( ! $r) {
# Here we can log that lock as expired too early
# }
});
return $lock;
}
getset
And the last one is described again in documentation. Marked with "left for historical reasons" note.
This time we store timestamp of the moment when the lock is to expire. We store it with setnx command. If it succeeds, we've acquired the lock. Otherwise, either someone else's holding the lock, or the lock has expired. Be it the latter, we use getset to set new value and if the old value hasn't changed, we've acquired the lock:
$r = $redis->setnx($name, $expires_at);
if ( ! $r) {
$cur_expires_at = $redis->get($name);
if ($cur_expires_at > time())
return FALSE;
$cur_expires_at_2 = $redis->getset($name, $expires_at);
if ($cur_expires_at_2 != $cur_expires_at)
return FALSE;
}
What makes me uncomfortable here is that we seem to have changed someone else's expires_at value, don't we?
On a side note, you can check which redis is it that you're using this way:
function get_redis_version() {
static $redis_version;
if ( ! $redis_version) {
$redis = get_redis();
$info = $redis->info();
$redis_version = $info['redis_version'];
}
return $redis_version;
}
if (version_compare(get_redis_version(), '2.6.12') >= 0) {
...
}
Some debugging functions:
function redis_var_dump($keys) {
foreach (get_redis()->keys($keys) as $key) {
$ttl = get_redis()->ttl($key);
printf("%s: %s%s%s", $key, get_redis()->get($key),
$ttl >= 0 ? sprintf(" (ttl: %s)", $ttl) : '',
nl());
}
}
function nl() {
return PHP_SAPI == 'cli' ? "\n" : '<br>';
}
What has happened to the XSLT processing in IE11?
On IE8/9/10, you can use:
if (window.ActiveXObject) {
var xslt = new ActiveXObject("Msxml2.XSLTemplate");
....
}
On Chrome/Firefox/Safari, you can use:
else {
var xsltProcessor = new XSLTProcessor();
}
But on IE11, neither of these are supported. Does anyone know how this can be accomplished?
Try
if (window.ActiveXObject || "ActiveXObject" in window)
This worked for me working with IE11 and allowed me to instantiate ActiveX objects since the standard old check was being bypassed.
This works for me on Chrome/Edge/Firefox/IE11
function loadXMLDoc(filename) {
if (window.ActiveXObject || "ActiveXObject" in window) {
xhttp = new ActiveXObject("Msxml2.XMLHTTP");
} else {
xhttp = new XMLHttpRequest();
}
xhttp.open("GET", filename, false);
xhttp.send("");
return xhttp.responseXML;
}
if (window.ActiveXObject || "ActiveXObject" in window) {
ie();
} else {
xml = loadXMLDoc("input.xml");
xsl = loadXMLDoc("mmlctop2_0.xsl");
if (document.implementation && document.implementation.createDocument) {
xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xsl);
resultDocument = xsltProcessor.transformToDocument(xml, document);
var serializer = new XMLSerializer();
var transformed = serializer.serializeToString(resultDocument.documentElement);
alert(transformed);
}
}
// https://msdn.microsoft.com/en-us/library/ms753809(v=vs.85).aspx
function ie() {
var xslt = new ActiveXObject("Msxml2.XSLTemplate.3.0");
var xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.3.0");
var xslProc;
xslDoc.async = false;
xslDoc.load("mmlctop2_0.xsl");
if (xslDoc.parseError.errorCode != 0) {
var myErr = xslDoc.parseError;
alert("You have error " + myErr.reason);
} else {
xslt.stylesheet = xslDoc;
var xmlDoc = new ActiveXObject("Msxml2.DOMDocument.3.0");
xmlDoc.async = false;
xmlDoc.load("input.xml");
if (xmlDoc.parseError.errorCode != 0) {
var myErr = xmlDoc.parseError;
alert("You have error " + myErr.reason);
} else {
xslProc = xslt.createProcessor();
xslProc.input = xmlDoc;
xslProc.addParameter("param1", "Hello");
xslProc.transform();
alert(xslProc.output);
}
}
}
You could consider Saxon CE, an XSLT 2.0 processor implemented entirely in JavaScript. This would give you a consistent API across all browsers and would allow you to code using the more powerful XSLT 2.0 language rather than 1.0.
The reason if(window.ActiveXObject) fails in IE11 is because for some reason window.ActiveXObject has become falsy, even though it is still a function. I've taken to being more explicit in my feature detection:
if(window.ActiveXObject !== undefined){
...
}
This approach also covers the case of checking for attributes that are present but not set to a truthy value:
if(document.createElement("span").draggable !== undefined){
...
}
For me running site in a compatibility mode in IE - 11 solved the issue....
Note : This might not be a solution , but I was in a situation where one if my old site was using above mentioned code. But I'm not in a position to Re-code the site
You Can use ("ActiveXObject" in window) which will allow all the IE browsers to come inside the if condition .
Exp :-
if ("ActiveXObject" in window) {
// Internet Explorer For all versions like IE8 , IE9 , IE10 or IE11 etc
}else{
// code for Mozilla, Firefox, Opera, etc.
}
I am developing a plugin (still a newby to development for 2.5) but somehow I don't even get the beast to do the most basic thing - it seems it is not launched at all. However, PHP's parsor-errors are shown on the Frontend, but when this code is triggered nothing happens - none of diagnostice message are shown on screen or in my logfile...
Where's the problem?
<?php
defined( '_JEXEC' ) or die( 'Restricted access' );
jimport('joomla.plugin.plugin');
class plgContentSIMPLE_Plugin extends JPlugin
{
function plgContentSIMPLE_Plugin( &$subject , $config ) {
echo "constructor!";
parent::__construct( $subject , $config );
}
function onPrepareContent ($article , $params, $limitstart)
{
oBDC ("oPC",$article , $params, $limitstart);
}
function onBeforeDisplayContent ($article , $params, $limitstart)
{
oBDC ("oBDC",$article , $params, $limitstart);
}
function onAfterDisplayContent ($article , $params, $limitstart)
{
oBDC ("oADC",$article , $params, $limitstart);
}
function oBDC($whoscalling,$article , $params, $limitstart)
{
echo "whoscalling = " . $whoscalling;
$myFile = "./obdc.log";
$fh = fopen($myFile, 'a'); // or die("can't open file");
$stringData = "\n whoscalling = " . $whoscalling;
fwrite($fh, $stringData);
fclose($fh);
}
}
How are you installing the plugin? Have you read the Joomla Plugin tutorial? Here is a pretty good tutorial, try getting this to work first -
https://www.inmotionhosting.com/support/edu/joomla-25/create-plugin
Several issues:
Naming conventions:
class plgContentSimple extends JPlugin
{
function __construct( &$subject , $config ) {
Event is called "onContentAfterDisplay"...
I'm trying to do a clean install SimpleTester on a new CodeIgniter application, following the instructions here: http://codeigniter.com/wiki/SimpleTester_-_Unit_testing_library
Everything's fine until step 6, when I add "simpletester" to the list of libraries that are autoloaded. As soon as I do that, visiting any page simply results in:
Fatal error: Class 'GroupTest' not found in
/path/to/app/application/libraries/simpletester.php on line 84
Grepping through the code for GroupTest I only see it referenced in comments, and in a readme file which states the following:
The GroupTest has been renamed TestSuite (see below).
It was removed completely in 1.1 in favour of this
name.
I tried modifying line 84 to replace GroupTest with TestSuite, but then I get the following error:
Fatal error: Call to undefined method TestSuite::addTestFile() in
/home/path/to/app/application/libraries/simpletester.php
on line 96
Is this a bug on their end? Has anyone seen this before?
I have run into the same issue. The GroupTest class can be found in test_case.php of version 1.0.1 of SimpleTest:
http://sourceforge.net/projects/simpletest/files/simpletest/simpletest_1.0.1/
Unfortunately, simply inserting v1.0.1 into the libraries folder doesn’t solve all the world’s problems. I no longer get the “Fatal error: Class ‘GroupTest’ not found ...” error, but I do get a segmentation fault and my site no longer works.
I have briefly tried to track down the issue but to no avail.
Note: I also responded on the CodeIgniter Wiki page containing the same question.
I had the same problem with a current project and found that the problem is that GroupTest was replaced with TestSuite which works a little differently.
This is the library code I use:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
$libraryDir = APPPATH . 'libraries/simpletest';
if(!is_dir($libraryDir))
exit("Simpletest must be located in \"$libraryDir\"");
require_once $libraryDir . '/unit_tester.php';
require_once $libraryDir . '/mock_objects.php';
require_once $libraryDir . '/collector.php';
class SimpleTester
{
/**
* What reporter should be used for display.
* Could be either HtmlReporter, SmallReporter, MinimalReporter or ShowPasses.
*/
public $Reporter = 'MinimalReporter';
private $testDir;
private $testTitle;
private $fileExtension;
public function __construct($params = false)
{
$ci =& get_instance();
$ci->config->load('simpletester');
if($params == false) {
$params['runFromIPs'] = $ci->config->item('runFromIPs');
$params['testDir'] = $ci->config->item('testDir');
$params['fileExtension'] = $ci->config->item('fileExtension');
$params['autorun'] = $ci->config->item('autorun');
$params['reporter'] = $ci->config->item('reporter');
$params['testTitle'] = $ci->config->item('testTitle');
}
if(isset($params['runFromIPs']) && strpos($params['runFromIPs'], $ci->input->server('SERVER_ADDR') === FALSE))
{
// Tests won't be run automatically from this IP.
$params['autorun'] = FALSE;
}
// Check if call was an AJAX call. No point in running test
// if not seen and may break the call.
$header = 'CONTENT_TYPE';
if(!empty($_SERVER[$header])) {
// #todo Content types could be placed in config.
$ajaxContentTypes = array('application/x-www-form-urlencoded', 'multipart/form-data');
foreach ($ajaxContentTypes as $ajaxContentType) {
if(false !== stripos($_SERVER[$header], $ajaxContentType))
{
$params['autorun'] = FALSE;
break;
}
}
}
$this->testDir = $params['testDir'];
$this->testTitle = $params['testTitle'];
$this->fileExtension = $params['fileExtension'];
if(isset($params['reporter']))
$this->Reporter = $params['reporter'];
if($params['autorun'] == TRUE)
echo $this->Run();
}
/**
* Run the tests, returning the reporter output.
*/
public function Run()
{
// Save superglobals that might be tested.
if(isset($_SESSION)) $oldsession = $_SESSION;
$oldrequest = $_REQUEST;
$oldpost = $_POST;
$oldget = $_GET;
$oldfiles = $_FILES;
$oldcookie = $_COOKIE;
$test_suite = new TestSuite($this->testTitle);
// Add files in tests_dir
if(is_dir($this->testDir))
{
if($dh = opendir($this->testDir))
{
while(($file = readdir($dh)) !== FALSE)
{
// Test if file ends with php, then include it.
if(substr($file, -(strlen($this->fileExtension)+1)) == '.' . $this->fileExtension)
{
$test_suite->addFile($this->testDir . "/$file");
}
}
closedir($dh);
}
}
// Start the tests
ob_start();
$test_suite->run(new $this->Reporter);
$output_buffer = ob_get_clean();
// Restore superglobals
if(isset($oldsession)) $_SESSION = $oldsession;
$_REQUEST = $oldrequest;
$_POST = $oldpost;
$_GET = $oldget;
$_FILES = $oldfiles;
$_COOKIE = $oldcookie;
return $output_buffer;
}
}
// Html output reporter classes //////////////////////////////////////
/**
* Display passes
*/
class ShowPasses extends HtmlReporter
{
function ShowPasses()
{
$this->HtmlReporter();
}
function paintPass($message)
{
parent::paintPass($message);
print "<span class=\"pass\">Pass</span>: ";
$breadcrumb = $this->getTestList();
array_shift($breadcrumb);
print implode("->", $breadcrumb);
print "->$message<br />\n";
}
function _getCss()
{
return parent::_getCss() . ' .pass {color:green;}';
}
}
/**
* Displays a tiny div in upper right corner when ok
*/
class SmallReporter extends HtmlReporter
{
var $test_name;
function ShowPasses()
{
$this->HtmlReporter();
}
function paintHeader($test_name)
{
$this->test_name = $test_name;
}
function paintFooter($test_name)
{
if($this->getFailCount() + $this->getExceptionCount() == 0)
{
$text = $this->getPassCount() . " tests ok";
print "<div style=\"background-color:#F5FFA8; text-align:center; right:10px; top:30px; border:2px solid green; z-index:10; position:absolute;\">$text</div>";
}
else
{
parent::paintFooter($test_name);
print "</div>";
}
}
function paintFail($message)
{
static $header = FALSE;
if(!$header)
{
$this->newPaintHeader();
$header = TRUE;
}
parent::paintFail($message);
}
function newPaintHeader()
{
$this->sendNoCacheHeaders();
print "<style type=\"text/css\">\n";
print $this->_getCss() . "\n";
print "</style>\n";
print "<h1 style=\"background-color:red; color:white;\">$this->test_name</h1>\n";
print "<div style=\"background-color:#FBFBF0;\">";
flush();
}
}
/**
* Minimal only displays on error
*/
class MinimalReporter extends SmallReporter
{
function paintFooter($test_name)
{
if($this->getFailCount() + $this->getExceptionCount() != 0)
{
parent::paintFooter($test_name);
print "</div>";
}
}
}
Works fine for me I haven't tested all the different reporters yet though. But the default one works fine.
And this is how I use it:
$this->load->library('simpletester');
echo $this->simpletester->Run();
And my config file is:
$config['testDir'] = APPPATH . 'tests';
$config['runFromIPs'] = '127.0.0.1';
$config['reporter'] = 'HtmlReporter';
$config['autorun'] = false;
$config['fileExtension'] = 'php';
$config['testTitle'] = 'My Unit Tests';