I have started looking at tutorials for making TVML/TVJS based apps for the new Apple TV, and I have two problems that makes the development process very tedious and impractical.
First thing I am having trouble understanding is how I am supposed to debug code that happens on startup of the application. I have connected the Safari debugger, and I do manage to hit some breakpoints, but only for code that is triggered by some user input. On startup I am loading an xml document from a remote location, and I will use this to dynamically generate the tvml template, but I cannot get the debugger to stop anywhere in the code that is running before the template is done rendering.
The other anti-productive problem I have is that I cannot seem to reload the JavaScript files in any other way than completely shutting down the application in the simulator (double-click the home button, and swipe the app away). This also makes the debugger quit, so I have to restart that one as well. This surely cannot be the way you are supposed to do continuous development and testing?
You can make the debugger stop at the first line when you choose the Auto Pause and Auto Show options from the Safari menu "Develop/Simulator".
You are correct about the exit issue.
One thing you can also try is to run App.reload() from the Safari Debugger console.
This also restarts the app, maybe in the future they can make it work so the debugger will not be gone.
But at the moment this also does not solve the issue.
For manual debugger output (aka console.log()), you could redirect the logging to the Xcode debugger.
(somewhere on the web) I found a way to actually do that, in short it looks like...
AppDelegate.Swift
func appController(appController: TVApplicationController, evaluateAppJavaScriptInContext jsContext: JSContext) {
let jsInterface: cJsInterface = cJsInterface();
jsContext.setObject(jsInterface, forKeyedSubscript: "swiftInterface")
}
App.js
// re-route console.log() to XCode debug window
var console = {
log: function() {
var message = '';
for(var i = 0; i < arguments.length; i++) {
message += arguments[i] + ' '
};
swiftInterface.log(message)
}
};
JsInterface.Swift
#objc protocol jsInterfaceProtocol : JSExport {
func log(message: String) -> Void
}
...
class cJsInterface: NSObject, jsInterfaceProtocol {
func log(message: String) -> Void {
print("JS: \(message)")
}
}
Complete sources in github: https://github.com/iBaa/PlexConnectApp/tree/f512dfd9c1cb2fbfed2da43c4e3837435b0b22af
I don't have any solution for the dying debugger myself...
Related
In my electron app I'd like to use console.trace() but want it to be collapsed by default. For that I use this in renderer process:
{
const {groupCollapsed, groupEnd, trace} = console;
console.trace = ((...args:any[]) =>
{
groupCollapsed(...args);
trace("");
groupEnd();
}).bind(console);
}
It works ok (time to time few messages from async functions getting grouped together), however in main process I'm bombarded by these messages (each trace call generates several lines):
[34352:0406/075851.692:ERROR:CONSOLE(6)] "console.assert", source: devtools://devtools/bundled/panels/console/console.js (6)
I'm unable reproduce this exact message with an electron snippet using electron v18.0.2, but it shows similar message instead:
[5636:0406/083113.750:INFO:CONSOLE(5)] "test", source: file:///C:/Users/dev/AppData/Local/Temp/tmp-34776-vztptWEofKLe/renderer.js (5)
[5636:0406/083113.751:INFO:CONSOLE(7)] "console.groupEnd", source: file:///C:/Users/dev/AppData/Local/Temp/tmp-34776-vztptWEofKLe/renderer.js (7)
so how can I see where that error is coming from (aka devtools://devtools/bundled/panels/console/console.js) and most importantly how to suppress it?
[EDIT]
This seems to have nothing to do with me replacing console.trace. It seems the issue is in electron itself. The last version that didn't have this issue was 18.0.0-alpha.5, since 18.0.0-beta.1 these messages appear. If I clear developer tools console, the messages stop for a while, but after some time they start showing up again with each message in dev tools.
I'm trying to reverse engineer a heavily obfuscated JS and one of the tricks the author does is to continuously call the debugger statement from within an anonymous function:
Unfortunately, I cannot right click and Never pause it, because each time the function is called a new anonymous function is spawned. The only way for me to inspect the code with DevTools open is to toggle the Disable all breakpoints button, but that disables my breakpoints too.
Is there any way to disable exclusively all debugger statements in Chrome?
In case there isnt, what could be done to bypass this anti-tampering trick?
Download the offending webworker.js file to your local drive, and use a text editor to replace all occurrences of "debugger" with ";".
Then use a Chrome extension to replace the remote resource with your local modified version.
https://chrome.google.com/webstore/detail/resource-override/pkoacgokdfckfpndoffpifphamojphii?hl=en
FYI: I do not endorse the above extension. It was just the first I found via Google.
This answer is for an old Chrome prior to 2021 where we could hack the internals of devtools itself by using devtools-on-devtools:
undock devtools into a separate window
press the full devtools hotkey - CtrlShifti or ⌘⌥i
paste the following code in this new devtools window console and run it
{
const rx = /\bdebugger\b/y;
const eventSymbol = SDK.DebuggerModel.Events.DebuggerPaused;
const original = [...SDK.targetManager._modelListeners.get(eventSymbol)]
.find(v => v.listener.name === '_debuggerPaused');
const debuggerModel = SDK.targetManager.models(SDK.DebuggerModel)[0];
SDK.targetManager.removeModelListener(
SDK.DebuggerModel,
eventSymbol,
original.listener,
original.thisObject);
SDK.targetManager.addModelListener(
SDK.DebuggerModel,
eventSymbol,
async function({data}) {
if (data._debuggerPausedDetails.reason === 'other') {
const frame = data._debuggerPausedDetails.callFrames[0];
const code = await frame._script.requestContent();
let {columnNumber: x, lineNumber: y} = frame._location;
let pos = 0;
while (y--)
pos = code.indexOf('\n', pos) + 1;
rx.lastIndex = Math.max(0, pos + x);
if (rx.test(code)) {
debuggerModel.resume();
return;
}
}
original.listener.apply(original.thisObject, arguments);
});
}
Notes:
You can save this code as a snippet in devtools to run it later.
To quickly switch docking mode in the main devtools press CtrlShiftD or ⌘⇧D
Theoretically, it's not that hard to put this code into resources.pak file in Chrome application directory. There are several tools to decompile/build that file so just add the code to any script that has something like SDK.DebuggerModel.Events.DebuggerPaused inside. One can even write a tool that does that automatically on Chrome update.
Right-click the in the gutter on the line with the debugger statement and select "Never pause here".
I am getting this error
System.IO.FileNotFoundException: 'Could not load file or assembly 'CefSharp.Core, Version=63.0.3.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138'. The system cannot find the file specified.'
I am trying to run the cefsharp.minimalexample.offscreen program in .net core 2.0. in visual studio 2017
what I have done so far
1 . Created .net core console application
2 . Installed NuGet packages Cefsharp.Offscreen (which installs the dependencies cefsharp.common and redist)
3 . Installed Microsoft.windows.compatibility nuget package to get the system.drawing in .net core (It was not working with System.Drawing.Common as the Cefsharp ScreenshotAsync function using system.drawing)
These steps will clear all the errors and the project will build successfully.
I am getting the above mentioned error.
I have checked all the required files mentioned in the Cefsharp documentation in the current running folder (debug). All files are available ,Still error is not going away.
It works fine in old Dot net versions 4.6.
I could not find any helping documents for implementing cefsharp.offscreen with .net core any where.
This is the code from the example provided in the Cefsharp.offscreen.
Please let me know if you can shed some light on this issue. Thanks in advance.
public class Program
{
private static ChromiumWebBrowser browser;
public static void Main(string[] args)
{
const string testUrl = "https://www.google.com/";
Console.WriteLine("This example application will load {0}, take a screenshot, and save it to your desktop.", testUrl);
Console.WriteLine("You may see Chromium debugging output, please wait...");
Console.WriteLine();
var settings = new CefSettings()
{
//By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache")
};
//Perform dependency check to make sure all relevant resources are in our output directory.
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);
// Create the offscreen Chromium browser.
browser = new ChromiumWebBrowser(testUrl);
// An event that is fired when the first page is finished loading.
// This returns to us from another thread.
browser.LoadingStateChanged += BrowserLoadingStateChanged;
// We have to wait for something, otherwise the process will exit too soon.
Console.ReadKey();
// Clean up Chromium objects. You need to call this in your application otherwise
// you will get a crash when closing.
Cef.Shutdown();
}
private static void BrowserLoadingStateChanged(object sender, LoadingStateChangedEventArgs e)
{
// Check to see if loading is complete - this event is called twice, one when loading starts
// second time when it's finished
// (rather than an iframe within the main frame).
if (!e.IsLoading)
{
// Remove the load event handler, because we only want one snapshot of the initial page.
browser.LoadingStateChanged -= BrowserLoadingStateChanged;
var scriptTask = browser.EvaluateScriptAsync("document.getElementById('lst-ib').value = 'CefSharp Was Here!'");
scriptTask.ContinueWith(t =>
{
//Give the browser a little time to render
Thread.Sleep(500);
// Wait for the screenshot to be taken.
var task = browser.ScreenshotAsync();
task.ContinueWith(x =>
{
// Make a file to save it to (e.g. C:\Users\jan\Desktop\CefSharp screenshot.png)
var screenshotPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "CefSharp screenshot.png");
Console.WriteLine();
Console.WriteLine("Screenshot ready. Saving to {0}", screenshotPath);
// Save the Bitmap to the path.
// The image type is auto-detected via the ".png" extension.
task.Result.Save(screenshotPath);
// We no longer need the Bitmap.
// Dispose it to avoid keeping the memory alive. Especially important in 32-bit applications.
task.Result.Dispose();
Console.WriteLine("Screenshot saved. Launching your default image viewer...");
// Tell Windows to launch the saved image.
Process.Start(screenshotPath);
Console.WriteLine("Image viewer launched. Press any key to exit.");
}, TaskScheduler.Default);
});
}
}
}
I am trying to test something here.
I am using electron and javascript. I am trying to load a profile into the page when a user selects it. None of my console log statements are showing in the console, however when the user makes the "profileSelect" change event, the values are loaded correctly. The reason I am testing this is because I am trying to add an addition to this file, checking a checkbox (it's not working either).
profileSelect.change(function(event) {
//If the value isn't null
console.log('yo')
if (profileSelect.val() == '') {
clearBilling();
} else {
ipcRenderer.once(profileSelect.val() + 'profileData', function(event, data) {
//Return card to style it was first added like
console.log('sshi')
//This allows us to parse the data on profile save
const cardParse = String(data.card.number).match(/.{3,4}/g).join(' ')
const dateParse = String(data.card.month) + ' / ' + String(data.card.year);
profileName.val(profileSelect.val());
billingFirstName.val(data.billing.firstName);
billingLastName.val(data.billing.lastName);
billingAddress1.val(data.billing.address1);
billingAddress2.val(data.billing.address2);
billingCity.val(data.billing.city);
billingState.val(data.billing.state);
billingZipcode.val(data.billing.zipcode);
billingCountry.val(data.billing.country);
billingPhone.val(data.billing.phone);
billingEmail.val(data.email);
shippingFirstName.val(data.shipping.firstName);
shippingLastName.val(data.shipping.lastName);
shippingAddress1.val(data.shipping.address1);
shippingAddress2.val(data.shipping.address2);
shippingCity.val(data.shipping.city);
shippingState.val(data.shipping.state);
shippingZipcode.val(data.shipping.zipcode);
shippingCountry.val(data.shipping.country);
shippingPhone.val(data.shipping.phone);
cardName.val(data.card.name);
cardNumber.val(cardParse);
cardCVV.val(data.card.code);
cardExpire.val(dateParse);
})
//Send the selected profile
ipcRenderer.send('requestProfile', profileSelect.val())
}
})
Why aren't the console log statements logging? Thanks for any input :)
Electron has 2 processes, and hence 2 consoles. The 2 processes are main and renderer, main being a node.js process, renderer being a browser process.
Main process console.log would be shown in the terminal (if running in dev) or in the browser console window if in the renderer process.
You seem to be logging from the renderer process as per the ipcRenderer statements.
The renderer console can be shown via the standard chrome devtools shortcut (as its running a chrome instance) (usually F12)
You won't be able to see any console statements from renderer in main, or main in renderer.
I am experiencing some different javascript behavior when running my site on Kindle Fire than through Chrome. In order to debug this I need access to something like the Chrome Developer Tool or Firebug. Any suggestions?
In the same boat here... was hoping adb logcat would help, but javascript console messages don't seem to appear there. Perhaps there's something that needs to be set on the device to direct console logs to logcat?
edit: found a decent solution: http://jsconsole.com -- allows you to set up a remote debug/logging console. Pretty simple (console logging only, so you'll need to dump a lot of into into the logs)... but it works well. Helped me track down the source of my issues, at least!
how-to: http://jsconsole.com/remote-debugging.html
I took a different approach and created a wrapper native app that pop up a dialog for JavaScript.
My wrapper code is rather large, so I took a snippet of the relavant parts. It actually works and will display ANY javascript error.
// registers the debugger to catch errors
WebView engine = (WebView) findViewById(R.id.web_engine);
engine.setWebChromeClient(new DebugClient(this));
// the class that manages the errors
private class DebugClient extends WebChromeClient {
Activity activity;
public DebugClient(Activity activity) {
this.activity = activity;
}
#Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
if (consoleMessage.messageLevel() == MessageLevel.ERROR
|| consoleMessage.messageLevel() == MessageLevel.WARNING) {
String title="Javascript error on line "
+ String.valueOf(consoleMessage.lineNumber())
+ " of " + consoleMessage.sourceId();
AlertBox alertBox=new AlertBox(activity, (ActionListener)null, title, consoleMessage.message(), "OK");
alertBox.show();
alertBoxes.add(alertBox);
}
return true;
}
}
To compile this, you'll need to install the Android SDK on your computer, and probably a Java IDE (Eclipse?) with ADT. Then you just do: create new project, add a WebView component into your layout/main.xml, and paste the code. Compile and install on your Kindle Fire.