KParts
browserrun.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 * 00003 * Copyright (C) 2002 David Faure <faure@kde.org> 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Library General Public 00006 * License version 2, as published by the Free Software Foundation. 00007 * 00008 * This library is distributed in the hope that it will be useful, 00009 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 * Library General Public License for more details. 00012 * 00013 * You should have received a copy of the GNU Library General Public License 00014 * along with this library; see the file COPYING.LIB. If not, write to 00015 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00016 * Boston, MA 02110-1301, USA. 00017 */ 00018 00019 #include "browserrun.h" 00020 #include "browserrun_p.h" 00021 00022 #include <kmessagebox.h> 00023 #include <kfiledialog.h> 00024 #include <kio/job.h> 00025 #include <kio/jobuidelegate.h> 00026 #include <kio/scheduler.h> 00027 #include <kio/copyjob.h> 00028 #include <klocale.h> 00029 #include <kshell.h> 00030 #include <kstringhandler.h> 00031 #include <kmimetypetrader.h> 00032 #include <ktemporaryfile.h> 00033 #include <kdebug.h> 00034 #include <kde_file.h> 00035 #include <kstandarddirs.h> 00036 #include <kdatetime.h> 00037 #include "browseropenorsavequestion.h" 00038 #include <kprotocolmanager.h> 00039 00040 using namespace KParts; 00041 00042 class BrowserRun::BrowserRunPrivate 00043 { 00044 public: 00045 bool m_bHideErrorDialog; 00046 bool m_bRemoveReferrer; 00047 bool m_bTrustedSource; 00048 KParts::OpenUrlArguments m_args; 00049 KParts::BrowserArguments m_browserArgs; 00050 00051 KParts::ReadOnlyPart *m_part; // QGuardedPtr? 00052 QPointer<QWidget> m_window; 00053 QString m_mimeType; 00054 QString m_contentDisposition; 00055 }; 00056 00057 BrowserRun::BrowserRun( const KUrl& url, const KParts::OpenUrlArguments& args, 00058 const KParts::BrowserArguments& browserArgs, 00059 KParts::ReadOnlyPart *part, QWidget* window, 00060 bool removeReferrer, bool trustedSource, bool hideErrorDialog ) 00061 : KRun( url, window, 0 /*mode*/, false /*is_local_file known*/, false /* no GUI */ ), 00062 d(new BrowserRunPrivate) 00063 { 00064 d->m_bHideErrorDialog = hideErrorDialog; 00065 d->m_bRemoveReferrer = removeReferrer; 00066 d->m_bTrustedSource = trustedSource; 00067 d->m_args = args; 00068 d->m_browserArgs = browserArgs; 00069 d->m_part = part; 00070 d->m_window = window; 00071 } 00072 00073 BrowserRun::~BrowserRun() 00074 { 00075 delete d; 00076 } 00077 00078 KParts::ReadOnlyPart* BrowserRun::part() const 00079 { 00080 return d->m_part; 00081 } 00082 00083 KUrl BrowserRun::url() const 00084 { 00085 return KRun::url(); 00086 } 00087 00088 void BrowserRun::init() 00089 { 00090 if ( d->m_bHideErrorDialog ) 00091 { 00092 // ### KRun doesn't call a virtual method when it finds out that the URL 00093 // is either malformed, or points to a non-existing local file... 00094 // So we need to reimplement some of the checks, to handle d->m_bHideErrorDialog 00095 if ( !KRun::url().isValid() ) { 00096 redirectToError( KIO::ERR_MALFORMED_URL, KRun::url().url() ); 00097 return; 00098 } 00099 if ( !isLocalFile() && !hasError() && KRun::url().isLocalFile() ) 00100 setIsLocalFile( true ); 00101 00102 if ( isLocalFile() ) { 00103 KDE_struct_stat buff; 00104 if ( KDE::stat( KRun::url().toLocalFile(), &buff ) == -1 ) 00105 { 00106 kDebug(1000) << KRun::url().toLocalFile() << "doesn't exist."; 00107 redirectToError( KIO::ERR_DOES_NOT_EXIST, KRun::url().toLocalFile() ); 00108 return; 00109 } 00110 setMode( buff.st_mode ); // while we're at it, save it for KRun::init() to use it 00111 } 00112 } 00113 KRun::init(); 00114 } 00115 00116 void BrowserRun::scanFile() 00117 { 00118 kDebug(1000) << KRun::url(); 00119 00120 // Let's check for well-known extensions 00121 // Not when there is a query in the URL, in any case. 00122 // Optimization for http/https, findByURL doesn't trust extensions over http. 00123 QString protocol = KRun::url().protocol(); 00124 00125 if (!KProtocolInfo::proxiedBy(protocol).isEmpty()) { 00126 QString dummy; 00127 protocol = KProtocolManager::slaveProtocol(KRun::url(), dummy); 00128 } 00129 00130 if ( KRun::url().query().isEmpty() && !protocol.startsWith(QLatin1String("http"))) 00131 { 00132 KMimeType::Ptr mime = KMimeType::findByUrl( KRun::url() ); 00133 Q_ASSERT( mime ); 00134 if ( !mime->isDefault() || isLocalFile() ) 00135 { 00136 kDebug(1000) << "MIME TYPE is" << mime->name(); 00137 mimeTypeDetermined( mime->name() ); 00138 return; 00139 } 00140 } 00141 00142 QMap<QString, QString>& metaData = d->m_args.metaData(); 00143 if ( d->m_part ) { 00144 const QString proto = d->m_part->url().protocol().toLower(); 00145 00146 if (proto == "https" || proto == "webdavs") { 00147 metaData.insert("main_frame_request", "TRUE" ); 00148 metaData.insert("ssl_was_in_use", "TRUE" ); 00149 // metaData.insert("ssl_activate_warnings", "TRUE" ); 00150 } else if (proto == "http" || proto == "webdav") { 00151 // metaData.insert("ssl_activate_warnings", "TRUE" ); 00152 metaData.insert("ssl_was_in_use", "FALSE" ); 00153 } 00154 00155 // Set the PropagateHttpHeader meta-data if it has not already been set... 00156 if (!metaData.contains("PropagateHttpHeader")) 00157 metaData.insert("PropagateHttpHeader", "TRUE"); 00158 } 00159 00160 KIO::TransferJob *job; 00161 if ( d->m_browserArgs.doPost() && KRun::url().protocol().startsWith(QLatin1String("http"))) { 00162 job = KIO::http_post( KRun::url(), d->m_browserArgs.postData, KIO::HideProgressInfo ); 00163 job->addMetaData( "content-type", d->m_browserArgs.contentType() ); 00164 } else { 00165 job = KIO::get(KRun::url(), 00166 d->m_args.reload() ? KIO::Reload : KIO::NoReload, 00167 KIO::HideProgressInfo); 00168 } 00169 00170 if ( d->m_bRemoveReferrer ) 00171 metaData.remove("referrer"); 00172 00173 job->addMetaData( metaData ); 00174 job->ui()->setWindow( d->m_window ); 00175 connect( job, SIGNAL( result( KJob *)), 00176 this, SLOT( slotBrowserScanFinished(KJob *))); 00177 connect( job, SIGNAL( mimetype( KIO::Job *, const QString &)), 00178 this, SLOT( slotBrowserMimetype(KIO::Job *, const QString &))); 00179 setJob( job ); 00180 } 00181 00182 void BrowserRun::slotBrowserScanFinished(KJob *job) 00183 { 00184 kDebug(1000) << job->error(); 00185 if ( job->error() == KIO::ERR_IS_DIRECTORY ) 00186 { 00187 // It is in fact a directory. This happens when HTTP redirects to FTP. 00188 // Due to the "protocol doesn't support listing" code in BrowserRun, we 00189 // assumed it was a file. 00190 kDebug(1000) << "It is in fact a directory!"; 00191 // Update our URL in case of a redirection 00192 KRun::setUrl( static_cast<KIO::TransferJob *>(job)->url() ); 00193 setJob( 0 ); 00194 mimeTypeDetermined( "inode/directory" ); 00195 } 00196 else 00197 { 00198 if ( job->error() ) 00199 handleError( job ); 00200 else 00201 KRun::slotScanFinished(job); 00202 } 00203 } 00204 00205 void BrowserRun::slotBrowserMimetype( KIO::Job *_job, const QString &type ) 00206 { 00207 Q_ASSERT( _job == KRun::job() ); Q_UNUSED(_job) 00208 KIO::TransferJob *job = static_cast<KIO::TransferJob *>(KRun::job()); 00209 // Update our URL in case of a redirection 00210 //kDebug(1000) << "old URL=" << KRun::url(); 00211 //kDebug(1000) << "new URL=" << job->url(); 00212 setUrl( job->url() ); 00213 00214 if (job->isErrorPage()) { 00215 d->m_mimeType = type; 00216 handleError(job); 00217 setJob( 0 ); 00218 } else { 00219 kDebug(1000) << "found" << type << "for" << KRun::url(); 00220 00221 // Suggested filename given by the server (e.g. HTTP content-disposition) 00222 // When set, we should really be saving instead of embedding 00223 const QString suggestedFileName = job->queryMetaData("content-disposition-filename"); 00224 setSuggestedFileName(suggestedFileName); // store it (in KRun) 00225 //kDebug(1000) << "suggestedFileName=" << suggestedFileName; 00226 d->m_contentDisposition = job->queryMetaData("content-disposition-type"); 00227 00228 const QString modificationTime = job->queryMetaData("content-disposition-modification-date"); 00229 if (!modificationTime.isEmpty()) { 00230 d->m_args.metaData().insert(QLatin1String("content-disposition-modification-date"), modificationTime); 00231 } 00232 00233 QMapIterator<QString,QString> it (job->metaData()); 00234 while (it.hasNext()) { 00235 it.next(); 00236 if (it.key().startsWith(QLatin1String("ssl_"), Qt::CaseInsensitive)) 00237 d->m_args.metaData().insert(it.key(), it.value()); 00238 } 00239 00240 // Make a copy to avoid a dead reference 00241 QString _type = type; 00242 job->putOnHold(); 00243 setJob( 0 ); 00244 00245 mimeTypeDetermined( _type ); 00246 } 00247 } 00248 00249 BrowserRun::NonEmbeddableResult BrowserRun::handleNonEmbeddable(const QString& mimeType) 00250 { 00251 KService::Ptr dummy; 00252 return handleNonEmbeddable(mimeType, &dummy); 00253 } 00254 00255 BrowserRun::NonEmbeddableResult BrowserRun::handleNonEmbeddable(const QString& _mimeType, KService::Ptr* selectedService) 00256 { 00257 QString mimeType( _mimeType ); 00258 Q_ASSERT( !hasFinished() ); // only come here if the mimetype couldn't be embedded 00259 // Support for saving remote files. 00260 if ( mimeType != "inode/directory" && // dirs can't be saved 00261 !KRun::url().isLocalFile() ) 00262 { 00263 if ( isTextExecutable(mimeType) ) 00264 mimeType = QLatin1String("text/plain"); // view, don't execute 00265 // ... -> ask whether to save 00266 BrowserOpenOrSaveQuestion question(d->m_window, KRun::url(), mimeType); 00267 question.setSuggestedFileName(suggestedFileName()); 00268 if (selectedService) 00269 question.setFeatures(BrowserOpenOrSaveQuestion::ServiceSelection); 00270 BrowserOpenOrSaveQuestion::Result res = question.askOpenOrSave(); 00271 if (res == BrowserOpenOrSaveQuestion::Save) { 00272 save( KRun::url(), suggestedFileName() ); 00273 kDebug(1000) << "Save: returning Handled"; 00274 setFinished( true ); 00275 return Handled; 00276 } 00277 else if (res == BrowserOpenOrSaveQuestion::Cancel) { 00278 // saving done or canceled 00279 kDebug(1000) << "Cancel: returning Handled"; 00280 setFinished( true ); 00281 return Handled; 00282 } 00283 else // "Open" chosen (done by KRun::foundMimeType, called when returning NotHandled) 00284 { 00285 // If we were in a POST, we can't just pass a URL to an external application. 00286 // We must save the data to a tempfile first. 00287 if ( d->m_browserArgs.doPost() ) 00288 { 00289 kDebug(1000) << "request comes from a POST, can't pass a URL to another app, need to save"; 00290 d->m_mimeType = mimeType; 00291 QString extension; 00292 QString fileName = suggestedFileName().isEmpty() ? KRun::url().fileName() : suggestedFileName(); 00293 int extensionPos = fileName.lastIndexOf( '.' ); 00294 if ( extensionPos != -1 ) 00295 extension = fileName.mid( extensionPos ); // keep the '.' 00296 KTemporaryFile tempFile; 00297 tempFile.setSuffix(extension); 00298 tempFile.setAutoRemove(false); 00299 tempFile.open(); 00300 KUrl destURL; 00301 destURL.setPath( tempFile.fileName() ); 00302 KIO::Job *job = KIO::file_copy( KRun::url(), destURL, 0600, KIO::Overwrite ); 00303 job->ui()->setWindow(d->m_window); 00304 connect( job, SIGNAL(result(KJob *)), 00305 this, SLOT(slotCopyToTempFileResult(KJob *)) ); 00306 return Delayed; // We'll continue after the job has finished 00307 } 00308 if (selectedService) 00309 *selectedService = question.selectedService(); 00310 } 00311 } 00312 00313 // Check if running is allowed 00314 if ( !d->m_bTrustedSource && // ... and untrusted source... 00315 !allowExecution( mimeType, KRun::url() ) ) // ...and the user said no (for executables etc.) 00316 { 00317 setFinished( true ); 00318 return Handled; 00319 } 00320 00321 KIO::SimpleJob::removeOnHold(); // Kill any slave that was put on hold. 00322 return NotHandled; 00323 } 00324 00325 //static 00326 bool BrowserRun::allowExecution( const QString &mimeType, const KUrl &url ) 00327 { 00328 if ( !KRun::isExecutable( mimeType ) ) 00329 return true; 00330 00331 if ( !url.isLocalFile() ) // Don't permit to execute remote files 00332 return false; 00333 00334 return ( KMessageBox::warningContinueCancel( 0, 00335 i18n( "Do you really want to execute '%1'?", url.prettyUrl() ), 00336 i18n("Execute File?"), KGuiItem(i18n("Execute")) ) == KMessageBox::Continue ); 00337 } 00338 00339 //static, deprecated 00340 #ifndef KDE_NO_DEPRECATED 00341 BrowserRun::AskSaveResult BrowserRun::askSave( const KUrl & url, KService::Ptr offer, const QString& mimeType, const QString & suggestedFileName ) 00342 { 00343 Q_UNUSED(offer); 00344 BrowserOpenOrSaveQuestion question(0, url, mimeType); 00345 question.setSuggestedFileName(suggestedFileName); 00346 const BrowserOpenOrSaveQuestion::Result result = question.askOpenOrSave(); 00347 return result == BrowserOpenOrSaveQuestion::Save ? Save 00348 : BrowserOpenOrSaveQuestion::Open ? Open 00349 : Cancel; 00350 } 00351 #endif 00352 00353 //static, deprecated 00354 #ifndef KDE_NO_DEPRECATED 00355 BrowserRun::AskSaveResult BrowserRun::askEmbedOrSave( const KUrl & url, const QString& mimeType, const QString & suggestedFileName, int flags ) 00356 { 00357 BrowserOpenOrSaveQuestion question(0, url, mimeType); 00358 question.setSuggestedFileName(suggestedFileName); 00359 const BrowserOpenOrSaveQuestion::Result result = question.askEmbedOrSave(flags); 00360 return result == BrowserOpenOrSaveQuestion::Save ? Save 00361 : BrowserOpenOrSaveQuestion::Embed ? Open 00362 : Cancel; 00363 } 00364 #endif 00365 00366 // Default implementation, overridden in KHTMLRun 00367 void BrowserRun::save( const KUrl & url, const QString & suggestedFileName ) 00368 { 00369 saveUrl(url, suggestedFileName, d->m_window, d->m_args); 00370 } 00371 00372 // static 00373 void BrowserRun::simpleSave( const KUrl & url, const QString & suggestedFileName, 00374 QWidget* window ) 00375 { 00376 saveUrl(url, suggestedFileName, window, KParts::OpenUrlArguments()); 00377 } 00378 00379 void KParts::BrowserRun::saveUrl(const KUrl & url, const QString & suggestedFileName, 00380 QWidget* window, const KParts::OpenUrlArguments& args) 00381 { 00382 // DownloadManager <-> konqueror integration 00383 // find if the integration is enabled 00384 // the empty key means no integration 00385 // only use the downloadmanager for non-local urls 00386 if ( !url.isLocalFile() ) 00387 { 00388 KConfigGroup cfg = KSharedConfig::openConfig("konquerorrc", KConfig::NoGlobals)->group("HTML Settings"); 00389 QString downloadManger = cfg.readPathEntry("DownloadManager", QString()); 00390 if (!downloadManger.isEmpty()) 00391 { 00392 // then find the download manager location 00393 kDebug(1000) << "Using: "<<downloadManger <<" as Download Manager"; 00394 QString cmd=KStandardDirs::findExe(downloadManger); 00395 if (cmd.isEmpty()) 00396 { 00397 QString errMsg=i18n("The Download Manager (%1) could not be found in your $PATH ", downloadManger); 00398 QString errMsgEx= i18n("Try to reinstall it \n\nThe integration with Konqueror will be disabled."); 00399 KMessageBox::detailedSorry(0,errMsg,errMsgEx); 00400 cfg.writePathEntry("DownloadManager",QString()); 00401 cfg.sync (); 00402 } 00403 else 00404 { 00405 // ### suggestedFileName not taken into account. Fix this (and 00406 // the duplicated code) with shiny new KDownload class for 3.2 (pfeiffer) 00407 // Until the shiny new class comes about, send the suggestedFileName 00408 // along with the actual URL to download. (DA) 00409 cmd += ' ' + KShell::quoteArg(url.url()); 00410 if ( !suggestedFileName.isEmpty() ) 00411 cmd += ' ' + KShell::quoteArg(suggestedFileName); 00412 00413 kDebug(1000) << "Calling command" << cmd; 00414 // slave is already on hold (slotBrowserMimetype()) 00415 KIO::Scheduler::publishSlaveOnHold(); 00416 KRun::runCommand(cmd, window); 00417 return; 00418 } 00419 } 00420 } 00421 00422 // no download manager available, let's do it ourself 00423 KFileDialog *dlg = new KFileDialog( QString(), QString() /*all files*/, 00424 window); 00425 dlg->setOperationMode( KFileDialog::Saving ); 00426 dlg->setCaption(i18n("Save As")); 00427 dlg->setConfirmOverwrite(true); 00428 00429 QString name; 00430 if ( !suggestedFileName.isEmpty() ) 00431 name = suggestedFileName; 00432 else 00433 name = url.fileName(KUrl::ObeyTrailingSlash); // can be empty, e.g. in case http://www.kde.org/ 00434 00435 dlg->setSelection(name); 00436 if ( dlg->exec() ) 00437 { 00438 KUrl destURL( dlg->selectedUrl() ); 00439 if ( destURL.isValid() ) 00440 { 00441 saveUrlUsingKIO(url, destURL, window, args.metaData()); 00442 } 00443 } 00444 delete dlg; 00445 } 00446 00447 void BrowserRun::saveUrlUsingKIO(const KUrl & srcUrl, const KUrl& destUrl, 00448 QWidget* window, const QMap<QString, QString> &metaData) 00449 { 00450 KIO::FileCopyJob *job = KIO::file_copy(srcUrl, destUrl, -1, KIO::Overwrite); 00451 00452 const QString modificationTime = metaData[QLatin1String("content-disposition-modification-date")]; 00453 if (!modificationTime.isEmpty()) { 00454 job->setModificationTime(KDateTime::fromString(modificationTime, KDateTime::RFCDate).dateTime()); 00455 } 00456 job->setMetaData(metaData); 00457 job->addMetaData("MaxCacheSize", "0"); // Don't store in http cache. 00458 job->addMetaData("cache", "cache"); // Use entry from cache if available. 00459 job->ui()->setWindow(window); 00460 job->ui()->setAutoErrorHandlingEnabled( true ); 00461 new DownloadJobWatcher(job, metaData); 00462 } 00463 00464 void BrowserRun::slotStatResult( KJob *job ) 00465 { 00466 if ( job->error() ) { 00467 kDebug(1000) << job->errorString(); 00468 handleError( job ); 00469 } else 00470 KRun::slotStatResult( job ); 00471 } 00472 00473 void BrowserRun::handleError( KJob * job ) 00474 { 00475 if ( !job ) { // Shouldn't happen, see docu. 00476 kWarning(1000) << "handleError called with job=0! hideErrorDialog=" << d->m_bHideErrorDialog; 00477 return; 00478 } 00479 00480 KIO::TransferJob *tjob = qobject_cast<KIO::TransferJob *>(job); 00481 if (tjob && tjob->isErrorPage() && !job->error()) { 00482 // The default handling of error pages is to show them like normal pages 00483 // But this is done here in handleError so that KHTMLRun can reimplement it 00484 tjob->putOnHold(); 00485 setJob(0); 00486 if (!d->m_mimeType.isEmpty()) 00487 mimeTypeDetermined(d->m_mimeType); 00488 return; 00489 } 00490 00491 if (d->m_bHideErrorDialog && job->error() != KIO::ERR_NO_CONTENT) 00492 { 00493 redirectToError( job->error(), job->errorText() ); 00494 return; 00495 } 00496 00497 // Reuse code in KRun, to benefit from d->m_showingError etc. 00498 KRun::slotStatResult( job ); 00499 } 00500 00501 // static 00502 KUrl BrowserRun::makeErrorUrl(int error, const QString& errorText, const QString& initialUrl) 00503 { 00504 /* 00505 * The format of the error:/ URL is error:/?query#url, 00506 * where two variables are passed in the query: 00507 * error = int kio error code, errText = QString error text from kio 00508 * The sub-url is the URL that we were trying to open. 00509 */ 00510 KUrl newURL(QString("error:/?error=%1&errText=%2") 00511 .arg( error ) 00512 .arg( QString::fromUtf8( QUrl::toPercentEncoding( errorText ) ) ) ); 00513 00514 QString cleanedOrigUrl = initialUrl; 00515 KUrl runURL = cleanedOrigUrl; 00516 if (runURL.isValid()) { 00517 runURL.setPass( QString() ); // don't put the password in the error URL 00518 cleanedOrigUrl = runURL.url(); 00519 } 00520 00521 newURL.setFragment(cleanedOrigUrl); 00522 return newURL; 00523 00524 // The kde3 approach broke with invalid urls, now that they become empty in qt4. 00525 //KUrl::List lst; 00526 //lst << newURL << runURL; 00527 //return KUrl::join(lst); 00528 } 00529 00530 void BrowserRun::redirectToError( int error, const QString& errorText ) 00531 { 00537 KRun::setUrl(makeErrorUrl(error, errorText, url().url())); 00538 setJob( 0 ); 00539 mimeTypeDetermined( "text/html" ); 00540 } 00541 00542 void BrowserRun::slotCopyToTempFileResult(KJob *job) 00543 { 00544 if ( job->error() ) { 00545 job->uiDelegate()->showErrorMessage(); 00546 } else { 00547 // Same as KRun::foundMimeType but with a different URL 00548 (void) (KRun::runUrl( static_cast<KIO::FileCopyJob *>(job)->destUrl(), d->m_mimeType, d->m_window )); 00549 } 00550 setError( true ); // see above 00551 setFinished( true ); 00552 } 00553 00554 bool BrowserRun::isTextExecutable( const QString &mimeType ) 00555 { 00556 return ( mimeType == "application/x-desktop" || 00557 mimeType == "application/x-shellscript" ); 00558 } 00559 00560 bool BrowserRun::hideErrorDialog() const 00561 { 00562 return d->m_bHideErrorDialog; 00563 } 00564 00565 QString BrowserRun::contentDisposition() const 00566 { 00567 return d->m_contentDisposition; 00568 } 00569 00570 bool BrowserRun::serverSuggestsSave() const 00571 { 00572 // RfC 2183, section 2.8: 00573 // Unrecognized disposition types should be treated as `attachment'. 00574 return !contentDisposition().isEmpty() && (contentDisposition() != "inline"); 00575 } 00576 00577 KParts::OpenUrlArguments& KParts::BrowserRun::arguments() 00578 { 00579 return d->m_args; 00580 } 00581 00582 KParts::BrowserArguments& KParts::BrowserRun::browserArguments() 00583 { 00584 return d->m_browserArgs; 00585 } 00586 00587 #include "browserrun.moc" 00588 #include "browserrun_p.moc"
KDE 4.6 API Reference